use super::vulkan_native_interop::{AndroidVulkanInterface, VkAndroidSurfaceCreateInfoKHR};
use crate::platform::skia_platform::SurfaceViewHandle;
use ferroui_base::logging::{LogEventLevel, Logger};
use ferroui_base::platform::surfaces::IPlatformRenderSurface;
use ferroui_base::PixelSize;
use ferroui_controls::platform::INativePlatformHandleSurface;
use ferroui_vulkan::ash::{self, vk};
use ferroui_vulkan::{
IVulkanInstance, IVulkanKhrSurfacePlatformSurface, IVulkanKhrSurfacePlatformSurfaceFactory,
IVulkanPlatformGraphicsContext, VkGetInstanceProcAddressDelegate, VulkanException, VulkanOptions,
VulkanPlatformGraphics, VulkanPlatformSpecificOptions,
};
use std::any::Any;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::Arc;
const VULKAN_LIBRARY: &str = "libvulkan.so";
pub(crate) struct VulkanSupport;
impl VulkanSupport {
fn vk_get_instance_proc_addr() -> Result<VkGetInstanceProcAddressDelegate, String> {
let entry = unsafe { ash::Entry::load_from(VULKAN_LIBRARY) }.map_err(|error| error.to_string())?;
let entry = Arc::new(entry);
Ok(Arc::new(move |instance: isize, name: &str| {
let Ok(name) = CString::new(name) else { return 0 };
let address = unsafe {
(entry.static_fn().get_instance_proc_addr)(vk::Handle::from_raw(instance as u64), name.as_ptr())
};
address.map_or(0, |address| address as usize as isize)
}))
}
pub fn try_initialize(options: Option<VulkanOptions>) -> Option<Arc<VulkanPlatformGraphics>> {
let get_proc_address = match Self::vk_get_instance_proc_addr() {
Ok(get_proc_address) => get_proc_address,
Err(e) => {
if let Some(logger) = Logger::try_get(LogEventLevel::Error, "Vulkan") {
logger.log_with_values(None, "Unable to initialize Vulkan rendering: {0}", &[&e]);
}
return None;
}
};
let mut platform_options = VulkanPlatformSpecificOptions {
required_instance_extensions: vec!["VK_KHR_android_surface".to_string()],
get_proc_address_delegate: Some(get_proc_address),
..Default::default()
};
platform_options.set_khr_surface_factory(Arc::new(VulkanSurfaceFactory));
VulkanPlatformGraphics::try_create(&options.unwrap_or_default(), platform_options)
}
fn create_android_surface(handle: isize, instance: &dyn IVulkanInstance) -> Result<u64, VulkanException> {
if handle == 0 {
panic!("Surface handle can't be 0x0 (Parameter 'handle')");
}
let vulkan_android = AndroidVulkanInterface::new(instance);
let create_info = VkAndroidSurfaceCreateInfoKHR::default().window(handle as *mut vk::ANativeWindow);
let (result, surface) = vulkan_android.vk_create_android_surface_khr(instance.handle(), &create_info);
VulkanException::throw_on_error("vkCreateAndroidSurfaceKHR", result)?;
Ok(surface)
}
}
pub(crate) struct VulkanSurfaceFactory;
fn handle_surface(surface: &dyn IPlatformRenderSurface) -> Option<&SurfaceViewHandle> {
surface.as_any().downcast_ref::<SurfaceViewHandle>()
}
impl IVulkanKhrSurfacePlatformSurfaceFactory for VulkanSurfaceFactory {
fn can_render_to_surface(
&self,
_context: &dyn IVulkanPlatformGraphicsContext,
surface: &Arc<dyn IPlatformRenderSurface>,
) -> bool {
handle_surface(&**surface).is_some()
}
fn create_surface(
&self,
_context: &dyn IVulkanPlatformGraphicsContext,
handle: &Arc<dyn IPlatformRenderSurface>,
) -> Rc<dyn IVulkanKhrSurfacePlatformSurface> {
Rc::new(AndroidVulkanSurface::new(handle.clone()))
}
}
struct AndroidVulkanSurface {
handle: Arc<dyn IPlatformRenderSurface>,
}
impl AndroidVulkanSurface {
fn new(handle: Arc<dyn IPlatformRenderSurface>) -> Self {
Self { handle }
}
fn handle(&self) -> &SurfaceViewHandle {
handle_surface(&*self.handle).expect("the factory creates the surface for a handle surface only")
}
}
impl IPlatformRenderSurface for AndroidVulkanSurface {
fn is_ready(&self) -> bool {
self.handle.is_ready()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl IVulkanKhrSurfacePlatformSurface for AndroidVulkanSurface {
fn scaling(&self) -> f64 {
INativePlatformHandleSurface::scaling(self.handle())
}
fn size(&self) -> PixelSize {
INativePlatformHandleSurface::size(self.handle())
}
fn create_surface(&self, context: &dyn IVulkanPlatformGraphicsContext) -> u64 {
let window = self.handle().native_window();
let handle = window.as_ref().map_or(0, |window| window.handle());
match VulkanSupport::create_android_surface(handle, &*context.instance()) {
Ok(surface) => surface,
Err(error) => panic!("{error}"),
}
}
fn dispose(&self) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_loader_is_opened_by_the_name_of_the_reference() {
assert_eq!(VULKAN_LIBRARY, "libvulkan.so");
}
}