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 },
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 ) -> 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 ) -> 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 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 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 => {
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 {
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 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: None,
441 polygon_mode: wgpu::PolygonMode::Fill,
444 unclipped_depth: false,
446 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}