comfy_wgpu/
lib.rs

1#![allow(clippy::uninlined_format_args)]
2#![allow(clippy::new_without_default)]
3
4pub use comfy_core::*;
5pub use wgpu::util::DeviceExt;
6
7pub use winit::event::{ElementState, Event, MouseScrollDelta, WindowEvent};
8
9pub use fontdue;
10
11mod batching;
12mod blood_canvas;
13mod bloom;
14mod debug;
15mod device;
16mod egui_integration;
17#[cfg(not(target_arch = "wasm32"))]
18mod hot_reload;
19mod instance;
20mod pipelines;
21mod post_processing;
22mod render_pass;
23mod renderer;
24mod screenshot;
25mod text;
26mod texture;
27mod utils;
28mod y_sort;
29
30pub use crate::batching::*;
31pub use crate::blood_canvas::*;
32pub use crate::bloom::*;
33pub use crate::debug::*;
34pub use crate::device::*;
35pub use crate::egui_integration::*;
36#[cfg(not(target_arch = "wasm32"))]
37pub use crate::hot_reload::*;
38pub use crate::instance::*;
39pub use crate::pipelines::*;
40pub use crate::post_processing::*;
41pub use crate::render_pass::*;
42pub use crate::renderer::*;
43pub use crate::screenshot::*;
44pub use crate::text::*;
45pub use crate::texture::*;
46pub use crate::utils::*;
47pub use crate::y_sort::*;
48
49pub use wgpu;
50pub use wgpu_types;
51
52pub trait Vertex {
53    fn desc<'a>() -> wgpu::VertexBufferLayout<'a>;
54}
55
56const ATTRIBS: [wgpu::VertexAttribute; 3] = wgpu::vertex_attr_array![
57    0 => Float32x3,
58    1 => Float32x2,
59    2 => Float32x4,
60];
61
62impl Vertex for SpriteVertex {
63    fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
64        wgpu::VertexBufferLayout {
65            array_stride: std::mem::size_of::<SpriteVertex>()
66                as wgpu::BufferAddress,
67            step_mode: wgpu::VertexStepMode::Vertex,
68            attributes: &ATTRIBS,
69        }
70    }
71}
72
73pub enum BufferType {
74    Vertex,
75    Index,
76    Instance,
77    Uniform,
78    Storage,
79    Read,
80}
81
82impl BufferType {
83    pub fn usage(&self) -> wgpu::BufferUsages {
84        match self {
85            BufferType::Vertex => {
86                wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST
87            }
88            BufferType::Index => {
89                wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST
90            }
91            BufferType::Instance => {
92                wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST
93            }
94            BufferType::Uniform => {
95                wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
96            }
97            BufferType::Read => {
98                wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ
99            }
100            BufferType::Storage => {
101                todo!()
102            }
103        }
104    }
105}
106
107pub fn power_preference_to_wgpu(
108    pref: PowerPreference,
109) -> wgpu::PowerPreference {
110    match pref {
111        PowerPreference::None => wgpu::PowerPreference::None,
112        PowerPreference::LowPower => wgpu::PowerPreference::LowPower,
113        PowerPreference::HighPerformance => {
114            wgpu::PowerPreference::HighPerformance
115        }
116    }
117}
118
119pub struct UniformBindGroup {
120    pub bind_group: wgpu::BindGroup,
121    pub layout: wgpu::BindGroupLayout,
122    pub buffer: wgpu::Buffer,
123}
124
125impl UniformBindGroup {
126    pub fn simple(
127        name: &str,
128        device: &wgpu::Device,
129        default_contents: &[u8],
130    ) -> Self {
131        let buffer =
132            device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
133                label: Some(&format!("{} Uniform Buffer", name)),
134                contents: default_contents,
135                usage: wgpu::BufferUsages::UNIFORM |
136                    wgpu::BufferUsages::COPY_DST,
137            });
138
139        let layout =
140            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
141                entries: &[wgpu::BindGroupLayoutEntry {
142                    binding: 0,
143                    visibility: wgpu::ShaderStages::VERTEX |
144                        wgpu::ShaderStages::FRAGMENT,
145                    ty: wgpu::BindingType::Buffer {
146                        ty: wgpu::BufferBindingType::Uniform,
147                        has_dynamic_offset: false,
148                        min_binding_size: None,
149                        //
150                        // has_dynamic_offset: true,
151                        // min_binding_size: wgpu::BufferSize::new(
152                        //     std::mem::size_of::<QuadUniform>() as u64,
153                        // ),
154                    },
155                    count: None,
156                }],
157                label: Some(&format!("{} Bind Group Layout", name)),
158            });
159
160        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
161            layout: &layout,
162            entries: &[wgpu::BindGroupEntry {
163                binding: 0,
164                resource: buffer.as_entire_binding(),
165            }],
166            label: Some(&format!("{} Bind Group", name)),
167        });
168
169        Self { bind_group, layout, buffer }
170    }
171}
172
173pub struct SizedBuffer {
174    pub buffer: wgpu::Buffer,
175    pub size: usize,
176    pub buffer_type: BufferType,
177    pub label: String,
178}
179
180impl SizedBuffer {
181    pub fn new(
182        label: &str,
183        device: &wgpu::Device,
184        size: usize,
185        buffer_type: BufferType,
186    ) -> Self {
187        let desc = wgpu::BufferDescriptor {
188            label: Some(label),
189            usage: buffer_type.usage(),
190            size: size as wgpu::BufferAddress,
191            mapped_at_creation: false,
192        };
193
194        let buffer = device.create_buffer(&desc);
195
196        Self { label: label.to_string(), size, buffer_type, buffer }
197    }
198
199    pub fn ensure_size_and_copy(
200        &mut self,
201        device: &wgpu::Device,
202        queue: &wgpu::Queue,
203        data: &[u8],
204    ) {
205        if data.len() > self.size {
206            self.buffer.destroy();
207            self.size = data.len();
208            self.buffer =
209                device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
210                    label: Some(&self.label),
211                    usage: self.buffer_type.usage(),
212                    contents: data,
213                });
214        } else {
215            queue.write_buffer(&self.buffer, 0, data);
216        }
217    }
218}
219
220pub trait DeviceExtensions {
221    fn simple_encoder(&self, label: &str) -> wgpu::CommandEncoder;
222    fn simple_bind_group(
223        &self,
224        label: Option<&str>,
225        texture: &Texture,
226        layout: &wgpu::BindGroupLayout,
227    ) -> wgpu::BindGroup;
228}
229
230impl DeviceExtensions for wgpu::Device {
231    fn simple_encoder(&self, label: &str) -> wgpu::CommandEncoder {
232        self.create_command_encoder(&wgpu::CommandEncoderDescriptor {
233            label: Some(label),
234        })
235    }
236
237    fn simple_bind_group(
238        &self,
239        label: Option<&str>,
240        texture: &Texture,
241        layout: &wgpu::BindGroupLayout,
242    ) -> wgpu::BindGroup {
243        self.create_bind_group(&wgpu::BindGroupDescriptor {
244            label,
245            layout,
246            entries: &[
247                wgpu::BindGroupEntry {
248                    binding: 0,
249                    resource: wgpu::BindingResource::TextureView(&texture.view),
250                },
251                wgpu::BindGroupEntry {
252                    binding: 1,
253                    resource: wgpu::BindingResource::Sampler(&texture.sampler),
254                },
255            ],
256        })
257    }
258}
259
260pub trait CommandEncoderExtensions {
261    fn simple_render_pass<'a>(
262        &'a mut self,
263        label: &str,
264        clear_color: Option<Color>,
265        view: &'a wgpu::TextureView,
266        // depth_stencil_attachment: Option<
267        //     wgpu::RenderPassDepthStencilAttachment,
268        // >,
269    ) -> wgpu::RenderPass;
270}
271
272impl CommandEncoderExtensions for wgpu::CommandEncoder {
273    fn simple_render_pass<'a>(
274        &'a mut self,
275        label: &str,
276        clear_color: Option<Color>,
277        view: &'a wgpu::TextureView,
278        // depth_stencil_attachment: Option<
279        //     wgpu::RenderPassDepthStencilAttachment,
280        // >,
281    ) -> wgpu::RenderPass {
282        self.begin_render_pass(&wgpu::RenderPassDescriptor {
283            label: Some(label),
284            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
285                view,
286                resolve_target: None,
287                ops: wgpu::Operations {
288                    load: color_to_clear_op(clear_color),
289                    store: wgpu::StoreOp::Store,
290                },
291            })],
292            depth_stencil_attachment: None,
293            timestamp_writes: None,
294            occlusion_query_set: None,
295        })
296    }
297}
298
299pub trait WgpuColorExtensions {
300    fn to_wgpu(&self) -> wgpu::Color;
301}
302
303impl WgpuColorExtensions for Color {
304    fn to_wgpu(&self) -> wgpu::Color {
305        wgpu::Color {
306            r: self.r as f64,
307            g: self.g as f64,
308            b: self.b as f64,
309            a: self.a as f64,
310        }
311    }
312}
313
314pub fn color_to_clear_op(color: Option<Color>) -> wgpu::LoadOp<wgpu::Color> {
315    match color {
316        Some(clear_color) => wgpu::LoadOp::Clear(clear_color.to_wgpu()),
317        None => wgpu::LoadOp::Load,
318    }
319}
320
321#[repr(C)]
322#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
323pub struct CameraUniform {
324    view_position: [f32; 4],
325    view_proj: [[f32; 4]; 4],
326}
327
328impl CameraUniform {
329    pub fn new() -> Self {
330        Self {
331            view_position: [0.0; 4],
332            view_proj: Mat4::IDENTITY.to_cols_array_2d(),
333        }
334    }
335
336    pub fn update_view_proj(&mut self, camera: &MainCamera) {
337        // Using Vec4 because of uniform 16 byte spacing requirement.
338        self.view_position = camera.center.extend(0.0).extend(1.0).into();
339        self.view_proj =
340            camera.build_view_projection_matrix().to_cols_array_2d();
341    }
342}
343
344pub fn create_render_pipeline(
345    label: &str,
346    device: &wgpu::Device,
347    layout: &wgpu::PipelineLayout,
348    color_format: wgpu::TextureFormat,
349    depth_format: Option<wgpu::TextureFormat>,
350    vertex_layouts: &[wgpu::VertexBufferLayout],
351    shader: &Shader,
352    blend_mode: BlendMode,
353) -> Result<wgpu::RenderPipeline> {
354    // let module = naga::front::wgsl::parse_str(&shader.source)?;
355    //
356    // let mut validator = naga::valid::Validator::new(
357    //     naga::valid::ValidationFlags::all(),
358    //     naga::valid::Capabilities::all(),
359    // );
360    //
361    // validator.validate(&module)?;
362
363    let wgpu_shader = shader_to_wgpu(shader);
364
365    let shader = device.create_shader_module(wgpu_shader);
366
367    info!("CREATED SHADER, GOT {:?}", shader);
368
369    let blend_state = match blend_mode {
370        BlendMode::Alpha => Some(wgpu::BlendState::ALPHA_BLENDING),
371        // BlendMode::Additive => Some(wgpu::BlendState::ALPHA_BLENDING),
372        // BlendMode::Additive => Some(wgpu::BlendState {
373        //     color: wgpu::BlendComponent {
374        //         src_factor: wgpu::BlendFactor::SrcAlpha,
375        //         dst_factor: wgpu::BlendFactor::DstAlpha,
376        //         operation: wgpu::BlendOperation::Add,
377        //     },
378        //     alpha: wgpu::BlendComponent {
379        //         src_factor: wgpu::BlendFactor::One,
380        //         dst_factor: wgpu::BlendFactor::One,
381        //         operation: wgpu::BlendOperation::Add,
382        //     }
383        // }),
384        BlendMode::Additive => {
385            Some(wgpu::BlendState {
386                color: wgpu::BlendComponent {
387                    src_factor: wgpu::BlendFactor::One,
388                    dst_factor: wgpu::BlendFactor::One,
389                    operation: wgpu::BlendOperation::Add,
390                },
391                // alpha: wgpu::BlendComponent::REPLACE,
392                alpha: wgpu::BlendComponent {
393                    src_factor: wgpu::BlendFactor::One,
394                    dst_factor: wgpu::BlendFactor::One,
395                    operation: wgpu::BlendOperation::Add,
396                },
397            })
398        }
399        BlendMode::None => Some(wgpu::BlendState::ALPHA_BLENDING),
400    };
401
402    // let blend_state = Some(wgpu::BlendState {
403    //     color: wgpu::BlendComponent {
404    //         src_factor: wgpu::BlendFactor::One,
405    //         dst_factor: wgpu::BlendFactor::One,
406    //         operation: wgpu::BlendOperation::Add,
407    //     },
408    //     // alpha: wgpu::BlendComponent::REPLACE,
409    //     alpha: wgpu::BlendComponent {
410    //         src_factor: wgpu::BlendFactor::One,
411    //         dst_factor: wgpu::BlendFactor::One,
412    //         operation: wgpu::BlendOperation::Add,
413    //     },
414    // });
415
416    let pipeline =
417        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
418            label: Some(label),
419            layout: Some(layout),
420            vertex: wgpu::VertexState {
421                module: &shader,
422                entry_point: "vs_main",
423                buffers: vertex_layouts,
424            },
425            fragment: Some(wgpu::FragmentState {
426                module: &shader,
427                entry_point: "fs_main",
428                targets: &[Some(wgpu::ColorTargetState {
429                    format: color_format,
430                    blend: blend_state,
431                    write_mask: wgpu::ColorWrites::ALL,
432                })],
433            }),
434
435            primitive: wgpu::PrimitiveState {
436                topology: wgpu::PrimitiveTopology::TriangleList,
437                strip_index_format: None,
438                front_face: wgpu::FrontFace::Ccw,
439                // cull_mode: Some(wgpu::Face::Back),
440                cull_mode: None,
441                // Settings this to anything other than Fill requires
442                // Features::NON_FILL_POLYGON_MODE
443                polygon_mode: wgpu::PolygonMode::Fill,
444                // Requires Features::DEPTH_CLIP_CONTROL
445                unclipped_depth: false,
446                // Requires Features::CONSERVATIVE_RASTERIZATION
447                conservative: false,
448            },
449
450            depth_stencil: depth_format.map(|format| {
451                wgpu::DepthStencilState {
452                    format,
453                    depth_write_enabled: true,
454                    depth_compare: wgpu::CompareFunction::Less,
455                    stencil: wgpu::StencilState::default(),
456                    bias: wgpu::DepthBiasState::default(),
457                }
458            }),
459
460            multisample: wgpu::MultisampleState {
461                count: 1,
462                mask: !0,
463                alpha_to_coverage_enabled: false,
464            },
465            multiview: None,
466        });
467
468    Ok(pipeline)
469}
470
471pub fn create_render_pipeline_with_layout(
472    name: &str,
473    device: &wgpu::Device,
474    color_format: wgpu::TextureFormat,
475    bind_group_layouts: &[&wgpu::BindGroupLayout],
476    vertex_layouts: &[wgpu::VertexBufferLayout],
477    shader: &Shader,
478    blend_mode: BlendMode,
479    enable_z_buffer: bool,
480) -> Result<wgpu::RenderPipeline> {
481    let layout =
482        device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
483            label: Some(&format!("{} Pipeline Layout", name)),
484            bind_group_layouts,
485            push_constant_ranges: &[],
486        });
487
488    create_render_pipeline(
489        &format!("{} Pipeline", name),
490        device,
491        &layout,
492        color_format,
493        if enable_z_buffer { Some(Texture::DEPTH_FORMAT) } else { None },
494        vertex_layouts,
495        shader,
496        blend_mode,
497    )
498}