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}