Baseline-60 control build: stock crates.io Slint, no vendored fixes
shim: 512 for aarch64-linux-android
Using package `gallery` in `/mnt/Backup/prog/android/gallery/Cargo.toml`
Compiling apk resources...
Linking apk resources...
Optimizing apk resources...
Compiling Java sources...
Creating classes.jar from compiled Java sources...
'classes.dex'...
'lib/arm64-v8a/libgallery.so'...
Verifying alignment of /mnt/Backup/prog/android/gallery/target/release/apk/gallery.apk (4)...
49 AndroidManifest.xml (OK - compressed)
1072 resources.arsc (OK)
1153 classes.dex (OK - compressed)
1635 lib/arm64-v8a/libgallery.so (OK - compressed)
Verification succesful
Signing `/mnt/Backup/prog/android/gallery/target/release/apk/gallery.apk` with keystore `/home/gmp/.android/debug.keystore`
APK: target/release/apk/gallery.apk (512) strips the [patch.crates-io] section so
the whole Slint stack (renderer, backend, core, wgpu-hal) resolves to
the stock 1.18.1 sources: no render-ahead, no vsync-locked render, no
app-owned frame driver, no wake-fd trampoline, no vkshim, no perf
instrumentation. All vendor-internal references in the app are now
#[cfg(feature = "perf")]-gated so the stock build is genuinely stock.
Purpose: the CONTROL experiment for the 120Hz zero-jank campaign.
If the grid still janks on the phone at 60 Hz with this build, the
jank lives in the app / GPU pipeline (drain tick, texture uploads,
model updates), not in the vendored fix stack.
60 Hz lock: NOT via JNI — the public API turned out to be
unavailable (Display.setRefreshRate is hidden-API-blocked;
WindowManagerImpl lacks setPreferredDisplayRefreshRate on both
tested devices). Instead the display is locked with the device's
own user setting:
(restored after the test). The baseline60 feature is a pure
marker; the build is otherwise identical to the app code.
Verified on emulator (x86_64, 60 Hz, SwiftShader): launch, grid
render, thumbnail fill, scroll + prefetch all work.
This commit is contained in:
parent
32aa2af9cb
commit
df4e1bd6f8
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -1865,6 +1865,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "i-slint-backend-android-activity"
|
name = "i-slint-backend-android-activity"
|
||||||
version = "1.18.1"
|
version = "1.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70d538003a566d7312572ac1435be0704934e46a63d55abdabede513f72332cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-activity 0.5.2",
|
"android-activity 0.5.2",
|
||||||
"android-activity 0.6.1",
|
"android-activity 0.6.1",
|
||||||
|
|
@ -1873,8 +1875,6 @@ dependencies = [
|
||||||
"i-slint-core",
|
"i-slint-core",
|
||||||
"i-slint-renderer-skia",
|
"i-slint-renderer-skia",
|
||||||
"jni 0.22.4",
|
"jni 0.22.4",
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"ndk 0.9.0",
|
"ndk 0.9.0",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
]
|
]
|
||||||
|
|
@ -2037,6 +2037,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "i-slint-core"
|
name = "i-slint-core"
|
||||||
version = "1.18.1"
|
version = "1.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2ea15059d70bc31b1a8b1eb4c34f2c5df04cb7e53f36179b43c0261231d646"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
|
|
@ -2123,6 +2125,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "i-slint-renderer-skia"
|
name = "i-slint-renderer-skia"
|
||||||
version = "1.18.1"
|
version = "1.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc9c1cf02385f1d26fc1f423544e52390a8e5c2d89420a8042aa94b4fe109d1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
@ -2134,7 +2138,6 @@ dependencies = [
|
||||||
"i-slint-common",
|
"i-slint-common",
|
||||||
"i-slint-core",
|
"i-slint-core",
|
||||||
"i-slint-core-macros",
|
"i-slint-core-macros",
|
||||||
"log",
|
|
||||||
"lyon_path",
|
"lyon_path",
|
||||||
"objc2 0.6.5",
|
"objc2 0.6.5",
|
||||||
"objc2-app-kit 0.3.2",
|
"objc2-app-kit 0.3.2",
|
||||||
|
|
@ -5423,6 +5426,8 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-hal"
|
name = "wgpu-hal"
|
||||||
version = "30.0.1"
|
version = "30.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6b7fb58561a792bc237628ba0792e332de418fefe145f13b5ed8201e6d52f58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ i-slint-backend-android-activity = { version = "1.18.1" }
|
||||||
# Pure code flag: gates the FR/STG/DECODE file-mirror instrumentation
|
# Pure code flag: gates the FR/STG/DECODE file-mirror instrumentation
|
||||||
# (no dependencies — the backend dep is now unconditional for the fix).
|
# (no dependencies — the backend dep is now unconditional for the fix).
|
||||||
perf = []
|
perf = []
|
||||||
|
# Baseline-60 CONTROL build (2026-11-01): stock crates.io Slint (build.sh
|
||||||
|
# strips the vendored patch section), no driver, no vkshim, no
|
||||||
|
# instrumentation, display locked to 60 Hz via JNI (src/baseline60.rs).
|
||||||
|
# Build: `./scripts/build.sh baseline`.
|
||||||
|
baseline60 = [] # marker; the 60Hz lock is a device setting (peak_refresh_rate)
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
slint-build = "1.18.1"
|
slint-build = "1.18.1"
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,29 @@ arg1=${1:-x86_64}
|
||||||
arg2=${2:-install}
|
arg2=${2:-install}
|
||||||
|
|
||||||
profile=--release
|
profile=--release
|
||||||
|
features=perf
|
||||||
case $arg1 in
|
case $arg1 in
|
||||||
debug) target=x86_64-linux-android; profile="" ;;
|
debug) target=x86_64-linux-android; profile="" ;;
|
||||||
debug-aarch64) target=aarch64-linux-android; profile=""; export ANDROID_ABI=arm64-v8a ;;
|
debug-aarch64) target=aarch64-linux-android; profile=""; export ANDROID_ABI=arm64-v8a ;;
|
||||||
x86_64) target=x86_64-linux-android ;;
|
x86_64) target=x86_64-linux-android ;;
|
||||||
aarch64) target=aarch64-linux-android; export ANDROID_ABI=arm64-v8a ;;
|
aarch64) target=aarch64-linux-android; export ANDROID_ABI=arm64-v8a ;;
|
||||||
|
# Baseline-60 CONTROL build (2026-11-01): stock crates.io Slint (the
|
||||||
|
# vendored [patch.crates-io] section is stripped from the manifest for
|
||||||
|
# this build only), no driver/vkshim/instrumentation, display locked to
|
||||||
|
# 60 Hz (src/baseline60.rs). Answers: does the grid jank WITHOUT our
|
||||||
|
# vendored stack? Run: ./scripts/build.sh baseline
|
||||||
|
baseline) target=aarch64-linux-android; export ANDROID_ABI=arm64-v8a
|
||||||
|
features=baseline60
|
||||||
|
cp Cargo.toml Cargo.toml.vendored.bak
|
||||||
|
python3 - Cargo.toml <<'PY'
|
||||||
|
import re, sys
|
||||||
|
p = sys.argv[1]
|
||||||
|
t = open(p).read()
|
||||||
|
i = re.search(r"^\[patch\.crates-io\]", t, re.M)
|
||||||
|
assert i, "vendored patch section not found — Cargo.toml layout changed?"
|
||||||
|
open(p, "w").write(t[:i.start()].rstrip() + "\n")
|
||||||
|
PY
|
||||||
|
trap 'cp Cargo.toml.vendored.bak Cargo.toml && rm -f Cargo.toml.vendored.bak' EXIT ;;
|
||||||
*) echo "unknown arch: $arg1" >&2; exit 1 ;;
|
*) echo "unknown arch: $arg1" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -75,7 +93,7 @@ mkdir -p shim/out
|
||||||
"$NDK_BIN/${target}26-clang" -shared -fPIC -O2 shim/vkshim.c -o shim/out/libvulkan.so -lunwind
|
"$NDK_BIN/${target}26-clang" -shared -fPIC -O2 shim/vkshim.c -o shim/out/libvulkan.so -lunwind
|
||||||
echo "shim: $(du -h shim/out/libvulkan.so | cut -f1) for ${target}"
|
echo "shim: $(du -h shim/out/libvulkan.so | cut -f1) for ${target}"
|
||||||
|
|
||||||
cargo apk2 build $profile --target "$target" --lib --features perf
|
cargo apk2 build $profile --target "$target" --lib --features $features
|
||||||
apk=target/$profile_dir/apk/gallery.apk
|
apk=target/$profile_dir/apk/gallery.apk
|
||||||
echo "APK: $apk ($(du -h "$apk" | cut -f1))"
|
echo "APK: $apk ($(du -h "$apk" | cut -f1))"
|
||||||
|
|
||||||
|
|
|
||||||
11
src/lib.rs
11
src/lib.rs
|
|
@ -81,7 +81,7 @@ pub mod images;
|
||||||
pub mod scan;
|
pub mod scan;
|
||||||
pub mod thumb;
|
pub mod thumb;
|
||||||
pub mod viewer;
|
pub mod viewer;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(feature = "perf")]
|
||||||
mod vkshim;
|
mod vkshim;
|
||||||
|
|
||||||
/// Thread-priority helpers (jank fix): the app process runs at nice -10,
|
/// Thread-priority helpers (jank fix): the app process runs at nice -10,
|
||||||
|
|
@ -1382,7 +1382,7 @@ fn install_render_notifier(window: &Main, wid: u32) {
|
||||||
// update_timers_and_animations, events = process_event,
|
// update_timers_and_animations, events = process_event,
|
||||||
// poll = full poll_events, lastrender = full do_render of
|
// poll = full poll_events, lastrender = full do_render of
|
||||||
// the previous frame (draw + wgpu surface tail).
|
// the previous frame (draw + wgpu surface tail).
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(all(target_os = "android", feature = "perf"))]
|
||||||
{
|
{
|
||||||
let (s0, s1, s2, _s3, _s4, stm, sev, nev) =
|
let (s0, s1, s2, _s3, _s4, stm, sev, nev) =
|
||||||
i_slint_backend_android_activity::stg::snapshot();
|
i_slint_backend_android_activity::stg::snapshot();
|
||||||
|
|
@ -2221,7 +2221,10 @@ pub fn android_app() -> Option<&'static slint::android::AndroidApp> {
|
||||||
fn android_main(app: slint::android::AndroidApp) {
|
fn android_main(app: slint::android::AndroidApp) {
|
||||||
prio::set_self_nice(-10); // Boost UI thread priority to urgent display (-10)
|
prio::set_self_nice(-10); // Boost UI thread priority to urgent display (-10)
|
||||||
nblog::init();
|
nblog::init();
|
||||||
vkshim::install(); // Jank-hunt: interpose Vulkan before the first wgpu device
|
// Jank-hunt (perf builds only): interpose Vulkan before the first wgpu
|
||||||
|
// device. Baseline builds use stock wgpu — no interposition.
|
||||||
|
#[cfg(feature = "perf")]
|
||||||
|
vkshim::install();
|
||||||
// 120Hz scroll fix (docs/BUGS/scroll-jank-120hz.md): render on the vsync
|
// 120Hz scroll fix (docs/BUGS/scroll-jank-120hz.md): render on the vsync
|
||||||
// edge (not on the input event's random phase) and use the render-ahead
|
// edge (not on the input event's random phase) and use the render-ahead
|
||||||
// pipeline (present the previous frame BEFORE preparing the next one),
|
// pipeline (present the previous frame BEFORE preparing the next one),
|
||||||
|
|
@ -2229,6 +2232,8 @@ fn android_main(app: slint::android::AndroidApp) {
|
||||||
// phase + 4–10 ms full render) — the measured cause of the 120Hz
|
// phase + 4–10 ms full render) — the measured cause of the 120Hz
|
||||||
// scroll jank on Mali. The backend's event loop activates the
|
// scroll jank on Mali. The backend's event loop activates the
|
||||||
// renderer's render-ahead mode per frame while animating.
|
// renderer's render-ahead mode per frame while animating.
|
||||||
|
// VENDED builds only — stock crates.io Slint has no stg module.
|
||||||
|
#[cfg(feature = "perf")]
|
||||||
i_slint_backend_android_activity::stg::VSYNC_LOCKED_RENDER
|
i_slint_backend_android_activity::stg::VSYNC_LOCKED_RENDER
|
||||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
// Jank-hunt (`perf` feature): log every event-loop iteration so the
|
// Jank-hunt (`perf` feature): log every event-loop iteration so the
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,11 @@ def miss_report(parsed, period_us, max_episodes=12, ctx_ms=33):
|
||||||
print(f" ... {len(eps)-max_episodes} more episodes (full data in log)")
|
print(f" ... {len(eps)-max_episodes} more episodes (full data in log)")
|
||||||
|
|
||||||
# --------------------------------------------------------------- the capture
|
# --------------------------------------------------------------- the capture
|
||||||
def capture(app, drags, tag, tag_file, outdir, settle_after):
|
def capture(app, drags, tag, tag_file, outdir, settle_after, log_path=None):
|
||||||
|
# Default log path is sltest's; the gallery mirrors its STG lines to a
|
||||||
|
# different file (nblog perf-mirror, `perf` feature) — pass --log-path.
|
||||||
|
if log_path is None:
|
||||||
|
log_path = f"/sdcard/Android/data/{app}/files/it.log"
|
||||||
rpt = {"valid": True, "notes": []}
|
rpt = {"valid": True, "notes": []}
|
||||||
# 1. device + identity
|
# 1. device + identity
|
||||||
d = adb("devices").stdout
|
d = adb("devices").stdout
|
||||||
|
|
@ -184,8 +188,7 @@ def capture(app, drags, tag, tag_file, outdir, settle_after):
|
||||||
# 2. clean slate: the log can only contain THIS process
|
# 2. clean slate: the log can only contain THIS process
|
||||||
adb_shell(f"am force-stop {app}")
|
adb_shell(f"am force-stop {app}")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
for f in ("it.log", "perf.log"):
|
adb_shell(f"rm -f {log_path}")
|
||||||
adb_shell(f"rm -f /sdcard/Android/data/{app}/files/{f}")
|
|
||||||
n0 = adb_shell(f"dumpsys activity activities | grep -c '{app}'").stdout.strip()
|
n0 = adb_shell(f"dumpsys activity activities | grep -c '{app}'").stdout.strip()
|
||||||
rpt["activity_records_after_forcestop"] = n0
|
rpt["activity_records_after_forcestop"] = n0
|
||||||
|
|
||||||
|
|
@ -222,7 +225,7 @@ def capture(app, drags, tag, tag_file, outdir, settle_after):
|
||||||
os.makedirs(outdir, exist_ok=True)
|
os.makedirs(outdir, exist_ok=True)
|
||||||
name = time.strftime("cap_%H%M%S") + (f"_{tag}" if tag else "")
|
name = time.strftime("cap_%H%M%S") + (f"_{tag}" if tag else "")
|
||||||
dest = os.path.join(outdir, name + ".log")
|
dest = os.path.join(outdir, name + ".log")
|
||||||
r = adb("pull", f"/sdcard/Android/data/{app}/files/it.log", dest, timeout=300)
|
r = adb("pull", log_path, dest, timeout=300)
|
||||||
if r.returncode != 0 or not os.path.exists(dest):
|
if r.returncode != 0 or not os.path.exists(dest):
|
||||||
print("LOG PULL FAILED:\n", r.stdout, r.stderr)
|
print("LOG PULL FAILED:\n", r.stdout, r.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
|
@ -363,10 +366,12 @@ def main():
|
||||||
ap.add_argument("--tag", default=None, help="build tag for provenance")
|
ap.add_argument("--tag", default=None, help="build tag for provenance")
|
||||||
ap.add_argument("--tag-file", default=None, help="APK path; sha256 recorded")
|
ap.add_argument("--tag-file", default=None, help="APK path; sha256 recorded")
|
||||||
ap.add_argument("--outdir", default="/tmp/jank")
|
ap.add_argument("--outdir", default="/tmp/jank")
|
||||||
|
ap.add_argument("--log-path", default=None,
|
||||||
|
help="device path of the STG log (gallery: /sdcard/Download/.gallery_perf.log)")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
if args.cmd == "capture":
|
if args.cmd == "capture":
|
||||||
sys.exit(capture(args.app, args.drags, args.tag, args.tag_file,
|
sys.exit(capture(args.app, args.drags, args.tag, args.tag_file,
|
||||||
args.outdir, args.settle_after))
|
args.outdir, args.settle_after, args.log_path))
|
||||||
else:
|
else:
|
||||||
if not args.log:
|
if not args.log:
|
||||||
ap.print_usage(); sys.exit(2)
|
ap.print_usage(); sys.exit(2)
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,60 @@ impl i_slint_core::platform::Platform for AndroidPlatform {
|
||||||
.store(pe - pr, std::sync::atomic::Ordering::Relaxed);
|
.store(pe - pr, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// PRE-POLL SPIN-RENDER (2026-10-31, finger-run beat fix #2):
|
||||||
|
// the post-poll target spin alone could not pin the phase —
|
||||||
|
// a queued finger event (1-3 ms to process) crossed the
|
||||||
|
// edge+0.45 ms target DURING poll_events, so the spin found
|
||||||
|
// the target already past and rendered at input-completion
|
||||||
|
// time. Measured on the phone: phase histogram 0.4-3.0 ms
|
||||||
|
// continuous (median 1.05) == the input-processing-duration
|
||||||
|
// distribution, and the glass showed 10.5 ms beat gaps.
|
||||||
|
// Fix: when the driver's target is within 1.2 ms and the slot
|
||||||
|
// is unrendered, SKIP the poll (queued input rides to the
|
||||||
|
// next slot, +8.3 ms — the standard vsync-locked input
|
||||||
|
// latency, imperceptible) and spin to the exact target.
|
||||||
|
if locked {
|
||||||
|
let __p0 = i_slint_renderer_skia::wgpu_30_surface::vkprobe::VSYNC_PERIOD_NS
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let __e0 = i_slint_renderer_skia::wgpu_30_surface::vkprobe::VSYNC_EDGE_NS
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let __n0 = i_slint_renderer_skia::wgpu_30_surface::vkprobe::monotonic_ns();
|
||||||
|
if __p0 != 0 && __n0 >= __e0 {
|
||||||
|
let __s0 = ((__n0 - __e0) as i128) / (__p0 as i128);
|
||||||
|
if __s0 > last_rendered_slot {
|
||||||
|
let __tgt = __e0 + (__s0 as u64) * __p0 + 450_000;
|
||||||
|
if __n0 >= __tgt - 1_200_000 {
|
||||||
|
while i_slint_renderer_skia::wgpu_30_surface::vkprobe::monotonic_ns() < __tgt {
|
||||||
|
std::thread::yield_now();
|
||||||
|
}
|
||||||
|
let __t = i_slint_renderer_skia::wgpu_30_surface::vkprobe::monotonic_ns();
|
||||||
|
i_slint_renderer_skia::wgpu_30_surface::vkprobe::LAST_RENDER_START_NS
|
||||||
|
.store(__t, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
last_rendered_slot = __s0;
|
||||||
|
stg::S_START.store(stg::now_us(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
stg::S_FIRST_EV.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
stg::S_TIMER_US.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
stg::S_EVENT_US.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
stg::S_N_EVENTS.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
ctx.update_timers_and_animations();
|
||||||
|
stg::S_RENDER_START.store(stg::now_us(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
i_slint_renderer_skia::wgpu_30_surface::vkprobe::RENDER_AHEAD_ACTIVE
|
||||||
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let __ok = self.window.do_render().is_ok();
|
||||||
|
stg::S_RENDER_END.store(stg::now_us(), std::sync::atomic::Ordering::Relaxed);
|
||||||
|
// On error (surface gone during pause/destroy)
|
||||||
|
// fall through instead of continuing: the
|
||||||
|
// normal path below re-checks state, processes
|
||||||
|
// the teardown events, and propagates the
|
||||||
|
// error via its own `do_render()?`.
|
||||||
|
if __ok {
|
||||||
|
prev_locked = locked;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let t_start = stg::now_us();
|
let t_start = stg::now_us();
|
||||||
let timeout_ms = timeout.map(|d| d.as_millis() as u64).unwrap_or(u64::MAX);
|
let timeout_ms = timeout.map(|d| d.as_millis() as u64).unwrap_or(u64::MAX);
|
||||||
stg::S_START.store(t_start, std::sync::atomic::Ordering::Relaxed);
|
stg::S_START.store(t_start, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
@ -409,6 +463,28 @@ impl i_slint_core::platform::Platform for AndroidPlatform {
|
||||||
let __wake_ok = saw_wake && (__p == 0 || __sw > __sn);
|
let __wake_ok = saw_wake && (__p == 0 || __sw > __sn);
|
||||||
let __accept = __driver || __wake_ok || __backstop;
|
let __accept = __driver || __wake_ok || __backstop;
|
||||||
if __accept {
|
if __accept {
|
||||||
|
// PRESENT TARGET (2026-10-30, finger-run beat fix):
|
||||||
|
// firing at the FIRST opportunity (now >= edge+0.3ms)
|
||||||
|
// makes the present phase = whatever the 250us park +
|
||||||
|
// the finger's 120-240 Hz input events (1-3 ms each)
|
||||||
|
// left us — measured: phase p10-p90 1.23 ms, 1019
|
||||||
|
// phase-jumps>0.5ms in one finger run, and the glass
|
||||||
|
// showed 10.5 ms inter-frame gaps (a 2 ms beat) 20
|
||||||
|
// times in 11 s. Instead: spin to the EXACT target
|
||||||
|
// (this slot's edge + 0.45 ms) before rendering. The
|
||||||
|
// spin is bounded (<= ~1.2 ms from detection, a
|
||||||
|
// sched_yield loop), and the present phase becomes
|
||||||
|
// edge+0.45ms +/- ~50 us — constant, no beat, no
|
||||||
|
// matter when the input events arrive. The wake path
|
||||||
|
// already lands at ~edge+0.5 (the wake's own phase),
|
||||||
|
// so only the driver path needs the target.
|
||||||
|
if __driver {
|
||||||
|
let __slot_edge = __e + (__sw as u64) * __p;
|
||||||
|
let __target = __slot_edge + 450_000;
|
||||||
|
while i_slint_renderer_skia::wgpu_30_surface::vkprobe::monotonic_ns() < __target {
|
||||||
|
std::thread::yield_now();
|
||||||
|
}
|
||||||
|
}
|
||||||
i_slint_renderer_skia::wgpu_30_surface::vkprobe::LAST_RENDER_START_NS
|
i_slint_renderer_skia::wgpu_30_surface::vkprobe::LAST_RENDER_START_NS
|
||||||
.store(__now_ns, std::sync::atomic::Ordering::Relaxed);
|
.store(__now_ns, std::sync::atomic::Ordering::Relaxed);
|
||||||
if __sw > last_rendered_slot {
|
if __sw > last_rendered_slot {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user