From 65fc162edc304eb828ab1ddb30d59644dff61f85 Mon Sep 17 00:00:00 2001 From: w Date: Thu, 27 Aug 2026 20:46:11 -0400 Subject: [PATCH 01/40] feat(pvm): add host-neutral app runtime --- Cargo.lock | 160 +- Makefile | 3 +- Package.swift | 9 +- .../Sources/TrUAPIHost/pvm_runtime.swift | 1747 +++++++++++++ .../pvm_runtimeFFI/include/module.modulemap | 7 + .../pvm_runtimeFFI/include/pvm_runtimeFFI.h | 698 ++++++ ios/truapi-host/scripts/sync-bindings.sh | 2 +- .../pvm-browser-runtime/LICENSE-MPL-2.0 | 373 +++ js/packages/pvm-browser-runtime/SOURCE | 10 + .../pvm-browser-runtime/dist/SHA256SUMS | 3 + .../dist/pvm-browser-runtime.wasm | Bin 0 -> 888063 bytes .../dist/pvm-gpu-worker.js | 1884 ++++++++++++++ .../pvm-browser-runtime/dist/pvm-worker.js | 1558 ++++++++++++ js/packages/pvm-browser-runtime/package.json | 28 + .../pvm-browser-runtime/scripts/build.mjs | 61 + .../pvm-browser-runtime/src/pvm-gpu-worker.js | 1884 ++++++++++++++ .../src/pvm-wasm-translated.js | 1162 +++++++++ .../src/pvm-wasm-worker-entry.js | 395 +++ .../test/artifacts.test.mjs | 43 + package-lock.json | 12 + package.json | 6 +- rust/crates/pvm-gpu-wire/Cargo.toml | 10 + rust/crates/pvm-gpu-wire/src/lib.rs | 1005 ++++++++ rust/crates/pvm-runtime/Cargo.toml | 33 + rust/crates/pvm-runtime/src/application.rs | 342 +++ rust/crates/pvm-runtime/src/corevm.rs | 851 +++++++ rust/crates/pvm-runtime/src/corevm/tests.rs | 62 + rust/crates/pvm-runtime/src/lib.rs | 1163 +++++++++ rust/crates/pvm-runtime/src/manifest.rs | 220 ++ rust/crates/pvm-runtime/src/native_ffi.rs | 290 +++ rust/crates/pvm-runtime/src/quake_keys.rs | 86 + rust/crates/pvm-runtime/src/tri2d.rs | 435 ++++ rust/crates/pvm-runtime/src/wasm.rs | 538 ++++ rust/crates/pvm-runtime/src/wasm_codegen.rs | 2228 +++++++++++++++++ .../tests/fixtures/framebuffer-test.polkavm | Bin 0 -> 488 bytes rust/crates/truapi-server/Cargo.toml | 1 + rust/crates/truapi-server/src/lib.rs | 3 + 37 files changed, 17303 insertions(+), 9 deletions(-) create mode 100644 ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift create mode 100644 ios/truapi-host/Sources/pvm_runtimeFFI/include/module.modulemap create mode 100644 ios/truapi-host/Sources/pvm_runtimeFFI/include/pvm_runtimeFFI.h create mode 100644 js/packages/pvm-browser-runtime/LICENSE-MPL-2.0 create mode 100644 js/packages/pvm-browser-runtime/SOURCE create mode 100644 js/packages/pvm-browser-runtime/dist/SHA256SUMS create mode 100755 js/packages/pvm-browser-runtime/dist/pvm-browser-runtime.wasm create mode 100644 js/packages/pvm-browser-runtime/dist/pvm-gpu-worker.js create mode 100644 js/packages/pvm-browser-runtime/dist/pvm-worker.js create mode 100644 js/packages/pvm-browser-runtime/package.json create mode 100644 js/packages/pvm-browser-runtime/scripts/build.mjs create mode 100644 js/packages/pvm-browser-runtime/src/pvm-gpu-worker.js create mode 100644 js/packages/pvm-browser-runtime/src/pvm-wasm-translated.js create mode 100644 js/packages/pvm-browser-runtime/src/pvm-wasm-worker-entry.js create mode 100644 js/packages/pvm-browser-runtime/test/artifacts.test.mjs create mode 100644 rust/crates/pvm-gpu-wire/Cargo.toml create mode 100644 rust/crates/pvm-gpu-wire/src/lib.rs create mode 100644 rust/crates/pvm-runtime/Cargo.toml create mode 100644 rust/crates/pvm-runtime/src/application.rs create mode 100644 rust/crates/pvm-runtime/src/corevm.rs create mode 100644 rust/crates/pvm-runtime/src/corevm/tests.rs create mode 100644 rust/crates/pvm-runtime/src/lib.rs create mode 100644 rust/crates/pvm-runtime/src/manifest.rs create mode 100644 rust/crates/pvm-runtime/src/native_ffi.rs create mode 100644 rust/crates/pvm-runtime/src/quake_keys.rs create mode 100644 rust/crates/pvm-runtime/src/tri2d.rs create mode 100644 rust/crates/pvm-runtime/src/wasm.rs create mode 100644 rust/crates/pvm-runtime/src/wasm_codegen.rs create mode 100644 rust/crates/pvm-runtime/tests/fixtures/framebuffer-test.polkavm diff --git a/Cargo.lock b/Cargo.lock index 5a3f46235..decc781da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1510,7 +1510,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef975e30683b2d965054bb0a836f8973857c4ebf6acf274fe46617cd285060d8" dependencies = [ - "foldhash", + "foldhash 0.2.0", "libm", "portable-atomic", "siphasher", @@ -1592,6 +1592,12 @@ 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" @@ -1941,6 +1947,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", + "foldhash 0.1.5", + "serde", ] [[package]] @@ -1951,7 +1959,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", "serde", "serde_core", ] @@ -1964,7 +1972,7 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -2620,6 +2628,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[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.189" @@ -3148,6 +3162,12 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "picosimd" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7823ffd00d2b55ebe51750a19f47f2a33cb1f1d135f5cba893379b81c4d44856" + [[package]] name = "pin-project" version = "1.1.13" @@ -3201,6 +3221,78 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "polkavm" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9#3df1d0309c4c81a1aad0a755d83570d203bba1d9" +dependencies = [ + "libc", + "log", + "picosimd", + "polkavm-assembler 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9)", + "polkavm-common 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9)", + "polkavm-linux-raw 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9)", +] + +[[package]] +name = "polkavm" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997#8cc3c5601aa614b0f6dfac2ae3ee271942b3c997" +dependencies = [ + "libc", + "log", + "picosimd", + "polkavm-assembler 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997)", + "polkavm-common 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997)", + "polkavm-linux-raw 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997)", +] + +[[package]] +name = "polkavm-assembler" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9#3df1d0309c4c81a1aad0a755d83570d203bba1d9" +dependencies = [ + "log", +] + +[[package]] +name = "polkavm-assembler" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997#8cc3c5601aa614b0f6dfac2ae3ee271942b3c997" +dependencies = [ + "log", +] + +[[package]] +name = "polkavm-common" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9#3df1d0309c4c81a1aad0a755d83570d203bba1d9" +dependencies = [ + "log", + "picosimd", + "polkavm-assembler 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9)", +] + +[[package]] +name = "polkavm-common" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997#8cc3c5601aa614b0f6dfac2ae3ee271942b3c997" +dependencies = [ + "log", + "picosimd", + "polkavm-assembler 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997)", +] + +[[package]] +name = "polkavm-linux-raw" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9#3df1d0309c4c81a1aad0a755d83570d203bba1d9" + +[[package]] +name = "polkavm-linux-raw" +version = "0.37.0" +source = "git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997#8cc3c5601aa614b0f6dfac2ae3ee271942b3c997" + [[package]] name = "polling" version = "3.11.0" @@ -3318,6 +3410,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pvm-gpu-wire" +version = "0.1.0" + +[[package]] +name = "pvm-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "log", + "polkavm 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=3df1d0309c4c81a1aad0a755d83570d203bba1d9)", + "polkavm 0.37.0 (git+https://github.com/paritytech/polkavm.git?rev=8cc3c5601aa614b0f6dfac2ae3ee271942b3c997)", + "pvm-gpu-wire", + "serde", + "serde_json", + "thiserror 1.0.69", + "uniffi", + "wasm-encoder", + "wasmi", + "wasmparser 0.240.0", +] + [[package]] name = "qrcode" version = "0.14.1" @@ -4469,6 +4583,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string-interner" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b" +dependencies = [ + "hashbrown 0.15.5", + "serde", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5287,6 +5411,7 @@ dependencies = [ "p256", "parity-scale-codec", "parking_lot", + "pvm-runtime", "rand 0.8.7", "scale-decode", "scale-info", @@ -5820,6 +5945,16 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31d56021e873866c968588ed85ccdf56db5c426e44afdb4618c39895104b920" +[[package]] +name = "wasm-encoder" +version = "0.240.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f" +dependencies = [ + "leb128fmt", + "wasmparser 0.240.0", +] + [[package]] name = "wasmi" version = "0.40.0" @@ -5833,7 +5968,7 @@ dependencies = [ "wasmi_collections", "wasmi_core", "wasmi_ir", - "wasmparser", + "wasmparser 0.221.3", ] [[package]] @@ -5841,6 +5976,9 @@ name = "wasmi_collections" version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" +dependencies = [ + "string-interner", +] [[package]] name = "wasmi_core" @@ -5868,6 +6006,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06bfa36ab3ac2be0dee563380147a5b81ba10dd8885d7fbbc9eb574be67d185" dependencies = [ "bitflags", + "indexmap", +] + +[[package]] +name = "wasmparser" +version = "0.240.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", + "serde", ] [[package]] diff --git a/Makefile b/Makefile index e4a7687db..5f50febf2 100644 --- a/Makefile +++ b/Makefile @@ -444,7 +444,8 @@ xcframework: uniffi ## Build truapi_server.xcframework for iOS device + simulato rm -rf $(XCFRAMEWORK_OUT) $(XCFRAMEWORK_HEADERS) mkdir -p $(XCFRAMEWORK_HEADERS) cp $(UNIFFI_SWIFT_TMP)/truapiFFI.h $(UNIFFI_SWIFT_TMP)/truapi_platformFFI.h \ - $(UNIFFI_SWIFT_TMP)/truapi_serverFFI.h $(XCFRAMEWORK_HEADERS)/ + $(UNIFFI_SWIFT_TMP)/truapi_serverFFI.h $(UNIFFI_SWIFT_TMP)/pvm_runtimeFFI.h \ + $(XCFRAMEWORK_HEADERS)/ cp $(UNIFFI_SWIFT_TMP)/truapi_serverFFI.modulemap $(XCFRAMEWORK_HEADERS)/module.modulemap slices=""; \ for target in $(XCFRAMEWORK_TARGETS); do \ diff --git a/Package.swift b/Package.swift index 8d53ea063..6a2eccabf 100644 --- a/Package.swift +++ b/Package.swift @@ -81,11 +81,18 @@ let package = Package( pkgConfig: nil, providers: [] ), + .systemLibrary( + name: "pvm_runtimeFFI", + path: "ios/truapi-host/Sources/pvm_runtimeFFI/include", + pkgConfig: nil, + providers: [] + ), binaryTarget, .target( name: "TrUAPIHost", dependencies: [ - "truapiFFI", "truapi_platformFFI", "truapi_serverFFI", "truapi_serverFFI_binary", + "truapiFFI", "truapi_platformFFI", "truapi_serverFFI", + "pvm_runtimeFFI", "truapi_serverFFI_binary", ], path: "ios/truapi-host/Sources/TrUAPIHost", resources: [.copy("Resources/truapi-container.js")] diff --git a/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift b/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift new file mode 100644 index 000000000..991898e8a --- /dev/null +++ b/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift @@ -0,0 +1,1747 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +// swiftlint:disable all +import Foundation + +// Depending on the consumer's build setup, the low-level FFI code +// might be in a separate module, or it might be compiled inline into +// this module. This is a bit of light hackery to work with both. +#if canImport(pvm_runtimeFFI) +import pvm_runtimeFFI +#endif + +fileprivate extension RustBuffer { + // Allocate a new buffer, copying the contents of a `UInt8` array. + init(bytes: [UInt8]) { + let rbuf = bytes.withUnsafeBufferPointer { ptr in + RustBuffer.from(ptr) + } + self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) + } + + static func empty() -> RustBuffer { + RustBuffer(capacity: 0, len:0, data: nil) + } + + static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { + try! rustCall { ffi_pvm_runtime_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } + } + + // Frees the buffer in place. + // The buffer must not be used after this is called. + func deallocate() { + try! rustCall { ffi_pvm_runtime_rustbuffer_free(self, $0) } + } +} + +fileprivate extension ForeignBytes { + init(bufferPointer: UnsafeBufferPointer) { + self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress) + } + + init(rawBufferPointer: UnsafeRawBufferPointer) { + self.init( + len: Int32(rawBufferPointer.count), + data: rawBufferPointer.baseAddress?.assumingMemoryBound(to: UInt8.self) + ) + } +} + +// Converter for `&[u8]` / `[ByRef] bytes` arguments. +// +// Conforms to `FfiConverter` so the compiler enforces the full converter +// method set. Only the scope-bound `lower(_:_body:)` overload is sound — +// zero-copy byte buffers only flow foreign -> Rust, and only in argument +// position. The four protocol-witness methods (`lift`, `lower`, `read`, +// `write`) `fatalError` at runtime if anyone reaches them. +// +// The scope-bound `lower` takes a closure because the `ForeignBytes` +// pointer is only guaranteed valid for the duration of +// `Data.withUnsafeBytes`. Callers must run the full FFI call inside +// the closure body. +fileprivate enum FfiConverterByRefBytes: FfiConverter { + typealias SwiftType = Data + typealias FfiType = ForeignBytes + + static func lower(_ value: Data, _ body: (ForeignBytes) throws -> R) rethrows -> R { + return try value.withUnsafeBytes { rawBuf in + try body(ForeignBytes(rawBufferPointer: rawBuf)) + } + } + + static func lower(_ value: Data) -> ForeignBytes { + fatalError("ByRef bytes cannot use the plain lower: returning ForeignBytes escapes the Data.withUnsafeBytes scope. Use the scope-bound lower(_:_body:) overload instead.") + } + + static func lift(_ value: ForeignBytes) throws -> Data { + fatalError("ByRef bytes cannot be lifted: zero-copy &[u8] only flows foreign->Rust") + } + + static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data { + fatalError("ByRef bytes cannot be read from a buffer: zero-copy &[u8] is only supported in argument position, not nested in records/options/etc.") + } + + static func write(_ value: Data, into buf: inout [UInt8]) { + fatalError("ByRef bytes cannot be written to a buffer: zero-copy &[u8] is only supported in argument position, not nested in records/options/etc.") + } +} + +// For every type used in the interface, we provide helper methods for conveniently +// lifting and lowering that type from C-compatible data, and for reading and writing +// values of that type in a buffer. + +// Helper classes/extensions that don't change. +// Someday, this will be in a library of its own. + +fileprivate extension Data { + init(rustBuffer: RustBuffer) { + self.init( + bytesNoCopy: rustBuffer.data!, + count: Int(rustBuffer.len), + deallocator: .none + ) + } +} + +// Define reader functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. +// +// With external types, one swift source file needs to be able to call the read +// method on another source file's FfiConverter, but then what visibility +// should Reader have? +// - If Reader is fileprivate, then this means the read() must also +// be fileprivate, which doesn't work with external types. +// - If Reader is internal/public, we'll get compile errors since both source +// files will try define the same type. +// +// Instead, the read() method and these helper functions input a tuple of data + +fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) { + (data: data, offset: 0) +} + +// Reads an integer at the current offset, in big-endian order, and advances +// the offset on success. Throws if reading the integer would move the +// offset past the end of the buffer. +fileprivate func readInt(_ reader: inout (data: Data, offset: Data.Index)) throws -> T { + let range = reader.offset...size + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + if T.self == UInt8.self { + let value = reader.data[reader.offset] + reader.offset += 1 + return value as! T + } + var value: T = 0 + let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)}) + reader.offset = range.upperBound + return value.bigEndian +} + +// Reads an arbitrary number of bytes, to be used to read +// raw bytes, this is useful when lifting strings +fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array { + let range = reader.offset..<(reader.offset+count) + guard reader.data.count >= range.upperBound else { + throw UniffiInternalError.bufferOverflow + } + var value = [UInt8](repeating: 0, count: count) + value.withUnsafeMutableBufferPointer({ buffer in + reader.data.copyBytes(to: buffer, from: range) + }) + reader.offset = range.upperBound + return value +} + +// Reads a float at the current offset. +fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float { + return Float(bitPattern: try readInt(&reader)) +} + +// Reads a float at the current offset. +fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double { + return Double(bitPattern: try readInt(&reader)) +} + +// Indicates if the offset has reached the end of the buffer. +fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool { + return reader.offset < reader.data.count +} + +// Define writer functionality. Normally this would be defined in a class or +// struct, but we use standalone functions instead in order to make external +// types work. See the above discussion on Readers for details. + +fileprivate func createWriter() -> [UInt8] { + return [] +} + +fileprivate func writeBytes(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 { + writer.append(contentsOf: byteArr) +} + +// Writes an integer in big-endian order. +// +// Warning: make sure what you are trying to write +// is in the correct type! +fileprivate func writeInt(_ writer: inout [UInt8], _ value: T) { + var value = value.bigEndian + withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) } +} + +fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) { + writeInt(&writer, value.bitPattern) +} + +fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) { + writeInt(&writer, value.bitPattern) +} + +// Protocol for types that transfer other types across the FFI. This is +// analogous to the Rust trait of the same name. +fileprivate protocol FfiConverter { + associatedtype FfiType + associatedtype SwiftType + + static func lift(_ value: FfiType) throws -> SwiftType + static func lower(_ value: SwiftType) -> FfiType + static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType + static func write(_ value: SwiftType, into buf: inout [UInt8]) +} + +// Types conforming to `Primitive` pass themselves directly over the FFI. +fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { } + +extension FfiConverterPrimitive { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ value: FfiType) throws -> SwiftType { + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> FfiType { + return value + } +} + +// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`. +// Used for complex types where it's hard to write a custom lift/lower. +fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {} + +extension FfiConverterRustBuffer { +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lift(_ buf: RustBuffer) throws -> SwiftType { + var reader = createReader(data: Data(rustBuffer: buf)) + let value = try read(from: &reader) + if hasRemaining(reader) { + throw UniffiInternalError.incompleteData + } + buf.deallocate() + return value + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public static func lower(_ value: SwiftType) -> RustBuffer { + var writer = createWriter() + write(value, into: &writer) + return RustBuffer(bytes: writer) + } +} +// An error type for FFI errors. These errors occur at the UniFFI level, not +// the library level. +fileprivate enum UniffiInternalError: LocalizedError { + case bufferOverflow + case incompleteData + case unexpectedOptionalTag + case unexpectedEnumCase + case unexpectedNullPointer + case unexpectedRustCallStatusCode + case unexpectedRustCallError + case unexpectedStaleHandle + case rustPanic(_ message: String) + + public var errorDescription: String? { + switch self { + case .bufferOverflow: return "Reading the requested value would read past the end of the buffer" + case .incompleteData: return "The buffer still has data after lifting its containing value" + case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1" + case .unexpectedEnumCase: return "Raw enum value doesn't match any cases" + case .unexpectedNullPointer: return "Raw pointer value was null" + case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code" + case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified" + case .unexpectedStaleHandle: return "The object in the handle map has been dropped already" + case let .rustPanic(message): return message + } + } +} + +fileprivate extension NSLock { + func withLock(f: () throws -> T) rethrows -> T { + self.lock() + defer { self.unlock() } + return try f() + } +} + +fileprivate let CALL_SUCCESS: Int8 = 0 +fileprivate let CALL_ERROR: Int8 = 1 +fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2 +fileprivate let CALL_CANCELLED: Int8 = 3 + +fileprivate extension RustCallStatus { + init() { + self.init( + code: CALL_SUCCESS, + errorBuf: RustBuffer.init( + capacity: 0, + len: 0, + data: nil + ) + ) + } +} + +private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { + let neverThrow: ((RustBuffer) throws -> Never)? = nil + return try makeRustCall(callback, errorHandler: neverThrow) +} + +private func rustCallWithError( + _ errorHandler: @escaping (RustBuffer) throws -> E, + _ callback: (UnsafeMutablePointer) -> T) throws -> T { + try makeRustCall(callback, errorHandler: errorHandler) +} + +private func makeRustCall( + _ callback: (UnsafeMutablePointer) -> T, + errorHandler: ((RustBuffer) throws -> E)? +) throws -> T { + uniffiEnsurePvmRuntimeInitialized() + var callStatus = RustCallStatus.init() + let returnedVal = callback(&callStatus) + try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler) + return returnedVal +} + +private func uniffiCheckCallStatus( + callStatus: RustCallStatus, + errorHandler: ((RustBuffer) throws -> E)? +) throws { + switch callStatus.code { + case CALL_SUCCESS: + return + + case CALL_ERROR: + if let errorHandler = errorHandler { + throw try errorHandler(callStatus.errorBuf) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.unexpectedRustCallError + } + + case CALL_UNEXPECTED_ERROR: + // When the rust code sees a panic, it tries to construct a RustBuffer + // with the message. But if that code panics, then it just sends back + // an empty buffer. + if callStatus.errorBuf.len > 0 { + throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.rustPanic("Rust panic") + } + + case CALL_CANCELLED: + fatalError("Cancellation not supported yet") + + default: + throw UniffiInternalError.unexpectedRustCallStatusCode + } +} + +private func uniffiTraitInterfaceCall( + callStatus: UnsafeMutablePointer, + makeCall: () throws -> T, + writeReturn: (T) -> () +) { + do { + try writeReturn(makeCall()) + } catch let error { + callStatus.pointee.code = CALL_UNEXPECTED_ERROR + callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error)) + } +} + +private func uniffiTraitInterfaceCallWithError( + callStatus: UnsafeMutablePointer, + makeCall: () throws -> T, + writeReturn: (T) -> (), + lowerError: (E) -> RustBuffer +) { + do { + try writeReturn(makeCall()) + } catch let error as E { + callStatus.pointee.code = CALL_ERROR + callStatus.pointee.errorBuf = lowerError(error) + } catch { + callStatus.pointee.code = CALL_UNEXPECTED_ERROR + callStatus.pointee.errorBuf = FfiConverterString.lower(String(describing: error)) + } +} +// Initial value and increment amount for handles. +// These ensure that SWIFT handles always have the lowest bit set +fileprivate let UNIFFI_HANDLEMAP_INITIAL: UInt64 = 1 +fileprivate let UNIFFI_HANDLEMAP_DELTA: UInt64 = 2 + +fileprivate final class UniffiHandleMap: @unchecked Sendable { + // All mutation happens with this lock held, which is why we implement @unchecked Sendable. + private let lock = NSLock() + private var map: [UInt64: T] = [:] + private var currentHandle: UInt64 = UNIFFI_HANDLEMAP_INITIAL + + func insert(obj: T) -> UInt64 { + lock.withLock { + return doInsert(obj) + } + } + + // Low-level insert function, this assumes `lock` is held. + private func doInsert(_ obj: T) -> UInt64 { + let handle = currentHandle + currentHandle += UNIFFI_HANDLEMAP_DELTA + map[handle] = obj + return handle + } + + func get(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map[handle] else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + func clone(handle: UInt64) throws -> UInt64 { + try lock.withLock { + guard let obj = map[handle] else { + throw UniffiInternalError.unexpectedStaleHandle + } + return doInsert(obj) + } + } + + @discardableResult + func remove(handle: UInt64) throws -> T { + try lock.withLock { + guard let obj = map.removeValue(forKey: handle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return obj + } + } + + var count: Int { + get { + map.count + } + } +} + + +// Public interface members begin here. + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { + typealias FfiType = UInt16 + typealias SwiftType = UInt16 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterInt16: FfiConverterPrimitive { + typealias FfiType = Int16 + typealias SwiftType = Int16 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int16 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: Int16, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt32: FfiConverterPrimitive { + typealias FfiType = UInt32 + typealias SwiftType = UInt32 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt32 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt64: FfiConverterPrimitive { + typealias FfiType = UInt64 + typealias SwiftType = UInt64 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt64 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterBool : FfiConverter { + typealias FfiType = Int8 + typealias SwiftType = Bool + + public static func lift(_ value: Int8) throws -> Bool { + return value != 0 + } + + public static func lower(_ value: Bool) -> Int8 { + return value ? 1 : 0 + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool { + return try lift(readInt(&buf)) + } + + public static func write(_ value: Bool, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterString: FfiConverter { + typealias SwiftType = String + typealias FfiType = RustBuffer + + public static func lift(_ value: RustBuffer) throws -> String { + defer { + value.deallocate() + } + if value.data == nil { + return String() + } + let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) + // Use Swift's native UTF-8 decoder; `String(bytes:encoding:.utf8)` goes + // through Foundation's NSString and silently strips a leading U+FEFF BOM. + // Invalid UTF-8 substitutes U+FFFD instead of trapping (unreachable + // given Rust's `String` invariant). + return String(decoding: bytes, as: UTF8.self) + } + + public static func lower(_ value: String) -> RustBuffer { + return value.utf8CString.withUnsafeBufferPointer { ptr in + // The swift string gives us int8_t, we want uint8_t. + ptr.withMemoryRebound(to: UInt8.self) { ptr in + // The swift string gives us a trailing null byte, we don't want it. + let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1)) + return RustBuffer.from(buf) + } + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String { + let len: Int32 = try readInt(&buf) + // See `lift` above for why we avoid Foundation's NSString-backed decoder here. + return String(decoding: try readBytes(&buf, count: Int(len)), as: UTF8.self) + } + + public static func write(_ value: String, into buf: inout [UInt8]) { + let len = Int32(value.utf8.count) + writeInt(&buf, len) + writeBytes(&buf, value.utf8) + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterData: FfiConverterRustBuffer { + typealias SwiftType = Data + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data { + let len: Int32 = try readInt(&buf) + return Data(try readBytes(&buf, count: Int(len))) + } + + public static func write(_ value: Data, into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + writeBytes(&buf, value) + } +} + + + + +public protocol NativePvmRuntimeProtocol: AnyObject, Sendable { + + func backend() throws -> String + + func gpuReady() throws -> Bool + + func `init`() throws + + func isExited() throws -> Bool + + func lastGasUsed() throws -> UInt64 + + func sendGpuEvent(bytes: Data) throws + + func sendInput(eventType: NativePvmInputEventType, code: UInt8, x: UInt16, y: UInt16) throws + + func setGpuCapabilities(bytes: Data) throws + + func takeAudio() throws -> NativePvmAudioChunk? + + func takeFrame() throws -> NativePvmFrame? + + func takeGpuBatch() throws -> NativePvmGpuBatch? + + func takeLog() throws -> String? + + func takeSave() throws -> Data? + + func takeTri2d() throws -> NativePvmTri2dFrame? + + func update() throws + +} +open class NativePvmRuntime: NativePvmRuntimeProtocol, @unchecked Sendable { + fileprivate let handle: UInt64 + + /// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public struct NoHandle { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + required public init(unsafeFromHandle handle: UInt64) { + self.handle = handle + } + + // This constructor can be used to instantiate a fake object. + // - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public init(noHandle: NoHandle) { + self.handle = 0 + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public func uniffiCloneHandle() -> UInt64 { + return try! rustCall { uniffi_pvm_runtime_fn_clone_nativepvmruntime(self.handle, $0) } + } +public convenience init(program: Data, assets: [NativePvmAsset], presentation: NativePvmPresentationProfile, audioEnabled: Bool, maxGasPerUpdate: UInt64)throws { + let handle = + try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_constructor_nativepvmruntime_new( + FfiConverterData.lower(program), + FfiConverterSequenceTypeNativePvmAsset.lower(assets), + FfiConverterTypeNativePvmPresentationProfile_lower(presentation), + FfiConverterBool.lower(audioEnabled), + FfiConverterUInt64.lower(maxGasPerUpdate),uniffiCallStatus + ) +} + self.init(unsafeFromHandle: handle) +} + + deinit { + if handle == 0 { + // Mock objects have handle=0 don't try to free them + return + } + + try! rustCall { uniffi_pvm_runtime_fn_free_nativepvmruntime(handle, $0) } + } + + + + +open func backend()throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_backend( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func gpuReady()throws -> Bool { + return try FfiConverterBool.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_gpu_ready( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func `init`()throws {try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_init( + self.uniffiCloneHandle(),uniffiCallStatus + ) +} +} + +open func isExited()throws -> Bool { + return try FfiConverterBool.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_is_exited( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func lastGasUsed()throws -> UInt64 { + return try FfiConverterUInt64.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_last_gas_used( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func sendGpuEvent(bytes: Data)throws {try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_send_gpu_event( + self.uniffiCloneHandle(), + FfiConverterData.lower(bytes),uniffiCallStatus + ) +} +} + +open func sendInput(eventType: NativePvmInputEventType, code: UInt8, x: UInt16, y: UInt16)throws {try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_send_input( + self.uniffiCloneHandle(), + FfiConverterTypeNativePvmInputEventType_lower(eventType), + FfiConverterUInt8.lower(code), + FfiConverterUInt16.lower(x), + FfiConverterUInt16.lower(y),uniffiCallStatus + ) +} +} + +open func setGpuCapabilities(bytes: Data)throws {try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_set_gpu_capabilities( + self.uniffiCloneHandle(), + FfiConverterData.lower(bytes),uniffiCallStatus + ) +} +} + +open func takeAudio()throws -> NativePvmAudioChunk? { + return try FfiConverterOptionTypeNativePvmAudioChunk.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_audio( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func takeFrame()throws -> NativePvmFrame? { + return try FfiConverterOptionTypeNativePvmFrame.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_frame( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func takeGpuBatch()throws -> NativePvmGpuBatch? { + return try FfiConverterOptionTypeNativePvmGpuBatch.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_gpu_batch( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func takeLog()throws -> String? { + return try FfiConverterOptionString.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_log( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func takeSave()throws -> Data? { + return try FfiConverterOptionData.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_save( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func takeTri2d()throws -> NativePvmTri2dFrame? { + return try FfiConverterOptionTypeNativePvmTri2dFrame.lift(try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_take_tri2d( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func update()throws {try rustCallWithError(FfiConverterTypeNativePvmError_lift) { + uniffiCallStatus in + uniffi_pvm_runtime_fn_method_nativepvmruntime_update( + self.uniffiCloneHandle(),uniffiCallStatus + ) +} +} + + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmRuntime: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = NativePvmRuntime + + public static func lift(_ handle: UInt64) throws -> NativePvmRuntime { + return NativePvmRuntime(unsafeFromHandle: handle) + } + + public static func lower(_ value: NativePvmRuntime) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmRuntime { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: NativePvmRuntime, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmRuntime_lift(_ handle: UInt64) throws -> NativePvmRuntime { + return try FfiConverterTypeNativePvmRuntime.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmRuntime_lower(_ value: NativePvmRuntime) -> UInt64 { + return FfiConverterTypeNativePvmRuntime.lower(value) +} + + + + +public struct NativePvmAsset: Equatable, Hashable { + public var path: String + public var bytes: Data + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(path: String, bytes: Data) { + self.path = path + self.bytes = bytes + } + + + + +} + +#if compiler(>=6) +extension NativePvmAsset: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmAsset: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmAsset { + return + try NativePvmAsset( + path: FfiConverterString.read(from: &buf), + bytes: FfiConverterData.read(from: &buf) + ) + } + + public static func write(_ value: NativePvmAsset, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterData.write(value.bytes, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmAsset_lift(_ buf: RustBuffer) throws -> NativePvmAsset { + return try FfiConverterTypeNativePvmAsset.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmAsset_lower(_ value: NativePvmAsset) -> RustBuffer { + return FfiConverterTypeNativePvmAsset.lower(value) +} + + +public struct NativePvmAudioChunk: Equatable, Hashable { + public var samples: [Int16] + public var sampleRate: UInt32 + public var channels: UInt32 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(samples: [Int16], sampleRate: UInt32, channels: UInt32) { + self.samples = samples + self.sampleRate = sampleRate + self.channels = channels + } + + + + +} + +#if compiler(>=6) +extension NativePvmAudioChunk: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmAudioChunk: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmAudioChunk { + return + try NativePvmAudioChunk( + samples: FfiConverterSequenceInt16.read(from: &buf), + sampleRate: FfiConverterUInt32.read(from: &buf), + channels: FfiConverterUInt32.read(from: &buf) + ) + } + + public static func write(_ value: NativePvmAudioChunk, into buf: inout [UInt8]) { + FfiConverterSequenceInt16.write(value.samples, into: &buf) + FfiConverterUInt32.write(value.sampleRate, into: &buf) + FfiConverterUInt32.write(value.channels, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmAudioChunk_lift(_ buf: RustBuffer) throws -> NativePvmAudioChunk { + return try FfiConverterTypeNativePvmAudioChunk.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmAudioChunk_lower(_ value: NativePvmAudioChunk) -> RustBuffer { + return FfiConverterTypeNativePvmAudioChunk.lower(value) +} + + +public struct NativePvmFrame: Equatable, Hashable { + public var width: UInt32 + public var height: UInt32 + public var argb: Data + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(width: UInt32, height: UInt32, argb: Data) { + self.width = width + self.height = height + self.argb = argb + } + + + + +} + +#if compiler(>=6) +extension NativePvmFrame: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmFrame: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmFrame { + return + try NativePvmFrame( + width: FfiConverterUInt32.read(from: &buf), + height: FfiConverterUInt32.read(from: &buf), + argb: FfiConverterData.read(from: &buf) + ) + } + + public static func write(_ value: NativePvmFrame, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.width, into: &buf) + FfiConverterUInt32.write(value.height, into: &buf) + FfiConverterData.write(value.argb, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmFrame_lift(_ buf: RustBuffer) throws -> NativePvmFrame { + return try FfiConverterTypeNativePvmFrame.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmFrame_lower(_ value: NativePvmFrame) -> RustBuffer { + return FfiConverterTypeNativePvmFrame.lower(value) +} + + +public struct NativePvmGpuBatch: Equatable, Hashable { + public var bytes: Data + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(bytes: Data) { + self.bytes = bytes + } + + + + +} + +#if compiler(>=6) +extension NativePvmGpuBatch: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmGpuBatch: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmGpuBatch { + return + try NativePvmGpuBatch( + bytes: FfiConverterData.read(from: &buf) + ) + } + + public static func write(_ value: NativePvmGpuBatch, into buf: inout [UInt8]) { + FfiConverterData.write(value.bytes, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmGpuBatch_lift(_ buf: RustBuffer) throws -> NativePvmGpuBatch { + return try FfiConverterTypeNativePvmGpuBatch.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmGpuBatch_lower(_ value: NativePvmGpuBatch) -> RustBuffer { + return FfiConverterTypeNativePvmGpuBatch.lower(value) +} + + +public struct NativePvmTri2dFrame: Equatable, Hashable { + public var width: UInt32 + public var height: UInt32 + public var drawCount: UInt32 + public var vertexCount: UInt32 + public var indexCount: UInt32 + public var bytes: Data + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(width: UInt32, height: UInt32, drawCount: UInt32, vertexCount: UInt32, indexCount: UInt32, bytes: Data) { + self.width = width + self.height = height + self.drawCount = drawCount + self.vertexCount = vertexCount + self.indexCount = indexCount + self.bytes = bytes + } + + + + +} + +#if compiler(>=6) +extension NativePvmTri2dFrame: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmTri2dFrame: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmTri2dFrame { + return + try NativePvmTri2dFrame( + width: FfiConverterUInt32.read(from: &buf), + height: FfiConverterUInt32.read(from: &buf), + drawCount: FfiConverterUInt32.read(from: &buf), + vertexCount: FfiConverterUInt32.read(from: &buf), + indexCount: FfiConverterUInt32.read(from: &buf), + bytes: FfiConverterData.read(from: &buf) + ) + } + + public static func write(_ value: NativePvmTri2dFrame, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.width, into: &buf) + FfiConverterUInt32.write(value.height, into: &buf) + FfiConverterUInt32.write(value.drawCount, into: &buf) + FfiConverterUInt32.write(value.vertexCount, into: &buf) + FfiConverterUInt32.write(value.indexCount, into: &buf) + FfiConverterData.write(value.bytes, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmTri2dFrame_lift(_ buf: RustBuffer) throws -> NativePvmTri2dFrame { + return try FfiConverterTypeNativePvmTri2dFrame.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmTri2dFrame_lower(_ value: NativePvmTri2dFrame) -> RustBuffer { + return FfiConverterTypeNativePvmTri2dFrame.lower(value) +} + + +public +enum NativePvmError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError { + + + + case Runtime(message: String + ) + case DuplicateAsset(path: String + ) + case RuntimePoisoned + + + + + + + public var errorDescription: String? { + String(reflecting: self) + } + +} + +#if compiler(>=6) +extension NativePvmError: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmError: FfiConverterRustBuffer { + typealias SwiftType = NativePvmError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .Runtime( + message: try FfiConverterString.read(from: &buf) + ) + case 2: return .DuplicateAsset( + path: try FfiConverterString.read(from: &buf) + ) + case 3: return .RuntimePoisoned + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: NativePvmError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .Runtime(message): + writeInt(&buf, Int32(1)) + FfiConverterString.write(message, into: &buf) + + + case let .DuplicateAsset(path): + writeInt(&buf, Int32(2)) + FfiConverterString.write(path, into: &buf) + + + case .RuntimePoisoned: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmError_lift(_ buf: RustBuffer) throws -> NativePvmError { + return try FfiConverterTypeNativePvmError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmError_lower(_ value: NativePvmError) -> RustBuffer { + return FfiConverterTypeNativePvmError.lower(value) +} + + + +public enum NativePvmInputEventType: Equatable, Hashable { + + case keyDown + case keyUp + case buttonDown + case buttonUp + case pointerMove + case pointerDelta + case surfaceMetrics + + + + + +} + +#if compiler(>=6) +extension NativePvmInputEventType: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmInputEventType: FfiConverterRustBuffer { + typealias SwiftType = NativePvmInputEventType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmInputEventType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .keyDown + + case 2: return .keyUp + + case 3: return .buttonDown + + case 4: return .buttonUp + + case 5: return .pointerMove + + case 6: return .pointerDelta + + case 7: return .surfaceMetrics + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: NativePvmInputEventType, into buf: inout [UInt8]) { + switch value { + + + case .keyDown: + writeInt(&buf, Int32(1)) + + + case .keyUp: + writeInt(&buf, Int32(2)) + + + case .buttonDown: + writeInt(&buf, Int32(3)) + + + case .buttonUp: + writeInt(&buf, Int32(4)) + + + case .pointerMove: + writeInt(&buf, Int32(5)) + + + case .pointerDelta: + writeInt(&buf, Int32(6)) + + + case .surfaceMetrics: + writeInt(&buf, Int32(7)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmInputEventType_lift(_ buf: RustBuffer) throws -> NativePvmInputEventType { + return try FfiConverterTypeNativePvmInputEventType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmInputEventType_lower(_ value: NativePvmInputEventType) -> RustBuffer { + return FfiConverterTypeNativePvmInputEventType.lower(value) +} + + + + +public enum NativePvmPresentationProfile: Equatable, Hashable { + + case framebuffer + case tri2d + case webGpuRaster + + + + + +} + +#if compiler(>=6) +extension NativePvmPresentationProfile: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeNativePvmPresentationProfile: FfiConverterRustBuffer { + typealias SwiftType = NativePvmPresentationProfile + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativePvmPresentationProfile { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .framebuffer + + case 2: return .tri2d + + case 3: return .webGpuRaster + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: NativePvmPresentationProfile, into buf: inout [UInt8]) { + switch value { + + + case .framebuffer: + writeInt(&buf, Int32(1)) + + + case .tri2d: + writeInt(&buf, Int32(2)) + + + case .webGpuRaster: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmPresentationProfile_lift(_ buf: RustBuffer) throws -> NativePvmPresentationProfile { + return try FfiConverterTypeNativePvmPresentationProfile.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeNativePvmPresentationProfile_lower(_ value: NativePvmPresentationProfile) -> RustBuffer { + return FfiConverterTypeNativePvmPresentationProfile.lower(value) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionString: FfiConverterRustBuffer { + typealias SwiftType = String? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterString.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterString.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionData: FfiConverterRustBuffer { + typealias SwiftType = Data? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterData.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterData.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeNativePvmAudioChunk: FfiConverterRustBuffer { + typealias SwiftType = NativePvmAudioChunk? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeNativePvmAudioChunk.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeNativePvmAudioChunk.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeNativePvmFrame: FfiConverterRustBuffer { + typealias SwiftType = NativePvmFrame? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeNativePvmFrame.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeNativePvmFrame.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeNativePvmGpuBatch: FfiConverterRustBuffer { + typealias SwiftType = NativePvmGpuBatch? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeNativePvmGpuBatch.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeNativePvmGpuBatch.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeNativePvmTri2dFrame: FfiConverterRustBuffer { + typealias SwiftType = NativePvmTri2dFrame? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeNativePvmTri2dFrame.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeNativePvmTri2dFrame.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceInt16: FfiConverterRustBuffer { + typealias SwiftType = [Int16] + + public static func write(_ value: [Int16], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterInt16.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Int16] { + let len: Int32 = try readInt(&buf) + var seq = [Int16]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterInt16.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeNativePvmAsset: FfiConverterRustBuffer { + typealias SwiftType = [NativePvmAsset] + + public static func write(_ value: [NativePvmAsset], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeNativePvmAsset.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [NativePvmAsset] { + let len: Int32 = try readInt(&buf) + var seq = [NativePvmAsset]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeNativePvmAsset.read(from: &buf)) + } + return seq + } +} + +private enum InitializationResult { + case ok + case contractVersionMismatch + case apiChecksumMismatch +} +// Use a global variable to perform the versioning checks. Swift ensures that +// the code inside is only computed once. +private let initializationResult: InitializationResult = { + // Get the bindings contract version from our ComponentInterface + let bindings_contract_version = 30 + // Get the scaffolding contract version by calling the into the dylib + let scaffolding_contract_version = ffi_pvm_runtime_uniffi_contract_version() + if bindings_contract_version != scaffolding_contract_version { + return InitializationResult.contractVersionMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_backend() != 56657) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_gpu_ready() != 61888) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_init() != 58620) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_is_exited() != 14672) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_last_gas_used() != 63988) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_send_gpu_event() != 11805) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_send_input() != 59274) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_set_gpu_capabilities() != 28903) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_audio() != 21780) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_frame() != 8124) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_gpu_batch() != 14686) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_log() != 61835) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_save() != 52485) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_tri2d() != 20193) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_method_nativepvmruntime_update() != 28698) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_pvm_runtime_checksum_constructor_nativepvmruntime_new() != 25625) { + return InitializationResult.apiChecksumMismatch + } + + return InitializationResult.ok +}() + +// Make the ensure init function public so that other modules which have external type references to +// our types can call it. +public func uniffiEnsurePvmRuntimeInitialized() { + switch initializationResult { + case .ok: + break + case .contractVersionMismatch: + fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project") + case .apiChecksumMismatch: + fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +// swiftlint:enable all \ No newline at end of file diff --git a/ios/truapi-host/Sources/pvm_runtimeFFI/include/module.modulemap b/ios/truapi-host/Sources/pvm_runtimeFFI/include/module.modulemap new file mode 100644 index 000000000..6c0b83bd7 --- /dev/null +++ b/ios/truapi-host/Sources/pvm_runtimeFFI/include/module.modulemap @@ -0,0 +1,7 @@ +module pvm_runtimeFFI { + header "pvm_runtimeFFI.h" + export * + use "Darwin" + use "_Builtin_stdbool" + use "_Builtin_stdint" +} \ No newline at end of file diff --git a/ios/truapi-host/Sources/pvm_runtimeFFI/include/pvm_runtimeFFI.h b/ios/truapi-host/Sources/pvm_runtimeFFI/include/pvm_runtimeFFI.h new file mode 100644 index 000000000..2423d9d8e --- /dev/null +++ b/ios/truapi-host/Sources/pvm_runtimeFFI/include/pvm_runtimeFFI.h @@ -0,0 +1,698 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +#pragma once + +#include +#include +#include + +// The following structs are used to implement the lowest level +// of the FFI, and thus useful to multiple uniffied crates. +// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H. +#ifdef UNIFFI_SHARED_H + // We also try to prevent mixing versions of shared uniffi header structs. + // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4 + #ifndef UNIFFI_SHARED_HEADER_V4 + #error Combining helper code from multiple versions of uniffi is not supported + #endif // ndef UNIFFI_SHARED_HEADER_V4 +#else +#define UNIFFI_SHARED_H +#define UNIFFI_SHARED_HEADER_V4 +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ + +typedef struct RustBuffer +{ + uint64_t capacity; + uint64_t len; + uint8_t *_Nullable data; +} RustBuffer; + +typedef struct ForeignBytes +{ + int32_t len; + const uint8_t *_Nullable data; +} ForeignBytes; + +// Error definitions +typedef struct RustCallStatus { + int8_t code; + RustBuffer errorBuf; +} RustCallStatus; + +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️ +#endif // def UNIFFI_SHARED_H +#ifndef UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK +#define UNIFFI_FFIDEF_RUST_FUTURE_CONTINUATION_CALLBACK +typedef void (*UniffiRustFutureContinuationCallback)(uint64_t, int8_t + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK +typedef void (*UniffiForeignFutureDroppedCallback)(uint64_t + ); + +#endif +#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE +#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_FREE +typedef void (*UniffiCallbackInterfaceFree)(uint64_t + ); + +#endif +#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE +#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_CLONE +typedef uint64_t (*UniffiCallbackInterfaceClone)(uint64_t + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_DROPPED_CALLBACK_STRUCT +typedef struct UniffiForeignFutureDroppedCallbackStruct { + uint64_t handle; + UniffiForeignFutureDroppedCallback _Nonnull free; +} UniffiForeignFutureDroppedCallbackStruct; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U8 +typedef struct UniffiForeignFutureResultU8 { + uint8_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultU8; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U8 +typedef void (*UniffiForeignFutureCompleteU8)(uint64_t, UniffiForeignFutureResultU8 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I8 +typedef struct UniffiForeignFutureResultI8 { + int8_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultI8; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I8 +typedef void (*UniffiForeignFutureCompleteI8)(uint64_t, UniffiForeignFutureResultI8 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U16 +typedef struct UniffiForeignFutureResultU16 { + uint16_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultU16; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U16 +typedef void (*UniffiForeignFutureCompleteU16)(uint64_t, UniffiForeignFutureResultU16 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I16 +typedef struct UniffiForeignFutureResultI16 { + int16_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultI16; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I16 +typedef void (*UniffiForeignFutureCompleteI16)(uint64_t, UniffiForeignFutureResultI16 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U32 +typedef struct UniffiForeignFutureResultU32 { + uint32_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultU32; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U32 +typedef void (*UniffiForeignFutureCompleteU32)(uint64_t, UniffiForeignFutureResultU32 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I32 +typedef struct UniffiForeignFutureResultI32 { + int32_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultI32; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I32 +typedef void (*UniffiForeignFutureCompleteI32)(uint64_t, UniffiForeignFutureResultI32 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_U64 +typedef struct UniffiForeignFutureResultU64 { + uint64_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultU64; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_U64 +typedef void (*UniffiForeignFutureCompleteU64)(uint64_t, UniffiForeignFutureResultU64 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_I64 +typedef struct UniffiForeignFutureResultI64 { + int64_t returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultI64; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_I64 +typedef void (*UniffiForeignFutureCompleteI64)(uint64_t, UniffiForeignFutureResultI64 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F32 +typedef struct UniffiForeignFutureResultF32 { + float returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultF32; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F32 +typedef void (*UniffiForeignFutureCompleteF32)(uint64_t, UniffiForeignFutureResultF32 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_F64 +typedef struct UniffiForeignFutureResultF64 { + double returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultF64; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64 +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_F64 +typedef void (*UniffiForeignFutureCompleteF64)(uint64_t, UniffiForeignFutureResultF64 + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_RUST_BUFFER +typedef struct UniffiForeignFutureResultRustBuffer { + RustBuffer returnValue; + RustCallStatus callStatus; +} UniffiForeignFutureResultRustBuffer; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_RUST_BUFFER +typedef void (*UniffiForeignFutureCompleteRustBuffer)(uint64_t, UniffiForeignFutureResultRustBuffer + ); + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_RESULT_VOID +typedef struct UniffiForeignFutureResultVoid { + RustCallStatus callStatus; +} UniffiForeignFutureResultVoid; + +#endif +#ifndef UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID +#define UNIFFI_FFIDEF_FOREIGN_FUTURE_COMPLETE_VOID +typedef void (*UniffiForeignFutureCompleteVoid)(uint64_t, UniffiForeignFutureResultVoid + ); + +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_CLONE_NATIVEPVMRUNTIME +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_CLONE_NATIVEPVMRUNTIME +uint64_t uniffi_pvm_runtime_fn_clone_nativepvmruntime(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_FREE_NATIVEPVMRUNTIME +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_FREE_NATIVEPVMRUNTIME +void uniffi_pvm_runtime_fn_free_nativepvmruntime(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_CONSTRUCTOR_NATIVEPVMRUNTIME_NEW +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_CONSTRUCTOR_NATIVEPVMRUNTIME_NEW +uint64_t uniffi_pvm_runtime_fn_constructor_nativepvmruntime_new(RustBuffer program, RustBuffer assets, RustBuffer presentation, int8_t audio_enabled, uint64_t max_gas_per_update, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_BACKEND +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_BACKEND +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_backend(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_GPU_READY +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_GPU_READY +int8_t uniffi_pvm_runtime_fn_method_nativepvmruntime_gpu_ready(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_INIT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_INIT +void uniffi_pvm_runtime_fn_method_nativepvmruntime_init(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_IS_EXITED +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_IS_EXITED +int8_t uniffi_pvm_runtime_fn_method_nativepvmruntime_is_exited(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_LAST_GAS_USED +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_LAST_GAS_USED +uint64_t uniffi_pvm_runtime_fn_method_nativepvmruntime_last_gas_used(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SEND_GPU_EVENT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SEND_GPU_EVENT +void uniffi_pvm_runtime_fn_method_nativepvmruntime_send_gpu_event(uint64_t ptr, RustBuffer bytes, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SEND_INPUT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SEND_INPUT +void uniffi_pvm_runtime_fn_method_nativepvmruntime_send_input(uint64_t ptr, RustBuffer event_type, uint8_t code, uint16_t x, uint16_t y, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SET_GPU_CAPABILITIES +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_SET_GPU_CAPABILITIES +void uniffi_pvm_runtime_fn_method_nativepvmruntime_set_gpu_capabilities(uint64_t ptr, RustBuffer bytes, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_AUDIO +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_AUDIO +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_audio(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_FRAME +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_FRAME +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_frame(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_GPU_BATCH +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_GPU_BATCH +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_gpu_batch(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_LOG +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_LOG +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_log(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_SAVE +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_SAVE +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_save(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_TRI2D +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_TAKE_TRI2D +RustBuffer uniffi_pvm_runtime_fn_method_nativepvmruntime_take_tri2d(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_UPDATE +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_FN_METHOD_NATIVEPVMRUNTIME_UPDATE +void uniffi_pvm_runtime_fn_method_nativepvmruntime_update(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_ALLOC +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_ALLOC +RustBuffer ffi_pvm_runtime_rustbuffer_alloc(uint64_t size, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_FROM_BYTES +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_FROM_BYTES +RustBuffer ffi_pvm_runtime_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_FREE +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_FREE +void ffi_pvm_runtime_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_RESERVE +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUSTBUFFER_RESERVE +RustBuffer ffi_pvm_runtime_rustbuffer_reserve(RustBuffer buf, uint64_t additional, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U8 +void ffi_pvm_runtime_rust_future_poll_u8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U8 +void ffi_pvm_runtime_rust_future_cancel_u8(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U8 +void ffi_pvm_runtime_rust_future_free_u8(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U8 +uint8_t ffi_pvm_runtime_rust_future_complete_u8(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I8 +void ffi_pvm_runtime_rust_future_poll_i8(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I8 +void ffi_pvm_runtime_rust_future_cancel_i8(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I8 +void ffi_pvm_runtime_rust_future_free_i8(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I8 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I8 +int8_t ffi_pvm_runtime_rust_future_complete_i8(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U16 +void ffi_pvm_runtime_rust_future_poll_u16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U16 +void ffi_pvm_runtime_rust_future_cancel_u16(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U16 +void ffi_pvm_runtime_rust_future_free_u16(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U16 +uint16_t ffi_pvm_runtime_rust_future_complete_u16(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I16 +void ffi_pvm_runtime_rust_future_poll_i16(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I16 +void ffi_pvm_runtime_rust_future_cancel_i16(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I16 +void ffi_pvm_runtime_rust_future_free_i16(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I16 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I16 +int16_t ffi_pvm_runtime_rust_future_complete_i16(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U32 +void ffi_pvm_runtime_rust_future_poll_u32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U32 +void ffi_pvm_runtime_rust_future_cancel_u32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U32 +void ffi_pvm_runtime_rust_future_free_u32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U32 +uint32_t ffi_pvm_runtime_rust_future_complete_u32(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I32 +void ffi_pvm_runtime_rust_future_poll_i32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I32 +void ffi_pvm_runtime_rust_future_cancel_i32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I32 +void ffi_pvm_runtime_rust_future_free_i32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I32 +int32_t ffi_pvm_runtime_rust_future_complete_i32(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_U64 +void ffi_pvm_runtime_rust_future_poll_u64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_U64 +void ffi_pvm_runtime_rust_future_cancel_u64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_U64 +void ffi_pvm_runtime_rust_future_free_u64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_U64 +uint64_t ffi_pvm_runtime_rust_future_complete_u64(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_I64 +void ffi_pvm_runtime_rust_future_poll_i64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_I64 +void ffi_pvm_runtime_rust_future_cancel_i64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_I64 +void ffi_pvm_runtime_rust_future_free_i64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_I64 +int64_t ffi_pvm_runtime_rust_future_complete_i64(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_F32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_F32 +void ffi_pvm_runtime_rust_future_poll_f32(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_F32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_F32 +void ffi_pvm_runtime_rust_future_cancel_f32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_F32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_F32 +void ffi_pvm_runtime_rust_future_free_f32(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_F32 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_F32 +float ffi_pvm_runtime_rust_future_complete_f32(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_F64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_F64 +void ffi_pvm_runtime_rust_future_poll_f64(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_F64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_F64 +void ffi_pvm_runtime_rust_future_cancel_f64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_F64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_F64 +void ffi_pvm_runtime_rust_future_free_f64(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_F64 +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_F64 +double ffi_pvm_runtime_rust_future_complete_f64(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_RUST_BUFFER +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_RUST_BUFFER +void ffi_pvm_runtime_rust_future_poll_rust_buffer(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_RUST_BUFFER +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_RUST_BUFFER +void ffi_pvm_runtime_rust_future_cancel_rust_buffer(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_RUST_BUFFER +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_RUST_BUFFER +void ffi_pvm_runtime_rust_future_free_rust_buffer(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_RUST_BUFFER +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_RUST_BUFFER +RustBuffer ffi_pvm_runtime_rust_future_complete_rust_buffer(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_VOID +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_POLL_VOID +void ffi_pvm_runtime_rust_future_poll_void(uint64_t handle, UniffiRustFutureContinuationCallback _Nonnull callback, uint64_t callback_data +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_VOID +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_CANCEL_VOID +void ffi_pvm_runtime_rust_future_cancel_void(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_VOID +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_FREE_VOID +void ffi_pvm_runtime_rust_future_free_void(uint64_t handle +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_VOID +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_RUST_FUTURE_COMPLETE_VOID +void ffi_pvm_runtime_rust_future_complete_void(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_BACKEND +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_BACKEND +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_backend(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_GPU_READY +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_GPU_READY +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_gpu_ready(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_INIT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_INIT +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_init(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_IS_EXITED +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_IS_EXITED +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_is_exited(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_LAST_GAS_USED +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_LAST_GAS_USED +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_last_gas_used(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SEND_GPU_EVENT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SEND_GPU_EVENT +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_send_gpu_event(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SEND_INPUT +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SEND_INPUT +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_send_input(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SET_GPU_CAPABILITIES +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_SET_GPU_CAPABILITIES +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_set_gpu_capabilities(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_AUDIO +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_AUDIO +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_audio(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_FRAME +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_FRAME +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_frame(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_GPU_BATCH +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_GPU_BATCH +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_gpu_batch(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_LOG +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_LOG +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_log(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_SAVE +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_SAVE +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_save(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_TRI2D +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_TAKE_TRI2D +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_take_tri2d(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_UPDATE +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_METHOD_NATIVEPVMRUNTIME_UPDATE +uint16_t uniffi_pvm_runtime_checksum_method_nativepvmruntime_update(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_CONSTRUCTOR_NATIVEPVMRUNTIME_NEW +#define UNIFFI_FFIDEF_UNIFFI_PVM_RUNTIME_CHECKSUM_CONSTRUCTOR_NATIVEPVMRUNTIME_NEW +uint16_t uniffi_pvm_runtime_checksum_constructor_nativepvmruntime_new(void + +); +#endif +#ifndef UNIFFI_FFIDEF_FFI_PVM_RUNTIME_UNIFFI_CONTRACT_VERSION +#define UNIFFI_FFIDEF_FFI_PVM_RUNTIME_UNIFFI_CONTRACT_VERSION +uint32_t ffi_pvm_runtime_uniffi_contract_version(void + +); +#endif + diff --git a/ios/truapi-host/scripts/sync-bindings.sh b/ios/truapi-host/scripts/sync-bindings.sh index 2305ca71f..76b5ec152 100755 --- a/ios/truapi-host/scripts/sync-bindings.sh +++ b/ios/truapi-host/scripts/sync-bindings.sh @@ -14,7 +14,7 @@ PACKAGE_ROOT="$(cd "$(dirname "$0")/.." && pwd)" TRUAPI_ROOT="$(cd "$PACKAGE_ROOT/../.." && pwd)" UNIFFI_OUT="$TRUAPI_ROOT/target/uniffi-swift-out" -NAMESPACES="truapi truapi_platform truapi_server" +NAMESPACES="truapi truapi_platform truapi_server pvm_runtime" CHECK_ONLY=0 case "${1-}" in diff --git a/js/packages/pvm-browser-runtime/LICENSE-MPL-2.0 b/js/packages/pvm-browser-runtime/LICENSE-MPL-2.0 new file mode 100644 index 000000000..fa0086a95 --- /dev/null +++ b/js/packages/pvm-browser-runtime/LICENSE-MPL-2.0 @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/js/packages/pvm-browser-runtime/SOURCE b/js/packages/pvm-browser-runtime/SOURCE new file mode 100644 index 000000000..0364263c9 --- /dev/null +++ b/js/packages/pvm-browser-runtime/SOURCE @@ -0,0 +1,10 @@ +PolkaVM App v2 browser runtime +Source repository: https://github.com/paritytech/host-rust-core +Source branch: feat/pvm-app-runtime +Rust crate: rust/crates/pvm-runtime +GPU wire crate: rust/crates/pvm-gpu-wire +PolkaVM native revision: 8cc3c5601aa614b0f6dfac2ae3ee271942b3c997 +PolkaVM wasm revision: 3df1d0309c4c81a1aad0a755d83570d203bba1d9 + +The package build compiles pvm-runtime for wasm32-unknown-unknown and bundles +its bounded PVM worker plus the Tri2D/WebGPU Raster browser transport. diff --git a/js/packages/pvm-browser-runtime/dist/SHA256SUMS b/js/packages/pvm-browser-runtime/dist/SHA256SUMS new file mode 100644 index 000000000..662136e85 --- /dev/null +++ b/js/packages/pvm-browser-runtime/dist/SHA256SUMS @@ -0,0 +1,3 @@ +3717814b7a32642fd5d889a6196c8aeac13cda70a867f369cb3f292da17b2bab pvm-browser-runtime.wasm +92735ce2f8223214d46cd5fce296ec089602d74681d6b43c6fcbc71755900636 pvm-worker.js +33418e2c81c117539569eb4cf91af4d058cdfae7dd4556b13f3687f6d6b3bae4 pvm-gpu-worker.js diff --git a/js/packages/pvm-browser-runtime/dist/pvm-browser-runtime.wasm b/js/packages/pvm-browser-runtime/dist/pvm-browser-runtime.wasm new file mode 100755 index 0000000000000000000000000000000000000000..e2a1cce4133037490d4d92ee2244d798d1523b9c GIT binary patch literal 888063 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®$=vM%g?%(R=qQevM%ht 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 Cv3wZ- literal 0 HcmV?d00001 diff --git a/js/packages/pvm-browser-runtime/dist/pvm-gpu-worker.js b/js/packages/pvm-browser-runtime/dist/pvm-gpu-worker.js new file mode 100644 index 000000000..375807456 --- /dev/null +++ b/js/packages/pvm-browser-runtime/dist/pvm-gpu-worker.js @@ -0,0 +1,1884 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* global GPUBufferUsage, GPUMapMode, GPUTextureUsage */ + +"use strict"; + +const WIRE_VERSION = 1; +const BATCH_HEADER_BYTES = 24; +const COMMAND_HEADER_BYTES = 8; +const EVENT_HEADER_BYTES = 24; +const MAX_BATCH_BYTES = 4 * 1024 * 1024; +const MAX_COMMANDS = 16_384; +const MAX_DIAGNOSTIC_BYTES = 8 * 1024; +const HANDLE_SLOT_MASK = (1 << 20) - 1; +const HANDLE_LIVE_BIT = 1 << 12; +const MAX_COMPILATIONS_PER_BATCH = 8; +const MAX_PENDING_BATCHES = 4; +const BATCH_ERROR_STALE_SURFACE = 4; +const MAX_RENDER_PASSES_PER_BATCH = 16; +const MAX_DRAWS_PER_BATCH = 8_192; +const MAX_TOTAL_BUFFER_BYTES = 64 * 1024 * 1024; +const MAX_TOTAL_TEXTURE_BYTES = 256 * 1024 * 1024; +const resourceLimits = new Map([ + ["buffer", 4_096], + ["texture", 512], + ["textureView", 1_024], + ["sampler", 128], + ["shader", 128], + ["bindGroupLayout", 128], + ["pipelineLayout", 64], + ["bindGroup", 512], + ["pipeline", 256], +]); +const bindGroupResourceKinds = new Map([ + [1, "buffer"], + [2, "sampler"], + [3, "textureView"], +]); +const textEncoder = new TextEncoder(); +const textDecoder = new TextDecoder("utf-8", { fatal: true }); + +const formats = new Map([ + [1, "rgba8unorm"], + [2, "rgba8unorm-srgb"], + [3, "bgra8unorm"], + [4, "bgra8unorm-srgb"], + [5, "depth24plus"], + [6, "depth32float"], +]); +const formatIds = new Map([...formats].map(([id, format]) => [format, id])); +const vertexFormats = new Map([ + [1, "float32"], + [2, "float32x2"], + [3, "float32x3"], + [4, "float32x4"], + [5, "uint32"], + [6, "uint32x2"], + [7, "uint32x4"], + [8, "unorm8x2"], + [9, "unorm8x4"], + [10, "snorm8x2"], + [11, "snorm8x4"], +]); +const indexFormats = new Map([ + [1, "uint16"], + [2, "uint32"], +]); +const addressModes = new Map([ + [1, "clamp-to-edge"], + [2, "repeat"], + [3, "mirror-repeat"], +]); +const filterModes = new Map([ + [1, "nearest"], + [2, "linear"], +]); +const compareFunctions = new Map([ + [1, "never"], + [2, "less"], + [3, "equal"], + [4, "less-equal"], + [5, "greater"], + [6, "not-equal"], + [7, "greater-equal"], + [8, "always"], +]); +const blendOperations = new Map([ + [1, "add"], + [2, "subtract"], + [3, "reverse-subtract"], + [4, "min"], + [5, "max"], +]); +const blendFactors = new Map([ + [1, "zero"], + [2, "one"], + [3, "src"], + [4, "one-minus-src"], + [5, "src-alpha"], + [6, "one-minus-src-alpha"], + [7, "dst"], + [8, "one-minus-dst"], + [9, "dst-alpha"], + [10, "one-minus-dst-alpha"], + [11, "src-alpha-saturated"], + [12, "constant"], + [13, "one-minus-constant"], +]); +const topologies = new Map([ + [1, "point-list"], + [2, "line-list"], + [3, "line-strip"], + [4, "triangle-list"], + [5, "triangle-strip"], +]); +const frontFaces = new Map([ + [1, "ccw"], + [2, "cw"], +]); +const cullModes = new Map([ + [0, "none"], + [1, "front"], + [2, "back"], +]); +const samplerBindingTypes = new Map([ + [1, "filtering"], + [2, "non-filtering"], + [3, "comparison"], +]); +const textureSampleTypes = new Map([ + [1, "float"], + [2, "unfilterable-float"], + [3, "depth"], + [4, "sint"], + [5, "uint"], +]); +const vertexStepModes = new Map([ + [1, "vertex"], + [2, "instance"], +]); +const textureAspects = new Map([ + [1, "all"], + [2, "depth-only"], +]); + +class ProtocolError extends Error { + constructor(message, commandIndex = 0xffffffff, errorCode = 1) { + super(message); + this.commandIndex = commandIndex; + this.errorCode = errorCode; + } +} + +class Reader { + constructor(bytes) { + if (!(bytes instanceof Uint8Array)) { + throw new ProtocolError("GPU payload is not bytes"); + } + this.bytes = bytes; + this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + this.offset = 0; + } + + take(length) { + const end = this.offset + length; + if (!Number.isSafeInteger(end) || end > this.bytes.byteLength) { + throw new ProtocolError("GPU payload is truncated"); + } + const value = this.bytes.subarray(this.offset, end); + this.offset = end; + return value; + } + + u8() { + return this.take(1)[0]; + } + + u16() { + const offset = this.offset; + this.take(2); + return this.view.getUint16(offset, true); + } + + u32() { + const offset = this.offset; + this.take(4); + return this.view.getUint32(offset, true); + } + + i32() { + const offset = this.offset; + this.take(4); + return this.view.getInt32(offset, true); + } + + u64() { + const offset = this.offset; + this.take(8); + const value = this.view.getBigUint64(offset, true); + if (value > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new ProtocolError("GPU integer exceeds the safe range"); + } + return Number(value); + } + + f32() { + const offset = this.offset; + this.take(4); + const value = this.view.getFloat32(offset, true); + if (!Number.isFinite(value)) { + throw new ProtocolError("GPU descriptor contains non-finite float"); + } + return value; + } + + text(length) { + return textDecoder.decode(this.take(length)); + } + + zero(length) { + if (this.take(length).some(byte => byte !== 0)) { + throw new ProtocolError("GPU descriptor has nonzero reserved bytes"); + } + } + + finish() { + if (this.offset !== this.bytes.byteLength) { + throw new ProtocolError("GPU descriptor has trailing bytes"); + } + } +} + +function mapped(table, value, label) { + const result = table.get(value); + if (result === undefined) { + throw new ProtocolError(`unsupported GPU ${label} ${value}`); + } + return result; +} + +function validHandle(id) { + return id !== 0 && (id & HANDLE_SLOT_MASK) !== 0 && id >>> 20 !== 0; +} + +function resource(catalog, id, kind, commandIndex) { + const entry = catalog.get(id); + if (!entry || (kind && entry.kind !== kind) || entry.failed) { + throw new ProtocolError( + `invalid ${kind || "resource"} handle ${id}`, + commandIndex + ); + } + return entry; +} + +function createResource(catalog, slots, id, kind, descriptor, commandIndex) { + if (!validHandle(id) || catalog.has(id)) { + throw new ProtocolError(`invalid new ${kind} handle ${id}`, commandIndex); + } + const slot = id & HANDLE_SLOT_MASK; + const generation = id >>> 20; + const prior = slots.get(slot); + if ( + (prior === undefined && generation !== 1) || + (prior !== undefined && + ((prior & HANDLE_LIVE_BIT) !== 0 || + generation !== (prior & ~HANDLE_LIVE_BIT) + 1)) + ) { + throw new ProtocolError(`stale new ${kind} handle ${id}`, commandIndex); + } + const entry = { kind, descriptor, value: null, failed: false }; + catalog.set(id, entry); + slots.set(slot, generation | HANDLE_LIVE_BIT); + return entry; +} + +function textureByteLength(descriptor) { + let width = descriptor.width; + let height = descriptor.height; + let total = 0; + for (let level = 0; level < descriptor.mipLevelCount; level++) { + const bytes = width * height * 4; + if ( + !Number.isSafeInteger(bytes) || + total > MAX_TOTAL_TEXTURE_BYTES - bytes + ) { + return MAX_TOTAL_TEXTURE_BYTES + 1; + } + total += bytes; + width = Math.max(1, Math.floor(width / 2)); + height = Math.max(1, Math.floor(height / 2)); + } + return total; +} + +function adjustResourceStats(stats, entry, delta, commandIndex = 0xffffffff) { + const count = (stats.counts.get(entry.kind) || 0) + delta; + const limit = resourceLimits.get(entry.kind); + if (count < 0 || limit === undefined || count > limit) { + throw new ProtocolError( + `GPU ${entry.kind} resource quota exceeded`, + commandIndex + ); + } + stats.counts.set(entry.kind, count); + if (entry.kind === "buffer") { + stats.bufferBytes += delta * entry.descriptor.size; + if (stats.bufferBytes < 0 || stats.bufferBytes > MAX_TOTAL_BUFFER_BYTES) { + throw new ProtocolError("GPU buffer memory quota exceeded", commandIndex); + } + } else if (entry.kind === "texture") { + stats.textureBytes += delta * textureByteLength(entry.descriptor); + if ( + stats.textureBytes < 0 || + stats.textureBytes > MAX_TOTAL_TEXTURE_BYTES + ) { + throw new ProtocolError( + "GPU texture memory quota exceeded", + commandIndex + ); + } + } +} + +function resourceStats(catalog) { + const stats = { counts: new Map(), bufferBytes: 0, textureBytes: 0 }; + for (const entry of catalog.values()) { + adjustResourceStats(stats, entry, 1); + } + return stats; +} + +function createBoundedResource( + catalog, + slots, + stats, + creations, + id, + kind, + descriptor, + commandIndex +) { + const entry = createResource( + catalog, + slots, + id, + kind, + descriptor, + commandIndex + ); + const creationCount = (creations.counts.get(kind) || 0) + 1; + if (creationCount > resourceLimits.get(kind)) { + throw new ProtocolError( + `too many GPU ${kind} creations in one batch`, + commandIndex + ); + } + creations.counts.set(kind, creationCount); + if (kind === "buffer") { + creations.bufferBytes += descriptor.size; + if (creations.bufferBytes > MAX_TOTAL_BUFFER_BYTES) { + throw new ProtocolError( + "GPU buffer allocation budget exceeded", + commandIndex + ); + } + } else if (kind === "texture") { + creations.textureBytes += textureByteLength(descriptor); + if (creations.textureBytes > MAX_TOTAL_TEXTURE_BYTES) { + throw new ProtocolError( + "GPU texture allocation budget exceeded", + commandIndex + ); + } + } else if (kind === "shader" || kind === "pipeline") { + creations.compilations++; + if (creations.compilations > MAX_COMPILATIONS_PER_BATCH) { + throw new ProtocolError( + "too many GPU compilations in one batch", + commandIndex + ); + } + } + adjustResourceStats(stats, entry, 1, commandIndex); + return entry; +} + +function parseCommands(bytes) { + if ( + bytes.byteLength < BATCH_HEADER_BYTES || + bytes.byteLength > MAX_BATCH_BYTES + ) { + throw new ProtocolError("GPU batch length is invalid"); + } + const reader = new Reader(bytes); + if ( + textDecoder.decode(reader.take(4)) !== "EPG1" || + reader.u16() !== WIRE_VERSION + ) { + throw new ProtocolError("GPU batch header is invalid"); + } + reader.zero(2); + if (reader.u32() !== bytes.byteLength) { + throw new ProtocolError("GPU batch length does not match its header"); + } + const commandCount = reader.u32(); + const sequence = reader.u64(); + if (!sequence || commandCount > MAX_COMMANDS) { + throw new ProtocolError("GPU batch sequence or command count is invalid"); + } + const commands = []; + for (let index = 0; index < commandCount; index++) { + const opcode = reader.u16(); + reader.zero(2); + const commandBytes = reader.u32(); + if (commandBytes < COMMAND_HEADER_BYTES || commandBytes % 4) { + throw new ProtocolError("GPU command length is invalid", index); + } + const payload = reader.take(commandBytes - COMMAND_HEADER_BYTES); + commands.push({ opcode, payload, index }); + } + reader.finish(); + return { sequence, commands }; +} + +// Parsing stays centralized so every opcode's exact wire shape is auditable. +// eslint-disable-next-line complexity +function parseCommand(command) { + const reader = new Reader(command.payload); + const result = { opcode: command.opcode, index: command.index }; + switch (command.opcode) { + case 1: + Object.assign(result, { + id: reader.u32(), + usage: reader.u32(), + size: reader.u64(), + }); + break; + case 2: { + result.id = reader.u32(); + reader.zero(4); + result.offset = reader.u64(); + const length = reader.u32(); + reader.zero(4); + result.data = reader.take(length).slice(); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 3: + Object.assign(result, { + id: reader.u32(), + width: reader.u32(), + height: reader.u32(), + mipLevelCount: reader.u16(), + sampleCount: reader.u16(), + format: mapped(formats, reader.u16(), "texture format"), + }); + if (reader.u8() !== 1) { + throw new ProtocolError("unsupported texture dimension", command.index); + } + reader.zero(1); + result.usage = reader.u32(); + break; + case 4: { + Object.assign(result, { + id: reader.u32(), + mipLevel: reader.u32(), + origin: { x: reader.u32(), y: reader.u32(), z: reader.u32() }, + size: { + width: reader.u32(), + height: reader.u32(), + depthOrArrayLayers: reader.u32(), + }, + bytesPerRow: reader.u32(), + rowsPerImage: reader.u32(), + }); + const length = reader.u32(); + result.data = reader.take(length).slice(); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 5: + Object.assign(result, { + id: reader.u32(), + addressModeU: mapped(addressModes, reader.u8(), "address mode"), + addressModeV: mapped(addressModes, reader.u8(), "address mode"), + addressModeW: mapped(addressModes, reader.u8(), "address mode"), + magFilter: mapped(filterModes, reader.u8(), "filter mode"), + minFilter: mapped(filterModes, reader.u8(), "filter mode"), + mipmapFilter: mapped(filterModes, reader.u8(), "filter mode"), + }); + { + const compare = reader.u8(); + result.compare = compare + ? mapped(compareFunctions, compare, "compare function") + : undefined; + } + result.maxAnisotropy = reader.u8(); + result.lodMinClamp = reader.f32(); + result.lodMaxClamp = reader.f32(); + reader.zero(4); + break; + case 6: { + result.id = reader.u32(); + const length = reader.u32(); + result.source = reader.text(length); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 7: { + result.id = reader.u32(); + const count = reader.u32(); + result.entries = []; + for (let i = 0; i < count; i++) { + const binding = reader.u32(); + const visibility = reader.u32(); + const kind = reader.u16(); + const flags = reader.u16(); + reader.zero(4); + const minBindingSize = reader.u64(); + const parameter0 = reader.u32(); + const parameter1 = reader.u32(); + let entry; + if (kind === 1) { + if (parameter0 || parameter1 || flags & ~1) { + throw new ProtocolError( + "invalid buffer binding layout", + command.index + ); + } + entry = { + binding, + visibility, + buffer: { + type: "uniform", + hasDynamicOffset: Boolean(flags & 1), + minBindingSize, + }, + }; + } else if (kind === 2) { + if (flags || minBindingSize || parameter1) { + throw new ProtocolError( + "invalid sampler binding layout", + command.index + ); + } + entry = { + binding, + visibility, + sampler: { + type: mapped( + samplerBindingTypes, + parameter0, + "sampler binding type" + ), + }, + }; + } else if (kind === 3) { + if (flags || minBindingSize) { + throw new ProtocolError( + "invalid texture binding layout", + command.index + ); + } + entry = { + binding, + visibility, + texture: { + sampleType: mapped( + textureSampleTypes, + parameter0, + "texture sample type" + ), + viewDimension: + parameter1 === 1 + ? "2d" + : (() => { + throw new ProtocolError( + "unsupported texture view dimension", + command.index + ); + })(), + multisampled: false, + }, + }; + } else { + throw new ProtocolError( + "unsupported bind group layout entry", + command.index + ); + } + result.entries.push(entry); + } + break; + } + case 8: { + result.id = reader.u32(); + const count = reader.u32(); + result.layouts = Array.from({ length: count }, () => reader.u32()); + break; + } + case 9: { + result.id = reader.u32(); + result.layout = reader.u32(); + const count = reader.u32(); + result.entries = []; + for (let i = 0; i < count; i++) { + const binding = reader.u32(); + const resourceId = reader.u32(); + const kind = reader.u16(); + if (kind !== 1 && kind !== 2 && kind !== 3) { + throw new ProtocolError( + "unsupported bind group entry kind", + command.index + ); + } + reader.zero(2); + reader.zero(4); + const offset = reader.u64(); + const size = reader.u64(); + result.entries.push({ binding, resourceId, kind, offset, size }); + } + break; + } + case 10: { + Object.assign(result, { + id: reader.u32(), + layout: reader.u32(), + shader: reader.u32(), + }); + const layoutCount = reader.u16(); + const attributeCount = reader.u16(); + const targetCount = reader.u16(); + result.flags = reader.u16(); + result.depthFormatId = reader.u16(); + result.sampleCount = reader.u16(); + result.topology = mapped(topologies, reader.u8(), "primitive topology"); + result.frontFace = mapped(frontFaces, reader.u8(), "front face"); + result.cullMode = mapped(cullModes, reader.u8(), "cull mode"); + const stripIndex = reader.u8(); + result.stripIndexFormat = stripIndex + ? mapped(indexFormats, stripIndex, "strip index format") + : undefined; + const depthCompare = reader.u8(); + result.depthCompare = depthCompare + ? mapped(compareFunctions, depthCompare, "depth compare") + : undefined; + reader.zero(11); + result.layouts = []; + for (let i = 0; i < layoutCount; i++) { + result.layouts.push({ + arrayStride: reader.u64(), + stepMode: mapped(vertexStepModes, reader.u8(), "vertex step mode"), + firstAttribute: (reader.zero(3), reader.u16()), + attributeCount: reader.u16(), + }); + } + result.attributes = []; + for (let i = 0; i < attributeCount; i++) { + result.attributes.push({ + format: mapped(vertexFormats, reader.u16(), "vertex format"), + shaderLocation: reader.u16(), + offset: reader.u64(), + }); + reader.zero(4); + } + result.targets = []; + for (let i = 0; i < targetCount; i++) { + const format = mapped(formats, reader.u16(), "color target format"); + const writeMask = reader.u16(); + const color = { + operation: mapped(blendOperations, reader.u8(), "blend operation"), + srcFactor: mapped(blendFactors, reader.u8(), "blend factor"), + dstFactor: mapped(blendFactors, reader.u8(), "blend factor"), + }; + const alpha = { + operation: mapped(blendOperations, reader.u8(), "blend operation"), + srcFactor: mapped(blendFactors, reader.u8(), "blend factor"), + dstFactor: mapped(blendFactors, reader.u8(), "blend factor"), + }; + reader.zero(6); + result.targets.push({ format, writeMask, blend: { color, alpha } }); + } + break; + } + case 11: + case 13: + result.id = reader.u32(); + break; + case 12: + Object.assign(result, { + colorView: reader.u32(), + depthView: reader.u32(), + surfaceGeneration: reader.u32(), + flags: reader.u32(), + clearColor: { + r: reader.f32(), + g: reader.f32(), + b: reader.f32(), + a: reader.f32(), + }, + clearDepth: reader.f32(), + }); + break; + case 14: + Object.assign(result, { + slot: reader.u32(), + buffer: reader.u32(), + offset: reader.u64(), + size: reader.u64(), + }); + break; + case 15: + Object.assign(result, { + buffer: reader.u32(), + format: mapped(indexFormats, reader.u32(), "index format"), + offset: reader.u64(), + size: reader.u64(), + }); + break; + case 16: { + result.bindIndex = reader.u32(); + result.bindGroup = reader.u32(); + const count = reader.u32(); + result.dynamicOffsets = Array.from({ length: count }, () => reader.u32()); + break; + } + case 17: + result.values = Array.from({ length: 6 }, () => reader.f32()); + break; + case 18: + case 19: + result.values = Array.from({ length: 4 }, () => reader.u32()); + break; + case 20: + result.values = [ + reader.u32(), + reader.u32(), + reader.u32(), + reader.i32(), + reader.u32(), + ]; + break; + case 21: + break; + case 22: + Object.assign(result, { + source: reader.u32(), + destination: reader.u32(), + sourceOffset: reader.u64(), + destinationOffset: reader.u64(), + size: reader.u64(), + }); + break; + case 23: + Object.assign(result, { + id: reader.u32(), + texture: reader.u32(), + format: mapped(formats, reader.u16(), "texture view format"), + }); + if (reader.u8() !== 1) { + throw new ProtocolError( + "unsupported texture view dimension", + command.index + ); + } + result.aspect = mapped(textureAspects, reader.u8(), "texture aspect"); + result.baseMipLevel = reader.u16(); + result.mipLevelCount = reader.u16(); + result.baseArrayLayer = reader.u16(); + result.arrayLayerCount = reader.u16(); + break; + default: + throw new ProtocolError( + `unsupported GPU opcode ${command.opcode}`, + command.index + ); + } + reader.finish(); + return result; +} + +class GpuEngine { + constructor( + canvas, + device, + context, + format, + limits, + dimensions, + testReadback, + testDeviceLoss + ) { + this.canvas = canvas; + this.device = device; + this.context = context; + this.format = format; + this.formatId = formatIds.get(format); + this.limits = limits; + this.resources = new Map(); + this.handleSlots = new Map(); + this.surfaceGeneration = 1; + this.deviceGeneration = 1; + this.lastSequence = 0; + this.stopped = false; + this.queue = Promise.resolve(); + this.pendingBatches = 0; + this.testReadbacksRemaining = testReadback ? 8 : 0; + this.testDeviceLossPending = testDeviceLoss; + this.pendingResize = null; + this.resizeScheduled = false; + this.resize(dimensions, false); + device.addEventListener("uncapturederror", event => { + this.emitTextEvent( + 4, + 0, + 1, + event.error?.message || "uncaptured WebGPU error" + ); + }); + void device.lost.then(info => { + if (!this.stopped) { + this.stopped = true; + this.emitTextEvent(7, 0, 1, info.message || "WebGPU device lost"); + } + }); + } + + static async create( + canvas, + requirements, + dimensions, + testReadback, + testDeviceLoss + ) { + if (!globalThis.navigator?.gpu) { + throw new Error("WebGPU is unavailable"); + } + const adapter = await navigator.gpu.requestAdapter(); + if (!adapter) { + throw new Error("WebGPU adapter is unavailable"); + } + const ceilings = { + maxTextureDimension2D: 4096, + maxBufferSize: 16 * 1024 * 1024, + maxBindingsPerBindGroup: 16, + maxBindGroups: 4, + maxVertexBuffers: 8, + maxVertexAttributes: 16, + maxColorAttachments: 4, + }; + const requested = {}; + for (const [name, ceiling] of Object.entries(ceilings)) { + const available = Number(adapter.limits[name]); + if (!Number.isFinite(available) || available <= 0) { + throw new Error(`WebGPU adapter omitted ${name}`); + } + requested[name] = Math.min(ceiling, available); + } + for (const [name, minimum] of Object.entries( + requirements.requiredLimits || {} + )) { + if ( + !(name in requested) || + !Number.isSafeInteger(minimum) || + minimum <= 0 || + requested[name] < minimum + ) { + throw new Error(`WebGPU does not satisfy required limit ${name}`); + } + } + if ( + Array.isArray(requirements.requiredFeatures) && + requirements.requiredFeatures.length + ) { + throw new Error( + "optional WebGPU features are unavailable in profile version 1" + ); + } + const device = await adapter.requestDevice({ + requiredFeatures: [], + requiredLimits: requested, + }); + const context = canvas.getContext("webgpu"); + if (!context) { + throw new Error("WebGPU canvas context is unavailable"); + } + const format = navigator.gpu.getPreferredCanvasFormat(); + if (!formatIds.has(format)) { + throw new Error(`unsupported WebGPU surface format ${format}`); + } + const limits = [ + requested.maxTextureDimension2D, + requested.maxBufferSize, + requested.maxBindingsPerBindGroup, + requested.maxBindGroups, + requested.maxVertexBuffers, + requested.maxVertexAttributes, + requested.maxColorAttachments, + 256 * 1024 * 1024, + 64 * 1024 * 1024, + 8192, + MAX_BATCH_BYTES, + 16 * 1024 * 1024, + ]; + return new GpuEngine( + canvas, + device, + context, + format, + limits, + dimensions, + testReadback, + testDeviceLoss + ); + } + + resize(dimensions, notify = true) { + const physicalWidth = Math.max( + 1, + Math.min(this.limits[0], dimensions.physicalWidth >>> 0) + ); + const physicalHeight = Math.max( + 1, + Math.min(this.limits[0], dimensions.physicalHeight >>> 0) + ); + const logicalWidth = Math.max(1, dimensions.logicalWidth >>> 0); + const logicalHeight = Math.max(1, dimensions.logicalHeight >>> 0); + const scale = Number(dimensions.scale); + if (!Number.isFinite(scale) || scale <= 0) { + throw new Error("invalid WebGPU surface scale"); + } + const changed = + this.physicalWidth !== physicalWidth || + this.physicalHeight !== physicalHeight || + this.logicalWidth !== logicalWidth || + this.logicalHeight !== logicalHeight || + this.scale !== scale; + this.physicalWidth = physicalWidth; + this.physicalHeight = physicalHeight; + this.logicalWidth = logicalWidth; + this.logicalHeight = logicalHeight; + this.scale = scale; + this.canvas.width = physicalWidth; + this.canvas.height = physicalHeight; + this.context.configure({ + device: this.device, + format: this.format, + alphaMode: "opaque", + usage: + GPUTextureUsage.RENDER_ATTACHMENT | + (this.testReadbacksRemaining > 0 ? GPUTextureUsage.COPY_SRC : 0), + }); + if (notify && changed) { + this.surfaceGeneration++; + postBytes("capabilities", this.capabilities()); + const payload = new Uint8Array(28); + const view = new DataView(payload.buffer); + for (const [offset, value] of [ + [0, this.surfaceGeneration], + [4, physicalWidth], + [8, physicalHeight], + [12, logicalWidth], + [16, logicalHeight], + ]) { + view.setUint32(offset, value, true); + } + view.setFloat32(20, scale, true); + view.setUint16(24, this.formatId, true); + postBytes("event", makeEvent(6, 0, payload)); + } + } + + scheduleResize(dimensions) { + this.pendingResize = dimensions; + if (this.resizeScheduled) { + return; + } + this.resizeScheduled = true; + this.queue = this.queue + .then(() => { + const latest = this.pendingResize; + this.pendingResize = null; + this.resizeScheduled = false; + if (!this.stopped && latest) { + this.resize(latest); + } + }) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }); + } + + capabilities() { + const bytes = new Uint8Array(56 + this.limits.length * 16); + const view = new DataView(bytes.buffer); + bytes.set(textEncoder.encode("EGC1"), 0); + view.setUint16(4, WIRE_VERSION, true); + view.setUint32(8, bytes.byteLength, true); + view.setUint16(12, this.formatId, true); + view.setUint32(16, this.physicalWidth, true); + view.setUint32(20, this.physicalHeight, true); + view.setUint32(24, this.logicalWidth, true); + view.setUint32(28, this.logicalHeight, true); + view.setFloat32(32, this.scale, true); + view.setUint32(36, this.surfaceGeneration, true); + view.setUint32(40, this.deviceGeneration, true); + view.setUint32(44, this.limits.length, true); + this.limits.forEach((value, index) => { + const offset = 56 + index * 16; + view.setUint16(offset, index + 1, true); + view.setBigUint64(offset + 4, BigInt(value), true); + }); + return bytes; + } + + submit(bytes) { + if (this.pendingBatches >= MAX_PENDING_BATCHES) { + this.emitBatchRejected(0xffffffff, 3, 0, "GPU submission queue is full"); + return; + } + this.pendingBatches++; + this.queue = this.queue + .then(() => this.execute(bytes)) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }) + .finally(() => { + this.pendingBatches--; + }); + } + + // Validation is one atomic state transition across the complete opcode set. + // eslint-disable-next-line complexity + validate(batch) { + if (batch.sequence <= this.lastSequence) { + throw new ProtocolError("GPU sequence is not increasing"); + } + const shadow = new Map(this.resources); + const slots = new Map(this.handleSlots); + let pass = false; + const stats = resourceStats(shadow); + const creations = { + counts: new Map(), + bufferBytes: 0, + textureBytes: 0, + compilations: 0, + }; + let renderPasses = 0; + let draws = 0; + const commands = batch.commands.map(parseCommand); + for (const command of commands) { + const index = command.index; + switch (command.opcode) { + case 1: + if (!command.size || command.size > this.limits[1]) { + throw new ProtocolError("invalid buffer size", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "buffer", + command, + index + ); + break; + case 2: + resource(shadow, command.id, "buffer", index); + break; + case 3: + if ( + !command.width || + !command.height || + command.width > this.limits[0] || + command.height > this.limits[0] || + !command.mipLevelCount || + command.mipLevelCount > 13 || + command.sampleCount !== 1 + ) { + throw new ProtocolError("invalid texture descriptor", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "texture", + command, + index + ); + break; + case 4: + resource(shadow, command.id, "texture", index); + break; + case 5: + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "sampler", + command, + index + ); + break; + case 6: + if (!command.source.length || command.source.length > 1024 * 1024) { + throw new ProtocolError("invalid WGSL source", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "shader", + command, + index + ); + break; + case 7: + if (command.entries.length > this.limits[2]) { + throw new ProtocolError("too many bind group entries", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "bindGroupLayout", + command, + index + ); + break; + case 8: + if (command.layouts.length > this.limits[3]) { + throw new ProtocolError("too many pipeline bind groups", index); + } + command.layouts.forEach(id => + resource(shadow, id, "bindGroupLayout", index) + ); + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "pipelineLayout", + command, + index + ); + break; + case 9: + if (command.entries.length > this.limits[2]) { + throw new ProtocolError("too many bind group entries", index); + } + resource(shadow, command.layout, "bindGroupLayout", index); + for (const entry of command.entries) { + resource( + shadow, + entry.resourceId, + bindGroupResourceKinds.get(entry.kind) || "", + index + ); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "bindGroup", + command, + index + ); + break; + case 10: + resource(shadow, command.layout, "pipelineLayout", index); + resource(shadow, command.shader, "shader", index); + if ( + command.layouts.length > this.limits[4] || + command.attributes.length > this.limits[5] || + command.targets.length > this.limits[6] + ) { + throw new ProtocolError( + "pipeline exceeds negotiated limits", + index + ); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "pipeline", + command, + index + ); + break; + case 11: { + const removed = resource(shadow, command.id, null, index); + adjustResourceStats(stats, removed, -1, index); + shadow.delete(command.id); + slots.set(command.id & HANDLE_SLOT_MASK, command.id >>> 20); + break; + } + case 12: + if (pass) { + throw new ProtocolError("nested render pass", index); + } + if (command.surfaceGeneration !== this.surfaceGeneration) { + throw new ProtocolError( + `render pass surface generation ${command.surfaceGeneration} ` + + `does not match current generation ${this.surfaceGeneration}`, + index, + BATCH_ERROR_STALE_SURFACE + ); + } + if (command.colorView !== 0) { + throw new ProtocolError( + "render pass uses a non-surface color view", + index + ); + } + renderPasses++; + if (renderPasses > MAX_RENDER_PASSES_PER_BATCH) { + throw new ProtocolError("too many render passes", index); + } + if (command.depthView) { + resource(shadow, command.depthView, "textureView", index); + } + pass = true; + break; + case 13: + if (!pass) { + throw new ProtocolError("pipeline set outside render pass", index); + } + resource(shadow, command.id, "pipeline", index); + break; + case 14: + if (command.slot >= this.limits[4]) { + throw new ProtocolError( + "vertex buffer slot exceeds negotiated limits", + index + ); + } + if (!pass) { + throw new ProtocolError( + "vertex buffer set outside render pass", + index + ); + } + resource(shadow, command.buffer, "buffer", index); + break; + case 15: + if (!pass) { + throw new ProtocolError( + "index buffer set outside render pass", + index + ); + } + resource(shadow, command.buffer, "buffer", index); + break; + case 16: + if (command.bindIndex >= this.limits[3]) { + throw new ProtocolError( + "bind group index exceeds negotiated limits", + index + ); + } + if (!pass) { + throw new ProtocolError( + "bind group set outside render pass", + index + ); + } + resource(shadow, command.bindGroup, "bindGroup", index); + break; + case 17: + case 18: + if (!pass) { + throw new ProtocolError( + "render command outside render pass", + index + ); + } + break; + case 19: + case 20: + if (!pass) { + throw new ProtocolError( + "render command outside render pass", + index + ); + } + draws++; + if (draws > MAX_DRAWS_PER_BATCH) { + throw new ProtocolError("too many draw commands", index); + } + break; + case 21: + if (!pass) { + throw new ProtocolError("render pass is not active", index); + } + pass = false; + break; + case 22: + if (pass) { + throw new ProtocolError("buffer copy inside render pass", index); + } + resource(shadow, command.source, "buffer", index); + resource(shadow, command.destination, "buffer", index); + break; + case 23: + resource(shadow, command.texture, "texture", index); + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "textureView", + command, + index + ); + break; + } + } + if (pass) { + throw new ProtocolError("render pass was not ended"); + } + return { commands, shadow, slots }; + } + + // Execution mirrors the validated opcode set and commits it transactionally. + // eslint-disable-next-line complexity + async execute(bytes) { + if (this.stopped) { + return; + } + let batch; + let validated; + try { + batch = parseCommands(bytes); + } catch (error) { + throw new Error(`malformed GPU batch: ${error.message}`); + } + try { + validated = this.validate(batch); + } catch (error) { + this.emitBatchRejected( + error.commandIndex ?? 0xffffffff, + error.errorCode ?? 1, + batch.sequence, + error.message + ); + return; + } + this.device.pushErrorScope("validation"); + this.device.pushErrorScope("out-of-memory"); + const next = new Map(this.resources); + let encoder = null; + let pass = null; + let surfaceView = null; + let surfaceTexture = null; + let readback = null; + const removed = []; + const shaders = []; + const created = []; + try { + for (const command of validated.commands) { + const entry = command.resourceEntry; + switch (command.opcode) { + case 1: + entry.value = this.device.createBuffer({ + size: command.size, + usage: command.usage, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 2: + this.device.queue.writeBuffer( + resource(next, command.id, "buffer", command.index).value, + command.offset, + command.data + ); + break; + case 3: + entry.value = this.device.createTexture({ + size: [command.width, command.height, 1], + mipLevelCount: command.mipLevelCount, + sampleCount: command.sampleCount, + dimension: "2d", + format: command.format, + usage: command.usage, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 4: + this.device.queue.writeTexture( + { + texture: resource(next, command.id, "texture", command.index) + .value, + mipLevel: command.mipLevel, + origin: command.origin, + }, + command.data, + { + offset: 0, + bytesPerRow: command.bytesPerRow, + rowsPerImage: command.rowsPerImage, + }, + command.size + ); + break; + case 5: + entry.value = this.device.createSampler(command); + next.set(command.id, entry); + created.push(entry); + break; + case 6: + entry.value = this.device.createShaderModule({ + code: command.source, + }); + next.set(command.id, entry); + created.push(entry); + shaders.push([entry, command.id]); + break; + case 7: + entry.value = this.device.createBindGroupLayout({ + entries: command.entries, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 8: + entry.value = this.device.createPipelineLayout({ + bindGroupLayouts: command.layouts.map( + id => resource(next, id, "bindGroupLayout", command.index).value + ), + }); + next.set(command.id, entry); + created.push(entry); + break; + case 9: + entry.value = this.device.createBindGroup({ + layout: resource( + next, + command.layout, + "bindGroupLayout", + command.index + ).value, + entries: command.entries.map(item => ({ + binding: item.binding, + resource: + item.kind === 1 + ? { + buffer: resource( + next, + item.resourceId, + "buffer", + command.index + ).value, + offset: item.offset, + size: item.size, + } + : resource( + next, + item.resourceId, + item.kind === 2 ? "sampler" : "textureView", + command.index + ).value, + })), + }); + next.set(command.id, entry); + created.push(entry); + break; + case 10: { + const buffers = command.layouts.map(layout => ({ + arrayStride: layout.arrayStride, + stepMode: layout.stepMode, + attributes: command.attributes.slice( + layout.firstAttribute, + layout.firstAttribute + layout.attributeCount + ), + })); + const descriptor = { + layout: resource( + next, + command.layout, + "pipelineLayout", + command.index + ).value, + vertex: { + module: resource(next, command.shader, "shader", command.index) + .value, + entryPoint: "vs_main", + buffers, + }, + fragment: { + module: resource(next, command.shader, "shader", command.index) + .value, + entryPoint: "fs_main", + targets: command.targets, + }, + primitive: { + topology: command.topology, + frontFace: command.frontFace, + cullMode: command.cullMode, + stripIndexFormat: command.stripIndexFormat, + }, + multisample: { count: command.sampleCount }, + }; + if (command.depthFormatId) { + descriptor.depthStencil = { + format: mapped(formats, command.depthFormatId, "depth format"), + depthWriteEnabled: Boolean(command.flags & 1), + depthCompare: command.depthCompare, + }; + } + entry.value = this.device.createRenderPipeline(descriptor); + next.set(command.id, entry); + created.push(entry); + break; + } + case 11: { + const removedEntry = resource( + next, + command.id, + null, + command.index + ); + removed.push(removedEntry); + next.delete(command.id); + break; + } + case 12: { + encoder ||= this.device.createCommandEncoder(); + const colorAttachment = { + view: (surfaceView ??= (surfaceTexture ??= + this.context.getCurrentTexture()).createView()), + loadOp: command.flags & 1 ? "load" : "clear", + storeOp: command.flags & 2 ? "store" : "discard", + clearValue: command.clearColor, + }; + const descriptor = { colorAttachments: [colorAttachment] }; + if (command.depthView) { + descriptor.depthStencilAttachment = { + view: resource( + next, + command.depthView, + "textureView", + command.index + ).value, + depthLoadOp: command.flags & 4 ? "load" : "clear", + depthStoreOp: command.flags & 8 ? "store" : "discard", + depthClearValue: command.clearDepth, + }; + } + pass = encoder.beginRenderPass(descriptor); + break; + } + case 13: + pass.setPipeline( + resource(next, command.id, "pipeline", command.index).value + ); + break; + case 14: + pass.setVertexBuffer( + command.slot, + resource(next, command.buffer, "buffer", command.index).value, + command.offset, + command.size + ); + break; + case 15: + pass.setIndexBuffer( + resource(next, command.buffer, "buffer", command.index).value, + command.format, + command.offset, + command.size + ); + break; + case 16: + pass.setBindGroup( + command.bindIndex, + resource(next, command.bindGroup, "bindGroup", command.index) + .value, + command.dynamicOffsets + ); + break; + case 17: + pass.setViewport(...command.values); + break; + case 18: + pass.setScissorRect(...command.values); + break; + case 19: + pass.draw(...command.values); + break; + case 20: + pass.drawIndexed(...command.values); + break; + case 21: + pass.end(); + pass = null; + break; + case 22: + encoder ||= this.device.createCommandEncoder(); + encoder.copyBufferToBuffer( + resource(next, command.source, "buffer", command.index).value, + command.sourceOffset, + resource(next, command.destination, "buffer", command.index) + .value, + command.destinationOffset, + command.size + ); + break; + case 23: + entry.value = resource( + next, + command.texture, + "texture", + command.index + ).value.createView({ + format: command.format, + dimension: "2d", + aspect: command.aspect, + baseMipLevel: command.baseMipLevel, + mipLevelCount: command.mipLevelCount, + baseArrayLayer: command.baseArrayLayer, + arrayLayerCount: command.arrayLayerCount, + }); + next.set(command.id, entry); + created.push(entry); + break; + } + } + if (encoder && surfaceTexture && this.testReadbacksRemaining > 0) { + readback = this.device.createBuffer({ + size: 3 * 256, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, + }); + const sampleLocations = [ + [this.physicalWidth >> 1, this.physicalHeight >> 1], + [ + Math.min(8, this.physicalWidth - 1), + Math.min(8, this.physicalHeight - 1), + ], + [Math.max(0, this.physicalWidth - 20), this.physicalHeight >> 1], + ]; + for (const [index, [x, y]] of sampleLocations.entries()) { + encoder.copyTextureToBuffer( + { texture: surfaceTexture, origin: { x, y } }, + { + buffer: readback, + offset: index * 256, + bytesPerRow: 256, + rowsPerImage: 1, + }, + { width: 1, height: 1, depthOrArrayLayers: 1 } + ); + } + } + if (encoder) { + this.device.queue.submit([encoder.finish()]); + } + } catch (error) { + created.forEach(entry => entry.value?.destroy?.()); + readback?.destroy(); + void this.device.popErrorScope(); + void this.device.popErrorScope(); + throw error; + } + const outOfMemory = await this.device.popErrorScope(); + const validation = await this.device.popErrorScope(); + const gpuError = outOfMemory || validation; + if (gpuError) { + readback?.destroy(); + created.forEach(entry => entry.value?.destroy?.()); + this.emitBatchRejected( + 0xffffffff, + outOfMemory ? 2 : 1, + batch.sequence, + gpuError.message + ); + return; + } + this.resources = next; + this.handleSlots = validated.slots; + this.lastSequence = batch.sequence; + removed.forEach(entry => entry.value?.destroy?.()); + shaders.forEach(([entry, handle]) => + this.watchShader(entry, handle, batch.sequence) + ); + await this.device.queue.onSubmittedWorkDone(); + if (readback) { + await readback.mapAsync(GPUMapMode.READ); + const readbackBytes = new Uint8Array(readback.getMappedRange()); + const samples = [0, 256, 512].map(offset => + Array.from(readbackBytes.subarray(offset, offset + 4)) + ); + readback.unmap(); + readback.destroy(); + this.testReadbacksRemaining--; + postMessage({ type: "test-readback", samples }); + } + postBytes("event", makeEvent(5, batch.sequence)); + postMessage({ type: "presented", sequence: batch.sequence }); + if (this.testDeviceLossPending && surfaceTexture) { + this.testDeviceLossPending = false; + this.device.destroy(); + } + } + + async watchShader(entry, handle, sequence) { + try { + const info = await entry.value.getCompilationInfo(); + for (const message of info.messages) { + let severity = 3; + if (message.type === "error") { + severity = 1; + } else if (message.type === "warning") { + severity = 2; + } + const text = diagnosticBytes(message.message || "shader diagnostic"); + const payload = new Uint8Array(32 + align4(text.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, handle, true); + view.setUint16(4, severity, true); + view.setUint16(6, text.truncated ? 1 : 0, true); + view.setUint32(8, message.lineNum || 0, true); + view.setUint32(12, message.linePos || 0, true); + view.setUint32(16, message.offset || 0, true); + view.setUint32(20, message.length || 0, true); + view.setUint32(24, text.bytes.byteLength, true); + payload.set(text.bytes, 32); + postBytes("event", makeEvent(2, sequence, payload)); + if (severity === 1) { + entry.failed = true; + } + } + } catch {} + } + + emitBatchRejected(commandIndex, errorCode, sequence, message) { + const diagnostic = diagnosticBytes( + `surface_generation=${this.surfaceGeneration} ` + + `physical=${this.physicalWidth}x${this.physicalHeight} ` + + `logical=${this.logicalWidth}x${this.logicalHeight} ` + + `scale=${this.scale} last_sequence=${this.lastSequence}: ${message}` + ); + const payload = new Uint8Array(16 + align4(diagnostic.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, commandIndex, true); + view.setUint32(4, errorCode, true); + view.setUint32(8, diagnostic.bytes.byteLength, true); + view.setUint32(12, diagnostic.truncated ? 1 : 0, true); + payload.set(diagnostic.bytes, 16); + postBytes("event", makeEvent(1, sequence, payload)); + } + + emitTextEvent(type, sequence, errorCode, message) { + const text = diagnosticBytes(message); + const payload = new Uint8Array(12 + align4(text.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, errorCode, true); + view.setUint32(4, text.bytes.byteLength, true); + view.setUint32(8, text.truncated ? 1 : 0, true); + payload.set(text.bytes, 12); + postBytes("event", makeEvent(type, sequence, payload)); + } + + reset() { + this.queue = this.queue + .then(() => { + if (this.stopped) { + return; + } + for (const entry of this.resources.values()) { + entry.value?.destroy?.(); + } + this.resources.clear(); + this.handleSlots.clear(); + this.pendingResize = null; + this.lastSequence = 0; + }) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }); + } + + stop() { + this.stopped = true; + for (const entry of this.resources.values()) { + entry.value?.destroy?.(); + } + this.pendingResize = null; + this.resources.clear(); + this.handleSlots.clear(); + this.device.destroy(); + } +} + +function align4(value) { + return (value + 3) & ~3; +} + +function diagnosticBytes(message) { + const encoded = textEncoder.encode(String(message)); + if (encoded.byteLength <= MAX_DIAGNOSTIC_BYTES) { + return { bytes: encoded, truncated: false }; + } + let end = MAX_DIAGNOSTIC_BYTES; + while (end > 0) { + try { + textDecoder.decode(encoded.subarray(0, end)); + break; + } catch { + end--; + } + } + return { bytes: encoded.slice(0, end), truncated: true }; +} + +function makeEvent(type, sequence, payload = new Uint8Array()) { + const bytes = new Uint8Array(EVENT_HEADER_BYTES + payload.byteLength); + const view = new DataView(bytes.buffer); + bytes.set(textEncoder.encode("EGE1"), 0); + view.setUint16(4, WIRE_VERSION, true); + view.setUint16(6, type, true); + view.setUint32(8, bytes.byteLength, true); + view.setBigUint64(16, BigInt(sequence), true); + bytes.set(payload, EVENT_HEADER_BYTES); + return bytes; +} + +function postBytes(type, bytes) { + postMessage({ type, bytes }, [bytes.buffer]); +} + +let engine = null; + +onmessage = event => { + const message = event.data; + if (message?.type === "init" && !engine) { + void GpuEngine.create( + message.canvas, + message.requirements || {}, + message.dimensions, + message.testReadback === true, + message.testDeviceLoss === true + ) + .then(created => { + engine = created; + postBytes("capabilities", engine.capabilities()); + }) + .catch(error => + postMessage({ type: "error", message: error.message || String(error) }) + ); + } else if (message?.type === "batch" && engine) { + engine.submit(new Uint8Array(message.bytes)); + } else if (message?.type === "resize" && engine) { + engine.scheduleResize(message.dimensions); + } else if (message?.type === "reset" && engine) { + engine.reset(); + } else if (message?.type === "stop" && engine) { + engine.stop(); + engine = null; + } +}; diff --git a/js/packages/pvm-browser-runtime/dist/pvm-worker.js b/js/packages/pvm-browser-runtime/dist/pvm-worker.js new file mode 100644 index 000000000..da32515c4 --- /dev/null +++ b/js/packages/pvm-browser-runtime/dist/pvm-worker.js @@ -0,0 +1,1558 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +(() => { + const STATUS_FINISHED = -1; + const STATUS_ECALL = -2; + const STATUS_TRAP = -3; + const STATUS_OUT_OF_GAS = -4; + const INPUT_EVENT_BYTES = 8; + const MAX_INPUT_EVENTS = 4096; + const MAX_HOSTCALLS_PER_INIT = 1024 * 1024; + const MAX_HOSTCALLS_PER_UPDATE = 8192; + const MAX_HOSTCALL_BYTES = 32 * 1024 * 1024; + const MAX_LOG_BYTES = 4 * 1024; + const MAX_SAVE_BYTES = 1024 * 1024; + const MAX_AUDIO_SAMPLES = 48000 * 2; + const MAX_FRAME_BYTES = 16 * 1024 * 1024; + const MAX_TRI2D_BYTES = 8 * 1024 * 1024; + const MAX_GPU_BATCH_BYTES = 4 * 1024 * 1024; + const MAX_GPU_EVENT_BYTES = 64 * 1024; + const MAX_GPU_EVENTS = 256; + const MAX_GPU_SUBMITS_PER_UPDATE = 8; + const MAX_GPU_COMMANDS = 16_384; + const GPU_ERROR_MALFORMED_BATCH = -2; + const GPU_ERROR_QUOTA_EXCEEDED = -3; + const GPU_ERROR_INVALID_STATE = -5; + const IOV_MAX = 1024n; + const AT_FDCWD = BigInt.asUintN(64, -100n); + const ENOSYS = 38; + const EFAULT = 14; + const ENOENT = 2; + const EBADF = 9; + const EACCES = 13; + const EINVAL = 22; + const SYS_OPENAT = 56n; + const SYS_CLOSE = 57n; + const SYS_LSEEK = 62n; + const SYS_READ = 63n; + const SYS_READV = 65n; + const SYS_WRITEV = 66n; + const SYS_EXIT = 93n; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + + function readMetadata(module) { + const sections = WebAssembly.Module.customSections( + module, + "epoca.pvm.meta", + ); + if (sections.length !== 1) { + throw new Error("translated PolkaVM module has invalid metadata"); + } + const bytes = new Uint8Array(sections[0]); + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + let offset = 0; + const requireBytes = (length) => { + if (offset + length > bytes.byteLength) { + throw new Error("translated PolkaVM metadata is truncated"); + } + }; + const readU16 = () => { + requireBytes(2); + const value = view.getUint16(offset, true); + offset += 2; + return value; + }; + const readU32 = () => { + requireBytes(4); + const value = view.getUint32(offset, true); + offset += 4; + return value; + }; + const readString = (length) => { + requireBytes(length); + const value = decoder.decode(bytes.subarray(offset, offset + length)); + offset += length; + return value; + }; + requireBytes(4); + if (decoder.decode(bytes.subarray(0, 4)) !== "EPM2") { + throw new Error("translated PolkaVM metadata has an incompatible ABI"); + } + offset = 4; + const is64Bit = readU32() !== 0; + const names = [ + "roAddress", + "roSize", + "roPhysical", + "rwAddress", + "rwSize", + "rwPhysical", + "heapBase", + "heapLimit", + "stackLow", + "stackHigh", + "stackPhysical", + ]; + const layout = {}; + for (const name of names) { + layout[name] = readU32(); + } + const imports = []; + const importCount = readU32(); + for (let index = 0; index < importCount; index++) { + const length = readU16(); + imports.push(length ? readString(length) : null); + } + const exports = new Map(); + const exportCount = readU32(); + for (let index = 0; index < exportCount; index++) { + const name = readString(readU16()); + const block = readU32(); + if (!name || exports.has(name)) { + throw new Error("translated PolkaVM metadata has invalid exports"); + } + exports.set(name, block); + } + if (offset !== bytes.byteLength) { + throw new Error("translated PolkaVM metadata has trailing data"); + } + return { is64Bit, layout, imports, exports }; + } + + function errno(code) { + return BigInt.asUintN(64, -BigInt(code)); + } + + function normalizedPath(path) { + while (path.startsWith("./")) { + path = path.slice(2); + } + return path.replace(/^\/+/, ""); + } + + function signedByte(value) { + return value > 127 ? value - 256 : value; + } + + function hidToCoreVm(code) { + if (code >= 0x04 && code <= 0x1d) { + return 0x61 + code - 0x04; + } + if (code >= 0x1e && code <= 0x26) { + return 0x31 + code - 0x1e; + } + const keys = new Map([ + [0x27, 0x30], + [0x28, 0x0a], + [0x58, 0x0a], + [0x29, 0x1b], + [0x2a, 0x08], + [0x2b, 0x09], + [0x2c, 0x20], + [0x2d, 0x2d], + [0x56, 0x2d], + [0x2e, 0x3d], + [0x2f, 0x5b], + [0x30, 0x5d], + [0x31, 0x5c], + [0x33, 0x3b], + [0x34, 0x27], + [0x35, 0x60], + [0x36, 0x2c], + [0x37, 0x2e], + [0x63, 0x2e], + [0x38, 0x2f], + [0x54, 0x2f], + [0x46, 0x91], + [0x47, 0x92], + [0x48, 0x93], + [0x49, 0x94], + [0x4a, 0x96], + [0x4b, 0x98], + [0x4c, 0x95], + [0x4d, 0x97], + [0x4e, 0x99], + [0x4f, 0x82], + [0x50, 0x83], + [0x51, 0x81], + [0x52, 0x80], + [0x55, 0x2a], + [0x57, 0x2b], + [0x59, 0x97], + [0x5a, 0x81], + [0x5b, 0x99], + [0x5c, 0x83], + [0x5d, 0x35], + [0x5e, 0x82], + [0x5f, 0x96], + [0x60, 0x80], + [0x61, 0x98], + [0x62, 0x2e], + [0xe0, 0x9c], + [0xe1, 0x9a], + [0xe2, 0x9e], + [0xe4, 0x9d], + [0xe5, 0x9b], + [0xe6, 0x9f], + ]); + if (code >= 0x3a && code <= 0x45) { + return 0x84 + code - 0x3a; + } + return keys.get(code); + } + + class TranslatedPvmRuntime { + constructor(module, assets, emit, maxGas, audioEnabled, gpuCapabilities = null) { + this.metadata = readMetadata(module); + this.instance = new WebAssembly.Instance(module, {}); + this.pvm = this.instance.exports; + this.memories = [ + this.pvm.memory_ro, + this.pvm.memory_rw, + this.pvm.memory_stack, + ]; + if ( + this.memories.some((memory) => !(memory instanceof WebAssembly.Memory)) + ) { + throw new Error("translated PolkaVM module is missing guest memories"); + } + this.assets = new Map( + assets.map((asset) => [ + normalizedPath(asset.path), + new Uint8Array(asset.bytes), + ]), + ); + this.emit = emit; + this.audioEnabled = audioEnabled; + this.gpuCapabilities = + gpuCapabilities instanceof Uint8Array ? gpuCapabilities.slice() : null; + this.gpuEvents = []; + this.gpuSubmits = 0; + this.gpuLastSequence = 0n; + this.maxGas = BigInt(maxGas); + this.input = []; + this.coreInput = []; + this.pointer = null; + this.timeMs = null; + this.clockStartedAt = performance.now(); + this.hostcalls = 0; + this.hostcallBytes = 0; + this.resumePending = false; + this.stopped = false; + this.coreVm = this.metadata.exports.has("_pvm_start"); + this.coreVmStarted = false; + this.palette = new Uint32Array(256); + this.palette.fill(0xffffffff); + this.audioChannels = 0; + this.audioSampleRate = 0; + this.fds = new Map(); + this.nextFd = 3; + this.imports = this.metadata.imports; + this.exports = this.metadata.exports; + for (let index = 0; index < 13; index++) { + if (!(this.pvm[`r${index}`] instanceof WebAssembly.Global)) { + throw new Error("translated PolkaVM module is missing registers"); + } + } + } + + initialize() { + this.#resetBudget(MAX_HOSTCALLS_PER_INIT); + if (this.coreVm) { + this.#setupCoreVm(); + return; + } + const init = this.exports.get("init"); + if (init !== undefined) { + this.#run(init, false); + } + } + + update(timeMs) { + if (this.stopped) { + return; + } + this.timeMs = timeMs; + this.gpuSubmits = 0; + this.#resetBudget( + this.coreVm && !this.coreVmStarted + ? MAX_HOSTCALLS_PER_INIT + : MAX_HOSTCALLS_PER_UPDATE, + ); + if (this.coreVm) { + this.#run(this.exports.get("_pvm_start"), true); + this.coreVmStarted = true; + return; + } + const update = this.exports.get("update"); + if (update === undefined) { + throw new Error("translated PolkaVM guest has no update export"); + } + this.#run(update, false); + } + + sendInput(bytes) { + if (this.stopped || bytes.byteLength !== INPUT_EVENT_BYTES) { + return; + } + if (!this.coreVm) { + if (this.input.length === MAX_INPUT_EVENTS) { + this.input.shift(); + } + this.input.push(bytes.slice()); + return; + } + const view = new DataView( + bytes.buffer, + bytes.byteOffset, + bytes.byteLength, + ); + const type = bytes[0]; + if (type === 1 || type === 2) { + const key = hidToCoreVm(bytes[1]); + if (key !== undefined) { + this.#queueCoreInput(key, type === 1 ? 1 : 0); + } + } else if (type === 3 || type === 4) { + const key = + bytes[1] >= 1 && bytes[1] <= 3 ? 0x9f + bytes[1] : undefined; + if (key !== undefined) { + this.#queueCoreInput(key, type === 3 ? 1 : 0); + } + } else if (type === 5) { + const current = [view.getUint16(2, true), view.getUint16(4, true)]; + if (this.pointer) { + this.#queueCoreInput( + 0xa3, + Math.max(-128, Math.min(127, current[0] - this.pointer[0])) & 0xff, + ); + this.#queueCoreInput( + 0xa4, + Math.max(-128, Math.min(127, current[1] - this.pointer[1])) & 0xff, + ); + } + this.pointer = current; + } else if (type === 6) { + this.#queueCoreInput( + 0xa3, + Math.max(-128, Math.min(127, view.getInt16(2, true))) & 0xff, + ); + this.#queueCoreInput( + 0xa4, + Math.max(-128, Math.min(127, view.getInt16(4, true))) & 0xff, + ); + } + } + + sendGpuEvent(bytes) { + if ( + this.stopped || + !(bytes instanceof Uint8Array) || + bytes.byteLength < 24 || + bytes.byteLength > MAX_GPU_EVENT_BYTES || + decoder.decode(bytes.subarray(0, 4)) !== "EGE1" + ) { + return; + } + if (this.gpuEvents.length === MAX_GPU_EVENTS) { + this.gpuEvents.shift(); + } + this.gpuEvents.push(bytes.slice()); + } + + stop() { + this.stopped = true; + this.input.length = 0; + this.coreInput.length = 0; + this.gpuEvents.length = 0; + } + + #resetBudget(hostcalls) { + this.hostcalls = hostcalls; + this.hostcallBytes = MAX_HOSTCALL_BYTES; + this.pvm.pvm_set_gas(this.maxGas); + } + + #run(entry, yieldOnFrame) { + let status; + if (this.resumePending) { + this.resumePending = false; + status = this.pvm.pvm_resume(); + } else { + if (entry === undefined) { + throw new Error("translated PolkaVM entrypoint is missing"); + } + status = this.pvm.pvm_begin(entry, this.maxGas); + } + for (;;) { + if (status === STATUS_FINISHED) { + if (this.coreVm) { + throw new Error("CoreVM guest exited"); + } + return; + } + if (status === STATUS_TRAP) { + throw new Error( + `translated PolkaVM execution trapped at ${this.pvm.trap_pc.value}`, + ); + } + if (status === STATUS_OUT_OF_GAS) { + throw new Error("translated PolkaVM guest ran out of gas"); + } + if (status !== STATUS_ECALL) { + throw new Error( + `translated PolkaVM returned invalid status ${status}`, + ); + } + const importIndex = this.pvm.ecall.value >>> 0; + const name = this.imports[importIndex]; + if (!name) { + throw new Error( + `translated PolkaVM called unknown import ${importIndex}`, + ); + } + if (--this.hostcalls < 0) { + throw new Error( + `translated PolkaVM guest exceeded hostcall budget in ${name}`, + ); + } + const yielded = this.coreVm + ? this.#handleCoreVmCall(name) + : this.#handleCooperativeCall(name); + if (yielded && yieldOnFrame) { + this.resumePending = true; + return; + } + status = this.pvm.pvm_resume(); + } + } + + #reg(index) { + return BigInt.asUintN(64, this.pvm[`r${index}`].value); + } + + #setReg(index, value) { + const normalized = this.metadata.is64Bit + ? BigInt.asIntN(64, BigInt(value)) + : BigInt.asUintN(32, BigInt(value)); + this.pvm[`r${index}`].value = normalized; + } + + #u32(value) { + return Number(value & 0xffffffffn) >>> 0; + } + + #range(address, length, write = false) { + address >>>= 0; + length >>>= 0; + const end = address + length; + if (end > 0x100000000) { + throw new Error( + "translated PolkaVM guest memory access is out of range", + ); + } + const { layout } = this.metadata; + let memory; + let physical; + if (address >= layout.stackLow && end <= layout.stackHigh) { + memory = this.memories[2]; + physical = address - layout.stackLow; + } else if ( + address >= layout.rwAddress && + end <= layout.rwAddress + this.memories[1].buffer.byteLength + ) { + memory = this.memories[1]; + physical = address - layout.rwAddress; + } else if ( + !write && + address >= layout.roAddress && + end <= layout.roAddress + layout.roSize + ) { + memory = this.memories[0]; + physical = address - layout.roAddress; + } else { + throw new Error( + "translated PolkaVM guest memory access is out of range", + ); + } + return new Uint8Array(memory.buffer, physical, length); + } + + #read(address, length) { + this.#chargeBytes(length); + return this.#range(address, length).slice(); + } + + #write(address, bytes) { + this.#chargeBytes(bytes.byteLength); + this.#range(address, bytes.byteLength, true).set(bytes); + } + + #chargeBytes(length) { + if ( + !Number.isSafeInteger(length) || + length < 0 || + length > this.hostcallBytes + ) { + throw new Error( + "translated PolkaVM guest exceeded hostcall byte budget", + ); + } + this.hostcallBytes -= length; + } + + #readU64(address) { + const bytes = this.#range(address >>> 0, 8); + return new DataView(bytes.buffer, bytes.byteOffset, 8).getBigUint64( + 0, + true, + ); + } + + #writeU64(address, value) { + const bytes = this.#range(address >>> 0, 8, true); + new DataView(bytes.buffer, bytes.byteOffset, 8).setBigUint64( + 0, + BigInt.asUintN(64, value), + true, + ); + } + + #readCString(address) { + const output = []; + for (let offset = 0; offset < 255; offset++) { + let byte; + try { + byte = this.#range((address + offset) >>> 0, 1)[0]; + } catch { + return null; + } + if (!byte) { + return new Uint8Array(output); + } + output.push(byte); + } + return null; + } + + #handleCooperativeCall(name) { + const a0 = this.#reg(7); + const a1 = this.#reg(8); + const a2 = this.#reg(9); + const a3 = this.#reg(10); + const a4 = this.#reg(11); + switch (name) { + case "host_present_frame": { + const width = this.#u32(a1); + const height = this.#u32(a2); + const stride = this.#u32(a3); + const rowBytes = width * 4; + const length = rowBytes * height; + if ( + !width || + !height || + stride !== rowBytes || + length > MAX_FRAME_BYTES + ) { + this.#setReg(7, 1n); + return false; + } + const source = this.#read(this.#u32(a0), length); + const pixels = new Uint8Array(length); + for (let index = 0; index < length; index += 4) { + pixels[index] = source[index + 2]; + pixels[index + 1] = source[index + 1]; + pixels[index + 2] = source[index]; + pixels[index + 3] = source[index + 3]; + } + this.emit({ type: "frame", width, height, pixels }, [pixels.buffer]); + this.#setReg(7, 0n); + return false; + } + case "host_tri2d_submit": { + const length = this.#u32(a1); + if (!length || length > MAX_TRI2D_BYTES) { + this.#setReg(7, 1n); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + this.emit({ type: "tri2d", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "epoca_gpu_capabilities": { + if (this.gpuCapabilities === null) { + this.#setReg(7, BigInt(GPU_ERROR_INVALID_STATE)); + return false; + } + const capacity = this.#u32(a1); + const required = this.gpuCapabilities.byteLength; + if (capacity < required) { + this.#setReg(7, BigInt(-required)); + return false; + } + this.#write(this.#u32(a0), this.gpuCapabilities); + this.#setReg(7, BigInt(required)); + return false; + } + case "epoca_gpu_submit": { + const length = this.#u32(a1); + if (this.gpuCapabilities === null) { + this.#setReg(7, BigInt(GPU_ERROR_INVALID_STATE)); + return false; + } + if ( + !length || + length > MAX_GPU_BATCH_BYTES || + this.gpuSubmits === MAX_GPU_SUBMITS_PER_UPDATE + ) { + this.#setReg( + 7, + BigInt( + this.gpuSubmits === MAX_GPU_SUBMITS_PER_UPDATE + ? GPU_ERROR_QUOTA_EXCEEDED + : GPU_ERROR_MALFORMED_BATCH, + ), + ); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + const sequence = this.#gpuBatchSequence(bytes); + if (sequence === null || sequence <= this.gpuLastSequence) { + this.#setReg(7, BigInt(GPU_ERROR_MALFORMED_BATCH)); + return false; + } + this.gpuSubmits++; + this.gpuLastSequence = sequence; + this.emit({ type: "gpu-batch", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "epoca_gpu_receive": { + const event = this.gpuEvents[0]; + if (event === undefined) { + this.#setReg(7, 0n); + return false; + } + const capacity = this.#u32(a1); + if (capacity < event.byteLength) { + this.#setReg(7, BigInt(-event.byteLength)); + return false; + } + this.#write(this.#u32(a0), event); + this.gpuEvents.shift(); + this.#setReg(7, BigInt(event.byteLength)); + return false; + } + case "host_poll_input": { + const capacity = this.#u32(a1); + const count = Math.min( + Math.floor(capacity / INPUT_EVENT_BYTES), + this.input.length, + ); + const output = new Uint8Array(count * INPUT_EVENT_BYTES); + for (let index = 0; index < count; index++) { + output.set(this.input.shift(), index * INPUT_EVENT_BYTES); + } + this.#write(this.#u32(a0), output); + this.#setReg(7, BigInt(output.byteLength)); + return false; + } + case "host_time_ms": { + const timeMs = this.timeMs ?? performance.now() - this.clockStartedAt; + this.#setReg(7, BigInt(Math.max(0, Math.trunc(timeMs)))); + return false; + } + case "host_sleep_ms": + this.timeMs = + (this.timeMs ?? performance.now() - this.clockStartedAt) + + Math.min(this.#u32(a0), 50); + return false; + case "host_audio_submit": { + if (!this.audioEnabled) { + this.#setReg(7, 3n); + return false; + } + const sampleCount = this.#u32(a1); + if ( + !sampleCount || + sampleCount % 2 || + sampleCount > MAX_AUDIO_SAMPLES + ) { + this.#setReg(7, 1n); + return false; + } + const samples = this.#read(this.#u32(a0), sampleCount * 2); + this.emit( + { type: "audio", sampleRate: 48000, channels: 2, samples }, + [samples.buffer], + ); + this.#setReg(7, 0n); + return false; + } + case "host_asset_read": { + const nameLength = this.#u32(a1); + const offset = this.#u32(a2); + const destination = this.#u32(a3); + const capacity = this.#u32(a4); + if (!nameLength || nameLength > 1024) { + this.#setReg(7, 0n); + return false; + } + const assetName = decoder.decode( + this.#read(this.#u32(a0), nameLength), + ); + const asset = this.assets.get(assetName); + if (!asset || offset >= asset.byteLength) { + this.#setReg(7, 0n); + return false; + } + const length = Math.min( + capacity, + asset.byteLength - offset, + 16 * 1024 * 1024, + ); + this.#write(destination, asset.subarray(offset, offset + length)); + this.#setReg(7, BigInt(length)); + return false; + } + case "host_save_submit": { + const length = this.#u32(a1); + if (!length || length > MAX_SAVE_BYTES) { + this.#setReg(7, 1n); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + this.emit({ type: "save", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "host_log": { + const length = Math.min(this.#u32(a1), MAX_LOG_BYTES); + const message = decoder.decode(this.#read(this.#u32(a0), length)); + this.emit({ type: "log", message }); + return false; + } + default: + throw new Error( + `translated PolkaVM guest uses unsupported import ${name}`, + ); + } + } + + #gpuBatchSequence(bytes) { + if ( + bytes.byteLength < 24 || + decoder.decode(bytes.subarray(0, 4)) !== "EPG1" + ) { + return null; + } + const view = new DataView( + bytes.buffer, + bytes.byteOffset, + bytes.byteLength, + ); + if ( + view.getUint16(4, true) !== 1 || + view.getUint16(6, true) !== 0 || + view.getUint32(8, true) !== bytes.byteLength + ) { + return null; + } + const commandCount = view.getUint32(12, true); + const sequence = view.getBigUint64(16, true); + if (sequence === 0n || commandCount > MAX_GPU_COMMANDS) { + return null; + } + let offset = 24; + for (let index = 0; index < commandCount; index++) { + if ( + offset + 8 > bytes.byteLength || + view.getUint16(offset + 2, true) !== 0 + ) { + return null; + } + const commandBytes = view.getUint32(offset + 4, true); + if ( + commandBytes < 8 || + commandBytes % 4 || + commandBytes > bytes.byteLength - offset + ) { + return null; + } + offset += commandBytes; + } + return offset === bytes.byteLength ? sequence : null; + } + + #setupCoreVm() { + let sp = BigInt(this.metadata.layout.stackHigh); + const argc = 1n; + sp -= (1n + argc + 1n + 0n + 1n + 4n) * 8n; + const addressInit = sp; + let pointer = sp; + this.#writeU64(Number(pointer), argc); + pointer += 8n; + const argument = encoder.encode("./quake\0"); + sp -= BigInt(argument.byteLength); + this.#write(Number(sp), argument); + this.#writeU64(Number(pointer), sp); + pointer += 16n; + pointer += 8n; + this.#writeU64(Number(pointer), 6n); + this.#writeU64(Number(pointer + 8n), 4096n); + this.#setReg(1, sp); + this.#setReg(7, addressInit); + } + + #queueCoreInput(key, value) { + if (!value && (key === 0xa3 || key === 0xa4)) { + return; + } + if (key === 0xa3 || key === 0xa4) { + const existing = this.coreInput.find((event) => event[0] === key); + if (existing) { + existing[1] = + Math.max( + -128, + Math.min(127, signedByte(existing[1]) + signedByte(value)), + ) & 0xff; + return; + } + } + if (this.coreInput.length === 256) { + this.coreInput.shift(); + } + this.coreInput.push([key, value]); + } + + #handleCoreVmCall(name) { + switch (name) { + case "pvm_set_palette": { + const palette = this.#read(this.#u32(this.#reg(7)), 256 * 3); + for (let index = 0; index < 256; index++) { + const offset = index * 3; + this.palette[index] = + palette[offset] | + (palette[offset + 1] << 8) | + (palette[offset + 2] << 16) | + 0xff000000; + } + return false; + } + case "pvm_display": { + const width = this.#u32(this.#reg(7)); + const height = this.#u32(this.#reg(8)); + const length = width * height; + if (!width || !height || length > MAX_FRAME_BYTES / 4) { + throw new Error("CoreVM guest supplied invalid frame dimensions"); + } + const indices = this.#read(this.#u32(this.#reg(9)), length); + const pixels = new Uint8Array(length * 4); + const rgba = new Uint32Array(pixels.buffer); + for (let index = 0; index < length; index++) { + rgba[index] = this.palette[indices[index]]; + } + this.emit({ type: "frame", width, height, pixels }, [pixels.buffer]); + return true; + } + case "pvm_fetch_epoca_inputs": + case "pvm_fetch_inputs": { + const count = Math.min( + this.#u32(this.#reg(8)), + this.coreInput.length, + ); + const output = new Uint8Array(count * 2); + for (let index = 0; index < count; index++) { + output.set(this.coreInput.shift(), index * 2); + } + this.#write(this.#u32(this.#reg(7)), output); + this.#setReg(7, BigInt(count)); + return false; + } + case "pvm_asset_read": { + const nameLength = this.#u32(this.#reg(8)); + const offset = this.#u32(this.#reg(9)); + const destination = this.#u32(this.#reg(10)); + const capacity = this.#u32(this.#reg(11)); + if (!nameLength || nameLength > 1024) { + this.#setReg(7, 0n); + return false; + } + const assetName = decoder.decode( + this.#read(this.#u32(this.#reg(7)), nameLength), + ); + const asset = this.assets.get(assetName); + if (!asset || offset >= asset.byteLength) { + this.#setReg(7, 0n); + return false; + } + const length = Math.min( + capacity, + asset.byteLength - offset, + 16 * 1024 * 1024, + ); + this.#write(destination, asset.subarray(offset, offset + length)); + this.#setReg(7, BigInt(length)); + return false; + } + case "host_audio_submit": { + if (!this.audioEnabled) { + this.#setReg(7, 3n); + return false; + } + const sampleCount = this.#u32(this.#reg(8)); + if ( + !sampleCount || + sampleCount % 2 || + sampleCount > MAX_AUDIO_SAMPLES + ) { + this.#setReg(7, 1n); + return false; + } + const samples = this.#read(this.#u32(this.#reg(7)), sampleCount * 2); + this.emit( + { type: "audio", sampleRate: 48000, channels: 2, samples }, + [samples.buffer], + ); + this.#setReg(7, 0n); + return false; + } + case "pvm_time_ms": { + const timeMs = this.timeMs ?? performance.now() - this.clockStartedAt; + this.#setReg(7, BigInt(Math.max(0, Math.trunc(timeMs)))); + return false; + } + case "host_log": { + const length = Math.min(this.#u32(this.#reg(8)), MAX_LOG_BYTES); + const message = decoder.decode( + this.#read(this.#u32(this.#reg(7)), length), + ); + this.emit({ type: "log", message }); + return false; + } + case "pvm_yield": + return true; + case "pvm_init_audio": { + const channels = this.#u32(this.#reg(7)); + const bitsPerSample = this.#u32(this.#reg(8)); + const sampleRate = this.#u32(this.#reg(9)); + if ( + bitsPerSample !== 16 || + channels < 1 || + channels > 2 || + sampleRate < 8000 || + sampleRate > 96000 + ) { + this.#setReg(7, 0n); + } else { + this.audioChannels = channels; + this.audioSampleRate = sampleRate; + this.#setReg(7, 1n); + } + return false; + } + case "pvm_output_audio": { + const frames = this.#u32(this.#reg(8)); + const sampleCount = Math.min(frames * this.audioChannels, 1024 * 64); + if (this.audioChannels && sampleCount) { + const samples = this.#read( + this.#u32(this.#reg(7)), + sampleCount * 2, + ); + this.emit( + { + type: "audio", + sampleRate: this.audioSampleRate, + channels: this.audioChannels, + samples, + }, + [samples.buffer], + ); + } + return false; + } + case "pvm_syscall": + return this.#handleCoreVmSyscall(); + default: + throw new Error( + `translated CoreVM guest uses unsupported import ${name}`, + ); + } + } + + #handleCoreVmSyscall() { + const syscall = this.#reg(7); + const a1 = this.#reg(8); + const a2 = this.#reg(9); + const a3 = this.#reg(10); + if (syscall === SYS_READ) { + this.#setReg(7, this.#readFile(a1, a2, a3)); + } else if (syscall === SYS_READV || syscall === SYS_WRITEV) { + if (!a3 || a3 > IOV_MAX) { + this.#setReg(7, errno(EINVAL)); + return false; + } + let total = 0n; + for (let index = 0n; index < a3; index++) { + let address; + let length; + try { + address = this.#readU64(this.#u32(a2 + index * 16n)); + length = this.#readU64(this.#u32(a2 + index * 16n + 8n)); + } catch { + this.#setReg(7, errno(EFAULT)); + return false; + } + const result = + syscall === SYS_READV + ? this.#readFile(a1, address, length) + : this.#writeFile(a1, address, length); + if (BigInt.asIntN(64, result) < 0n) { + this.#setReg(7, result); + return false; + } + total += length; + } + this.#setReg(7, total); + } else if (syscall === SYS_EXIT) { + if (a1 === 0n) { + throw new Error("CoreVM guest exited"); + } + throw new Error(`CoreVM guest exited with status ${a1}`); + } else if (syscall === SYS_OPENAT) { + if (a1 !== AT_FDCWD) { + this.#setReg(7, errno(ENOSYS)); + return false; + } + const pathBytes = this.#readCString(this.#u32(a2)); + if (!pathBytes) { + this.#setReg(7, errno(EFAULT)); + return false; + } + const path = normalizedPath(decoder.decode(pathBytes)); + const flags = a3; + const asset = this.assets.get(path); + if (!asset) { + this.#setReg(7, errno(ENOENT)); + } else if (flags & 3n) { + this.#setReg(7, errno(EACCES)); + } else { + const fd = this.nextFd++; + this.fds.set(fd, { bytes: asset, position: 0n }); + this.#setReg(7, BigInt(fd)); + } + } else if (syscall === SYS_LSEEK) { + this.#setReg(7, this.#seekFile(a1, a2, a3)); + } else if (syscall === SYS_CLOSE) { + const fd = this.#fdNumber(a1); + if (fd === null || !this.fds.delete(fd)) { + this.#setReg(7, errno(EBADF)); + } else { + this.#setReg(7, 0n); + } + } else { + this.#setReg(7, errno(ENOSYS)); + } + return false; + } + + #fdNumber(value) { + return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : null; + } + + #readFile(fdValue, address, length) { + const fd = this.#fdNumber(fdValue); + const file = fd === null ? null : this.fds.get(fd); + if (!file) { + return errno(EBADF); + } + if ( + address + length > 0x100000000n || + length > BigInt(MAX_HOSTCALL_BYTES) + ) { + return errno(EFAULT); + } + const end = + file.position + length < BigInt(file.bytes.byteLength) + ? file.position + length + : BigInt(file.bytes.byteLength); + if (file.position >= end) { + return 0n; + } + const count = Number(end - file.position); + try { + this.#write( + this.#u32(address), + file.bytes.subarray( + Number(file.position), + Number(file.position) + count, + ), + ); + } catch { + return errno(EFAULT); + } + file.position += BigInt(count); + return BigInt(count); + } + + #writeFile(fdValue, address, length) { + if (fdValue !== 1n && fdValue !== 2n) { + return errno(EBADF); + } + if ( + address + length > 0x100000000n || + length > BigInt(MAX_HOSTCALL_BYTES) + ) { + return errno(EFAULT); + } + try { + const byteLength = Number(length); + this.#chargeBytes(byteLength); + const bytes = this.#range(this.#u32(address), byteLength); + const message = decoder.decode(bytes.subarray(0, MAX_LOG_BYTES)); + if (message) { + this.emit({ type: "log", message }); + } + } catch { + return errno(EFAULT); + } + return length; + } + + #seekFile(fdValue, offsetValue, whence) { + const fd = this.#fdNumber(fdValue); + const file = fd === null ? null : this.fds.get(fd); + if (!file) { + return errno(EBADF); + } + const offset = BigInt.asIntN(64, offsetValue); + const fileLength = BigInt(file.bytes.byteLength); + if (whence === 0n) { + file.position = BigInt.asUintN(64, offset); + } else if (whence === 1n) { + file.position = BigInt.asUintN( + 64, + BigInt.asIntN(64, file.position) + offset, + ); + if (file.position > fileLength) { + file.position = fileLength; + } + } else if (whence === 2n) { + file.position = BigInt.asUintN( + 64, + BigInt.asIntN(64, fileLength) + offset, + ); + if (file.position > fileLength) { + file.position = fileLength; + } + } else { + return errno(EINVAL); + } + return file.position; + } + } + + globalThis.TranslatedPvmRuntime = TranslatedPvmRuntime; +})(); + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const FRAME_INTERVAL_MS = 1000 / 60; +const MAX_GAS_PER_UPDATE = 500_000_000; +const MAX_TRANSLATED_LOOPS_PER_UPDATE = 50_000_000; +const decoder = new TextDecoder(); +const encoder = new TextEncoder(); + +let pvm; +let translated; +let backend = "interpreter"; +let running = false; +let timer; +let startedAt = 0; +let updateCount = 0; +const updateSamples = []; +const tickChannel = new MessageChannel(); +tickChannel.port1.onmessage = tick; + +function errorText() { + const pointer = pvm.pvm_browser_error_pointer(); + const length = pvm.pvm_browser_error_length(); + return decoder.decode(new Uint8Array(pvm.memory.buffer, pointer, length)); +} + +function check(status, operation) { + if (status !== 0) { + throw new Error(`${operation}: ${errorText()}`); + } +} + +function stage(bytes) { + const source = + bytes instanceof Uint8Array + ? bytes + : new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); + const pointer = pvm.pvm_browser_staging_reserve(source.byteLength); + if (!pointer) { + throw new Error(`reserve browser runtime memory: ${errorText()}`); + } + new Uint8Array(pvm.memory.buffer, pointer, source.byteLength).set(source); +} + +function addAsset(asset) { + const path = encoder.encode(asset.path.replace(/^\/+/, "")); + const bytes = new Uint8Array(asset.bytes); + const packed = new Uint8Array(path.byteLength + bytes.byteLength); + packed.set(path); + packed.set(bytes, path.byteLength); + stage(packed); + check( + pvm.pvm_browser_launch_add_asset(path.byteLength), + `mount browser asset ${asset.path}`, + ); +} + +function drainFrame() { + if (!pvm.pvm_browser_take_frame()) { + return; + } + const width = pvm.pvm_browser_frame_width(); + const height = pvm.pvm_browser_frame_height(); + const length = pvm.pvm_browser_frame_length(); + const source = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_frame_pointer(), + length, + ); + const pixels = new Uint8Array(length); + for (let index = 0; index < length; index += 4) { + pixels[index] = source[index + 2]; + pixels[index + 1] = source[index + 1]; + pixels[index + 2] = source[index]; + pixels[index + 3] = source[index + 3]; + } + postMessage({ type: "frame", width, height, pixels }, [pixels.buffer]); +} + +function drainTri2d() { + if (!pvm.pvm_browser_take_tri2d?.()) { + return; + } + const length = pvm.pvm_browser_tri2d_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_tri2d_pointer(), + length, + ).slice(); + postMessage({ type: "tri2d", bytes }, [bytes.buffer]); +} + +function drainGpuBatches() { + while (pvm.pvm_browser_take_gpu_batch?.()) { + const length = pvm.pvm_browser_gpu_batch_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_gpu_batch_pointer(), + length, + ).slice(); + postMessage({ type: "gpu-batch", bytes }, [bytes.buffer]); + } +} + +function drainAudio() { + while (pvm.pvm_browser_take_audio()) { + const sampleRate = pvm.pvm_browser_audio_sample_rate(); + const channels = pvm.pvm_browser_audio_channels(); + const length = pvm.pvm_browser_audio_length() * 2; + const samples = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_audio_pointer(), + length, + ).slice(); + postMessage({ type: "audio", sampleRate, channels, samples }, [ + samples.buffer, + ]); + } +} + +function drainSave() { + while (pvm.pvm_browser_take_save()) { + const length = pvm.pvm_browser_save_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_save_pointer(), + length, + ).slice(); + postMessage({ type: "save", bytes }, [bytes.buffer]); + } +} + +function drainLogs() { + while (pvm.pvm_browser_take_log()) { + const pointer = pvm.pvm_browser_log_pointer(); + const length = pvm.pvm_browser_log_length(); + const message = decoder.decode( + new Uint8Array(pvm.memory.buffer, pointer, length), + ); + postMessage({ type: "log", message }); + } +} + +function tick() { + if (!running) { + return; + } + const before = performance.now(); + try { + if (translated) { + translated.update(before - startedAt); + } else { + check( + pvm.pvm_browser_update(before - startedAt), + "update PolkaVM browser guest", + ); + drainFrame(); + drainTri2d(); + drainGpuBatches(); + drainAudio(); + drainSave(); + drainLogs(); + } + } catch (error) { + running = false; + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + return; + } + const elapsed = performance.now() - before; + updateCount++; + updateSamples.push(elapsed); + if (updateSamples.length > 600) { + updateSamples.shift(); + } + if (updateCount % 120 === 0) { + const sorted = [...updateSamples].sort((a, b) => a - b); + const percentile = (value) => + sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * value))]; + postMessage({ + type: "metrics", + updates: updateCount, + updateP50Ms: percentile(0.5), + updateP95Ms: percentile(0.95), + updateMaxMs: sorted[sorted.length - 1], + }); + } + const delay = FRAME_INTERVAL_MS - elapsed; + if (delay > 0) { + timer = setTimeout(tick, delay); + } else { + tickChannel.port2.postMessage(null); + } +} + +async function start(message) { + if (pvm || running) { + throw new Error("PolkaVM browser worker is already started"); + } + const bootStarted = performance.now(); + let translationMs = 0; + let compilationMs = 0; + let translatedWasmBytes = 0; + let cacheHit = false; + const instantiated = await WebAssembly.instantiate(message.runtime, {}); + pvm = instantiated.instance.exports; + if (pvm.pvm_browser_abi_version() !== 1) { + throw new Error("PolkaVM browser runtime has an incompatible ABI"); + } + const program = new Uint8Array(message.program); + stage(program); + const pendingOutputs = []; + try { + let module = message.compiledModule; + let bytes = + message.compiledBytes instanceof ArrayBuffer + ? new Uint8Array(message.compiledBytes) + : null; + cacheHit = module instanceof WebAssembly.Module || bytes !== null; + if (!(module instanceof WebAssembly.Module)) { + if (bytes === null) { + const translationStarted = performance.now(); + check( + pvm.pvm_browser_translate_staged(), + "translate PolkaVM browser guest", + ); + translationMs = performance.now() - translationStarted; + const pointer = pvm.pvm_browser_translation_pointer(); + const length = pvm.pvm_browser_translation_length(); + bytes = new Uint8Array(pvm.memory.buffer, pointer, length).slice(); + const persistent = bytes.slice(); + postMessage( + { type: "translated", cacheKey: message.cacheKey, bytes: persistent }, + [persistent.buffer], + ); + } + translatedWasmBytes = bytes.byteLength; + const compilationStarted = performance.now(); + module = await WebAssembly.compile(bytes); + compilationMs = performance.now() - compilationStarted; + try { + postMessage({ type: "compiled", cacheKey: message.cacheKey, module }); + } catch {} + } + translated = new globalThis.TranslatedPvmRuntime( + module, + message.assets, + (output, transfers = []) => { + if (running) { + postMessage(output, transfers); + } else { + pendingOutputs.push({ output, transfers }); + } + }, + MAX_TRANSLATED_LOOPS_PER_UPDATE, + message.audioEnabled, + message.gpuCapabilities instanceof ArrayBuffer + ? new Uint8Array(message.gpuCapabilities) + : null, + ); + translated.initialize(); + backend = "compiler"; + } catch (error) { + translated = null; + pendingOutputs.length = 0; + console.warn(`PolkaVM translation failed; using interpreter: ${error}`); + const presentation = + message.graphicsProfile === "tri2d" + ? 1 + : message.graphicsProfile === "webgpu-raster" + ? 2 + : 0; + const begin = pvm.pvm_browser_launch_begin_v2; + if (typeof begin !== "function") { + throw new Error("PolkaVM interpreter does not support graphics profiles"); + } + stage(program); + check( + begin( + MAX_GAS_PER_UPDATE, + message.audioEnabled ? 1 : 0, + presentation, + ), + "begin PolkaVM browser launch", + ); + for (const asset of message.assets) { + addAsset(asset); + } + check(pvm.pvm_browser_launch_start(), "start PolkaVM browser launch"); + if (message.graphicsProfile === "webgpu-raster") { + if (!(message.gpuCapabilities instanceof ArrayBuffer)) { + throw new Error("WebGPU capabilities are required before PVM initialization"); + } + stage(new Uint8Array(message.gpuCapabilities)); + check( + pvm.pvm_browser_set_gpu_capabilities(), + "set PolkaVM browser GPU capabilities", + ); + } + try { + check(pvm.pvm_browser_init(), "initialize PolkaVM browser guest"); + } catch (initError) { + drainLogs(); + throw initError; + } + drainTri2d(); + drainGpuBatches(); + drainLogs(); + } + startedAt = performance.now(); + running = true; + postMessage({ + type: "ready", + backend, + cacheHit, + translationMs, + compilationMs, + translatedWasmBytes, + startupMs: performance.now() - bootStarted, + }); + for (const { output, transfers } of pendingOutputs) { + postMessage(output, transfers); + } + tick(); +} + +function sendInput(bytes) { + if (!running || bytes.byteLength !== 8) { + return; + } + if (translated) { + translated.sendInput(bytes); + return; + } + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + check( + pvm.pvm_browser_send_input( + bytes[0], + bytes[1], + view.getUint16(2, true), + view.getUint16(4, true), + ), + "send PolkaVM browser input", + ); +} + +function sendGpuEvent(bytes) { + if (!running || !pvm || !bytes.byteLength) { + return; + } + if (translated) { + translated.sendGpuEvent(bytes); + return; + } + stage(bytes); + check(pvm.pvm_browser_send_gpu_event(), "send PolkaVM browser GPU event"); +} + +onmessage = (event) => { + const message = event.data; + if (message?.type === "start") { + void start(message).catch((error) => { + running = false; + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + }); + } else if (message?.type === "input") { + try { + sendInput(new Uint8Array(message.bytes)); + } catch (error) { + running = false; + clearTimeout(timer); + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + } + } else if (message?.type === "gpu-event") { + try { + sendGpuEvent(new Uint8Array(message.bytes)); + } catch (error) { + running = false; + clearTimeout(timer); + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + } + } else if (message?.type === "stop") { + running = false; + clearTimeout(timer); + translated?.stop(); + pvm?.pvm_browser_reset(); + postMessage({ type: "terminated" }); + } +}; diff --git a/js/packages/pvm-browser-runtime/package.json b/js/packages/pvm-browser-runtime/package.json new file mode 100644 index 000000000..01b962ef4 --- /dev/null +++ b/js/packages/pvm-browser-runtime/package.json @@ -0,0 +1,28 @@ +{ + "name": "@parity/pvm-browser-runtime", + "version": "0.1.0", + "description": "Host-neutral PolkaVM App v2 browser runtime artifacts", + "license": "MPL-2.0", + "type": "module", + "files": [ + "dist", + "LICENSE-MPL-2.0", + "SOURCE" + ], + "scripts": { + "build": "node scripts/build.mjs", + "test": "npm run build && node --test test/*.test.mjs" + }, + "exports": { + "./runtime.wasm": "./dist/pvm-browser-runtime.wasm", + "./worker": "./dist/pvm-worker.js", + "./gpu-worker": "./dist/pvm-gpu-worker.js", + "./checksums": "./dist/SHA256SUMS" + }, + "engines": { + "node": ">=22" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/js/packages/pvm-browser-runtime/scripts/build.mjs b/js/packages/pvm-browser-runtime/scripts/build.mjs new file mode 100644 index 000000000..165587ded --- /dev/null +++ b/js/packages/pvm-browser-runtime/scripts/build.mjs @@ -0,0 +1,61 @@ +import { createHash } from "node:crypto"; +import { + copyFile, + mkdir, + readFile, + rm, + writeFile, +} from "node:fs/promises"; +import { spawnSync } from "node:child_process"; +import { resolve } from "node:path"; + +const packageRoot = resolve(import.meta.dirname, ".."); +const repositoryRoot = resolve(packageRoot, "../../.."); +const dist = resolve(packageRoot, "dist"); +const source = resolve(packageRoot, "src"); +const wasm = + process.env.PVM_RUNTIME_WASM ?? + resolve( + repositoryRoot, + "target/wasm32-unknown-unknown/release/pvm_runtime.wasm", + ); + +if (process.env.PVM_RUNTIME_WASM === undefined) { + const build = spawnSync( + "cargo", + [ + "build", + "--locked", + "--release", + "--target", + "wasm32-unknown-unknown", + "-p", + "pvm-runtime", + ], + { cwd: repositoryRoot, stdio: "inherit" }, + ); + if (build.status !== 0) process.exit(build.status ?? 1); +} + +await rm(dist, { recursive: true, force: true }); +await mkdir(dist, { recursive: true }); +const translated = await readFile(resolve(source, "pvm-wasm-translated.js")); +const workerEntry = await readFile(resolve(source, "pvm-wasm-worker-entry.js")); +await copyFile(wasm, resolve(dist, "pvm-browser-runtime.wasm")); +await copyFile(resolve(source, "pvm-gpu-worker.js"), resolve(dist, "pvm-gpu-worker.js")); +await writeFile( + resolve(dist, "pvm-worker.js"), + Buffer.concat([translated, Buffer.from("\n"), workerEntry]), +); + +const files = [ + "pvm-browser-runtime.wasm", + "pvm-worker.js", + "pvm-gpu-worker.js", +]; +const sums = []; +for (const file of files) { + const bytes = await readFile(resolve(dist, file)); + sums.push(`${createHash("sha256").update(bytes).digest("hex")} ${file}`); +} +await writeFile(resolve(dist, "SHA256SUMS"), `${sums.join("\n")}\n`); diff --git a/js/packages/pvm-browser-runtime/src/pvm-gpu-worker.js b/js/packages/pvm-browser-runtime/src/pvm-gpu-worker.js new file mode 100644 index 000000000..375807456 --- /dev/null +++ b/js/packages/pvm-browser-runtime/src/pvm-gpu-worker.js @@ -0,0 +1,1884 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* global GPUBufferUsage, GPUMapMode, GPUTextureUsage */ + +"use strict"; + +const WIRE_VERSION = 1; +const BATCH_HEADER_BYTES = 24; +const COMMAND_HEADER_BYTES = 8; +const EVENT_HEADER_BYTES = 24; +const MAX_BATCH_BYTES = 4 * 1024 * 1024; +const MAX_COMMANDS = 16_384; +const MAX_DIAGNOSTIC_BYTES = 8 * 1024; +const HANDLE_SLOT_MASK = (1 << 20) - 1; +const HANDLE_LIVE_BIT = 1 << 12; +const MAX_COMPILATIONS_PER_BATCH = 8; +const MAX_PENDING_BATCHES = 4; +const BATCH_ERROR_STALE_SURFACE = 4; +const MAX_RENDER_PASSES_PER_BATCH = 16; +const MAX_DRAWS_PER_BATCH = 8_192; +const MAX_TOTAL_BUFFER_BYTES = 64 * 1024 * 1024; +const MAX_TOTAL_TEXTURE_BYTES = 256 * 1024 * 1024; +const resourceLimits = new Map([ + ["buffer", 4_096], + ["texture", 512], + ["textureView", 1_024], + ["sampler", 128], + ["shader", 128], + ["bindGroupLayout", 128], + ["pipelineLayout", 64], + ["bindGroup", 512], + ["pipeline", 256], +]); +const bindGroupResourceKinds = new Map([ + [1, "buffer"], + [2, "sampler"], + [3, "textureView"], +]); +const textEncoder = new TextEncoder(); +const textDecoder = new TextDecoder("utf-8", { fatal: true }); + +const formats = new Map([ + [1, "rgba8unorm"], + [2, "rgba8unorm-srgb"], + [3, "bgra8unorm"], + [4, "bgra8unorm-srgb"], + [5, "depth24plus"], + [6, "depth32float"], +]); +const formatIds = new Map([...formats].map(([id, format]) => [format, id])); +const vertexFormats = new Map([ + [1, "float32"], + [2, "float32x2"], + [3, "float32x3"], + [4, "float32x4"], + [5, "uint32"], + [6, "uint32x2"], + [7, "uint32x4"], + [8, "unorm8x2"], + [9, "unorm8x4"], + [10, "snorm8x2"], + [11, "snorm8x4"], +]); +const indexFormats = new Map([ + [1, "uint16"], + [2, "uint32"], +]); +const addressModes = new Map([ + [1, "clamp-to-edge"], + [2, "repeat"], + [3, "mirror-repeat"], +]); +const filterModes = new Map([ + [1, "nearest"], + [2, "linear"], +]); +const compareFunctions = new Map([ + [1, "never"], + [2, "less"], + [3, "equal"], + [4, "less-equal"], + [5, "greater"], + [6, "not-equal"], + [7, "greater-equal"], + [8, "always"], +]); +const blendOperations = new Map([ + [1, "add"], + [2, "subtract"], + [3, "reverse-subtract"], + [4, "min"], + [5, "max"], +]); +const blendFactors = new Map([ + [1, "zero"], + [2, "one"], + [3, "src"], + [4, "one-minus-src"], + [5, "src-alpha"], + [6, "one-minus-src-alpha"], + [7, "dst"], + [8, "one-minus-dst"], + [9, "dst-alpha"], + [10, "one-minus-dst-alpha"], + [11, "src-alpha-saturated"], + [12, "constant"], + [13, "one-minus-constant"], +]); +const topologies = new Map([ + [1, "point-list"], + [2, "line-list"], + [3, "line-strip"], + [4, "triangle-list"], + [5, "triangle-strip"], +]); +const frontFaces = new Map([ + [1, "ccw"], + [2, "cw"], +]); +const cullModes = new Map([ + [0, "none"], + [1, "front"], + [2, "back"], +]); +const samplerBindingTypes = new Map([ + [1, "filtering"], + [2, "non-filtering"], + [3, "comparison"], +]); +const textureSampleTypes = new Map([ + [1, "float"], + [2, "unfilterable-float"], + [3, "depth"], + [4, "sint"], + [5, "uint"], +]); +const vertexStepModes = new Map([ + [1, "vertex"], + [2, "instance"], +]); +const textureAspects = new Map([ + [1, "all"], + [2, "depth-only"], +]); + +class ProtocolError extends Error { + constructor(message, commandIndex = 0xffffffff, errorCode = 1) { + super(message); + this.commandIndex = commandIndex; + this.errorCode = errorCode; + } +} + +class Reader { + constructor(bytes) { + if (!(bytes instanceof Uint8Array)) { + throw new ProtocolError("GPU payload is not bytes"); + } + this.bytes = bytes; + this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + this.offset = 0; + } + + take(length) { + const end = this.offset + length; + if (!Number.isSafeInteger(end) || end > this.bytes.byteLength) { + throw new ProtocolError("GPU payload is truncated"); + } + const value = this.bytes.subarray(this.offset, end); + this.offset = end; + return value; + } + + u8() { + return this.take(1)[0]; + } + + u16() { + const offset = this.offset; + this.take(2); + return this.view.getUint16(offset, true); + } + + u32() { + const offset = this.offset; + this.take(4); + return this.view.getUint32(offset, true); + } + + i32() { + const offset = this.offset; + this.take(4); + return this.view.getInt32(offset, true); + } + + u64() { + const offset = this.offset; + this.take(8); + const value = this.view.getBigUint64(offset, true); + if (value > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new ProtocolError("GPU integer exceeds the safe range"); + } + return Number(value); + } + + f32() { + const offset = this.offset; + this.take(4); + const value = this.view.getFloat32(offset, true); + if (!Number.isFinite(value)) { + throw new ProtocolError("GPU descriptor contains non-finite float"); + } + return value; + } + + text(length) { + return textDecoder.decode(this.take(length)); + } + + zero(length) { + if (this.take(length).some(byte => byte !== 0)) { + throw new ProtocolError("GPU descriptor has nonzero reserved bytes"); + } + } + + finish() { + if (this.offset !== this.bytes.byteLength) { + throw new ProtocolError("GPU descriptor has trailing bytes"); + } + } +} + +function mapped(table, value, label) { + const result = table.get(value); + if (result === undefined) { + throw new ProtocolError(`unsupported GPU ${label} ${value}`); + } + return result; +} + +function validHandle(id) { + return id !== 0 && (id & HANDLE_SLOT_MASK) !== 0 && id >>> 20 !== 0; +} + +function resource(catalog, id, kind, commandIndex) { + const entry = catalog.get(id); + if (!entry || (kind && entry.kind !== kind) || entry.failed) { + throw new ProtocolError( + `invalid ${kind || "resource"} handle ${id}`, + commandIndex + ); + } + return entry; +} + +function createResource(catalog, slots, id, kind, descriptor, commandIndex) { + if (!validHandle(id) || catalog.has(id)) { + throw new ProtocolError(`invalid new ${kind} handle ${id}`, commandIndex); + } + const slot = id & HANDLE_SLOT_MASK; + const generation = id >>> 20; + const prior = slots.get(slot); + if ( + (prior === undefined && generation !== 1) || + (prior !== undefined && + ((prior & HANDLE_LIVE_BIT) !== 0 || + generation !== (prior & ~HANDLE_LIVE_BIT) + 1)) + ) { + throw new ProtocolError(`stale new ${kind} handle ${id}`, commandIndex); + } + const entry = { kind, descriptor, value: null, failed: false }; + catalog.set(id, entry); + slots.set(slot, generation | HANDLE_LIVE_BIT); + return entry; +} + +function textureByteLength(descriptor) { + let width = descriptor.width; + let height = descriptor.height; + let total = 0; + for (let level = 0; level < descriptor.mipLevelCount; level++) { + const bytes = width * height * 4; + if ( + !Number.isSafeInteger(bytes) || + total > MAX_TOTAL_TEXTURE_BYTES - bytes + ) { + return MAX_TOTAL_TEXTURE_BYTES + 1; + } + total += bytes; + width = Math.max(1, Math.floor(width / 2)); + height = Math.max(1, Math.floor(height / 2)); + } + return total; +} + +function adjustResourceStats(stats, entry, delta, commandIndex = 0xffffffff) { + const count = (stats.counts.get(entry.kind) || 0) + delta; + const limit = resourceLimits.get(entry.kind); + if (count < 0 || limit === undefined || count > limit) { + throw new ProtocolError( + `GPU ${entry.kind} resource quota exceeded`, + commandIndex + ); + } + stats.counts.set(entry.kind, count); + if (entry.kind === "buffer") { + stats.bufferBytes += delta * entry.descriptor.size; + if (stats.bufferBytes < 0 || stats.bufferBytes > MAX_TOTAL_BUFFER_BYTES) { + throw new ProtocolError("GPU buffer memory quota exceeded", commandIndex); + } + } else if (entry.kind === "texture") { + stats.textureBytes += delta * textureByteLength(entry.descriptor); + if ( + stats.textureBytes < 0 || + stats.textureBytes > MAX_TOTAL_TEXTURE_BYTES + ) { + throw new ProtocolError( + "GPU texture memory quota exceeded", + commandIndex + ); + } + } +} + +function resourceStats(catalog) { + const stats = { counts: new Map(), bufferBytes: 0, textureBytes: 0 }; + for (const entry of catalog.values()) { + adjustResourceStats(stats, entry, 1); + } + return stats; +} + +function createBoundedResource( + catalog, + slots, + stats, + creations, + id, + kind, + descriptor, + commandIndex +) { + const entry = createResource( + catalog, + slots, + id, + kind, + descriptor, + commandIndex + ); + const creationCount = (creations.counts.get(kind) || 0) + 1; + if (creationCount > resourceLimits.get(kind)) { + throw new ProtocolError( + `too many GPU ${kind} creations in one batch`, + commandIndex + ); + } + creations.counts.set(kind, creationCount); + if (kind === "buffer") { + creations.bufferBytes += descriptor.size; + if (creations.bufferBytes > MAX_TOTAL_BUFFER_BYTES) { + throw new ProtocolError( + "GPU buffer allocation budget exceeded", + commandIndex + ); + } + } else if (kind === "texture") { + creations.textureBytes += textureByteLength(descriptor); + if (creations.textureBytes > MAX_TOTAL_TEXTURE_BYTES) { + throw new ProtocolError( + "GPU texture allocation budget exceeded", + commandIndex + ); + } + } else if (kind === "shader" || kind === "pipeline") { + creations.compilations++; + if (creations.compilations > MAX_COMPILATIONS_PER_BATCH) { + throw new ProtocolError( + "too many GPU compilations in one batch", + commandIndex + ); + } + } + adjustResourceStats(stats, entry, 1, commandIndex); + return entry; +} + +function parseCommands(bytes) { + if ( + bytes.byteLength < BATCH_HEADER_BYTES || + bytes.byteLength > MAX_BATCH_BYTES + ) { + throw new ProtocolError("GPU batch length is invalid"); + } + const reader = new Reader(bytes); + if ( + textDecoder.decode(reader.take(4)) !== "EPG1" || + reader.u16() !== WIRE_VERSION + ) { + throw new ProtocolError("GPU batch header is invalid"); + } + reader.zero(2); + if (reader.u32() !== bytes.byteLength) { + throw new ProtocolError("GPU batch length does not match its header"); + } + const commandCount = reader.u32(); + const sequence = reader.u64(); + if (!sequence || commandCount > MAX_COMMANDS) { + throw new ProtocolError("GPU batch sequence or command count is invalid"); + } + const commands = []; + for (let index = 0; index < commandCount; index++) { + const opcode = reader.u16(); + reader.zero(2); + const commandBytes = reader.u32(); + if (commandBytes < COMMAND_HEADER_BYTES || commandBytes % 4) { + throw new ProtocolError("GPU command length is invalid", index); + } + const payload = reader.take(commandBytes - COMMAND_HEADER_BYTES); + commands.push({ opcode, payload, index }); + } + reader.finish(); + return { sequence, commands }; +} + +// Parsing stays centralized so every opcode's exact wire shape is auditable. +// eslint-disable-next-line complexity +function parseCommand(command) { + const reader = new Reader(command.payload); + const result = { opcode: command.opcode, index: command.index }; + switch (command.opcode) { + case 1: + Object.assign(result, { + id: reader.u32(), + usage: reader.u32(), + size: reader.u64(), + }); + break; + case 2: { + result.id = reader.u32(); + reader.zero(4); + result.offset = reader.u64(); + const length = reader.u32(); + reader.zero(4); + result.data = reader.take(length).slice(); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 3: + Object.assign(result, { + id: reader.u32(), + width: reader.u32(), + height: reader.u32(), + mipLevelCount: reader.u16(), + sampleCount: reader.u16(), + format: mapped(formats, reader.u16(), "texture format"), + }); + if (reader.u8() !== 1) { + throw new ProtocolError("unsupported texture dimension", command.index); + } + reader.zero(1); + result.usage = reader.u32(); + break; + case 4: { + Object.assign(result, { + id: reader.u32(), + mipLevel: reader.u32(), + origin: { x: reader.u32(), y: reader.u32(), z: reader.u32() }, + size: { + width: reader.u32(), + height: reader.u32(), + depthOrArrayLayers: reader.u32(), + }, + bytesPerRow: reader.u32(), + rowsPerImage: reader.u32(), + }); + const length = reader.u32(); + result.data = reader.take(length).slice(); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 5: + Object.assign(result, { + id: reader.u32(), + addressModeU: mapped(addressModes, reader.u8(), "address mode"), + addressModeV: mapped(addressModes, reader.u8(), "address mode"), + addressModeW: mapped(addressModes, reader.u8(), "address mode"), + magFilter: mapped(filterModes, reader.u8(), "filter mode"), + minFilter: mapped(filterModes, reader.u8(), "filter mode"), + mipmapFilter: mapped(filterModes, reader.u8(), "filter mode"), + }); + { + const compare = reader.u8(); + result.compare = compare + ? mapped(compareFunctions, compare, "compare function") + : undefined; + } + result.maxAnisotropy = reader.u8(); + result.lodMinClamp = reader.f32(); + result.lodMaxClamp = reader.f32(); + reader.zero(4); + break; + case 6: { + result.id = reader.u32(); + const length = reader.u32(); + result.source = reader.text(length); + reader.zero(reader.bytes.byteLength - reader.offset); + break; + } + case 7: { + result.id = reader.u32(); + const count = reader.u32(); + result.entries = []; + for (let i = 0; i < count; i++) { + const binding = reader.u32(); + const visibility = reader.u32(); + const kind = reader.u16(); + const flags = reader.u16(); + reader.zero(4); + const minBindingSize = reader.u64(); + const parameter0 = reader.u32(); + const parameter1 = reader.u32(); + let entry; + if (kind === 1) { + if (parameter0 || parameter1 || flags & ~1) { + throw new ProtocolError( + "invalid buffer binding layout", + command.index + ); + } + entry = { + binding, + visibility, + buffer: { + type: "uniform", + hasDynamicOffset: Boolean(flags & 1), + minBindingSize, + }, + }; + } else if (kind === 2) { + if (flags || minBindingSize || parameter1) { + throw new ProtocolError( + "invalid sampler binding layout", + command.index + ); + } + entry = { + binding, + visibility, + sampler: { + type: mapped( + samplerBindingTypes, + parameter0, + "sampler binding type" + ), + }, + }; + } else if (kind === 3) { + if (flags || minBindingSize) { + throw new ProtocolError( + "invalid texture binding layout", + command.index + ); + } + entry = { + binding, + visibility, + texture: { + sampleType: mapped( + textureSampleTypes, + parameter0, + "texture sample type" + ), + viewDimension: + parameter1 === 1 + ? "2d" + : (() => { + throw new ProtocolError( + "unsupported texture view dimension", + command.index + ); + })(), + multisampled: false, + }, + }; + } else { + throw new ProtocolError( + "unsupported bind group layout entry", + command.index + ); + } + result.entries.push(entry); + } + break; + } + case 8: { + result.id = reader.u32(); + const count = reader.u32(); + result.layouts = Array.from({ length: count }, () => reader.u32()); + break; + } + case 9: { + result.id = reader.u32(); + result.layout = reader.u32(); + const count = reader.u32(); + result.entries = []; + for (let i = 0; i < count; i++) { + const binding = reader.u32(); + const resourceId = reader.u32(); + const kind = reader.u16(); + if (kind !== 1 && kind !== 2 && kind !== 3) { + throw new ProtocolError( + "unsupported bind group entry kind", + command.index + ); + } + reader.zero(2); + reader.zero(4); + const offset = reader.u64(); + const size = reader.u64(); + result.entries.push({ binding, resourceId, kind, offset, size }); + } + break; + } + case 10: { + Object.assign(result, { + id: reader.u32(), + layout: reader.u32(), + shader: reader.u32(), + }); + const layoutCount = reader.u16(); + const attributeCount = reader.u16(); + const targetCount = reader.u16(); + result.flags = reader.u16(); + result.depthFormatId = reader.u16(); + result.sampleCount = reader.u16(); + result.topology = mapped(topologies, reader.u8(), "primitive topology"); + result.frontFace = mapped(frontFaces, reader.u8(), "front face"); + result.cullMode = mapped(cullModes, reader.u8(), "cull mode"); + const stripIndex = reader.u8(); + result.stripIndexFormat = stripIndex + ? mapped(indexFormats, stripIndex, "strip index format") + : undefined; + const depthCompare = reader.u8(); + result.depthCompare = depthCompare + ? mapped(compareFunctions, depthCompare, "depth compare") + : undefined; + reader.zero(11); + result.layouts = []; + for (let i = 0; i < layoutCount; i++) { + result.layouts.push({ + arrayStride: reader.u64(), + stepMode: mapped(vertexStepModes, reader.u8(), "vertex step mode"), + firstAttribute: (reader.zero(3), reader.u16()), + attributeCount: reader.u16(), + }); + } + result.attributes = []; + for (let i = 0; i < attributeCount; i++) { + result.attributes.push({ + format: mapped(vertexFormats, reader.u16(), "vertex format"), + shaderLocation: reader.u16(), + offset: reader.u64(), + }); + reader.zero(4); + } + result.targets = []; + for (let i = 0; i < targetCount; i++) { + const format = mapped(formats, reader.u16(), "color target format"); + const writeMask = reader.u16(); + const color = { + operation: mapped(blendOperations, reader.u8(), "blend operation"), + srcFactor: mapped(blendFactors, reader.u8(), "blend factor"), + dstFactor: mapped(blendFactors, reader.u8(), "blend factor"), + }; + const alpha = { + operation: mapped(blendOperations, reader.u8(), "blend operation"), + srcFactor: mapped(blendFactors, reader.u8(), "blend factor"), + dstFactor: mapped(blendFactors, reader.u8(), "blend factor"), + }; + reader.zero(6); + result.targets.push({ format, writeMask, blend: { color, alpha } }); + } + break; + } + case 11: + case 13: + result.id = reader.u32(); + break; + case 12: + Object.assign(result, { + colorView: reader.u32(), + depthView: reader.u32(), + surfaceGeneration: reader.u32(), + flags: reader.u32(), + clearColor: { + r: reader.f32(), + g: reader.f32(), + b: reader.f32(), + a: reader.f32(), + }, + clearDepth: reader.f32(), + }); + break; + case 14: + Object.assign(result, { + slot: reader.u32(), + buffer: reader.u32(), + offset: reader.u64(), + size: reader.u64(), + }); + break; + case 15: + Object.assign(result, { + buffer: reader.u32(), + format: mapped(indexFormats, reader.u32(), "index format"), + offset: reader.u64(), + size: reader.u64(), + }); + break; + case 16: { + result.bindIndex = reader.u32(); + result.bindGroup = reader.u32(); + const count = reader.u32(); + result.dynamicOffsets = Array.from({ length: count }, () => reader.u32()); + break; + } + case 17: + result.values = Array.from({ length: 6 }, () => reader.f32()); + break; + case 18: + case 19: + result.values = Array.from({ length: 4 }, () => reader.u32()); + break; + case 20: + result.values = [ + reader.u32(), + reader.u32(), + reader.u32(), + reader.i32(), + reader.u32(), + ]; + break; + case 21: + break; + case 22: + Object.assign(result, { + source: reader.u32(), + destination: reader.u32(), + sourceOffset: reader.u64(), + destinationOffset: reader.u64(), + size: reader.u64(), + }); + break; + case 23: + Object.assign(result, { + id: reader.u32(), + texture: reader.u32(), + format: mapped(formats, reader.u16(), "texture view format"), + }); + if (reader.u8() !== 1) { + throw new ProtocolError( + "unsupported texture view dimension", + command.index + ); + } + result.aspect = mapped(textureAspects, reader.u8(), "texture aspect"); + result.baseMipLevel = reader.u16(); + result.mipLevelCount = reader.u16(); + result.baseArrayLayer = reader.u16(); + result.arrayLayerCount = reader.u16(); + break; + default: + throw new ProtocolError( + `unsupported GPU opcode ${command.opcode}`, + command.index + ); + } + reader.finish(); + return result; +} + +class GpuEngine { + constructor( + canvas, + device, + context, + format, + limits, + dimensions, + testReadback, + testDeviceLoss + ) { + this.canvas = canvas; + this.device = device; + this.context = context; + this.format = format; + this.formatId = formatIds.get(format); + this.limits = limits; + this.resources = new Map(); + this.handleSlots = new Map(); + this.surfaceGeneration = 1; + this.deviceGeneration = 1; + this.lastSequence = 0; + this.stopped = false; + this.queue = Promise.resolve(); + this.pendingBatches = 0; + this.testReadbacksRemaining = testReadback ? 8 : 0; + this.testDeviceLossPending = testDeviceLoss; + this.pendingResize = null; + this.resizeScheduled = false; + this.resize(dimensions, false); + device.addEventListener("uncapturederror", event => { + this.emitTextEvent( + 4, + 0, + 1, + event.error?.message || "uncaptured WebGPU error" + ); + }); + void device.lost.then(info => { + if (!this.stopped) { + this.stopped = true; + this.emitTextEvent(7, 0, 1, info.message || "WebGPU device lost"); + } + }); + } + + static async create( + canvas, + requirements, + dimensions, + testReadback, + testDeviceLoss + ) { + if (!globalThis.navigator?.gpu) { + throw new Error("WebGPU is unavailable"); + } + const adapter = await navigator.gpu.requestAdapter(); + if (!adapter) { + throw new Error("WebGPU adapter is unavailable"); + } + const ceilings = { + maxTextureDimension2D: 4096, + maxBufferSize: 16 * 1024 * 1024, + maxBindingsPerBindGroup: 16, + maxBindGroups: 4, + maxVertexBuffers: 8, + maxVertexAttributes: 16, + maxColorAttachments: 4, + }; + const requested = {}; + for (const [name, ceiling] of Object.entries(ceilings)) { + const available = Number(adapter.limits[name]); + if (!Number.isFinite(available) || available <= 0) { + throw new Error(`WebGPU adapter omitted ${name}`); + } + requested[name] = Math.min(ceiling, available); + } + for (const [name, minimum] of Object.entries( + requirements.requiredLimits || {} + )) { + if ( + !(name in requested) || + !Number.isSafeInteger(minimum) || + minimum <= 0 || + requested[name] < minimum + ) { + throw new Error(`WebGPU does not satisfy required limit ${name}`); + } + } + if ( + Array.isArray(requirements.requiredFeatures) && + requirements.requiredFeatures.length + ) { + throw new Error( + "optional WebGPU features are unavailable in profile version 1" + ); + } + const device = await adapter.requestDevice({ + requiredFeatures: [], + requiredLimits: requested, + }); + const context = canvas.getContext("webgpu"); + if (!context) { + throw new Error("WebGPU canvas context is unavailable"); + } + const format = navigator.gpu.getPreferredCanvasFormat(); + if (!formatIds.has(format)) { + throw new Error(`unsupported WebGPU surface format ${format}`); + } + const limits = [ + requested.maxTextureDimension2D, + requested.maxBufferSize, + requested.maxBindingsPerBindGroup, + requested.maxBindGroups, + requested.maxVertexBuffers, + requested.maxVertexAttributes, + requested.maxColorAttachments, + 256 * 1024 * 1024, + 64 * 1024 * 1024, + 8192, + MAX_BATCH_BYTES, + 16 * 1024 * 1024, + ]; + return new GpuEngine( + canvas, + device, + context, + format, + limits, + dimensions, + testReadback, + testDeviceLoss + ); + } + + resize(dimensions, notify = true) { + const physicalWidth = Math.max( + 1, + Math.min(this.limits[0], dimensions.physicalWidth >>> 0) + ); + const physicalHeight = Math.max( + 1, + Math.min(this.limits[0], dimensions.physicalHeight >>> 0) + ); + const logicalWidth = Math.max(1, dimensions.logicalWidth >>> 0); + const logicalHeight = Math.max(1, dimensions.logicalHeight >>> 0); + const scale = Number(dimensions.scale); + if (!Number.isFinite(scale) || scale <= 0) { + throw new Error("invalid WebGPU surface scale"); + } + const changed = + this.physicalWidth !== physicalWidth || + this.physicalHeight !== physicalHeight || + this.logicalWidth !== logicalWidth || + this.logicalHeight !== logicalHeight || + this.scale !== scale; + this.physicalWidth = physicalWidth; + this.physicalHeight = physicalHeight; + this.logicalWidth = logicalWidth; + this.logicalHeight = logicalHeight; + this.scale = scale; + this.canvas.width = physicalWidth; + this.canvas.height = physicalHeight; + this.context.configure({ + device: this.device, + format: this.format, + alphaMode: "opaque", + usage: + GPUTextureUsage.RENDER_ATTACHMENT | + (this.testReadbacksRemaining > 0 ? GPUTextureUsage.COPY_SRC : 0), + }); + if (notify && changed) { + this.surfaceGeneration++; + postBytes("capabilities", this.capabilities()); + const payload = new Uint8Array(28); + const view = new DataView(payload.buffer); + for (const [offset, value] of [ + [0, this.surfaceGeneration], + [4, physicalWidth], + [8, physicalHeight], + [12, logicalWidth], + [16, logicalHeight], + ]) { + view.setUint32(offset, value, true); + } + view.setFloat32(20, scale, true); + view.setUint16(24, this.formatId, true); + postBytes("event", makeEvent(6, 0, payload)); + } + } + + scheduleResize(dimensions) { + this.pendingResize = dimensions; + if (this.resizeScheduled) { + return; + } + this.resizeScheduled = true; + this.queue = this.queue + .then(() => { + const latest = this.pendingResize; + this.pendingResize = null; + this.resizeScheduled = false; + if (!this.stopped && latest) { + this.resize(latest); + } + }) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }); + } + + capabilities() { + const bytes = new Uint8Array(56 + this.limits.length * 16); + const view = new DataView(bytes.buffer); + bytes.set(textEncoder.encode("EGC1"), 0); + view.setUint16(4, WIRE_VERSION, true); + view.setUint32(8, bytes.byteLength, true); + view.setUint16(12, this.formatId, true); + view.setUint32(16, this.physicalWidth, true); + view.setUint32(20, this.physicalHeight, true); + view.setUint32(24, this.logicalWidth, true); + view.setUint32(28, this.logicalHeight, true); + view.setFloat32(32, this.scale, true); + view.setUint32(36, this.surfaceGeneration, true); + view.setUint32(40, this.deviceGeneration, true); + view.setUint32(44, this.limits.length, true); + this.limits.forEach((value, index) => { + const offset = 56 + index * 16; + view.setUint16(offset, index + 1, true); + view.setBigUint64(offset + 4, BigInt(value), true); + }); + return bytes; + } + + submit(bytes) { + if (this.pendingBatches >= MAX_PENDING_BATCHES) { + this.emitBatchRejected(0xffffffff, 3, 0, "GPU submission queue is full"); + return; + } + this.pendingBatches++; + this.queue = this.queue + .then(() => this.execute(bytes)) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }) + .finally(() => { + this.pendingBatches--; + }); + } + + // Validation is one atomic state transition across the complete opcode set. + // eslint-disable-next-line complexity + validate(batch) { + if (batch.sequence <= this.lastSequence) { + throw new ProtocolError("GPU sequence is not increasing"); + } + const shadow = new Map(this.resources); + const slots = new Map(this.handleSlots); + let pass = false; + const stats = resourceStats(shadow); + const creations = { + counts: new Map(), + bufferBytes: 0, + textureBytes: 0, + compilations: 0, + }; + let renderPasses = 0; + let draws = 0; + const commands = batch.commands.map(parseCommand); + for (const command of commands) { + const index = command.index; + switch (command.opcode) { + case 1: + if (!command.size || command.size > this.limits[1]) { + throw new ProtocolError("invalid buffer size", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "buffer", + command, + index + ); + break; + case 2: + resource(shadow, command.id, "buffer", index); + break; + case 3: + if ( + !command.width || + !command.height || + command.width > this.limits[0] || + command.height > this.limits[0] || + !command.mipLevelCount || + command.mipLevelCount > 13 || + command.sampleCount !== 1 + ) { + throw new ProtocolError("invalid texture descriptor", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "texture", + command, + index + ); + break; + case 4: + resource(shadow, command.id, "texture", index); + break; + case 5: + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "sampler", + command, + index + ); + break; + case 6: + if (!command.source.length || command.source.length > 1024 * 1024) { + throw new ProtocolError("invalid WGSL source", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "shader", + command, + index + ); + break; + case 7: + if (command.entries.length > this.limits[2]) { + throw new ProtocolError("too many bind group entries", index); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "bindGroupLayout", + command, + index + ); + break; + case 8: + if (command.layouts.length > this.limits[3]) { + throw new ProtocolError("too many pipeline bind groups", index); + } + command.layouts.forEach(id => + resource(shadow, id, "bindGroupLayout", index) + ); + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "pipelineLayout", + command, + index + ); + break; + case 9: + if (command.entries.length > this.limits[2]) { + throw new ProtocolError("too many bind group entries", index); + } + resource(shadow, command.layout, "bindGroupLayout", index); + for (const entry of command.entries) { + resource( + shadow, + entry.resourceId, + bindGroupResourceKinds.get(entry.kind) || "", + index + ); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "bindGroup", + command, + index + ); + break; + case 10: + resource(shadow, command.layout, "pipelineLayout", index); + resource(shadow, command.shader, "shader", index); + if ( + command.layouts.length > this.limits[4] || + command.attributes.length > this.limits[5] || + command.targets.length > this.limits[6] + ) { + throw new ProtocolError( + "pipeline exceeds negotiated limits", + index + ); + } + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "pipeline", + command, + index + ); + break; + case 11: { + const removed = resource(shadow, command.id, null, index); + adjustResourceStats(stats, removed, -1, index); + shadow.delete(command.id); + slots.set(command.id & HANDLE_SLOT_MASK, command.id >>> 20); + break; + } + case 12: + if (pass) { + throw new ProtocolError("nested render pass", index); + } + if (command.surfaceGeneration !== this.surfaceGeneration) { + throw new ProtocolError( + `render pass surface generation ${command.surfaceGeneration} ` + + `does not match current generation ${this.surfaceGeneration}`, + index, + BATCH_ERROR_STALE_SURFACE + ); + } + if (command.colorView !== 0) { + throw new ProtocolError( + "render pass uses a non-surface color view", + index + ); + } + renderPasses++; + if (renderPasses > MAX_RENDER_PASSES_PER_BATCH) { + throw new ProtocolError("too many render passes", index); + } + if (command.depthView) { + resource(shadow, command.depthView, "textureView", index); + } + pass = true; + break; + case 13: + if (!pass) { + throw new ProtocolError("pipeline set outside render pass", index); + } + resource(shadow, command.id, "pipeline", index); + break; + case 14: + if (command.slot >= this.limits[4]) { + throw new ProtocolError( + "vertex buffer slot exceeds negotiated limits", + index + ); + } + if (!pass) { + throw new ProtocolError( + "vertex buffer set outside render pass", + index + ); + } + resource(shadow, command.buffer, "buffer", index); + break; + case 15: + if (!pass) { + throw new ProtocolError( + "index buffer set outside render pass", + index + ); + } + resource(shadow, command.buffer, "buffer", index); + break; + case 16: + if (command.bindIndex >= this.limits[3]) { + throw new ProtocolError( + "bind group index exceeds negotiated limits", + index + ); + } + if (!pass) { + throw new ProtocolError( + "bind group set outside render pass", + index + ); + } + resource(shadow, command.bindGroup, "bindGroup", index); + break; + case 17: + case 18: + if (!pass) { + throw new ProtocolError( + "render command outside render pass", + index + ); + } + break; + case 19: + case 20: + if (!pass) { + throw new ProtocolError( + "render command outside render pass", + index + ); + } + draws++; + if (draws > MAX_DRAWS_PER_BATCH) { + throw new ProtocolError("too many draw commands", index); + } + break; + case 21: + if (!pass) { + throw new ProtocolError("render pass is not active", index); + } + pass = false; + break; + case 22: + if (pass) { + throw new ProtocolError("buffer copy inside render pass", index); + } + resource(shadow, command.source, "buffer", index); + resource(shadow, command.destination, "buffer", index); + break; + case 23: + resource(shadow, command.texture, "texture", index); + command.resourceEntry = createBoundedResource( + shadow, + slots, + stats, + creations, + command.id, + "textureView", + command, + index + ); + break; + } + } + if (pass) { + throw new ProtocolError("render pass was not ended"); + } + return { commands, shadow, slots }; + } + + // Execution mirrors the validated opcode set and commits it transactionally. + // eslint-disable-next-line complexity + async execute(bytes) { + if (this.stopped) { + return; + } + let batch; + let validated; + try { + batch = parseCommands(bytes); + } catch (error) { + throw new Error(`malformed GPU batch: ${error.message}`); + } + try { + validated = this.validate(batch); + } catch (error) { + this.emitBatchRejected( + error.commandIndex ?? 0xffffffff, + error.errorCode ?? 1, + batch.sequence, + error.message + ); + return; + } + this.device.pushErrorScope("validation"); + this.device.pushErrorScope("out-of-memory"); + const next = new Map(this.resources); + let encoder = null; + let pass = null; + let surfaceView = null; + let surfaceTexture = null; + let readback = null; + const removed = []; + const shaders = []; + const created = []; + try { + for (const command of validated.commands) { + const entry = command.resourceEntry; + switch (command.opcode) { + case 1: + entry.value = this.device.createBuffer({ + size: command.size, + usage: command.usage, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 2: + this.device.queue.writeBuffer( + resource(next, command.id, "buffer", command.index).value, + command.offset, + command.data + ); + break; + case 3: + entry.value = this.device.createTexture({ + size: [command.width, command.height, 1], + mipLevelCount: command.mipLevelCount, + sampleCount: command.sampleCount, + dimension: "2d", + format: command.format, + usage: command.usage, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 4: + this.device.queue.writeTexture( + { + texture: resource(next, command.id, "texture", command.index) + .value, + mipLevel: command.mipLevel, + origin: command.origin, + }, + command.data, + { + offset: 0, + bytesPerRow: command.bytesPerRow, + rowsPerImage: command.rowsPerImage, + }, + command.size + ); + break; + case 5: + entry.value = this.device.createSampler(command); + next.set(command.id, entry); + created.push(entry); + break; + case 6: + entry.value = this.device.createShaderModule({ + code: command.source, + }); + next.set(command.id, entry); + created.push(entry); + shaders.push([entry, command.id]); + break; + case 7: + entry.value = this.device.createBindGroupLayout({ + entries: command.entries, + }); + next.set(command.id, entry); + created.push(entry); + break; + case 8: + entry.value = this.device.createPipelineLayout({ + bindGroupLayouts: command.layouts.map( + id => resource(next, id, "bindGroupLayout", command.index).value + ), + }); + next.set(command.id, entry); + created.push(entry); + break; + case 9: + entry.value = this.device.createBindGroup({ + layout: resource( + next, + command.layout, + "bindGroupLayout", + command.index + ).value, + entries: command.entries.map(item => ({ + binding: item.binding, + resource: + item.kind === 1 + ? { + buffer: resource( + next, + item.resourceId, + "buffer", + command.index + ).value, + offset: item.offset, + size: item.size, + } + : resource( + next, + item.resourceId, + item.kind === 2 ? "sampler" : "textureView", + command.index + ).value, + })), + }); + next.set(command.id, entry); + created.push(entry); + break; + case 10: { + const buffers = command.layouts.map(layout => ({ + arrayStride: layout.arrayStride, + stepMode: layout.stepMode, + attributes: command.attributes.slice( + layout.firstAttribute, + layout.firstAttribute + layout.attributeCount + ), + })); + const descriptor = { + layout: resource( + next, + command.layout, + "pipelineLayout", + command.index + ).value, + vertex: { + module: resource(next, command.shader, "shader", command.index) + .value, + entryPoint: "vs_main", + buffers, + }, + fragment: { + module: resource(next, command.shader, "shader", command.index) + .value, + entryPoint: "fs_main", + targets: command.targets, + }, + primitive: { + topology: command.topology, + frontFace: command.frontFace, + cullMode: command.cullMode, + stripIndexFormat: command.stripIndexFormat, + }, + multisample: { count: command.sampleCount }, + }; + if (command.depthFormatId) { + descriptor.depthStencil = { + format: mapped(formats, command.depthFormatId, "depth format"), + depthWriteEnabled: Boolean(command.flags & 1), + depthCompare: command.depthCompare, + }; + } + entry.value = this.device.createRenderPipeline(descriptor); + next.set(command.id, entry); + created.push(entry); + break; + } + case 11: { + const removedEntry = resource( + next, + command.id, + null, + command.index + ); + removed.push(removedEntry); + next.delete(command.id); + break; + } + case 12: { + encoder ||= this.device.createCommandEncoder(); + const colorAttachment = { + view: (surfaceView ??= (surfaceTexture ??= + this.context.getCurrentTexture()).createView()), + loadOp: command.flags & 1 ? "load" : "clear", + storeOp: command.flags & 2 ? "store" : "discard", + clearValue: command.clearColor, + }; + const descriptor = { colorAttachments: [colorAttachment] }; + if (command.depthView) { + descriptor.depthStencilAttachment = { + view: resource( + next, + command.depthView, + "textureView", + command.index + ).value, + depthLoadOp: command.flags & 4 ? "load" : "clear", + depthStoreOp: command.flags & 8 ? "store" : "discard", + depthClearValue: command.clearDepth, + }; + } + pass = encoder.beginRenderPass(descriptor); + break; + } + case 13: + pass.setPipeline( + resource(next, command.id, "pipeline", command.index).value + ); + break; + case 14: + pass.setVertexBuffer( + command.slot, + resource(next, command.buffer, "buffer", command.index).value, + command.offset, + command.size + ); + break; + case 15: + pass.setIndexBuffer( + resource(next, command.buffer, "buffer", command.index).value, + command.format, + command.offset, + command.size + ); + break; + case 16: + pass.setBindGroup( + command.bindIndex, + resource(next, command.bindGroup, "bindGroup", command.index) + .value, + command.dynamicOffsets + ); + break; + case 17: + pass.setViewport(...command.values); + break; + case 18: + pass.setScissorRect(...command.values); + break; + case 19: + pass.draw(...command.values); + break; + case 20: + pass.drawIndexed(...command.values); + break; + case 21: + pass.end(); + pass = null; + break; + case 22: + encoder ||= this.device.createCommandEncoder(); + encoder.copyBufferToBuffer( + resource(next, command.source, "buffer", command.index).value, + command.sourceOffset, + resource(next, command.destination, "buffer", command.index) + .value, + command.destinationOffset, + command.size + ); + break; + case 23: + entry.value = resource( + next, + command.texture, + "texture", + command.index + ).value.createView({ + format: command.format, + dimension: "2d", + aspect: command.aspect, + baseMipLevel: command.baseMipLevel, + mipLevelCount: command.mipLevelCount, + baseArrayLayer: command.baseArrayLayer, + arrayLayerCount: command.arrayLayerCount, + }); + next.set(command.id, entry); + created.push(entry); + break; + } + } + if (encoder && surfaceTexture && this.testReadbacksRemaining > 0) { + readback = this.device.createBuffer({ + size: 3 * 256, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, + }); + const sampleLocations = [ + [this.physicalWidth >> 1, this.physicalHeight >> 1], + [ + Math.min(8, this.physicalWidth - 1), + Math.min(8, this.physicalHeight - 1), + ], + [Math.max(0, this.physicalWidth - 20), this.physicalHeight >> 1], + ]; + for (const [index, [x, y]] of sampleLocations.entries()) { + encoder.copyTextureToBuffer( + { texture: surfaceTexture, origin: { x, y } }, + { + buffer: readback, + offset: index * 256, + bytesPerRow: 256, + rowsPerImage: 1, + }, + { width: 1, height: 1, depthOrArrayLayers: 1 } + ); + } + } + if (encoder) { + this.device.queue.submit([encoder.finish()]); + } + } catch (error) { + created.forEach(entry => entry.value?.destroy?.()); + readback?.destroy(); + void this.device.popErrorScope(); + void this.device.popErrorScope(); + throw error; + } + const outOfMemory = await this.device.popErrorScope(); + const validation = await this.device.popErrorScope(); + const gpuError = outOfMemory || validation; + if (gpuError) { + readback?.destroy(); + created.forEach(entry => entry.value?.destroy?.()); + this.emitBatchRejected( + 0xffffffff, + outOfMemory ? 2 : 1, + batch.sequence, + gpuError.message + ); + return; + } + this.resources = next; + this.handleSlots = validated.slots; + this.lastSequence = batch.sequence; + removed.forEach(entry => entry.value?.destroy?.()); + shaders.forEach(([entry, handle]) => + this.watchShader(entry, handle, batch.sequence) + ); + await this.device.queue.onSubmittedWorkDone(); + if (readback) { + await readback.mapAsync(GPUMapMode.READ); + const readbackBytes = new Uint8Array(readback.getMappedRange()); + const samples = [0, 256, 512].map(offset => + Array.from(readbackBytes.subarray(offset, offset + 4)) + ); + readback.unmap(); + readback.destroy(); + this.testReadbacksRemaining--; + postMessage({ type: "test-readback", samples }); + } + postBytes("event", makeEvent(5, batch.sequence)); + postMessage({ type: "presented", sequence: batch.sequence }); + if (this.testDeviceLossPending && surfaceTexture) { + this.testDeviceLossPending = false; + this.device.destroy(); + } + } + + async watchShader(entry, handle, sequence) { + try { + const info = await entry.value.getCompilationInfo(); + for (const message of info.messages) { + let severity = 3; + if (message.type === "error") { + severity = 1; + } else if (message.type === "warning") { + severity = 2; + } + const text = diagnosticBytes(message.message || "shader diagnostic"); + const payload = new Uint8Array(32 + align4(text.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, handle, true); + view.setUint16(4, severity, true); + view.setUint16(6, text.truncated ? 1 : 0, true); + view.setUint32(8, message.lineNum || 0, true); + view.setUint32(12, message.linePos || 0, true); + view.setUint32(16, message.offset || 0, true); + view.setUint32(20, message.length || 0, true); + view.setUint32(24, text.bytes.byteLength, true); + payload.set(text.bytes, 32); + postBytes("event", makeEvent(2, sequence, payload)); + if (severity === 1) { + entry.failed = true; + } + } + } catch {} + } + + emitBatchRejected(commandIndex, errorCode, sequence, message) { + const diagnostic = diagnosticBytes( + `surface_generation=${this.surfaceGeneration} ` + + `physical=${this.physicalWidth}x${this.physicalHeight} ` + + `logical=${this.logicalWidth}x${this.logicalHeight} ` + + `scale=${this.scale} last_sequence=${this.lastSequence}: ${message}` + ); + const payload = new Uint8Array(16 + align4(diagnostic.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, commandIndex, true); + view.setUint32(4, errorCode, true); + view.setUint32(8, diagnostic.bytes.byteLength, true); + view.setUint32(12, diagnostic.truncated ? 1 : 0, true); + payload.set(diagnostic.bytes, 16); + postBytes("event", makeEvent(1, sequence, payload)); + } + + emitTextEvent(type, sequence, errorCode, message) { + const text = diagnosticBytes(message); + const payload = new Uint8Array(12 + align4(text.bytes.byteLength)); + const view = new DataView(payload.buffer); + view.setUint32(0, errorCode, true); + view.setUint32(4, text.bytes.byteLength, true); + view.setUint32(8, text.truncated ? 1 : 0, true); + payload.set(text.bytes, 12); + postBytes("event", makeEvent(type, sequence, payload)); + } + + reset() { + this.queue = this.queue + .then(() => { + if (this.stopped) { + return; + } + for (const entry of this.resources.values()) { + entry.value?.destroy?.(); + } + this.resources.clear(); + this.handleSlots.clear(); + this.pendingResize = null; + this.lastSequence = 0; + }) + .catch(error => { + postMessage({ type: "error", message: error.message || String(error) }); + this.stopped = true; + }); + } + + stop() { + this.stopped = true; + for (const entry of this.resources.values()) { + entry.value?.destroy?.(); + } + this.pendingResize = null; + this.resources.clear(); + this.handleSlots.clear(); + this.device.destroy(); + } +} + +function align4(value) { + return (value + 3) & ~3; +} + +function diagnosticBytes(message) { + const encoded = textEncoder.encode(String(message)); + if (encoded.byteLength <= MAX_DIAGNOSTIC_BYTES) { + return { bytes: encoded, truncated: false }; + } + let end = MAX_DIAGNOSTIC_BYTES; + while (end > 0) { + try { + textDecoder.decode(encoded.subarray(0, end)); + break; + } catch { + end--; + } + } + return { bytes: encoded.slice(0, end), truncated: true }; +} + +function makeEvent(type, sequence, payload = new Uint8Array()) { + const bytes = new Uint8Array(EVENT_HEADER_BYTES + payload.byteLength); + const view = new DataView(bytes.buffer); + bytes.set(textEncoder.encode("EGE1"), 0); + view.setUint16(4, WIRE_VERSION, true); + view.setUint16(6, type, true); + view.setUint32(8, bytes.byteLength, true); + view.setBigUint64(16, BigInt(sequence), true); + bytes.set(payload, EVENT_HEADER_BYTES); + return bytes; +} + +function postBytes(type, bytes) { + postMessage({ type, bytes }, [bytes.buffer]); +} + +let engine = null; + +onmessage = event => { + const message = event.data; + if (message?.type === "init" && !engine) { + void GpuEngine.create( + message.canvas, + message.requirements || {}, + message.dimensions, + message.testReadback === true, + message.testDeviceLoss === true + ) + .then(created => { + engine = created; + postBytes("capabilities", engine.capabilities()); + }) + .catch(error => + postMessage({ type: "error", message: error.message || String(error) }) + ); + } else if (message?.type === "batch" && engine) { + engine.submit(new Uint8Array(message.bytes)); + } else if (message?.type === "resize" && engine) { + engine.scheduleResize(message.dimensions); + } else if (message?.type === "reset" && engine) { + engine.reset(); + } else if (message?.type === "stop" && engine) { + engine.stop(); + engine = null; + } +}; diff --git a/js/packages/pvm-browser-runtime/src/pvm-wasm-translated.js b/js/packages/pvm-browser-runtime/src/pvm-wasm-translated.js new file mode 100644 index 000000000..984146ce6 --- /dev/null +++ b/js/packages/pvm-browser-runtime/src/pvm-wasm-translated.js @@ -0,0 +1,1162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +(() => { + const STATUS_FINISHED = -1; + const STATUS_ECALL = -2; + const STATUS_TRAP = -3; + const STATUS_OUT_OF_GAS = -4; + const INPUT_EVENT_BYTES = 8; + const MAX_INPUT_EVENTS = 4096; + const MAX_HOSTCALLS_PER_INIT = 1024 * 1024; + const MAX_HOSTCALLS_PER_UPDATE = 8192; + const MAX_HOSTCALL_BYTES = 32 * 1024 * 1024; + const MAX_LOG_BYTES = 4 * 1024; + const MAX_SAVE_BYTES = 1024 * 1024; + const MAX_AUDIO_SAMPLES = 48000 * 2; + const MAX_FRAME_BYTES = 16 * 1024 * 1024; + const MAX_TRI2D_BYTES = 8 * 1024 * 1024; + const MAX_GPU_BATCH_BYTES = 4 * 1024 * 1024; + const MAX_GPU_EVENT_BYTES = 64 * 1024; + const MAX_GPU_EVENTS = 256; + const MAX_GPU_SUBMITS_PER_UPDATE = 8; + const MAX_GPU_COMMANDS = 16_384; + const GPU_ERROR_MALFORMED_BATCH = -2; + const GPU_ERROR_QUOTA_EXCEEDED = -3; + const GPU_ERROR_INVALID_STATE = -5; + const IOV_MAX = 1024n; + const AT_FDCWD = BigInt.asUintN(64, -100n); + const ENOSYS = 38; + const EFAULT = 14; + const ENOENT = 2; + const EBADF = 9; + const EACCES = 13; + const EINVAL = 22; + const SYS_OPENAT = 56n; + const SYS_CLOSE = 57n; + const SYS_LSEEK = 62n; + const SYS_READ = 63n; + const SYS_READV = 65n; + const SYS_WRITEV = 66n; + const SYS_EXIT = 93n; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + + function readMetadata(module) { + const sections = WebAssembly.Module.customSections( + module, + "epoca.pvm.meta", + ); + if (sections.length !== 1) { + throw new Error("translated PolkaVM module has invalid metadata"); + } + const bytes = new Uint8Array(sections[0]); + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + let offset = 0; + const requireBytes = (length) => { + if (offset + length > bytes.byteLength) { + throw new Error("translated PolkaVM metadata is truncated"); + } + }; + const readU16 = () => { + requireBytes(2); + const value = view.getUint16(offset, true); + offset += 2; + return value; + }; + const readU32 = () => { + requireBytes(4); + const value = view.getUint32(offset, true); + offset += 4; + return value; + }; + const readString = (length) => { + requireBytes(length); + const value = decoder.decode(bytes.subarray(offset, offset + length)); + offset += length; + return value; + }; + requireBytes(4); + if (decoder.decode(bytes.subarray(0, 4)) !== "EPM2") { + throw new Error("translated PolkaVM metadata has an incompatible ABI"); + } + offset = 4; + const is64Bit = readU32() !== 0; + const names = [ + "roAddress", + "roSize", + "roPhysical", + "rwAddress", + "rwSize", + "rwPhysical", + "heapBase", + "heapLimit", + "stackLow", + "stackHigh", + "stackPhysical", + ]; + const layout = {}; + for (const name of names) { + layout[name] = readU32(); + } + const imports = []; + const importCount = readU32(); + for (let index = 0; index < importCount; index++) { + const length = readU16(); + imports.push(length ? readString(length) : null); + } + const exports = new Map(); + const exportCount = readU32(); + for (let index = 0; index < exportCount; index++) { + const name = readString(readU16()); + const block = readU32(); + if (!name || exports.has(name)) { + throw new Error("translated PolkaVM metadata has invalid exports"); + } + exports.set(name, block); + } + if (offset !== bytes.byteLength) { + throw new Error("translated PolkaVM metadata has trailing data"); + } + return { is64Bit, layout, imports, exports }; + } + + function errno(code) { + return BigInt.asUintN(64, -BigInt(code)); + } + + function normalizedPath(path) { + while (path.startsWith("./")) { + path = path.slice(2); + } + return path.replace(/^\/+/, ""); + } + + function signedByte(value) { + return value > 127 ? value - 256 : value; + } + + function hidToCoreVm(code) { + if (code >= 0x04 && code <= 0x1d) { + return 0x61 + code - 0x04; + } + if (code >= 0x1e && code <= 0x26) { + return 0x31 + code - 0x1e; + } + const keys = new Map([ + [0x27, 0x30], + [0x28, 0x0a], + [0x58, 0x0a], + [0x29, 0x1b], + [0x2a, 0x08], + [0x2b, 0x09], + [0x2c, 0x20], + [0x2d, 0x2d], + [0x56, 0x2d], + [0x2e, 0x3d], + [0x2f, 0x5b], + [0x30, 0x5d], + [0x31, 0x5c], + [0x33, 0x3b], + [0x34, 0x27], + [0x35, 0x60], + [0x36, 0x2c], + [0x37, 0x2e], + [0x63, 0x2e], + [0x38, 0x2f], + [0x54, 0x2f], + [0x46, 0x91], + [0x47, 0x92], + [0x48, 0x93], + [0x49, 0x94], + [0x4a, 0x96], + [0x4b, 0x98], + [0x4c, 0x95], + [0x4d, 0x97], + [0x4e, 0x99], + [0x4f, 0x82], + [0x50, 0x83], + [0x51, 0x81], + [0x52, 0x80], + [0x55, 0x2a], + [0x57, 0x2b], + [0x59, 0x97], + [0x5a, 0x81], + [0x5b, 0x99], + [0x5c, 0x83], + [0x5d, 0x35], + [0x5e, 0x82], + [0x5f, 0x96], + [0x60, 0x80], + [0x61, 0x98], + [0x62, 0x2e], + [0xe0, 0x9c], + [0xe1, 0x9a], + [0xe2, 0x9e], + [0xe4, 0x9d], + [0xe5, 0x9b], + [0xe6, 0x9f], + ]); + if (code >= 0x3a && code <= 0x45) { + return 0x84 + code - 0x3a; + } + return keys.get(code); + } + + class TranslatedPvmRuntime { + constructor(module, assets, emit, maxGas, audioEnabled, gpuCapabilities = null) { + this.metadata = readMetadata(module); + this.instance = new WebAssembly.Instance(module, {}); + this.pvm = this.instance.exports; + this.memories = [ + this.pvm.memory_ro, + this.pvm.memory_rw, + this.pvm.memory_stack, + ]; + if ( + this.memories.some((memory) => !(memory instanceof WebAssembly.Memory)) + ) { + throw new Error("translated PolkaVM module is missing guest memories"); + } + this.assets = new Map( + assets.map((asset) => [ + normalizedPath(asset.path), + new Uint8Array(asset.bytes), + ]), + ); + this.emit = emit; + this.audioEnabled = audioEnabled; + this.gpuCapabilities = + gpuCapabilities instanceof Uint8Array ? gpuCapabilities.slice() : null; + this.gpuEvents = []; + this.gpuSubmits = 0; + this.gpuLastSequence = 0n; + this.maxGas = BigInt(maxGas); + this.input = []; + this.coreInput = []; + this.pointer = null; + this.timeMs = null; + this.clockStartedAt = performance.now(); + this.hostcalls = 0; + this.hostcallBytes = 0; + this.resumePending = false; + this.stopped = false; + this.coreVm = this.metadata.exports.has("_pvm_start"); + this.coreVmStarted = false; + this.palette = new Uint32Array(256); + this.palette.fill(0xffffffff); + this.audioChannels = 0; + this.audioSampleRate = 0; + this.fds = new Map(); + this.nextFd = 3; + this.imports = this.metadata.imports; + this.exports = this.metadata.exports; + for (let index = 0; index < 13; index++) { + if (!(this.pvm[`r${index}`] instanceof WebAssembly.Global)) { + throw new Error("translated PolkaVM module is missing registers"); + } + } + } + + initialize() { + this.#resetBudget(MAX_HOSTCALLS_PER_INIT); + if (this.coreVm) { + this.#setupCoreVm(); + return; + } + const init = this.exports.get("init"); + if (init !== undefined) { + this.#run(init, false); + } + } + + update(timeMs) { + if (this.stopped) { + return; + } + this.timeMs = timeMs; + this.gpuSubmits = 0; + this.#resetBudget( + this.coreVm && !this.coreVmStarted + ? MAX_HOSTCALLS_PER_INIT + : MAX_HOSTCALLS_PER_UPDATE, + ); + if (this.coreVm) { + this.#run(this.exports.get("_pvm_start"), true); + this.coreVmStarted = true; + return; + } + const update = this.exports.get("update"); + if (update === undefined) { + throw new Error("translated PolkaVM guest has no update export"); + } + this.#run(update, false); + } + + sendInput(bytes) { + if (this.stopped || bytes.byteLength !== INPUT_EVENT_BYTES) { + return; + } + if (!this.coreVm) { + if (this.input.length === MAX_INPUT_EVENTS) { + this.input.shift(); + } + this.input.push(bytes.slice()); + return; + } + const view = new DataView( + bytes.buffer, + bytes.byteOffset, + bytes.byteLength, + ); + const type = bytes[0]; + if (type === 1 || type === 2) { + const key = hidToCoreVm(bytes[1]); + if (key !== undefined) { + this.#queueCoreInput(key, type === 1 ? 1 : 0); + } + } else if (type === 3 || type === 4) { + const key = + bytes[1] >= 1 && bytes[1] <= 3 ? 0x9f + bytes[1] : undefined; + if (key !== undefined) { + this.#queueCoreInput(key, type === 3 ? 1 : 0); + } + } else if (type === 5) { + const current = [view.getUint16(2, true), view.getUint16(4, true)]; + if (this.pointer) { + this.#queueCoreInput( + 0xa3, + Math.max(-128, Math.min(127, current[0] - this.pointer[0])) & 0xff, + ); + this.#queueCoreInput( + 0xa4, + Math.max(-128, Math.min(127, current[1] - this.pointer[1])) & 0xff, + ); + } + this.pointer = current; + } else if (type === 6) { + this.#queueCoreInput( + 0xa3, + Math.max(-128, Math.min(127, view.getInt16(2, true))) & 0xff, + ); + this.#queueCoreInput( + 0xa4, + Math.max(-128, Math.min(127, view.getInt16(4, true))) & 0xff, + ); + } + } + + sendGpuEvent(bytes) { + if ( + this.stopped || + !(bytes instanceof Uint8Array) || + bytes.byteLength < 24 || + bytes.byteLength > MAX_GPU_EVENT_BYTES || + decoder.decode(bytes.subarray(0, 4)) !== "EGE1" + ) { + return; + } + if (this.gpuEvents.length === MAX_GPU_EVENTS) { + this.gpuEvents.shift(); + } + this.gpuEvents.push(bytes.slice()); + } + + stop() { + this.stopped = true; + this.input.length = 0; + this.coreInput.length = 0; + this.gpuEvents.length = 0; + } + + #resetBudget(hostcalls) { + this.hostcalls = hostcalls; + this.hostcallBytes = MAX_HOSTCALL_BYTES; + this.pvm.pvm_set_gas(this.maxGas); + } + + #run(entry, yieldOnFrame) { + let status; + if (this.resumePending) { + this.resumePending = false; + status = this.pvm.pvm_resume(); + } else { + if (entry === undefined) { + throw new Error("translated PolkaVM entrypoint is missing"); + } + status = this.pvm.pvm_begin(entry, this.maxGas); + } + for (;;) { + if (status === STATUS_FINISHED) { + if (this.coreVm) { + throw new Error("CoreVM guest exited"); + } + return; + } + if (status === STATUS_TRAP) { + throw new Error( + `translated PolkaVM execution trapped at ${this.pvm.trap_pc.value}`, + ); + } + if (status === STATUS_OUT_OF_GAS) { + throw new Error("translated PolkaVM guest ran out of gas"); + } + if (status !== STATUS_ECALL) { + throw new Error( + `translated PolkaVM returned invalid status ${status}`, + ); + } + const importIndex = this.pvm.ecall.value >>> 0; + const name = this.imports[importIndex]; + if (!name) { + throw new Error( + `translated PolkaVM called unknown import ${importIndex}`, + ); + } + if (--this.hostcalls < 0) { + throw new Error( + `translated PolkaVM guest exceeded hostcall budget in ${name}`, + ); + } + const yielded = this.coreVm + ? this.#handleCoreVmCall(name) + : this.#handleCooperativeCall(name); + if (yielded && yieldOnFrame) { + this.resumePending = true; + return; + } + status = this.pvm.pvm_resume(); + } + } + + #reg(index) { + return BigInt.asUintN(64, this.pvm[`r${index}`].value); + } + + #setReg(index, value) { + const normalized = this.metadata.is64Bit + ? BigInt.asIntN(64, BigInt(value)) + : BigInt.asUintN(32, BigInt(value)); + this.pvm[`r${index}`].value = normalized; + } + + #u32(value) { + return Number(value & 0xffffffffn) >>> 0; + } + + #range(address, length, write = false) { + address >>>= 0; + length >>>= 0; + const end = address + length; + if (end > 0x100000000) { + throw new Error( + "translated PolkaVM guest memory access is out of range", + ); + } + const { layout } = this.metadata; + let memory; + let physical; + if (address >= layout.stackLow && end <= layout.stackHigh) { + memory = this.memories[2]; + physical = address - layout.stackLow; + } else if ( + address >= layout.rwAddress && + end <= layout.rwAddress + this.memories[1].buffer.byteLength + ) { + memory = this.memories[1]; + physical = address - layout.rwAddress; + } else if ( + !write && + address >= layout.roAddress && + end <= layout.roAddress + layout.roSize + ) { + memory = this.memories[0]; + physical = address - layout.roAddress; + } else { + throw new Error( + "translated PolkaVM guest memory access is out of range", + ); + } + return new Uint8Array(memory.buffer, physical, length); + } + + #read(address, length) { + this.#chargeBytes(length); + return this.#range(address, length).slice(); + } + + #write(address, bytes) { + this.#chargeBytes(bytes.byteLength); + this.#range(address, bytes.byteLength, true).set(bytes); + } + + #chargeBytes(length) { + if ( + !Number.isSafeInteger(length) || + length < 0 || + length > this.hostcallBytes + ) { + throw new Error( + "translated PolkaVM guest exceeded hostcall byte budget", + ); + } + this.hostcallBytes -= length; + } + + #readU64(address) { + const bytes = this.#range(address >>> 0, 8); + return new DataView(bytes.buffer, bytes.byteOffset, 8).getBigUint64( + 0, + true, + ); + } + + #writeU64(address, value) { + const bytes = this.#range(address >>> 0, 8, true); + new DataView(bytes.buffer, bytes.byteOffset, 8).setBigUint64( + 0, + BigInt.asUintN(64, value), + true, + ); + } + + #readCString(address) { + const output = []; + for (let offset = 0; offset < 255; offset++) { + let byte; + try { + byte = this.#range((address + offset) >>> 0, 1)[0]; + } catch { + return null; + } + if (!byte) { + return new Uint8Array(output); + } + output.push(byte); + } + return null; + } + + #handleCooperativeCall(name) { + const a0 = this.#reg(7); + const a1 = this.#reg(8); + const a2 = this.#reg(9); + const a3 = this.#reg(10); + const a4 = this.#reg(11); + switch (name) { + case "host_present_frame": { + const width = this.#u32(a1); + const height = this.#u32(a2); + const stride = this.#u32(a3); + const rowBytes = width * 4; + const length = rowBytes * height; + if ( + !width || + !height || + stride !== rowBytes || + length > MAX_FRAME_BYTES + ) { + this.#setReg(7, 1n); + return false; + } + const source = this.#read(this.#u32(a0), length); + const pixels = new Uint8Array(length); + for (let index = 0; index < length; index += 4) { + pixels[index] = source[index + 2]; + pixels[index + 1] = source[index + 1]; + pixels[index + 2] = source[index]; + pixels[index + 3] = source[index + 3]; + } + this.emit({ type: "frame", width, height, pixels }, [pixels.buffer]); + this.#setReg(7, 0n); + return false; + } + case "host_tri2d_submit": { + const length = this.#u32(a1); + if (!length || length > MAX_TRI2D_BYTES) { + this.#setReg(7, 1n); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + this.emit({ type: "tri2d", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "epoca_gpu_capabilities": { + if (this.gpuCapabilities === null) { + this.#setReg(7, BigInt(GPU_ERROR_INVALID_STATE)); + return false; + } + const capacity = this.#u32(a1); + const required = this.gpuCapabilities.byteLength; + if (capacity < required) { + this.#setReg(7, BigInt(-required)); + return false; + } + this.#write(this.#u32(a0), this.gpuCapabilities); + this.#setReg(7, BigInt(required)); + return false; + } + case "epoca_gpu_submit": { + const length = this.#u32(a1); + if (this.gpuCapabilities === null) { + this.#setReg(7, BigInt(GPU_ERROR_INVALID_STATE)); + return false; + } + if ( + !length || + length > MAX_GPU_BATCH_BYTES || + this.gpuSubmits === MAX_GPU_SUBMITS_PER_UPDATE + ) { + this.#setReg( + 7, + BigInt( + this.gpuSubmits === MAX_GPU_SUBMITS_PER_UPDATE + ? GPU_ERROR_QUOTA_EXCEEDED + : GPU_ERROR_MALFORMED_BATCH, + ), + ); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + const sequence = this.#gpuBatchSequence(bytes); + if (sequence === null || sequence <= this.gpuLastSequence) { + this.#setReg(7, BigInt(GPU_ERROR_MALFORMED_BATCH)); + return false; + } + this.gpuSubmits++; + this.gpuLastSequence = sequence; + this.emit({ type: "gpu-batch", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "epoca_gpu_receive": { + const event = this.gpuEvents[0]; + if (event === undefined) { + this.#setReg(7, 0n); + return false; + } + const capacity = this.#u32(a1); + if (capacity < event.byteLength) { + this.#setReg(7, BigInt(-event.byteLength)); + return false; + } + this.#write(this.#u32(a0), event); + this.gpuEvents.shift(); + this.#setReg(7, BigInt(event.byteLength)); + return false; + } + case "host_poll_input": { + const capacity = this.#u32(a1); + const count = Math.min( + Math.floor(capacity / INPUT_EVENT_BYTES), + this.input.length, + ); + const output = new Uint8Array(count * INPUT_EVENT_BYTES); + for (let index = 0; index < count; index++) { + output.set(this.input.shift(), index * INPUT_EVENT_BYTES); + } + this.#write(this.#u32(a0), output); + this.#setReg(7, BigInt(output.byteLength)); + return false; + } + case "host_time_ms": { + const timeMs = this.timeMs ?? performance.now() - this.clockStartedAt; + this.#setReg(7, BigInt(Math.max(0, Math.trunc(timeMs)))); + return false; + } + case "host_sleep_ms": + this.timeMs = + (this.timeMs ?? performance.now() - this.clockStartedAt) + + Math.min(this.#u32(a0), 50); + return false; + case "host_audio_submit": { + if (!this.audioEnabled) { + this.#setReg(7, 3n); + return false; + } + const sampleCount = this.#u32(a1); + if ( + !sampleCount || + sampleCount % 2 || + sampleCount > MAX_AUDIO_SAMPLES + ) { + this.#setReg(7, 1n); + return false; + } + const samples = this.#read(this.#u32(a0), sampleCount * 2); + this.emit( + { type: "audio", sampleRate: 48000, channels: 2, samples }, + [samples.buffer], + ); + this.#setReg(7, 0n); + return false; + } + case "host_asset_read": { + const nameLength = this.#u32(a1); + const offset = this.#u32(a2); + const destination = this.#u32(a3); + const capacity = this.#u32(a4); + if (!nameLength || nameLength > 1024) { + this.#setReg(7, 0n); + return false; + } + const assetName = decoder.decode( + this.#read(this.#u32(a0), nameLength), + ); + const asset = this.assets.get(assetName); + if (!asset || offset >= asset.byteLength) { + this.#setReg(7, 0n); + return false; + } + const length = Math.min( + capacity, + asset.byteLength - offset, + 16 * 1024 * 1024, + ); + this.#write(destination, asset.subarray(offset, offset + length)); + this.#setReg(7, BigInt(length)); + return false; + } + case "host_save_submit": { + const length = this.#u32(a1); + if (!length || length > MAX_SAVE_BYTES) { + this.#setReg(7, 1n); + return false; + } + const bytes = this.#read(this.#u32(a0), length); + this.emit({ type: "save", bytes }, [bytes.buffer]); + this.#setReg(7, 0n); + return false; + } + case "host_log": { + const length = Math.min(this.#u32(a1), MAX_LOG_BYTES); + const message = decoder.decode(this.#read(this.#u32(a0), length)); + this.emit({ type: "log", message }); + return false; + } + default: + throw new Error( + `translated PolkaVM guest uses unsupported import ${name}`, + ); + } + } + + #gpuBatchSequence(bytes) { + if ( + bytes.byteLength < 24 || + decoder.decode(bytes.subarray(0, 4)) !== "EPG1" + ) { + return null; + } + const view = new DataView( + bytes.buffer, + bytes.byteOffset, + bytes.byteLength, + ); + if ( + view.getUint16(4, true) !== 1 || + view.getUint16(6, true) !== 0 || + view.getUint32(8, true) !== bytes.byteLength + ) { + return null; + } + const commandCount = view.getUint32(12, true); + const sequence = view.getBigUint64(16, true); + if (sequence === 0n || commandCount > MAX_GPU_COMMANDS) { + return null; + } + let offset = 24; + for (let index = 0; index < commandCount; index++) { + if ( + offset + 8 > bytes.byteLength || + view.getUint16(offset + 2, true) !== 0 + ) { + return null; + } + const commandBytes = view.getUint32(offset + 4, true); + if ( + commandBytes < 8 || + commandBytes % 4 || + commandBytes > bytes.byteLength - offset + ) { + return null; + } + offset += commandBytes; + } + return offset === bytes.byteLength ? sequence : null; + } + + #setupCoreVm() { + let sp = BigInt(this.metadata.layout.stackHigh); + const argc = 1n; + sp -= (1n + argc + 1n + 0n + 1n + 4n) * 8n; + const addressInit = sp; + let pointer = sp; + this.#writeU64(Number(pointer), argc); + pointer += 8n; + const argument = encoder.encode("./quake\0"); + sp -= BigInt(argument.byteLength); + this.#write(Number(sp), argument); + this.#writeU64(Number(pointer), sp); + pointer += 16n; + pointer += 8n; + this.#writeU64(Number(pointer), 6n); + this.#writeU64(Number(pointer + 8n), 4096n); + this.#setReg(1, sp); + this.#setReg(7, addressInit); + } + + #queueCoreInput(key, value) { + if (!value && (key === 0xa3 || key === 0xa4)) { + return; + } + if (key === 0xa3 || key === 0xa4) { + const existing = this.coreInput.find((event) => event[0] === key); + if (existing) { + existing[1] = + Math.max( + -128, + Math.min(127, signedByte(existing[1]) + signedByte(value)), + ) & 0xff; + return; + } + } + if (this.coreInput.length === 256) { + this.coreInput.shift(); + } + this.coreInput.push([key, value]); + } + + #handleCoreVmCall(name) { + switch (name) { + case "pvm_set_palette": { + const palette = this.#read(this.#u32(this.#reg(7)), 256 * 3); + for (let index = 0; index < 256; index++) { + const offset = index * 3; + this.palette[index] = + palette[offset] | + (palette[offset + 1] << 8) | + (palette[offset + 2] << 16) | + 0xff000000; + } + return false; + } + case "pvm_display": { + const width = this.#u32(this.#reg(7)); + const height = this.#u32(this.#reg(8)); + const length = width * height; + if (!width || !height || length > MAX_FRAME_BYTES / 4) { + throw new Error("CoreVM guest supplied invalid frame dimensions"); + } + const indices = this.#read(this.#u32(this.#reg(9)), length); + const pixels = new Uint8Array(length * 4); + const rgba = new Uint32Array(pixels.buffer); + for (let index = 0; index < length; index++) { + rgba[index] = this.palette[indices[index]]; + } + this.emit({ type: "frame", width, height, pixels }, [pixels.buffer]); + return true; + } + case "pvm_fetch_epoca_inputs": + case "pvm_fetch_inputs": { + const count = Math.min( + this.#u32(this.#reg(8)), + this.coreInput.length, + ); + const output = new Uint8Array(count * 2); + for (let index = 0; index < count; index++) { + output.set(this.coreInput.shift(), index * 2); + } + this.#write(this.#u32(this.#reg(7)), output); + this.#setReg(7, BigInt(count)); + return false; + } + case "pvm_asset_read": { + const nameLength = this.#u32(this.#reg(8)); + const offset = this.#u32(this.#reg(9)); + const destination = this.#u32(this.#reg(10)); + const capacity = this.#u32(this.#reg(11)); + if (!nameLength || nameLength > 1024) { + this.#setReg(7, 0n); + return false; + } + const assetName = decoder.decode( + this.#read(this.#u32(this.#reg(7)), nameLength), + ); + const asset = this.assets.get(assetName); + if (!asset || offset >= asset.byteLength) { + this.#setReg(7, 0n); + return false; + } + const length = Math.min( + capacity, + asset.byteLength - offset, + 16 * 1024 * 1024, + ); + this.#write(destination, asset.subarray(offset, offset + length)); + this.#setReg(7, BigInt(length)); + return false; + } + case "host_audio_submit": { + if (!this.audioEnabled) { + this.#setReg(7, 3n); + return false; + } + const sampleCount = this.#u32(this.#reg(8)); + if ( + !sampleCount || + sampleCount % 2 || + sampleCount > MAX_AUDIO_SAMPLES + ) { + this.#setReg(7, 1n); + return false; + } + const samples = this.#read(this.#u32(this.#reg(7)), sampleCount * 2); + this.emit( + { type: "audio", sampleRate: 48000, channels: 2, samples }, + [samples.buffer], + ); + this.#setReg(7, 0n); + return false; + } + case "pvm_time_ms": { + const timeMs = this.timeMs ?? performance.now() - this.clockStartedAt; + this.#setReg(7, BigInt(Math.max(0, Math.trunc(timeMs)))); + return false; + } + case "host_log": { + const length = Math.min(this.#u32(this.#reg(8)), MAX_LOG_BYTES); + const message = decoder.decode( + this.#read(this.#u32(this.#reg(7)), length), + ); + this.emit({ type: "log", message }); + return false; + } + case "pvm_yield": + return true; + case "pvm_init_audio": { + const channels = this.#u32(this.#reg(7)); + const bitsPerSample = this.#u32(this.#reg(8)); + const sampleRate = this.#u32(this.#reg(9)); + if ( + bitsPerSample !== 16 || + channels < 1 || + channels > 2 || + sampleRate < 8000 || + sampleRate > 96000 + ) { + this.#setReg(7, 0n); + } else { + this.audioChannels = channels; + this.audioSampleRate = sampleRate; + this.#setReg(7, 1n); + } + return false; + } + case "pvm_output_audio": { + const frames = this.#u32(this.#reg(8)); + const sampleCount = Math.min(frames * this.audioChannels, 1024 * 64); + if (this.audioChannels && sampleCount) { + const samples = this.#read( + this.#u32(this.#reg(7)), + sampleCount * 2, + ); + this.emit( + { + type: "audio", + sampleRate: this.audioSampleRate, + channels: this.audioChannels, + samples, + }, + [samples.buffer], + ); + } + return false; + } + case "pvm_syscall": + return this.#handleCoreVmSyscall(); + default: + throw new Error( + `translated CoreVM guest uses unsupported import ${name}`, + ); + } + } + + #handleCoreVmSyscall() { + const syscall = this.#reg(7); + const a1 = this.#reg(8); + const a2 = this.#reg(9); + const a3 = this.#reg(10); + if (syscall === SYS_READ) { + this.#setReg(7, this.#readFile(a1, a2, a3)); + } else if (syscall === SYS_READV || syscall === SYS_WRITEV) { + if (!a3 || a3 > IOV_MAX) { + this.#setReg(7, errno(EINVAL)); + return false; + } + let total = 0n; + for (let index = 0n; index < a3; index++) { + let address; + let length; + try { + address = this.#readU64(this.#u32(a2 + index * 16n)); + length = this.#readU64(this.#u32(a2 + index * 16n + 8n)); + } catch { + this.#setReg(7, errno(EFAULT)); + return false; + } + const result = + syscall === SYS_READV + ? this.#readFile(a1, address, length) + : this.#writeFile(a1, address, length); + if (BigInt.asIntN(64, result) < 0n) { + this.#setReg(7, result); + return false; + } + total += length; + } + this.#setReg(7, total); + } else if (syscall === SYS_EXIT) { + if (a1 === 0n) { + throw new Error("CoreVM guest exited"); + } + throw new Error(`CoreVM guest exited with status ${a1}`); + } else if (syscall === SYS_OPENAT) { + if (a1 !== AT_FDCWD) { + this.#setReg(7, errno(ENOSYS)); + return false; + } + const pathBytes = this.#readCString(this.#u32(a2)); + if (!pathBytes) { + this.#setReg(7, errno(EFAULT)); + return false; + } + const path = normalizedPath(decoder.decode(pathBytes)); + const flags = a3; + const asset = this.assets.get(path); + if (!asset) { + this.#setReg(7, errno(ENOENT)); + } else if (flags & 3n) { + this.#setReg(7, errno(EACCES)); + } else { + const fd = this.nextFd++; + this.fds.set(fd, { bytes: asset, position: 0n }); + this.#setReg(7, BigInt(fd)); + } + } else if (syscall === SYS_LSEEK) { + this.#setReg(7, this.#seekFile(a1, a2, a3)); + } else if (syscall === SYS_CLOSE) { + const fd = this.#fdNumber(a1); + if (fd === null || !this.fds.delete(fd)) { + this.#setReg(7, errno(EBADF)); + } else { + this.#setReg(7, 0n); + } + } else { + this.#setReg(7, errno(ENOSYS)); + } + return false; + } + + #fdNumber(value) { + return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : null; + } + + #readFile(fdValue, address, length) { + const fd = this.#fdNumber(fdValue); + const file = fd === null ? null : this.fds.get(fd); + if (!file) { + return errno(EBADF); + } + if ( + address + length > 0x100000000n || + length > BigInt(MAX_HOSTCALL_BYTES) + ) { + return errno(EFAULT); + } + const end = + file.position + length < BigInt(file.bytes.byteLength) + ? file.position + length + : BigInt(file.bytes.byteLength); + if (file.position >= end) { + return 0n; + } + const count = Number(end - file.position); + try { + this.#write( + this.#u32(address), + file.bytes.subarray( + Number(file.position), + Number(file.position) + count, + ), + ); + } catch { + return errno(EFAULT); + } + file.position += BigInt(count); + return BigInt(count); + } + + #writeFile(fdValue, address, length) { + if (fdValue !== 1n && fdValue !== 2n) { + return errno(EBADF); + } + if ( + address + length > 0x100000000n || + length > BigInt(MAX_HOSTCALL_BYTES) + ) { + return errno(EFAULT); + } + try { + const byteLength = Number(length); + this.#chargeBytes(byteLength); + const bytes = this.#range(this.#u32(address), byteLength); + const message = decoder.decode(bytes.subarray(0, MAX_LOG_BYTES)); + if (message) { + this.emit({ type: "log", message }); + } + } catch { + return errno(EFAULT); + } + return length; + } + + #seekFile(fdValue, offsetValue, whence) { + const fd = this.#fdNumber(fdValue); + const file = fd === null ? null : this.fds.get(fd); + if (!file) { + return errno(EBADF); + } + const offset = BigInt.asIntN(64, offsetValue); + const fileLength = BigInt(file.bytes.byteLength); + if (whence === 0n) { + file.position = BigInt.asUintN(64, offset); + } else if (whence === 1n) { + file.position = BigInt.asUintN( + 64, + BigInt.asIntN(64, file.position) + offset, + ); + if (file.position > fileLength) { + file.position = fileLength; + } + } else if (whence === 2n) { + file.position = BigInt.asUintN( + 64, + BigInt.asIntN(64, fileLength) + offset, + ); + if (file.position > fileLength) { + file.position = fileLength; + } + } else { + return errno(EINVAL); + } + return file.position; + } + } + + globalThis.TranslatedPvmRuntime = TranslatedPvmRuntime; +})(); diff --git a/js/packages/pvm-browser-runtime/src/pvm-wasm-worker-entry.js b/js/packages/pvm-browser-runtime/src/pvm-wasm-worker-entry.js new file mode 100644 index 000000000..37840f4a3 --- /dev/null +++ b/js/packages/pvm-browser-runtime/src/pvm-wasm-worker-entry.js @@ -0,0 +1,395 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const FRAME_INTERVAL_MS = 1000 / 60; +const MAX_GAS_PER_UPDATE = 500_000_000; +const MAX_TRANSLATED_LOOPS_PER_UPDATE = 50_000_000; +const decoder = new TextDecoder(); +const encoder = new TextEncoder(); + +let pvm; +let translated; +let backend = "interpreter"; +let running = false; +let timer; +let startedAt = 0; +let updateCount = 0; +const updateSamples = []; +const tickChannel = new MessageChannel(); +tickChannel.port1.onmessage = tick; + +function errorText() { + const pointer = pvm.pvm_browser_error_pointer(); + const length = pvm.pvm_browser_error_length(); + return decoder.decode(new Uint8Array(pvm.memory.buffer, pointer, length)); +} + +function check(status, operation) { + if (status !== 0) { + throw new Error(`${operation}: ${errorText()}`); + } +} + +function stage(bytes) { + const source = + bytes instanceof Uint8Array + ? bytes + : new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); + const pointer = pvm.pvm_browser_staging_reserve(source.byteLength); + if (!pointer) { + throw new Error(`reserve browser runtime memory: ${errorText()}`); + } + new Uint8Array(pvm.memory.buffer, pointer, source.byteLength).set(source); +} + +function addAsset(asset) { + const path = encoder.encode(asset.path.replace(/^\/+/, "")); + const bytes = new Uint8Array(asset.bytes); + const packed = new Uint8Array(path.byteLength + bytes.byteLength); + packed.set(path); + packed.set(bytes, path.byteLength); + stage(packed); + check( + pvm.pvm_browser_launch_add_asset(path.byteLength), + `mount browser asset ${asset.path}`, + ); +} + +function drainFrame() { + if (!pvm.pvm_browser_take_frame()) { + return; + } + const width = pvm.pvm_browser_frame_width(); + const height = pvm.pvm_browser_frame_height(); + const length = pvm.pvm_browser_frame_length(); + const source = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_frame_pointer(), + length, + ); + const pixels = new Uint8Array(length); + for (let index = 0; index < length; index += 4) { + pixels[index] = source[index + 2]; + pixels[index + 1] = source[index + 1]; + pixels[index + 2] = source[index]; + pixels[index + 3] = source[index + 3]; + } + postMessage({ type: "frame", width, height, pixels }, [pixels.buffer]); +} + +function drainTri2d() { + if (!pvm.pvm_browser_take_tri2d?.()) { + return; + } + const length = pvm.pvm_browser_tri2d_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_tri2d_pointer(), + length, + ).slice(); + postMessage({ type: "tri2d", bytes }, [bytes.buffer]); +} + +function drainGpuBatches() { + while (pvm.pvm_browser_take_gpu_batch?.()) { + const length = pvm.pvm_browser_gpu_batch_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_gpu_batch_pointer(), + length, + ).slice(); + postMessage({ type: "gpu-batch", bytes }, [bytes.buffer]); + } +} + +function drainAudio() { + while (pvm.pvm_browser_take_audio()) { + const sampleRate = pvm.pvm_browser_audio_sample_rate(); + const channels = pvm.pvm_browser_audio_channels(); + const length = pvm.pvm_browser_audio_length() * 2; + const samples = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_audio_pointer(), + length, + ).slice(); + postMessage({ type: "audio", sampleRate, channels, samples }, [ + samples.buffer, + ]); + } +} + +function drainSave() { + while (pvm.pvm_browser_take_save()) { + const length = pvm.pvm_browser_save_length(); + const bytes = new Uint8Array( + pvm.memory.buffer, + pvm.pvm_browser_save_pointer(), + length, + ).slice(); + postMessage({ type: "save", bytes }, [bytes.buffer]); + } +} + +function drainLogs() { + while (pvm.pvm_browser_take_log()) { + const pointer = pvm.pvm_browser_log_pointer(); + const length = pvm.pvm_browser_log_length(); + const message = decoder.decode( + new Uint8Array(pvm.memory.buffer, pointer, length), + ); + postMessage({ type: "log", message }); + } +} + +function tick() { + if (!running) { + return; + } + const before = performance.now(); + try { + if (translated) { + translated.update(before - startedAt); + } else { + check( + pvm.pvm_browser_update(before - startedAt), + "update PolkaVM browser guest", + ); + drainFrame(); + drainTri2d(); + drainGpuBatches(); + drainAudio(); + drainSave(); + drainLogs(); + } + } catch (error) { + running = false; + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + return; + } + const elapsed = performance.now() - before; + updateCount++; + updateSamples.push(elapsed); + if (updateSamples.length > 600) { + updateSamples.shift(); + } + if (updateCount % 120 === 0) { + const sorted = [...updateSamples].sort((a, b) => a - b); + const percentile = (value) => + sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * value))]; + postMessage({ + type: "metrics", + updates: updateCount, + updateP50Ms: percentile(0.5), + updateP95Ms: percentile(0.95), + updateMaxMs: sorted[sorted.length - 1], + }); + } + const delay = FRAME_INTERVAL_MS - elapsed; + if (delay > 0) { + timer = setTimeout(tick, delay); + } else { + tickChannel.port2.postMessage(null); + } +} + +async function start(message) { + if (pvm || running) { + throw new Error("PolkaVM browser worker is already started"); + } + const bootStarted = performance.now(); + let translationMs = 0; + let compilationMs = 0; + let translatedWasmBytes = 0; + let cacheHit = false; + const instantiated = await WebAssembly.instantiate(message.runtime, {}); + pvm = instantiated.instance.exports; + if (pvm.pvm_browser_abi_version() !== 1) { + throw new Error("PolkaVM browser runtime has an incompatible ABI"); + } + const program = new Uint8Array(message.program); + stage(program); + const pendingOutputs = []; + try { + let module = message.compiledModule; + let bytes = + message.compiledBytes instanceof ArrayBuffer + ? new Uint8Array(message.compiledBytes) + : null; + cacheHit = module instanceof WebAssembly.Module || bytes !== null; + if (!(module instanceof WebAssembly.Module)) { + if (bytes === null) { + const translationStarted = performance.now(); + check( + pvm.pvm_browser_translate_staged(), + "translate PolkaVM browser guest", + ); + translationMs = performance.now() - translationStarted; + const pointer = pvm.pvm_browser_translation_pointer(); + const length = pvm.pvm_browser_translation_length(); + bytes = new Uint8Array(pvm.memory.buffer, pointer, length).slice(); + const persistent = bytes.slice(); + postMessage( + { type: "translated", cacheKey: message.cacheKey, bytes: persistent }, + [persistent.buffer], + ); + } + translatedWasmBytes = bytes.byteLength; + const compilationStarted = performance.now(); + module = await WebAssembly.compile(bytes); + compilationMs = performance.now() - compilationStarted; + try { + postMessage({ type: "compiled", cacheKey: message.cacheKey, module }); + } catch {} + } + translated = new globalThis.TranslatedPvmRuntime( + module, + message.assets, + (output, transfers = []) => { + if (running) { + postMessage(output, transfers); + } else { + pendingOutputs.push({ output, transfers }); + } + }, + MAX_TRANSLATED_LOOPS_PER_UPDATE, + message.audioEnabled, + message.gpuCapabilities instanceof ArrayBuffer + ? new Uint8Array(message.gpuCapabilities) + : null, + ); + translated.initialize(); + backend = "compiler"; + } catch (error) { + translated = null; + pendingOutputs.length = 0; + console.warn(`PolkaVM translation failed; using interpreter: ${error}`); + const presentation = + message.graphicsProfile === "tri2d" + ? 1 + : message.graphicsProfile === "webgpu-raster" + ? 2 + : 0; + const begin = pvm.pvm_browser_launch_begin_v2; + if (typeof begin !== "function") { + throw new Error("PolkaVM interpreter does not support graphics profiles"); + } + stage(program); + check( + begin( + MAX_GAS_PER_UPDATE, + message.audioEnabled ? 1 : 0, + presentation, + ), + "begin PolkaVM browser launch", + ); + for (const asset of message.assets) { + addAsset(asset); + } + check(pvm.pvm_browser_launch_start(), "start PolkaVM browser launch"); + if (message.graphicsProfile === "webgpu-raster") { + if (!(message.gpuCapabilities instanceof ArrayBuffer)) { + throw new Error("WebGPU capabilities are required before PVM initialization"); + } + stage(new Uint8Array(message.gpuCapabilities)); + check( + pvm.pvm_browser_set_gpu_capabilities(), + "set PolkaVM browser GPU capabilities", + ); + } + try { + check(pvm.pvm_browser_init(), "initialize PolkaVM browser guest"); + } catch (initError) { + drainLogs(); + throw initError; + } + drainTri2d(); + drainGpuBatches(); + drainLogs(); + } + startedAt = performance.now(); + running = true; + postMessage({ + type: "ready", + backend, + cacheHit, + translationMs, + compilationMs, + translatedWasmBytes, + startupMs: performance.now() - bootStarted, + }); + for (const { output, transfers } of pendingOutputs) { + postMessage(output, transfers); + } + tick(); +} + +function sendInput(bytes) { + if (!running || bytes.byteLength !== 8) { + return; + } + if (translated) { + translated.sendInput(bytes); + return; + } + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + check( + pvm.pvm_browser_send_input( + bytes[0], + bytes[1], + view.getUint16(2, true), + view.getUint16(4, true), + ), + "send PolkaVM browser input", + ); +} + +function sendGpuEvent(bytes) { + if (!running || !pvm || !bytes.byteLength) { + return; + } + if (translated) { + translated.sendGpuEvent(bytes); + return; + } + stage(bytes); + check(pvm.pvm_browser_send_gpu_event(), "send PolkaVM browser GPU event"); +} + +onmessage = (event) => { + const message = event.data; + if (message?.type === "start") { + void start(message).catch((error) => { + running = false; + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + }); + } else if (message?.type === "input") { + try { + sendInput(new Uint8Array(message.bytes)); + } catch (error) { + running = false; + clearTimeout(timer); + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + } + } else if (message?.type === "gpu-event") { + try { + sendGpuEvent(new Uint8Array(message.bytes)); + } catch (error) { + running = false; + clearTimeout(timer); + postMessage({ type: "error", message: error.message }); + postMessage({ type: "terminated" }); + } + } else if (message?.type === "stop") { + running = false; + clearTimeout(timer); + translated?.stop(); + pvm?.pvm_browser_reset(); + postMessage({ type: "terminated" }); + } +}; diff --git a/js/packages/pvm-browser-runtime/test/artifacts.test.mjs b/js/packages/pvm-browser-runtime/test/artifacts.test.mjs new file mode 100644 index 000000000..a2a9fcaff --- /dev/null +++ b/js/packages/pvm-browser-runtime/test/artifacts.test.mjs @@ -0,0 +1,43 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import test from "node:test"; + +const root = resolve(import.meta.dirname, ".."); +const dist = resolve(root, "dist"); + +test("browser runtime artifacts match their published checksums", async () => { + const expected = new Map( + (await readFile(resolve(dist, "SHA256SUMS"), "utf8")) + .trim() + .split("\n") + .map((line) => { + const [digest, file] = line.split(/\s+/, 2); + return [file, digest]; + }), + ); + for (const file of [ + "pvm-browser-runtime.wasm", + "pvm-worker.js", + "pvm-gpu-worker.js", + ]) { + const bytes = await readFile(resolve(dist, file)); + assert.equal(createHash("sha256").update(bytes).digest("hex"), expected.get(file)); + } +}); + +test("Wasm runtime exports the neutral graphics profile ABI", async () => { + const bytes = await readFile(resolve(dist, "pvm-browser-runtime.wasm")); + const module = await WebAssembly.compile(bytes); + const exports = new Set(WebAssembly.Module.exports(module).map(({ name }) => name)); + for (const name of [ + "pvm_browser_launch_begin_v2", + "pvm_browser_take_tri2d", + "pvm_browser_set_gpu_capabilities", + "pvm_browser_send_gpu_event", + "pvm_browser_take_gpu_batch", + ]) { + assert.ok(exports.has(name), `missing ${name}`); + } +}); diff --git a/package-lock.json b/package-lock.json index 309fcde6d..7cfb23784 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,14 @@ "node": ">=22" } }, + "js/packages/pvm-browser-runtime": { + "name": "@parity/pvm-browser-runtime", + "version": "0.1.0", + "license": "MPL-2.0", + "engines": { + "node": ">=22" + } + }, "js/packages/truapi": { "name": "@parity/truapi", "version": "0.11.0", @@ -428,6 +436,10 @@ "node": ">= 8" } }, + "node_modules/@parity/pvm-browser-runtime": { + "resolved": "js/packages/pvm-browser-runtime", + "link": true + }, "node_modules/@parity/truapi": { "resolved": "js/packages/truapi", "link": true diff --git a/package.json b/package.json index 7996292e5..494efca41 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,14 @@ "node": ">=22" }, "scripts": { - "build": "npm run build --prefix js/packages/truapi && cd playground && yarn build", + "build": "npm run build:pvm-browser && npm run build --prefix js/packages/truapi && cd playground && yarn build", + "build:pvm-browser": "npm run build --prefix js/packages/pvm-browser-runtime", "codegen": "./scripts/codegen.sh", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "format:check": "prettier --check \"**/*.{ts,tsx,md}\"", "typecheck": "npm run typecheck --prefix js/packages/truapi && cd playground && npx tsc --noEmit --noUnusedLocals --noUnusedParameters", - "test": "cargo test --workspace && npm test --prefix js/packages/truapi", + "test": "cargo test --workspace && npm test --prefix js/packages/truapi && npm run test:pvm-browser", + "test:pvm-browser": "npm test --prefix js/packages/pvm-browser-runtime", "test:scripts": "node --test scripts/lib/*.test.mjs", "lint": "npm run format:check && npm run typecheck", "changeset": "changeset", diff --git a/rust/crates/pvm-gpu-wire/Cargo.toml b/rust/crates/pvm-gpu-wire/Cargo.toml new file mode 100644 index 000000000..895858625 --- /dev/null +++ b/rust/crates/pvm-gpu-wire/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pvm-gpu-wire" +version = "0.1.0" +edition = "2021" +description = "Bounded host-neutral WebGPU Raster wire protocol for PolkaVM applications" +license = "MPL-2.0" +publish = false + +[lib] +crate-type = ["rlib"] diff --git a/rust/crates/pvm-gpu-wire/src/lib.rs b/rust/crates/pvm-gpu-wire/src/lib.rs new file mode 100644 index 000000000..7ad01e36b --- /dev/null +++ b/rust/crates/pvm-gpu-wire/src/lib.rs @@ -0,0 +1,1005 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#![no_std] + +use core::fmt; + +pub const GPU_WIRE_MAGIC: [u8; 4] = *b"EPG1"; +pub const GPU_WIRE_VERSION: u16 = 1; +pub const GPU_BATCH_HEADER_BYTES: usize = 24; +pub const GPU_COMMAND_HEADER_BYTES: usize = 8; +pub const MAX_GPU_BATCH_BYTES: usize = 4 * 1024 * 1024; +pub const MAX_GPU_COMMANDS: u32 = 16_384; +pub const GPU_CAPABILITIES_MAGIC: [u8; 4] = *b"EGC1"; +pub const GPU_CAPABILITIES_HEADER_BYTES: usize = 56; +pub const GPU_CAPABILITY_ENTRY_BYTES: usize = 16; +pub const GPU_EVENT_MAGIC: [u8; 4] = *b"EGE1"; +pub const GPU_EVENT_HEADER_BYTES: usize = 24; +pub const MAX_GPU_EVENT_BYTES: usize = 64 * 1024; +pub const MAX_GPU_DIAGNOSTIC_BYTES: usize = 8 * 1024; +pub const GPU_HANDLE_SLOT_BITS: u32 = 20; +pub const GPU_HANDLE_SLOT_MASK: u32 = (1 << GPU_HANDLE_SLOT_BITS) - 1; +pub const GPU_HANDLE_MAX_GENERATION: u32 = (1 << (32 - GPU_HANDLE_SLOT_BITS)) - 1; +pub const MAX_GPU_SUBMITS_PER_TICK: u32 = 8; +pub const MAX_GPU_UPLOAD_BYTES_PER_TICK: usize = 16 * 1024 * 1024; +pub const MAX_GPU_QUEUED_BATCHES: usize = 4; +pub const MAX_GPU_QUEUED_EVENTS: usize = 256; +pub const MAX_GPU_BUFFERS: usize = 4_096; +pub const MAX_GPU_BUFFER_BYTES: usize = 16 * 1024 * 1024; +pub const MAX_GPU_TOTAL_BUFFER_BYTES: usize = 64 * 1024 * 1024; +pub const MAX_GPU_TEXTURES: usize = 512; +pub const MAX_GPU_TOTAL_TEXTURE_BYTES: usize = 256 * 1024 * 1024; +pub const MAX_GPU_TEXTURE_DIMENSION_2D: u32 = 4_096; +pub const MAX_GPU_TEXTURE_SAMPLE_COUNT: u32 = 1; +pub const MAX_GPU_TEXTURE_MIP_LEVELS: u32 = 13; +pub const MAX_GPU_TEXTURE_VIEWS: usize = 1_024; +pub const MAX_GPU_SAMPLERS: usize = 128; +pub const MAX_GPU_SHADER_MODULES: usize = 128; +pub const MAX_GPU_WGSL_BYTES: usize = 1024 * 1024; +pub const MAX_GPU_COMPILATIONS: usize = 8; +pub const MAX_GPU_BIND_GROUP_LAYOUTS: usize = 128; +pub const MAX_GPU_PIPELINE_LAYOUTS: usize = 64; +pub const MAX_GPU_BIND_GROUPS: usize = 512; +pub const MAX_GPU_RENDER_PIPELINES: usize = 256; +pub const MAX_GPU_BIND_GROUPS_PER_PIPELINE: usize = 4; +pub const MAX_GPU_BINDINGS_PER_GROUP: usize = 16; +pub const MAX_GPU_VERTEX_BUFFERS: usize = 8; +pub const MAX_GPU_VERTEX_ATTRIBUTES: usize = 16; +pub const MAX_GPU_COLOR_ATTACHMENTS: usize = 4; +pub const MAX_GPU_RENDER_PASSES_PER_BATCH: usize = 16; +pub const MAX_GPU_DRAWS_PER_BATCH: usize = 8_192; + +pub const GPU_SUBMIT_ACCEPTED: i32 = 0; +pub const GPU_SUBMIT_BUSY: i32 = 1; +pub const GPU_ERROR_INVALID_GUEST_RANGE: i32 = -1; +pub const GPU_ERROR_MALFORMED_BATCH: i32 = -2; +pub const GPU_ERROR_QUOTA_EXCEEDED: i32 = -3; +pub const GPU_ERROR_INVALID_HANDLE: i32 = -4; +pub const GPU_ERROR_INVALID_STATE: i32 = -5; +pub const GPU_ERROR_STOPPED: i32 = -6; + +pub const GPU_BATCH_ERROR_STALE_SURFACE: u32 = 4; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuCapabilityKey { + MaxTextureDimension2d = 1, + MaxBufferSize = 2, + MaxBindingsPerBindGroup = 3, + MaxBindGroups = 4, + MaxVertexBuffers = 5, + MaxVertexAttributes = 6, + MaxColorAttachments = 7, + MaxTextureBytes = 8, + MaxBufferBytes = 9, + MaxDrawsPerBatch = 10, + MaxBatchBytes = 11, + MaxUploadBytesPerTick = 12, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuEventType { + BatchRejected = 1, + ShaderDiagnostic = 2, + ResourceFailed = 3, + UncapturedError = 4, + SubmissionComplete = 5, + SurfaceChanged = 6, + DeviceLost = 7, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuOpcode { + CreateBuffer = 1, + WriteBuffer = 2, + CreateTexture = 3, + WriteTexture = 4, + CreateSampler = 5, + CreateShaderWgsl = 6, + CreateBindGroupLayout = 7, + CreatePipelineLayout = 8, + CreateBindGroup = 9, + CreateRenderPipeline = 10, + DestroyResource = 11, + BeginRenderPass = 12, + SetPipeline = 13, + SetVertexBuffer = 14, + SetIndexBuffer = 15, + SetBindGroup = 16, + SetViewport = 17, + SetScissorRect = 18, + Draw = 19, + DrawIndexed = 20, + EndRenderPass = 21, + CopyBufferToBuffer = 22, + CreateTextureView = 23, +} + +impl TryFrom for GpuOpcode { + type Error = (); + + fn try_from(value: u16) -> Result { + match value { + 1 => Ok(Self::CreateBuffer), + 2 => Ok(Self::WriteBuffer), + 3 => Ok(Self::CreateTexture), + 4 => Ok(Self::WriteTexture), + 5 => Ok(Self::CreateSampler), + 6 => Ok(Self::CreateShaderWgsl), + 7 => Ok(Self::CreateBindGroupLayout), + 8 => Ok(Self::CreatePipelineLayout), + 9 => Ok(Self::CreateBindGroup), + 10 => Ok(Self::CreateRenderPipeline), + 11 => Ok(Self::DestroyResource), + 12 => Ok(Self::BeginRenderPass), + 13 => Ok(Self::SetPipeline), + 14 => Ok(Self::SetVertexBuffer), + 15 => Ok(Self::SetIndexBuffer), + 16 => Ok(Self::SetBindGroup), + 17 => Ok(Self::SetViewport), + 18 => Ok(Self::SetScissorRect), + 19 => Ok(Self::Draw), + 20 => Ok(Self::DrawIndexed), + 21 => Ok(Self::EndRenderPass), + 22 => Ok(Self::CopyBufferToBuffer), + 23 => Ok(Self::CreateTextureView), + _ => Err(()), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuTextureFormat { + Rgba8Unorm = 1, + Rgba8UnormSrgb = 2, + Bgra8Unorm = 3, + Bgra8UnormSrgb = 4, + Depth24Plus = 5, + Depth32Float = 6, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuVertexFormat { + Float32 = 1, + Float32x2 = 2, + Float32x3 = 3, + Float32x4 = 4, + Uint32 = 5, + Uint32x2 = 6, + Uint32x4 = 7, + Unorm8x2 = 8, + Unorm8x4 = 9, + Snorm8x2 = 10, + Snorm8x4 = 11, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuIndexFormat { + Uint16 = 1, + Uint32 = 2, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuAddressMode { + ClampToEdge = 1, + Repeat = 2, + MirrorRepeat = 3, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuFilterMode { + Nearest = 1, + Linear = 2, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuCompareFunction { + Never = 1, + Less = 2, + Equal = 3, + LessEqual = 4, + Greater = 5, + NotEqual = 6, + GreaterEqual = 7, + Always = 8, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuBlendOperation { + Add = 1, + Subtract = 2, + ReverseSubtract = 3, + Min = 4, + Max = 5, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuBlendFactor { + Zero = 1, + One = 2, + Src = 3, + OneMinusSrc = 4, + SrcAlpha = 5, + OneMinusSrcAlpha = 6, + Dst = 7, + OneMinusDst = 8, + DstAlpha = 9, + OneMinusDstAlpha = 10, + SrcAlphaSaturated = 11, + Constant = 12, + OneMinusConstant = 13, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuPrimitiveTopology { + PointList = 1, + LineList = 2, + LineStrip = 3, + TriangleList = 4, + TriangleStrip = 5, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuFrontFace { + Ccw = 1, + Cw = 2, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuCullMode { + Front = 1, + Back = 2, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u16)] +pub enum GpuBindingKind { + UniformBuffer = 1, + Sampler = 2, + Texture = 3, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum GpuSamplerBindingType { + Filtering = 1, + NonFiltering = 2, + Comparison = 3, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum GpuTextureSampleType { + FloatFilterable = 1, + FloatUnfilterable = 2, + Depth = 3, + Sint = 4, + Uint = 5, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuVertexStepMode { + Vertex = 1, + Instance = 2, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuTextureDimension { + D2 = 1, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum GpuTextureViewDimension { + D2 = 1, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum GpuTextureAspect { + All = 1, + DepthOnly = 2, +} + +pub const GPU_BUFFER_USAGE_COPY_SRC: u32 = 4; +pub const GPU_BUFFER_USAGE_COPY_DST: u32 = 8; +pub const GPU_BUFFER_USAGE_INDEX: u32 = 16; +pub const GPU_BUFFER_USAGE_VERTEX: u32 = 32; +pub const GPU_BUFFER_USAGE_UNIFORM: u32 = 64; +pub const GPU_TEXTURE_USAGE_COPY_SRC: u32 = 1; +pub const GPU_TEXTURE_USAGE_COPY_DST: u32 = 2; +pub const GPU_TEXTURE_USAGE_TEXTURE_BINDING: u32 = 4; +pub const GPU_TEXTURE_USAGE_RENDER_ATTACHMENT: u32 = 16; +pub const GPU_SHADER_STAGE_VERTEX: u32 = 1; +pub const GPU_SHADER_STAGE_FRAGMENT: u32 = 2; +pub const GPU_COLOR_WRITE_RED: u16 = 1; +pub const GPU_COLOR_WRITE_GREEN: u16 = 2; +pub const GPU_COLOR_WRITE_BLUE: u16 = 4; +pub const GPU_COLOR_WRITE_ALPHA: u16 = 8; +pub const GPU_RENDER_PASS_COLOR_LOAD: u32 = 1; +pub const GPU_RENDER_PASS_COLOR_STORE: u32 = 2; +pub const GPU_RENDER_PASS_DEPTH_LOAD: u32 = 4; +pub const GPU_RENDER_PASS_DEPTH_STORE: u32 = 8; +pub const GPU_BINDING_HAS_DYNAMIC_OFFSET: u16 = 1; +pub const GPU_PIPELINE_DEPTH_WRITE: u16 = 1; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum GpuWireError { + BatchTooLarge { + actual: usize, + }, + TruncatedBatchHeader { + actual: usize, + }, + InvalidMagic, + UnsupportedVersion { + version: u16, + }, + ReservedBatchFlags { + flags: u16, + }, + BatchLengthMismatch { + declared: usize, + actual: usize, + }, + EmptySequence, + TooManyCommands { + count: u32, + }, + TruncatedCommandHeader { + index: u32, + }, + InvalidCommandLength { + index: u32, + length: usize, + }, + ReservedCommandFlags { + index: u32, + flags: u16, + }, + UnknownOpcode { + index: u32, + opcode: u16, + }, + InvalidPayloadLength { + index: u32, + opcode: GpuOpcode, + expected: usize, + actual: usize, + }, + InvalidWgslUtf8 { + index: u32, + }, + NonZeroPadding { + index: u32, + }, + TrailingCommandBytes { + actual: usize, + }, + IntegerOverflow { + index: u32, + }, +} + +impl fmt::Display for GpuWireError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::BatchTooLarge { actual } => { + write!(formatter, "GPU batch is {actual} bytes; maximum is {MAX_GPU_BATCH_BYTES}") + } + Self::TruncatedBatchHeader { actual } => write!( + formatter, + "GPU batch header is truncated: got {actual} bytes, need {GPU_BATCH_HEADER_BYTES}" + ), + Self::InvalidMagic => formatter.write_str("GPU batch has invalid magic"), + Self::UnsupportedVersion { version } => { + write!(formatter, "unsupported GPU wire version {version}") + } + Self::ReservedBatchFlags { flags } => { + write!(formatter, "GPU batch has reserved flags 0x{flags:04x}") + } + Self::BatchLengthMismatch { declared, actual } => write!( + formatter, + "GPU batch declares {declared} bytes but contains {actual}" + ), + Self::EmptySequence => formatter.write_str("GPU batch sequence must be nonzero"), + Self::TooManyCommands { count } => write!( + formatter, + "GPU batch declares {count} commands; maximum is {MAX_GPU_COMMANDS}" + ), + Self::TruncatedCommandHeader { index } => { + write!(formatter, "GPU command {index} header is truncated") + } + Self::InvalidCommandLength { index, length } => write!( + formatter, + "GPU command {index} has invalid aligned length {length}" + ), + Self::ReservedCommandFlags { index, flags } => write!( + formatter, + "GPU command {index} has reserved flags 0x{flags:04x}" + ), + Self::UnknownOpcode { index, opcode } => { + write!(formatter, "GPU command {index} uses unknown opcode {opcode}") + } + Self::InvalidPayloadLength { + index, + opcode, + expected, + actual, + } => write!( + formatter, + "GPU command {index} ({opcode:?}) needs {expected} payload bytes but contains {actual}" + ), + Self::InvalidWgslUtf8 { index } => { + write!(formatter, "GPU command {index} contains non-UTF-8 WGSL") + } + Self::NonZeroPadding { index } => { + write!(formatter, "GPU command {index} has nonzero padding") + } + Self::TrailingCommandBytes { actual } => { + write!(formatter, "GPU batch has {actual} trailing command bytes") + } + Self::IntegerOverflow { index } => { + write!(formatter, "GPU command {index} payload length overflows") + } + } + } +} + +impl core::error::Error for GpuWireError {} + +#[derive(Clone, Copy, Debug)] +pub struct GpuBatch<'a> { + sequence: u64, + command_count: u32, + command_bytes: &'a [u8], +} + +impl<'a> GpuBatch<'a> { + pub fn sequence(self) -> u64 { + self.sequence + } + + pub fn command_count(self) -> u32 { + self.command_count + } + + pub fn commands(self) -> GpuCommands<'a> { + GpuCommands { + bytes: self.command_bytes, + remaining: self.command_count, + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct GpuCommand<'a> { + pub opcode: GpuOpcode, + pub payload: &'a [u8], +} + +pub struct GpuCommands<'a> { + bytes: &'a [u8], + remaining: u32, +} + +impl<'a> Iterator for GpuCommands<'a> { + type Item = GpuCommand<'a>; + + fn next(&mut self) -> Option { + if self.remaining == 0 { + return None; + } + let opcode = GpuOpcode::try_from(u16_at(self.bytes, 0)).ok()?; + let command_bytes = u32_at(self.bytes, 4) as usize; + let payload = &self.bytes[GPU_COMMAND_HEADER_BYTES..command_bytes]; + self.bytes = &self.bytes[command_bytes..]; + self.remaining -= 1; + Some(GpuCommand { opcode, payload }) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.remaining as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for GpuCommands<'_> {} + +pub fn decode_gpu_batch(bytes: &[u8]) -> Result, GpuWireError> { + if bytes.len() > MAX_GPU_BATCH_BYTES { + return Err(GpuWireError::BatchTooLarge { + actual: bytes.len(), + }); + } + if bytes.len() < GPU_BATCH_HEADER_BYTES { + return Err(GpuWireError::TruncatedBatchHeader { + actual: bytes.len(), + }); + } + if bytes[..4] != GPU_WIRE_MAGIC { + return Err(GpuWireError::InvalidMagic); + } + let version = u16_at(bytes, 4); + if version != GPU_WIRE_VERSION { + return Err(GpuWireError::UnsupportedVersion { version }); + } + let flags = u16_at(bytes, 6); + if flags != 0 { + return Err(GpuWireError::ReservedBatchFlags { flags }); + } + let declared_length = u32_at(bytes, 8) as usize; + if declared_length != bytes.len() { + return Err(GpuWireError::BatchLengthMismatch { + declared: declared_length, + actual: bytes.len(), + }); + } + let command_count = u32_at(bytes, 12); + if command_count > MAX_GPU_COMMANDS { + return Err(GpuWireError::TooManyCommands { + count: command_count, + }); + } + let sequence = u64_at(bytes, 16); + if sequence == 0 { + return Err(GpuWireError::EmptySequence); + } + + let mut command_bytes = &bytes[GPU_BATCH_HEADER_BYTES..]; + for index in 0..command_count { + if command_bytes.len() < GPU_COMMAND_HEADER_BYTES { + return Err(GpuWireError::TruncatedCommandHeader { index }); + } + let opcode_value = u16_at(command_bytes, 0); + let command_flags = u16_at(command_bytes, 2); + if command_flags != 0 { + return Err(GpuWireError::ReservedCommandFlags { + index, + flags: command_flags, + }); + } + let command_length = u32_at(command_bytes, 4) as usize; + if command_length < GPU_COMMAND_HEADER_BYTES + || !command_length.is_multiple_of(4) + || command_length > command_bytes.len() + { + return Err(GpuWireError::InvalidCommandLength { + index, + length: command_length, + }); + } + let opcode = + GpuOpcode::try_from(opcode_value).map_err(|()| GpuWireError::UnknownOpcode { + index, + opcode: opcode_value, + })?; + validate_payload( + index, + opcode, + &command_bytes[GPU_COMMAND_HEADER_BYTES..command_length], + )?; + command_bytes = &command_bytes[command_length..]; + } + if !command_bytes.is_empty() { + return Err(GpuWireError::TrailingCommandBytes { + actual: command_bytes.len(), + }); + } + + Ok(GpuBatch { + sequence, + command_count, + command_bytes: &bytes[GPU_BATCH_HEADER_BYTES..], + }) +} + +fn validate_payload(index: u32, opcode: GpuOpcode, payload: &[u8]) -> Result<(), GpuWireError> { + match opcode { + GpuOpcode::CreateBuffer => exact_payload(index, opcode, payload, 16), + GpuOpcode::WriteBuffer => inline_payload(index, opcode, payload, 24, 16, false), + GpuOpcode::CreateTexture => exact_payload(index, opcode, payload, 24), + GpuOpcode::WriteTexture => inline_payload(index, opcode, payload, 44, 40, false), + GpuOpcode::CreateSampler => exact_payload(index, opcode, payload, 24), + GpuOpcode::CreateShaderWgsl => inline_payload(index, opcode, payload, 8, 4, true), + GpuOpcode::CreateBindGroupLayout => counted_payload(index, opcode, payload, 8, 4, 32), + GpuOpcode::CreatePipelineLayout => counted_payload(index, opcode, payload, 8, 4, 4), + GpuOpcode::CreateBindGroup => counted_payload(index, opcode, payload, 12, 8, 32), + GpuOpcode::CreateRenderPipeline => pipeline_payload(index, opcode, payload), + GpuOpcode::DestroyResource | GpuOpcode::SetPipeline => { + exact_payload(index, opcode, payload, 4) + } + GpuOpcode::BeginRenderPass => exact_payload(index, opcode, payload, 36), + GpuOpcode::CopyBufferToBuffer => exact_payload(index, opcode, payload, 32), + GpuOpcode::SetVertexBuffer | GpuOpcode::SetIndexBuffer | GpuOpcode::SetViewport => { + exact_payload(index, opcode, payload, 24) + } + GpuOpcode::SetBindGroup => counted_payload(index, opcode, payload, 12, 8, 4), + GpuOpcode::SetScissorRect | GpuOpcode::Draw => exact_payload(index, opcode, payload, 16), + GpuOpcode::DrawIndexed => exact_payload(index, opcode, payload, 20), + GpuOpcode::EndRenderPass => exact_payload(index, opcode, payload, 0), + GpuOpcode::CreateTextureView => exact_payload(index, opcode, payload, 20), + } +} + +fn exact_payload( + index: u32, + opcode: GpuOpcode, + payload: &[u8], + expected: usize, +) -> Result<(), GpuWireError> { + if payload.len() != expected { + return Err(GpuWireError::InvalidPayloadLength { + index, + opcode, + expected, + actual: payload.len(), + }); + } + Ok(()) +} + +fn inline_payload( + index: u32, + opcode: GpuOpcode, + payload: &[u8], + header_bytes: usize, + length_offset: usize, + utf8: bool, +) -> Result<(), GpuWireError> { + if payload.len() < header_bytes { + return Err(GpuWireError::InvalidPayloadLength { + index, + opcode, + expected: header_bytes, + actual: payload.len(), + }); + } + let inline_bytes = u32_at(payload, length_offset) as usize; + let unpadded = header_bytes + .checked_add(inline_bytes) + .ok_or(GpuWireError::IntegerOverflow { index })?; + let expected = align4(unpadded).ok_or(GpuWireError::IntegerOverflow { index })?; + if payload.len() != expected { + return Err(GpuWireError::InvalidPayloadLength { + index, + opcode, + expected, + actual: payload.len(), + }); + } + if utf8 && core::str::from_utf8(&payload[header_bytes..unpadded]).is_err() { + return Err(GpuWireError::InvalidWgslUtf8 { index }); + } + if payload[unpadded..].iter().any(|byte| *byte != 0) { + return Err(GpuWireError::NonZeroPadding { index }); + } + Ok(()) +} + +fn counted_payload( + index: u32, + opcode: GpuOpcode, + payload: &[u8], + header_bytes: usize, + count_offset: usize, + stride: usize, +) -> Result<(), GpuWireError> { + if payload.len() < header_bytes { + return Err(GpuWireError::InvalidPayloadLength { + index, + opcode, + expected: header_bytes, + actual: payload.len(), + }); + } + let count = u32_at(payload, count_offset) as usize; + let expected = count + .checked_mul(stride) + .and_then(|entries| header_bytes.checked_add(entries)) + .ok_or(GpuWireError::IntegerOverflow { index })?; + exact_payload(index, opcode, payload, expected) +} + +fn pipeline_payload(index: u32, opcode: GpuOpcode, payload: &[u8]) -> Result<(), GpuWireError> { + const HEADER_BYTES: usize = 40; + if payload.len() < HEADER_BYTES { + return Err(GpuWireError::InvalidPayloadLength { + index, + opcode, + expected: HEADER_BYTES, + actual: payload.len(), + }); + } + let vertex_layouts = u16_at(payload, 12) as usize; + let vertex_attributes = u16_at(payload, 14) as usize; + let color_targets = u16_at(payload, 16) as usize; + let expected = vertex_layouts + .checked_mul(16) + .and_then(|value| { + vertex_attributes + .checked_mul(16) + .and_then(|next| value.checked_add(next)) + }) + .and_then(|value| { + color_targets + .checked_mul(16) + .and_then(|next| value.checked_add(next)) + }) + .and_then(|arrays| HEADER_BYTES.checked_add(arrays)) + .ok_or(GpuWireError::IntegerOverflow { index })?; + exact_payload(index, opcode, payload, expected) +} + +fn align4(value: usize) -> Option { + value.checked_add(3).map(|value| value & !3) +} + +fn u16_at(bytes: &[u8], offset: usize) -> u16 { + u16::from_le_bytes( + bytes[offset..offset + 2] + .try_into() + .expect("validated field offset"), + ) +} + +fn u32_at(bytes: &[u8], offset: usize) -> u32 { + u32::from_le_bytes( + bytes[offset..offset + 4] + .try_into() + .expect("validated field offset"), + ) +} + +fn u64_at(bytes: &[u8], offset: usize) -> u64 { + u64::from_le_bytes( + bytes[offset..offset + 8] + .try_into() + .expect("validated field offset"), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + extern crate std; + + use std::vec; + use std::vec::Vec; + + const DESTROY_FIXTURE: [u8; 36] = [ + 0x45, 0x50, 0x47, 0x31, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + ]; + + #[test] + fn decodes_golden_destroy_fixture_without_allocating() { + let batch = decode_gpu_batch(&DESTROY_FIXTURE).expect("valid fixture"); + assert_eq!(batch.sequence(), 7); + assert_eq!(batch.command_count(), 1); + let commands = batch.commands().collect::>(); + assert_eq!(commands.len(), 1); + assert_eq!(commands[0].opcode, GpuOpcode::DestroyResource); + assert_eq!(commands[0].payload, 42u32.to_le_bytes()); + } + + #[test] + fn accepts_padded_utf8_shader_source() { + let mut payload = Vec::new(); + payload.extend_from_slice(&9u32.to_le_bytes()); + payload.extend_from_slice(&3u32.to_le_bytes()); + payload.extend_from_slice(b"abc"); + payload.push(0); + let batch = single_command(GpuOpcode::CreateShaderWgsl, &payload); + assert!(decode_gpu_batch(&batch).is_ok()); + } + + #[test] + fn rejects_unknown_opcode() { + let mut batch = DESTROY_FIXTURE; + batch[24..26].copy_from_slice(&99u16.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::UnknownOpcode { + index: 0, + opcode: 99, + } + ); + } + + #[test] + fn rejects_declared_batch_length_mismatch() { + let mut batch = DESTROY_FIXTURE; + batch[8..12].copy_from_slice(&35u32.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::BatchLengthMismatch { + declared: 35, + actual: 36, + } + ); + } + + #[test] + fn rejects_extra_well_formed_command() { + let mut batch = DESTROY_FIXTURE.to_vec(); + batch.extend_from_slice(&DESTROY_FIXTURE[24..]); + let batch_length = batch.len() as u32; + batch[8..12].copy_from_slice(&batch_length.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::TrailingCommandBytes { actual: 12 } + ); + } + + #[test] + fn rejects_nonzero_inline_padding() { + let mut payload = Vec::new(); + payload.extend_from_slice(&9u32.to_le_bytes()); + payload.extend_from_slice(&1u32.to_le_bytes()); + payload.extend_from_slice(b"x"); + payload.extend_from_slice(&[0, 1, 0]); + let batch = single_command(GpuOpcode::CreateShaderWgsl, &payload); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::NonZeroPadding { index: 0 } + ); + } + + #[test] + fn rejects_non_utf8_shader_source() { + let mut payload = Vec::new(); + payload.extend_from_slice(&9u32.to_le_bytes()); + payload.extend_from_slice(&1u32.to_le_bytes()); + payload.extend_from_slice(&[0xff, 0, 0, 0]); + let batch = single_command(GpuOpcode::CreateShaderWgsl, &payload); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::InvalidWgslUtf8 { index: 0 } + ); + } + + #[test] + fn rejects_variable_payload_count_mismatch() { + let mut payload = Vec::new(); + payload.extend_from_slice(&1u32.to_le_bytes()); + payload.extend_from_slice(&2u32.to_le_bytes()); + payload.extend_from_slice(&7u32.to_le_bytes()); + let batch = single_command(GpuOpcode::CreatePipelineLayout, &payload); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::InvalidPayloadLength { + index: 0, + opcode: GpuOpcode::CreatePipelineLayout, + expected: 16, + actual: 12, + } + ); + } + + #[test] + fn accepts_every_v1_opcode_payload_shape() { + let payloads = [ + (GpuOpcode::CreateBuffer, 16), + (GpuOpcode::WriteBuffer, 24), + (GpuOpcode::CreateTexture, 24), + (GpuOpcode::WriteTexture, 44), + (GpuOpcode::CreateSampler, 24), + (GpuOpcode::CreateShaderWgsl, 8), + (GpuOpcode::CreateBindGroupLayout, 8), + (GpuOpcode::CreatePipelineLayout, 8), + (GpuOpcode::CreateBindGroup, 12), + (GpuOpcode::CreateRenderPipeline, 40), + (GpuOpcode::DestroyResource, 4), + (GpuOpcode::BeginRenderPass, 36), + (GpuOpcode::SetPipeline, 4), + (GpuOpcode::SetVertexBuffer, 24), + (GpuOpcode::SetIndexBuffer, 24), + (GpuOpcode::SetBindGroup, 12), + (GpuOpcode::SetViewport, 24), + (GpuOpcode::SetScissorRect, 16), + (GpuOpcode::Draw, 16), + (GpuOpcode::DrawIndexed, 20), + (GpuOpcode::EndRenderPass, 0), + (GpuOpcode::CopyBufferToBuffer, 32), + (GpuOpcode::CreateTextureView, 20), + ]; + for (opcode, payload_bytes) in payloads { + let batch = single_command(opcode, &vec![0; payload_bytes]); + decode_gpu_batch(&batch) + .unwrap_or_else(|error| panic!("{opcode:?} fixture failed: {error}")); + } + } + + #[test] + fn rejects_unaligned_command_length() { + let mut batch = DESTROY_FIXTURE; + batch[28..32].copy_from_slice(&10u32.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::InvalidCommandLength { + index: 0, + length: 10, + } + ); + } + + #[test] + fn rejects_reserved_batch_flags() { + let mut batch = DESTROY_FIXTURE; + batch[6..8].copy_from_slice(&1u16.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::ReservedBatchFlags { flags: 1 } + ); + } + + #[test] + fn rejects_zero_sequence() { + let mut batch = DESTROY_FIXTURE; + batch[16..24].fill(0); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::EmptySequence + ); + } + + #[test] + fn rejects_trailing_partial_command() { + let mut batch = DESTROY_FIXTURE.to_vec(); + batch.push(0); + let batch_length = batch.len() as u32; + batch[8..12].copy_from_slice(&batch_length.to_le_bytes()); + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::TrailingCommandBytes { actual: 1 } + ); + } + + #[test] + fn rejects_batches_above_the_byte_ceiling_before_decoding() { + let batch = vec![0; MAX_GPU_BATCH_BYTES + 1]; + assert_eq!( + decode_gpu_batch(&batch).unwrap_err(), + GpuWireError::BatchTooLarge { + actual: MAX_GPU_BATCH_BYTES + 1, + } + ); + } + + fn single_command(opcode: GpuOpcode, payload: &[u8]) -> Vec { + assert_eq!(payload.len() % 4, 0); + let command_length = GPU_COMMAND_HEADER_BYTES + payload.len(); + let batch_length = GPU_BATCH_HEADER_BYTES + command_length; + let mut bytes = Vec::with_capacity(batch_length); + bytes.extend_from_slice(&GPU_WIRE_MAGIC); + bytes.extend_from_slice(&GPU_WIRE_VERSION.to_le_bytes()); + bytes.extend_from_slice(&0u16.to_le_bytes()); + bytes.extend_from_slice(&(batch_length as u32).to_le_bytes()); + bytes.extend_from_slice(&1u32.to_le_bytes()); + bytes.extend_from_slice(&1u64.to_le_bytes()); + bytes.extend_from_slice(&(opcode as u16).to_le_bytes()); + bytes.extend_from_slice(&0u16.to_le_bytes()); + bytes.extend_from_slice(&(command_length as u32).to_le_bytes()); + bytes.extend_from_slice(payload); + bytes + } +} diff --git a/rust/crates/pvm-runtime/Cargo.toml b/rust/crates/pvm-runtime/Cargo.toml new file mode 100644 index 000000000..bf20c43d2 --- /dev/null +++ b/rust/crates/pvm-runtime/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "pvm-runtime" +version = "0.1.0" +edition = "2021" +description = "Host-neutral App manifest v2 PolkaVM runtime and browser execution wrapper" +license = "MPL-2.0" +publish = false + +[lib] +crate-type = ["rlib", "cdylib", "staticlib"] + +[features] +ffi = ["dep:thiserror", "dep:uniffi"] + +[dependencies] +anyhow = "1" +log = "0.4" +pvm-gpu-wire = { path = "../pvm-gpu-wire" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +wasm-encoder = "0.240" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +polkavm = { git = "https://github.com/paritytech/polkavm.git", rev = "8cc3c5601aa614b0f6dfac2ae3ee271942b3c997", version = "=0.37.0", features = ["generic-sandbox"] } +thiserror = { version = "1", optional = true } +uniffi = { workspace = true, optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +polkavm-wasm = { package = "polkavm", git = "https://github.com/paritytech/polkavm.git", rev = "3df1d0309c4c81a1aad0a755d83570d203bba1d9", version = "=0.37.0", features = ["generic-sandbox"] } + +[dev-dependencies] +wasmi = "0.40" +wasmparser = "0.240" diff --git a/rust/crates/pvm-runtime/src/application.rs b/rust/crates/pvm-runtime/src/application.rs new file mode 100644 index 000000000..af05edd32 --- /dev/null +++ b/rust/crates/pvm-runtime/src/application.rs @@ -0,0 +1,342 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::corevm::{Interruption, Vm}; +use crate::{ + AudioChunk, Frame, GpuBatch, InputEvent, InputEventType, PresentationProfile, Runtime, + Tri2dFrame, MAX_FRAME_BYTES, MAX_GUEST_RW_DATA_BYTES, MAX_GUEST_STACK_BYTES, +}; +use anyhow::{anyhow, Context, Result}; +use polkavm::ProgramBlob; +use std::collections::{HashMap, VecDeque}; + +const MAX_INTERRUPTS_PER_UPDATE: usize = 8_192; +const MAX_QUEUED_AUDIO_CHUNKS: usize = 64; + +pub enum ApplicationRuntime { + Cooperative(Runtime), + CoreVm(CoreVmRuntime), +} + +pub struct CoreVmRuntime { + vm: Vm, + frame: Option, + audio: VecDeque, + palette: [[u8; 3]; 256], + sample_rate: u32, + channels: u32, + pointer: Option<(u16, u16)>, + audio_enabled: bool, + max_gas_per_update: u64, + exited: bool, +} + +impl ApplicationRuntime { + pub fn new( + program: &[u8], + assets: HashMap>, + presentation: PresentationProfile, + audio_enabled: bool, + max_gas_per_update: u64, + ) -> Result { + Self::new_with_backend( + program, + assets, + presentation, + audio_enabled, + max_gas_per_update, + crate::preferred_backend(), + ) + } + + pub fn new_with_backend( + program: &[u8], + assets: HashMap>, + presentation: PresentationProfile, + audio_enabled: bool, + max_gas_per_update: u64, + backend: polkavm::BackendKind, + ) -> Result { + let blob = ProgramBlob::parse(program.into()).context("parse PolkaVM program")?; + if blob.rw_data_size() > MAX_GUEST_RW_DATA_BYTES { + return Err(anyhow!( + "guest read-write data exceeds {MAX_GUEST_RW_DATA_BYTES} bytes" + )); + } + if blob.stack_size() > MAX_GUEST_STACK_BYTES { + return Err(anyhow!("guest stack exceeds {MAX_GUEST_STACK_BYTES} bytes")); + } + let is_corevm = blob.exports().any(|export| export.symbol() == "_pvm_start"); + if !is_corevm { + return Runtime::new_with_backend( + program, + assets, + presentation, + audio_enabled, + max_gas_per_update, + backend, + ) + .map(Self::Cooperative); + } + if presentation != PresentationProfile::Framebuffer { + return Err(anyhow!( + "CoreVM guests require the framebuffer presentation profile" + )); + } + + let mut vm = Vm::from_blob(blob, backend).context("create CoreVM guest")?; + for (path, bytes) in assets { + vm.register_file(&path, bytes); + } + vm.setup(["./quake"]).map_err(|error| anyhow!(error))?; + Ok(Self::CoreVm(CoreVmRuntime { + vm, + frame: None, + audio: VecDeque::new(), + palette: [[255; 3]; 256], + sample_rate: 0, + channels: 0, + audio_enabled, + pointer: None, + max_gas_per_update, + exited: false, + })) + } + + pub fn init(&mut self) -> Result<()> { + match self { + Self::Cooperative(runtime) => runtime.init(), + Self::CoreVm(_) => Ok(()), + } + } + + pub fn update(&mut self) -> Result<()> { + match self { + Self::Cooperative(runtime) => runtime.update(), + Self::CoreVm(runtime) => runtime.update(), + } + } + + pub fn backend(&self) -> polkavm::BackendKind { + match self { + Self::Cooperative(runtime) => runtime.backend(), + Self::CoreVm(runtime) => runtime.vm.backend(), + } + } + pub fn last_gas_used(&self) -> u64 { + match self { + Self::Cooperative(runtime) => runtime.last_gas_used(), + Self::CoreVm(runtime) => runtime + .max_gas_per_update + .saturating_sub(runtime.vm.gas_remaining()), + } + } + + pub fn send_input(&mut self, event: InputEvent) { + match self { + Self::Cooperative(runtime) => runtime.send_input(event), + Self::CoreVm(runtime) => runtime.send_input(event), + } + } + + pub fn gpu_ready(&self) -> bool { + match self { + Self::Cooperative(runtime) => runtime.gpu_ready(), + Self::CoreVm(_) => true, + } + } + + pub fn set_gpu_capabilities(&mut self, bytes: Vec) -> Result<()> { + match self { + Self::Cooperative(runtime) => runtime.set_gpu_capabilities(bytes), + Self::CoreVm(_) => Err(anyhow!("CoreVM does not support GPU capabilities")), + } + } + + pub fn send_gpu_event(&mut self, bytes: Vec) -> Result<()> { + match self { + Self::Cooperative(runtime) => runtime.send_gpu_event(bytes), + Self::CoreVm(_) => Err(anyhow!("CoreVM does not support GPU events")), + } + } + + pub fn take_gpu_batch(&mut self) -> Option { + match self { + Self::Cooperative(runtime) => runtime.take_gpu_batch(), + Self::CoreVm(_) => None, + } + } + + #[cfg(target_arch = "wasm32")] + pub fn set_time_ms(&mut self, time_ms: u64) { + if let Self::Cooperative(runtime) = self { + runtime.set_time_ms(time_ms); + } + } + + pub fn take_frame(&mut self) -> Option { + match self { + Self::Cooperative(runtime) => runtime.take_frame(), + Self::CoreVm(runtime) => runtime.frame.take(), + } + } + pub fn take_tri2d(&mut self) -> Option { + match self { + Self::Cooperative(runtime) => runtime.take_tri2d(), + Self::CoreVm(_) => None, + } + } + + pub fn take_audio(&mut self) -> Option { + match self { + Self::Cooperative(runtime) => runtime.take_audio(), + Self::CoreVm(runtime) => runtime.audio.pop_front(), + } + } + + pub fn take_log(&mut self) -> Option { + match self { + Self::Cooperative(runtime) => runtime.take_log(), + Self::CoreVm(_) => None, + } + } + + pub fn is_exited(&self) -> bool { + matches!(self, Self::CoreVm(runtime) if runtime.exited) + } + + pub fn take_save(&mut self) -> Option> { + match self { + Self::Cooperative(runtime) => runtime.take_save(), + Self::CoreVm(_) => None, + } + } +} + +impl CoreVmRuntime { + fn update(&mut self) -> Result<()> { + if self.exited { + return Ok(()); + } + self.vm.set_gas(self.max_gas_per_update); + for _ in 0..MAX_INTERRUPTS_PER_UPDATE { + match self.vm.run().map_err(|error| anyhow!(error))? { + Interruption::Exit => { + self.exited = true; + return Ok(()); + } + Interruption::Yield => return Ok(()), + Interruption::SetPalette { palette } => { + if palette.len() != 256 * 3 { + return Err(anyhow!("guest supplied an invalid Quake palette")); + } + for (target, source) in self.palette.iter_mut().zip(palette.chunks_exact(3)) { + target.copy_from_slice(source); + } + } + Interruption::Display { + width, + height, + framebuffer, + } => { + let width = u32::try_from(width).context("Quake frame width overflow")?; + let height = u32::try_from(height).context("Quake frame height overflow")?; + let pixels = width + .checked_mul(height) + .ok_or_else(|| anyhow!("Quake frame dimensions overflow"))? + as usize; + if pixels == 0 || pixels > MAX_FRAME_BYTES / 4 || framebuffer.len() != pixels { + return Err(anyhow!("guest supplied an invalid Quake frame")); + } + let mut argb = Vec::with_capacity(pixels * 4); + for index in framebuffer { + let [red, green, blue] = self.palette[index as usize]; + argb.extend_from_slice(&[blue, green, red, 255]); + } + self.frame = Some(Frame { + width, + height, + argb, + }); + return Ok(()); + } + Interruption::AudioInit { + channels, + sample_rate, + } => { + if !self.audio_enabled { + self.channels = 0; + self.sample_rate = 0; + continue; + } + if !(1..=2).contains(&channels) || !(8_000..=96_000).contains(&sample_rate) { + return Err(anyhow!("guest requested an unsupported audio format")); + } + self.channels = channels; + self.sample_rate = sample_rate; + } + Interruption::AudioFrame { buffer } => { + if !self.audio_enabled { + continue; + } + if self.channels == 0 { + self.channels = crate::AUDIO_CHANNELS; + self.sample_rate = crate::AUDIO_SAMPLE_RATE; + } + if buffer.is_empty() || buffer.len() % self.channels as usize != 0 { + continue; + } + if self.audio.len() == MAX_QUEUED_AUDIO_CHUNKS { + self.audio.pop_front(); + } + self.audio.push_back(AudioChunk { + samples: buffer, + sample_rate: self.sample_rate, + channels: self.channels, + }); + } + } + } + Err(anyhow!("guest exceeded interruption budget")) + } + + fn send_input(&mut self, event: InputEvent) { + if self.vm.uses_epoca_inputs() { + self.vm.send_epoca_input(event); + return; + } + match event.event_type { + InputEventType::KeyDown | InputEventType::KeyUp => { + if let Some(key) = crate::quake_keys::from_hid(event.code) { + self.vm + .send_key(key, event.event_type == InputEventType::KeyDown); + } + } + InputEventType::ButtonDown | InputEventType::ButtonUp => { + if let Some(key) = crate::quake_keys::from_button(event.code) { + self.vm + .send_key(key, event.event_type == InputEventType::ButtonDown); + } + } + InputEventType::PointerMove => { + if let Some((previous_x, previous_y)) = self.pointer { + self.vm.send_mouse_move( + signed_delta(event.x, previous_x), + signed_delta(event.y, previous_y), + ); + } + self.pointer = Some((event.x, event.y)); + } + InputEventType::PointerDelta => self.vm.send_mouse_move( + (event.x as i16).clamp(i8::MIN as i16, i8::MAX as i16) as i8, + (event.y as i16).clamp(i8::MIN as i16, i8::MAX as i16) as i8, + ), + InputEventType::SurfaceMetrics => {} + } + } +} + +fn signed_delta(current: u16, previous: u16) -> i8 { + (i32::from(current) - i32::from(previous)).clamp(i8::MIN as i32, i8::MAX as i32) as i8 +} diff --git a/rust/crates/pvm-runtime/src/corevm.rs b/rust/crates/pvm-runtime/src/corevm.rs new file mode 100644 index 000000000..492ba8511 --- /dev/null +++ b/rust/crates/pvm-runtime/src/corevm.rs @@ -0,0 +1,851 @@ +/* SPDX-License-Identifier: Apache-2.0 OR MIT + * Vendored from paritytech/polkavm examples/quake/src/vm.rs at + * 3df1d0309c4c81a1aad0a755d83570d203bba1d9 and adapted for Epoca. + */ + +#![allow(non_upper_case_globals)] + +use polkavm::{ + Config, Engine, GasMeteringKind, InterruptKind, MemoryAccessError, Module, ModuleConfig, + ProgramBlob, ProgramCounter, RawInstance, Reg, +}; +use std::collections::{BTreeMap, VecDeque}; +use std::mem::MaybeUninit; +use std::sync::Arc; +use std::time::Instant; + +struct File { + blob: Vec, +} + +struct Fd { + file: Arc, + position: u64, +} + +struct OpenFiles { + descriptors: BTreeMap, + next: u64, +} + +impl OpenFiles { + fn new() -> Self { + Self { + descriptors: BTreeMap::new(), + next: 3, + } + } + + fn open(&mut self, file: Arc) -> Result { + if self.descriptors.len() >= MAX_OPEN_FILES { + return Err(EMFILE); + } + let fd = self.next; + self.next = self.next.checked_add(1).ok_or(EMFILE)?; + self.descriptors.insert(fd, Fd { file, position: 0 }); + Ok(fd) + } + + fn get_mut(&mut self, fd: u64) -> Option<&mut Fd> { + self.descriptors.get_mut(&fd) + } + + fn remove(&mut self, fd: u64) -> Option { + self.descriptors.remove(&fd) + } +} + +pub struct Vm { + start: ProgramCounter, + instance: RawInstance, + backend: polkavm::BackendKind, + filesystem: BTreeMap, Arc>, + open_files: OpenFiles, + input_events: VecDeque, + audio_channels: u32, + epoca_input_events: VecDeque<[u8; crate::INPUT_EVENT_BYTES]>, + started: Instant, + + import_syscall: Option, + import_set_palette: Option, + import_display: Option, + import_fetch_inputs: Option, + import_init_audio: Option, + import_output_audio: Option, + import_epoca_inputs: Option, + import_epoca_audio: Option, + import_asset_read: Option, + import_time_ms: Option, + import_log: Option, + import_yield: Option, +} + +#[derive(Copy, Clone)] +#[repr(C)] +struct InputEvent { + key: u8, + value: u8, +} + +const SYS_read: u64 = 63; +const SYS_readv: u64 = 65; +const SYS_writev: u64 = 66; +const SYS_exit: u64 = 93; +const SYS_openat: u64 = 56; +const SYS_lseek: u64 = 62; +const SYS_close: u64 = 57; +const SEEK_SET: u64 = 0; +const SEEK_CUR: u64 = 1; +const SEEK_END: u64 = 2; +const FILENO_STDOUT: u64 = 1; +const FILENO_STDERR: u64 = 2; +const ENOSYS: u64 = 38; +const EFAULT: u64 = 14; +const ENOENT: u64 = 2; +const EBADF: u64 = 9; +const EACCES: u64 = 13; +const EINVAL: u64 = 22; +const EMFILE: u64 = 24; +const AT_FDCWD: u64 = (-100_i64) as u64; +const IOV_MAX: u64 = 1024; +const MAX_QUEUED_INPUT_EVENTS: usize = 256; +const MAX_GUEST_WRITE_BYTES: u64 = 4 * 1024; +const MAX_OPEN_FILES: usize = 256; +const O_WRONLY: u64 = 1; +const O_RDWR: u64 = 2; +const AT_PAGESZ: u64 = 6; + +fn errno(error: u64) -> u64 { + (-(error as i64)) as u64 +} + +fn normalize_path(path: &str) -> String { + path.trim_start_matches("./") + .trim_start_matches('/') + .to_owned() +} + +fn seek_position(current: u64, length: u64, offset: i64, whence: u64) -> Result { + let base = match whence { + SEEK_SET => 0, + SEEK_CUR => current, + SEEK_END => length, + _ => return Err(EINVAL), + }; + u64::try_from(i128::from(base) + i128::from(offset)).map_err(|_| EINVAL) +} + +fn queued_input_chunks( + events: &VecDeque, + limit: usize, +) -> (&[InputEvent], &[InputEvent]) { + let remaining = limit.min(events.len()); + let (first, second) = events.as_slices(); + let first = &first[..first.len().min(remaining)]; + let second = &second[..second.len().min(remaining - first.len())]; + (first, second) +} + +fn input_destination(address: u64, event_offset: usize) -> Result { + let byte_offset = event_offset + .checked_mul(core::mem::size_of::()) + .and_then(|offset| u64::try_from(offset).ok()) + .ok_or_else(|| "input address overflow".to_owned())?; + address + .checked_add(byte_offset) + .and_then(|address| u32::try_from(address).ok()) + .ok_or_else(|| "input address is out of range".to_owned()) +} + +pub enum Interruption { + Exit, + Yield, + SetPalette { + palette: Vec, + }, + Display { + width: u64, + height: u64, + framebuffer: Vec, + }, + AudioInit { + channels: u32, + sample_rate: u32, + }, + AudioFrame { + buffer: Vec, + }, +} + +impl Vm { + pub fn from_blob( + blob: ProgramBlob, + backend: polkavm::BackendKind, + ) -> Result { + let mut config = Config::new(); + config.set_backend(Some(backend)); + config.set_sandboxing_enabled(true); + #[cfg(target_os = "macos")] + { + config.set_sandbox(Some(polkavm::SandboxKind::Generic)); + config.set_allow_experimental(true); + } + let engine = Engine::new(&config)?; + let backend = engine.backend(); + let mut module_config = ModuleConfig::new(); + module_config.set_gas_metering(Some(GasMeteringKind::Sync)); + #[cfg(not(target_arch = "wasm32"))] + module_config.set_max_heap_size(Some(crate::MAX_GUEST_HEAP_BYTES)); + #[cfg(target_os = "macos")] + module_config.set_page_size(16_384); + let module = Module::from_blob(&engine, &module_config, blob)?; + + let start = module + .exports() + .find(|export| export.symbol() == "_pvm_start") + .ok_or_else(|| "'_pvm_start' export not found".to_string())? + .program_counter(); + + let mut import_syscall = None; + let mut import_set_palette = None; + let mut import_display = None; + let mut import_fetch_inputs = None; + let mut import_init_audio = None; + let mut import_output_audio = None; + let mut import_epoca_inputs = None; + let mut import_epoca_audio = None; + let mut import_asset_read = None; + let mut import_time_ms = None; + let mut import_log = None; + let mut import_yield = None; + + for (import_index, import) in module.imports().into_iter().enumerate() { + let Some(import) = import else { + continue; + }; + + let import_index = import_index as u32; + match import.as_bytes() { + b"pvm_syscall" => import_syscall = Some(import_index), + b"pvm_set_palette" => import_set_palette = Some(import_index), + b"pvm_display" => import_display = Some(import_index), + b"pvm_fetch_inputs" => import_fetch_inputs = Some(import_index), + b"pvm_init_audio" => import_init_audio = Some(import_index), + b"pvm_output_audio" => import_output_audio = Some(import_index), + b"pvm_fetch_epoca_inputs" => import_epoca_inputs = Some(import_index), + b"host_audio_submit" => import_epoca_audio = Some(import_index), + b"pvm_asset_read" => import_asset_read = Some(import_index), + b"pvm_time_ms" => import_time_ms = Some(import_index), + b"host_log" => import_log = Some(import_index), + b"pvm_yield" => import_yield = Some(import_index), + _ => return Err(format!("unsupported import: {}", import).into()), + } + } + + let mut instance = module.instantiate()?; + instance.set_interpreter_guest_memory_limit(Some(crate::MAX_GUEST_HEAP_BYTES as usize)); + Ok(Self { + start, + instance, + backend, + filesystem: BTreeMap::new(), + open_files: OpenFiles::new(), + input_events: VecDeque::with_capacity(MAX_QUEUED_INPUT_EVENTS), + audio_channels: 0, + epoca_input_events: VecDeque::with_capacity(MAX_QUEUED_INPUT_EVENTS), + started: Instant::now(), + import_syscall, + import_set_palette, + import_display, + import_fetch_inputs, + import_init_audio, + import_output_audio, + import_epoca_inputs, + import_epoca_audio, + import_asset_read, + import_time_ms, + import_log, + import_yield, + }) + } + + pub fn backend(&self) -> polkavm::BackendKind { + self.backend + } + + pub fn set_gas(&mut self, gas: u64) { + self.instance.set_gas(gas.min(i64::MAX as u64) as i64); + } + pub fn gas_remaining(&self) -> u64 { + self.instance.gas().max(0) as u64 + } + + fn send_input_event(&mut self, key: u8, value: u8) { + if key == crate::quake_keys::MOUSE_X || key == crate::quake_keys::MOUSE_Y { + if let Some(event) = self.input_events.iter_mut().find(|event| event.key == key) { + event.value = (event.value as i8).saturating_add(value as i8) as u8; + return; + } + } + + if self.input_events.len() == MAX_QUEUED_INPUT_EVENTS { + self.input_events.pop_front(); + } + self.input_events.push_back(InputEvent { key, value }); + } + + pub fn send_key(&mut self, key: u8, is_pressed: bool) { + self.send_input_event(key, if is_pressed { 1 } else { 0 }); + } + + pub fn send_mouse_move(&mut self, delta_x: i8, delta_y: i8) { + if delta_x != 0 { + self.send_input_event(crate::quake_keys::MOUSE_X, delta_x as u8); + } + + if delta_y != 0 { + self.send_input_event(crate::quake_keys::MOUSE_Y, delta_y as u8); + } + } + pub fn uses_epoca_inputs(&self) -> bool { + self.import_epoca_inputs.is_some() + } + + pub fn send_epoca_input(&mut self, event: crate::InputEvent) { + if self.epoca_input_events.len() == MAX_QUEUED_INPUT_EVENTS { + self.epoca_input_events.pop_front(); + } + self.epoca_input_events.push_back(event.encode()); + } + + fn read_cstr(&mut self, address: u64) -> Result>, String> { + // FIXME: This is slow. + let mut buffer = Vec::new(); + for offset in 0..255 { + let Some(address) = address + .checked_add(offset) + .and_then(|address| u32::try_from(address).ok()) + else { + return Ok(None); + }; + match self.instance.read_u8(address) { + Ok(byte) => { + if byte == 0 { + return Ok(Some(buffer)); + } + + buffer.push(byte) + } + Err(MemoryAccessError::Error(error)) => return Err(error.into()), + Err(MemoryAccessError::OutOfRangeAccess { .. }) => return Ok(None), + Err(MemoryAccessError::MemoryLimitReached) => return Ok(None), + } + } + + Ok(None) + } + + pub fn register_file(&mut self, path: &str, blob: Vec) { + let normalized = normalize_path(path); + self.filesystem + .insert(normalized.into_bytes(), Arc::new(File { blob })); + } + + fn handle_open(&mut self, path: &[u8], flags: u64) -> u64 { + let path = normalize_path(&String::from_utf8_lossy(path)); + log::debug!("Open: path={path:?}, flags=0x{flags:x}"); + + if let Some(file) = self.filesystem.get(path.as_bytes()) { + if (flags & (O_WRONLY | O_RDWR)) != 0 { + return errno(EACCES); + } + + return match self.open_files.open(Arc::clone(file)) { + Ok(fd) => fd, + Err(error) => errno(error), + }; + } + + errno(ENOENT) + } + + fn handle_lseek(&mut self, fd: u64, offset: i64, whence: u64) -> u64 { + log::trace!("Seek: fd={fd}, offset={offset}, whence={whence}"); + + let Some(fd) = self.open_files.get_mut(fd) else { + log::trace!(" -> BADF"); + return errno(EBADF); + }; + + let Ok(position) = seek_position(fd.position, fd.file.blob.len() as u64, offset, whence) + else { + log::trace!(" -> EINVAL"); + return errno(EINVAL); + }; + fd.position = position; + + log::trace!(" -> offset={}", fd.position); + fd.position + } + + fn handle_read(&mut self, fd: u64, address: u64, length: u64) -> Result { + log::trace!("Read: fd={fd}, address=0x{address:x}, length={length}"); + + let Some(fd) = self.open_files.get_mut(fd) else { + log::trace!(" -> EBADF"); + return Ok(errno(EBADF)); + }; + + if address.checked_add(length).is_none() || u32::try_from(address + length).is_err() { + log::trace!(" -> EFAULT"); + return Ok(errno(EFAULT)); + } + + let Ok(address) = u32::try_from(address) else { + log::trace!(" -> EFAULT"); + return Ok(errno(EFAULT)); + }; + + let end = core::cmp::min(fd.position.wrapping_add(length), fd.file.blob.len() as u64); + if fd.position >= end || fd.position >= fd.file.blob.len() as u64 { + log::trace!(" -> offset={}, length=0", fd.position); + return Ok(0); + } + + let blob = &fd.file.blob[fd.position as usize..end as usize]; + match self.instance.write_memory(address, blob) { + Ok(()) => {} + Err(MemoryAccessError::Error(error)) => return Err(error.into()), + Err(MemoryAccessError::OutOfRangeAccess { .. }) => { + log::trace!(" -> EFAULT"); + return Ok(errno(EFAULT)); + } + Err(MemoryAccessError::MemoryLimitReached) => return Ok(errno(EFAULT)), + } + + let length_out = blob.len() as u64; + log::trace!( + " -> offset={}, length={}, new offset={}", + fd.position, + length_out, + fd.position + length_out + ); + + fd.position += length_out; + Ok(length_out) + } + + fn handle_write(&mut self, fd: u64, address: u64, length: u64) -> Result { + if fd != FILENO_STDOUT && fd != FILENO_STDERR { + return Ok(errno(EBADF)); + } + + let length = length.min(MAX_GUEST_WRITE_BYTES); + if address.checked_add(length).is_none() || u32::try_from(address + length).is_err() { + return Ok(errno(EFAULT)); + } + + let Ok(address) = u32::try_from(address) else { + return Ok(errno(EFAULT)); + }; + + let data = match self.instance.read_memory(address, length as u32) { + Ok(data) => data, + Err(MemoryAccessError::Error(error)) => return Err(error.into()), + Err(MemoryAccessError::OutOfRangeAccess { .. }) + | Err(MemoryAccessError::MemoryLimitReached) => return Ok(errno(EFAULT)), + }; + eprint!("{}", String::from_utf8_lossy(&data)); + Ok(length) + } + + fn handle_close(&mut self, fd: u64) -> u64 { + log::debug!("Close: fd = {fd}"); + let Some(_fd) = self.open_files.remove(fd) else { + log::trace!(" -> EBADF"); + return errno(EBADF); + }; + + 0 + } + + #[allow(non_upper_case_globals)] + pub fn setup<'a, I>(&mut self, argv: I) -> Result<(), String> + where + I: IntoIterator, + ::IntoIter: ExactSizeIterator, + { + let argv = argv.into_iter(); + let argc = argv.len() as u64; + let envp: &[&str] = &[]; + let envp_len = envp.len() as u64; + let auxv: &[(u64, u64)] = &[(AT_PAGESZ, 4096)]; + let auxv_len = auxv.len() as u64; + + let mut sp = self.instance.module().default_sp(); + + sp -= (1 + argc + 1 + envp_len + 1 + (auxv_len + 1) * 2) * 8; + let address_init = sp; + + let mut p = sp; + self.instance.write_u64(p as u32, argc)?; + p += 8; + + for arg in argv { + sp -= arg.len() as u64 + 1; + self.instance.write_memory(sp as u32, arg.as_bytes())?; + self.instance.write_u64(p as u32, sp)?; + p += 8; + } + p += 8; // Null pointer. + + for arg in envp { + sp -= arg.len() as u64 + 1; + self.instance.write_memory(sp as u32, arg.as_bytes())?; + self.instance.write_u64(p as u32, sp)?; + p += 8; + } + p += 8; // Null pointer. + + for &(key, value) in auxv { + self.instance.write_u64(p as u32, key)?; + p += 8; + self.instance.write_u64(p as u32, value)?; + p += 8; + } + + self.instance.set_reg(Reg::SP, sp); + self.instance.set_reg(Reg::A0, address_init); + self.instance.set_reg(Reg::RA, polkavm::RETURN_TO_HOST); + self.instance.set_next_program_counter(self.start); + Ok(()) + } + + pub fn run(&mut self) -> Result { + 'outer_loop: loop { + #[allow(clippy::redundant_guards)] // Disable buggy lint. + match self.instance.run()? { + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_set_palette => { + let address = self.instance.reg(Reg::A0); + log::debug!("Set palette called: 0x{:x}", address); + let palette = self.instance.read_memory(address as u32, 256 * 3)?; + return Ok(Interruption::SetPalette { palette }); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_display => { + let width = self.instance.reg(Reg::A0); + let height = self.instance.reg(Reg::A1); + let address = self.instance.reg(Reg::A2); + log::trace!("Display called: {}x{}, 0x{:x}", width, height, address); + let pixels = width + .checked_mul(height) + .ok_or_else(|| "frame dimensions overflow".to_owned())?; + if pixels == 0 || pixels > (crate::MAX_FRAME_BYTES / 4) as u64 { + return Err("frame dimensions exceed the host limit".into()); + } + let address = u32::try_from(address) + .map_err(|_| "frame address is out of range".to_owned())?; + let framebuffer = self.instance.read_memory(address, pixels as u32)?; + return Ok(Interruption::Display { + width, + height, + framebuffer, + }); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_epoca_inputs => { + let address = u32::try_from(self.instance.reg(Reg::A0)) + .map_err(|_| "input address is out of range".to_owned())?; + let capacity = + usize::try_from(self.instance.reg(Reg::A1)).unwrap_or(usize::MAX); + let event_count = + (capacity / crate::INPUT_EVENT_BYTES).min(self.epoca_input_events.len()); + let mut written = 0usize; + for _ in 0..event_count { + let Some(event) = self.epoca_input_events.pop_front() else { + break; + }; + let destination = address + .checked_add(written as u32) + .ok_or_else(|| "input destination overflow".to_owned())?; + self.instance.write_memory(destination, &event)?; + written += event.len(); + } + self.instance.set_reg(Reg::A0, written as u64); + continue; + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_asset_read => { + let name_address = u32::try_from(self.instance.reg(Reg::A0)) + .map_err(|_| "asset name address is out of range".to_owned())?; + let name_length = usize::try_from(self.instance.reg(Reg::A1)) + .unwrap_or(usize::MAX) + .min(crate::MAX_ASSET_NAME_BYTES); + let offset = usize::try_from(self.instance.reg(Reg::A2)).unwrap_or(usize::MAX); + let destination = u32::try_from(self.instance.reg(Reg::A3)) + .map_err(|_| "asset destination is out of range".to_owned())?; + let capacity = usize::try_from(self.instance.reg(Reg::A4)) + .unwrap_or(usize::MAX) + .min(crate::MAX_ASSET_READ_BYTES); + let name = self + .instance + .read_memory(name_address, name_length as u32)?; + let Some(file) = self.filesystem.get(&name) else { + self.instance.set_reg(Reg::A0, 0); + continue; + }; + let length = capacity.min(file.blob.len().saturating_sub(offset)); + if length == 0 { + self.instance.set_reg(Reg::A0, 0); + continue; + } + self.instance + .write_memory(destination, &file.blob[offset..offset + length])?; + self.instance.set_reg(Reg::A0, length as u64); + continue; + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_time_ms => { + self.instance + .set_reg(Reg::A0, self.started.elapsed().as_millis() as u64); + continue; + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_log => { + let address = u32::try_from(self.instance.reg(Reg::A0)) + .map_err(|_| "log address is out of range".to_owned())?; + let length = + u32::try_from(self.instance.reg(Reg::A1).min(crate::MAX_LOG_BYTES as u64)) + .map_err(|_| "log length is out of range".to_owned())?; + let message = self.instance.read_memory(address, length)?; + log::info!(target: "epoca_pvm_guest", "{}", String::from_utf8_lossy(&message)); + continue; + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_yield => { + return Ok(Interruption::Yield); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_epoca_audio => { + let address = u32::try_from(self.instance.reg(Reg::A0)) + .map_err(|_| "audio address is out of range".to_owned())?; + let sample_count = + usize::try_from(self.instance.reg(Reg::A1)).unwrap_or(usize::MAX); + if sample_count == 0 + || sample_count % crate::AUDIO_CHANNELS as usize != 0 + || sample_count > crate::MAX_AUDIO_SAMPLES_PER_CALL + { + self.instance.set_reg(Reg::A0, 1); + continue; + } + let mut buffer = vec![0i16; sample_count]; + self.instance.read_memory_into(address, unsafe { + core::slice::from_raw_parts_mut( + buffer.as_mut_ptr().cast::(), + sample_count * core::mem::size_of::(), + ) + })?; + self.instance.set_reg(Reg::A0, 0); + return Ok(Interruption::AudioFrame { buffer }); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_fetch_inputs => { + let address = self.instance.reg(Reg::A0); + let requested = + usize::try_from(self.instance.reg(Reg::A1)).unwrap_or(usize::MAX); + let (first, second) = queued_input_chunks(&self.input_events, requested); + let mut written = 0usize; + + for events in [first, second] { + if events.is_empty() { + continue; + } + let address = input_destination(address, written)?; + self.instance.write_memory(address, unsafe { + core::slice::from_raw_parts( + events.as_ptr().cast::(), + core::mem::size_of_val(events), + ) + })?; + written += events.len(); + } + + for _ in 0..written { + self.input_events.pop_front(); + } + self.instance.set_reg(Reg::A0, written as u64); + continue; + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_init_audio => { + let channels = self.instance.reg(Reg::A0) as u32; + let bits_per_sample = self.instance.reg(Reg::A1); + let sample_rate = self.instance.reg(Reg::A2) as u32; + if bits_per_sample != 16 { + self.instance.set_reg(Reg::A0, 0); + continue; + } + + self.audio_channels = channels; + self.instance.set_reg(Reg::A0, 1); + return Ok(Interruption::AudioInit { + channels, + sample_rate, + }); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_output_audio => { + let address = self.instance.reg(Reg::A0); + let samples = self.instance.reg(Reg::A1) as usize; + let channels = self.audio_channels as usize; + let length = samples.saturating_mul(channels).min(1024 * 64); + let address = u32::try_from(address) + .map_err(|_| "audio address is out of range".to_owned())?; + let mut buffer: Vec = Vec::with_capacity(length); + unsafe { + self.instance.read_memory_into( + address, + core::slice::from_raw_parts_mut( + buffer + .spare_capacity_mut() + .as_mut_ptr() + .cast::>(), + length * core::mem::size_of::(), + ), + )?; + buffer.set_len(length); + } + + return Ok(Interruption::AudioFrame { buffer }); + } + InterruptKind::Ecalli(hostcall) if Some(hostcall) == self.import_syscall => { + let syscall = self.instance.reg(Reg::A0); + let a1 = self.instance.reg(Reg::A1); + let a2 = self.instance.reg(Reg::A2); + let a3 = self.instance.reg(Reg::A3); + let a4 = self.instance.reg(Reg::A4); + let a5 = self.instance.reg(Reg::A5); + let pc = self.instance.program_counter(); + log::trace!( + "Syscall at pc={pc:?}: {syscall:>3}, args = [0x{a1:>016x}, 0x{a2:>016x}, 0x{a3:>016x}, 0x{a4:>016x}, 0x{a5:>016x}]" + ); + + match syscall { + SYS_read => { + let result = self.handle_read(a1, a2, a3)?; + self.instance.set_reg(Reg::A0, result); + continue; + } + SYS_readv => { + if a3 == 0 || a3 > IOV_MAX { + self.instance.set_reg(Reg::A0, errno(EINVAL)); + continue; + } + + let mut total_length = 0u64; + for n in 0..a3 { + let address = + self.instance.read_u64(a2.wrapping_add(n * 16) as u32)?; + let length = self + .instance + .read_u64(a2.wrapping_add(n * 16).wrapping_add(8) as u32)?; + let bytes_read = self.handle_read(a1, address, length)?; + if (bytes_read as i64) < 0 { + self.instance.set_reg(Reg::A0, bytes_read); + continue 'outer_loop; + } + + total_length = total_length + .checked_add(bytes_read) + .ok_or_else(|| "readv byte count overflow".to_owned())?; + if bytes_read < length { + break; + } + } + + self.instance.set_reg(Reg::A0, total_length); + continue; + } + SYS_writev => { + if a3 == 0 || a3 > IOV_MAX { + self.instance.set_reg(Reg::A0, errno(EINVAL)); + continue; + } + + let mut total_length = 0u64; + for n in 0..a3 { + let address = + self.instance.read_u64(a2.wrapping_add(n * 16) as u32)?; + let length = self + .instance + .read_u64(a2.wrapping_add(n * 16).wrapping_add(8) as u32)?; + let bytes_written = self.handle_write(a1, address, length)?; + if (bytes_written as i64) < 0 { + self.instance.set_reg(Reg::A0, bytes_written); + continue 'outer_loop; + } + + total_length = total_length + .checked_add(bytes_written) + .ok_or_else(|| "writev byte count overflow".to_owned())?; + if bytes_written < length { + break; + } + } + + self.instance.set_reg(Reg::A0, total_length); + continue; + } + SYS_exit => { + log::info!("Exit called: status={}", a1); + if a1 == 0 { + return Ok(Interruption::Exit); + } else { + return Err(format!("exit called with status: {a1}")); + } + } + SYS_openat => { + if a1 == AT_FDCWD { + let Some(path) = self.read_cstr(a2)? else { + self.instance.set_reg(Reg::A0, errno(EFAULT)); + continue; + }; + + let result = self.handle_open(&path, a3); + self.instance.set_reg(Reg::A0, result); + continue; + } + } + SYS_lseek => { + let result = self.handle_lseek(a1, a2 as i64, a3); + self.instance.set_reg(Reg::A0, result); + continue; + } + SYS_close => { + let result = self.handle_close(a1); + self.instance.set_reg(Reg::A0, result); + continue; + } + _ => { + log::debug!("Unimplemented syscall at pc={pc:?}: {syscall:>3}, args = [0x{a1:>016x}, 0x{a2:>016x}, 0x{a3:>016x}, 0x{a4:>016x}, 0x{a5:>016x}]"); + } + } + + self.instance.set_reg(Reg::A0, errno(ENOSYS)); + } + InterruptKind::Finished => { + return Ok(Interruption::Exit); + } + InterruptKind::Ecalli(hostcall) => { + return Err(format!("unsupported host call: {hostcall}")); + } + InterruptKind::Trap => { + return Err(format!( + "execution trapped at {:?}", + self.instance.program_counter() + )); + } + InterruptKind::NotEnoughGas => { + return Err("ran out of gas".into()); + } + InterruptKind::Segfault(address) => { + return Err(format!("guest segfault at {address:?}")); + } + InterruptKind::Step => return Err("unexpected guest step".into()), + } + } + } +} + +#[cfg(test)] +mod tests; diff --git a/rust/crates/pvm-runtime/src/corevm/tests.rs b/rust/crates/pvm-runtime/src/corevm/tests.rs new file mode 100644 index 000000000..ad59c7ab9 --- /dev/null +++ b/rust/crates/pvm-runtime/src/corevm/tests.rs @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::*; + +#[test] +fn open_files_enforce_the_descriptor_limit() { + let file = Arc::new(File { blob: Vec::new() }); + let mut open_files = OpenFiles::new(); + for expected in 3..3 + MAX_OPEN_FILES as u64 { + assert_eq!(open_files.open(Arc::clone(&file)), Ok(expected)); + } + assert_eq!(open_files.open(Arc::clone(&file)), Err(EMFILE)); + assert_eq!(open_files.descriptors.len(), MAX_OPEN_FILES); + + assert!(open_files.remove(3).is_some()); + assert_eq!( + open_files.open(file), + Ok(3 + MAX_OPEN_FILES as u64), + "closing a descriptor should restore capacity" + ); +} + +#[test] +fn seeks_reject_negative_offsets_without_clamping_valid_offsets() { + assert_eq!(seek_position(5, 10, -6, SEEK_CUR), Err(EINVAL)); + assert_eq!(seek_position(0, 10, -11, SEEK_END), Err(EINVAL)); + assert_eq!(seek_position(0, 10, 12, SEEK_SET), Ok(12)); + assert_eq!(seek_position(5, 10, 3, SEEK_CUR), Ok(8)); + assert_eq!(seek_position(0, 10, -3, SEEK_END), Ok(7)); +} + +#[test] +fn wrapped_input_chunks_preserve_order_and_destination() { + let mut events = VecDeque::with_capacity(4); + for key in 1..=4 { + events.push_back(InputEvent { key, value: 1 }); + } + events.pop_front(); + events.pop_front(); + events.push_back(InputEvent { key: 5, value: 1 }); + events.push_back(InputEvent { key: 6, value: 1 }); + + let (first, second) = queued_input_chunks(&events, events.len()); + assert!( + !second.is_empty(), + "test queue should cross its ring boundary" + ); + let keys: Vec<_> = first.iter().chain(second).map(|event| event.key).collect(); + assert_eq!(keys, [3, 4, 5, 6]); + assert_eq!( + input_destination(100, first.len()).unwrap(), + 100 + u32::try_from(core::mem::size_of_val(first)).unwrap() + ); + + let written = first.len() + second.len(); + for _ in 0..written { + events.pop_front(); + } + assert!(events.is_empty(), "every reported event should be consumed"); +} diff --git a/rust/crates/pvm-runtime/src/lib.rs b/rust/crates/pvm-runtime/src/lib.rs new file mode 100644 index 000000000..e6b8b82d4 --- /dev/null +++ b/rust/crates/pvm-runtime/src/lib.rs @@ -0,0 +1,1163 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#[cfg(target_arch = "wasm32")] +extern crate polkavm_wasm as polkavm; + +mod application; +mod corevm; +pub use pvm_gpu_wire as gpu_wire; +mod manifest; +#[cfg(all(not(target_arch = "wasm32"), feature = "ffi"))] +mod native_ffi; +mod quake_keys; +mod tri2d; +#[cfg(target_arch = "wasm32")] +mod wasm; +#[cfg(any(target_arch = "wasm32", test))] +mod wasm_codegen; + +#[cfg(all(not(target_arch = "wasm32"), feature = "ffi"))] +pub use native_ffi::*; + +#[cfg(all(not(target_arch = "wasm32"), feature = "ffi"))] +uniffi::setup_scaffolding!(); + +pub use application::ApplicationRuntime; +pub use manifest::AppDescriptor; + +use anyhow::{anyhow, Context, Result}; +use polkavm::{BackendKind, CallError, Config, Engine, Instance, Linker, Module, ProgramBlob}; +use std::collections::{HashMap, VecDeque}; +use std::mem::size_of; +#[cfg(not(target_arch = "wasm32"))] +use std::time::{Duration, Instant}; +pub use tri2d::{ + Tri2dFrame, MAX_TRI2D_BYTES, MAX_TRI2D_COMMANDS, MAX_TRI2D_DRAWS, MAX_TRI2D_INDICES, + MAX_TRI2D_SURFACE_SIZE, MAX_TRI2D_TEXTURES, MAX_TRI2D_TEXTURE_BYTES, MAX_TRI2D_TEXTURE_SIZE, + MAX_TRI2D_VERTICES, TRI2D_HEADER_BYTES, TRI2D_MAGIC, TRI2D_VERSION, +}; + +pub const ABI_VERSION: u32 = 1; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PresentationProfile { + Framebuffer, + Tri2d, + WebGpuRaster, +} + +impl PresentationProfile { + pub fn parse(value: &str) -> Result { + match value { + "framebuffer" => Ok(Self::Framebuffer), + "tri2d" => Ok(Self::Tri2d), + "webgpu-raster" => Ok(Self::WebGpuRaster), + _ => Err(anyhow!("unsupported presentation profile {value}")), + } + } + + pub fn as_str(self) -> &'static str { + match self { + Self::Framebuffer => "framebuffer", + Self::Tri2d => "tri2d", + Self::WebGpuRaster => "webgpu-raster", + } + } +} +pub const BYTES_PER_PIXEL: usize = 4; +pub const MAX_PROGRAM_BYTES: usize = 16 * 1024 * 1024; +pub const MAX_FRAME_BYTES: usize = 16 * 1024 * 1024; +pub const MAX_GUEST_READ: usize = MAX_FRAME_BYTES; +pub const MAX_GUEST_RW_DATA_BYTES: u32 = 64 * 1024 * 1024; +pub const MAX_GUEST_STACK_BYTES: u32 = 16 * 1024 * 1024; +pub const MAX_GUEST_HEAP_BYTES: u32 = 128 * 1024 * 1024; +pub const MAX_ASSET_FILES: usize = 2_048; +pub const MAX_ASSET_FILE_BYTES: usize = 64 * 1024 * 1024; +pub const MAX_ASSET_BYTES: usize = 128 * 1024 * 1024; +pub const INPUT_EVENT_BYTES: usize = 8; +pub const AUDIO_SAMPLE_RATE: u32 = 48_000; +pub const AUDIO_CHANNELS: u32 = 2; +pub const MAX_AUDIO_SAMPLES_PER_CALL: usize = AUDIO_SAMPLE_RATE as usize * AUDIO_CHANNELS as usize; +const MAX_ASSET_NAME_BYTES: usize = 1_024; +const MAX_ASSET_READ_BYTES: usize = 16 * 1024 * 1024; +const MAX_HOSTCALL_BYTES_PER_TICK: usize = 32 * 1024 * 1024; +const MAX_HOSTCALLS_PER_INIT: u32 = 131_072; +const MAX_HOSTCALLS_PER_UPDATE: u32 = 8_192; +const MAX_SLEEP_MS_PER_INIT: u32 = 100; +const MAX_SLEEP_MS_PER_UPDATE: u32 = 50; +const MAX_QUEUED_AUDIO_SAMPLES: usize = AUDIO_SAMPLE_RATE as usize * AUDIO_CHANNELS as usize * 2; +const MAX_QUEUED_INPUT_EVENTS: usize = 4_096; +const MAX_SAVE_BYTES: usize = 1024 * 1024; +const MAX_LOG_BYTES: usize = 4 * 1024; +const MAX_QUEUED_LOGS: usize = 64; +const MAX_QUEUED_GPU_BATCHES: usize = 4; +const MAX_QUEUED_GPU_EVENTS: usize = 256; +const MAX_GPU_SUBMITS_PER_TICK: u32 = 8; +const MAX_GPU_UPLOAD_BYTES_PER_TICK: usize = 16 * 1024 * 1024; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum InputEventType { + KeyDown = 1, + KeyUp = 2, + ButtonDown = 3, + ButtonUp = 4, + PointerMove = 5, + PointerDelta = 6, + SurfaceMetrics = 7, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct InputEvent { + pub event_type: InputEventType, + pub code: u8, + pub x: u16, + pub y: u16, +} + +impl InputEvent { + fn encode(self) -> [u8; INPUT_EVENT_BYTES] { + let x = self.x.to_le_bytes(); + let y = self.y.to_le_bytes(); + [ + self.event_type as u8, + self.code, + x[0], + x[1], + y[0], + y[1], + 0, + 0, + ] + } +} + +#[derive(Debug)] +pub struct Frame { + pub width: u32, + pub height: u32, + /// Packed 0xAARRGGBB pixels. On little-endian guests these bytes are BGRA. + pub argb: Vec, +} + +#[derive(Debug)] +pub struct AudioChunk { + /// Interleaved little-endian signed 16-bit samples. + pub samples: Vec, + pub sample_rate: u32, + pub channels: u32, +} + +#[derive(Debug)] +pub struct GpuBatch { + pub bytes: Vec, +} + +struct HostClock { + #[cfg(not(target_arch = "wasm32"))] + started: Instant, + #[cfg(target_arch = "wasm32")] + now_ms: u64, +} + +impl HostClock { + fn new() -> Self { + Self { + #[cfg(not(target_arch = "wasm32"))] + started: Instant::now(), + #[cfg(target_arch = "wasm32")] + now_ms: 0, + } + } + + fn elapsed_ms(&self) -> u64 { + #[cfg(not(target_arch = "wasm32"))] + { + self.started.elapsed().as_millis() as u64 + } + #[cfg(target_arch = "wasm32")] + { + self.now_ms + } + } + + fn sleep_ms(&mut self, duration_ms: u32) { + #[cfg(not(target_arch = "wasm32"))] + std::thread::sleep(Duration::from_millis(duration_ms.into())); + #[cfg(target_arch = "wasm32")] + { + self.now_ms = self.now_ms.saturating_add(duration_ms.into()); + } + } + + #[cfg(target_arch = "wasm32")] + fn set_time_ms(&mut self, time_ms: u64) { + self.now_ms = self.now_ms.max(time_ms); + } +} + +pub(crate) fn preferred_backend() -> BackendKind { + #[cfg(any(target_arch = "wasm32", target_os = "ios", target_os = "android"))] + { + BackendKind::Interpreter + } + #[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")))] + { + BackendKind::Compiler + } +} + +struct HostState { + frame: Option, + tri2d: Option, + tri2d_state: tri2d::Tri2dState, + audio_enabled: bool, + presentation: PresentationProfile, + tri2d_submitted: bool, + audio: VecDeque, + audio_samples: usize, + input: VecDeque, + assets: HashMap>, + clock: HostClock, + logs: VecDeque, + save: Option>, + hostcall_bytes_remaining: usize, + hostcalls_remaining: u32, + sleep_ms_remaining: u32, + gpu_capabilities: Option>, + gpu_batches: VecDeque, + gpu_events: VecDeque>, + gpu_last_sequence: u64, + gpu_submits_remaining: u32, + gpu_upload_bytes_remaining: usize, +} + +impl HostState { + fn new( + assets: HashMap>, + presentation: PresentationProfile, + audio_enabled: bool, + ) -> Self { + Self { + frame: None, + tri2d: None, + presentation, + audio_enabled, + tri2d_state: tri2d::Tri2dState::default(), + tri2d_submitted: false, + audio: VecDeque::new(), + audio_samples: 0, + input: VecDeque::new(), + assets, + clock: HostClock::new(), + logs: VecDeque::new(), + save: None, + hostcall_bytes_remaining: 0, + hostcalls_remaining: 0, + sleep_ms_remaining: 0, + gpu_capabilities: None, + gpu_batches: VecDeque::new(), + gpu_events: VecDeque::new(), + gpu_last_sequence: 0, + gpu_submits_remaining: 0, + gpu_upload_bytes_remaining: 0, + } + } + + fn reset_hostcall_budget(&mut self, max_hostcalls: u32, max_sleep_ms: u32) { + self.hostcall_bytes_remaining = MAX_HOSTCALL_BYTES_PER_TICK; + self.hostcalls_remaining = max_hostcalls; + self.sleep_ms_remaining = max_sleep_ms; + self.tri2d_submitted = false; + self.gpu_submits_remaining = MAX_GPU_SUBMITS_PER_TICK; + self.gpu_upload_bytes_remaining = MAX_GPU_UPLOAD_BYTES_PER_TICK; + } + + fn charge_hostcall(&mut self, bytes: usize) -> Result<()> { + if self.hostcalls_remaining == 0 { + return Err(anyhow!("guest exceeded host-call count budget")); + } + self.hostcalls_remaining -= 1; + self.charge_hostcall_bytes(bytes) + } + + fn charge_hostcall_bytes(&mut self, bytes: usize) -> Result<()> { + if bytes > self.hostcall_bytes_remaining { + return Err(anyhow!("guest exceeded host-call byte budget")); + } + self.hostcall_bytes_remaining -= bytes; + Ok(()) + } + + fn queue_input(&mut self, event: InputEvent) { + if event.event_type == InputEventType::SurfaceMetrics { + if let Some(position) = self + .input + .iter() + .rposition(|queued| queued.event_type == InputEventType::SurfaceMetrics) + { + self.input.remove(position); + } + } else if event.event_type == InputEventType::PointerMove + && self + .input + .back() + .is_some_and(|queued| queued.event_type == InputEventType::PointerMove) + { + self.input.pop_back(); + } + if self.input.len() == MAX_QUEUED_INPUT_EVENTS { + let discardable = self + .input + .iter() + .position(|queued| queued.event_type == InputEventType::PointerMove) + .or_else(|| { + self.input + .iter() + .position(|queued| queued.event_type != InputEventType::SurfaceMetrics) + }); + if let Some(position) = discardable { + self.input.remove(position); + } else { + self.input.pop_front(); + } + } + self.input.push_back(event); + } +} + +pub struct Runtime { + instance: Instance, + state: HostState, + max_gas_per_update: u64, + last_gas_used: u64, + backend: polkavm::BackendKind, +} + +impl Runtime { + pub fn new( + program: &[u8], + assets: HashMap>, + presentation: PresentationProfile, + audio_enabled: bool, + max_gas_per_update: u64, + ) -> Result { + Self::new_with_backend( + program, + assets, + presentation, + audio_enabled, + max_gas_per_update, + preferred_backend(), + ) + } + + pub fn new_with_backend( + program: &[u8], + assets: HashMap>, + presentation: PresentationProfile, + audio_enabled: bool, + max_gas_per_update: u64, + backend: BackendKind, + ) -> Result { + let blob = ProgramBlob::parse(program.into()).context("parse PolkaVM program")?; + if blob.rw_data_size() > MAX_GUEST_RW_DATA_BYTES { + return Err(anyhow!( + "guest read-write data exceeds {MAX_GUEST_RW_DATA_BYTES} bytes" + )); + } + if blob.stack_size() > MAX_GUEST_STACK_BYTES { + return Err(anyhow!("guest stack exceeds {MAX_GUEST_STACK_BYTES} bytes")); + } + let mut engine_config = Config::new(); + // macOS requires PolkaVM's experimental generic sandbox for native + // recompilation. Keep sandboxing enabled while opting into that boundary. + engine_config.set_backend(Some(backend)); + engine_config.set_sandboxing_enabled(true); + #[cfg(target_os = "macos")] + { + engine_config.set_sandbox(Some(polkavm::SandboxKind::Generic)); + engine_config.set_allow_experimental(true); + } + let engine = Engine::new(&engine_config).context("create PolkaVM engine")?; + let backend = engine.backend(); + + let mut module_config = polkavm::ModuleConfig::default(); + module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync)); + #[cfg(not(target_arch = "wasm32"))] + module_config.set_max_heap_size(Some(MAX_GUEST_HEAP_BYTES)); + #[cfg(target_os = "macos")] + module_config.set_page_size(16_384); + + let module = + Module::from_blob(&engine, &module_config, blob).context("compile PolkaVM module")?; + let mut linker: Linker = Linker::new(); + + linker + .define_typed( + "host_present_frame", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + width: u32, + height: u32, + stride: u32| + -> Result { + let row_bytes = width + .checked_mul(BYTES_PER_PIXEL as u32) + .ok_or_else(|| anyhow!("frame row size overflow"))?; + if stride != row_bytes { + return Ok(1); + } + let byte_len = row_bytes + .checked_mul(height) + .ok_or_else(|| anyhow!("frame size overflow"))? + as usize; + if byte_len == 0 || byte_len > MAX_FRAME_BYTES { + return Ok(1); + } + caller.user_data.charge_hostcall(byte_len)?; + if caller.user_data.presentation != PresentationProfile::Framebuffer { + return Ok(3); + } + let argb = read_guest_memory(caller.instance, pointer, byte_len)?; + caller.user_data.frame = Some(Frame { + width, + height, + argb, + }); + Ok(0) + }, + ) + .context("define host_present_frame")?; + + linker + .define_typed( + "host_tri2d_submit", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + length: u32| + -> Result { + let length = length as usize; + if length == 0 || length > MAX_TRI2D_BYTES { + return Ok(1); + } + caller.user_data.charge_hostcall(length)?; + if caller.user_data.presentation != PresentationProfile::Tri2d { + return Ok(3); + } + if caller.user_data.tri2d_submitted { + return Ok(2); + } + let bytes = read_guest_memory(caller.instance, pointer, length)?; + let Ok((next_state, metadata)) = + tri2d::validate_tri2d(&bytes, &caller.user_data.tri2d_state) + else { + return Ok(1); + }; + caller.user_data.tri2d_state = next_state; + caller.user_data.tri2d_submitted = true; + caller.user_data.tri2d = Some(Tri2dFrame { + width: metadata.width, + height: metadata.height, + draw_count: metadata.draw_count, + vertex_count: metadata.vertex_count, + index_count: metadata.index_count, + bytes, + }); + Ok(0) + }, + ) + .context("define host_tri2d_submit")?; + + linker + .define_typed( + "epoca_gpu_capabilities", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + capacity: u32| + -> Result { + caller.user_data.charge_hostcall(0)?; + if caller.user_data.presentation != PresentationProfile::WebGpuRaster { + return Ok(gpu_wire::GPU_ERROR_INVALID_STATE); + } + let Some(required_len) = + caller.user_data.gpu_capabilities.as_ref().map(Vec::len) + else { + return Ok(0); + }; + let required = i32::try_from(required_len) + .map_err(|_| anyhow!("GPU capabilities length overflow"))?; + if (capacity as usize) < required_len { + return Ok(-required); + } + caller.user_data.charge_hostcall_bytes(required_len)?; + let capabilities = caller.user_data.gpu_capabilities.as_ref().unwrap(); + if caller.instance.write_memory(pointer, capabilities).is_err() { + return Ok(gpu_wire::GPU_ERROR_INVALID_GUEST_RANGE); + } + Ok(required) + }, + ) + .context("define epoca_gpu_capabilities")?; + + linker + .define_typed( + "epoca_gpu_submit", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + length: u32| + -> Result { + let length = length as usize; + caller.user_data.charge_hostcall(length)?; + if caller.user_data.presentation != PresentationProfile::WebGpuRaster { + return Ok(gpu_wire::GPU_ERROR_INVALID_STATE); + } + if length == 0 || length > gpu_wire::MAX_GPU_BATCH_BYTES { + return Ok(gpu_wire::GPU_ERROR_MALFORMED_BATCH); + } + if caller.user_data.gpu_capabilities.is_none() { + return Ok(gpu_wire::GPU_ERROR_INVALID_STATE); + } + if caller.user_data.gpu_batches.len() == MAX_QUEUED_GPU_BATCHES { + return Ok(gpu_wire::GPU_SUBMIT_BUSY); + } + if caller.user_data.gpu_submits_remaining == 0 { + return Ok(gpu_wire::GPU_ERROR_QUOTA_EXCEEDED); + } + let Ok(bytes) = read_guest_memory(caller.instance, pointer, length) else { + return Ok(gpu_wire::GPU_ERROR_INVALID_GUEST_RANGE); + }; + let Ok(batch) = gpu_wire::decode_gpu_batch(&bytes) else { + return Ok(gpu_wire::GPU_ERROR_MALFORMED_BATCH); + }; + if batch.sequence() <= caller.user_data.gpu_last_sequence { + return Ok(gpu_wire::GPU_ERROR_INVALID_STATE); + } + let Some(upload_bytes) = gpu_inline_upload_bytes(&batch) else { + return Ok(gpu_wire::GPU_ERROR_MALFORMED_BATCH); + }; + if upload_bytes > caller.user_data.gpu_upload_bytes_remaining { + return Ok(gpu_wire::GPU_ERROR_QUOTA_EXCEEDED); + } + caller.user_data.gpu_submits_remaining -= 1; + caller.user_data.gpu_upload_bytes_remaining -= upload_bytes; + caller.user_data.gpu_last_sequence = batch.sequence(); + caller.user_data.gpu_batches.push_back(GpuBatch { bytes }); + Ok(gpu_wire::GPU_SUBMIT_ACCEPTED) + }, + ) + .context("define epoca_gpu_submit")?; + + linker + .define_typed( + "epoca_gpu_receive", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + capacity: u32| + -> Result { + caller.user_data.charge_hostcall(0)?; + if caller.user_data.presentation != PresentationProfile::WebGpuRaster { + return Ok(gpu_wire::GPU_ERROR_INVALID_STATE); + } + let Some(required_len) = caller.user_data.gpu_events.front().map(Vec::len) + else { + return Ok(0); + }; + let required = i32::try_from(required_len) + .map_err(|_| anyhow!("GPU event length overflow"))?; + if (capacity as usize) < required_len { + return Ok(-required); + } + caller.user_data.charge_hostcall_bytes(required_len)?; + let event = caller.user_data.gpu_events.front().unwrap(); + if caller.instance.write_memory(pointer, event).is_err() { + return Ok(gpu_wire::GPU_ERROR_INVALID_GUEST_RANGE); + } + caller.user_data.gpu_events.pop_front(); + Ok(required) + }, + ) + .context("define epoca_gpu_receive")?; + + linker + .define_typed( + "host_poll_input", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + capacity: u32| + -> Result { + let event_count = + (capacity as usize / INPUT_EVENT_BYTES).min(caller.user_data.input.len()); + let byte_count = event_count + .checked_mul(INPUT_EVENT_BYTES) + .ok_or_else(|| anyhow!("input byte count overflow"))?; + caller.user_data.charge_hostcall(byte_count)?; + let mut written = 0u32; + for _ in 0..event_count { + let Some(event) = caller.user_data.input.pop_front() else { + break; + }; + let destination = pointer + .checked_add(written) + .ok_or_else(|| anyhow!("guest input destination overflow"))?; + caller + .instance + .write_memory(destination, &event.encode()) + .map_err(|error| anyhow!("write guest input: {error:?}"))?; + written += INPUT_EVENT_BYTES as u32; + } + Ok(written) + }, + ) + .context("define host_poll_input")?; + + linker + .define_typed( + "host_time_ms", + |caller: polkavm::Caller<'_, HostState>| -> Result { + caller.user_data.charge_hostcall(0)?; + Ok(caller.user_data.clock.elapsed_ms()) + }, + ) + .context("define host_time_ms")?; + + linker + .define_typed( + "host_sleep_ms", + |caller: polkavm::Caller<'_, HostState>, duration_ms: u32| -> Result<()> { + caller.user_data.charge_hostcall(0)?; + let duration_ms = duration_ms.min(caller.user_data.sleep_ms_remaining); + caller.user_data.sleep_ms_remaining -= duration_ms; + caller.user_data.clock.sleep_ms(duration_ms); + Ok(()) + }, + ) + .context("define host_sleep_ms")?; + + linker + .define_typed( + "host_audio_submit", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + sample_count: u32| + -> Result { + let sample_count = sample_count as usize; + if sample_count == 0 + || sample_count % AUDIO_CHANNELS as usize != 0 + || sample_count > MAX_AUDIO_SAMPLES_PER_CALL + || caller.user_data.audio_samples + sample_count > MAX_QUEUED_AUDIO_SAMPLES + { + return Ok(1); + } + let byte_count = sample_count + .checked_mul(size_of::()) + .ok_or_else(|| anyhow!("audio byte count overflow"))?; + caller.user_data.charge_hostcall(byte_count)?; + if !caller.user_data.audio_enabled { + return Ok(3); + } + let bytes = read_guest_memory(caller.instance, pointer, byte_count)?; + let samples = bytes + .chunks_exact(2) + .map(|sample| i16::from_le_bytes([sample[0], sample[1]])) + .collect(); + caller.user_data.audio_samples += sample_count; + caller.user_data.audio.push_back(AudioChunk { + samples, + sample_rate: AUDIO_SAMPLE_RATE, + channels: AUDIO_CHANNELS, + }); + Ok(0) + }, + ) + .context("define host_audio_submit")?; + + linker + .define_typed( + "host_asset_read", + |caller: polkavm::Caller<'_, HostState>, + name_pointer: u32, + name_length: u32, + offset: u32, + destination: u32, + capacity: u32| + -> Result { + let name_length = name_length as usize; + caller.user_data.charge_hostcall(name_length)?; + if name_length == 0 || name_length > MAX_ASSET_NAME_BYTES { + return Ok(0); + } + let name = read_guest_memory(caller.instance, name_pointer, name_length)?; + let Ok(name) = std::str::from_utf8(&name) else { + return Ok(0); + }; + let Some(asset_length) = caller.user_data.assets.get(name).map(Vec::len) else { + return Ok(0); + }; + let offset = offset as usize; + if offset >= asset_length { + return Ok(0); + } + let length = (capacity as usize) + .min(asset_length - offset) + .min(MAX_ASSET_READ_BYTES); + caller.user_data.charge_hostcall_bytes(length)?; + let asset = &caller.user_data.assets[name]; + caller + .instance + .write_memory(destination, &asset[offset..offset + length]) + .map_err(|error| anyhow!("write guest asset: {error:?}"))?; + Ok(length as u32) + }, + ) + .context("define host_asset_read")?; + + linker + .define_typed( + "host_save_submit", + |caller: polkavm::Caller<'_, HostState>, + pointer: u32, + length: u32| + -> Result { + let length = length as usize; + if length == 0 || length > MAX_SAVE_BYTES { + return Ok(1); + } + caller.user_data.charge_hostcall(length)?; + caller.user_data.save = + Some(read_guest_memory(caller.instance, pointer, length)?); + Ok(0) + }, + ) + .context("define host_save_submit")?; + + linker + .define_typed( + "host_log", + |caller: polkavm::Caller<'_, HostState>, pointer: u32, length: u32| -> Result<()> { + let length = (length as usize).min(MAX_LOG_BYTES); + caller.user_data.charge_hostcall(length)?; + let bytes = read_guest_memory(caller.instance, pointer, length)?; + if caller.user_data.logs.len() == MAX_QUEUED_LOGS { + caller.user_data.logs.pop_front(); + } + caller + .user_data + .logs + .push_back(String::from_utf8_lossy(&bytes).into_owned()); + Ok(()) + }, + ) + .context("define host_log")?; + + let instance = linker + .instantiate_pre(&module) + .context("pre-instantiate PolkaVM module")? + .instantiate() + .context("instantiate PolkaVM module")?; + + Ok(Self { + instance, + state: HostState::new(assets, presentation, audio_enabled), + max_gas_per_update, + last_gas_used: 0, + backend, + }) + } + + pub fn init(&mut self) -> Result<()> { + let gas = self.max_gas_per_update.min(i64::MAX as u64) as i64; + self.instance.set_gas(gas); + self.state + .reset_hostcall_budget(MAX_HOSTCALLS_PER_INIT, MAX_SLEEP_MS_PER_INIT); + let result = self + .instance + .call_typed_and_get_result::<(), ()>(&mut self.state, "init", ()); + self.record_gas_used(gas); + map_call_result(result, "init") + } + + pub fn update(&mut self) -> Result<()> { + let gas = self.max_gas_per_update.min(i64::MAX as u64) as i64; + self.instance.set_gas(gas); + self.state + .reset_hostcall_budget(MAX_HOSTCALLS_PER_UPDATE, MAX_SLEEP_MS_PER_UPDATE); + let result = + self.instance + .call_typed_and_get_result::<(), ()>(&mut self.state, "update", ()); + self.record_gas_used(gas); + map_call_result(result, "update").map_err(|error| { + if let Some(log) = self.state.logs.back() { + error.context(format!("last guest log: {log}")) + } else { + error + } + }) + } + + fn record_gas_used(&mut self, budget: i64) { + let remaining = self.instance.gas().max(0); + self.last_gas_used = (budget - remaining.min(budget)) as u64; + } + + pub fn last_gas_used(&self) -> u64 { + self.last_gas_used + } + + pub fn backend(&self) -> polkavm::BackendKind { + self.backend + } + + pub fn send_input(&mut self, event: InputEvent) { + self.state.queue_input(event); + } + + pub fn gpu_ready(&self) -> bool { + self.state.presentation != PresentationProfile::WebGpuRaster + || self.state.gpu_capabilities.is_some() + } + + pub fn set_gpu_capabilities(&mut self, bytes: Vec) -> Result<()> { + if self.state.presentation != PresentationProfile::WebGpuRaster { + return Err(anyhow!("GPU capabilities sent to a non-GPU application")); + } + validate_gpu_capabilities(&bytes)?; + self.state.gpu_capabilities = Some(bytes); + Ok(()) + } + + pub fn send_gpu_event(&mut self, bytes: Vec) -> Result<()> { + if self.state.presentation != PresentationProfile::WebGpuRaster { + return Err(anyhow!("GPU event sent to a non-GPU application")); + } + validate_gpu_event(&bytes)?; + if self.state.gpu_events.len() == MAX_QUEUED_GPU_EVENTS { + return Err(anyhow!("GPU event queue overflow")); + } + self.state.gpu_events.push_back(bytes); + Ok(()) + } + + pub fn take_gpu_batch(&mut self) -> Option { + self.state.gpu_batches.pop_front() + } + + #[cfg(target_arch = "wasm32")] + pub fn set_time_ms(&mut self, time_ms: u64) { + self.state.clock.set_time_ms(time_ms); + } + + pub fn take_frame(&mut self) -> Option { + self.state.frame.take() + } + + pub fn take_tri2d(&mut self) -> Option { + self.state.tri2d.take() + } + + pub fn take_audio(&mut self) -> Option { + let chunk = self.state.audio.pop_front()?; + self.state.audio_samples -= chunk.samples.len(); + Some(chunk) + } + + pub fn take_log(&mut self) -> Option { + self.state.logs.pop_front() + } + + pub fn take_save(&mut self) -> Option> { + self.state.save.take() + } +} + +fn gpu_inline_upload_bytes(batch: &gpu_wire::GpuBatch<'_>) -> Option { + batch.commands().try_fold(0usize, |total, command| { + let bytes = match command.opcode { + gpu_wire::GpuOpcode::WriteBuffer => gpu_u32(command.payload, 16)? as usize, + gpu_wire::GpuOpcode::WriteTexture => gpu_u32(command.payload, 40)? as usize, + _ => 0, + }; + total.checked_add(bytes) + }) +} + +fn validate_gpu_capabilities(bytes: &[u8]) -> Result<()> { + const HEADER_BYTES: usize = 56; + const ENTRY_BYTES: usize = 16; + if bytes.len() < HEADER_BYTES + || bytes[..4] != gpu_wire::GPU_CAPABILITIES_MAGIC + || gpu_u16(bytes, 4) != Some(gpu_wire::GPU_WIRE_VERSION) + || gpu_u16(bytes, 6) != Some(0) + || gpu_u32(bytes, 8) != Some(bytes.len() as u32) + || !matches!(gpu_u16(bytes, 12), Some(1..=4)) + || gpu_u16(bytes, 14) != Some(0) + || gpu_u32(bytes, 16) == Some(0) + || gpu_u32(bytes, 20) == Some(0) + || gpu_u32(bytes, 24) == Some(0) + || gpu_u32(bytes, 28) == Some(0) + || gpu_u32(bytes, 36) == Some(0) + || gpu_u32(bytes, 40) == Some(0) + || gpu_u32(bytes, 48) != Some(0) + || gpu_u32(bytes, 52) != Some(0) + { + return Err(anyhow!("invalid GPU capabilities record")); + } + let scale = f32::from_bits(gpu_u32(bytes, 32).unwrap()); + if !scale.is_finite() || scale <= 0.0 { + return Err(anyhow!("invalid GPU capabilities scale")); + } + let count = gpu_u32(bytes, 44).unwrap() as usize; + if count > 12 || bytes.len() != HEADER_BYTES + count * ENTRY_BYTES { + return Err(anyhow!("invalid GPU capabilities limit table")); + } + let mut previous = 0; + for entry in bytes[HEADER_BYTES..].chunks_exact(ENTRY_BYTES) { + let key = gpu_u16(entry, 0).unwrap(); + if key <= previous + || key > 12 + || gpu_u16(entry, 2) != Some(0) + || gpu_u64(entry, 4) == Some(0) + || gpu_u32(entry, 12) != Some(0) + { + return Err(anyhow!("invalid GPU capability limit entry")); + } + previous = key; + } + Ok(()) +} + +fn validate_gpu_event(bytes: &[u8]) -> Result<()> { + if bytes.len() < gpu_wire::GPU_EVENT_HEADER_BYTES + || bytes.len() > gpu_wire::MAX_GPU_EVENT_BYTES + || bytes[..4] != gpu_wire::GPU_EVENT_MAGIC + || gpu_u16(bytes, 4) != Some(gpu_wire::GPU_WIRE_VERSION) + || !matches!(gpu_u16(bytes, 6), Some(1..=7)) + || gpu_u32(bytes, 8) != Some(bytes.len() as u32) + || gpu_u32(bytes, 12) != Some(0) + { + return Err(anyhow!("invalid GPU event envelope")); + } + let event_type = gpu_u16(bytes, 6).unwrap(); + let payload = &bytes[gpu_wire::GPU_EVENT_HEADER_BYTES..]; + match event_type { + 1 | 3 => validate_gpu_text(payload, 8, 12, 16), + 2 => { + if payload.len() < 32 + || gpu_u16(payload, 6).is_none_or(|flags| flags > 1) + || gpu_u32(payload, 28) != Some(0) + { + return Err(anyhow!("invalid GPU shader diagnostic")); + } + validate_gpu_text(payload, 24, 6, 32) + } + 4 | 7 => validate_gpu_text(payload, 4, 8, 12), + 5 if payload.is_empty() => Ok(()), + 6 => { + if payload.len() != 28 + || gpu_u32(payload, 0) == Some(0) + || gpu_u32(payload, 4) == Some(0) + || gpu_u32(payload, 8) == Some(0) + || gpu_u32(payload, 12) == Some(0) + || gpu_u32(payload, 16) == Some(0) + || !f32::from_bits(gpu_u32(payload, 20).unwrap()).is_finite() + || f32::from_bits(gpu_u32(payload, 20).unwrap()) <= 0.0 + || !matches!(gpu_u16(payload, 24), Some(1..=4)) + || gpu_u16(payload, 26) != Some(0) + { + return Err(anyhow!("invalid GPU surface-change event")); + } + Ok(()) + } + _ => Err(anyhow!("invalid GPU event payload")), + } +} + +fn validate_gpu_text( + payload: &[u8], + length_offset: usize, + flags_offset: usize, + text_offset: usize, +) -> Result<()> { + let Some(text_bytes) = gpu_u32(payload, length_offset).map(|value| value as usize) else { + return Err(anyhow!("truncated GPU text event")); + }; + let flags = if flags_offset == 6 { + gpu_u16(payload, flags_offset).map(u32::from) + } else { + gpu_u32(payload, flags_offset) + }; + let padded = text_bytes + .checked_add(3) + .map(|value| value & !3) + .and_then(|value| text_offset.checked_add(value)) + .ok_or_else(|| anyhow!("GPU event text length overflow"))?; + if text_bytes > gpu_wire::MAX_GPU_DIAGNOSTIC_BYTES + || flags.is_none_or(|value| value > 1) + || payload.len() != padded + || core::str::from_utf8(&payload[text_offset..text_offset + text_bytes]).is_err() + || payload[text_offset + text_bytes..] + .iter() + .any(|byte| *byte != 0) + { + return Err(anyhow!("invalid GPU text event")); + } + Ok(()) +} + +fn gpu_u16(bytes: &[u8], offset: usize) -> Option { + Some(u16::from_le_bytes( + bytes.get(offset..offset + 2)?.try_into().ok()?, + )) +} + +fn gpu_u32(bytes: &[u8], offset: usize) -> Option { + Some(u32::from_le_bytes( + bytes.get(offset..offset + 4)?.try_into().ok()?, + )) +} + +fn gpu_u64(bytes: &[u8], offset: usize) -> Option { + Some(u64::from_le_bytes( + bytes.get(offset..offset + 8)?.try_into().ok()?, + )) +} + +fn read_guest_memory( + instance: &mut polkavm::RawInstance, + pointer: u32, + length: usize, +) -> Result> { + if length > MAX_GUEST_READ { + return Err(anyhow!("guest read exceeds {MAX_GUEST_READ} bytes")); + } + let mut bytes = vec![0; length]; + instance + .read_memory_into(pointer, &mut bytes[..]) + .map_err(|error| anyhow!("read guest memory: {error:?}"))?; + Ok(bytes) +} + +fn map_call_result(result: Result<(), CallError>, phase: &str) -> Result<()> { + match result { + Ok(()) => Ok(()), + Err(CallError::Trap) => Err(anyhow!("guest trapped during {phase}")), + Err(CallError::NotEnoughGas) => Err(anyhow!("guest exhausted gas during {phase}")), + Err(CallError::Error(error)) => Err(error).context(format!("guest error during {phase}")), + Err(CallError::User(error)) => Err(error).context(format!("host error during {phase}")), + Err(error) => Err(anyhow!( + "unexpected PolkaVM error during {phase}: {error:?}" + )), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn presentation_profile_uses_tri2d_without_legacy_alias() { + assert_eq!( + PresentationProfile::parse("tri2d").expect("tri2d profile should parse"), + PresentationProfile::Tri2d + ); + assert!( + PresentationProfile::parse("ui-mesh").is_err(), + "the provisional profile name must not remain an alias" + ); + } + + #[test] + fn input_queue_coalesces_pointer_motion_and_stays_bounded() { + let mut state = HostState::new(HashMap::new(), PresentationProfile::Framebuffer, false); + for coordinate in 0..10_000u16 { + state.queue_input(InputEvent { + event_type: InputEventType::PointerMove, + code: 0, + x: coordinate, + y: coordinate, + }); + } + assert_eq!(state.input.len(), 1); + assert_eq!(state.input.back().unwrap().x, 9_999); + + state.queue_input(InputEvent { + event_type: InputEventType::SurfaceMetrics, + code: 32, + x: 1_280, + y: 800, + }); + state.queue_input(InputEvent { + event_type: InputEventType::SurfaceMetrics, + code: 64, + x: 2_560, + y: 1_600, + }); + assert_eq!( + state + .input + .iter() + .filter(|event| event.event_type == InputEventType::SurfaceMetrics) + .count(), + 1 + ); + assert_eq!(state.input.back().unwrap().x, 2_560); + + for index in 0..(MAX_QUEUED_INPUT_EVENTS + 100) { + state.queue_input(InputEvent { + event_type: InputEventType::KeyDown, + code: index as u8, + x: 0, + y: 0, + }); + } + assert_eq!(state.input.len(), MAX_QUEUED_INPUT_EVENTS); + assert_eq!( + state + .input + .iter() + .filter(|event| event.event_type == InputEventType::SurfaceMetrics) + .count(), + 1 + ); + state.queue_input(InputEvent { + event_type: InputEventType::SurfaceMetrics, + code: 64, + x: 3_000, + y: 2_000, + }); + assert_eq!(state.input.len(), MAX_QUEUED_INPUT_EVENTS); + assert_eq!( + state.input.back().unwrap().event_type, + InputEventType::SurfaceMetrics + ); + } + + #[test] + fn surface_change_rejects_non_positive_and_non_finite_scale() { + let mut event = vec![0; gpu_wire::GPU_EVENT_HEADER_BYTES + 28]; + event[..4].copy_from_slice(&gpu_wire::GPU_EVENT_MAGIC); + event[4..6].copy_from_slice(&gpu_wire::GPU_WIRE_VERSION.to_le_bytes()); + event[6..8].copy_from_slice(&(gpu_wire::GpuEventType::SurfaceChanged as u16).to_le_bytes()); + let event_len = event.len() as u32; + event[8..12].copy_from_slice(&event_len.to_le_bytes()); + let payload = &mut event[gpu_wire::GPU_EVENT_HEADER_BYTES..]; + for offset in [0, 4, 8, 12, 16] { + payload[offset..offset + 4].copy_from_slice(&1u32.to_le_bytes()); + } + payload[24..26] + .copy_from_slice(&(gpu_wire::GpuTextureFormat::Rgba8Unorm as u16).to_le_bytes()); + + payload[20..24].copy_from_slice(&1.0f32.to_le_bytes()); + validate_gpu_event(&event).expect("positive finite scale should be valid"); + for scale in [0.0f32, -1.0, f32::NAN, f32::INFINITY] { + event[gpu_wire::GPU_EVENT_HEADER_BYTES + 20..gpu_wire::GPU_EVENT_HEADER_BYTES + 24] + .copy_from_slice(&scale.to_le_bytes()); + assert!( + validate_gpu_event(&event).is_err(), + "invalid scale {scale:?} must fail closed" + ); + } + } +} diff --git a/rust/crates/pvm-runtime/src/manifest.rs b/rust/crates/pvm-runtime/src/manifest.rs new file mode 100644 index 000000000..e5a0d66c8 --- /dev/null +++ b/rust/crates/pvm-runtime/src/manifest.rs @@ -0,0 +1,220 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::PresentationProfile; +use anyhow::{anyhow, bail, Context, Result}; +use serde::Deserialize; +use std::collections::BTreeMap; + +/// Validated host-facing description of a strict App manifest v2 executable. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppDescriptor { + /// Semantic application version. + pub app_version: Vec, + /// PolkaVM entrypoint within the verified application archive. + pub program_path: String, + /// Selected presentation profile. + pub presentation: PresentationProfile, + /// Whether the application may submit audio. + pub audio_enabled: bool, + /// Required device-input features. + pub input_features: Vec, + /// Required WebGPU limits, empty for other profiles. + pub gpu_limits: BTreeMap, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Manifest { + #[serde(rename = "$v")] + version: u32, + kind: String, + #[serde(rename = "appVersion")] + app_version: Vec, + runtime: Runtime, + capabilities: Capabilities, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Runtime { + kind: String, + #[serde(rename = "abiVersion")] + abi_version: u32, + entrypoint: String, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Capabilities { + graphics: Graphics, + #[serde(rename = "deviceInput")] + device_input: Option, + audio: Option

Tk5Q+Jm6R}FKHPAE^_C&qtC)nUV27X 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 Date: Fri, 28 Aug 2026 04:32:56 -0400 Subject: [PATCH 10/40] fix(pvm): satisfy release quality gates --- deny.toml | 4 ++++ .../Sources/TrUAPIHost/pvm_runtime.swift | 8 ++++---- .../pvm-browser-runtime/dist/SHA256SUMS | 2 +- .../dist/pvm-browser-runtime.wasm | Bin 888397 -> 887521 bytes rust/crates/pvm-runtime/src/application.rs | 6 +++++- rust/crates/pvm-runtime/src/lib.rs | 10 ++++++---- rust/crates/pvm-runtime/src/native_ffi.rs | 6 +++--- rust/crates/pvm-runtime/src/wasm_codegen.rs | 9 +++++++-- .../truapi-host-cli/src/frame_server.rs | 3 +++ 9 files changed, 33 insertions(+), 15 deletions(-) diff --git a/deny.toml b/deny.toml index 1a454ec77..e5a99db32 100644 --- a/deny.toml +++ b/deny.toml @@ -41,6 +41,10 @@ exceptions = [ # BSL-1.0 is permissive, but scope it so it cannot spread to other crates. { name = "clipboard-win", allow = ["BSL-1.0"] }, { name = "error-code", allow = ["BSL-1.0"] }, + # The host-neutral PVM runtime and wire protocol are intentionally MPL-2.0 + # so their Rust and browser implementations retain file-level copyleft. + { name = "pvm-gpu-wire", allow = ["MPL-2.0"] }, + { name = "pvm-runtime", allow = ["MPL-2.0"] }, { name = "uniffi", allow = ["MPL-2.0"] }, { name = "uniffi_bindgen", allow = ["MPL-2.0"] }, { name = "uniffi_checksum_derive", allow = ["MPL-2.0"] }, diff --git a/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift b/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift index 991898e8a..633a0212a 100644 --- a/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift +++ b/ios/truapi-host/Sources/TrUAPIHost/pvm_runtime.swift @@ -1211,7 +1211,7 @@ enum NativePvmError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError - case Runtime(message: String + case Runtime(detail: String ) case DuplicateAsset(path: String ) @@ -1246,7 +1246,7 @@ public struct FfiConverterTypeNativePvmError: FfiConverterRustBuffer { case 1: return .Runtime( - message: try FfiConverterString.read(from: &buf) + detail: try FfiConverterString.read(from: &buf) ) case 2: return .DuplicateAsset( path: try FfiConverterString.read(from: &buf) @@ -1264,9 +1264,9 @@ public struct FfiConverterTypeNativePvmError: FfiConverterRustBuffer { - case let .Runtime(message): + case let .Runtime(detail): writeInt(&buf, Int32(1)) - FfiConverterString.write(message, into: &buf) + FfiConverterString.write(detail, into: &buf) case let .DuplicateAsset(path): diff --git a/js/packages/pvm-browser-runtime/dist/SHA256SUMS b/js/packages/pvm-browser-runtime/dist/SHA256SUMS index 8b9f359b7..2ce137d0e 100644 --- a/js/packages/pvm-browser-runtime/dist/SHA256SUMS +++ b/js/packages/pvm-browser-runtime/dist/SHA256SUMS @@ -1,4 +1,4 @@ -6d0ff4045f0bc278fc352d692bfb6e5f664e46fa0d792ee31a70cbcf0d05c095 pvm-browser-runtime.wasm +1648d88456922f88991f8e8a79f1f92163287595fc51e95edeeffb849d8624d0 pvm-browser-runtime.wasm 36f9b7eaba64ec43481ce5ec58f8393f50060f20396567ebd6de8c0a3dc2e204 pvm-worker.js 33418e2c81c117539569eb4cf91af4d058cdfae7dd4556b13f3687f6d6b3bae4 pvm-gpu-worker.js 91aab4b2f7a790e9b0cdfd01a256523272f2cd842acc01081d9f0a8aae048254 pvm-wasm-translated.js diff --git a/js/packages/pvm-browser-runtime/dist/pvm-browser-runtime.wasm b/js/packages/pvm-browser-runtime/dist/pvm-browser-runtime.wasm index 0e46acb93bdcad2910946674b9f8de7f1d0352fb..1696077a2181e70e357f4d0988c2db432d3d78d5 100755 GIT binary patch literal 887521 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�`0C)xq6N)jt=V{Xg) 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 C3~CAh literal 888397 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