Repository navigation
feat(ffi): structs that can grow, plane sizes, new statuses, versions - #153
Merged
Merged
Conversation
From 1.0 on the C API is to stay compatible, and an audit of v0.16.0 found that it could not grow without breaking programs (#152, D69). This breaks it once, before 1.0, so that it need not break again: - Every struct passed by pointer starts with struct_size, which the caller sets to sizeof the struct. emberwave reads and writes no more than that, and takes the members a shorter struct lacks as zeros, so a minor version can add a member at the end. A size below the struct's first is INVALID_ARGUMENT. EmberwaveVulkanDevice points to its instance now, and EmberwaveSignalling's reserved words go. - EmberwavePlane and EmberwaveSourcePlane carry size, the bytes at data, which the calls check, instead of trusting the stride and the rows. - EMBERWAVE_STATUS_UNSUPPORTED for a call the system lacks, which was NO_GPU, and EMBERWAVE_STATUS_BUFFER_TOO_SMALL for an output array, plane or budget too small, which was INVALID_ARGUMENT. The header says to treat any status but OK as a failure. - emberwave_vulkan_instance_extensions takes a capacity apart from the count, as emberwave_frame_packets does. - Metal's two submits are emberwave_decoder_submit_texture_metal and emberwave_decoder_submit_plane_textures_metal, as Vulkan's and Direct3D's have their suffixes. - EMBERWAVE_VERSION and its parts in the header, and emberwave_version_number() in the library, tied to the crate's version at compile time. - Compile-time assertions of the threads the docs promise, in the C API and in the core; callbacks must not call emberwave; the calls on the caller's Metal, Vulkan and Direct3D objects are marked provisional. BREAKING CHANGE: every C caller sets struct_size and the planes' size, renames the Metal submits, and checks the new statuses. Claude-Session: https://claude.ai/code/session_019fJGoCTvP5e7n599q3ZQ42
The review found no unsound code and no functional bug, but two flaws in the growth scheme, which this fixes before anything ships: - EmberwaveVulkanInstance and EmberwaveVulkanSync ended in a 32-bit member after 64-bit ones, so 4 bytes of padding followed. A member added there would leave sizeof, and so struct_size, as it was, and the library would read it from an older caller's padding. Their 32-bit members come first now, and no struct has padding after its last member. - FIRST_SIZE was size_of, so the first member added would have refused every program built with the older header. The macro now takes the last member of each struct's first layout, which fixes FIRST_SIZE, and the last member now; it asserts at compile time that struct_size comes first and that nothing pads the end. A test pins the first sizes on 64-bit systems. And docs that contradicted the code: the budget gives BUFFER_TOO_SMALL; the calls that lack their graphics API give UNSUPPORTED, and the enum's doc says which calls do; the planes' safety contracts name size; the Vulkan device's contract names its instance; BUFFER_TOO_SMALL stores a size only where the call can tell it. A Vulkan test covers a NULL or short instance. Claude-Session: https://claude.ai/code/session_019fJGoCTvP5e7n599q3ZQ42
This was referenced Oct 31, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
From 1.0 on the C API is to stay compatible. An audit of v0.16.0 by our review agent found that it could not grow without breaking programs (#152). This pull request breaks it once, before 1.0, so that it need not break again. It is the C half of #152; the Rust half follows.
Changes
struct_size, which the caller sets tosizeofthe struct, for structs in and out.read_structandwrite_struct: no more bytes than the size, and zeros for the members a shorter struct lacks.EMBERWAVE_STATUS_INVALID_ARGUMENT.EmberwaveVulkanDevicepoints to its instance.EmberwaveSignalling'sreservedwords go.Defaultsets the size, for Rust callers and the tests.EmberwavePlaneandEmberwaveSourcePlanecarrysize, which the calls check.EMBERWAVE_STATUS_UNSUPPORTEDreplacesNO_GPUfor a call the system lacks.EMBERWAVE_STATUS_BUFFER_TOO_SMALLreplacesINVALID_ARGUMENTfor an output array, a decode's plane or an encode's budget that is too small.emberwave_vulkan_instance_extensionstakes a capacity apart from the count, asemberwave_frame_packetsdoes. Its counts stayuint32_t, as Vulkan's are.emberwave_decoder_submit_texture_metalandemberwave_decoder_submit_plane_textures_metal.EMBERWAVE_VERSION_MAJOR,_MINOR,_PATCHandEMBERWAVE_VERSIONin the header, andemberwave_version_number()in the library. A compile-time assertion ties them to the crate's version.Send + Sync, and decoders, encoders and texturesSend;GpuContext,DecoderandEncoder.ARCHITECTURE.md: the C API section and D69.CONTRIBUTING.md: the struct rule and the release rule.CHANGELOG.md.api.rs,encoder.rs,metal.rs,vulkan.rs,d3d12.rs).Testing
cargo test --release --workspace, with new tests for a size below the first (refused, nothing written), a larger struct (filled up to what this version knows, the rest untouched), a zeroed options struct, a decode's plane too small, an encode's source plane too small, and the header's version against the library's.scripts/check_c_example.sh: the C examples decode every vector and encode two sources.scripts/bench_app_on_mac.sh -autorun: the benchmark app's texture, plane and queue checks pass.cargo clippy --target x86_64-pc-windows-msvc --workspace --all-targets -D warningscompiles the Vulkan and Direct3D code and their tests (vulkan.rs,d3d12.rs) without a warning. They run in CI on lavapipe and WARP. A new Vulkan test checks a names array one short.cargo fmt --check, workspace clippy,--no-default-features, and wasm32.Benchmark impact
None.
Review
Our review agent found no unsound code and no functional bug. It probed the helpers from C, with oversized structs, sizes 0 to 11 and NULL, and plane spans with canary bytes. It compiled the header as C99 to C17 and C++11 and C++17, and type-checked the Swift sources. It found two flaws in the growth scheme and stale docs, all fixed in 3d7cf7c:
EmberwaveVulkanInstanceandEmberwaveVulkanSyncended with four bytes of padding. A member added there would have leftstruct_sizeunchanged, and the library would have read it from an older caller's padding. Their 32-bit members now come first.FIRST_SIZEwassize_of, so the first member added would have refused every older program. The macro now takes the last member of each struct's first layout, and asserts at compile time thatstruct_sizecomes first and that no padding follows the last member. A test pins the first sizes.INVALID_ARGUMENTfor a budget too small andNO_GPUoff-platform; they now nameBUFFER_TOO_SMALLandUNSUPPORTED. The enum's doc now says which calls giveUNSUPPORTED. The plane contracts namesize, and the Vulkan device contract names its instance.NULLor short instance.Related issue
Part of #152.