Skip to content

Fixed and updated model 0 and model 1 for framegen with clean-room re… - #8

Open
utkarshdalal wants to merge 2 commits into
xXJSONDeruloXx:mainfrom
GameNative:main
Open

Fixed and updated model 0 and model 1 for framegen with clean-room re…#8
utkarshdalal wants to merge 2 commits into
xXJSONDeruloXx:mainfrom
GameNative:main

Conversation

@utkarshdalal

Copy link
Copy Markdown
Collaborator

…write (#1)

teldommm pushed a commit to teldommm/bionic-fg that referenced this pull request Jul 29, 2026
…ndroid wrapper ICDs (Winlator/Turnip) (xXJSONDeruloXx#6)

* fix(manifest): declare disable_environment so implicit-layer loaders don't skip the layer

The Vulkan spec requires an implicit layer manifest to declare BOTH
enable_environment and disable_environment. With only enable_environment,
strict loaders (e.g. the glibc Khronos loader used inside Winlator-style
wrapper-ICD setups) reject the layer at manifest-parse time:

  Layer "VK_LAYER_BIONIC_framegen" doesn't contain required layer object
  disable_environment in the manifest JSON file, skipping this layer

Add disable_environment (BIONIC_FG_DISABLE=1) to satisfy the spec so the
layer is actually discovered and loaded.

* feat: single-device mode + layer-dispatch routing for Android wrapper ICDs

Makes the frame-gen layer work on Android Winlator-style wrapper ICDs
(wrapper_icd -> Turnip via adrenotools), where the original standalone
second-device design hangs/crashes. Changes:

- Single-device FramegenContext: run interpolation on the application's OWN
  VkDevice (vk::Device::wrap of the app instance/physical/device/queue)
  instead of spinning a second VkInstance+VkDevice and doing cross-device
  AHB sharing. The two-device path deadlocked on this stack (compute submit /
  queue-wait across distinct wrapper-ICD instances never completes). JNI
  standalone path keeps the original Device::create().
- Route physical-device queries through the layer's device/instance dispatch
  (memPropsFn from nextGIPA) instead of global vkXxx symbols. Global calls
  with chain-level handles from inside a layer can be rejected as invalid
  (the same hazard CreateDevice already guards against), which was crashing
  AHB import (vkGetPhysicalDeviceMemoryProperties).
- Bound the present-path fence waits (UINT64_MAX -> 250ms) at all three wait
  sites: on a timeout, present the real frame (no hang); after 2 consecutive
  timeouts, disable framegen for that swapchain so the app runs full speed
  instead of stuttering into an ANR.
- Remove two *instance* extensions (VK_KHR_external_memory_capabilities,
  VK_KHR_get_physical_device_properties2) wrongly listed in the device-ext
  array kRequiredDeviceExts[]; they always reported 'Device ext not available'
  (harmless, filtered before vkCreateDevice, but misleading).
- Add an optional fps_limit (conf.toml key / BIONIC_FG_FPS_LIMIT env, 0=off,
  10..200): paces the application's real present calls so on-screen FPS =
  limit x multiplier; generated frames bypass the pacer. Works in passthrough
  too.

Verified on-device (Adreno 750, Turnip, DXVK 2.4.1): 2x/3x/4x frame gen and
the fps limiter all working, no hangs.

* fix(layer): track copy and generated-frame fence timeouts separately

Addresses review: the shared fenceTimeouts counter was reset on every copy-
fence success, before the generated-frame fence waits. At 2x a single generated
timeout bumped it to 1, then the next frame's successful copy reset it to 0, so
the disable-after-two-timeouts behaviour could never trigger for generated
frames. Split into copyFenceTimeouts / genFenceTimeouts, each reset only when a
fence of its own type signals; framegen is disabled once either reaches the
threshold.

* feat(layer): split sync-incompatibility timeout from generated-frame cadence deadline

Addresses review (250ms arbitrary / could mis-fire on a legitimately slow frame;
the generated-frame skip is really hang-recovery):
- Replace the fixed 250ms wait with a generous sync-incompatibility timeout
  scaled off the frame budget (4x base interval, clamped 200ms..1s) when an fps
  limit is set, else a 500ms fallback. Bounded waits always return so ANR can't
  happen regardless; the timeout only governs how long before a never-signaling
  path is judged stuck.
- Raise the consecutive-timeout disable threshold 2 -> 6 so transient slow frames
  (shader compilation, queue backlog) don't permanently disable framegen.
- Give generated frames a separate, shorter cadence deadline (~2x the target
  output interval = 1s/(base*mult)) when an fps limit is set: a frame that misses
  it is skipped to hold pace and is NOT counted as a sync failure; only a timeout
  at the full sync bound counts toward disabling.

* feat(layer): review follow-ups — self-describing fps cap, variance-aware cadence, opt-in derive/even-pace

Addresses PR xXJSONDeruloXx#6 review threads:
- C (fps_limit:211 / xXJSONDeruloXx#8): split fps_limit_enabled bool from the remembered
  fps_limit value so conf.toml is self-describing; respect any positive value
  (10-200 is a frontend convenience, no longer clamped in the layer). Back-compat:
  a config without fps_limit_enabled treats fps_limit>0 as enabled.
- B/derive (1093): the budget-derived sync-incompatibility timeout is now opt-in
  (derive_sync_timeout, default off -> fixed 500ms fallback) since deriving at
  init is unreliable.
- B/cadence (1334): generated-frame deadline is now variance-aware — derived from
  a measured base-interval EWMA (a cap is a ceiling, not a perf promise) with a
  1.5x tolerance band, so a slightly-late frame is still presented and only a big
  overrun is skipped to hold pace.
- D (1200): opt-in even_pace flag spaces generated presents across the base
  interval at 1s/(base*mult); off by default.
- E (manifest:17): document that disable_environment is loader-required for
  implicit layers and not a second toggle.

* fix(layer): make sync-failure force-disable sticky vs conf hot-reload

A swapchain auto-disabled after kMaxFenceTimeouts stored the kill in
conf.enabled, which the hot-reload path overwrites wholesale from the
file (st.conf = newConf). So any conf.toml touch — notably the in-game
flow-scale slider — silently re-armed framegen on a present path already
proven sync-incompatible with the ICD. Split the runtime kill into a
sticky SwapState::framegenForceDisabled that hot-reload never clears;
the QueuePresent gate honours it, and the clean re-attempt point stays a
swapchain recreate (fresh SwapState defaults it false).

Answers PR xXJSONDeruloXx#6 review thread on layer.cpp:1093 (within-session recovery).
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.

1 participant