Skip to content
dream-soft-render
api

egui adapter

render_egui, set_skip_unchanged_frames, and the egui_adapter module: RenderFrame, RenderOutcome, RenderTimings, TextureEvidence, egui_texture_id, format_repaint_delay and the benchmark workload.

Everything here needs the egui feature. Hosting egui explains how the pieces fit together.

use dream_soft_render::SoftwareRenderer;
use dream_soft_render::egui_adapter::{RenderFrame, RenderOutcome, egui};

fn main() -> std::io::Result<()> {
    let context = egui::Context::default();
    let mut renderer = SoftwareRenderer::default();
    let outcome: RenderOutcome = renderer.render_egui(
        320,
        240,
        &RenderFrame::new(&context),
        |ui| {
            ui.label("Hello");
        },
    )?;
    assert!(outcome.surface_changed);
    // The first frame uploads egui's font atlas.
    assert_eq!(outcome.texture_evidence.full_upload_count, 1);
    Ok(())
}

render_egui#

pub fn render_egui(&mut self, width: usize, height: usize, frame: &RenderFrame<'_>, run_ui: impl FnMut(&mut egui::Ui)) -> io::Result<RenderOutcome>

A method of SoftwareRenderer. Runs run_ui for one egui frame of widthxheight points, applies egui’s texture uploads, tessellates at one pixel per point, and rasterizes into the surface, unless the frame is identical to the last one. Then frees the textures egui released. With frame.synthetic_workload set, it rasterizes that workload instead and does not run run_ui.

Returns an error when the surface cannot be sized, an upload is malformed, over the texture budget or longer than 65536 texels on a side, a mesh’s index count is not a multiple of three or an index is past its vertices, a clip rectangle is not finite, or egui emits a paint callback. Mesh errors carry the crate’s Error. When a frame fails lists each error’s text and what state the renderer is left in.

set_skip_unchanged_frames#

pub const fn set_skip_unchanged_frames(&mut self, skip: bool)

A method of SoftwareRenderer. Whether render_egui may skip a frame whose tessellated output is bit-identical to the last one, with no texture uploads, and keep the surface. On by default. Turn it off to rasterize every frame, as a benchmark must.

RenderFrame#

pub struct RenderFrame<'a>

What one render_egui call needs besides the UI: the egui context, and the diagnostics switches. Every field is public.

FieldType
context&'a egui::ContextThe context the UI runs in
logOption<&'a dyn Fn(&str)>Where diagnostic lines go; None discards them
log_frameboolMeasure each stage, and log surface totals and stage timings
log_render_statsboolLog the renderer’s deep statistics. Slow
hitch_log_thresholdOption<Duration>When set, measure each stage even without log_frame. Nothing compares against it but the host
frame_indexu64Printed in the log lines
repaint_request_due_before_frameboolPrinted in the render_stats line
synthetic_workloadOption<&'a SampledRectModulatedWorkload>Rasterize this instead of running the UI
pub const fn new(context: &'a egui::Context) -> RenderFrame<'a>

A frame for context with every diagnostic off and no workload. Build others from it with struct update syntax: RenderFrame { log_frame: true, ..RenderFrame::new(&context) }.

RenderOutcome#

pub struct RenderOutcome

What one render_egui call did. Clone, Copy, Debug.

FieldType
repaint_delayDurationHow long egui wants to wait before the next frame. Duration::MAX: no repaint requested
timingsOption<RenderTimings>Stage timings, when log_frame or hitch_log_threshold asked for them
primitive_countusizeThe clipped primitives egui produced
texture_evidenceTextureEvidenceThe texture store after the frame, and this frame’s uploads
surface_changedboolfalse when the frame matched the last one and the surface was left as it was

RenderTimings#

pub struct RenderTimings

How long each stage of one render_egui call took, in microseconds, every field a u128. Clone, Copy, Debug.

Field
resize_clearSizing the surface, and clearing it if the size changed
egui_runRunning the UI
texture_applyApplying egui’s texture uploads
tessellateTessellating egui’s shapes
rasterizeRasterizing, or for a skipped frame, finding that it is unchanged
texture_freeFreeing the textures egui released
totalThe whole call

TextureEvidence#

pub struct TextureEvidence

The texture store after a frame, and the uploads egui made during it. Every field a usize. Clone, Copy, Debug, PartialEq, Eq.

Field
countTextures stored, yours and egui’s
bytesBytes of texture pixels stored, against MAX_TEXTURE_BYTES
set_countUploads egui made this frame
set_bytesBytes those uploads carried
full_upload_countUploads that replaced or created a whole texture
partial_update_countUploads that patched part of one

egui_texture_id#

pub const fn egui_texture_id(texture: TextureId) -> egui::TextureId

The egui::TextureId that makes an egui mesh, egui::Image or painter call sample a texture made with create_texture: egui::TextureId::User with the handle’s number. egui uploads only Managed textures, so the two kinds never collide.

format_repaint_delay#

pub fn format_repaint_delay(repaint_delay: Duration) -> String

A repaint delay for a log line: none for Duration::MAX, otherwise Duration’s Debug form, such as 16ms.

egui#

pub use egui;

The egui crate this one is built against, 0.34. Using it through the re-export keeps your egui types and the renderer’s the same version.

SampledRectModulatedWorkload#

pub struct SampledRectModulatedWorkload

A fixed rasterizer benchmark, used by dream-ini’s PortMaster build to measure the renderer on a device without egui in the measurement. Debug, PartialEq, Eq.

It is 192 one-pixel-tall textured rectangles, 20 of them 2 pixels wide, 152 of 5 and 20 of 10, each sampling a run of a 64x4 texture with mixed alpha and tinted with one non-white color: the shape of glyph rows. With RenderFrame::synthetic_workload set, render_egui clears the surface to (17, 20, 28), rasterizes the workload, and reports a changed surface with one primitive, without running the UI.

Item
fn new(viewport_width: usize, viewport_height: usize) -> SampledRectModulatedWorkloadLays the rectangles out in rows for a surface of that size
const fn matches_viewport(&self, width: usize, height: usize) -> boolWhether it was built for that size. render_egui does not check; rebuild it when the surface size changes
fn config_log_line(&self, frame_limit: u64) -> StringOne line describing the workload, for a benchmark log
const DESCRIPTION: &'static strThe workload’s shape in words