steamengine_renderer/
lib.rs

1/// This module constrains an api to communicate to WGPU
2use std::{fs::File, io::Read};
3
4use bind_group::BindGroupEntryBuilder;
5use bytemuck::NoUninit;
6use errors::{RendererSetupError, TextureError};
7use texture::{Texture, TextureBuilder, TextureDimensions};
8use tracing::*;
9use vertex::Vertex;
10use wgpu::{
11    BackendOptions, Backends, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
12    Buffer, BufferDescriptor, BufferUsages, CommandEncoder, InstanceFlags, PresentMode,
13    SurfaceTexture, TextureFormat, TextureView, TextureViewDescriptor, Trace,
14    util::{BufferInitDescriptor, DeviceExt},
15};
16
17use winit::window::Window;
18
19/// This module is an utility to build bind groups
20pub mod bind_group;
21/// This module contrains the errors
22pub mod errors;
23/// This module contrains a macro to build a simple render pass
24#[macro_use]
25pub mod render_pass;
26pub mod instances;
27/// This module contrains an utilities to create a render pipeline
28pub mod render_pipeline;
29/// This module contrains a utility to create textures
30pub mod texture;
31/// This module contrains an utilities to load vertex
32pub mod vertex;
33
34/// This the builder of the renderer, in this builder you can to set the parameters of the renderer Ex: performance mode
35pub struct RendererBuilder {
36    backends: Backends,
37    flags: InstanceFlags,
38    backend_options: BackendOptions,
39    power_preference: wgpu::PowerPreference,
40    force_fallback_adapter: bool,
41    required_features: wgpu::Features,
42    required_limits: wgpu::Limits,
43    memory_hints: wgpu::MemoryHints,
44    surface_format: fn(caps: &wgpu::SurfaceCapabilities) -> wgpu::TextureFormat,
45    usage: wgpu::TextureUsages,
46    present_mode: fn(caps: &wgpu::SurfaceCapabilities) -> PresentMode,
47    alpha_mode: fn(caps: &wgpu::SurfaceCapabilities) -> wgpu::CompositeAlphaMode,
48    view_formats: Vec<wgpu::TextureFormat>,
49    trace: Trace,
50    desired_maximum_frame_latency: u32,
51}
52impl RendererBuilder {
53    pub fn new() -> Self {
54        RendererBuilder {
55            backends: Backends::all(),
56            flags: InstanceFlags::empty(),
57            backend_options: BackendOptions::default(),
58            power_preference: wgpu::PowerPreference::default(),
59            force_fallback_adapter: false,
60            required_features: wgpu::Features::empty(),
61            required_limits: wgpu::Limits::downlevel_defaults(),
62            memory_hints: wgpu::MemoryHints::default(),
63            surface_format: |caps| {
64                caps.formats
65                    .iter()
66                    .copied()
67                    .find(|f| f.is_srgb())
68                    .unwrap_or(caps.formats[0])
69            },
70            trace: Trace::Off,
71            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
72            present_mode: |caps| caps.present_modes[0],
73            alpha_mode: |caps| caps.alpha_modes[0],
74            view_formats: Vec::new(),
75            desired_maximum_frame_latency: 2,
76        }
77    }
78    /// Sets the backend of wgpu, example, Vulkan or OpenGL
79    pub fn backends(mut self, backends: Backends) -> Self {
80        self.backends = backends;
81        self
82    }
83    /// Sets the flags of the renderer
84    pub fn flags(mut self, flags: InstanceFlags) -> Self {
85        self.flags = flags;
86        self
87    }
88    /// Sets the options of the backend
89    pub fn backend_options(mut self, backend_options: BackendOptions) -> Self {
90        self.backend_options = backend_options;
91        self
92    }
93    /// Sets the performance mode
94    pub fn power_preference(mut self, power_preference: wgpu::PowerPreference) -> Self {
95        self.power_preference = power_preference;
96        self
97    }
98    pub fn force_fallback_adapter(mut self, force_fallback_adapter: bool) -> Self {
99        self.force_fallback_adapter = force_fallback_adapter;
100        self
101    }
102    pub fn required_features(mut self, required_features: wgpu::Features) -> Self {
103        self.required_features = required_features;
104        self
105    }
106    pub fn required_limits(mut self, required_limits: wgpu::Limits) -> Self {
107        self.required_limits = required_limits;
108        self
109    }
110    pub fn memory_hints(mut self, memory_hints: wgpu::MemoryHints) -> Self {
111        self.memory_hints = memory_hints;
112        self
113    }
114    pub fn surface_format(
115        mut self,
116        surface_format: fn(caps: &wgpu::SurfaceCapabilities) -> wgpu::TextureFormat,
117    ) -> Self {
118        self.surface_format = surface_format;
119        self
120    }
121    pub fn usage(mut self, usage: wgpu::TextureUsages) -> Self {
122        self.usage = usage;
123        self
124    }
125    pub fn present_mode(
126        mut self,
127        present_mode: fn(caps: &wgpu::SurfaceCapabilities) -> PresentMode,
128    ) -> Self {
129        self.present_mode = present_mode;
130        self
131    }
132    pub fn alpha_mode(
133        mut self,
134        alpha_mode: fn(caps: &wgpu::SurfaceCapabilities) -> wgpu::CompositeAlphaMode,
135    ) -> Self {
136        self.alpha_mode = alpha_mode;
137        self
138    }
139    pub fn view_formats(mut self, view_formats: Vec<wgpu::TextureFormat>) -> Self {
140        self.view_formats = view_formats;
141        self
142    }
143    pub fn desired_maximum_frame_latency(mut self, desired_maximum_frame_latency: u32) -> Self {
144        self.desired_maximum_frame_latency = desired_maximum_frame_latency;
145        self
146    }
147    pub fn trace(mut self, trace: Trace) -> Self {
148        self.trace = trace;
149        self
150    }
151    pub async fn build<'a>(
152        self,
153        window: std::sync::Arc<Window>,
154        size: (u32, u32),
155    ) -> Result<Renderer<'a>, RendererSetupError> {
156        // The instance is a handle to our GPU
157        // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
158        trace!("Creating renderer");
159        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
160            backends: self.backends,
161            flags: self.flags,
162            backend_options: self.backend_options,
163        });
164
165        let surface = instance.create_surface(window)?;
166
167        trace!("Surface created");
168        let adapter = instance
169            .request_adapter(&wgpu::RequestAdapterOptions {
170                power_preference: self.power_preference,
171                compatible_surface: Some(&surface),
172                force_fallback_adapter: self.force_fallback_adapter,
173            })
174            .await?;
175        let features = adapter.features();
176        if !features.contains(self.required_features) {
177            error!("The device dont support the features")
178        }
179        trace!("Adapter created");
180        let (device, queue) = adapter
181            .request_device(&wgpu::DeviceDescriptor {
182                label: None,
183                required_features: self.required_features,
184                // WebGL doesn't support all of wgpu's features, so if
185                // we're building for the web we'll have to disable some.
186                required_limits: self.required_limits,
187                memory_hints: self.memory_hints,
188                trace: self.trace,
189            })
190            .await?;
191        trace!("Device and Queue created");
192
193        let surface_caps = surface.get_capabilities(&adapter);
194        // Shader code in this tutorial assumes an Srgb surface texture. Using a different
195        // one will result all the colors coming out darker. If you want to support non
196        // Srgb surfaces, you'll need to account for that when drawing to the frame.
197        let surface_format = (self.surface_format)(&surface_caps);
198        let config = wgpu::SurfaceConfiguration {
199            usage: self.usage,
200            format: surface_format,
201            width: size.0,
202            height: size.1,
203            present_mode: (self.present_mode)(&surface_caps),
204            alpha_mode: (self.alpha_mode)(&surface_caps),
205            view_formats: self.view_formats,
206            desired_maximum_frame_latency: self.desired_maximum_frame_latency,
207        };
208        trace!("Config created");
209        let surface = std::sync::RwLock::new(surface);
210        let config = std::sync::RwLock::new(config);
211        let size = std::sync::RwLock::new(size);
212        trace!("Renderer builded");
213        Ok(Renderer {
214            surface,
215            device,
216            queue,
217            config,
218            size,
219        })
220    }
221}
222/// this struct contrais all the components to render
223pub struct Renderer<'a> {
224    pub surface: std::sync::RwLock<wgpu::Surface<'a>>,
225    pub device: wgpu::Device,
226    pub queue: wgpu::Queue,
227    pub config: std::sync::RwLock<wgpu::SurfaceConfiguration>,
228    pub size: std::sync::RwLock<(u32, u32)>,
229}
230
231impl<'a> Renderer<'a> {
232    /// create a new render_pass encoder
233    pub fn create_encoder(
234        &self,
235    ) -> Result<(CommandEncoder, TextureView, SurfaceTexture), wgpu::SurfaceError> {
236        trace!("Renderer creating encoder");
237        let output = self
238            .surface
239            .read()
240            .expect("Cannot read surface")
241            .get_current_texture()?;
242        let view = output
243            .texture
244            .create_view(&wgpu::TextureViewDescriptor::default());
245
246        Ok((
247            self.device
248                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
249                    label: Some("Render Encoder"),
250                }),
251            view,
252            output,
253        ))
254    }
255    /// gets the surface
256    pub fn surface(&self) -> std::sync::RwLockReadGuard<wgpu::Surface> {
257        self.surface.read().expect("Cannot read surface")
258    }
259    /// gets the device
260    pub fn device(&self) -> &wgpu::Device {
261        &self.device
262    }
263    /// gets the queue
264    pub fn queue(&self) -> &wgpu::Queue {
265        &self.queue
266    }
267    /// gets the config
268    pub fn config(&self) -> std::sync::RwLockReadGuard<wgpu::SurfaceConfiguration> {
269        self.config.read().expect("Cannot read config")
270    }
271    /// gets the size
272    pub fn size(&self) -> (u32, u32) {
273        self.size.read().unwrap().clone()
274    }
275    pub fn resize(&self, new_size: &(u32, u32)) {
276        if new_size.0 > 0 && new_size.1 > 0 {
277            *self.size.write().expect("Cannot write size") = new_size.clone();
278            self.config.write().expect("Cannot write config").width = new_size.0;
279            self.config.write().expect("Cannot write config").height = new_size.1;
280            self.surface
281                .write()
282                .unwrap()
283                .configure(&self.device, &self.config.read().unwrap());
284        }
285    }
286    /// init a new buffer with a data
287    pub fn init_buffer<A>(&self, label: &str, usage: BufferUsages, content: &[A]) -> Buffer
288    where
289        A: NoUninit,
290    {
291        self.device.create_buffer_init(&BufferInitDescriptor {
292            label: Some(label),
293            contents: bytemuck::cast_slice(content),
294            usage,
295        })
296    }
297    pub fn create_buffer(&self, label: &str, usage: BufferUsages, size: u64) -> Buffer {
298        self.device().create_buffer(&BufferDescriptor {
299            size,
300            usage,
301            label: Some(label),
302            mapped_at_creation: false,
303        })
304    }
305    /// create a new bind group
306    pub fn bind_group(
307        &self,
308        label: &str,
309        entries: &[BindGroupEntryBuilder],
310    ) -> (wgpu::BindGroup, wgpu::BindGroupLayout) {
311        trace!("Renderer building bind group -- {}", label);
312        let layout_entries: Vec<wgpu::BindGroupLayoutEntry> = entries
313            .iter()
314            .map(|entry| BindGroupLayoutEntry {
315                binding: entry.binding,
316                visibility: entry.visibility,
317                ty: entry.ty,
318                count: entry.count,
319            })
320            .collect();
321        trace!("With {} entries -- {}", layout_entries.len(), label);
322        let layout = self
323            .device
324            .create_bind_group_layout(&BindGroupLayoutDescriptor {
325                label: Some(label),
326                entries: &layout_entries,
327            });
328        trace!("Layout created -- {}", label);
329        let entries: Vec<_> = entries
330            .iter()
331            .map(|entry| BindGroupEntry {
332                binding: entry.binding,
333                resource: entry
334                    .resource
335                    .clone()
336                    .expect("Resource binding in bind group not defined"),
337            })
338            .collect();
339        trace!("{} Entries builded into BindGroupEntry", label);
340        let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
341            label: Some(label),
342            layout: &layout,
343            entries: &entries,
344        });
345        trace!("Finnish bind group creation -- {}", label);
346        (bind_group, layout)
347    }
348    /// init a new texture
349    pub fn init_texture(
350        &self,
351        label: &'static str,
352        view_formats: Option<&'static [TextureFormat]>,
353        builder: TextureBuilder,
354    ) -> Texture {
355        builder.build(label, view_formats, self)
356    }
357    /// simple load a png texture from file
358    pub fn simple_png_texture_file(&self, file: &mut File) -> Result<Texture, TextureError> {
359        let mut buffer = Vec::new();
360        file.read_to_end(&mut buffer)?;
361        self.simple_png_texture_bytes(buffer.as_slice())
362    }
363    /// simple load a png texture from bytes
364    pub fn simple_png_texture_bytes(&self, bytes: &[u8]) -> Result<Texture, TextureError> {
365        let diffuse_image = image::load_from_memory(bytes)?;
366        let diffuse_rgba = diffuse_image.to_rgba8();
367        use image::GenericImageView;
368        let dimensions = diffuse_image.dimensions();
369
370        let mut texture = self.init_texture(
371            "texture",
372            None,
373            TextureBuilder::new()
374                .dimension(TextureDimensions::D2(dimensions.0, dimensions.1))
375                .data(diffuse_rgba.to_vec()),
376        );
377
378        texture.texture_view(TextureViewDescriptor::default());
379
380        texture.texture_sampler(
381            wgpu::SamplerDescriptor {
382                address_mode_u: wgpu::AddressMode::ClampToEdge,
383                address_mode_v: wgpu::AddressMode::ClampToEdge,
384                address_mode_w: wgpu::AddressMode::ClampToEdge,
385                mag_filter: wgpu::FilterMode::Linear,
386                min_filter: wgpu::FilterMode::Nearest,
387                mipmap_filter: wgpu::FilterMode::Nearest,
388                ..Default::default()
389            },
390            self,
391        );
392
393        texture.default_bind_group("texture bind group", self);
394        Ok(texture)
395    }
396    /// Load vertices to buffer
397    pub fn init_buffer_from_vertices<A: Vertex>(&self, label: &str, vertices: &[A]) -> Buffer {
398        self.init_buffer(label, BufferUsages::VERTEX, vertices)
399    }
400    /// Load indices to buffer
401    pub fn init_buffer_from_indices<A: NoUninit>(&self, label: &str, indices: &[A]) -> Buffer {
402        self.init_buffer(label, BufferUsages::INDEX, indices)
403    }
404    /// Load a index buffer and vertex buffer
405    /// Return: (vertex buffer, index buffer)
406    pub fn init_buffers_from_model<A: Vertex, B: NoUninit>(
407        &self,
408        label: &str,
409        vertices: &[A],
410        indices: &[B],
411    ) -> (Buffer, Buffer) {
412        trace!(
413            "Initializing model buffers with {} vertices and {} indices -- {}",
414            vertices.len(),
415            indices.len(),
416            label
417        );
418        let vertices_label = format!("{} - VERTICES", label);
419        let indices_label = format!("{} - INDICES", label);
420
421        let vertices_buffer = self.init_buffer_from_vertices(vertices_label.as_str(), vertices);
422        let indices_buffer = self.init_buffer_from_indices(indices_label.as_str(), indices);
423
424        (vertices_buffer, indices_buffer)
425    }
426    pub fn update_buffer<A: NoUninit>(&self, buffer: &Buffer, data: &[A]) {
427        self.queue()
428            .write_buffer(buffer, 0, bytemuck::cast_slice(data));
429    }
430    pub fn update_buffer_entry<A: NoUninit>(&self, buffer: &Buffer, id: u64, data: A) {
431        let size = std::mem::size_of::<A>();
432        let offset = size as u64 * id;
433        self.queue()
434            .write_buffer(buffer, offset, bytemuck::cast_slice(&[data]));
435    }
436}