rust-windowing/glutin

memory usage keep increasing when resizing window

zhcy2050 opened this issue · 10 comments

the window example,memory usage keep increasing when resizing window

I am on windows 10.

Video memory usage or some other memory? glutin doesn't store anything so it's likely a winit issue.

I'm not sure which memory usage is increasing, my pc has integrated gpu which use shared memory. (The performance page shows gpu shared memory is increased).
I also tried the basic winit example,resize without any issue,the memory usage is keep around 5M.
The glutin example run with cargo run --example window,as for compare,when keep resizing,memory usage is grow fast to more than 1GB.

That sounds like a video driver bug and has nothing to do with glutin. There's no code in glutin that is being running on resize, there're no resources hold by glutin, so from what I can see there're other libraries with similar issues on windows. Try updating video driver or something like that. It's likely the buffers are not being released.

Note, if your system has EGL, I'd suggest to try using it.

@kchibisov I am seeing this same issue and as best I can tell it is something in glutin or glutin-winit that is causing the behavior.

Some quick notes:

  1. I am unable to reproduce this behavior with winit alone.
  2. I am seeing the same issue with WGL and EGL.
  3. I am seeing the same on debug and release builds.

Reproduceable Example:

use std::num::NonZeroU32;

use glow::HasContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;

use raw_window_handle::HasRawWindowHandle;

use glutin::config::ConfigTemplateBuilder;
use glutin::context::{ContextApi, ContextAttributesBuilder, Version};
use glutin::display::GetGlDisplay;
use glutin::prelude::*;
use glutin::surface::SwapInterval;

use glutin_winit::{self, ApiPrefence, DisplayBuilder, GlWindow};

pub fn main() {
    let event_loop = EventLoop::new();

    let window_builder = Some(WindowBuilder::new());

    let template = ConfigTemplateBuilder::new();

    let display_builder = DisplayBuilder::new()
        .with_window_builder(window_builder)
        .with_preference(ApiPrefence::PreferEgl);

    let (mut window, gl_config) = display_builder
        .build(&event_loop, template, |configs| {
            configs
                .reduce(|accum, config| {
                    if config.num_samples() > accum.num_samples() && config.hardware_accelerated() {
                        config
                    } else {
                        accum
                    }
                })
                .unwrap()
        })
        .unwrap();

    let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());

    let gl_display = gl_config.display();

    let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);

    let fallback_context_attributes = ContextAttributesBuilder::new()
        .with_context_api(ContextApi::Gles(None))
        .build(raw_window_handle);

    let legacy_context_attributes = ContextAttributesBuilder::new()
        .with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
        .build(raw_window_handle);

    let mut not_current_gl_context = Some(unsafe {
        gl_display
            .create_context(&gl_config, &context_attributes)
            .unwrap_or_else(|_| {
                gl_display
                    .create_context(&gl_config, &fallback_context_attributes)
                    .unwrap_or_else(|_| {
                        gl_display
                            .create_context(&gl_config, &legacy_context_attributes)
                            .expect("failed to create context")
                    })
            })
    });

    let mut state = None;
    event_loop.run(move |event, window_target, control_flow| {
        control_flow.set_wait();
        match event {
            Event::Resumed => {
                let window = window.take().unwrap_or_else(|| {
                    let window_builder = WindowBuilder::new().with_transparent(true);
                    glutin_winit::finalize_window(window_target, window_builder, &gl_config)
                        .unwrap()
                });

                let attrs = window.build_surface_attributes(<_>::default());
                let gl_surface = unsafe {
                    gl_config
                        .display()
                        .create_window_surface(&gl_config, &attrs)
                        .unwrap()
                };

                let gl_context = not_current_gl_context
                    .take()
                    .unwrap()
                    .make_current(&gl_surface)
                    .unwrap();

                let gl = unsafe {
                    glow::Context::from_loader_function_cstr(|symbol| {
                        gl_config.display().get_proc_address(symbol)
                    })
                };

                if let Err(res) = gl_surface
                    .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
                {
                    eprintln!("Error setting vsync: {res:?}");
                }

                assert!(state
                    .replace((gl, gl_context, gl_surface, window))
                    .is_none());
            }
            Event::Suspended => {}
            Event::WindowEvent { event, .. } => match event {
                WindowEvent::CloseRequested => {
                    control_flow.set_exit();
                }
                _ => (),
            },
            Event::RedrawEventsCleared => {
                if let Some((gl, gl_context, gl_surface, window)) = &state {
                    window.request_redraw();
                    unsafe {
                        gl.clear_color(0.1, 0.1, 0.1, 0.9);
                        gl.clear(glow::COLOR_BUFFER_BIT);
                    }
                    gl_surface.swap_buffers(gl_context).unwrap();
                }
            }
            _ => (),
        }
    })
}

cargo.toml:

[package]
name = "glutin-test"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
winit = "0.28.2"
glutin = "0.30.6"
glutin-winit = "0.3.0"
glow = "0.12.1"
raw-window-handle = "0.5.0"

glutin doesn't have dynamic memory, growable state, or anything like that at all. On windows it even no-ops the resizes. What I think is happening is that your video driver is holding back buffers or doing some weird shit.

You're surely free to profile the glutin's memory management, but it simply doesn't allocate anything during runtime(only when you create a window).

Maybe some call is required on Windows, but I can't repro with EGL on Wayland/X11 and you know, it's EGL, there's zero platform specific code into that.

I'd suggest to run under memory profiler and figure out where the memory lays, but I'm sure it'll outside of glutin, because it doesn't touch dynamic memory after you've created the context/window.

@kchibisov Alright, I'll try to profile and see if I can figure it out. I will report back.

@kchibisov Alright, I did some profiling and can see that the memory allocations are done by ig9icd64.dll which is part of the Intel UHD Graphics driver. I update to the latest driver on my machine and the issue still exists. But either way, it appears that you are correct that something is going on in the video driver itself.

I mean you can clearly check that in glutin code for api, there're no dynamic memory usage at all other than a few vecs when building something and then dropping them right away :p