mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -04:00
egui-wgpu: let paint callbacks read the backdrop
Adds `CallbackTrait::needs_backdrop`, so an effect can transform what egui has already drawn rather than draw over it. Background blur behind a window or a panel is the motivating case. Nothing can sample the texture it is currently drawing into, so this needs the render pass interrupted and the half-drawn frame copied aside. The renderer does not own its pass, so the integration has to drive that: `render_from` draws until it reaches a callback that wants a backdrop and stops, and `capture_backdrop` copies the frame and lets the callback run its own passes over it. `render` still works as before for integrations that do not care; callbacks that asked for a backdrop just get a plain `paint`. The surface texture cannot be copied from on every platform, which is why `CaptureState` exists for screenshots. `Painter` now renders into that texture whenever a backdrop is needed too, and blits it onto the surface afterwards, so `copy_textures` is split into `copy_to_buffer` and `blit_to_surface`. `egui_kittest`'s wgpu renderer drives the same loop, so backdrop effects can be snapshot-tested. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -208,24 +208,64 @@ impl crate::TestRenderer for WgpuTestRenderer {
|
||||
|
||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
{
|
||||
let mut pass = encoder
|
||||
.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Egui Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
..Default::default()
|
||||
})
|
||||
.forget_lifetime();
|
||||
// A paint callback may need to see what egui has already drawn, e.g. to blur the
|
||||
// background behind a panel. Nothing can sample the texture it is drawing into, so
|
||||
// that needs the render pass interrupted and the half-drawn frame copied aside.
|
||||
let backdrop_texture = egui_wgpu::Renderer::needs_backdrop(&tessellated).then(|| {
|
||||
egui_wgpu::BackdropTexture::new(
|
||||
&self.render_state.device,
|
||||
screen.size_in_pixels,
|
||||
self.render_state.target_format,
|
||||
)
|
||||
});
|
||||
let mut cursor = egui_wgpu::RenderCursor::default();
|
||||
let mut backdrop = None;
|
||||
let mut load = wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT);
|
||||
|
||||
renderer.render(&mut pass, &tessellated, &screen);
|
||||
loop {
|
||||
let progress = {
|
||||
let mut pass = encoder
|
||||
.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Egui Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
..Default::default()
|
||||
})
|
||||
.forget_lifetime();
|
||||
|
||||
renderer.render_from(
|
||||
&mut pass,
|
||||
&tessellated,
|
||||
&screen,
|
||||
&mut cursor,
|
||||
backdrop.as_ref(),
|
||||
)
|
||||
};
|
||||
|
||||
if progress == egui_wgpu::RenderProgress::Done {
|
||||
break;
|
||||
}
|
||||
|
||||
let Some(backdrop_texture) = backdrop_texture.as_ref() else {
|
||||
break;
|
||||
};
|
||||
backdrop = renderer.capture_backdrop(
|
||||
&self.render_state.device,
|
||||
&self.render_state.queue,
|
||||
&mut encoder,
|
||||
&tessellated,
|
||||
cursor,
|
||||
&texture,
|
||||
backdrop_texture,
|
||||
);
|
||||
load = wgpu::LoadOp::Load;
|
||||
}
|
||||
|
||||
self.render_state
|
||||
|
||||
Reference in New Issue
Block a user