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
118
119
120
121
122
123
124
125
126
127
128
129
use anyhow::Result;
use super::Renderer;
impl Renderer {
/// Render egui overlay on top of the terminal
pub(crate) fn render_egui(
&mut self,
surface_texture: &wgpu::SurfaceTexture,
egui_output: egui::FullOutput,
egui_ctx: &egui::Context,
force_opaque: bool,
) -> Result<()> {
use wgpu::TextureViewDescriptor;
// Create view of the surface texture
let view = surface_texture
.texture
.create_view(&TextureViewDescriptor::default());
// Create command encoder for egui
let mut encoder =
self.cell_renderer
.device()
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("egui encoder"),
});
// Convert egui output to screen descriptor
let screen_descriptor = egui_wgpu::ScreenDescriptor {
size_in_pixels: [self.size.width, self.size.height],
pixels_per_point: egui_output.pixels_per_point,
};
// Update egui textures
for (id, image_delta) in &egui_output.textures_delta.set {
// egui 0.34 can deliver a partial font-atlas patch before this
// renderer has allocated the font texture — e.g. when an earlier
// frame ran egui but skipped the GPU render, dropping the full
// (pos = None) font upload. egui-wgpu panics on a partial update of
// an unallocated texture (emilk/egui#8228), so pre-allocate it with
// the complete current font atlas before applying the patch.
if image_delta.pos.is_some() && self.egui_renderer.texture(id).is_none() {
let full_image = egui_ctx
.fonts(|f| egui::epaint::ImageData::Color(std::sync::Arc::new(f.image())));
self.egui_renderer.update_texture(
self.cell_renderer.device(),
self.cell_renderer.queue(),
*id,
&egui::epaint::ImageDelta {
pos: None,
image: full_image,
options: image_delta.options,
},
);
}
self.egui_renderer.update_texture(
self.cell_renderer.device(),
self.cell_renderer.queue(),
*id,
image_delta,
);
}
// Tessellate egui shapes into paint jobs
let mut paint_jobs = egui_ctx.tessellate(egui_output.shapes, egui_output.pixels_per_point);
// If requested, force all egui vertices to full opacity so UI stays solid
if force_opaque {
for job in paint_jobs.iter_mut() {
match &mut job.primitive {
egui::epaint::Primitive::Mesh(mesh) => {
for v in mesh.vertices.iter_mut() {
v.color[3] = 255;
}
}
egui::epaint::Primitive::Callback(_) => {}
}
}
}
// Update egui buffers
self.egui_renderer.update_buffers(
self.cell_renderer.device(),
self.cell_renderer.queue(),
&mut encoder,
&paint_jobs,
&screen_descriptor,
);
// Render egui on top of the terminal content
{
let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("egui render pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, // Don't clear - render on top of terminal
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// Convert to 'static lifetime as required by egui_renderer.render()
let mut render_pass = render_pass.forget_lifetime();
self.egui_renderer
.render(&mut render_pass, &paint_jobs, &screen_descriptor);
} // render_pass dropped here
// Submit egui commands
self.cell_renderer
.queue()
.submit(std::iter::once(encoder.finish()));
// Free egui textures
for id in &egui_output.textures_delta.free {
self.egui_renderer.free_texture(id);
}
Ok(())
}
}