mirror of
https://github.com/obhq/obliteration.git
synced 2024-11-23 03:09:52 +00:00
Passes device list to Slint UI (#1093)
Co-authored-by: tompro <tomas.prochazka@apertia.cz>
This commit is contained in:
parent
f9c14d68e5
commit
7f7c5429bf
40
gui/src/graphics/metal.rs
Normal file
40
gui/src/graphics/metal.rs
Normal file
@ -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
gui/src/graphics/mod.rs
Normal file
25
gui/src/graphics/mod.rs
Normal file
@ -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
gui/src/graphics/vulkan.rs
Normal file
93
gui/src/graphics/vulkan.rs
Normal file
@ -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),
|
||||
}
|
@ -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>() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
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;
|
||||
@ -8,6 +9,7 @@ use thiserror::Error;
|
||||
mod args;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod graphics;
|
||||
mod param;
|
||||
mod pkg;
|
||||
mod profile;
|
||||
@ -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());
|
||||
@ -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),
|
||||
}
|
||||
|
@ -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() {
|
||||
@ -49,7 +49,7 @@ 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] };
|
||||
@ -57,7 +57,7 @@ impl Drop for Metal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Screen for Metal {
|
||||
impl Screen for MetalScreen {
|
||||
type Buffer = MetalBuffer;
|
||||
type UpdateErr = UpdateError;
|
||||
|
||||
@ -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")]
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
@ -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)
|
||||
@ -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()),
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user