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
4pub use wgpu;
5#[cfg(feature = "sync")]
6pub use pollster;
7#[cfg(feature = "winit")]
8pub use winit;
9
10mod macromath;
11pub mod bindgroup;
12pub mod buffer;
13pub mod pipeline;
14pub mod sampler;
15pub mod shaderprimitive;
16pub mod shaderstruct;
17pub mod texture;
18pub mod typemap;
19pub mod vertexformat;
20pub mod vertexstruct;
21#[cfg(feature = "winit")]
22pub mod winitutils;
23
24pub mod prelude {
25    pub use crate::{
26        bindgroup::*,
27        buffer::*,
28        pipeline::*,
29        sampler::*,
30        shaderprimitive,
31        shaderstruct::*,
32        texture::*,
33        vertexformat,
34        vertexstruct::*,
35        *,
36    };
37    #[cfg(feature = "winit")]
38    pub use crate::winitutils::*;
39}
40
41
42/// The master object that manages everything `wgpu`-related.
43///
44/// On construction it initializes the instance, adapter, device, and queue
45/// using some default settings so you don't need to worry about that repetitive
46/// boilerplate. Not all options are customizable but it should work well
47/// enough.
48///
49/// Since construction is handled with `async` functions for whatever ungodly
50/// reason, `pollster` is a dependency by default. You can disable it if you
51/// already use `tokio` or something similar.
52///
53/// The render context also keeps track of your buffer, bind group, texture,
54/// and pipeline definition types, so you can construct the from a central
55/// location. Since regular macros don't support changing identifiers, this is
56/// implemented by type parameters.
57///
58/// ```
59/// render_context! { Context {
60///     // List of `wgpu::Features` for the device
61///     features: [MULTIVIEW, VERTEX_WRITABLE_STORAGE],  // idk I usually just leave these blank
62///     // Map of values to override from `wgpu::Limits` defaults for the device
63///     limits: {max_vertex_attributes: 17, max_vertex_buffers: 4},  // same here, random values - definitely don't use
64///     // Numbered (and ordered!) list of your bind group definitions
65///     bind_groups: [
66///         0 => GroupOne,
67///         1 => GroupThree,
68///         2 => GroupNegativeSixtyNine,
69///     ],
70///     // Also numbered (and also ordered!) list of your pipeline definitions
71///     pipelines: [
72///         0 => MainPipeline,
73///         1 => NotMainPipeline,
74///     ],
75/// } }
76/// ```
77#[macro_export]
78macro_rules! render_context {
79    { $struct:ident {
80        features: [
81            $( $feature:ident ),* $(,)?
82        ],
83        limits: {
84            $( $limit:ident : $limitval:expr ),* $(,)?
85        },
86        bind_groups: [
87            $( $bindgroupnum:tt => $bindgroup:ty ),* $(,)?
88        ],
89        pipelines: [
90            $( $pipelinenum:tt => $pipeline:ty ),* $(,)?
91        ] $(,)?
92    } } => {
93        typemap! { BindGroupLayoutTypes [
94            $( $bindgroupnum => $crate::bindgroup::MewBindGroupLayout<$bindgroup> , )*
95        ] }
96
97        typemap! { PipelineLayoutTypes [
98            $( $pipelinenum => $crate::pipeline::MewPipelineLayout<$pipeline> , )*
99        ] }
100
101        pub struct $struct {
102            pub instance: $crate::wgpu::Instance,
103            pub adapter: $crate::wgpu::Adapter,
104            pub device: $crate::wgpu::Device,
105            pub queue: $crate::wgpu::Queue,
106            bind_group_layouts: BindGroupLayoutTypes,
107            pipeline_layouts: PipelineLayoutTypes,
108        }
109
110        $crate::render_context_sync! { $struct }
111
112        impl $struct {
113            pub async fn new(backends: $crate::wgpu::Backends) -> Result<Self, Box<dyn ::std::error::Error>> {
114                let instance = $crate::wgpu::Instance::new(&wgpu::InstanceDescriptor {
115                    backends,
116                    flags: $crate::wgpu::InstanceFlags::empty(),
117                    ..Default::default()
118                });
119
120                let adapter = instance.request_adapter(
121                    &$crate::wgpu::RequestAdapterOptions {
122                        power_preference: $crate::wgpu::PowerPreference::default(),
123                        compatible_surface: None,
124                        force_fallback_adapter: false,
125                    },
126                ).await.inspect_err(|e| eprintln!("Could not get display adapter: {e}"))?;
127
128                let (device, queue) = adapter.request_device(
129                    &$crate::wgpu::DeviceDescriptor {
130                        label: Some("Device"),
131                        required_features: $crate::wgpu::Features::empty() $( | $crate::wgpu::Features::$feature )*,
132                        required_limits: $crate::wgpu::Limits {
133                            $( $limit: $limitval, )*
134                            ..Default::default()
135                        },
136                        memory_hints: Default::default(),
137                        trace: $crate::wgpu::Trace::Off,
138                    }
139                ).await.inspect_err(|e| eprintln!("Could not get display device: {e}"))?;
140
141                let bind_group_layouts = BindGroupLayoutTypes (
142                    $( {
143                        let desc = <$bindgroup>::layout_desc();
144                        let layout = device.create_bind_group_layout(&desc);
145                        $crate::bindgroup::MewBindGroupLayout::new(layout)
146                    }, )*
147                );
148
149                let pipeline_layouts = PipelineLayoutTypes (
150                    $( {
151                        let bind_groups = <$pipeline>::get_layouts_from_refs(&bind_group_layouts);
152                        let bind_groups_arr = <$pipeline>::bind_group_layouts(&bind_groups);
153                        let desc = <$pipeline>::layout_desc(&bind_groups_arr);
154                        let layout = device.create_pipeline_layout(&desc);
155                        $crate::pipeline::MewPipelineLayout::new(layout)
156                    }, )*
157                );
158
159                Ok(Self {
160                    instance,
161                    adapter,
162                    device,
163                    queue,
164                    bind_group_layouts,
165                    pipeline_layouts,
166                })
167            }
168
169            pub fn new_buffer<BUFFER: $crate::buffer::MewBuffer>(&self, inner_size: u64) -> BUFFER {
170                let raw_buffer = self.device.create_buffer(&BUFFER::buffer_desc(inner_size));
171                BUFFER::new(raw_buffer)
172            }
173
174            pub fn new_sampler<SAMPLER: $crate::sampler::MewSampler>(&self) -> SAMPLER {
175                let raw_sampler = self.device.create_sampler(&SAMPLER::buffer_desc());
176                SAMPLER::new(raw_sampler)
177            }
178
179            pub fn new_texture<TEXTURE: $crate::texture::MewTexture>(&self, inner_size: (u32, u32, u32)) -> TEXTURE {
180                let raw_texture = self.device.create_texture(&TEXTURE::buffer_desc(inner_size));
181                TEXTURE::new(raw_texture)
182            }
183
184            pub fn new_bind_group<BIND: $crate::bindgroup::MewBindGroup>(&self, buffers: BIND::BufferSet) -> BIND
185            where
186                BindGroupLayoutTypes: AsRef<$crate::bindgroup::MewBindGroupLayout<BIND>>
187            {
188                let raw_bind_group = {
189                    let layout: &$crate::bindgroup::MewBindGroupLayout<BIND> = self.bind_group_layouts.as_ref();
190                    let entries = BIND::bind_group_entries(&buffers);
191                    let desc = BIND::bind_group_desc(layout, &entries);
192                    self.device.create_bind_group(&desc)
193                };
194                BIND::new(raw_bind_group, buffers)
195            }
196
197            pub fn new_pipeline<PIPE: $crate::pipeline::MewPipeline>(
198                &self,
199                surface_fmt: Option<$crate::wgpu::TextureFormat>,
200                shader: &$crate::wgpu::ShaderModule,
201                vertex_entry: Option<&str>,
202                fragment_entry: Option<&str>,
203            ) -> PIPE
204            where
205                PipelineLayoutTypes: AsRef<$crate::pipeline::MewPipelineLayout<PIPE>>
206            {
207                let raw_pipeline = {
208                    let layout: &$crate::pipeline::MewPipelineLayout<PIPE> = self.pipeline_layouts.as_ref();
209                    let targets = PIPE::fragment_targets(surface_fmt);
210                    let desc = &PIPE::pipeline_desc(layout, shader, vertex_entry, fragment_entry, &targets);
211                    self.device.create_render_pipeline(desc)
212                };
213                PIPE::new(raw_pipeline)
214            }
215        }
216
217        impl $crate::MewRenderContext for $struct {
218            fn instance(&self) -> &wgpu::Instance {
219                &self.instance
220            }
221
222            fn adapter(&self) -> &wgpu::Adapter {
223                &self.adapter
224            }
225
226            fn device(&self) -> &wgpu::Device {
227                &self.device
228            }
229
230            fn queue(&self) -> &wgpu::Queue {
231                &self.queue
232            }
233        }
234    };
235}
236
237#[cfg(feature = "sync")]
238#[macro_export]
239macro_rules! render_context_sync {
240    { $struct:ident } => {
241        impl $struct {
242            pub fn poll_new(backends: $crate::wgpu::Backends) -> Result<Self, Box<dyn ::std::error::Error>> {
243                $crate::pollster::block_on(Self::new(backends))
244            }
245        }
246    };
247}
248
249#[cfg(not(feature = "sync"))]
250#[macro_export]
251macro_rules! render_context_sync {
252    { $struct:ident } => {};
253}
254
255
256
257
258pub trait MewRenderContext {
259    fn instance(&self) -> &wgpu::Instance;
260    fn adapter(&self) -> &wgpu::Adapter;
261    fn device(&self) -> &wgpu::Device;
262    fn queue(&self) -> &wgpu::Queue;
263}