taika/
lib.rs

1//! A low-cost abstraction layer on top of [wgpu](https://crates.io/crates/wgpu) and [winit](https://crates.io/crates/winit) to make their APIs more ergonomic.
2//!
3//! # State
4//! Taika is early in development, meaning large API changes are bound to happen. However it is currently being used for a production ready game
5//! which serves as a good testbed for the library.
6//!
7//! # Goals
8//! 1. Simplify window creation
9//! 2. Introduce "RenderPasses" and "RenderPipelines", which are common tropes in game
10//!    engines.
11//! 3. Make API changes in WGPU and Winit less frustrating by providing a semi-stable API. API
12//!    changes will still happen though.
13//! 4. Give full access to WGPU
14//!
15//! In addition to these goals taika also includes some common utilities mainly targeted towards
16//! game-development. Taika also includes a super basic form of asset management. It is designed to
17//! be built upon, not to be a full-fledged asset management system.
18//!
19//! ## What taika doesn't do:
20//! - Input-handling, you can do this yourself by listening to the winit events that are passed
21//!   through to your event-handler
22//! - Audio, use other libraries
23//! - Make rendering easy. You still have to write shaders, and implement the drawable trait, to
24//!   actually issue the drawcalls to the GPU. Taika doesn't make any drawcalls by itself
25//!
26//! # Notes
27//! - The naming of [`rendering::RenderPass`] and [`rendering::RenderPipeline`] is a bit confusing at they are also used in
28//!   wgpu.
29//! - No examples currently!
30//!
31//!
32//! # Getting Started
33//! Use the [`EventLoop`] struct to get started
34use std::sync::{Arc, Mutex};
35pub use wgpu;
36pub use winit;
37
38use winit::event_loop::ControlFlow;
39
40mod app_handler;
41pub mod asset_management;
42pub mod events;
43pub mod math;
44pub mod rendering;
45pub mod window;
46
47static QUIT: Mutex<bool> = Mutex::new(false);
48
49/// Settings for the renderer
50#[derive(Debug, Clone)]
51pub struct RenderSettings {
52    /// Whether or not to enable vsync. VSYNC on might lead to some latency
53    pub vsync: bool,
54    /// Features for wgpu
55    pub required_features: wgpu::Features,
56    /// Maximun allowed framerate, only applies is `vsync` is false
57    pub max_framerate: Option<u16>,
58}
59
60impl Default for RenderSettings {
61    fn default() -> Self {
62        Self {
63            vsync: true,
64            required_features: wgpu::Features::empty(),
65            max_framerate: None,
66        }
67    }
68}
69
70/// Request the event loop to quit, closing all windows
71pub fn request_quit() {
72    let mut quit = QUIT.lock().unwrap();
73    *quit = true;
74}
75
76/// Used to create windows and run the main loop of the application.
77pub struct EventLoop<'a> {
78    handle: winit::event_loop::EventLoop<()>,
79    windows: Vec<Arc<Mutex<window::Window<'a>>>>,
80    pub(crate) render_settings: RenderSettings,
81}
82
83impl<'a> EventLoop<'a> {
84    /// Initializes a new taika event loop.
85    /// The event loop is used to create windows and run the main loop of the application.
86    pub fn new(
87        render_settings: RenderSettings,
88    ) -> Result<EventLoop<'a>, winit::error::EventLoopError> {
89        let event_loop = winit::event_loop::EventLoop::new()?;
90        event_loop.set_control_flow(ControlFlow::Wait);
91        Ok(EventLoop {
92            handle: event_loop,
93            windows: Vec::new(),
94            render_settings,
95        })
96    }
97
98    /// Returns the underlying winit event loop
99    pub fn get_event_loop(&self) -> &winit::event_loop::EventLoop<()> {
100        &self.handle
101    }
102
103    /// Runs the event loop. This function will block until all windows are closed.
104    pub async fn run(self) {
105        let instance = wgpu::Instance::default();
106        // now lets init our windows' surfaces
107        let adapter = instance
108            .request_adapter(&wgpu::RequestAdapterOptions {
109                power_preference: wgpu::PowerPreference::HighPerformance,
110                force_fallback_adapter: false,
111                compatible_surface: None,
112            })
113            .await;
114        let adapter = match adapter {
115            Ok(adapter) => adapter,
116            Err(e) => {
117                eprintln!(
118                    "No suitable adapter found, taika will now exit. Error:\n{}",
119                    e
120                );
121                return;
122            }
123        };
124        let (device, queue) = adapter
125            .request_device(&wgpu::DeviceDescriptor {
126                label: None,
127                required_features: self.render_settings.required_features,
128                required_limits: wgpu::Limits::downlevel_defaults()
129                    .using_resolution(adapter.limits()),
130                memory_hints: wgpu::MemoryHints::Performance,
131                trace: wgpu::Trace::Off,
132            })
133            .await
134            .expect("Failed to create device");
135        let windows = self.windows.clone();
136        let device = Arc::new(Mutex::new(device));
137        let queue = Arc::new(Mutex::new(queue));
138        let mut state = app_handler::AppState {
139            device,
140            queue,
141            windows,
142            adapter,
143            instance,
144            render_settings: self.render_settings.clone(),
145        };
146        self.handle.run_app(&mut state).unwrap()
147    }
148}