jeremyletang / rust-sfml

SFML bindings for Rust
Other
638 stars 88 forks source link

How to properly update a texture assigned to a sprite (E0502 - burrow checker woes)? #317

Closed TeHMoroS closed 1 year ago

TeHMoroS commented 1 year ago

Hello.

I'm having a problem regarding updating textures, that might be Rust specific and not quite tested with SFML bindings, as I haven't found a single example of this being done.

Let's start with the code. Some basic stuff: create a texture, a shape, a pixel buffer, then update the texture and draw the shape:

    // window creation has here - irrelevant in the example

    const TEXTURE_SIZE: usize = 1000;

    let mut display_texture = Texture::new().unwrap();
    if !display_texture.create(TEXTURE_SIZE as u32, TEXTURE_SIZE as u32) {
        println!("Error creating a display texture!");
        return;
    }

    let mut display_sprite = RectangleShape::new();
    display_sprite.set_size(Vector2f::new(window.size().x as f32, window.size().y as f32));
    display_sprite.set_texture(&display_texture, false);

    let screen_ratio = window.size().y as f32 / window.size().x as f32;

    // not doing anything with the buffer yet, not important right now
    let pixel_buffer: [u8; TEXTURE_SIZE * TEXTURE_SIZE * 4] = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];

    loop {
        // event handling was here- irrelevant in the example

        unsafe {
            display_texture.update_from_pixels(
                &pixel_buffer,
                TEXTURE_SIZE as u32,
                (TEXTURE_SIZE as f32 * screen_ratio) as u32,
                0,
                0,
            );
        }

        window.draw(&display_sprite);
        window.display();
    }

The code more or less follows the structure of the Pong example. Any calculation errors are irrelevant at this stage, as it does not compile.

Now, trying to compile it, the borrow checker comes in and start complaining about mutable/immutable borrow combinations:

display_sprite.set_texture(&display_texture, false); - immutable display_texture.update_from_pixels(...) - mutable window.draw(&display_sprite); - immutable (and if not that, then it complains that the Drop trait on the shape might need an immutable borrow)

Basically I cannot update a texture (mutable) that has already been assigned to a shape (immutable). As long as the assignment is made, I cannot do a mutable burrow on the texture.

Now, it was supposed to be more or less a physics sandbox experiment with some heavy pixel manipulation. I figured: "sure, I'll just manipulate the texture content". Things like that were possible in C++, but Rust is being more strict here (it has it's rules, I know), so maybe there's another way of approaching this?

One way I was thinking about was creating independent, one-shot textures, but that goes against SFMLs "use as few textures as possible" rule, so I has trying to reuse the one already created. Advice much appreciated. ;)

crumblingstatue commented 1 year ago

Generally, when using rust-sfml, my advice is to have short lived sprites that you create right before drawing. Creating a Sprite is very cheap, so it shouldn't have a big impact on performance. You can also reuse the same Sprite to draw different textures with set_texture, so a lot of the time you can get away with creating a single sprite right before drawing, and draw everything with it. That being said, @dogunbound has implemented RcTexture and RcSprite, which should allow doing what you intended to do, although I personally don't use these types.

TeHMoroS commented 1 year ago

@crumblingstatue So, I moved the sprite/shape creation code (let mut display_sprite... and the following 2 lines) above the window.draw(...) call. Poof, the problem's gone. Thank you very much for that piece of advice.

Performance-wise, indeed the impact is nowhere to be seen. I have a steady 60FPS with vsync enabled and some crazy numbers in the range of 300-1000 FPS with vsync disabled (it is a simple demo, so...), so this still has a lot of potential for other things.

I'll try to contribute an example of doing pixel manipulation with rust-sfml, so this case is also covered.

TeHMoroS commented 1 year ago

Thank you, problem solved. :)