Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 57 additions & 10 deletions src/devices/src/virtio/gpu/virtio_gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,14 @@ impl VirtioGpu {
})
}

#[allow(clippy::too_many_arguments)]
pub fn new(
pub fn create_rutabaga(
mem: GuestMemoryMmap,
queue_ctl: Arc<Mutex<VirtQueue>>,
interrupt: InterruptTransport,
fence_state: Arc<Mutex<FenceState>>,
virgl_flags: u32,
#[cfg(target_os = "macos")] map_sender: Sender<WorkerMessage>,
export_table: Option<ExportTable>,
displays: Box<[DisplayInfo]>,
display_backend: DisplayBackend,
) -> Self {
) -> Option<Rutabaga> {
let xdg_runtime_dir = match env::var("XDG_RUNTIME_DIR") {
Ok(dir) => dir,
Err(_) => "/run/user/1000".to_string(),
Expand Down Expand Up @@ -274,12 +271,62 @@ impl VirtioGpu {
builder
};

let fence_state = Arc::new(Mutex::new(Default::default()));
let fence =
Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt);
let rutabaga = builder
.build(fence, None)
.expect("Rutabaga initialization failed!");
builder.clone().build(fence.clone(), None).ok()
}

pub fn create_fallback_rutabaga(
mem: GuestMemoryMmap,
queue_ctl: Arc<Mutex<VirtQueue>>,
interrupt: InterruptTransport,
fence_state: Arc<Mutex<FenceState>>,
) -> Option<Rutabaga> {
const VIRGLRENDERER_NO_VIRGL: u32 = 1 << 7;
let builder = RutabagaBuilder::new(
rutabaga_gfx::RutabagaComponentType::VirglRenderer,
VIRGLRENDERER_NO_VIRGL,
0,
);

let fence =
Self::create_fence_handler(mem, queue_ctl.clone(), fence_state.clone(), interrupt);
builder.clone().build(fence.clone(), None).ok()
}

#[allow(clippy::too_many_arguments)]
pub fn new(
mem: GuestMemoryMmap,
queue_ctl: Arc<Mutex<VirtQueue>>,
interrupt: InterruptTransport,
virgl_flags: u32,
#[cfg(target_os = "macos")] map_sender: Sender<WorkerMessage>,
export_table: Option<ExportTable>,
displays: Box<[DisplayInfo]>,
display_backend: DisplayBackend,
) -> Self {
let fence_state = Arc::new(Mutex::new(Default::default()));

let rutabaga = match Self::create_rutabaga(
mem.clone(),
queue_ctl.clone(),
interrupt.clone(),
fence_state.clone(),
virgl_flags,
export_table.clone(),
) {
Some(rutabaga) => rutabaga,
None => {
error!("Failed to create virtio_gpu backend with the requested parameters");
Self::create_fallback_rutabaga(
mem.clone(),
queue_ctl.clone(),
interrupt.clone(),
fence_state.clone(),
)
.expect("Fallback rutabaga initialization failed")
}
};

let display_backend = display_backend
.create_instance()
Expand Down
24 changes: 13 additions & 11 deletions src/rutabaga_gfx/src/virgl_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,17 +301,6 @@ impl VirglRenderer {
}
}

// virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
// Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
// to whichever thread called this function first.
static INIT_ONCE: AtomicBool = AtomicBool::new(false);
if INIT_ONCE
.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
.is_err()
{
return Err(RutabagaError::AlreadyInUse);
}

unsafe { virgl_set_debug_callback(Some(debug_callback)) };

// Cookie is intentionally never freed because virglrenderer never gets uninitialized.
Expand All @@ -335,6 +324,19 @@ impl VirglRenderer {
)
};

if ret == 0 {
// virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
// Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
// to whichever thread called this function first.
static INIT_ONCE: AtomicBool = AtomicBool::new(false);
if INIT_ONCE
.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
.is_err()
{
return Err(RutabagaError::AlreadyInUse);
}
}
Comment on lines +327 to +338
Copy link
Collaborator

@mtjhrc mtjhrc Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original intention of this was to prevent calling virgl_renderer_init when it was already initialized, no? But this change allows initializing virglrenderer a second time. I'm not sure if that is even a problem though...

If we want to keep the check working you can is still keep the check before virgl_renderer_init:

if INIT_ONCE.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() { 
    return `RutabagaError::AlreadyInUse` 
}

But after calling virgl_renderer_init, set it to the actual result:
INIT_ONCE = (virgl_renderer_init() == 0)

(Not sure about the memory ordering...)


ret_to_res(ret)?;
Ok(Box::new(VirglRenderer {}))
}
Expand Down
Loading