mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2025-12-29 00:41:19 +00:00
vhost-device-gpu: Add null backend and test
Add a no-op null backend that allows vhost-device-gpu to compile with --no-default-features and update the README, enabling testing of all feature combinations. The null backend is automatically selected when no-default features are enabled. Signed-off-by: Dorinda Bassey <dbassey@redhat.com>
This commit is contained in:
parent
fc904aca8f
commit
736c5d109d
@ -18,8 +18,8 @@ A virtio-gpu device using the vhost-user protocol.
|
||||
|
||||
-g, --gpu-mode <GPU_MODE>
|
||||
The mode specifies which backend implementation to use
|
||||
|
||||
[possible values: virglrenderer, gfxstream]
|
||||
|
||||
[possible values: virglrenderer, gfxstream, null]
|
||||
|
||||
-c, --capset <CAPSET>
|
||||
Comma separated list of enabled capsets
|
||||
@ -63,8 +63,9 @@ A virtio-gpu device using the vhost-user protocol.
|
||||
Print version
|
||||
```
|
||||
|
||||
_NOTE_: Option `-g, --gpu-mode` can only accept the `gfxstream` value if the
|
||||
crate has been built with the `backend-gfxstream` feature, which is the default.
|
||||
_NOTE_: Option `-g, --gpu-mode` can only accept the `virglrenderer` or `gfxstream`
|
||||
values if the crate has been built with the `backend-virgl` or `backend-gfxstream`
|
||||
features respectively (both are enabled by default). The `null` mode is always available.
|
||||
|
||||
## Limitations
|
||||
|
||||
@ -87,7 +88,7 @@ Because blob resources are not yet supported, some capsets are limited:
|
||||
- gfxstream-vulkan and gfxstream-gles support are exposed, but can practically only be used for display output, there is no hardware acceleration yet.
|
||||
## Features
|
||||
|
||||
This crate supports two GPU backends: gfxstream (default) and virglrenderer.
|
||||
This crate supports three GPU backends: virglrenderer, gfxstream (both enabled by default), and null.
|
||||
|
||||
The **virglrenderer** backend uses the [virglrenderer-rs](https://crates.io/crates/virglrenderer-rs)
|
||||
crate, which provides Rust bindings to the native virglrenderer library. It translates
|
||||
@ -98,24 +99,36 @@ The **gfxstream** backend leverages the [rutabaga_gfx](https://crates.io/crates/
|
||||
crate. With gfxstream rendering mode, GLES and Vulkan calls are forwarded to the host
|
||||
with minimal modification.
|
||||
|
||||
The **null** backend is a no-op implementation that accepts all GPU commands but performs
|
||||
no actual rendering. This backend is primarily intended for testing and CI purposes.
|
||||
|
||||
Install the development packages for your distro, then build with:
|
||||
|
||||
```session
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
gfxstream support is compiled by default, it can be disabled by not building with the `backend-gfxstream` feature flag, for example:
|
||||
Both virglrenderer and gfxstream support are compiled by default. The null backend is
|
||||
always available. To build with specific backends:
|
||||
|
||||
```session
|
||||
# Build with only virglrenderer (and null)
|
||||
$ cargo build --no-default-features --features backend-virgl
|
||||
|
||||
# Build with only gfxstream (and null)
|
||||
$ cargo build --no-default-features --features backend-gfxstream
|
||||
|
||||
# Build with null backend only (for testing)
|
||||
$ cargo build --no-default-features
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
First start the daemon on the host machine using either of the 2 gpu modes:
|
||||
First start the daemon on the host machine using one of the available gpu modes:
|
||||
|
||||
1) `virglrenderer` (if the crate has been compiled with the feature `backend-virgl`)
|
||||
2) `gfxstream` (if the crate has been compiled with the feature `backend-gfxstream`)
|
||||
3) `null` (always available, for testing)
|
||||
|
||||
```shell
|
||||
host# vhost-device-gpu --socket-path /tmp/gpu.socket --gpu-mode virglrenderer
|
||||
|
||||
@ -2,8 +2,10 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
#[cfg(any(feature = "backend-virgl", feature = "backend-gfxstream"))]
|
||||
mod common;
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
pub mod gfxstream;
|
||||
pub mod null;
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
pub mod virgl;
|
||||
|
||||
463
vhost-device-gpu/src/backend/null.rs
Normal file
463
vhost-device-gpu/src/backend/null.rs
Normal file
@ -0,0 +1,463 @@
|
||||
// Null backend
|
||||
// Copyright 2025 Red Hat Inc
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
|
||||
|
||||
use log::trace;
|
||||
use rutabaga_gfx::RutabagaFence;
|
||||
use vhost::vhost_user::{
|
||||
gpu_message::{VhostUserGpuCursorPos, VhostUserGpuEdidRequest},
|
||||
GpuBackend,
|
||||
};
|
||||
use vm_memory::{GuestAddress, GuestMemoryMmap, VolatileSlice};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::{
|
||||
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
|
||||
protocol::{virtio_gpu_rect, GpuResponse, VirtioGpuResult},
|
||||
renderer::Renderer,
|
||||
GpuConfig,
|
||||
};
|
||||
|
||||
pub struct NullAdapter {
|
||||
_gpu_backend: GpuBackend,
|
||||
}
|
||||
|
||||
impl NullAdapter {
|
||||
pub fn new(
|
||||
_queue_ctl: &vhost_user_backend::VringRwLock,
|
||||
_config: &GpuConfig,
|
||||
gpu_backend: GpuBackend,
|
||||
) -> Self {
|
||||
trace!("NullAdapter created");
|
||||
Self {
|
||||
_gpu_backend: gpu_backend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for NullAdapter {
|
||||
fn resource_create_3d(
|
||||
&mut self,
|
||||
_resource_id: u32,
|
||||
_args: ResourceCreate3d,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::resource_create_3d - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn unref_resource(&mut self, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::unref_resource - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn transfer_write(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_transfer: Transfer3DDesc,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::transfer_write - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn transfer_write_2d(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_transfer: Transfer3DDesc,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::transfer_write_2d - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn transfer_read(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_transfer: Transfer3DDesc,
|
||||
_buf: Option<VolatileSlice>,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::transfer_read - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn attach_backing(
|
||||
&mut self,
|
||||
_resource_id: u32,
|
||||
_mem: &GuestMemoryMmap,
|
||||
_vecs: Vec<(GuestAddress, usize)>,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::attach_backing - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn detach_backing(&mut self, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::detach_backing - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn update_cursor(
|
||||
&mut self,
|
||||
_resource_id: u32,
|
||||
_cursor_pos: VhostUserGpuCursorPos,
|
||||
_hot_x: u32,
|
||||
_hot_y: u32,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::update_cursor - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn move_cursor(
|
||||
&mut self,
|
||||
_resource_id: u32,
|
||||
_cursor: VhostUserGpuCursorPos,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::move_cursor - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn resource_assign_uuid(&self, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::resource_assign_uuid - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn get_capset_info(&self, _capset_index: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::get_capset_info - no capsets");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn get_capset(&self, _capset_id: u32, _capset_version: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::get_capset - no capsets");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn create_context(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_context_init: u32,
|
||||
_context_name: Option<&str>,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::create_context - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn destroy_context(&mut self, _ctx_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::destroy_context - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn context_attach_resource(&mut self, _ctx_id: u32, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::context_attach_resource - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn context_detach_resource(&mut self, _ctx_id: u32, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::context_detach_resource - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn submit_command(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_commands: &mut [u8],
|
||||
_fence_ids: &[u64],
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::submit_command - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn create_fence(&mut self, _rutabaga_fence: RutabagaFence) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::create_fence - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn process_fence(
|
||||
&mut self,
|
||||
_ring: VirtioGpuRing,
|
||||
_fence_id: u64,
|
||||
_desc_index: u16,
|
||||
_len: u32,
|
||||
) -> bool {
|
||||
trace!("NullAdapter::process_fence - no-op");
|
||||
true
|
||||
}
|
||||
|
||||
fn get_event_poll_fd(&self) -> Option<EventFd> {
|
||||
trace!("NullAdapter::get_event_poll_fd - no-op");
|
||||
None
|
||||
}
|
||||
|
||||
fn event_poll(&self) {
|
||||
trace!("NullAdapter::event_poll - no-op");
|
||||
}
|
||||
|
||||
fn force_ctx_0(&self) {
|
||||
trace!("NullAdapter::force_ctx_0 - no-op");
|
||||
}
|
||||
|
||||
fn display_info(&self) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::display_info - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn get_edid(&self, _edid_req: VhostUserGpuEdidRequest) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::get_edid - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn set_scanout(
|
||||
&mut self,
|
||||
_scanout_id: u32,
|
||||
_resource_id: u32,
|
||||
_rect: virtio_gpu_rect,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::set_scanout - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn flush_resource(&mut self, _resource_id: u32, _rect: virtio_gpu_rect) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::flush_resource - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn resource_create_blob(
|
||||
&mut self,
|
||||
_ctx_id: u32,
|
||||
_resource_id: u32,
|
||||
_blob_id: u64,
|
||||
_size: u64,
|
||||
_blob_mem: u32,
|
||||
_blob_flags: u32,
|
||||
) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::resource_create_blob - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn resource_map_blob(&mut self, _resource_id: u32, _offset: u64) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::resource_map_blob - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
|
||||
fn resource_unmap_blob(&mut self, _resource_id: u32) -> VirtioGpuResult {
|
||||
trace!("NullAdapter::resource_unmap_blob - no-op");
|
||||
Ok(GpuResponse::OkNoData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use vhost_user_backend::{VringRwLock, VringT};
|
||||
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
|
||||
|
||||
use super::*;
|
||||
use crate::{GpuFlags, GpuMode};
|
||||
|
||||
fn create_null_adapter() -> NullAdapter {
|
||||
let (_, backend) = UnixStream::pair().unwrap();
|
||||
let gpu_backend = GpuBackend::from_stream(backend);
|
||||
let mem = GuestMemoryAtomic::new(
|
||||
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
|
||||
);
|
||||
let vring = VringRwLock::new(mem, 0x100).unwrap();
|
||||
let config = GpuConfig::new(GpuMode::Null, None, GpuFlags::default()).unwrap();
|
||||
|
||||
NullAdapter::new(&vring, &config, gpu_backend)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_creation() {
|
||||
// Verify that NullAdapter can be successfully created
|
||||
let _adapter = create_null_adapter();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_resource_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
|
||||
// Verify resource creation returns success without doing anything
|
||||
let resource_create = ResourceCreate3d {
|
||||
target: 2,
|
||||
format: 1,
|
||||
bind: 1,
|
||||
width: 640,
|
||||
height: 480,
|
||||
depth: 1,
|
||||
array_size: 1,
|
||||
last_level: 0,
|
||||
nr_samples: 0,
|
||||
flags: 0,
|
||||
};
|
||||
let result = adapter.resource_create_3d(1, resource_create);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify unreferencing a resource succeeds
|
||||
let result = adapter.unref_resource(1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify attaching and detaching backing memory succeeds
|
||||
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
|
||||
let result = adapter.attach_backing(1, &mem, vec![]);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
let result = adapter.detach_backing(1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_transfer_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
let transfer = Transfer3DDesc::new_2d(0, 0, 640, 480, 0);
|
||||
|
||||
// Verify 3D transfer write succeeds
|
||||
let result = adapter.transfer_write(0, 1, transfer);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify 2D transfer write succeeds
|
||||
let result = adapter.transfer_write_2d(0, 1, transfer);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify transfer read succeeds
|
||||
let result = adapter.transfer_read(0, 1, transfer, None);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_context_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
|
||||
// Verify context creation succeeds
|
||||
let result = adapter.create_context(1, 0, Some("test"));
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify attaching a resource to a context succeeds
|
||||
let result = adapter.context_attach_resource(1, 1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify detaching a resource from a context succeeds
|
||||
let result = adapter.context_detach_resource(1, 1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify context destruction succeeds
|
||||
let result = adapter.destroy_context(1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_display_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
|
||||
// Verify getting display info succeeds
|
||||
let result = adapter.display_info();
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify getting EDID info succeeds
|
||||
let result = adapter.get_edid(VhostUserGpuEdidRequest { scanout_id: 0 });
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify setting scanout succeeds
|
||||
let result = adapter.set_scanout(0, 1, virtio_gpu_rect::default());
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify flushing a resource succeeds
|
||||
let result = adapter.flush_resource(1, virtio_gpu_rect::default());
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_cursor_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
let cursor_pos = VhostUserGpuCursorPos {
|
||||
scanout_id: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
// Verify updating cursor succeeds
|
||||
let result = adapter.update_cursor(1, cursor_pos, 0, 0);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify moving cursor succeeds
|
||||
let result = adapter.move_cursor(1, cursor_pos);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_capset_operations() {
|
||||
let adapter = create_null_adapter();
|
||||
|
||||
// Verify getting capset info returns success (null backend has no capsets)
|
||||
let result = adapter.get_capset_info(0);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify getting capset returns success (null backend has no capsets)
|
||||
let result = adapter.get_capset(0, 0);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_command_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
let mut commands = vec![0u8; 64];
|
||||
|
||||
// Verify submitting commands succeeds
|
||||
let result = adapter.submit_command(1, &mut commands, &[]);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_fence_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
let fence = RutabagaFence {
|
||||
flags: 0,
|
||||
fence_id: 1,
|
||||
ctx_id: 0,
|
||||
ring_idx: 0,
|
||||
};
|
||||
|
||||
// Verify creating a fence succeeds
|
||||
let result = adapter.create_fence(fence);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify processing fence returns true (fence is immediately ready in null
|
||||
// backend)
|
||||
let ready = adapter.process_fence(VirtioGpuRing::Global, 1, 0, 0);
|
||||
assert!(ready);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_blob_operations() {
|
||||
let mut adapter = create_null_adapter();
|
||||
|
||||
// Verify blob resource creation succeeds
|
||||
let result = adapter.resource_create_blob(0, 1, 1, 4096, 0, 0);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify mapping blob resource succeeds
|
||||
let result = adapter.resource_map_blob(1, 0);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify unmapping blob resource succeeds
|
||||
let result = adapter.resource_unmap_blob(1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_adapter_misc_operations() {
|
||||
let adapter = create_null_adapter();
|
||||
|
||||
// Verify assigning UUID to resource succeeds
|
||||
let result = adapter.resource_assign_uuid(1);
|
||||
assert!(matches!(result, Ok(GpuResponse::OkNoData)));
|
||||
|
||||
// Verify no event poll fd is provided (null backend has no events)
|
||||
let event_fd = adapter.get_event_poll_fd();
|
||||
assert!(event_fd.is_none());
|
||||
|
||||
// Verify event polling and force_ctx_0 don't panic (they're no-ops)
|
||||
adapter.event_poll();
|
||||
adapter.force_ctx_0();
|
||||
}
|
||||
}
|
||||
@ -77,6 +77,7 @@ use crate::backend::gfxstream::GfxstreamAdapter;
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
use crate::backend::virgl::VirglRendererAdapter;
|
||||
use crate::{
|
||||
backend::null::NullAdapter,
|
||||
gpu_types::{ResourceCreate3d, Transfer3DDesc, VirtioGpuRing},
|
||||
protocol::{
|
||||
virtio_gpu_ctrl_hdr, virtio_gpu_ctx_create, virtio_gpu_get_edid,
|
||||
@ -630,6 +631,17 @@ impl VhostUserGpuBackendInner {
|
||||
device_event,
|
||||
vrings
|
||||
),
|
||||
|
||||
GpuMode::Null => handle_adapter!(
|
||||
NullAdapter,
|
||||
TLS_NULL,
|
||||
|control_vring, gpu_backend| {
|
||||
NullAdapter::new(control_vring, &self.gpu_config, gpu_backend)
|
||||
},
|
||||
self,
|
||||
device_event,
|
||||
vrings
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
/// Generates an implementation of `From<Transfer3DDesc>` for any compatible
|
||||
/// target struct.
|
||||
#[cfg(any(feature = "backend-virgl", feature = "backend-gfxstream"))]
|
||||
macro_rules! impl_transfer3d_from_desc {
|
||||
($target:path) => {
|
||||
impl From<Transfer3DDesc> for $target {
|
||||
@ -25,6 +26,7 @@ macro_rules! impl_transfer3d_from_desc {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "backend-virgl", feature = "backend-gfxstream"))]
|
||||
macro_rules! impl_from_resource_create3d {
|
||||
($target:ty) => {
|
||||
impl From<ResourceCreate3d> for $target {
|
||||
@ -48,7 +50,9 @@ macro_rules! impl_from_resource_create3d {
|
||||
|
||||
use std::{collections::BTreeMap, os::raw::c_void};
|
||||
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
use rutabaga_gfx::Transfer3D;
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
use virglrenderer::Transfer3D as VirglTransfer3D;
|
||||
|
||||
use crate::protocol::virtio_gpu_rect;
|
||||
@ -87,8 +91,10 @@ impl Transfer3DDesc {
|
||||
}
|
||||
// Invoke the macro for both targets
|
||||
// rutabaga_gfx::Transfer3D
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
impl_transfer3d_from_desc!(Transfer3D);
|
||||
// virglrenderer::Transfer3D
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
impl_transfer3d_from_desc!(VirglTransfer3D);
|
||||
|
||||
// These are neutral types that can be used by all backends
|
||||
@ -136,7 +142,9 @@ pub struct ResourceCreate3d {
|
||||
}
|
||||
|
||||
// Invoke the macro for both targets
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
impl_from_resource_create3d!(rutabaga_gfx::ResourceCreate3D);
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
impl_from_resource_create3d!(virglrenderer::ResourceCreate3D);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
||||
@ -44,6 +44,7 @@ pub enum GpuMode {
|
||||
VirglRenderer,
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
Gfxstream,
|
||||
Null,
|
||||
}
|
||||
|
||||
impl Display for GpuMode {
|
||||
@ -53,6 +54,7 @@ impl Display for GpuMode {
|
||||
Self::VirglRenderer => write!(f, "virglrenderer"),
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
Self::Gfxstream => write!(f, "gfxstream"),
|
||||
Self::Null => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,7 +81,12 @@ bitflags! {
|
||||
|
||||
impl Display for GpuCapset {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_empty() {
|
||||
return write!(f, "none");
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
#[allow(unused_assignments)]
|
||||
for capset in self.iter() {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
@ -88,15 +95,15 @@ impl Display for GpuCapset {
|
||||
|
||||
match capset {
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
Self::VIRGL => write!(f, "virgl"),
|
||||
Self::VIRGL => write!(f, "virgl")?,
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
Self::VIRGL2 => write!(f, "virgl2"),
|
||||
Self::VIRGL2 => write!(f, "virgl2")?,
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
Self::GFXSTREAM_VULKAN => write!(f, "gfxstream-vulkan"),
|
||||
Self::GFXSTREAM_VULKAN => write!(f, "gfxstream-vulkan")?,
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
Self::GFXSTREAM_GLES => write!(f, "gfxstream-gles"),
|
||||
Self::GFXSTREAM_GLES => write!(f, "gfxstream-gles")?,
|
||||
_ => panic!("Unknown capset {:#x}", self.bits()),
|
||||
}?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -165,6 +172,7 @@ impl GpuConfig {
|
||||
GpuMode::VirglRenderer => Self::DEFAULT_VIRGLRENDER_CAPSET_MASK,
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
GpuMode::Gfxstream => Self::DEFAULT_GFXSTREAM_CAPSET_MASK,
|
||||
GpuMode::Null => GpuCapset::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,6 +182,7 @@ impl GpuConfig {
|
||||
GpuMode::VirglRenderer => GpuCapset::ALL_VIRGLRENDERER_CAPSETS,
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
GpuMode::Gfxstream => GpuCapset::ALL_GFXSTREAM_CAPSETS,
|
||||
GpuMode::Null => GpuCapset::empty(),
|
||||
};
|
||||
for capset in capset.iter() {
|
||||
if !supported_capset_mask.contains(capset) {
|
||||
|
||||
@ -12,6 +12,9 @@ use vhost_device_gpu::{start_backend, GpuCapset, GpuConfig, GpuConfigError, GpuF
|
||||
|
||||
#[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[repr(u64)]
|
||||
// __Null is a placeholder to prevent a zero-variant enum when building with
|
||||
// --no-default-features, not an implementation of the non-exhaustive pattern
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
pub enum CapsetName {
|
||||
/// [virglrenderer] OpenGL implementation, superseded by Virgl2
|
||||
#[cfg(feature = "backend-virgl")]
|
||||
@ -32,10 +35,20 @@ pub enum CapsetName {
|
||||
/// hardware acceleration yet
|
||||
#[cfg(feature = "backend-gfxstream")]
|
||||
GfxstreamGles = GpuCapset::GFXSTREAM_GLES.bits(),
|
||||
|
||||
/// Placeholder variant to prevent zero-variant enum when no backend
|
||||
/// features are enabled. The null backend doesn't use capsets, so this
|
||||
/// maps to GpuCapset::empty().
|
||||
#[doc(hidden)]
|
||||
__Null = 0,
|
||||
}
|
||||
|
||||
impl From<CapsetName> for GpuCapset {
|
||||
fn from(capset_name: CapsetName) -> GpuCapset {
|
||||
if matches!(capset_name, CapsetName::__Null) {
|
||||
return GpuCapset::empty();
|
||||
}
|
||||
|
||||
GpuCapset::from_bits(capset_name as u64)
|
||||
.expect("Internal error: CapsetName enum is incorrectly defined")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user