1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -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:
Lucas Meurer
2026-07-29 18:16:48 +02:00
committed by GitHub
parent d99b665ec0
commit 6268c84d8a
21 changed files with 291 additions and 130 deletions

View File

@@ -28,18 +28,21 @@ pub fn criterion_benchmark(c: &mut Criterion) {
// The most end-to-end benchmark.
c.bench_function("demo_with_tessellate__realistic", |b| {
b.iter(|| {
let full_output = ctx.run_ui(RawInput::default(), |ui| {
let mut full_output = ctx.run_ui(RawInput::default(), |ui| {
demo_windows.ui(ui);
});
ctx.tessellate(full_output.shapes, full_output.pixels_per_point)
ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
});
});
c.bench_function("demo_no_tessellate", |b| {
b.iter(|| {
ctx.run_ui(RawInput::default(), |ui| {
let output = ctx.run_ui(RawInput::default(), |ui| {
demo_windows.ui(ui);
})
});
output.drop_without_applying_deltas();
});
});
@@ -49,6 +52,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("demo_only_tessellate", |b| {
b.iter(|| ctx.tessellate(full_output.shapes.clone(), full_output.pixels_per_point));
});
full_output.drop_without_applying_deltas();
}
if false {
@@ -66,7 +70,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{
let ctx = egui::Context::default();
let _ = ctx.run_ui(RawInput::default(), |ui| {
let output = ctx.run_ui(RawInput::default(), |ui| {
c.bench_function("label &str", |b| {
b.iter_batched_ref(
|| create_benchmark_ui(ui),
@@ -86,11 +90,12 @@ pub fn criterion_benchmark(c: &mut Criterion) {
);
});
});
output.drop_without_applying_deltas();
}
{
let ctx = egui::Context::default();
let _ = ctx.run_ui(RawInput::default(), |ui| {
let output = ctx.run_ui(RawInput::default(), |ui| {
let mut group = c.benchmark_group("button");
// To ensure we have a valid image, let's use the font texture. The size
@@ -134,6 +139,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
);
});
});
output.drop_without_applying_deltas();
}
{

View File

@@ -72,11 +72,12 @@ fn test_egui_e2e() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
let full_output = ctx.run_ui(raw_input.clone(), |ui| {
let mut full_output = ctx.run_ui(raw_input.clone(), |ui| {
demo_windows.ui(ui);
});
let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
assert!(!clipped_primitives.is_empty());
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
}
}
@@ -91,7 +92,7 @@ fn test_egui_zero_window_size() {
const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES {
let full_output = ctx.run_ui(raw_input.clone(), |ui| {
let mut full_output = ctx.run_ui(raw_input.clone(), |ui| {
demo_windows.ui(ui);
});
let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
@@ -100,6 +101,7 @@ fn test_egui_zero_window_size() {
"There should be nothing to show, has at least one primitive with clip_rect: {:?}",
clipped_primitives[0].clip_rect
);
full_output.textures_delta.clear(); // Don't panic on drop with unapplied deltas
}
}