use hai_pal::env::{get_hai_env, RenderingBackend, RenderingPresentMode};
use log::info;
use std::sync::Arc;
use wgpu::{Device, Instance, Queue, Surface, SurfaceConfiguration};
use winit::dpi::{LogicalSize, Size};
use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget};
use winit::window::WindowBuilder;
use winit::{dpi::PhysicalSize, window::Window};
use crate::user_event::UserEvent;
pub fn create_eventloop() -> EventLoop<UserEvent> {
let event_loop: EventLoop<UserEvent> = EventLoopBuilder::with_user_event().build().unwrap();
event_loop
}
pub fn create_window(event_loop: &EventLoopWindowTarget<UserEvent>) -> Arc<Window> {
let window = WindowBuilder::new()
.with_inner_size(Size::Logical(LogicalSize::new(1280., 720.)))
.with_resizable(false)
.with_visible(false)
.build(event_loop)
.unwrap();
#[cfg(all(feature = "web", target_arch = "wasm32"))]
{
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(window.canvas()))
.ok()
})
.expect("couldn't append canvas to document body");
}
Arc::new(window)
}
pub fn create_wgpu_surface(
window: &Arc<Window>,
) -> (
Arc<Instance>,
Arc<Surface<'static>>,
Arc<Device>,
Arc<Queue>,
SurfaceConfiguration,
) {
#[cfg(not(feature = "web"))]
let (instance, surface, device, queue, config) =
futures::executor::block_on(create_surface_inner(window, &window.inner_size()));
#[cfg(feature = "web")]
let (surface, device, queue, config) =
{ pollster::block_on(create_surface_inner(&window, &window.inner_size())) };
let instance = Arc::new(instance);
let surface = Arc::new(surface);
let device = Arc::new(device);
let queue = Arc::new(queue);
(instance, surface, device, queue, config)
}
pub(self) async fn create_surface_inner(
window: &Arc<Window>,
size: &PhysicalSize<u32>,
) -> (
Instance,
Surface<'static>,
Device,
Queue,
SurfaceConfiguration,
) {
let backends = match get_hai_env().backend {
RenderingBackend::Auto => wgpu::Backends::all(),
RenderingBackend::Vulkan => wgpu::Backends::VULKAN,
RenderingBackend::Metal => wgpu::Backends::METAL,
RenderingBackend::DX12 => wgpu::Backends::DX12,
RenderingBackend::WebGPU => wgpu::Backends::BROWSER_WEBGPU,
RenderingBackend::GLES => wgpu::Backends::GL,
};
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
dx12_shader_compiler: wgpu::Dx12Compiler::Fxc,
..Default::default()
});
let surface = instance
.create_surface(window.clone())
.expect("Failed to create surface.");
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
})
.await
.expect("No suitable GPU adapters found on the system.");
#[cfg(not(feature = "web"))]
{
let adapter_info = adapter.get_info();
info!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
}
let required_limits = if !cfg!(feature = "web") {
wgpu::Limits::default()
} else {
wgpu::Limits::downlevel_webgl2_defaults()
};
let required_features = if adapter
.features()
.contains(wgpu::Features::BGRA8UNORM_STORAGE)
{
wgpu::Features::default()
| wgpu::Features::BGRA8UNORM_STORAGE
| wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
} else {
wgpu::Features::default()
};
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
required_features,
required_limits,
label: None,
},
None, )
.await
.expect("Unable to find a suitable GPU adapter.");
let caps = surface.get_capabilities(&adapter);
let format = *caps
.formats
.iter()
.find(|f| f.is_srgb())
.expect("Cannot find a proper surface format.");
info!("Surface format: {:?}", format);
let alpha_mode = *caps
.alpha_modes
.get(0)
.expect("Cannot find a proper surface alpha mode.");
info!("Alpha mode: {:?}", alpha_mode);
#[cfg(not(feature = "web"))]
let present_mode = match get_hai_env().present_mode {
RenderingPresentMode::Recommended => {
if caps.present_modes.contains(&wgpu::PresentMode::Mailbox) {
wgpu::PresentMode::Mailbox
} else if caps.present_modes.contains(&wgpu::PresentMode::FifoRelaxed) {
wgpu::PresentMode::FifoRelaxed
} else {
wgpu::PresentMode::Fifo
}
}
RenderingPresentMode::AutoVsync => wgpu::PresentMode::AutoVsync,
RenderingPresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync,
};
#[cfg(feature = "web")]
let present_mode = wgpu::PresentMode::AutoVsync;
info!("Present mode: {:?}", present_mode);
let usage = if adapter.get_info().backend == wgpu::Backend::Gl {
wgpu::TextureUsages::RENDER_ATTACHMENT
} else {
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC
};
let config = wgpu::SurfaceConfiguration {
usage,
format,
width: size.width,
height: size.height,
present_mode,
alpha_mode,
view_formats: vec![],
desired_maximum_frame_latency: get_hai_env().desired_maximum_frame_latency,
};
surface.configure(&device, &config);
(instance, surface, device, queue, config)
}