Skip to content

feat(assets): openFd opens a bundled APK asset as a file descriptor - #38

Merged
leonardomag31-lab merged 3 commits into
mainfrom
feat/asset-fd
Oct 1, 2026
Merged

leonardomag31-lab merged 3 commits into
mainfrom
feat/asset-fd

Conversation

@leonardomag31-lab

@leonardomag31-lab leonardomag31-lab commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Why

labelle-bgfx#168 plays a bundled music file on Android: it decodes the APK asset with video.decodeTrack, which reads a file descriptor. To get that fd, the PR declared the AAsset* externs in bgfx and read ANativeActivity.assetManager by a hand-counted index (fields[8]). @apotema's review asked for that to live here instead: Android code goes in labelle-android, bgfx only gets generic seams (the rule from labelle-bgfx#172).

What

  • assets.openFd(activity, name) ?AssetFd: opens name (relative to the APK's assets/) and returns { fd, start, len } for the fd decoders here (video.decodeTrack, video.VideoDecoder). AssetFd.close() closes the descriptor; the caller owns it.
    • Null for a null activity, an empty name, a name over 255 bytes, a missing or compressed asset, and always off Android.
    • Callable from any thread (bgfx calls it from its decode worker): the NDK's AAssetManager is thread-safe and the AAsset lives only inside the call.
  • src/jni/asset_fd.c: reads activity->assetManager by name through <android/native_activity.h>, then does AAssetManager_open → AAsset_openFileDescriptor64 → AAsset_close (the fd is dup'd, so it outlives the AAsset). Same shape as the other jni/*.c files.
  • root.zig exports assets; build.zig compiles the C file.

The asset must be stored uncompressed in the APK, which the android provider already does.

Tests

  • zig build test: host 133/133. The 5 new tests:
    • null off Android
    • the name reaches the JNI half NUL-terminated, and the fd and range come back
    • a failed open is null
    • null activity / empty / over-long names never reach the JNI half, and the 255-byte boundary is accepted
    • openFd analyses on every target: takes openFd's address so the Android compile-check type-checks the JNI branch. That branch was only reached lazily before, which let a calling-convention mismatch through that the first device build caught. I checked that the check fails if the mismatch comes back.
  • ANDROID_NDK_HOME=… zig build test -Dtarget=aarch64-linux-android: compiles.
  • On device (P42, Android 16): Flying Platform #976 built against this branch (local: pin) and the reworked labelle-bgfx#168. The in-game theme MP3 opens through openFd and decodes to exactly 5,375,040 frames (the same as the old path), gameplay runs at 52 FPS, and the theme plays.

Next

Release (v0.4.1) → labelle-bgfx#168 bumps its labelle-android pin to it (the same line #186 moves to 0.4.0) → bgfx release → FP #976's pin.

Not in scope: labelle-bgfx's src/video/backend.zig has the same hand-rolled asset open; moving it onto openFd is the follow-up @apotema mentioned.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

labelle-bgfx#168 plays a bundled music file on Android by decoding it
with video.decodeTrack, which reads an fd. To get one, bgfx declared the
AAsset* externs itself and read ANativeActivity.assetManager by a
hand-counted field index (fields[8]). Android code belongs here, not in
the backends, and the struct layout belongs to the NDK header.

assets.openFd(activity, name) returns the APK fd plus the asset's byte
range; jni/asset_fd.c reads activity->assetManager by name through
<android/native_activity.h>. The name checks are split from the JNI half
so host tests drive them, and a test takes openFd's address so the
Android compile-check type-checks the JNI branch.
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 1d8ed252-8fcc-459a-b345-cc42ffac2912

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@apotema apotema left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. This is exactly the seam the #168 review asked for (reviewed at ff6f338; Claude, on Alexandre's behalf).

  • jni/asset_fd.c: reads assetManager through <android/native_activity.h>, so there's no more hand-counted fields[8]. The AAsset is closed after AAsset_openFileDescriptor64, so the caller owns only the dup'd fd. A compressed entry gives -1, as documented.
  • assets.zig: the name checks are split from the JNI half, so host tests cover NUL-termination, the 255-byte boundary, and null/empty/over-long names never reaching JNI. _ = &openFd makes the Android compile-check analyse the JNI branch. The thread-safety note is right: AAssetManager is thread-safe, and the asset is opened and closed in one call.
  • CI: all 9 checks green.

Before merging:

  1. Merge main: the branch is 1 behind; main has since released 0.5.0, the Vulkan auto default.
  2. Release as v0.6.0, not v0.4.1: 0.5.0 already shipped, and this adds public API. Bump build.zig.zon to 0.6.0 in this PR or a release PR, then tag v0.6.0.
  3. Add a labelle-registry entry for 0.6.0 (commit + sha256 of the commit tarball, as in #12/#13), or FP's provider lock can't resolve it.

leonardomag31-lab and others added 2 commits October 1, 2026 17:27
Alexandre's approval on #38 asked for v0.6.0, not v0.4.1: 0.5.0 (the
Vulkan `auto` default) shipped meanwhile, and `assets.openFd` is new
public API.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leonardomag31-lab
leonardomag31-lab merged commit 7a656df into main Oct 1, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants