#gpu #renderer #graphics #ffi #api-bindings

virglrenderer

Safe and idiomatic Rust wrapper for virglrenderer

3 releases

0.1.3 Dec 15, 2025
0.1.2 Sep 5, 2025
0.1.0 Sep 4, 2025

#470 in Graphics APIs

Download history 27/week @ 2025-10-23 85/week @ 2025-10-30 74/week @ 2025-11-06 253/week @ 2025-11-13 238/week @ 2025-11-20 6/week @ 2025-11-27 2/week @ 2025-12-04 58/week @ 2025-12-11 50/week @ 2026-01-01 205/week @ 2026-01-08 130/week @ 2026-01-15 41/week @ 2026-01-22 23/week @ 2026-01-29 12/week @ 2026-02-05

207 downloads per month
Used in vhost-device-gpu

MIT license

91KB
2K SLoC

virglrenderer

Rust bindings for virglrenderer.

This crate provides a safe and idiomatic Rust API for interacting with the virglrenderer library, built on top of the raw FFI bindings in virglrenderer-sys.

Features

  • Safe abstraction over the virglrenderer C API
  • Optional support for unstable API features via the virgl_renderer_unstable feature flag
  • Designed to be used in virtual GPU and rendering backend integrations

Documentation

See the crate documentation for usage and API reference.

License

Licensed under MIT.


lib.rs:

Rust bindings for virglrenderer

virglrenderer is a crate that provides safe Rust bindings for libvirglrenderer, a library for hardware-accelerated 3D graphics rendering in virtualized environments. It enables a host-side application to act as a graphics server, processing commands from a guest virtual machine.

Programs that interact with virglrenderer usually react to events from the guest by processing command streams and signaling the completion of operations with fences.

Getting started

Most programs that interact with virglrenderer will need a few basic objects:

  • A VirglRenderer that manages the global state of the rendering process. This object is responsible for initialization, cleanup, and handling global events.
  • A VirglResource that represents a graphics resource, such as a texture or buffer, which can be created and managed by the renderer.

This is how they can be created:

use virglrenderer::{VirglRenderer, VirglError, FenceHandler, VirglRendererFlags};
use std::os::fd::OwnedFd;

// A simple fence handler that prints the fence ID.
struct MyFenceHandler;

impl FenceHandler for MyFenceHandler {
    fn call(&self, fence_id: u64, ctx_id: u32, _ring_idx: u8) {
        println!("Fence complete: {} on context {}", fence_id, ctx_id);
    }
}

fn main() -> Result<(), VirglError> {
    let flags = VirglRendererFlags::new()
        .use_egl(true)
        .use_glx(true)
        .use_virgl(true)
        .use_venus(true)
        .use_surfaceless(true)
        .use_external_blob(true)
        .use_async_fence_cb(true)
        .use_thread_sync(true);

    // Initialize the renderer with a fence handler.
    let renderer = VirglRenderer::init(
        flags, // Flags
        Box::new(MyFenceHandler),
        None, // Optional render server file descriptor
    )?;

    // Create a rendering context.
    renderer.create_context(
        1, // Unique context ID
        0, // Context initialization flags
        Some("my-app-context") // Optional context name
    )?;

    Ok(())
}

Common Operations

Now you can start creating resources, attaching them to the context, and processing command streams.

use virglrenderer::{VirglRenderer, VirglError, ResourceCreate3D, VirglRendererFlags};
use std::os::fd::OwnedFd;

// A simple fence handler (same as above).

fn main() -> Result<(), VirglError> {
    let flags = VirglRendererFlags::new()
        .use_egl(true)
        .use_glx(true)
        .use_virgl(true)
        .use_venus(true)
        .use_surfaceless(true)
        .use_external_blob(true)
        .use_async_fence_cb(true)
        .use_thread_sync(true);

    // Initialize the renderer with a fence handler.
    let renderer = VirglRenderer::init(
        flags, // Flags
        Box::new(MyFenceHandler),
        None, // Optional render server file descriptor
    )?;

    let ctx_id = 1;
    renderer.create_context(ctx_id, 0, Some("my-app-context"))?;

    // Create a 3D resource, like a texture.
    let resource_create_3d = ResourceCreate3D {
        target: 0x0C,   // Example: GL_TEXTURE_2D
        format: 0x8058, // Example: GL_RGBA8
        bind: 0x01,     // Example: GL_TEXTURE_BINDING
        width: 256,
        height: 256,
        depth: 1,
        array_size: 1,
        last_level: 0,
        nr_samples: 0,
        flags: 0,
    };

    // Create the resource with a unique ID and attach it to the context.
    let mut resource = renderer.create_3d(100, resource_create_3d)?;

    renderer.ctx_attach_resource(ctx_id, resource.resource_id);

    // Here, you would process incoming commands and submit them to the context
    // using `renderer.submit_cmd(ctx_id, ...)` and transfer data using `renderer.transfer_write(...)`.

    // Detach the resource when you are done with it.
    renderer.ctx_detach_resource(ctx_id, resource.resource_id);

    Ok(())
}

Note that resource IDs and context IDs are globally unique and are used to refer to objects in commands from the guest.


Event Handling and Integration

The virglrenderer library requires periodic event processing to handle incoming commands and dispatch fence callbacks.

You can integrate virglrenderer into your application's event loop using one of two methods:

  • Direct Polling: Call event_poll() periodically in a loop.
  • File Descriptor Integration (recommended): Use poll_descriptor() to obtain a pollable fd, then register it with your application’s event loop (e.g. epoll, mio, or tokio::AsyncFd).

Here's an example using the event_poll() method:

use virglrenderer::{VirglRenderer, VirglRendererFlags, FenceHandler, VirglError};

fn main() -> Result<(), VirglError> {
    let flags = VirglRendererFlags::new()
        .use_egl(true)
        .use_glx(true)
        .use_virgl(true)
        .use_venus(true)
        .use_surfaceless(true)
        .use_external_blob(true)
        .use_async_fence_cb(true)
        .use_thread_sync(true);

    // Initialize the renderer with a fence handler.
    let renderer = VirglRenderer::init(
        flags, // Flags
        Box::new(MyFenceHandler),
        None, // Optional render server file descriptor
    )?;
    loop {
        renderer.event_poll();
        std::thread::sleep(std::time::Duration::from_millis(10));
    }
}

This will continuously process events from the renderer, ensuring commands and fence callbacks are handled correctly.

Safety Notes

  • virglrenderer is a C library: these bindings wrap unsafe FFI calls in safe Rust APIs.
  • Resource IDs and Context IDs must be unique and guest-driven.
  • Fence callbacks run asynchronously and should avoid blocking.

For more details, see the virglrenderer project.

Dependencies

~0.2–2.5MB
~51K SLoC