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

Drawing from a render target on master causes a memory leak #886

Open
JaniM opened this issue Jan 18, 2025 · 7 comments
Open

Drawing from a render target on master causes a memory leak #886

JaniM opened this issue Jan 18, 2025 · 7 comments

Comments

@JaniM
Copy link

JaniM commented Jan 18, 2025

The following code snippet leaks memory on each iteration, very quickly exhausting all system memory.

Tested on MacOS on 0.4.13 and master. This does NOT happen on 0.4.13, ie. this is a regression.

use macroquad::prelude::*;

const VIRTUAL_WIDTH: f32 = 1280.0;
const VIRTUAL_HEIGHT: f32 = 720.0;

#[macroquad::main("Letterbox")]
async fn main() {
    loop {
        let render_target = render_target(VIRTUAL_WIDTH as u32, VIRTUAL_HEIGHT as u32);

        let mut render_target_cam =
            Camera2D::from_display_rect(Rect::new(0., 0., VIRTUAL_WIDTH, VIRTUAL_HEIGHT));
        render_target_cam.render_target = Some(render_target.clone());

        // Deleting the draw removes the memory leak
        draw_texture_ex(
            &render_target.texture,
            0.0,
            0.0,
            WHITE,
            DrawTextureParams {
                dest_size: Some(vec2(VIRTUAL_WIDTH, VIRTUAL_HEIGHT)),
                ..Default::default()
            },
        );

        next_frame().await;
    }
}
@KurlykovDanila
Copy link
Contributor

Are you sure the problem is in the method draw_texture_ex?
I removed it and the memory leak remained. The leak is clearly in this block of code, because if you take it out of the loop, the problem will disappear.

let render_target = render_target(VIRTUAL_WIDTH as u32, VIRTUAL_HEIGHT as u32);
let mut render_target_cam = Camera2D::from_display_rect(Rect::new(0., 0., VIRTUAL_WIDTH, VIRTUAL_HEIGHT));
render_target_cam.render_target = Some(render_target.clone());

This code has no problems:

use macroquad::prelude::*;

const VIRTUAL_WIDTH: f32 = 1280.0;
const VIRTUAL_HEIGHT: f32 = 720.0;

#[macroquad::main("Letterbox")]
async fn main() {
    let render_target = render_target(VIRTUAL_WIDTH as u32, VIRTUAL_HEIGHT as u32);
    let mut render_target_cam = Camera2D::from_display_rect(Rect::new(0., 0., VIRTUAL_WIDTH, VIRTUAL_HEIGHT));
    render_target_cam.render_target = Some(render_target.clone());

    loop {
        draw_texture_ex(
            &render_target.texture,
            0.0,
            0.0,
            WHITE,
            DrawTextureParams {
                dest_size: Some(vec2(VIRTUAL_WIDTH, VIRTUAL_HEIGHT)),
                ..Default::default()
            },
        );

        next_frame().await;
    }
}

@JaniM
Copy link
Author

JaniM commented Jan 20, 2025

I am sure, on my machine it does not leak without the draw. Ie. something about drawing the texture prevents the texture from being dropped. The reason your snippet doesn't exhibit a leak is that we never drop and create a new texture.

@KurlykovDanila
Copy link
Contributor

Interesting, because on my machine this function causes a memory leak:

use macroquad::prelude::*;

#[macroquad::main("Letterbox")]
async fn main() {
    loop {
        let render_target = render_target(1280, 720);

        next_frame().await;
    }
}

@JaniM
Copy link
Author

JaniM commented Jan 20, 2025

I just noticed I forgot to remove the camera from the reproduction, it is irrelevant.

Interesting, because on my machine this function causes a memory leak:

Oh huh, this does leak for me too, but very slowly. With the draw (below), it blows a GB in a couple seconds. Without, it takes several minutes.

use macroquad::prelude::*;

#[macroquad::main("Letterbox")]
async fn main() {
    loop {
        let render_target = render_target(1280, 720);

        draw_texture_ex(&render_target.texture, 0.0, 0.0, WHITE, Default::default());

        next_frame().await;
    }
}

@JaniM
Copy link
Author

JaniM commented Jan 20, 2025

Hmm. It seems to be a difference in virtual and real memory. Both versions grow in real memory at the same rate, but the draw version blows through virtual memory.

With draw:

Image

Without draw:

Image

@KurlykovDanila
Copy link
Contributor

https://github.com/not-fl3/miniquad/blob/8b8dc6225693ef8c461a0cd44724e33baa9d9fe0/src/graphics/gl.rs#L904
I found that this line adds textures and on my machine this vector only grows and does not decrease. That is why there is a leak. I am trying to figure out the difference with the 0.4.13 version to understand how it freed them.

@KurlykovDanila
Copy link
Contributor

https://github.com/not-fl3/miniquad/blob/8b8dc6225693ef8c461a0cd44724e33baa9d9fe0/src/graphics/gl.rs#L904 I found that this line adds textures and on my machine this vector only grows and does not decrease. That is why there is a leak. I am trying to figure out the difference with the 0.4.13 version to understand how it freed them.

Most likely, this is not the problem, because this vector is never cleared. (In any version, ever)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants