mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Prevent accidentally dropping TexturesDelta (#8356)
We had a ton of issues around `TexturesDelta` that weren't properly applied because we early-out of some function: * https://github.com/emilk/egui/pull/8313 * https://github.com/emilk/egui/pull/8250 * https://github.com/emilk/egui/pull/8279 This PR changes texture updates, so that we always store them after taking them out of `FullOutput` and keep the delta around until it's actually applied (by passing &mut refs and draining instead of iterating). So even if we add a new early return somewhere, that can't break texture updates. It also optimizes `TexturesDelta::append` by dropping any previous deltas if there's a new `whole` delta or a `free`. It also adds a debug assert that any `TexturesDelta` is empty when dropped, as an additional safeguard in case the bug sneaks back in. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -324,7 +324,6 @@ impl AppRunner {
|
||||
|
||||
/// Paint the results of the last call to [`Self::logic`].
|
||||
pub fn paint(&mut self) {
|
||||
let textures_delta = std::mem::take(&mut self.textures_delta);
|
||||
let clipped_primitives = std::mem::take(&mut self.clipped_primitives);
|
||||
|
||||
if let Some(clipped_primitives) = clipped_primitives {
|
||||
@@ -347,7 +346,7 @@ impl AppRunner {
|
||||
self.app.clear_color(&self.egui_ctx.global_style().visuals),
|
||||
&clipped_primitives,
|
||||
self.egui_ctx.pixels_per_point(),
|
||||
&textures_delta,
|
||||
&mut self.textures_delta,
|
||||
screenshot_commands,
|
||||
) {
|
||||
log::error!("Failed to paint: {}", super::string_from_js_value(&err));
|
||||
|
||||
@@ -24,7 +24,7 @@ pub(crate) trait WebPainter {
|
||||
clear_color: [f32; 4],
|
||||
clipped_primitives: &[egui::ClippedPrimitive],
|
||||
pixels_per_point: f32,
|
||||
textures_delta: &egui::TexturesDelta,
|
||||
textures_delta: &mut egui::TexturesDelta,
|
||||
capture: Vec<UserData>,
|
||||
) -> Result<(), JsValue>;
|
||||
|
||||
|
||||
@@ -61,13 +61,16 @@ impl WebPainter for WebPainterGlow {
|
||||
clear_color: [f32; 4],
|
||||
clipped_primitives: &[egui::ClippedPrimitive],
|
||||
pixels_per_point: f32,
|
||||
textures_delta: &egui::TexturesDelta,
|
||||
textures_delta: &mut egui::TexturesDelta,
|
||||
capture: Vec<UserData>,
|
||||
) -> Result<(), JsValue> {
|
||||
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
|
||||
|
||||
for (id, image_delta) in &textures_delta.set {
|
||||
self.painter.set_texture(*id, image_delta);
|
||||
#[expect(clippy::iter_over_hash_type)] // Order doesn't matter here
|
||||
for (id, image_deltas) in textures_delta.set.drain() {
|
||||
for image_delta in image_deltas {
|
||||
self.painter.set_texture(id, &image_delta);
|
||||
}
|
||||
}
|
||||
|
||||
egui_glow::painter::clear(self.painter.gl(), canvas_dimension, clear_color);
|
||||
@@ -79,7 +82,8 @@ impl WebPainter for WebPainterGlow {
|
||||
self.screenshots.push((image, capture));
|
||||
}
|
||||
|
||||
for &id in &textures_delta.free {
|
||||
#[expect(clippy::iter_over_hash_type)] // Order doesn't matter here
|
||||
for id in textures_delta.free.drain() {
|
||||
self.painter.free_texture(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ impl WebPainter for WebPainterWgpu {
|
||||
clear_color: [f32; 4],
|
||||
clipped_primitives: &[egui::ClippedPrimitive],
|
||||
pixels_per_point: f32,
|
||||
textures_delta: &egui::TexturesDelta,
|
||||
textures_delta: &mut egui::TexturesDelta,
|
||||
capture_data: Vec<UserData>,
|
||||
) -> Result<(), JsValue> {
|
||||
let capture = !capture_data.is_empty();
|
||||
@@ -210,13 +210,16 @@ impl WebPainter for WebPainterWgpu {
|
||||
|
||||
let user_cmd_bufs = {
|
||||
let mut renderer = render_state.renderer.write();
|
||||
for (id, image_delta) in &textures_delta.set {
|
||||
renderer.update_texture(
|
||||
&render_state.device,
|
||||
&render_state.queue,
|
||||
*id,
|
||||
image_delta,
|
||||
);
|
||||
#[expect(clippy::iter_over_hash_type)] // Order doesn't matter here
|
||||
for (id, image_deltas) in textures_delta.set.drain() {
|
||||
for image_delta in image_deltas {
|
||||
renderer.update_texture(
|
||||
&render_state.device,
|
||||
&render_state.queue,
|
||||
id,
|
||||
&image_delta,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.update_buffers(
|
||||
@@ -388,8 +391,9 @@ impl WebPainter for WebPainterWgpu {
|
||||
// However, once we called `wgpu::Queue::submit`, it is up for wgpu to determine how long the underlying gpu resource has to live.
|
||||
{
|
||||
let mut renderer = render_state.renderer.write();
|
||||
for id in &textures_delta.free {
|
||||
renderer.free_texture(id);
|
||||
#[expect(clippy::iter_over_hash_type)] // Order doesn't matter here
|
||||
for id in textures_delta.free.drain() {
|
||||
renderer.free_texture(&id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user