Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ce91055
fix(native): mask secure-field text before it reaches the renderer
tiensonqin Oct 12, 2026
896a106
fix(native): share the stacking/clip walk between paint and hit-test
tiensonqin Oct 12, 2026
c7ac207
Merge origin/feat/ml-backend into perf/ml-c1-secure-field-mask
tiensonqin Oct 12, 2026
16c4909
Merge remote-tracking branch 'origin/perf/ml-c1-secure-field-mask' in…
tiensonqin Oct 12, 2026
3cf6a38
test(macos): re-pin headless golden for masked demo secure field
tiensonqin Oct 12, 2026
a7923b7
Merge remote-tracking branch 'origin/feat/ml-backend' into ci-205
tiensonqin Oct 12, 2026
416a1bf
fix(macos): drop rect_intersect left unused by the shared hit walk
tiensonqin Oct 12, 2026
0af4e81
Merge branch 'ci-205' into ci-207
tiensonqin Oct 12, 2026
8b36153
Merge remote-tracking branch 'origin/feat/ml-backend' into perf/ml-c4…
tiensonqin Oct 12, 2026
955dea1
fix(native): distinguish node-not-drawn from subtree-invisible in cul…
tiensonqin Oct 12, 2026
1789cc2
Merge remote-tracking branch 'origin/feat/ml-backend' into ci-207
tiensonqin Oct 12, 2026
07c0343
test(native): assert actions/state in headless runs; share the ladder…
tiensonqin Oct 12, 2026
e4aa8fd
test(windows): assert scripted headless runs replay the same actions,…
tiensonqin Oct 12, 2026
47a2f50
Merge origin/feat/ml-backend into perf/ml-c2-shared-hit-walk
tiensonqin Oct 12, 2026
c9fc7c3
Merge origin/perf/ml-c2-shared-hit-walk into perf/ml-c4-subtree-culling
tiensonqin Oct 12, 2026
b639f92
Merge perf/ml-c4-subtree-culling into perf/ml-c8-interaction-tests
tiensonqin Oct 12, 2026
d5dc2fe
Merge origin/feat/ml-backend into perf/ml-c2-shared-hit-walk
tiensonqin Oct 12, 2026
7dd931d
Merge remote-tracking branch 'origin/feat/ml-backend' into ci-207
tiensonqin Oct 12, 2026
0831a1a
Merge perf/ml-c2-shared-hit-walk into perf/ml-c4-subtree-culling
tiensonqin Oct 12, 2026
76d383c
Merge remote-tracking branch 'origin/perf/ml-c4-subtree-culling' into…
tiensonqin Oct 12, 2026
a22569d
Merge origin/feat/ml-backend into perf/ml-c4-subtree-culling
tiensonqin Oct 12, 2026
629cbe8
Merge remote-tracking branch 'origin/perf/ml-c4-subtree-culling' into…
tiensonqin Oct 12, 2026
9a8bf2c
Merge origin/feat/ml-backend into perf/ml-c8-interaction-tests
tiensonqin Oct 13, 2026
7155f98
Merge remote-tracking branch 'origin/feat/ml-backend' into tmp-sync-229
tiensonqin Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions platform/native/lui_window/lui_window.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,86 @@ module Frame = struct
t.last_present_ms <- now_ms
end

module Renderer_ladder = struct
(* The renderer-ladder walk shared by the platform drivers. A ladder
is an ordered list of backend rungs (Vulkan/Gl/Cpu/Noop on Linux,
D3d11/Gl/Cpu/Noop on Windows): [walk] tries each rung's init left
to right — [try_rung] returning [None] steps down — and records
the untried tail so a mid-render failure can [demote] the active
rung and resume the walk below it. The platform supplies the
per-rung init (SDL/GL/Vulkan/D3D11 specifics), a [release] for
the dropped rung's resources and an optional [before_walk] hook
(the SDL driver re-creates its window there when the next rung
needs different flags); the sequencing, loss logging and the
exception-catching [renderer_live] wrapper live here once so the
fallback path can be exercised off-device. *)

type 'a attempt = {
renderer : Lui_host.renderer;
state : 'a;
(** rung payload the driver reads back — e.g. the live-GL flag
or the D3D11 device held for shutdown *)
}

type ('rung, 'a) t = {
warn : string -> unit;
release : 'a attempt -> unit;
before_walk : 'rung list -> unit;
exhausted_msg : string;
noop : 'a attempt;
try_rung : 'rung -> rest:'rung list -> 'a attempt option;
mutable remaining : 'rung list;
mutable active : 'a attempt;
}

let rec walk t = function
| [] ->
t.remaining <- [];
t.warn t.exhausted_msg;
t.noop
| rung :: rest ->
(* The tail lands in [remaining] before the try so a later
demote resumes strictly below the active rung — a backend
that already lost its device is never retried. *)
t.remaining <- rest;
(match t.try_rung rung ~rest with
| Some a -> a
| None -> walk t rest)

let create ~warn ~release ?(before_walk = fun _ -> ())
?(exhausted_msg =
"all renderer rungs failed; using no-op loop renderer")
~noop ~try_rung rungs =
let t =
{ warn; release; before_walk; exhausted_msg; noop; try_rung;
remaining = []; active = noop }
in
t.active <- walk t rungs;
t

let active t = t.active
let remaining t = t.remaining

let demote t ~reason =
t.warn
(Printf.sprintf "renderer %s lost (%s); stepping down the ladder"
t.active.renderer.Lui_host.name reason);
t.release t.active;
t.before_walk t.remaining;
t.active <- walk t t.remaining

(* The host owns a stable record whose render delegates to the live
rung, so a demote needs no host rebuild. *)
let renderer_live t =
{ Lui_host.name = "lui (resilient)";
render =
(fun s ->
try t.active.renderer.Lui_host.render s
with exn ->
demote t ~reason:(Printexc.to_string exn);
t.active.renderer.Lui_host.render s) }
end

module Multi = struct
(* Multi-window manager: a registry of independent surfaces. Each
surface owns a full pipeline — host (store, layout, scene,
Expand Down
53 changes: 53 additions & 0 deletions platform/native/lui_window/lui_window.mli
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,59 @@ module Frame : sig
interval. *)
end

(** The renderer-ladder walk shared by the platform drivers.

A ladder is an ordered list of backend rungs; {!create} tries each
rung's init left to right — [try_rung] returning [None] steps
down — and records the untried tail so a mid-render failure can
{!demote} the active rung and resume the walk below it. The
platform supplies the per-rung init, a [release] for the dropped
rung's resources and an optional [before_walk] hook (used to
re-create the OS window when the next rung needs different
flags); the sequencing, loss logging and the exception-catching
{!renderer_live} wrapper live here once so the fallback path can
be exercised with injected rung failures off-device. *)
module Renderer_ladder : sig
type 'a attempt = {
renderer : Lui_host.renderer;
state : 'a;
(** rung payload the driver reads back — e.g. the live-GL flag
or the D3D11 device held for shutdown *)
}

type ('rung, 'a) t

val create :
warn:(string -> unit) ->
release:('a attempt -> unit) ->
?before_walk:('rung list -> unit) ->
?exhausted_msg:string ->
noop:'a attempt ->
try_rung:('rung -> rest:'rung list -> 'a attempt option) ->
'rung list -> ('rung, 'a) t
(** [create ~warn ~release ~noop ~try_rung rungs] walks [rungs],
returning the state with the first successful rung active.
[try_rung rung ~rest] is the platform's init for one rung:
[Some attempt] on success, [None] to step down. [noop] is the
terminal attempt used when every rung fails; [release] drops a
demoted rung's resources; [before_walk] runs once per demote
with the remaining rungs (the SDL driver re-creates its window
there); [exhausted_msg] customises the empty-walk warning. *)

val active : ('rung, 'a) t -> 'a attempt
val remaining : ('rung, 'a) t -> 'rung list
(** Rungs below the active one — what a demote will walk. *)

val demote : ('rung, 'a) t -> reason:string -> unit
(** Drop the active rung (release + warn) and resume the walk at
{!remaining}. *)

val renderer_live : ('rung, 'a) t -> Lui_host.renderer
(** A stable renderer record whose render delegates to the active
rung: a render exception demotes once and retries on the next
rung inside the same call, so the host needs no rebuild. *)
end

(** Multi-window manager: a registry of independent surfaces.

Each surface owns a full pipeline — {!Lui_host} (store, layout,
Expand Down
Loading
Loading