Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows Screen to be used directly by VMM #1204

Merged
merged 2 commits into from
Dec 27, 2024
Merged
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
5 changes: 3 additions & 2 deletions gui/src/graphics/metal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use super::Graphics;
use crate::profile::Profile;
use metal::Device;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use thiserror::Error;
use winit::window::WindowAttributes;

mod screen;
mod window;

pub fn new() -> Result<impl Graphics, GraphicsError> {
Ok(Metal {
Expand All @@ -33,7 +34,7 @@ impl Graphics for Metal {
self,
profile: &Profile,
attrs: WindowAttributes,
) -> Result<Rc<Self::Screen>, GraphicsError> {
) -> Result<Arc<Self::Screen>, GraphicsError> {
todo!()
}
}
Expand Down
116 changes: 8 additions & 108 deletions gui/src/graphics/metal/screen.rs
Original file line number Diff line number Diff line change
@@ -1,133 +1,33 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::graphics::Screen;
use crate::rt::{Hook, RuntimeWindow};
use crate::vmm::VmmScreen;
use metal::{CAMetalLayer, Device, MetalLayer};
use objc::runtime::{Object, NO, YES};
use objc::{msg_send, sel, sel_impl};
use std::error::Error;
use std::ptr::null_mut;
use std::sync::Arc;
use metal::{Device, MetalLayer};
use thiserror::Error;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{DeviceId, ElementState, InnerSizeWriter, MouseButton, StartCause};
use winit::event_loop::ControlFlow;
use winit::window::WindowId;

/// Implementation of [`Screen`] using Metal.
///
/// Fields in this struct need to be dropped in a correct order.
pub struct MetalScreen {
view: *mut Object,
layer: MetalLayer,
device: Device,
}

impl MetalScreen {
pub fn new() -> Result<Self, MetalError> {
todo!()
}

pub fn from_screen(screen: &VmmScreen) -> Result<Self, MetalError> {
// Get Metal device.
let device = match Device::system_default() {
Some(v) => v,
None => return Err(MetalError::GetDeviceFailed),
};

// Setup Metal layer.
let layer = MetalLayer::new();

layer.set_device(&device);

// Set view layer.
let view = screen.view as *mut Object;

let _: () = unsafe { msg_send![view, setLayer:layer.as_ref()] };
let _: () = unsafe { msg_send![view, setWantsLayer:YES] };

Ok(Self {
view,
layer,
device,
})
}
}

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 RuntimeWindow for MetalScreen {
fn on_resized(&self, new: PhysicalSize<u32>) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_close_requested(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_focused(&self, gained: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_cursor_moved(
&self,
dev: DeviceId,
pos: PhysicalPosition<f64>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_cursor_left(&self, dev: DeviceId) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_mouse_input(
&self,
dev: DeviceId,
st: ElementState,
btn: MouseButton,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_scale_factor_changed(
&self,
new: f64,
sw: InnerSizeWriter,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_redraw_requested(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}
}

impl Hook for MetalScreen {
fn new_events(&self, cause: &StartCause) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn pre_window_event(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
Ok(Self { device })
}

fn window_destroyed(&self, id: WindowId) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn post_window_event(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}
/// # Safety
/// The returned [`MetalLayer`] must be dropped before this [`MetalScreen`].
pub unsafe fn create_layer(&self) -> MetalLayer {
let layer = MetalLayer::new();

fn about_to_wait(&self) -> Result<ControlFlow, Box<dyn Error + Send + Sync>> {
todo!()
layer.set_device(&self.device);
layer
}
}

Expand Down
122 changes: 122 additions & 0 deletions gui/src/graphics/metal/window.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use super::MetalScreen;
use crate::rt::{Hook, RuntimeWindow};
use metal::{CAMetalLayer, MetalLayer};
use objc::runtime::{Object, NO, YES};
use objc::{msg_send, sel, sel_impl};
use rwh05::{HasRawWindowHandle, RawWindowHandle};
use std::error::Error;
use std::ptr::null_mut;
use std::rc::Rc;
use std::sync::Arc;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{DeviceId, ElementState, InnerSizeWriter, MouseButton, StartCause};
use winit::event_loop::ControlFlow;
use winit::window::{Window, WindowId};

/// Implementation of [`RuntimeWindow`] and [`Hook`] for Metal.
pub struct MetalWindow {
view: *mut Object,
layer: MetalLayer,
window: Window,
screen: Arc<MetalScreen>,
}

impl MetalWindow {
pub fn new(
screen: &Arc<MetalScreen>,
window: Window,
) -> Result<Rc<Self>, Box<dyn Error + Send + Sync>> {
let layer = unsafe { screen.create_layer() };
let view = match window.raw_window_handle() {
RawWindowHandle::AppKit(v) => v.ns_view as *mut Object,
_ => unreachable!(),
};

let _: () = unsafe { msg_send![view, setLayer:layer.as_ref()] };
let _: () = unsafe { msg_send![view, setWantsLayer:YES] };

Ok(Rc::new(Self {
view,
layer,
window,
screen: screen.clone(),
}))
}
}

impl Drop for MetalWindow {
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 RuntimeWindow for MetalWindow {
fn on_resized(&self, new: PhysicalSize<u32>) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_close_requested(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_focused(&self, gained: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_cursor_moved(
&self,
dev: DeviceId,
pos: PhysicalPosition<f64>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_cursor_left(&self, dev: DeviceId) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_mouse_input(
&self,
dev: DeviceId,
st: ElementState,
btn: MouseButton,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_scale_factor_changed(
&self,
new: f64,
sw: InnerSizeWriter,
) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn on_redraw_requested(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}
}

impl Hook for MetalWindow {
fn new_events(&self, cause: &StartCause) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn pre_window_event(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn window_destroyed(&self, id: WindowId) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn post_window_event(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
}

fn about_to_wait(&self) -> Result<ControlFlow, Box<dyn Error + Send + Sync>> {
todo!()
}
}
12 changes: 8 additions & 4 deletions gui/src/graphics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
pub use self::engine::{new, GraphicsError};

use crate::profile::Profile;
use crate::rt::Hook;
use std::rc::Rc;
use std::sync::Arc;
use winit::window::WindowAttributes;

#[cfg_attr(target_os = "macos", path = "metal/mod.rs")]
Expand All @@ -16,16 +15,21 @@ pub trait Graphics: Sized + 'static {
type Screen: Screen;

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

/// Currently this method was designed to run only once per application lifetime.
fn create_screen(
self,
profile: &Profile,
attrs: WindowAttributes,
) -> Result<Rc<Self::Screen>, GraphicsError>;
) -> Result<Arc<Self::Screen>, GraphicsError>;
}

pub trait PhysicalDevice: Sized {
fn name(&self) -> &str;
}

/// Encapsulates a platform-specific window for drawing a VM screen.
pub trait Screen: Hook {}
///
/// This trait act as a thin layer for graphics engine for the VMM to use. At compile-time this
/// layer will be optimized out and aggressively inlined the same as Hypervisor trait.
pub trait Screen: Send + Sync + 'static {}
18 changes: 11 additions & 7 deletions gui/src/graphics/vulkan/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use self::screen::VulkanScreen;
use self::window::VulkanWindow;
use super::Graphics;
use crate::profile::Profile;
use crate::rt::{create_window, raw_display_handle, RuntimeError};
Expand All @@ -9,11 +10,12 @@ use ash::{Entry, Instance};
use ash_window::enumerate_required_extensions;
use std::ffi::CStr;
use std::mem::ManuallyDrop;
use std::rc::Rc;
use std::sync::Arc;
use thiserror::Error;
use winit::window::WindowAttributes;

mod screen;
mod window;

pub fn new() -> Result<impl Graphics, GraphicsError> {
// Get required extensions for window.
Expand Down Expand Up @@ -114,9 +116,14 @@ impl Graphics for Vulkan {
self,
profile: &Profile,
attrs: WindowAttributes,
) -> Result<Rc<Self::Screen>, GraphicsError> {
create_window(attrs, move |w| VulkanScreen::new(self, profile, w))
.map_err(GraphicsError::CreateWindow)
) -> Result<Arc<Self::Screen>, GraphicsError> {
let screen = VulkanScreen::new(self, profile).map(Arc::new)?;
let window = create_window(attrs, |w| VulkanWindow::new(&screen, w))
.map_err(GraphicsError::CreateWindow)?;

crate::rt::push_hook(window);

Ok(screen)
}
}

Expand Down Expand Up @@ -157,9 +164,6 @@ pub enum GraphicsError {
#[error("no Vulkan device supports graphics operations with Vulkan 1.3")]
NoSuitableDevice,

#[error("couldn't find suitable queue")]
NoQueue,

#[error("couldn't create a logical device")]
CreateDevice(#[source] ash::vk::Result),

Expand Down
Loading