mew/lib.rs
1#![doc(html_logo_url = "https://64-tesseract.ftp.sh/tesseract.gif", html_favicon_url = "https://64-tesseract.ftp.sh/tesseract.gif")]
2#![doc = include_str!("../README.md")]
3
4use std::{
5 any::TypeId,
6 cell::RefCell,
7 collections::BTreeMap,
8};
9use thiserror::Error;
10
11pub use wgpu;
12#[cfg(feature = "poll")]
13pub use pollster;
14#[cfg(feature = "winit")]
15pub use winit;
16
17pub mod doc_example;
18
19mod macromath;
20pub mod bindgroup;
21pub mod buffer;
22pub mod pipeline;
23pub mod sampler;
24pub mod shaderprimitive;
25pub mod shaderstruct;
26pub mod texture;
27//pub mod typemap;
28pub mod vertexformat;
29pub mod vertexstruct;
30#[cfg(feature = "winit")]
31pub mod winitutils;
32
33pub mod prelude {
34 pub use crate::{
35 bindgroup::*,
36 buffer::*,
37 pipeline::*,
38 sampler::*,
39 shaderprimitive,
40 shaderstruct::*,
41 texture::*,
42 vertexformat,
43 vertexstruct::*,
44 *,
45 };
46 #[cfg(feature = "winit")]
47 pub use crate::winitutils::*;
48}
49
50
51/*
52/// The master object that manages everything `wgpu`-related.
53///
54/// On construction it initializes the instance, adapter, device, and queue
55/// using some default settings so you don't need to worry about that repetitive
56/// boilerplate. Not all options are customizable but it should work well
57/// enough.
58///
59/// The render context also keeps track of your buffer, bind group, texture,
60/// and pipeline definition types, so you can construct the from a central
61/// location. Since regular macros don't support changing identifiers, this is
62/// implemented by type parameters.
63///
64/// ```
65/// render_context! { Context {
66/// // List of `wgpu::Features` for the device
67/// features: [MULTIVIEW, VERTEX_WRITABLE_STORAGE], // idk I usually just leave these blank
68/// // Map of values to override from `wgpu::Limits` defaults for the device
69/// limits: {max_vertex_attributes: 17, max_vertex_buffers: 4}, // same here, random values - definitely don't use
70/// // Numbered (and ordered!) list of your bind group definitions
71/// bind_groups: [
72/// 0 => GroupOne,
73/// 1 => GroupThree,
74/// 2 => GroupNegativeSixtyNine,
75/// ],
76/// // Also numbered (and also ordered!) list of your pipeline definitions
77/// pipelines: [
78/// 0 => MainPipeline,
79/// 1 => NotMainPipeline,
80/// ],
81/// } }
82/// ```
83#[macro_export]
84macro_rules! render_context {
85 { $struct:ident {
86 features: [
87 $( $feature:ident ),* $(,)?
88 ],
89 limits: {
90 $( $limit:ident : $limitval:expr ),* $(,)?
91 },
92 bind_groups: [
93 $( $bindgroupnum:tt => $bindgroup:ty ),* $(,)?
94 ],
95 pipelines: [
96 $( $pipelinenum:tt => $pipeline:ty ),* $(,)?
97 ] $(,)?
98 } } => {
99 typemap! { BindGroupLayoutTypes [
100 $( $bindgroupnum => $crate::bindgroup::MewBindGroupLayout<$bindgroup> , )*
101 ] }
102
103 typemap! { PipelineLayoutTypes [
104 $( $pipelinenum => $crate::pipeline::MewPipelineLayout<$pipeline> , )*
105 ] }
106
107 pub struct $struct {
108 pub instance: $crate::wgpu::Instance,
109 pub adapter: $crate::wgpu::Adapter,
110 pub device: $crate::wgpu::Device,
111 pub queue: $crate::wgpu::Queue,
112 bind_group_layouts: BindGroupLayoutTypes,
113 pipeline_layouts: PipelineLayoutTypes,
114 }
115
116 impl $struct {
117 pub async fn new(backends: $crate::wgpu::Backends) -> Result<Self, Box<dyn ::std::error::Error>> {
118 let instance = $crate::wgpu::Instance::new(&wgpu::InstanceDescriptor {
119 backends,
120 flags: $crate::wgpu::InstanceFlags::empty(),
121 ..Default::default()
122 });
123
124 Self::new_with_instance(instance).await
125 }
126
127 pub async fn new_with_instance(instance: $crate::wgpu::Instance) -> Result<Self, Box<dyn ::std::error::Error>> {
128 let adapter = instance.request_adapter(
129 &$crate::wgpu::RequestAdapterOptions {
130 power_preference: $crate::wgpu::PowerPreference::default(),
131 compatible_surface: None,
132 force_fallback_adapter: false,
133 },
134 ).await.inspect_err(|e| eprintln!("Could not get display adapter: {e}"))?;
135
136 let (device, queue) = adapter.request_device(
137 &$crate::wgpu::DeviceDescriptor {
138 label: Some("Device"),
139 required_features: $crate::wgpu::Features::empty() $( | $crate::wgpu::Features::$feature )*,
140 required_limits: $crate::wgpu::Limits {
141 $( $limit: $limitval, )*
142 ..Default::default()
143 },
144 memory_hints: Default::default(),
145 trace: $crate::wgpu::Trace::Off,
146 }
147 ).await.inspect_err(|e| eprintln!("Could not get display device: {e}"))?;
148
149 let bind_group_layouts = BindGroupLayoutTypes (
150 $( {
151 let desc = <$bindgroup>::layout_desc();
152 let layout = device.create_bind_group_layout(&desc);
153 $crate::bindgroup::MewBindGroupLayout::new(layout)
154 }, )*
155 );
156
157 let pipeline_layouts = PipelineLayoutTypes (
158 $( {
159 let bind_groups = <$pipeline>::get_layouts_from_refs(&bind_group_layouts);
160 let bind_groups_arr = <$pipeline>::bind_group_layouts(&bind_groups);
161 let desc = <$pipeline>::layout_desc(&bind_groups_arr);
162 let layout = device.create_pipeline_layout(&desc);
163 $crate::pipeline::MewPipelineLayout::new(layout)
164 }, )*
165 );
166
167 Ok(Self {
168 instance,
169 adapter,
170 device,
171 queue,
172 bind_group_layouts,
173 pipeline_layouts,
174 })
175 }
176
177 pub fn new_buffer<BUFFER: $crate::buffer::MewBuffer>(&self, inner_size: u64) -> BUFFER {
178 let raw_buffer = self.device.create_buffer(&BUFFER::buffer_desc(inner_size));
179 BUFFER::new(raw_buffer)
180 }
181
182 pub fn new_sampler<SAMPLER: $crate::sampler::MewSampler>(&self) -> SAMPLER {
183 let raw_sampler = self.device.create_sampler(&SAMPLER::buffer_desc());
184 SAMPLER::new(raw_sampler)
185 }
186
187 pub fn new_texture<TEXTURE: $crate::texture::MewTexture>(&self, inner_size: (u32, u32, u32), format: $crate::wgpu::TextureFormat) -> TEXTURE {
188 let raw_texture = self.device.create_texture(&TEXTURE::buffer_desc(inner_size, format));
189 TEXTURE::new(raw_texture)
190 }
191
192 pub fn new_bind_group<BIND: $crate::bindgroup::MewBindGroup>(&self, buffers: BIND::BufferSet) -> BIND
193 where
194 BindGroupLayoutTypes: AsRef<$crate::bindgroup::MewBindGroupLayout<BIND>>
195 {
196 let raw_bind_group = {
197 let layout: &$crate::bindgroup::MewBindGroupLayout<BIND> = self.bind_group_layouts.as_ref();
198 let entries = BIND::bind_group_entries(&buffers);
199 let desc = BIND::bind_group_desc(layout, &entries);
200 self.device.create_bind_group(&desc)
201 };
202 BIND::new(raw_bind_group, buffers)
203 }
204
205 pub fn new_pipeline<PIPE: $crate::pipeline::MewPipeline>(
206 &self,
207 surface_fmt: Option<$crate::wgpu::TextureFormat>,
208 shader: &$crate::wgpu::ShaderModule,
209 vertex_entry: Option<&str>,
210 fragment_entry: Option<&str>,
211 ) -> PIPE
212 where
213 PipelineLayoutTypes: AsRef<$crate::pipeline::MewPipelineLayout<PIPE>>
214 {
215 let raw_pipeline = {
216 let layout: &$crate::pipeline::MewPipelineLayout<PIPE> = self.pipeline_layouts.as_ref();
217 let targets = PIPE::fragment_targets(surface_fmt);
218 let desc = &PIPE::pipeline_desc(layout, shader, vertex_entry, fragment_entry, &targets);
219 self.device.create_render_pipeline(desc)
220 };
221 PIPE::new(raw_pipeline)
222 }
223 }
224
225 impl $crate::MewRenderContext for $struct {
226 fn instance(&self) -> &wgpu::Instance {
227 &self.instance
228 }
229
230 fn adapter(&self) -> &wgpu::Adapter {
231 &self.adapter
232 }
233
234 fn device(&self) -> &wgpu::Device {
235 &self.device
236 }
237
238 fn queue(&self) -> &wgpu::Queue {
239 &self.queue
240 }
241 }
242 };
243}
244*/
245
246
247#[derive(Clone, Debug, Error)]
248pub enum BuildContextError {
249 #[error(transparent)]
250 Adapter(#[from] wgpu::RequestAdapterError),
251 #[error(transparent)]
252 Device(#[from] wgpu::RequestDeviceError),
253}
254
255
256#[must_use]
257#[derive(Clone, Debug)]
258pub struct RenderContextBuilder {
259 instance: Result<wgpu::Instance, wgpu::Backends>,
260 compatible_surface: Option<std::sync::Arc<wgpu::Surface<'static>>>,
261 features: wgpu::Features,
262 limits: wgpu::Limits,
263}
264
265impl RenderContextBuilder {
266 pub fn new_with_instance(instance: wgpu::Instance) -> Self {
267 #[allow(unused_mut)]
268 let mut builder = Self {
269 instance: Ok(instance),
270 compatible_surface: None,
271 features: wgpu::Features::default(),
272 limits: wgpu::Limits::default(),
273 };
274 #[cfg(feature = "webgl")]
275 builder.with_limits(wgpu::Limits::downlevel_webgl2_defaults());
276 builder
277 }
278
279 pub fn new_with_backends(backends: wgpu::Backends) -> Self {
280 #[allow(unused_mut)]
281 let mut builder = Self {
282 instance: Err(backends),
283 compatible_surface: None,
284 features: wgpu::Features::default(),
285 limits: wgpu::Limits::default(),
286 };
287 #[cfg(feature = "webgl")]
288 builder.with_limits(wgpu::Limits::downlevel_webgl2_defaults());
289 builder
290 }
291
292 pub fn with_compatible_surface(&mut self, surface: Option<std::sync::Arc<wgpu::Surface<'static>>>) -> &mut Self {
293 self.compatible_surface = surface;
294 self
295 }
296
297 pub fn with_features(&mut self, features: wgpu::Features) -> &mut Self {
298 self.features = features;
299 self
300 }
301
302 pub fn with_limits(&mut self, limits: wgpu::Limits) -> &mut Self {
303 self.limits = limits;
304 self
305 }
306
307 pub fn get_instance(&mut self) -> wgpu::Instance {
308 self.instance = Ok(self.instance.clone().unwrap_or_else(|backends| wgpu::Instance::new(&wgpu::InstanceDescriptor {
309 backends,
310 ..Default::default()
311 })));
312 self.instance.clone().unwrap()
313 }
314
315 pub async fn build(mut self) -> Result<RenderContext, BuildContextError> {
316 let instance = self.get_instance();
317
318 let adapter = instance.request_adapter(
319 &wgpu::RequestAdapterOptions {
320 power_preference: wgpu::PowerPreference::default(),
321 compatible_surface: self.compatible_surface.as_deref(),
322 force_fallback_adapter: false,
323 },
324 ).await?;
325
326 let (device, queue) = adapter.request_device(
327 &wgpu::DeviceDescriptor {
328 label: Some("Device"),
329 required_features: self.features,
330 required_limits: self.limits,
331 memory_hints: Default::default(),
332 trace: wgpu::Trace::Off,
333 }
334 ).await?;
335
336 Ok(RenderContext::new(instance, adapter, device, queue))
337 }
338
339 #[cfg(feature = "poll")]
340 pub fn build_poll(self) -> Result<RenderContext, BuildContextError> {
341 pollster::block_on(self.build())
342 }
343
344 #[cfg(all(feature = "winit", any(feature = "webgl", feature = "poll")))]
345 pub fn deferred(self) -> winitutils::DeferredContext {
346 winitutils::DeferredContext::new(self)
347 }
348}
349
350
351#[derive(Debug)]
352struct LayoutMaps {
353 bind_groups: RefCell<BTreeMap<TypeId, wgpu::BindGroupLayout>>,
354 pipelines: RefCell<BTreeMap<TypeId, wgpu::PipelineLayout>>,
355}
356
357impl LayoutMaps {
358 fn new() -> Self {
359 Self {
360 bind_groups: RefCell::new(BTreeMap::new()),
361 pipelines: RefCell::new(BTreeMap::new()),
362 }
363 }
364}
365
366
367#[derive(Debug)]
368pub struct RenderContext {
369 pub instance: wgpu::Instance,
370 pub adapter: wgpu::Adapter,
371 pub device: wgpu::Device,
372 pub queue: wgpu::Queue,
373 // TODO: Potentially put in Rc
374 layouts: LayoutMaps,
375}
376
377impl RenderContext {
378 pub fn new(instance: wgpu::Instance, adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue) -> Self {
379 Self {
380 instance,
381 adapter,
382 device,
383 queue,
384 layouts: LayoutMaps::new(),
385 }
386 }
387
388
389 pub fn new_buffer<BUFFER: buffer::MewBuffer>(&self, inner_size: u64) -> BUFFER {
390 let raw_buffer = self.device.create_buffer(&BUFFER::buffer_desc(inner_size));
391 BUFFER::new(raw_buffer)
392 }
393
394 pub fn new_sampler<SAMPLER: sampler::MewSampler>(&self) -> SAMPLER {
395 let raw_sampler = self.device.create_sampler(&SAMPLER::buffer_desc());
396 SAMPLER::new(raw_sampler)
397 }
398
399 pub fn new_texture<TEXTURE: texture::MewTexture>(&self, inner_size: (u32, u32, u32), format: wgpu::TextureFormat) -> TEXTURE {
400 let raw_texture = self.device.create_texture(&TEXTURE::buffer_desc(inner_size, format));
401 TEXTURE::new(raw_texture)
402 }
403
404 fn get_bind_group_layout_manual(&self, type_id: TypeId, desc: wgpu::BindGroupLayoutDescriptor<'static>) -> wgpu::BindGroupLayout {
405 let mut layouts = self.layouts.bind_groups.borrow_mut();
406 layouts.entry(type_id).or_insert_with(|| self.device.create_bind_group_layout(&desc)).clone()
407 }
408
409 fn get_bind_group_layout<BIND: bindgroup::MewBindGroup + 'static>(&self) -> bindgroup::MewBindGroupLayout<BIND> {
410 let layout = self.get_bind_group_layout_manual(TypeId::of::<BIND>(), BIND::layout_desc());
411 bindgroup::MewBindGroupLayout::new(layout)
412 }
413
414 pub fn new_bind_group<BIND: bindgroup::MewBindGroup + 'static>(&self, buffers: BIND::BufferSet) -> BIND {
415 let raw_bind_group = {
416 let layout = self.get_bind_group_layout::<BIND>();
417 let entries = BIND::bind_group_entries(&buffers);
418 let desc = BIND::bind_group_desc(&layout, &entries);
419 self.device.create_bind_group(&desc)
420 };
421 BIND::new(raw_bind_group, buffers)
422 }
423
424
425 fn get_pipeline_layout<PIPE: pipeline::MewPipeline + 'static>(&self) -> pipeline::MewPipelineLayout<PIPE> {
426 let layout = {
427 let mut layouts = self.layouts.pipelines.borrow_mut();
428 layouts.entry(TypeId::of::<PIPE>()).or_insert_with(|| {
429 let bind_group_descs = PIPE::bind_group_layout_types_array();
430 let bind_group_layouts = PIPE::map_bind_group_array(bind_group_descs, |(type_id, desc)| self.get_bind_group_layout_manual(type_id, desc));
431 let bind_group_refs = PIPE::bind_group_ref_array(&bind_group_layouts);
432 let desc = PIPE::layout_desc(&bind_group_refs);
433 self.device.create_pipeline_layout(&desc)
434 }).clone()
435 };
436 pipeline::MewPipelineLayout::new(layout)
437 }
438
439 pub fn new_pipeline<PIPE: pipeline::MewPipeline + 'static>(
440 &self,
441 surface_fmt: Option<wgpu::TextureFormat>,
442 shader: &wgpu::ShaderModule,
443 vertex_entry: Option<&str>,
444 fragment_entry: Option<&str>,
445 ) -> PIPE {
446 let raw_pipeline = {
447 let layout = self.get_pipeline_layout::<PIPE>();
448 let targets = PIPE::fragment_targets(surface_fmt);
449 let desc = &PIPE::pipeline_desc(&layout, shader, vertex_entry, fragment_entry, &targets);
450 self.device.create_render_pipeline(desc)
451 };
452 PIPE::new(raw_pipeline)
453 }
454}