cranpose-app-shell 0.22.0

App lifecycle, frame cadence and input dispatch for Cranpose
Documentation
const DEFAULT_FRAME_STAGE_TELEMETRY_THRESHOLD_MS: f64 = 4.0;

pub(super) fn frame_stage_telemetry_threshold_ms() -> Option<f64> {
    let diagnostics = &cranpose_core::DIAGNOSTICS;
    diagnostics
        .frame_stage_telemetry_ms
        .threshold_ms()
        .or_else(|| {
            diagnostics
                .frame_stage_telemetry
                .is_set()
                .then_some(DEFAULT_FRAME_STAGE_TELEMETRY_THRESHOLD_MS)
        })
}

/// The stages of a frame that its telemetry times: start, initial layout,
/// post-layout recomposition, dispatch and render.
pub(super) type FrameStages = cranpose_core::StageTimes<5>;

pub(super) fn log_frame_stage_telemetry(stages: &FrameStages) {
    let Some(threshold_ms) = frame_stage_telemetry_threshold_ms() else {
        return;
    };
    let Some(
        [
            frame_start,
            after_initial_layout,
            after_layout,
            after_dispatch,
            after_render,
        ],
    ) = stages.times()
    else {
        return;
    };
    let ms = cranpose_core::stage_ms;
    let total_ms = ms(frame_start, after_render);
    if total_ms < threshold_ms {
        return;
    }
    log::warn!(
        "[frame-stage-telemetry] total_ms={total_ms:.2} layout_ms={:.2} initial_layout_ms={:.2} post_layout_ms={:.2} dispatch_ms={:.2} scene_ms={:.2}",
        ms(frame_start, after_layout),
        ms(frame_start, after_initial_layout),
        ms(after_initial_layout, after_layout),
        ms(after_layout, after_dispatch),
        ms(after_dispatch, after_render),
    );
}

pub(super) fn render_phase_dirty_diagnostics_enabled() -> bool {
    cranpose_core::DIAGNOSTICS.render_phase_dirty_diag.is_set()
}

pub(super) struct RenderPhaseDirtyDiagnostics<'a> {
    pub(super) render_dirty: bool,
    pub(super) pointer_dirty: bool,
    pub(super) scene_dirty: bool,
    pub(super) draw_repass_pending: bool,
    pub(super) draw_dirty_nodes: usize,
    pub(super) layer_property_nodes: usize,
    pub(super) layout_dirty_nodes: usize,
    pub(super) moved_nodes: usize,
    pub(super) structural_dirty_nodes: usize,
    pub(super) scene_updates: usize,
    pub(super) dirty_node_ids: Option<String>,
    pub(super) render_only_dirty: bool,
    pub(super) recomposed_this_frame: bool,
    pub(super) path: &'a str,
}

pub(super) fn log_render_phase_dirty_diagnostics(diagnostics: RenderPhaseDirtyDiagnostics<'_>) {
    if !render_phase_dirty_diagnostics_enabled() {
        return;
    }

    let RenderPhaseDirtyDiagnostics {
        render_dirty,
        pointer_dirty,
        scene_dirty,
        draw_repass_pending,
        draw_dirty_nodes,
        layer_property_nodes,
        layout_dirty_nodes,
        moved_nodes,
        structural_dirty_nodes,
        scene_updates,
        dirty_node_ids,
        render_only_dirty,
        recomposed_this_frame,
        path,
    } = diagnostics;
    if let Some(dirty_node_ids) = dirty_node_ids {
        log::warn!(
            "[render-phase-dirty] path={path} render_dirty={render_dirty} pointer_dirty={pointer_dirty} scene_dirty={scene_dirty} draw_repass_pending={draw_repass_pending} draw_dirty_nodes={draw_dirty_nodes} layer_property_nodes={layer_property_nodes} layout_dirty_nodes={layout_dirty_nodes} moved_nodes={moved_nodes} structural_dirty_nodes={structural_dirty_nodes} scene_updates={scene_updates} render_only_dirty={render_only_dirty} recomposed_this_frame={recomposed_this_frame} ids={dirty_node_ids}",
        );
    } else {
        log::warn!(
            "[render-phase-dirty] path={path} render_dirty={render_dirty} pointer_dirty={pointer_dirty} scene_dirty={scene_dirty} draw_repass_pending={draw_repass_pending} draw_dirty_nodes={draw_dirty_nodes} layer_property_nodes={layer_property_nodes} layout_dirty_nodes={layout_dirty_nodes} moved_nodes={moved_nodes} structural_dirty_nodes={structural_dirty_nodes} scene_updates={scene_updates} render_only_dirty={render_only_dirty} recomposed_this_frame={recomposed_this_frame}",
        );
    }
}