Skip to content

feat: auto (Vulkan on Vulkan 1.1) is the default Android renderer; v0.5.0 (#30) - #39

Merged
apotema merged 2 commits into
mainfrom
feat/30-default-auto
Oct 1, 2026
Merged

apotema merged 2 commits into
mainfrom
feat/30-default-auto

Conversation

@apotema

@apotema apotema commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Owner decision (2026-10-01, labelle-bgfx#172): Vulkan becomes the default now, as auto (Vulkan when the device reports android.hardware.vulkan.version ≥ 1.1, else GLES), without waiting for the FP#978 gate.

  • Settings default (tools/settings.zig): no renderer key → default_renderer = .auto (was .gles). The strict parse is unchanged. manifest.Inputs.renderer defaults to the same constant.
  • D7 manifest: so the default manifest stamps labelle.renderer=auto and the optional <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="false"/>. An explicit "gles" produces the old manifest, with no Vulkan feature.
  • Runtime default (src/renderer.zig): a missing labelle.renderer meta-data now means default_setting = .auto, so an APK packaged before the setting existed behaves like a new default one. It goes through the device check and logs renderer: <v> (source: auto; default), using the new Source.auto_default. An unreadable (JNI error) or too-long meta-data still means gles, and an invalid value still means gles with a warning. decide's query now returns the MetaRead outcome, so it can tell "absent" apart from "read error".
  • Crash guard: precedence is unchanged (intent → crash guard → setting → auto's device check). An absent meta-data's guard stamp setting is now auto (was gles), the same as an APK that stamps auto. A default auto that resolves to Vulkan goes through beginVulkanStart/afterStart exactly like an explicit vulkan.
  • Docs: docs/renderer.md (default table, resolution rules, log lines, markers, and "Vulkan is the default" replacing the "after the gate" section), the README settings table and example, and docs/emulator-testing.md.
  • Version: build.zig.zon is now 0.5.0. The repo has no CHANGELOG, so the changelog text is here for the release notes: "Vulkan (auto) is now the default Android renderer; set "renderer": "gles" to opt out. Devices without Vulkan 1.1, or where Vulkan init fails, use GLES (device check + init fallback + crash guard)." No tag or release.

Tests (each asserts the path that ran, not just the value)

  • tools/package/manifest.zig:
    • default (no key) → parsed renderer == default_renderer == .auto → the manifest has the auto meta-data and the optional feature, and is identical to the explicit-auto manifest
    • explicit gles → the CLI's old manifest plus only the gles meta-data, no vulkan
  • src/renderer.zig:
    • missing meta-data → source = .auto_default, with the device check called once: Vulkan on a Vulkan 1.1 device, GLES without one
    • explicit gles never runs the device check
    • read error and too-long → gles from setting, with no device check
    • the guard still beats the default auto (meta and device never asked), and the intent still beats both
  • src/crash_guard.zig, full launches wired like resolve (LaunchQuery.launch):
    • a default auto writes the start mark 1 auto before init
    • a crash before stable → the next launch is gles (crash-guard) and stays there; an explicit auto APK is guarded by the same marker; an opt-out to gles resets it
    • a stable start clears its mark at the threshold, and the next launch is Vulkan again
    • an unrecordable start → crash_guard_unrecorded
    • no Vulkan 1.1 → GLES with no mark
  • tests/provider/e2e.py: the default fixture's packaged manifest has labelle.renderer=auto and the Vulkan feature.

Commands run locally (macOS arm64, Zig 0.16.0)

Command Exit
zig fmt --check . 0
zig build test --summary all (291/293 passed, 2 skipped) 0
zig build install-provider test-provider --summary all (135/135) 0
python3 tests/provider/stdio_e2e.py --tool zig-out/bin/labelle-android 0
python3 tests/provider/e2e.py --cli <labelle-cli d7827eb>/zig-out/bin/labelle --zig $(command -v zig) --require-doctor-aggregation (fake SDK) 0
ANDROID_NDK_HOME=…/ndk/27.0.12077973 zig build test -Dtarget=aarch64-linux-android --summary all 0

Not done here

  • Not run locally: provider-ndk (the real-SDK APK, Linux CI only), the 32-bit-failure check, and Windows.
  • The ticket's FP bump and device smoke tests (SM-T505 / P42) come later, in a separate step.

Closes #30
Part of labelle-toolkit/labelle-bgfx#172

https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ


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

)

A providers/android.json without a `renderer` key now means `auto`
(was `gles`), so the default manifest carries the optional Vulkan 1.1
uses-feature (D7). The runtime default for an APK without the
`labelle.renderer` meta-data is `auto` too (source `auto; default`);
an unreadable or too-long meta-data still means gles. Explicit `gles`
is the opt-out and produces the old manifest.

Resolution order and the crash guard are unchanged: a default `auto`
that resolves to Vulkan is marked before init, cleared when stable and
falls back to GLES after a crashed start, like an explicit `vulkan`.

Bumps the version to 0.5.0.

Claude-Session: https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T01:16:07.121759Z 09f5373 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 34 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 102 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 6bff1d69-f2cd-4fd5-a8bc-7951371ec3e1

📥 Commits

Reviewing files that changed from the base of the PR and between 4fd90ad and 09f5373.

📒 Files selected for processing (3)
  • docs/renderer.md
  • src/jni/renderer_query.c
  • src/renderer.zig

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 40e41c52-767b-49f4-9d32-a73b5278f7df

📥 Commits

Reviewing files that changed from the base of the PR and between 6bcad92 and 4fd90ad.

📒 Files selected for processing (9)
  • README.md
  • build.zig.zon
  • docs/emulator-testing.md
  • docs/renderer.md
  • src/crash_guard.zig
  • src/renderer.zig
  • tests/provider/e2e.py
  • tools/package/manifest.zig
  • tools/settings.zig

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

The provider and runtime renderer defaults change to auto. Generated manifests include optional Vulkan feature metadata for the default. At launch, devices reporting Vulkan 1.1 or newer use Vulkan; other devices use GLES. The crash guard covers Vulkan starts selected by default auto.

Changes

Renderer default and Vulkan selection

Layer / File(s) Summary
Set the default and generate manifest metadata
tools/settings.zig, tools/package/manifest.zig, tests/provider/e2e.py, README.md, docs/renderer.md, docs/emulator-testing.md, build.zig.zon
Missing renderer settings now use auto. Generated manifests include auto metadata and the optional Vulkan feature; explicit gles omits the feature. Tests and documentation reflect these settings, and the package version changes to 0.5.0.
Resolve renderer metadata at launch
src/renderer.zig, docs/renderer.md
Absent metadata resolves to auto, with a distinct source and log detail. Vulkan is selected when the device reports Vulkan 1.1 or newer. Read errors, overlong values, and invalid values resolve to GLES.
Guard Vulkan starts selected by default auto
src/crash_guard.zig, docs/renderer.md
The crash guard treats absent metadata as auto and covers Vulkan starts selected by that default. Tests cover repeated starts, marker updates, recording failure, and devices without Vulkan 1.1 support.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ProviderConfig
  participant Settings
  participant ManifestGenerator
  participant APKManifest
  participant JniQuery
  participant Renderer
  participant Device
  participant CrashGuard
  ProviderConfig->>Settings: Read renderer setting or use default auto
  Settings->>ManifestGenerator: Supply renderer setting
  ManifestGenerator->>APKManifest: Add renderer metadata and optional Vulkan feature
  APKManifest->>JniQuery: Provide renderer metadata at launch
  JniQuery->>Renderer: Return MetaRead outcome
  Renderer->>Device: Query Vulkan 1.1 support
  Device-->>Renderer: Return support result
  opt Vulkan selected
    Renderer->>CrashGuard: Record Vulkan start
  end
Loading

Merge Risk: ⚪ Minimal · up to 4fd90

The default renderer changes to auto, so Vulkan is used on devices reporting Vulkan 1.1 or newer and GLES elsewhere. The crash-guard path is stated to cover default-auto Vulkan starts. No concrete merge-blocking issue was found. Device smoke tests are deferred and should be run before release.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Issue [#30] requires the default renderer to be vulkan and requires the FP dependency update with removal of explicit renderer settings. This PR implements auto as the default and explicitly defer… Use the issue-approved default behavior and update the FP dependency and its renderer settings, or update [#30] to approve the auto design and track the FP changes separately. Add automated coverage for any changed default contract.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the renderer default change, runtime behavior, manifest updates, tests, version bump, and deferred work.
Title check ✅ Passed The title clearly identifies the main change: auto with Vulkan on Vulkan 1.1 is now the default Android renderer, with the version bump.
Out of Scope Changes check ✅ Passed The changed renderer logic, manifest feature handling, crash-guard tests, settings tests, documentation, README example, and 0.5.0 version bump all support the renderer-default objective in [#30]. N…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Full details: Linked Issues check

Explanation

Issue [#30] requires the default renderer to be vulkan and requires the FP dependency update with removal of explicit renderer settings. This PR implements auto as the default and explicitly defers the FP update. The PR does implement the optional Vulkan manifest feature, the 0.5.0 minor version, renderer resolution tests, and crash-guard tests. Manual SM-T505 and P42 smoke tests are not coding requirements for this assessment.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • 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 commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Codex, OpenAI LLM. VERDICT: CHANGES REQUESTED at exact 4fd90ad.

The new missing→auto default exposes an existing JNI ambiguity: src/jni/renderer_query.c returns -1 when Bundle.getString returns null, with the comment "Missing key, or a non-string value." MetaRead.fromCode(-1) now maps both to .absent, so a present boolean/integer/resource-valued labelle.renderer becomes auto/Vulkan on a capable device and is logged as a missing/default setting. Previously that ambiguity fell back to GLES. It can also make the guard compare/record an auto stamp for invalid metadata.

Distinguish true absence (Bundle.containsKey false) from a present non-string value. Return a separate invalid/read-error outcome for the latter so it chooses GLES and has accurate provenance/guard semantics. Add boolean/integer metadata coverage through the JNI contract boundary; pure .absent tests do not exercise this conflation.

Root and the single read-only helper otherwise found no actionable source issue in the default/provider feature, JNI-error/too-long GLES path, provenance or guard change. Current CI is green. This is a source finding; I have not yet run this head's Windows host tests or device controls. #188 diagnostic build remains the sole local heavy job. No approval/merge/release.

…absent (#30)

The JNI read returned -1 for both a missing key and a present non-string
value (Bundle.getString returns null for both), and MetaRead mapped -1 to
the new default `auto`, so a malformed meta-data could enable Vulkan and
stamp `auto` in the crash-guard marker.

renderer_query.c now uses Bundle.containsKey + get + instanceof String and
returns -4 for a present-but-not-a-String/null value. MetaRead.wrong_type
maps it to gles via a new `setting_invalid` source (logged as
`setting; invalid meta-data`, also used for invalid and too-long strings),
never runs the device check, and stamps `gles` for the crash guard. Only
-1 (absent) reaches the default auto.

Claude-Session: https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ
@apotema

apotema commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the CHANGES REQUESTED finding (absent vs present non-string meta-data) in 09f5373.

JNI (src/jni/renderer_query.c): the read now uses Bundle.containsKey and then Bundle.get plus IsInstanceOf(java/lang/String), in place of getString. The return codes are distinct and documented on the function:

  • -1: absent. Either there's no meta-data bundle or containsKey is false. This is the only code that leads to the default auto.
  • -4 (new): the key is present but its value isn't a String (Boolean, Integer, resource id) or is null.
  • -2: JNI failure or pending exception. Unchanged: exceptions are cleared before PopLocalFrame and the result is -2.
  • -3: the value is too long.
  • >= 0: the String's length.

Zig (src/renderer.zig):

  • MetaRead.fromCode(-4) returns the new .wrong_type.
  • decide maps .wrong_type to gles with the new source setting_invalid. The log line is renderer: gles (source: setting; invalid meta-data), and there's a warning: labelle.renderer meta-data is not a string ...; using gles. The device check never runs.
  • guardSetting() is "gles" for .wrong_type, so the crash-guard marker is never stamped auto for it.
  • Invalid and too-long strings now also report setting_invalid, for consistent provenance.
  • A read error keeps source: setting and leaves the guard setting unknown, as before.

Tests: a new host test drives raw JNI codes through MetaRead.fromCode into decide, and asserts the source, whether the device check ran, and the guard stamp:

  • -1: auto_default, the device check ran, stamp auto.
  • -4: gles/setting_invalid, no device check, stamp gles.
  • -2 and -9: gles/setting, no device check, guard setting null.
  • -3: setting_invalid.
  • >= 0: parsed as before.

The C file is #ifdef __ANDROID__ and there's no host JNI harness in this repo, so the C side of the contract is covered by the Android cross-compile plus the contract comment, not a host test. I haven't run it on a device.

Docs: docs/renderer.md has the rule-3 row and the new log lines.

Results (all exit 0):

  • zig fmt --check .
  • zig build test --summary all: 293/295 passed, 2 skipped
  • zig build install-provider test-provider --summary all
  • ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/27.0.12077973 zig build test -Dtarget=aarch64-linux-android --summary all: the JNI C compiles for aarch64-android

@apotema

apotema commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

VERDICT: APPROVED at exact 09f5373.

Codex, OpenAI LLM. The earlier absent/non-string JNI finding is fixed. Root and the single read-only helper found no remaining actionable source issue in renderer policy, provenance, guard stamps or generic/Android placement.

Independent Windows validation:

  • Clean named checkout at that head: Zig0.16 zig build test -j2 --summary all, exit0, 289 passed/6 skipped (platform-specific Windows skips). Dedicated native x86_64 Android standalone fixture built against this exact provider head and backend0.33 implementation, ReleaseFast/-j2, exit0; all12 APK variants signed/verified.
  • Rootable API36 Google APIs emulator, host GPU, cores2, official bundled Khronos1.4.363. Fifteen fresh-data policy cases plus one early-stop/relaunch guard case all pass actual bgfx renderer, source and >=120 actual presented frames (observed180–240), each retained launch18seconds. Explicit GLES/Vulkan/auto, absent→auto default, both debug overrides, invalid override; non-debuggable GLES/Vulkan/auto ignore opposing overrides; invalid/oversized strings and real Boolean/Integer/resource-valued metadata choose GLES with source setting; invalid meta-data. Aapt manifests prove metadata types and debuggable flags. No fatal signal/exception or captured VUID headers in retained case logs.
  • Deliberately delayed frame submission leaves the default-auto startup marker present after11seconds while fewer than120frames have been presented; stable launch clears it after both thresholds. Force-stop during initial delay leaves a genuine startup marker; next absent/default-auto launch chooses actualGLES/sourcecrash-guard, renders240frames and retains disabled marker. This tests abrupt early process exit, not a reproduced native driver crash. No synthetic markers.

First10 device cases passed before an ADB host disconnect; saved infrastructure failure, restarted emulator and reran only remaining6 successfully. Device evidence does not claim feature-negative hardware behavior or JNI read-exception injection; those remain source/host contract coverage. Emulator is off and no build is active.

Evidence under D:/prj/labelle-toolkit: labelle-android-39-evidence/{results,summary}.json, per-case logs/PNGs/manifests, guard-11sec marker/log; build/package/host-test logs and exit files, retained first-run ADB failure and successful remaining-run log. All8 current CI jobs green; no inline review comments; CodeRabbit explicitly rate-limited at this head, ignored per protocol. Approval is for #39, not #188/#195 or the separate production-device gate. Author handles merge/release/order.

@apotema
apotema merged commit dadab4c into main Oct 1, 2026
9 checks passed
@apotema
apotema deleted the feat/30-default-auto branch October 1, 2026 02:15
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.

Android Vulkan (labelle-bgfx#172) P7: make vulkan the default renderer (after the gate)

1 participant