mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -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:
@@ -775,6 +775,7 @@ impl Context {
|
||||
/// ui.label("Hello egui!");
|
||||
/// });
|
||||
/// // handle full_output
|
||||
/// # full_output.drop_without_applying_deltas();
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput {
|
||||
@@ -892,6 +893,7 @@ impl Context {
|
||||
///
|
||||
/// let full_output = ctx.end_pass();
|
||||
/// // handle full_output
|
||||
/// # full_output.drop_without_applying_deltas();
|
||||
/// ```
|
||||
pub fn begin_pass(&self, mut new_input: RawInput) {
|
||||
profiling::function_scope!();
|
||||
@@ -4296,6 +4298,7 @@ mod test {
|
||||
assert_eq!(num_calls, 1);
|
||||
assert_eq!(output.platform_output.num_completed_passes, 1);
|
||||
assert!(!output.platform_output.requested_discard());
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
|
||||
// A single call, with a denied request to discard:
|
||||
@@ -4321,6 +4324,7 @@ mod test {
|
||||
.reason,
|
||||
"test"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4341,6 +4345,7 @@ mod test {
|
||||
assert_eq!(num_calls, 1);
|
||||
assert_eq!(output.platform_output.num_completed_passes, 1);
|
||||
assert!(!output.platform_output.requested_discard());
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
|
||||
// Request discard once:
|
||||
@@ -4363,6 +4368,7 @@ mod test {
|
||||
!output.platform_output.requested_discard(),
|
||||
"The request should have been cleared when fulfilled"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
|
||||
// Request discard twice:
|
||||
@@ -4387,6 +4393,7 @@ mod test {
|
||||
output.platform_output.requested_discard(),
|
||||
"The unfulfilled request should be reported"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4415,6 +4422,7 @@ mod test {
|
||||
!output.platform_output.requested_discard(),
|
||||
"The request should have been cleared when fulfilled"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ pub struct FullOutput {
|
||||
|
||||
/// Texture changes since last frame (including the font texture).
|
||||
///
|
||||
/// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,
|
||||
/// The backend needs to apply [`crate::TexturesDelta::push`] _before_ painting,
|
||||
/// and free any texture in [`crate::TexturesDelta::free`] _after_ painting.
|
||||
///
|
||||
/// It is assumed that all egui viewports share the same painter and texture namespace.
|
||||
@@ -68,6 +68,12 @@ impl FullOutput {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`epaint::textures::TexturesDelta`] will panic when dropped with still unapplied deltas,
|
||||
/// this is a helper to clear the deltas.
|
||||
pub fn drop_without_applying_deltas(mut self) {
|
||||
self.textures_delta.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about text being edited.
|
||||
|
||||
@@ -673,18 +673,20 @@ pub enum WidgetType {
|
||||
pub fn __run_test_ctx(mut run_ui: impl FnMut(&Context)) {
|
||||
let ctx = Context::default();
|
||||
ctx.set_fonts(FontDefinitions::empty()); // prevent fonts from being loaded (save CPU time)
|
||||
let _ = ctx.run_ui(Default::default(), |ui| {
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
run_ui(ui.ctx());
|
||||
});
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
|
||||
/// For use in tests; especially doctests.
|
||||
pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) {
|
||||
let ctx = Context::default();
|
||||
ctx.set_fonts(FontDefinitions::empty()); // prevent fonts from being loaded (save CPU time)
|
||||
let _ = ctx.run_ui(Default::default(), |ui| {
|
||||
let output = ctx.run_ui(Default::default(), |ui| {
|
||||
add_contents(ui);
|
||||
});
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
|
||||
pub fn accesskit_root_id() -> Id {
|
||||
|
||||
@@ -773,7 +773,7 @@ mod tests {
|
||||
.or_default()
|
||||
.selection = Some(test_selection());
|
||||
|
||||
let _ = ctx.run_ui(RawInput::default(), |_| {});
|
||||
let output = ctx.run_ui(RawInput::default(), |_| {});
|
||||
assert!(
|
||||
plugin
|
||||
.lock()
|
||||
@@ -782,11 +782,13 @@ mod tests {
|
||||
.is_some_and(ViewportLabelSelectionState::has_selection),
|
||||
"a pass in another viewport must not clear the child viewport selection"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
|
||||
let _ = ctx.run_ui(child_viewport_input(child_viewport_id), |_| {});
|
||||
let output = ctx.run_ui(child_viewport_input(child_viewport_id), |_| {});
|
||||
assert!(
|
||||
!plugin.lock().has_selection(),
|
||||
"the selection must be cleared when its labels disappear from the same viewport"
|
||||
);
|
||||
output.drop_without_applying_deltas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,8 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use epaint::{Pos2, Vec2};
|
||||
|
||||
use crate::{AsId, Context, Id, Ui};
|
||||
use epaint::{Pos2, Vec2};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user