1#![cfg_attr(docsrs, feature(doc_cfg))]
4#![allow(rustdoc::private_intra_doc_links)]
5#![doc(
6 html_logo_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg",
7 html_favicon_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg"
8)]
9pub mod graph;
11pub mod pipelines;
13pub mod primitives;
15pub mod resource;
16pub mod utils;
18
19use glam::{UVec3, uvec3};
20
21use crate::{
22 graph::{AnyGlobalRenderNodeTrait, GlobalRenderGraph, RenderPackets},
23 primitives::{mesh_items::MeshItemsBuffer, viewport::ViewportUniform, vitems::VItemsBuffer},
24 resource::{PipelinesPool, RenderPool, RenderTextures},
25 utils::{WgpuBuffer, WgpuVecBuffer},
26};
27use ranim_core::store::CoreItemStore;
28use utils::WgpuContext;
29
30#[cfg(feature = "profiling")]
31pub static PUFFIN_GPU_PROFILER: std::sync::LazyLock<std::sync::Mutex<puffin::GlobalProfiler>> =
35 std::sync::LazyLock::new(|| std::sync::Mutex::new(puffin::GlobalProfiler::default()));
36
37#[allow(unused)]
38#[cfg(feature = "profiling")]
39mod profiling_utils {
40 use wgpu_profiler::GpuTimerQueryResult;
41
42 pub fn scopes_to_console_recursive(results: &[GpuTimerQueryResult], indentation: u32) {
43 for scope in results {
44 if indentation > 0 {
45 print!("{:<width$}", "|", width = 4);
46 }
47
48 if let Some(time) = &scope.time {
49 println!(
50 "{:.3}μs - {}",
51 (time.end - time.start) * 1000.0 * 1000.0,
52 scope.label
53 );
54 } else {
55 println!("n/a - {}", scope.label);
56 }
57
58 if !scope.nested_queries.is_empty() {
59 scopes_to_console_recursive(&scope.nested_queries, indentation + 1);
60 }
61 }
62 }
63
64 pub fn console_output(
65 results: &Option<Vec<GpuTimerQueryResult>>,
66 enabled_features: wgpu::Features,
67 ) {
68 puffin::profile_scope!("console_output");
69 print!("\x1B[2J\x1B[1;1H"); println!("Welcome to wgpu_profiler demo!");
71 println!();
72 println!(
73 "Press space to write out a trace file that can be viewed in chrome's chrome://tracing"
74 );
75 println!();
76 match results {
77 Some(results) => {
78 scopes_to_console_recursive(results, 0);
79 }
80 None => println!("No profiling results available yet!"),
81 }
82 }
83}
84
85#[derive(Clone, Copy)]
86pub struct RenderContext<'a> {
87 pub render_textures: &'a RenderTextures,
88 pub render_pool: &'a RenderPool,
89 pub render_packets: &'a RenderPackets,
90 pub pipelines: &'a PipelinesPool,
91 pub wgpu_ctx: &'a WgpuContext,
92 pub resolution_info: &'a ResolutionInfo,
93 pub clear_color: wgpu::Color,
94 pub merged_buffer: Option<&'a VItemsBuffer>,
96 pub merged_mesh_buffer: Option<&'a MeshItemsBuffer>,
98}
99
100pub struct Renderer {
102 width: u32,
103 height: u32,
104 pub(crate) resolution_info: ResolutionInfo,
105 pub(crate) pipelines: PipelinesPool,
106 packets: RenderPackets,
107 render_graph: GlobalRenderGraph,
108
109 merged_buffer: Option<VItemsBuffer>,
111 merged_mesh_buffer: Option<MeshItemsBuffer>,
113
114 #[cfg(feature = "profiling")]
115 pub(crate) profiler: wgpu_profiler::GpuProfiler,
116}
117
118impl Renderer {
119 pub fn width(&self) -> u32 {
120 self.width
121 }
122
123 pub fn height(&self) -> u32 {
124 self.height
125 }
126
127 pub fn ratio(&self) -> f32 {
128 self.width as f32 / self.height as f32
129 }
130
131 fn build_render_graph() -> GlobalRenderGraph {
132 use graph::*;
133 let mut render_graph = GlobalRenderGraph::new();
134 let clear = render_graph.insert_node(ClearNode);
135 let view_render = render_graph.insert_node({
136 use graph::view::*;
137 let mut render_graph = ViewRenderGraph::new();
138 let vitem_compute = render_graph.insert_node(MergedVItemComputeNode);
139 let vitem_depth = render_graph.insert_node(MergedVItemDepthNode);
140 let mesh_depth = render_graph.insert_node(MergedMeshItemDepthNode);
141 let vitem_color = render_graph.insert_node(MergedVItemColorNode);
142 let mesh_color = render_graph.insert_node(MergedMeshItemColorNode);
143
144 render_graph.insert_edge(vitem_compute, vitem_depth);
145 render_graph.insert_edge(vitem_depth, vitem_color);
146 render_graph.insert_edge(vitem_depth, mesh_color);
147 render_graph.insert_edge(mesh_depth, mesh_color);
148 render_graph.insert_edge(mesh_depth, vitem_color);
149 render_graph
150 });
151 let oit_resolve = render_graph.insert_node(OITResolveNode);
152 render_graph.insert_edge(clear, view_render);
153 render_graph.insert_edge(view_render, oit_resolve);
154 render_graph
155 }
156
157 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
159 Self::new_with_graph(ctx, width, height, oit_layers, Self::build_render_graph())
160 }
161
162 pub fn new_with_graph(
164 ctx: &WgpuContext,
165 width: u32,
166 height: u32,
167 oit_layers: usize,
168 render_graph: GlobalRenderGraph,
169 ) -> Self {
170 let resolution_info = ResolutionInfo::new(ctx, width, height, oit_layers);
171
172 #[cfg(feature = "profiling")]
173 let profiler = wgpu_profiler::GpuProfiler::new(
174 &ctx.device,
175 wgpu_profiler::GpuProfilerSettings::default(),
176 )
177 .unwrap();
178
179 Self {
180 width,
181 height,
182 resolution_info,
183 pipelines: PipelinesPool::default(),
184 packets: RenderPackets::default(),
185 render_graph,
186 merged_buffer: None,
187 merged_mesh_buffer: None,
188 #[cfg(feature = "profiling")]
189 profiler,
190 }
191 }
192
193 pub fn new_render_textures(&self, ctx: &WgpuContext) -> RenderTextures {
194 RenderTextures::new(ctx, self.width, self.height)
195 }
196
197 pub fn render_store_with_pool(
199 &mut self,
200 ctx: &WgpuContext,
201 render_textures: &mut RenderTextures,
202 clear_color: wgpu::Color,
203 store: &CoreItemStore,
204 pool: &mut RenderPool,
205 ) {
206 let camera_frame = &store.camera_frames[0];
208 let viewport = ViewportUniform::from_camera_frame(camera_frame, self.width, self.height);
209 self.packets.push(pool.alloc_packet(ctx, &viewport));
210
211 let merged = self
213 .merged_buffer
214 .get_or_insert_with(|| VItemsBuffer::new(ctx));
215 merged.update(ctx, &store.vitems);
216
217 let merged_mesh = self
219 .merged_mesh_buffer
220 .get_or_insert_with(|| MeshItemsBuffer::new(ctx));
221 merged_mesh.update(ctx, &store.mesh_items);
222
223 {
225 #[cfg(feature = "profiling")]
226 profiling::scope!("render");
227
228 let mut encoder = ctx
229 .device
230 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
231
232 {
233 #[cfg(feature = "profiling")]
234 let mut scope = self.profiler.scope("render", &mut encoder);
235
236 let render_ctx = RenderContext {
237 pipelines: &self.pipelines,
238 render_textures,
239 render_packets: &self.packets,
240 render_pool: pool,
241 wgpu_ctx: ctx,
242 resolution_info: &self.resolution_info,
243 clear_color,
244 merged_buffer: self.merged_buffer.as_ref(),
245 merged_mesh_buffer: self.merged_mesh_buffer.as_ref(),
246 };
247
248 self.render_graph.exec(
249 #[cfg(not(feature = "profiling"))]
250 &mut encoder,
251 #[cfg(feature = "profiling")]
252 &mut scope,
253 render_ctx,
254 );
255 }
256
257 #[cfg(not(feature = "profiling"))]
258 ctx.queue.submit(Some(encoder.finish()));
259
260 #[cfg(feature = "profiling")]
261 {
262 self.profiler.resolve_queries(&mut encoder);
263 {
264 profiling::scope!("submit");
265 ctx.queue.submit(Some(encoder.finish()));
266 }
267
268 self.profiler.end_frame().unwrap();
269
270 ctx.device
271 .poll(wgpu::PollType::wait_indefinitely())
272 .unwrap();
273 let latest_profiler_results = self
274 .profiler
275 .process_finished_frame(ctx.queue.get_timestamp_period());
276 let mut gpu_profiler = PUFFIN_GPU_PROFILER.lock().unwrap();
277 wgpu_profiler::puffin::output_frame_to_puffin(
278 &mut gpu_profiler,
279 &latest_profiler_results.unwrap(),
280 );
281 gpu_profiler.new_frame();
282 }
283
284 render_textures.mark_dirty();
285 }
286
287 self.packets.clear();
288 }
289}
290
291#[allow(unused)]
292pub struct ResolutionInfo {
293 buffer: WgpuBuffer<UVec3>,
294 pub(crate) pixel_count_buffer: WgpuVecBuffer<u32>,
295 oit_colors_buffer: WgpuVecBuffer<u32>,
296 oit_depths_buffer: WgpuVecBuffer<f32>,
297 bind_group: wgpu::BindGroup,
298}
299
300impl ResolutionInfo {
301 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
302 let buffer = WgpuBuffer::new_init(
303 ctx,
304 Some("ResolutionInfo Buffer"),
305 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
306 uvec3(width, height, oit_layers as u32),
307 );
308
309 let pixel_count = (width * height) as usize;
310 let total_nodes = pixel_count * oit_layers;
311
312 let pixel_count_buffer = WgpuVecBuffer::new(
313 ctx,
314 Some("OIT Pixel Count Buffer"),
315 wgpu::BufferUsages::STORAGE
316 | wgpu::BufferUsages::COPY_DST
317 | wgpu::BufferUsages::COPY_SRC,
318 pixel_count,
319 );
320 let oit_colors_buffer = WgpuVecBuffer::new(
321 ctx,
322 Some("OIT Colors Buffer"),
323 wgpu::BufferUsages::STORAGE
324 | wgpu::BufferUsages::COPY_SRC
325 | wgpu::BufferUsages::COPY_DST,
326 total_nodes,
327 );
328 let oit_depths_buffer = WgpuVecBuffer::new(
329 ctx,
330 Some("OIT Depths Buffer"),
331 wgpu::BufferUsages::STORAGE
332 | wgpu::BufferUsages::COPY_SRC
333 | wgpu::BufferUsages::COPY_DST,
334 total_nodes,
335 );
336
337 let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
338 label: Some("ResolutionInfo BindGroup"),
339 layout: &Self::create_bind_group_layout(ctx),
340 entries: &[
341 wgpu::BindGroupEntry {
342 binding: 0,
343 resource: buffer.as_ref().as_entire_binding(),
344 },
345 wgpu::BindGroupEntry {
346 binding: 1,
347 resource: wgpu::BindingResource::Buffer(
348 pixel_count_buffer.buffer.as_entire_buffer_binding(),
349 ),
350 },
351 wgpu::BindGroupEntry {
352 binding: 2,
353 resource: wgpu::BindingResource::Buffer(
354 oit_colors_buffer.buffer.as_entire_buffer_binding(),
355 ),
356 },
357 wgpu::BindGroupEntry {
358 binding: 3,
359 resource: wgpu::BindingResource::Buffer(
360 oit_depths_buffer.buffer.as_entire_buffer_binding(),
361 ),
362 },
363 ],
364 });
365
366 Self {
367 buffer,
368 bind_group,
369 oit_colors_buffer,
370 oit_depths_buffer,
371 pixel_count_buffer,
372 }
373 }
374 pub fn create_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
410 ctx.device
411 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
412 label: Some("ResolutionInfo BindGroupLayout"),
413 entries: &[
414 wgpu::BindGroupLayoutEntry {
415 binding: 0,
416 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT
417 | wgpu::ShaderStages::COMPUTE,
418 ty: wgpu::BindingType::Buffer {
419 ty: wgpu::BufferBindingType::Uniform,
420 has_dynamic_offset: false,
421 min_binding_size: None,
422 },
423 count: None,
424 },
425 wgpu::BindGroupLayoutEntry {
426 binding: 1,
427 visibility: wgpu::ShaderStages::FRAGMENT,
428 ty: wgpu::BindingType::Buffer {
429 ty: wgpu::BufferBindingType::Storage { read_only: false },
430 has_dynamic_offset: false,
431 min_binding_size: None,
432 },
433 count: None,
434 },
435 wgpu::BindGroupLayoutEntry {
436 binding: 2,
437 visibility: wgpu::ShaderStages::FRAGMENT,
438 ty: wgpu::BindingType::Buffer {
439 ty: wgpu::BufferBindingType::Storage { read_only: false },
440 has_dynamic_offset: false,
441 min_binding_size: None,
442 },
443 count: None,
444 },
445 wgpu::BindGroupLayoutEntry {
446 binding: 3,
447 visibility: wgpu::ShaderStages::FRAGMENT,
448 ty: wgpu::BindingType::Buffer {
449 ty: wgpu::BufferBindingType::Storage { read_only: false },
450 has_dynamic_offset: false,
451 min_binding_size: None,
452 },
453 count: None,
454 },
455 ],
456 })
457 }
458}