Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak when window is minimized #2659

Open
4 tasks done
daniel-prause opened this issue Nov 4, 2024 · 0 comments
Open
4 tasks done

Memory leak when window is minimized #2659

daniel-prause opened this issue Nov 4, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@daniel-prause
Copy link

Is your issue REALLY a bug?

  • My issue is indeed a bug!
  • I am not crazy! I will not fill out this form just to ask a question or request a feature. Pinky promise.

Is there an existing issue for this?

  • I have searched the existing issues.

Is this issue related to iced?

  • My hardware is compatible and my graphics drivers are up-to-date.

What happened?

Hi there!

When I modify e.g. the ferris example like that:

use iced::time;
use iced::time::Instant;
use iced::widget::{
    center, checkbox, column, container, image, pick_list, row, slider, text,
};
use iced::window;
use iced::{
    Bottom, Center, Color, ContentFit, Degrees, Element, Fill, Radians,
    Rotation, Subscription, Theme,
};
pub fn main() -> iced::Result {
    iced::application("Ferris - Iced", Image::update, Image::view)
        .subscription(Image::subscription)
        .theme(|_| Theme::TokyoNight)
        .run()
}

struct Image {
    width: f32,
    opacity: f32,
    rotation: Rotation,
    content_fit: ContentFit,
    spin: bool,
    last_tick: Instant,
}

#[derive(Debug, Clone, Copy)]
enum Message {
    WidthChanged(f32),
    OpacityChanged(f32),
    RotationStrategyChanged(RotationStrategy),
    RotationChanged(Degrees),
    ContentFitChanged(ContentFit),
    SpinToggled(bool),
    RedrawRequested(Instant),
}

impl Image {
    fn update(&mut self, message: Message) {
        match message {
            Message::WidthChanged(width) => {
                self.width = width;
            }
            Message::OpacityChanged(opacity) => {
                self.opacity = opacity;
            }
            Message::RotationStrategyChanged(strategy) => {
                self.rotation = match strategy {
                    RotationStrategy::Floating => {
                        Rotation::Floating(self.rotation.radians())
                    }
                    RotationStrategy::Solid => {
                        Rotation::Solid(self.rotation.radians())
                    }
                };
            }
            Message::RotationChanged(rotation) => {
                self.rotation = match self.rotation {
                    Rotation::Floating(_) => {
                        Rotation::Floating(rotation.into())
                    }
                    Rotation::Solid(_) => Rotation::Solid(rotation.into()),
                };
            }
            Message::ContentFitChanged(content_fit) => {
                self.content_fit = content_fit;
            }
            Message::SpinToggled(spin) => {
                self.spin = spin;
                self.last_tick = Instant::now();
            }
            Message::RedrawRequested(now) => {
                const ROTATION_SPEED: Degrees = Degrees(360.0);

                let delta = (now - self.last_tick).as_millis() as f32 / 1_000.0;

                *self.rotation.radians_mut() = (self.rotation.radians()
                    + ROTATION_SPEED * delta)
                    % (2.0 * Radians::PI);

                self.last_tick = now;
            }
        }
    }

    fn subscription(&self) -> Subscription<Message> {
        if self.spin {
            window::frames().map(Message::RedrawRequested)
        } else {
            time::every(time::Duration::from_millis(500))
                .map(|_| Message::RedrawRequested(Instant::now()))
        }
    }

    fn view(&self) -> Element<Message> {
        let image: iced::widget::Image<iced::widget::image::Handle> =
            iced::widget::Image::new(image::Handle::from_rgba(
                256,
                64,
                vec![128; 256 * 64 * 4],
            ));
        let i_am_ferris = column!["Hello!", image, "I am Ferris!"]
            .spacing(20)
            .align_x(Center);

        let fit = row![
            pick_list(
                [
                    ContentFit::Contain,
                    ContentFit::Cover,
                    ContentFit::Fill,
                    ContentFit::None,
                    ContentFit::ScaleDown
                ],
                Some(self.content_fit),
                Message::ContentFitChanged
            )
            .width(Fill),
            pick_list(
                [RotationStrategy::Floating, RotationStrategy::Solid],
                Some(match self.rotation {
                    Rotation::Floating(_) => RotationStrategy::Floating,
                    Rotation::Solid(_) => RotationStrategy::Solid,
                }),
                Message::RotationStrategyChanged,
            )
            .width(Fill),
        ]
        .spacing(10)
        .align_y(Bottom);

        let properties = row![
            with_value(
                slider(100.0..=500.0, self.width, Message::WidthChanged),
                format!("Width: {}px", self.width)
            ),
            with_value(
                slider(0.0..=1.0, self.opacity, Message::OpacityChanged)
                    .step(0.01),
                format!("Opacity: {:.2}", self.opacity)
            ),
            with_value(
                row![
                    slider(
                        Degrees::RANGE,
                        self.rotation.degrees(),
                        Message::RotationChanged
                    ),
                    checkbox("Spin!", self.spin)
                        .text_size(12)
                        .on_toggle(Message::SpinToggled)
                        .size(12)
                ]
                .spacing(10)
                .align_y(Center),
                format!("Rotation: {:.0}°", f32::from(self.rotation.degrees()))
            )
        ]
        .spacing(10)
        .align_y(Bottom);

        container(column![fit, center(i_am_ferris), properties].spacing(10))
            .padding(10)
            .into()
    }
}

impl Default for Image {
    fn default() -> Self {
        Self {
            width: 300.0,
            opacity: 1.0,
            rotation: Rotation::default(),
            content_fit: ContentFit::default(),
            spin: false,
            last_tick: Instant::now(),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum RotationStrategy {
    Floating,
    Solid,
}

impl std::fmt::Display for RotationStrategy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::Floating => "Floating",
            Self::Solid => "Solid",
        })
    }
}

fn with_value<'a>(
    control: impl Into<Element<'a, Message>>,
    value: String,
) -> Element<'a, Message> {
    column![control.into(), text(value).size(12).line_height(1.0)]
        .spacing(2)
        .align_x(Center)
        .into()
}

as soon as I minimize the window, the 256*64 image that is generated on the fly creates a memory leak.
If the image is not added to the column, no memory leak occurs.
I suspect that as soon a the window is minimized, the old images seem to be "cached" or not freed anymore.
I added chrono to the toml file as well as a dependency for the tick subscription.

What is the expected behavior?

No memory leak to occur. I cross checked it with version 0.12.1 and there was no memory leak.

Version

crates.io release

Operating System

Windows

Do you have any log output?

No response

@daniel-prause daniel-prause added the bug Something isn't working label Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant