pub struct FrameReport {
pub frame_index: u64,
pub paint_damage: FrameDamage,
pub present_damage: FrameDamage,
pub accumulated_paint_damage: FrameDamage,
pub accumulated_present_damage: FrameDamage,
pub frames_since_reset: u32,
pub reset_generation: u64,
pub relayout_iterations: u32,
pub dom_regenerations: u32,
pub layout_passes: u32,
pub hit_depth_cap: bool,
pub terminal_result: u8,
}Expand description
Per-frame observability record hung off LayoutWindow.
This is what makes damage + frame-work counters visible to an E2E assertion:
CallbackInfo::get_layout_window().frame_report. The CPU backend writes
paint_damage / present_damage / frame_index after every render_frame;
the event loop writes the work counters.
The *_since_reset counters are STICKY: they are never cleared automatically
(a per-tick reset would race the assertion that wants to read them). Use the
reset_frame_counters debug op to zero them at a known point in a test.
Fields§
§frame_index: u64Monotonic index of the last CPU-rendered frame.
paint_damage: FrameDamagePAINT damage of the last frame — the pixels actually re-rasterised.
present_damage: FrameDamagePRESENT damage of the last frame — the pixels that changed on screen (⊇ paint damage; a scroll memmoves a large region but paints a strip).
accumulated_paint_damage: FrameDamageUNION of the paint damage of every frame since the last counter reset.
This is what a test must assert on: between the step that changed
something and the assertion, the engine may render further (idle) frames
whose damage is None, which would clobber paint_damage. The
accumulated damage is stable across those.
accumulated_present_damage: FrameDamageUNION of the present damage of every frame since the last counter reset.
frames_since_reset: u32Frames rendered since the last counter reset.
reset_generation: u64Generation of the last observed reset request (see [request_frame_report_reset]).
relayout_iterations: u32Highest process_window_events recursion depth reached since the last
counter reset. > 1 means the frame did not converge in one pass.
THIS IS AN EVENT-PASS COUNTER AND NOT A LAYOUT COUNTER — the name is
historical and has misled every reader of it. 0 means “no state delta
was processed”, which is what an idle frame looks like; it does NOT mean
“no layout ran”. A mutation that arrives through the CALLBACK API
(set_node_css_override, set_node_text, set_node_classes, …) never
enters process_window_events at all, yet routes through
ShouldIncrementalRelayout → a FULL relayout of the root DOM. Asserting
max_relayouts: 0 over such a step therefore passes while the engine
re-laid-out the whole tree. Use Self::layout_passes for that
question.
dom_regenerations: u32Number of regenerate_layout() (i.e. layout_callback) runs since the
last counter reset.
layout_passes: u32Number of times LAYOUT ACTUALLY RAN since the last counter reset.
Incremented in LayoutWindow::layout_and_generate_display_list, the
one funnel both hosts and both paths go through — the shells’
regenerate_layout / incremental_relayout and the E2E runner’s
regenerate_layout() / relayout_only(). So it counts layout work
regardless of what scheduled it, which is exactly what the other two
counters each miss on their own:
| step | relayout_iterations | dom_regenerations | layout_passes |
|---|---|---|---|
| idle tick | 0 | 0 | 0 |
| inert pointer event | 1 | 0 | 0 |
set_node_css_override | 0 | 0 | 1 |
| resize / mount | 1 / 0 | 1 | 1 |
The second and third rows are the two directions the over-invalidation
families were reading wrongly: an input-free timeline never reaches the
event pass (counter 0, a vacuous pass even while a manager rebuilds the
display list every frame), and a callback-API mutation runs a full
relayout while the counter stays 0.
hit_depth_cap: booltrue if MAX_EVENT_RECURSION_DEPTH was ever hit since the last reset.
Today the engine only log_warns on this; this flag is what lets a test
turn an invalidation loop into a red assertion instead of a silent cap.
terminal_result: u8The last terminal ProcessEventResult (as its u8 discriminant order).
Implementations§
Source§impl FrameReport
impl FrameReport
Sourcepub fn sync_generation_to(&mut self, requested_generation: u64)
pub fn sync_generation_to(&mut self, requested_generation: u64)
Zero the work counters + accumulated damage if requested_generation
(this window’s LayoutWindow::frame_report_reset_request) has moved
since this report last observed it. Called by every writer of the report
before it writes.
Sourcepub fn as_of_generation(&self, requested_generation: u64) -> Self
pub fn as_of_generation(&self, requested_generation: u64) -> Self
This report as a READER must see it: the counters and accumulated damage
as of requested_generation.
The reset is applied lazily (a writer zeroes the fields on its next
write), because the op that requests it — and the assertion that reads
the result — only ever hold &LayoutWindow through CallbackInfo.
Without this, every read between reset_frame_counters and the next
frame returned the counters and damage from BEFORE the reset: an
assertion placed right after the reset silently measured the previous
checkpoint’s work.
Sourcepub fn reset_counters(&mut self)
pub fn reset_counters(&mut self)
Zero the sticky work counters + accumulated damage.
Sourcepub fn record_frame_at_generation(
&mut self,
requested_generation: u64,
paint: FrameDamage,
present: FrameDamage,
)
pub fn record_frame_at_generation( &mut self, requested_generation: u64, paint: FrameDamage, present: FrameDamage, )
Record the damage of a freshly rendered frame: it becomes the last-frame damage AND is merged into the accumulated damage since the last reset.
Prefer LayoutWindow::record_frame, which supplies the window’s reset
generation; this form is for tests that hold a bare report.
Trait Implementations§
Source§impl Clone for FrameReport
impl Clone for FrameReport
Source§fn clone(&self) -> FrameReport
fn clone(&self) -> FrameReport
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for FrameReport
impl Debug for FrameReport
Source§impl Default for FrameReport
impl Default for FrameReport
Source§fn default() -> FrameReport
fn default() -> FrameReport
impl Eq for FrameReport
Source§impl PartialEq for FrameReport
impl PartialEq for FrameReport
impl StructuralPartialEq for FrameReport
Auto Trait Implementations§
impl Freeze for FrameReport
impl RefUnwindSafe for FrameReport
impl Send for FrameReport
impl Sync for FrameReport
impl Unpin for FrameReport
impl UnsafeUnpin for FrameReport
impl UnwindSafe for FrameReport
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more