Search code examples
rustglium

Efficient 2D rendering with Glium


I'm using Glium to do rendering for an emulator I'm writing. I've pieced together something that works (based on this example) but I suspect that it's pretty inefficient. Here's the relevant function:

fn update_screen(display: &Display, screen: &Rc<RefCell<NesScreen>>) {
    let target = display.draw();

    // Write screen buffer
    let borrowed_scr = screen.borrow();
    let mut buf = vec![0_u8; 256 * 240 * 3];
    buf.clone_from_slice(&borrowed_scr.screen_buffer[..]);
    let screen = RawImage2d::from_raw_rgb_reversed(buf, SCREEN_DIMENSIONS);
    glium::Texture2d::new(display, screen)
        .unwrap()
        .as_surface()
        .fill(&target, MagnifySamplerFilter::Nearest);

    target.finish().unwrap();
}

At a high level, this is what I'm doing:

  • Borrow NesScreen which contains the screen buffer, which is an array.
  • Clone the screen buffer into a vector
  • Create a texture from the vector data and render it

My suspicion is that cloning the entire screen buffer via clone_from_slice is really inefficient. The RawImage2d::from_raw_rgb_reversed function takes ownership of the vector passed into it, so I'm not sure how to do this in a way that avoids the clone.

So, two questions:

  • Is this actually inefficient? I don't have enough experience rendering stuff to know intuitively.

  • If so, is there a more efficient way to do this? I've scoured Glium quite a bit but there isn't much specific to 2D rendering.


Solution

  • This won't be a very good answer, but maybe a few things here could help you.


    First of all: is this really inefficient? That's really hard to say, especially the OpenGL part, as OpenGL performance depends a lot on when synchronization is required/requested.

    As for the cloning of the screen buffer: you are merely copying 180kb, which is not too much. I quickly benchmarked it on my machine and cloning a 180kb vector takes around 5µs, which is really not a lot.

    Note that you can create a RawImage2d without using a method, because all fields are public. This means that you can avoid the simple 5µs clone if you create a reversed vector yourself. However, reversing the vector with the method glium uses is a lot slower than just cloning the vector; on my machine it takes 170µs for a vector of the same length. This is probably still tolerable if you just want to achieve 60fps = 17ms per frame, but still not very nice.

    You could think about using the correct row ordering in your original array to avoid this problem. OR you could, instead of directly copying the texture to the framebuffer, just draw a fullscreen quad (one vertex for each screen corner) with the texture on it. Sure, then you need a mesh, a shader and all that stuff, but you could just "reverse" the image by tweaking the texture coordinates.

    Lastly, I unfortunately don't know a lot about the time the GPU takes to execute the OpenGL commands. I'd guess that it's not optimal because OpenGL doesn't have a lot of room to schedule your commands, but has to execute them right away (forced synchronization). But maybe that's not avoidable in your case.