mirror of
https://github.com/obhq/obliteration.git
synced 2024-11-26 20:50:22 +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();
|
let line = loc.line();
|
||||||
|
|
||||||
// Get message.
|
// 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>() {
|
let msg = if let Some(&p) = info.payload().downcast_ref::<&str>() {
|
||||||
p
|
p
|
||||||
} else if let Some(p) = info.payload().downcast_ref::<String>() {
|
} else if let Some(p) = info.payload().downcast_ref::<String>() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use args::CliArgs;
|
use args::CliArgs;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use debug::DebugServer;
|
use debug::DebugServer;
|
||||||
|
use graphics::{GraphicsApi, PhysicalDevice};
|
||||||
use slint::{ComponentHandle, ModelExt, ModelRc, SharedString, VecModel};
|
use slint::{ComponentHandle, ModelExt, ModelRc, SharedString, VecModel};
|
||||||
use std::process::{ExitCode, Termination};
|
use std::process::{ExitCode, Termination};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@ -8,6 +9,7 @@ use thiserror::Error;
|
|||||||
mod args;
|
mod args;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod graphics;
|
||||||
mod param;
|
mod param;
|
||||||
mod pkg;
|
mod pkg;
|
||||||
mod profile;
|
mod profile;
|
||||||
@ -70,6 +72,17 @@ impl App {
|
|||||||
fn new() -> Result<Self, ApplicationError> {
|
fn new() -> Result<Self, ApplicationError> {
|
||||||
let main_window = ui::MainWindow::new().map_err(ApplicationError::CreateMainWindow)?;
|
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()));
|
let games = ModelRc::new(VecModel::from(Vec::new()));
|
||||||
|
|
||||||
main_window.set_games(games.clone());
|
main_window.set_games(games.clone());
|
||||||
@ -172,6 +185,9 @@ pub enum ApplicationError {
|
|||||||
#[error("failed to create main window")]
|
#[error("failed to create main window")]
|
||||||
CreateMainWindow(#[source] slint::PlatformError),
|
CreateMainWindow(#[source] slint::PlatformError),
|
||||||
|
|
||||||
|
#[error("failed to initialize graphics API")]
|
||||||
|
InitGraphicsApi(#[source] <graphics::DefaultApi as GraphicsApi>::CreateError),
|
||||||
|
|
||||||
#[error("failed to run main window")]
|
#[error("failed to run main window")]
|
||||||
RunMainWindow(#[source] slint::PlatformError),
|
RunMainWindow(#[source] slint::PlatformError),
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,14 @@ mod buffer;
|
|||||||
/// Implementation of [`Screen`] using Metal.
|
/// Implementation of [`Screen`] using Metal.
|
||||||
///
|
///
|
||||||
/// Fields in this struct need to be dropped in a correct order.
|
/// Fields in this struct need to be dropped in a correct order.
|
||||||
pub struct Metal {
|
pub struct MetalScreen {
|
||||||
view: *mut Object,
|
view: *mut Object,
|
||||||
buffer: Arc<MetalBuffer>,
|
buffer: Arc<MetalBuffer>,
|
||||||
layer: MetalLayer,
|
layer: MetalLayer,
|
||||||
device: Device,
|
device: Device,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metal {
|
impl MetalScreen {
|
||||||
pub fn from_screen(screen: &VmmScreen) -> Result<Self, MetalError> {
|
pub fn from_screen(screen: &VmmScreen) -> Result<Self, MetalError> {
|
||||||
// Get Metal device.
|
// Get Metal device.
|
||||||
let device = match Device::system_default() {
|
let device = match Device::system_default() {
|
||||||
@ -49,7 +49,7 @@ impl Metal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Metal {
|
impl Drop for MetalScreen {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let l: *mut CAMetalLayer = null_mut();
|
let l: *mut CAMetalLayer = null_mut();
|
||||||
let _: () = unsafe { msg_send![self.view, setWantsLayer:NO] };
|
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 Buffer = MetalBuffer;
|
||||||
type UpdateErr = UpdateError;
|
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)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MetalError {
|
pub enum MetalError {
|
||||||
#[error("couldn't get default MTLDevice")]
|
#[error("couldn't get default MTLDevice")]
|
||||||
|
@ -7,13 +7,13 @@ use std::sync::Arc;
|
|||||||
mod engine;
|
mod engine;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub type Default = self::engine::Vulkan;
|
pub type Default = self::engine::VulkanScreen;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub type Default = self::engine::Metal;
|
pub type Default = self::engine::MetalScreen;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub type ScreenError = self::engine::VulkanError;
|
pub type ScreenError = self::engine::VulkanScreenError;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub type ScreenError = self::engine::MetalError;
|
pub type ScreenError = self::engine::MetalError;
|
||||||
|
@ -10,13 +10,13 @@ use thiserror::Error;
|
|||||||
mod buffer;
|
mod buffer;
|
||||||
|
|
||||||
/// Implementation of [`Screen`] using Vulkan.
|
/// Implementation of [`Screen`] using Vulkan.
|
||||||
pub struct Vulkan {
|
pub struct VulkanScreen {
|
||||||
buffer: Arc<VulkanBuffer>,
|
buffer: Arc<VulkanBuffer>,
|
||||||
device: Device,
|
device: Device,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vulkan {
|
impl VulkanScreen {
|
||||||
pub fn from_screen(screen: &VmmScreen) -> Result<Self, VulkanError> {
|
pub fn from_screen(screen: &VmmScreen) -> Result<Self, VulkanScreenError> {
|
||||||
let entry = ash::Entry::linked();
|
let entry = ash::Entry::linked();
|
||||||
|
|
||||||
let instance = unsafe {
|
let instance = unsafe {
|
||||||
@ -34,11 +34,11 @@ impl Vulkan {
|
|||||||
let queue = unsafe { instance.get_physical_device_queue_family_properties(physical) }
|
let queue = unsafe { instance.get_physical_device_queue_family_properties(physical) }
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
|
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
|
||||||
.ok_or(VulkanError::NoQueue)?;
|
.ok_or(VulkanScreenError::NoQueue)?;
|
||||||
|
|
||||||
let queue = queue
|
let queue = queue
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| VulkanError::QueueOutOfBounds(queue))?;
|
.map_err(|_| VulkanScreenError::QueueOutOfBounds(queue))?;
|
||||||
|
|
||||||
let queues = DeviceQueueCreateInfo::default()
|
let queues = DeviceQueueCreateInfo::default()
|
||||||
.queue_family_index(queue)
|
.queue_family_index(queue)
|
||||||
@ -47,7 +47,7 @@ impl Vulkan {
|
|||||||
// Create logical device.
|
// Create logical device.
|
||||||
let device = DeviceCreateInfo::default().queue_create_infos(std::slice::from_ref(&queues));
|
let device = DeviceCreateInfo::default().queue_create_infos(std::slice::from_ref(&queues));
|
||||||
let device = unsafe { instance.create_device(physical, &device, None) }
|
let device = unsafe { instance.create_device(physical, &device, None) }
|
||||||
.map_err(VulkanError::CreateDeviceFailed)?;
|
.map_err(VulkanScreenError::CreateDeviceFailed)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
buffer: Arc::new(VulkanBuffer::new()),
|
buffer: Arc::new(VulkanBuffer::new()),
|
||||||
@ -56,14 +56,14 @@ impl Vulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Vulkan {
|
impl Drop for VulkanScreen {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { self.device.device_wait_idle().unwrap() };
|
unsafe { self.device.device_wait_idle().unwrap() };
|
||||||
unsafe { self.device.destroy_device(None) };
|
unsafe { self.device.destroy_device(None) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen for Vulkan {
|
impl Screen for VulkanScreen {
|
||||||
type Buffer = VulkanBuffer;
|
type Buffer = VulkanBuffer;
|
||||||
type UpdateErr = UpdateError;
|
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)]
|
#[derive(Debug, Error)]
|
||||||
pub enum VulkanError {
|
pub enum VulkanScreenError {
|
||||||
#[error("couldn't find suitable queue")]
|
#[error("couldn't find suitable queue")]
|
||||||
NoQueue,
|
NoQueue,
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ mod unix;
|
|||||||
///
|
///
|
||||||
/// The reason this function accept an [`FnMut`] instead of [`FnOnce`] to support exiting the
|
/// 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
|
/// 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.
|
/// other variables need to be dropped before exiting the thread.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
Loading…
Reference in New Issue
Block a user