john01dav/softbuffer

As the popularity of the library minifb shows, it is useful to put a 2D...

As the popularity of the library winit), and minifb's implementation of window management is not ideal

Overview

As the popularity of the library minifb shows, it is useful to put a 2D buffer/image on a window in a platform-independent way. Minifb's approach to doing window management itself, however, is problematic code duplication. We already have very high quality libraries for this in the Rust ecosystem (such as winit), and minifb's implementation of window management is not ideal. For example, it occasionally segfaults on some platforms and is missing key features such as the ability to set a window icon. While it would be possible to add these features to minifb, it makes more sense to instead use the standard window handling systems.

Softbuffer integrates with the raw-window-handle crate to allow writing to a window in a cross-platform way while using the very high quality dedicated window management libraries that are available in the Rust ecosystem.

What about pixels? Pixels accomplishes a very similar goal to softbuffer, however there are two key differences. Pixels provides some capacity for GPU-accelerated post-processing of what is displayed, while Softbuffer does not. Due to not having this post-processing, Softbuffer does not rely on the GPU or hardware accelerated graphics stack in any way, and is thus more portable to installations that do not have access to hardware acceleration (e.g. VMs, older computers, computers with misconfigured drivers). Softbuffer should be used over pixels when its GPU-accelerated post-processing effects are not needed.

License & Credits

This library is dual-licensed under MIT or Apache-2.0, just like minifb and rust. Significant portions of code were taken from the minifb library to do platform-specific work.

Platform support:

Some, but not all, platforms supported in raw-window-handle are supported by Softbuffer. Pull requests are welcome to add new platforms! Nonetheless, all major desktop platforms that winit uses on desktop are supported.

For now, the priority for new platforms is:

  1. to have at least one platform on each OS working (e.g. one of Win32 or WinRT, or one of Xlib, Xcb, and Wayland) and
  2. for that one platform on each OS to be the one that winit uses.

(PRs will be accepted for any platform, even if it does not follow the above priority.)

✅: Present | ❌: Absent

  • AndroidNdk ❌
  • AppKit ✅ (Thanks to Seo Sanghyeon and lunixbochs!)
  • Orbital ❌
  • UiKit ❌
  • Wayland ✅ (Wayland support in winit is immature at the moment, so it might be wise to force X11 if you're using winit)
  • Web ✅ (Thanks to Liamolucko!)
  • Win32 ✅
  • WinRt ❌
  • Xcb ❌
  • Xlib ✅

Example

use softbuffer::GraphicsContext;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let mut graphics_context = unsafe { GraphicsContext::new(window) }.unwrap();

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;

        match event {
            Event::RedrawRequested(window_id) if window_id == graphics_context.window().id() => {
                let (width, height) = {
                    let size = graphics_context.window().inner_size();
                    (size.width, size.height)
                };
                let buffer = (0..((width * height) as usize))
                    .map(|index| {
                        let y = index / (width as usize);
                        let x = index % (width as usize);
                        let red = x % 255;
                        let green = y % 255;
                        let blue = (x * y) % 255;

                        let color = blue | (green << 8) | (red << 16);

                        color as u32
                    })
                    .collect::<Vec<_>>();

                graphics_context.set_buffer(&buffer, width as u16, height as u16);
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                window_id,
            } if window_id == graphics_context.window().id() => {
                *control_flow = ControlFlow::Exit;
            }
            _ => {}
        }
    });
}

Changelog

See git tags for associated commits.

0.1.1

  • Added WASM support (Thanks to Liamolucko!)
  • CALayer is now used for Mac OS backend, which is more flexible about what happens in the windowing library (Thanks to lunixbochs!)

0.1.0

Initial published version with support for Linux (X11 and Wayland), Mac OS (but buggy), and WIndows.

Issues

Collection of the latest Issues

i509VCB

i509VCB

Comment Icon0

It would be easier for organisation and contribution purposes to have issue labels for platforms

LoganDark

LoganDark

Comment Icon0

On Windows 10, set_buffer can take upwards of 15-20 ms, which is noticeably slow.

Perhaps a no-copy API could be introduced to mitigate this.

LoganDark

LoganDark

Comment Icon0

Right now, softbuffer seems to completely ignore the alpha channel. It would be nice if this was supported.

LoganDark

LoganDark

Comment Icon3

It takes a slice. Is this slice immediately cloned, or does the window keep a reference to it? Should I double-buffer, or can I clobber the buffer after the call to set_buffer?

Normally the fact that the slice doesn't stay borrowed would be a hint, but as this is a relatively new library using unsafe code, it's possible that it could be accidentally unsound, which is why documenting this would be helpful.

i509VCB

i509VCB

Comment Icon0

A few things I noticed: The WlShm and WlShmPool protocol objects are leaked when dropping a GraphicsContext

The client could modify pixels while the compositor is using the buffer. This is not allowed per wl_surface protocol.

Committing a pending wl_buffer allows the compositor to read the pixels in the wl_buffer. The compositor may access the pixels at any time after the wl_surface.commit request. When the compositor will not access the pixels anymore, it will send the wl_buffer.release event. Only after receiving wl_buffer.release, the client may reuse the wl_buffer. A wl_buffer that has been attached and then replaced by another attach instead of committed will not receive a release event, and is not used by the compositor.

In order to handle that safely, setting the buffer while the compositor is using the previous buffer either needs to tell the user to wait, needs to block or use double buffering.

Also because of this requirement, dropping a GraphicsContext implies we must block until the compositor to releases the buffer.

i509VCB

i509VCB

Comment Icon1

Many compositors implement damage tracking, meaning that commiting a new buffer when calling set_buffer does nothing.

A proper fix would involve the caller giving the implementation a set of rectangles where content has changed.

lunixbochs

lunixbochs

Comment Icon3

Here's an example usage of XShm (originally from Fabrice Bellard's TinyGL): https://github.com/lunixbochs/tinygles/blob/unstable/src/gles/glx.c#L107

Basically, this allows you to blit pixels directly into X11's memory, saving both a memory copy and the general overhead of passing your image through an X11 socket.

In the non-shm case, you also shouldn't need to create a new XImage every frame as you're doing now. You only really need to do it on startup and when the window is resized. You can just call XPutImage on an existing XImage repeatedly, as done here: https://github.com/lunixbochs/tinygles/blob/unstable/src/gles/glx.c#L368

Information - Updated Jul 24, 2022

Stars: 31
Forks: 7
Issues: 18

Coloring terminal output

term-painter is a cross-platform library for coloring UI terminals

Coloring terminal output

Cross-platform library for system information fetching

Examples can be found Async-first

Cross-platform library for system information fetching

GDK Rust RPC Bitcoin/Liquid bridge

GDK is a cross-platform, cross-language library for Blockstream Green wallets

GDK Rust RPC Bitcoin/Liquid bridge

A cross-platform library for retrieving per-address debug information

the debug information, and exposes an interface for finding

A cross-platform library for retrieving per-address debug information

rust-clipboard is a cross-platform library for getting and setting the contents of the OS-level clipboard

It has been tested on Windows, Mac OSX, GNU/Linux, and FreeBSD

rust-clipboard is a cross-platform library for getting and setting the contents of the OS-level clipboard

A cross-platorm library and utility to manage passwords

Currently supports Linux, macOS, and Windows

A cross-platorm library and utility to manage passwords

minifb is a cross platform library written in Rust and that makes it easy to...

minifb is a cross platform library written in Changelog

minifb is a cross platform library written in Rust and that makes it easy to...

A cross-platform library for colorizing paths according to the LS_COLORS environment variable (like ls)

Information about the LS_COLORS environment variable is sparse

A cross-platform library for colorizing paths according to the LS_COLORS environment variable (like ls)

Cross-platform library for binary debugging and memory hacking written in Rust

👍 Cross-platform: udbg wraps the details of different interfaces on different platform, and provides uniform interfaces

Cross-platform library for binary debugging and memory hacking written in Rust

A cross-platform library for opening OS pipes, like those from

The Rust standard library provides

A cross-platform library for opening OS pipes, like those from
Facebook Instagram Twitter GitHub Dribbble
Privacy