Skip to content

Commit

Permalink
Passes device list to Slint UI (#1093)
Browse files Browse the repository at this point in the history
Co-authored-by: tompro <tomas.prochazka@apertia.cz>
  • Loading branch information
SuchAFuriousDeath and SuchAFuriousDeath authored Nov 15, 2024
1 parent f9c14d6 commit 7f7c542
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 19 deletions.
40 changes: 40 additions & 0 deletions gui/src/graphics/metal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use self::buffer::MetalBuffer;
use super::{Screen, ScreenBuffer};
use crate::vmm::VmmScreen;
use metal::{CAMetalLayer, Device, MetalLayer};
use objc::runtime::{Object, NO, YES};
use objc::{msg_send, sel, sel_impl};
use std::ptr::null_mut;
use std::sync::Arc;
use thiserror::Error;

pub struct Metal {
devices: Vec<metal::Device>,
}

impl super::GraphicsApi for Metal {
type PhysicalDevice = metal::Device;

type CreateError = MetalCreateError;

fn new() -> Result<Self, Self::CreateError> {
Ok(Self {
devices: Device::all(),
})
}

fn physical_devices(&self) -> &[Self::PhysicalDevice] {
&self.devices
}
}

impl super::PhysicalDevice for metal::Device {
fn name(&self) -> &str {
self.name()
}
}

/// Represents an error when [`Metal::new()`] fails.
#[derive(Debug, Error)]
pub enum MetalCreateError {}
25 changes: 25 additions & 0 deletions gui/src/graphics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

#[cfg_attr(target_os = "macos", path = "metal.rs")]
#[cfg_attr(not(target_os = "macos"), path = "vulkan.rs")]
mod api;

#[cfg(not(target_os = "macos"))]
pub type DefaultApi = self::api::Vulkan;

#[cfg(target_os = "macos")]
pub type DefaultApi = self::api::Metal;

pub trait GraphicsApi: Sized + 'static {
type PhysicalDevice: PhysicalDevice;

type CreateError: core::error::Error;

fn new() -> Result<Self, Self::CreateError>;

fn physical_devices(&self) -> &[Self::PhysicalDevice];
}

pub trait PhysicalDevice: Sized {
fn name(&self) -> &str;
}
93 changes: 93 additions & 0 deletions gui/src/graphics/vulkan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use ash::vk::{ApplicationInfo, InstanceCreateInfo};
use std::ffi::CStr;
use thiserror::Error;

pub struct Vulkan {
entry: ash::Entry,
instance: ash::Instance,
devices: Vec<VulkanPhysicalDevice>,
}

impl super::GraphicsApi for Vulkan {
type PhysicalDevice = VulkanPhysicalDevice;

type CreateError = VulkanCreateError;

fn new() -> Result<Self, Self::CreateError> {
let entry = ash::Entry::linked();

let app_info = ApplicationInfo::default().application_name(c"Obliteration");

let create_info = InstanceCreateInfo::default().application_info(&app_info);

let instance = unsafe { entry.create_instance(&create_info, None) }
.map_err(VulkanCreateError::CreateInstanceFailed)?;

let devices = unsafe { instance.enumerate_physical_devices() }
.map_err(VulkanCreateError::EnumeratePhysicalDevicesFailed)?
.into_iter()
.map(
|device| -> Result<VulkanPhysicalDevice, VulkanCreateError> {
let properties = unsafe { instance.get_physical_device_properties(device) };

let name = CStr::from_bytes_until_nul(unsafe {
std::slice::from_raw_parts(
properties.device_name.as_ptr().cast(),
properties.device_name.len(),
)
})
.map_err(|_| VulkanCreateError::DeviceNameInvalid)?
.to_str()
.map_err(VulkanCreateError::DeviceNameInvalidUtf8)?
.to_owned();

Ok(VulkanPhysicalDevice { device, name })
},
)
.collect::<Result<_, VulkanCreateError>>()?;

Ok(Self {
entry,
instance,
devices,
})
}

fn physical_devices(&self) -> &[Self::PhysicalDevice] {
&self.devices
}
}

impl Drop for Vulkan {
fn drop(&mut self) {
unsafe { self.instance.destroy_instance(None) };
}
}

pub struct VulkanPhysicalDevice {
device: ash::vk::PhysicalDevice,
name: String,
}

impl super::PhysicalDevice for VulkanPhysicalDevice {
fn name(&self) -> &str {
&self.name
}
}

/// Represents an error when [`Vulkan::new()`] fails.
#[derive(Debug, Error)]
pub enum VulkanCreateError {
#[error("couldn't create Vulkan instance")]
CreateInstanceFailed(#[source] ash::vk::Result),

#[error("couldn't enumerate physical devices")]
EnumeratePhysicalDevicesFailed(#[source] ash::vk::Result),

#[error("no null byte in device name")]
DeviceNameInvalid,

#[error("device name is not valid UTF-8")]
DeviceNameInvalidUtf8(#[source] std::str::Utf8Error),
}
1 change: 1 addition & 0 deletions gui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub unsafe extern "C-unwind" fn set_panic_hook(
let line = loc.line();

// Get message.
//TODO: use payload_as_str() when https://github.com/rust-lang/rust/issues/125175 is stable.
let msg = if let Some(&p) = info.payload().downcast_ref::<&str>() {
p
} else if let Some(p) = info.payload().downcast_ref::<String>() {
Expand Down
16 changes: 16 additions & 0 deletions gui/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use args::CliArgs;
use clap::Parser;
use debug::DebugServer;
use graphics::{GraphicsApi, PhysicalDevice};
use slint::{ComponentHandle, ModelExt, ModelRc, SharedString, VecModel};
use std::process::{ExitCode, Termination};
use thiserror::Error;

mod args;
mod debug;
mod error;
mod graphics;
mod param;
mod pkg;
mod profile;
Expand Down Expand Up @@ -70,6 +72,17 @@ impl App {
fn new() -> Result<Self, ApplicationError> {
let main_window = ui::MainWindow::new().map_err(ApplicationError::CreateMainWindow)?;

let graphics_api =
graphics::DefaultApi::new().map_err(ApplicationError::InitGraphicsApi)?;

let devices: Vec<SharedString> = graphics_api
.physical_devices()
.into_iter()
.map(|d| SharedString::from(d.name()))
.collect();

main_window.set_devices(ModelRc::new(VecModel::from(devices)));

let games = ModelRc::new(VecModel::from(Vec::new()));

main_window.set_games(games.clone());
Expand Down Expand Up @@ -172,6 +185,9 @@ pub enum ApplicationError {
#[error("failed to create main window")]
CreateMainWindow(#[source] slint::PlatformError),

#[error("failed to initialize graphics API")]
InitGraphicsApi(#[source] <graphics::DefaultApi as GraphicsApi>::CreateError),

#[error("failed to run main window")]
RunMainWindow(#[source] slint::PlatformError),
}
10 changes: 5 additions & 5 deletions gui/src/screen/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ mod buffer;
/// Implementation of [`Screen`] using Metal.
///
/// Fields in this struct need to be dropped in a correct order.
pub struct Metal {
pub struct MetalScreen {
view: *mut Object,
buffer: Arc<MetalBuffer>,
layer: MetalLayer,
device: Device,
}

impl Metal {
impl MetalScreen {
pub fn from_screen(screen: &VmmScreen) -> Result<Self, MetalError> {
// Get Metal device.
let device = match Device::system_default() {
Expand Down Expand Up @@ -49,15 +49,15 @@ impl Metal {
}
}

impl Drop for Metal {
impl Drop for MetalScreen {
fn drop(&mut self) {
let l: *mut CAMetalLayer = null_mut();
let _: () = unsafe { msg_send![self.view, setWantsLayer:NO] };
let _: () = unsafe { msg_send![self.view, setLayer:l] };
}
}

impl Screen for Metal {
impl Screen for MetalScreen {
type Buffer = MetalBuffer;
type UpdateErr = UpdateError;

Expand All @@ -70,7 +70,7 @@ impl Screen for Metal {
}
}

/// Represents an error when [`Metal::new()`] fails.
/// Represents an error when [`MetalScreen::new()`] fails.
#[derive(Debug, Error)]
pub enum MetalError {
#[error("couldn't get default MTLDevice")]
Expand Down
6 changes: 3 additions & 3 deletions gui/src/screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use std::sync::Arc;
mod engine;

#[cfg(not(target_os = "macos"))]
pub type Default = self::engine::Vulkan;
pub type Default = self::engine::VulkanScreen;

#[cfg(target_os = "macos")]
pub type Default = self::engine::Metal;
pub type Default = self::engine::MetalScreen;

#[cfg(not(target_os = "macos"))]
pub type ScreenError = self::engine::VulkanError;
pub type ScreenError = self::engine::VulkanScreenError;

#[cfg(target_os = "macos")]
pub type ScreenError = self::engine::MetalError;
Expand Down
20 changes: 10 additions & 10 deletions gui/src/screen/vulkan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ use thiserror::Error;
mod buffer;

/// Implementation of [`Screen`] using Vulkan.
pub struct Vulkan {
pub struct VulkanScreen {
buffer: Arc<VulkanBuffer>,
device: Device,
}

impl Vulkan {
pub fn from_screen(screen: &VmmScreen) -> Result<Self, VulkanError> {
impl VulkanScreen {
pub fn from_screen(screen: &VmmScreen) -> Result<Self, VulkanScreenError> {
let entry = ash::Entry::linked();

let instance = unsafe {
Expand All @@ -34,11 +34,11 @@ impl Vulkan {
let queue = unsafe { instance.get_physical_device_queue_family_properties(physical) }
.into_iter()
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
.ok_or(VulkanError::NoQueue)?;
.ok_or(VulkanScreenError::NoQueue)?;

let queue = queue
.try_into()
.map_err(|_| VulkanError::QueueOutOfBounds(queue))?;
.map_err(|_| VulkanScreenError::QueueOutOfBounds(queue))?;

let queues = DeviceQueueCreateInfo::default()
.queue_family_index(queue)
Expand All @@ -47,7 +47,7 @@ impl Vulkan {
// Create logical device.
let device = DeviceCreateInfo::default().queue_create_infos(std::slice::from_ref(&queues));
let device = unsafe { instance.create_device(physical, &device, None) }
.map_err(VulkanError::CreateDeviceFailed)?;
.map_err(VulkanScreenError::CreateDeviceFailed)?;

Ok(Self {
buffer: Arc::new(VulkanBuffer::new()),
Expand All @@ -56,14 +56,14 @@ impl Vulkan {
}
}

impl Drop for Vulkan {
impl Drop for VulkanScreen {
fn drop(&mut self) {
unsafe { self.device.device_wait_idle().unwrap() };
unsafe { self.device.destroy_device(None) };
}
}

impl Screen for Vulkan {
impl Screen for VulkanScreen {
type Buffer = VulkanBuffer;
type UpdateErr = UpdateError;

Expand All @@ -76,9 +76,9 @@ impl Screen for Vulkan {
}
}

/// Represents an error when [`Vulkan::new()`] fails.
/// Represents an error when [`VulkanScreen::new()`] fails.
#[derive(Debug, Error)]
pub enum VulkanError {
pub enum VulkanScreenError {
#[error("couldn't find suitable queue")]
NoQueue,

Expand Down
2 changes: 1 addition & 1 deletion src/llt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod unix;
///
/// The reason this function accept an [`FnMut`] instead of [`FnOnce`] to support exiting the
/// thread without returning from the `entry` (e.g. using `pthread_exit`). [`FnOnce`] requires the
/// function to live on the stack while [`FnMut`] is not. The caller still need to make sure no
/// function to live on the stack while [`FnMut`] does not. The caller still need to make sure no
/// other variables need to be dropped before exiting the thread.
///
/// # Safety
Expand Down

0 comments on commit 7f7c542

Please sign in to comment.