1use std::{ops::Deref, rc::Rc, sync::Arc};
22
23use crate::{
24 environment::Environment,
25 kernel::Kernel,
26 plugin::Plugin,
27 render::{
28 error::RenderError,
29 eventually::Eventually,
30 graph::{EmptyNode, RenderGraph},
31 main_pass::{MainPassDriverNode, MainPassNode},
32 resource::{Head, Surface, Texture, TextureView},
33 settings::{RendererSettings, WgpuSettings},
34 systems::{
35 cleanup_system::cleanup_system, resource_system::ResourceSystem,
36 sort_phase_system::sort_phase_system,
37 tile_view_pattern_system::tile_view_pattern_system,
38 },
39 },
40 schedule::{Schedule, StageLabel},
41 tcs::{
42 system::{stage::SystemStage, SystemContainer},
43 world::World,
44 },
45 window::{HeadedMapWindow, MapWindow},
46};
47
48pub mod graph;
49pub mod resource;
50mod systems;
51
52mod graph_runner;
54mod main_pass;
55pub mod shaders;
56mod translucent_pass; pub mod builder;
60pub mod camera;
61pub mod error;
62pub mod eventually;
63pub mod render_commands;
64pub mod render_phase;
65pub mod settings;
66pub mod tile_view_pattern;
67pub mod view_state;
68
69pub use shaders::ShaderVertex;
70
71use crate::{
72 render::{
73 render_phase::{LayerItem, RenderPhase, TileMaskItem, TranslucentItem},
74 systems::{graph_runner_system::GraphRunnerSystem, upload_system::upload_system},
75 tile_view_pattern::{ViewTileSources, WgpuTileViewPattern},
76 translucent_pass::TranslucentPassNode,
77 },
78 window::PhysicalSize,
79};
80
81pub(crate) const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; #[derive(Debug, Hash, PartialEq, Eq, Clone)]
85pub enum RenderStageLabel {
86 Extract,
88
89 Prepare,
92
93 Queue,
98
99 PhaseSort,
101
102 Render,
105
106 Cleanup,
108}
109
110impl StageLabel for RenderStageLabel {
111 fn dyn_clone(&self) -> Box<dyn StageLabel> {
112 Box::new(self.clone())
113 }
114}
115
116pub struct RenderResources {
117 pub surface: Surface,
118 pub render_target: Eventually<TextureView>,
119 pub depth_texture: Eventually<Texture>,
120 pub multisampling_texture: Eventually<Option<Texture>>,
121}
122
123impl RenderResources {
124 pub fn new(surface: Surface) -> Self {
125 Self {
126 render_target: Default::default(),
127 depth_texture: Default::default(),
128 multisampling_texture: Default::default(),
129 surface,
130 }
131 }
132
133 pub fn recreate_surface<MW>(
134 &mut self,
135 window: &MW,
136 instance: &wgpu::Instance,
137 ) -> Result<(), RenderError>
138 where
139 MW: MapWindow + HeadedMapWindow,
140 {
141 self.surface.recreate::<MW>(window, instance)
142 }
143
144 pub fn surface(&self) -> &Surface {
145 &self.surface
146 }
147}
148
149pub struct Renderer {
150 pub instance: wgpu::Instance,
151 pub device: Arc<wgpu::Device>, pub queue: wgpu::Queue,
153 pub adapter: wgpu::Adapter,
154
155 pub wgpu_settings: WgpuSettings,
156 pub settings: RendererSettings,
157
158 pub resources: RenderResources,
159 pub render_graph: RenderGraph,
160}
161
162impl Renderer {
163 pub async fn initialize<MW>(
166 window: &MW,
167 wgpu_settings: WgpuSettings,
168 settings: RendererSettings,
169 ) -> Result<Self, RenderError>
170 where
171 MW: MapWindow + HeadedMapWindow,
172 {
173 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
174 backends: wgpu_settings.backends.unwrap_or(wgpu::Backends::all()),
175 flags: Default::default(),
176 dx12_shader_compiler: Default::default(),
177 gles_minor_version: Default::default(),
178 });
179
180 let surface: wgpu::Surface = unsafe {
181 instance
182 .create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window.handle())?)?
183 };
184
185 let (adapter, device, queue) = Self::request_device(
186 &instance,
187 &wgpu_settings,
188 &wgpu::RequestAdapterOptions {
189 power_preference: wgpu_settings.power_preference,
190 force_fallback_adapter: false,
191 compatible_surface: Some(&surface),
192 },
193 )
194 .await?;
195
196 let surface = Surface::from_surface(surface, &adapter, window, &settings);
197
198 match surface.head() {
199 Head::Headed(window) => window.configure(&device),
200 Head::Headless(_) => {}
201 }
202
203 Ok(Self {
204 instance,
205 device: Arc::new(device),
206 queue,
207 adapter,
208 wgpu_settings,
209 settings,
210 resources: RenderResources::new(surface),
211 render_graph: Default::default(),
212 })
213 }
214
215 pub async fn initialize_headless<MW>(
216 window: &MW,
217 wgpu_settings: WgpuSettings,
218 settings: RendererSettings,
219 ) -> Result<Self, RenderError>
220 where
221 MW: MapWindow,
222 {
223 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
224 backends: wgpu_settings.backends.unwrap_or(wgpu::Backends::all()),
225 flags: Default::default(),
226 dx12_shader_compiler: Default::default(),
227 gles_minor_version: Default::default(),
228 });
229
230 let (adapter, device, queue) = Self::request_device(
231 &instance,
232 &wgpu_settings,
233 &wgpu::RequestAdapterOptions {
234 power_preference: wgpu_settings.power_preference,
235 force_fallback_adapter: false,
236 compatible_surface: None,
237 },
238 )
239 .await?;
240
241 let surface = Surface::from_image(&device, window, &settings);
242
243 Ok(Self {
244 instance,
245 device: Arc::new(device),
246 queue,
247 adapter,
248 wgpu_settings,
249 settings,
250 resources: RenderResources::new(surface),
251 render_graph: Default::default(),
252 })
253 }
254
255 pub fn resize_surface(&mut self, size: PhysicalSize) {
256 self.resources.surface.resize(size)
257 }
258
259 async fn request_device(
261 instance: &wgpu::Instance,
262 settings: &WgpuSettings,
263 request_adapter_options: &wgpu::RequestAdapterOptions<'_, '_>,
264 ) -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue), RenderError> {
265 let adapter = instance
266 .request_adapter(request_adapter_options)
267 .await
268 .ok_or(RenderError::RequestAdaptor)?;
269
270 let adapter_info = adapter.get_info();
271
272 #[cfg(not(target_arch = "wasm32"))]
273 let trace_path = if settings.record_trace {
274 let path = std::path::Path::new("wgpu_trace");
275 let _ = std::fs::create_dir(path);
277 Some(path)
278 } else {
279 None
280 };
281
282 #[cfg(target_arch = "wasm32")]
283 let trace_path = None;
284
285 let mut features =
286 adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
287 if adapter_info.device_type == wgpu::DeviceType::DiscreteGpu {
288 features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS;
293 }
294 let mut limits = adapter.limits();
295
296 if let Some(disabled_features) = settings.disabled_features {
298 features -= disabled_features;
299 }
300 features |= settings.features;
302
303 if let Some(constrained_limits) = settings.constrained_limits.as_ref() {
305 limits = wgpu::Limits {
311 max_texture_dimension_1d: limits
312 .max_texture_dimension_1d
313 .min(constrained_limits.max_texture_dimension_1d),
314 max_texture_dimension_2d: limits
315 .max_texture_dimension_2d
316 .min(constrained_limits.max_texture_dimension_2d),
317 max_texture_dimension_3d: limits
318 .max_texture_dimension_3d
319 .min(constrained_limits.max_texture_dimension_3d),
320 max_texture_array_layers: limits
321 .max_texture_array_layers
322 .min(constrained_limits.max_texture_array_layers),
323 max_bind_groups: limits
324 .max_bind_groups
325 .min(constrained_limits.max_bind_groups),
326 max_bindings_per_bind_group: limits
327 .max_bindings_per_bind_group
328 .min(constrained_limits.max_bindings_per_bind_group),
329 max_dynamic_uniform_buffers_per_pipeline_layout: limits
330 .max_dynamic_uniform_buffers_per_pipeline_layout
331 .min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
332 max_dynamic_storage_buffers_per_pipeline_layout: limits
333 .max_dynamic_storage_buffers_per_pipeline_layout
334 .min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
335 max_sampled_textures_per_shader_stage: limits
336 .max_sampled_textures_per_shader_stage
337 .min(constrained_limits.max_sampled_textures_per_shader_stage),
338 max_samplers_per_shader_stage: limits
339 .max_samplers_per_shader_stage
340 .min(constrained_limits.max_samplers_per_shader_stage),
341 max_storage_buffers_per_shader_stage: limits
342 .max_storage_buffers_per_shader_stage
343 .min(constrained_limits.max_storage_buffers_per_shader_stage),
344 max_storage_textures_per_shader_stage: limits
345 .max_storage_textures_per_shader_stage
346 .min(constrained_limits.max_storage_textures_per_shader_stage),
347 max_uniform_buffers_per_shader_stage: limits
348 .max_uniform_buffers_per_shader_stage
349 .min(constrained_limits.max_uniform_buffers_per_shader_stage),
350 max_uniform_buffer_binding_size: limits
351 .max_uniform_buffer_binding_size
352 .min(constrained_limits.max_uniform_buffer_binding_size),
353 max_storage_buffer_binding_size: limits
354 .max_storage_buffer_binding_size
355 .min(constrained_limits.max_storage_buffer_binding_size),
356 max_vertex_buffers: limits
357 .max_vertex_buffers
358 .min(constrained_limits.max_vertex_buffers),
359 max_vertex_attributes: limits
360 .max_vertex_attributes
361 .min(constrained_limits.max_vertex_attributes),
362 max_vertex_buffer_array_stride: limits
363 .max_vertex_buffer_array_stride
364 .min(constrained_limits.max_vertex_buffer_array_stride),
365 max_push_constant_size: limits
366 .max_push_constant_size
367 .min(constrained_limits.max_push_constant_size),
368 min_uniform_buffer_offset_alignment: limits
369 .min_uniform_buffer_offset_alignment
370 .max(constrained_limits.min_uniform_buffer_offset_alignment),
371 min_storage_buffer_offset_alignment: limits
372 .min_storage_buffer_offset_alignment
373 .max(constrained_limits.min_storage_buffer_offset_alignment),
374 max_inter_stage_shader_components: limits
375 .max_inter_stage_shader_components
376 .min(constrained_limits.max_inter_stage_shader_components),
377 max_color_attachments: limits
378 .max_color_attachments
379 .min(constrained_limits.max_color_attachments),
380 max_color_attachment_bytes_per_sample: limits
381 .max_color_attachment_bytes_per_sample
382 .min(constrained_limits.max_color_attachment_bytes_per_sample),
383 max_compute_workgroup_storage_size: limits
384 .max_compute_workgroup_storage_size
385 .min(constrained_limits.max_compute_workgroup_storage_size),
386 max_compute_invocations_per_workgroup: limits
387 .max_compute_invocations_per_workgroup
388 .min(constrained_limits.max_compute_invocations_per_workgroup),
389 max_compute_workgroup_size_x: limits
390 .max_compute_workgroup_size_x
391 .min(constrained_limits.max_compute_workgroup_size_x),
392 max_compute_workgroup_size_y: limits
393 .max_compute_workgroup_size_y
394 .min(constrained_limits.max_compute_workgroup_size_y),
395 max_compute_workgroup_size_z: limits
396 .max_compute_workgroup_size_z
397 .min(constrained_limits.max_compute_workgroup_size_z),
398 max_compute_workgroups_per_dimension: limits
399 .max_compute_workgroups_per_dimension
400 .min(constrained_limits.max_compute_workgroups_per_dimension),
401 min_subgroup_size: 0,
402 max_buffer_size: limits
403 .max_buffer_size
404 .min(constrained_limits.max_buffer_size),
405 max_non_sampler_bindings: limits
406 .max_non_sampler_bindings
407 .min(constrained_limits.max_non_sampler_bindings),
408 max_subgroup_size: 0,
409 };
410 }
411
412 let (device, queue) = adapter
413 .request_device(
414 &wgpu::DeviceDescriptor {
415 label: settings.device_label.as_ref().map(|a| a.as_ref()),
416 required_features: features,
417 required_limits: limits,
418 memory_hints: wgpu::MemoryHints::default(),
419 },
420 trace_path,
421 )
422 .await?;
423 Ok((adapter, device, queue))
424 }
425
426 pub fn instance(&self) -> &wgpu::Instance {
427 &self.instance
428 }
429 pub fn device(&self) -> &wgpu::Device {
430 &self.device
431 }
432 pub fn queue(&self) -> &wgpu::Queue {
433 &self.queue
434 }
435 pub fn state(&self) -> &RenderResources {
436 &self.resources
437 }
438 pub fn surface(&self) -> &Surface {
439 &self.resources.surface
440 }
441}
442
443#[cfg(test)]
444#[cfg(not(target_arch = "wasm32"))]
445mod tests {
446 use crate::{
447 tcs::world::World,
448 window::{MapWindow, MapWindowConfig, PhysicalSize, WindowCreateError},
449 };
450
451 #[derive(Clone)]
452 pub struct HeadlessMapWindowConfig {
453 size: PhysicalSize,
454 }
455
456 impl MapWindowConfig for HeadlessMapWindowConfig {
457 type MapWindow = HeadlessMapWindow;
458
459 fn create(&self) -> Result<Self::MapWindow, WindowCreateError> {
460 Ok(Self::MapWindow { size: self.size })
461 }
462 }
463
464 pub struct HeadlessMapWindow {
465 size: PhysicalSize,
466 }
467
468 impl MapWindow for HeadlessMapWindow {
469 fn size(&self) -> PhysicalSize {
470 self.size
471 }
472 }
473
474 #[tokio::test]
475 async fn test_render() {
476 use log::LevelFilter;
477
478 use crate::render::{
479 graph::RenderGraph, graph_runner::RenderGraphRunner, resource::Surface,
480 RenderResources, RendererSettings,
481 };
482
483 let _ = env_logger::builder()
484 .filter_level(LevelFilter::Trace)
485 .is_test(true)
486 .try_init();
487 let graph = RenderGraph::default();
488
489 let backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::all());
490 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
491 backends,
492 flags: Default::default(),
493 dx12_shader_compiler: Default::default(),
494 gles_minor_version: Default::default(),
495 });
496 let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, None)
497 .await
498 .expect("Unable to initialize adapter");
499
500 let (device, queue) = adapter
501 .request_device(
502 &wgpu::DeviceDescriptor {
503 label: None,
504 required_features: wgpu::Features::default(),
505 required_limits: wgpu::Limits::default(),
506 memory_hints: wgpu::MemoryHints::default(),
507 },
508 None,
509 )
510 .await
511 .expect("Unable to request device");
512
513 let render_state = RenderResources::new(Surface::from_image(
514 &device,
515 &HeadlessMapWindow {
516 size: PhysicalSize::new(100, 100).expect("invalid headless map size"),
517 },
518 &RendererSettings::default(),
519 ));
520
521 let world = World::default();
522 RenderGraphRunner::run(&graph, &device, &queue, &render_state, &world)
523 .expect("failed to run graph runner");
524 }
525}
526
527pub mod main_graph {
532 pub mod input {}
534 pub mod node {
536 pub const MAIN_PASS_DEPENDENCIES: &str = "main_pass_dependencies";
537 pub const MAIN_PASS_DRIVER: &str = "main_pass_driver";
538 }
539}
540
541mod draw_graph {
543 pub const NAME: &str = "draw";
544 pub mod input {}
546 pub mod node {
548 pub const MAIN_PASS: &str = "main_pass";
549 pub const TRANSLUCENT_PASS: &str = "translucent_pass";
550 }
551}
552
553pub struct MaskPipeline(pub wgpu::RenderPipeline);
554impl Deref for MaskPipeline {
555 type Target = wgpu::RenderPipeline;
556
557 fn deref(&self) -> &Self::Target {
558 &self.0
559 }
560}
561
562#[derive(Default)]
564pub struct RenderPlugin;
565
566impl<E: Environment> Plugin<E> for RenderPlugin {
567 fn build(
568 &self,
569 schedule: &mut Schedule,
570 _kernel: Rc<Kernel<E>>,
571 world: &mut World,
572 graph: &mut RenderGraph,
573 ) {
574 let resources = &mut world.resources;
575
576 let mut draw_graph = RenderGraph::default();
577 draw_graph.add_node(draw_graph::node::MAIN_PASS, MainPassNode::new());
579 draw_graph.add_node(
581 draw_graph::node::TRANSLUCENT_PASS,
582 TranslucentPassNode::new(),
583 );
584 let input_node_id = draw_graph.set_input(vec![]);
586 draw_graph
588 .add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)
589 .expect("main pass or draw node does not exist");
590 draw_graph
591 .add_node_edge(
592 draw_graph::node::MAIN_PASS,
593 draw_graph::node::TRANSLUCENT_PASS,
594 )
595 .expect("main pass or draw node does not exist");
596
597 graph.add_sub_graph(draw_graph::NAME, draw_graph);
598 graph.add_node(main_graph::node::MAIN_PASS_DEPENDENCIES, EmptyNode);
599 graph.add_node(main_graph::node::MAIN_PASS_DRIVER, MainPassDriverNode);
600 graph
601 .add_node_edge(
602 main_graph::node::MAIN_PASS_DEPENDENCIES,
603 main_graph::node::MAIN_PASS_DRIVER,
604 )
605 .expect("main pass driver or dependencies do not exist");
606
607 resources.init::<RenderPhase<LayerItem>>();
609 resources.init::<RenderPhase<TileMaskItem>>();
610 resources.init::<RenderPhase<TranslucentItem>>();
611 resources.insert(Eventually::<WgpuTileViewPattern>::Uninitialized);
613 resources.init::<ViewTileSources>();
614 resources.insert(Eventually::<MaskPipeline>::Uninitialized);
616
617 schedule.add_stage(RenderStageLabel::Extract, SystemStage::default());
618 schedule.add_stage(
619 RenderStageLabel::Prepare,
620 SystemStage::default().with_system(SystemContainer::new(ResourceSystem)),
621 );
622 schedule.add_stage(
623 RenderStageLabel::Queue,
624 SystemStage::default()
625 .with_system(tile_view_pattern_system)
626 .with_system(upload_system),
627 );
628 schedule.add_stage(
629 RenderStageLabel::PhaseSort,
630 SystemStage::default().with_system(sort_phase_system),
631 );
632 schedule.add_stage(
633 RenderStageLabel::Render,
634 SystemStage::default().with_system(SystemContainer::new(GraphRunnerSystem)),
635 );
636 schedule.add_stage(
637 RenderStageLabel::Cleanup,
638 SystemStage::default().with_system(cleanup_system),
639 );
640 }
641}