1use crate::context::GraphicsContext;
2
3pub struct Renderer {
8 context: &'static GraphicsContext,
9}
10
11impl Renderer {
12 pub fn new(context: &'static GraphicsContext) -> Self {
14 Self { context }
15 }
16
17 pub fn context(&self) -> &'static GraphicsContext {
19 self.context
20 }
21
22 pub fn device(&self) -> &wgpu::Device {
24 &self.context.device
25 }
26
27 pub fn queue(&self) -> &wgpu::Queue {
29 &self.context.queue
30 }
31
32 pub fn create_shader(&self, label: Option<&str>, source: &str) -> wgpu::ShaderModule {
34 self.context
35 .device
36 .create_shader_module(wgpu::ShaderModuleDescriptor {
37 label,
38 source: wgpu::ShaderSource::Wgsl(source.into()),
39 })
40 }
41
42 pub fn create_vertex_buffer<T: bytemuck::Pod>(
44 &self,
45 label: Option<&str>,
46 data: &[T],
47 ) -> wgpu::Buffer {
48 let buffer = self.context.device.create_buffer(&wgpu::BufferDescriptor {
49 label,
50 size: std::mem::size_of_val(data) as u64,
51 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
52 mapped_at_creation: false,
53 });
54
55 self.context
56 .queue
57 .write_buffer(&buffer, 0, bytemuck::cast_slice(data));
58
59 buffer
60 }
61
62 pub fn create_index_buffer<T: bytemuck::Pod>(
64 &self,
65 label: Option<&str>,
66 data: &[T],
67 ) -> wgpu::Buffer {
68 let buffer = self.context.device.create_buffer(&wgpu::BufferDescriptor {
69 label,
70 size: std::mem::size_of_val(data) as u64,
71 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
72 mapped_at_creation: false,
73 });
74
75 self.context
76 .queue
77 .write_buffer(&buffer, 0, bytemuck::cast_slice(data));
78
79 buffer
80 }
81
82 pub fn create_uniform_buffer<T: bytemuck::Pod>(
84 &self,
85 label: Option<&str>,
86 data: &T,
87 ) -> wgpu::Buffer {
88 let buffer = self.context.device.create_buffer(&wgpu::BufferDescriptor {
89 label,
90 size: std::mem::size_of::<T>() as u64,
91 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
92 mapped_at_creation: false,
93 });
94
95 self.context.queue.write_buffer(
96 &buffer,
97 0,
98 bytemuck::cast_slice(std::slice::from_ref(data)),
99 );
100
101 buffer
102 }
103
104 pub fn update_uniform_buffer<T: bytemuck::Pod>(&self, buffer: &wgpu::Buffer, data: &T) {
106 self.context.queue.write_buffer(
107 buffer,
108 0,
109 bytemuck::cast_slice(std::slice::from_ref(data)),
110 );
111 }
112
113 pub fn create_storage_buffer(
122 &self,
123 label: Option<&str>,
124 size: u64,
125 read_only: bool,
126 ) -> wgpu::Buffer {
127 let usage = if read_only {
128 wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST
129 } else {
130 wgpu::BufferUsages::STORAGE
131 | wgpu::BufferUsages::COPY_DST
132 | wgpu::BufferUsages::COPY_SRC
133 };
134
135 self.context.device.create_buffer(&wgpu::BufferDescriptor {
136 label,
137 size,
138 usage,
139 mapped_at_creation: false,
140 })
141 }
142
143 pub fn create_storage_buffer_init<T: bytemuck::Pod>(
152 &self,
153 label: Option<&str>,
154 data: &[T],
155 read_only: bool,
156 ) -> wgpu::Buffer {
157 let usage = if read_only {
158 wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST
159 } else {
160 wgpu::BufferUsages::STORAGE
161 | wgpu::BufferUsages::COPY_DST
162 | wgpu::BufferUsages::COPY_SRC
163 };
164
165 let buffer = self.context.device.create_buffer(&wgpu::BufferDescriptor {
166 label,
167 size: std::mem::size_of_val(data) as u64,
168 usage,
169 mapped_at_creation: false,
170 });
171
172 self.context
173 .queue
174 .write_buffer(&buffer, 0, bytemuck::cast_slice(data));
175
176 buffer
177 }
178
179 pub fn update_storage_buffer<T: bytemuck::Pod>(
187 &self,
188 buffer: &wgpu::Buffer,
189 offset: u64,
190 data: &[T],
191 ) {
192 self.context
193 .queue
194 .write_buffer(buffer, offset, bytemuck::cast_slice(data));
195 }
196
197 pub fn create_texture(&self, descriptor: &wgpu::TextureDescriptor) -> wgpu::Texture {
199 self.context.device.create_texture(descriptor)
200 }
201
202 pub fn create_texture_2d(
204 &self,
205 label: Option<&str>,
206 width: u32,
207 height: u32,
208 format: wgpu::TextureFormat,
209 usage: wgpu::TextureUsages,
210 data: &[u8],
211 ) -> wgpu::Texture {
212 let size = wgpu::Extent3d {
213 width,
214 height,
215 depth_or_array_layers: 1,
216 };
217
218 let texture = self
219 .context
220 .device
221 .create_texture(&wgpu::TextureDescriptor {
222 label,
223 size,
224 mip_level_count: 1,
225 sample_count: 1,
226 dimension: wgpu::TextureDimension::D2,
227 format,
228 usage: usage | wgpu::TextureUsages::COPY_DST,
229 view_formats: &[],
230 });
231
232 let bytes_per_pixel = format.block_copy_size(None).unwrap();
233
234 self.context.queue.write_texture(
235 wgpu::TexelCopyTextureInfo {
236 texture: &texture,
237 mip_level: 0,
238 origin: wgpu::Origin3d::ZERO,
239 aspect: wgpu::TextureAspect::All,
240 },
241 data,
242 wgpu::TexelCopyBufferLayout {
243 offset: 0,
244 bytes_per_row: Some(width * bytes_per_pixel),
245 rows_per_image: Some(height),
246 },
247 size,
248 );
249
250 texture
251 }
252
253 pub fn create_sampler(&self, descriptor: &wgpu::SamplerDescriptor) -> wgpu::Sampler {
255 self.context.device.create_sampler(descriptor)
256 }
257
258 pub fn create_linear_sampler(&self, label: Option<&str>) -> wgpu::Sampler {
260 self.context
261 .device
262 .create_sampler(&wgpu::SamplerDescriptor {
263 label,
264 address_mode_u: wgpu::AddressMode::ClampToEdge,
265 address_mode_v: wgpu::AddressMode::ClampToEdge,
266 address_mode_w: wgpu::AddressMode::ClampToEdge,
267 mag_filter: wgpu::FilterMode::Linear,
268 min_filter: wgpu::FilterMode::Linear,
269 mipmap_filter: wgpu::FilterMode::Nearest,
270 ..Default::default()
271 })
272 }
273
274 pub fn create_nearest_sampler(&self, label: Option<&str>) -> wgpu::Sampler {
276 self.context
277 .device
278 .create_sampler(&wgpu::SamplerDescriptor {
279 label,
280 address_mode_u: wgpu::AddressMode::ClampToEdge,
281 address_mode_v: wgpu::AddressMode::ClampToEdge,
282 address_mode_w: wgpu::AddressMode::ClampToEdge,
283 mag_filter: wgpu::FilterMode::Nearest,
284 min_filter: wgpu::FilterMode::Nearest,
285 mipmap_filter: wgpu::FilterMode::Nearest,
286 ..Default::default()
287 })
288 }
289
290 pub fn create_bind_group_layout(
292 &self,
293 label: Option<&str>,
294 entries: &[wgpu::BindGroupLayoutEntry],
295 ) -> wgpu::BindGroupLayout {
296 self.context
297 .device
298 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label, entries })
299 }
300
301 pub fn create_bind_group(
303 &self,
304 label: Option<&str>,
305 layout: &wgpu::BindGroupLayout,
306 entries: &[wgpu::BindGroupEntry],
307 ) -> wgpu::BindGroup {
308 self.context
309 .device
310 .create_bind_group(&wgpu::BindGroupDescriptor {
311 label,
312 layout,
313 entries,
314 })
315 }
316
317 pub fn create_pipeline_layout(
319 &self,
320 label: Option<&str>,
321 bind_group_layouts: &[&wgpu::BindGroupLayout],
322 push_constant_ranges: &[wgpu::PushConstantRange],
323 ) -> wgpu::PipelineLayout {
324 self.context
325 .device
326 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
327 label,
328 bind_group_layouts,
329 push_constant_ranges,
330 })
331 }
332
333 pub fn create_render_pipeline(
335 &self,
336 descriptor: &wgpu::RenderPipelineDescriptor,
337 ) -> wgpu::RenderPipeline {
338 self.context.device.create_render_pipeline(descriptor)
339 }
340
341 pub fn create_compute_pipeline(
343 &self,
344 descriptor: &wgpu::ComputePipelineDescriptor,
345 ) -> wgpu::ComputePipeline {
346 self.context.device.create_compute_pipeline(descriptor)
347 }
348
349 pub fn create_command_encoder(&self, label: Option<&str>) -> wgpu::CommandEncoder {
351 self.context
352 .device
353 .create_command_encoder(&wgpu::CommandEncoderDescriptor { label })
354 }
355
356 pub fn submit<I>(&self, command_buffers: I)
358 where
359 I: IntoIterator<Item = wgpu::CommandBuffer>,
360 {
361 self.context.queue.submit(command_buffers);
362 }
363}
364
365pub struct RenderPipelineBuilder<'a> {
367 renderer: &'a Renderer,
368 label: Option<&'a str>,
369 shader: Option<&'a wgpu::ShaderModule>,
370 vertex_entry: &'a str,
371 fragment_entry: &'a str,
372 layout: Option<&'a wgpu::PipelineLayout>,
373 vertex_buffers: Vec<wgpu::VertexBufferLayout<'a>>,
374 color_targets: Vec<Option<wgpu::ColorTargetState>>,
375 primitive: wgpu::PrimitiveState,
376 depth_stencil: Option<wgpu::DepthStencilState>,
377 multisample: wgpu::MultisampleState,
378}
379
380impl<'a> RenderPipelineBuilder<'a> {
381 pub fn new(renderer: &'a Renderer) -> Self {
382 Self {
383 renderer,
384 label: None,
385 shader: None,
386 vertex_entry: "vs_main",
387 fragment_entry: "fs_main",
388 layout: None,
389 vertex_buffers: Vec::new(),
390 color_targets: Vec::new(),
391 primitive: wgpu::PrimitiveState {
392 topology: wgpu::PrimitiveTopology::TriangleList,
393 strip_index_format: None,
394 front_face: wgpu::FrontFace::Ccw,
395 cull_mode: Some(wgpu::Face::Back),
396 polygon_mode: wgpu::PolygonMode::Fill,
397 unclipped_depth: false,
398 conservative: false,
399 },
400 depth_stencil: None,
401 multisample: wgpu::MultisampleState {
402 count: 1,
403 mask: !0,
404 alpha_to_coverage_enabled: false,
405 },
406 }
407 }
408
409 pub fn label(mut self, label: &'a str) -> Self {
410 self.label = Some(label);
411 self
412 }
413
414 pub fn shader(mut self, shader: &'a wgpu::ShaderModule) -> Self {
415 self.shader = Some(shader);
416 self
417 }
418
419 pub fn vertex_entry(mut self, entry: &'a str) -> Self {
420 self.vertex_entry = entry;
421 self
422 }
423
424 pub fn fragment_entry(mut self, entry: &'a str) -> Self {
425 self.fragment_entry = entry;
426 self
427 }
428
429 pub fn layout(mut self, layout: &'a wgpu::PipelineLayout) -> Self {
430 self.layout = Some(layout);
431 self
432 }
433
434 pub fn vertex_buffer(mut self, layout: wgpu::VertexBufferLayout<'a>) -> Self {
435 self.vertex_buffers.push(layout);
436 self
437 }
438
439 pub fn color_target(mut self, target: wgpu::ColorTargetState) -> Self {
440 self.color_targets.push(Some(target));
441 self
442 }
443
444 pub fn primitive(mut self, primitive: wgpu::PrimitiveState) -> Self {
445 self.primitive = primitive;
446 self
447 }
448
449 pub fn depth_stencil(mut self, depth_stencil: wgpu::DepthStencilState) -> Self {
450 self.depth_stencil = Some(depth_stencil);
451 self
452 }
453
454 pub fn multisample(mut self, multisample: wgpu::MultisampleState) -> Self {
455 self.multisample = multisample;
456 self
457 }
458
459 pub fn build(self) -> wgpu::RenderPipeline {
460 let shader = self.shader.expect("Shader module is required");
461 let layout = self.layout.expect("Pipeline layout is required");
462
463 self.renderer
464 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
465 label: self.label,
466 layout: Some(layout),
467 vertex: wgpu::VertexState {
468 module: shader,
469 entry_point: Some(self.vertex_entry),
470 buffers: &self.vertex_buffers,
471 compilation_options: wgpu::PipelineCompilationOptions::default(),
472 },
473 fragment: Some(wgpu::FragmentState {
474 module: shader,
475 entry_point: Some(self.fragment_entry),
476 targets: &self.color_targets,
477 compilation_options: wgpu::PipelineCompilationOptions::default(),
478 }),
479 primitive: self.primitive,
480 depth_stencil: self.depth_stencil,
481 multisample: self.multisample,
482 multiview: None,
483 cache: None,
484 })
485 }
486}
487
488pub struct ComputePipelineBuilder<'a> {
501 renderer: &'a Renderer,
502 label: Option<&'a str>,
503 shader: Option<&'a wgpu::ShaderModule>,
504 entry: &'a str,
505 layout: Option<&'a wgpu::PipelineLayout>,
506}
507
508impl<'a> ComputePipelineBuilder<'a> {
509 pub fn new(renderer: &'a Renderer) -> Self {
511 Self {
512 renderer,
513 label: None,
514 shader: None,
515 entry: "main",
516 layout: None,
517 }
518 }
519
520 pub fn label(mut self, label: &'a str) -> Self {
522 self.label = Some(label);
523 self
524 }
525
526 pub fn shader(mut self, shader: &'a wgpu::ShaderModule) -> Self {
528 self.shader = Some(shader);
529 self
530 }
531
532 pub fn entry(mut self, entry: &'a str) -> Self {
536 self.entry = entry;
537 self
538 }
539
540 pub fn layout(mut self, layout: &'a wgpu::PipelineLayout) -> Self {
542 self.layout = Some(layout);
543 self
544 }
545
546 pub fn build(self) -> wgpu::ComputePipeline {
552 let shader = self.shader.expect("Shader module is required");
553 let layout = self.layout.expect("Pipeline layout is required");
554
555 self.renderer
556 .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
557 label: self.label,
558 layout: Some(layout),
559 module: shader,
560 entry_point: Some(self.entry),
561 compilation_options: wgpu::PipelineCompilationOptions::default(),
562 cache: None,
563 })
564 }
565}