1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::{Color, DrawCommand, FontConfig, RendererError};
pub trait RenderBackend {
/// Begin a new frame. Note: `scale_factor` and `generation` may be ignored by backends that receive pre-scaled commands (see `SoftwareRenderer::begin_frame`).
fn begin_frame(
&mut self,
width: u32,
height: u32,
scale_factor: f32,
generation: u64,
) -> Result<(), RendererError>;
/// Process and present all draw commands for this frame. Must be called exactly once per frame after `begin_frame`.
fn render_frame(
&mut self,
commands: &[DrawCommand],
clear_color: Option<Color>,
) -> Result<(), RendererError>;
/// The most recently rendered frame as premultiplied RGBA8888 (`[R, G, B, A]` per pixel, row-major), if
/// this backend renders to an offscreen target. Windowed/on-screen backends present directly and return
/// `None`. Used to read back pixels from a headless render pass.
fn read_rgba(&self) -> Option<Vec<u8>> {
None
}
/// Called once on the thread that will drive this backend, before its first frame.
///
/// A backend built on the UI thread and then moved to a render thread has to re-establish whatever
/// per-thread state its constructor set up there. The software rasteriser keeps its glyph shaper and
/// shadow caches in a thread-local, so without this the render thread finds an empty slot and builds a
/// default one — with no font config. On desktop that silently falls back to system fonts; on Android
/// there are none to find and cosmic-text aborts the process with "no default font found".
fn bind_to_render_thread(&mut self) {}
/// How long the render thread should go without a frame before calling
/// [`sweep_idle_caches`](Self::sweep_idle_caches). `None` (the default) means never.
///
/// Exists because a backend's caches may be thread-local: they then belong to the render thread, and
/// nothing on the UI thread can reach them — so the sweep has to be driven from the thread that owns
/// them, and only that thread knows when it has been idle.
fn idle_sweep_after(&self) -> Option<std::time::Duration> {
None
}
/// Drops cache entries no frame has asked for within their idle horizon. Called once per idle stretch,
/// on the render thread, after [`idle_sweep_after`](Self::idle_sweep_after) has elapsed with no frame.
fn sweep_idle_caches(&mut self) {}
/// Whether this backend applies `begin_frame`'s `scale_factor` itself — the hardware path folds it into
/// the shader's transform. A backend that returns `false` (the default, and what the software rasteriser
/// does) must be handed commands already scaled into physical pixels, which is why the frame pipeline
/// runs [`ScaleScratch`](crate::ScaleScratch) for it.
fn applies_scale_factor(&self) -> bool {
false
}
}
// Lets an installed renderer travel the frame pipeline, which is generic over `R: RenderBackend + Send` so it can own a concrete backend and hand it back on join.
impl RenderBackend for Box<dyn RenderBackend + Send> {
fn begin_frame(
&mut self,
width: u32,
height: u32,
scale_factor: f32,
generation: u64,
) -> Result<(), RendererError> {
(**self).begin_frame(width, height, scale_factor, generation)
}
fn render_frame(
&mut self,
commands: &[DrawCommand],
clear_color: Option<Color>,
) -> Result<(), RendererError> {
(**self).render_frame(commands, clear_color)
}
fn read_rgba(&self) -> Option<Vec<u8>> {
(**self).read_rgba()
}
fn bind_to_render_thread(&mut self) {
(**self).bind_to_render_thread()
}
fn idle_sweep_after(&self) -> Option<std::time::Duration> {
(**self).idle_sweep_after()
}
fn sweep_idle_caches(&mut self) {
(**self).sweep_idle_caches()
}
fn applies_scale_factor(&self) -> bool {
(**self).applies_scale_factor()
}
}
/// What a renderer is built from, beyond the surface it draws on.
pub struct RendererBuild<'a> {
/// The faces the app's text is shaped with — the same set the layout-time measurer was configured with, since
/// measure and draw have to agree on what a string is as wide as.
pub fonts: &'a FontConfig,
/// Whether the app asked for a transparent surface. A renderer is *built* for one or the other.
pub transparent: bool,
}
/// Builds the renderer for a surface — the seam an out-of-tree frontend installs to draw Telar's frames itself.
///
/// Generic over the window type because that is the platform's business: whoever brings a `Platform` brings the
/// window this draws on. The backend is boxed and `Send` so the frame pipeline can move it to its own thread.
pub trait RendererFactory<W>: 'static {
fn build(
&self,
window: &W,
build: RendererBuild<'_>,
) -> Result<Box<dyn RenderBackend + Send>, RendererError>;
}