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)] 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 { use $crate::macros::AlignedAs;
20
21 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 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] }, Vertex { position: [-CORNER,-CORNER, 0.0], tex_coords: [0.0, 1.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, Vertex { position: [ CORNER,-CORNER, 0.0], tex_coords: [1.0, 1.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, Vertex { position: [ CORNER, CORNER, 0.0], tex_coords: [1.0, 0.0, 1.0], tint: [1.0, 1.0, 1.0, 1.0] }, ];
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, strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Back),
232 polygon_mode: wgpu::PolygonMode::Fill,
234 unclipped_depth: false,
235 conservative: false,
236 },
237 multisample: wgpu::MultisampleState {
238 count: 1, mask: !0, alpha_to_coverage_enabled: false, },
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