Expand description
§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
VirglRendererthat manages the global state of the rendering process. This object is responsible for initialization, cleanup, and handling global events. - A
VirglContextthat provides a way to submit rendering commands from the guest. All commands are associated with a specific context. - A
VirglResourcethat 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, VirglContext, 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_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.
let ctx = VirglContext::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.
- Creating Resources: Use
VirglRenderer::create_3dorVirglRenderer::create_blob. - Attaching Backing Memory: Map guest memory to virgl resources.
- Submitting Commands: Send rendering commands to a
VirglContext.
use virglrenderer::{VirglRenderer, VirglContext, VirglError, ResourceCreate3D, VirglRendererFlags};
use std::os::fd::OwnedFd;
// A simple fence handler (same as above).
fn main() -> Result<(), VirglError> {
let flags = VirglRendererFlags::new()
.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 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)?;
ctx.attach(&mut resource);
// Here, you would process incoming commands and submit them to the context
// using `ctx.submit_cmd(...)` and transfer data using `renderer.transfer_write(...)`.
// Detach the resource when you are done with it.
ctx.detach(&resource);
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, ortokio::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_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.
Re-exports§
pub use flags::VirglRendererFlags;
Modules§
Structs§
- Iovec
- Resource3D
Info - Resource
Create3D - Resource
Create Blob - Transfer3D
- Virgl
Box - Virgl
Context - Virgl
Handle - Virgl
Renderer - Virgl
Resource