pixel_engine_backend/
lib.rs

1use bytemuck::{Pod, Zeroable};
2use wgpu::util::DeviceExt;
3pub use winit;
4use winit::window::Window;
5pub mod decals;
6mod texture;
7
8#[macro_use]
9mod macros {
10    #[repr(C)] // guarantee 'bytes' comes after '_align'
11    pub struct AlignedAs<Align, Bytes: ?Sized> {
12        pub _align: [Align; 0],
13        pub bytes: Bytes,
14    }
15
16    macro_rules! include_bytes_align_as {
17        ($align_ty:ty; $($path:tt)*) => {
18            {  // const block expression to encapsulate the static
19                use $crate::macros::AlignedAs;
20
21                // this assignment is made possible by CoerceUnsized
22                static ALIGNED: &AlignedAs::<$align_ty, [u8]> = &AlignedAs {
23                    _align: [],
24                    bytes: *include_bytes!($($path)*),
25                };
26
27                &ALIGNED.bytes
28            }
29        };
30    }
31}
32
33pub trait VertexTrait {
34    fn desc<'a>() -> wgpu::VertexBufferLayout<'a>;
35}
36#[repr(C)]
37#[derive(Copy, Clone, Debug, Pod, Zeroable, PartialEq)]
38pub(crate) struct Vertex {
39    position: [f32; 3],
40    // UV + q for warped Decal
41    tex_coords: [f32; 3],
42
43    tint: [f32; 4],
44}
45
46impl VertexTrait for Vertex {
47    fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
48        use std::mem;
49        wgpu::VertexBufferLayout {
50            array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
51            step_mode: wgpu::VertexStepMode::Vertex,
52            attributes: &[
53                wgpu::VertexAttribute {
54                    offset: 0,
55                    shader_location: 0,
56                    format: wgpu::VertexFormat::Float32x3,
57                },
58                wgpu::VertexAttribute {
59                    offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
60                    shader_location: 1,
61                    format: wgpu::VertexFormat::Float32x3,
62                },
63                wgpu::VertexAttribute {
64                    offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
65                    shader_location: 2,
66                    format: wgpu::VertexFormat::Float32x4,
67                },
68            ],
69        }
70    }
71}
72
73pub const VERTEX_BUFFER_SIZE: u64 = std::mem::size_of::<[Vertex; 4]>() as u64;
74
75const CORNER: f32 = 1f32;
76#[rustfmt::skip]
77const VERTICES: &[Vertex] = &[
78    Vertex { position: [-CORNER, CORNER, 0.0], tex_coords: [0.0, 0.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, // A
79    Vertex { position: [-CORNER,-CORNER, 0.0], tex_coords: [0.0, 1.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, // B
80    Vertex { position: [ CORNER,-CORNER, 0.0], tex_coords: [1.0, 1.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, // C
81    Vertex { position: [ CORNER, CORNER, 0.0], tex_coords: [1.0, 0.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, // D
82];
83
84#[rustfmt::skip]
85pub(crate) const INDICES: &[u16] = &[
86    0, 1, 3,
87    1, 2, 3,
88];
89
90#[allow(dead_code)]
91pub struct Context {
92    surface: wgpu::Surface,
93    device: wgpu::Device,
94    queue: wgpu::Queue,
95    config: wgpu::SurfaceConfiguration,
96
97    render_pipeline: wgpu::RenderPipeline,
98    vertex_buffer: wgpu::Buffer,
99    index_buffer: wgpu::Buffer,
100    num_indices: u32,
101    bind_group_layout: wgpu::BindGroupLayout,
102
103    dcm: decals::DecalContextManager,
104}
105
106impl Context {
107    pub async fn new(window: &Window, px_size: (u32, u32, u32)) -> Self {
108        let size = window.inner_size();
109        let instance = wgpu::Instance::new(wgpu::Backends::all());
110
111        let surface = unsafe { instance.create_surface(window) };
112
113        let adapter = instance
114            .request_adapter(&wgpu::RequestAdapterOptions {
115                power_preference: wgpu::PowerPreference::default(),
116                compatible_surface: Some(&surface),
117                force_fallback_adapter: false,
118            })
119            .await
120            .expect("Error when requesting Adapter");
121
122        let (device, queue) = adapter
123            .request_device(
124                &wgpu::DeviceDescriptor {
125                    features: wgpu::Features::empty(),
126                    #[cfg(not(target_arch = "wasm32"))]
127                    limits: Default::default(),
128                    #[cfg(target_arch = "wasm32")]
129                    limits: wgpu::Limits::downlevel_webgl2_defaults(),
130                    label: Some("device_request"),
131                },
132                None,
133            )
134            .await
135            .expect("Error when getting device and queue");
136
137        device.on_uncaptured_error(|error| panic!("[WGPU Error] {}", error));
138        let config = wgpu::SurfaceConfiguration {
139            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
140            width: size.width,
141            height: size.height,
142            #[cfg(target_arch = "wasm32")]
143            format: wgpu::TextureFormat::Rgba8UnormSrgb,
144            #[cfg(not(target_arch = "wasm32"))]
145            format: wgpu::TextureFormat::Bgra8UnormSrgb,
146            present_mode: wgpu::PresentMode::AutoNoVsync,
147            alpha_mode: wgpu::CompositeAlphaMode::Auto,
148        };
149
150        surface.configure(&device, &config);
151
152        let vs_raw = include_bytes_align_as!(u32; concat!(
153            env!("CARGO_MANIFEST_DIR"),
154            "/shaders/shader.vert.spv"
155        ));
156
157        let fs_raw = include_bytes_align_as!(u32; concat!(
158            env!("CARGO_MANIFEST_DIR"),
159            "/shaders/shader.frag.spv"
160        ));
161
162        let vs_data: &[u32] = bytemuck::cast_slice(vs_raw);
163        let fs_data: &[u32] = bytemuck::cast_slice(fs_raw);
164
165        let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
166            label: Some("vs_module"),
167            source: wgpu::ShaderSource::SpirV(std::borrow::Cow::from(vs_data)),
168        });
169        let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
170            label: Some("fs_module"),
171            source: wgpu::ShaderSource::SpirV(std::borrow::Cow::from(fs_data)),
172        });
173
174        let texture_bind_group_layout =
175            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
176                entries: &[
177                    wgpu::BindGroupLayoutEntry {
178                        count: None,
179                        binding: 0,
180                        visibility: wgpu::ShaderStages::FRAGMENT,
181                        ty: wgpu::BindingType::Texture {
182                            multisampled: false,
183                            view_dimension: wgpu::TextureViewDimension::D2,
184                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
185                        },
186                    },
187                    wgpu::BindGroupLayoutEntry {
188                        binding: 1,
189                        visibility: wgpu::ShaderStages::FRAGMENT,
190                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
191                        count: None,
192                    },
193                ],
194                label: Some("texture_bind_group_layout"),
195            });
196
197        let render_pipeline_layout =
198            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
199                label: Some("pipeline_layout"),
200                bind_group_layouts: &[&texture_bind_group_layout],
201                push_constant_ranges: &[],
202            });
203
204        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
205            label: Some("pipeline"),
206            layout: Some(&render_pipeline_layout),
207            vertex: wgpu::VertexState {
208                module: &vs_module,
209                entry_point: "main",
210                buffers: &[Vertex::desc()],
211            },
212            fragment: Some(wgpu::FragmentState {
213                module: &fs_module,
214                entry_point: "main",
215                targets: &[Some(wgpu::ColorTargetState {
216                    #[cfg(target_arch = "wasm32")]
217                    format: wgpu::TextureFormat::Rgba8UnormSrgb,
218                    #[cfg(not(target_arch = "wasm32"))]
219                    format: wgpu::TextureFormat::Bgra8UnormSrgb,
220                    write_mask: wgpu::ColorWrites::ALL,
221                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
222                })],
223            }),
224            depth_stencil: None,
225            multiview: None,
226
227            primitive: wgpu::PrimitiveState {
228                topology: wgpu::PrimitiveTopology::TriangleList, // 1.
229                strip_index_format: None,                        //
230                front_face: wgpu::FrontFace::Ccw,                // 2.
231                cull_mode: Some(wgpu::Face::Back),
232                // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
233                polygon_mode: wgpu::PolygonMode::Fill,
234                unclipped_depth: false,
235                conservative: false,
236            },
237            multisample: wgpu::MultisampleState {
238                count: 1,                         // 2.
239                mask: !0,                         // 3.
240                alpha_to_coverage_enabled: false, // 4.
241            },
242        });
243        let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
244            label: Some("Render Encoder"),
245        });
246
247        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
248            label: Some("Vertex Buffer"),
249            contents: bytemuck::cast_slice(VERTICES),
250            usage: wgpu::BufferUsages::COPY_DST
251                | wgpu::BufferUsages::VERTEX
252                | wgpu::BufferUsages::COPY_SRC,
253        });
254        let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
255            label: Some("index_buffer"),
256            contents: bytemuck::cast_slice(INDICES),
257            usage: wgpu::BufferUsages::COPY_DST
258                | wgpu::BufferUsages::INDEX
259                | wgpu::BufferUsages::COPY_SRC,
260        });
261        queue.submit(std::iter::once(encoder.finish()));
262        let num_indices = INDICES.len() as u32;
263        let (dcm, cmd) = decals::DecalContextManager::new(
264            &device,
265            &queue,
266            &texture_bind_group_layout,
267            (
268                &vec![0, 0, 0, 255].repeat((px_size.0 * px_size.1) as usize),
269                (px_size.0, px_size.1),
270            ),
271        );
272        queue.submit(std::iter::once(cmd));
273        Self {
274            surface,
275            device,
276            queue,
277            render_pipeline,
278            vertex_buffer,
279            config,
280            index_buffer,
281            num_indices,
282            bind_group_layout: texture_bind_group_layout,
283            dcm,
284        }
285    }
286
287    pub fn render(&mut self, data: &[u8]) {
288        self.dcm.update_main_texture(&self.queue, data);
289        self.render_no_update();
290    }
291
292    pub fn render_no_update(&mut self) {
293        if let Ok(frame) = self.surface.get_current_texture() {
294            let mut encoder = self
295                .device
296                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
297                    label: Some("Render Encoder"),
298                });
299
300            {
301                let view = frame
302                    .texture
303                    .create_view(&wgpu::TextureViewDescriptor::default());
304                let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
305                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
306                        view: &view,
307                        resolve_target: None,
308                        ops: wgpu::Operations {
309                            load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
310
311                            store: true,
312                        },
313                    })],
314                    depth_stencil_attachment: None,
315                    label: Some("Render Pass"),
316                });
317
318                use decals::DrawDecals;
319
320                render_pass.set_pipeline(&self.render_pipeline);
321
322                render_pass.draw_decals(&mut self.dcm, &mut self.device, &mut self.queue);
323            }
324            self.queue.submit(std::iter::once(encoder.finish()));
325            frame.present();
326        }
327    }
328
329    pub fn create_decal(&mut self, spr: (&[u8], (u32, u32))) -> decals::Decal {
330        decals::Decal::create(self, spr)
331    }
332    pub fn draw_decal_instance(&mut self, decal_instance: decals::DecalInstances) {
333        self.dcm.add_instance(decal_instance);
334    }
335}
336
337/*
338event_loop.run(move |event, _, control_flow| {
339    let screen_buffer_slice = state.get_screen_slice();
340   match event {
341        Event::WindowEvent {
342            ref event,
343            window_id,
344        } if window_id == window.id() => match event {
345            WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
346            WindowEvent::KeyboardInput { input, .. } => match input {
347                KeyboardInput {
348                    state: ElementState::Pressed,
349                    virtual_keycode: Some(VirtualKeyCode::Escape),
350                    ..
351                } => *control_flow = ControlFlow::Exit,
352                _ => {}
353            },
354            _ => {}
355        },
356        Event::RedrawRequested(_) => {
357            state.render();
358        }
359        Event::MainEventsCleared => {
360            // RedrawRequested will only trigger once, unless we manually
361            // request it.
362            window.request_redraw();
363        }
364        _ => {}
365    }
366});
367*/