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 {
158 Self::new_with_graph(ctx, width, height, oit_layers, Self::build_render_graph())
159 }
160
161 pub fn new_with_graph(
162 ctx: &WgpuContext,
163 width: u32,
164 height: u32,
165 oit_layers: usize,
166 render_graph: GlobalRenderGraph,
167 ) -> Self {
168 let resolution_info = ResolutionInfo::new(ctx, width, height, oit_layers);
169
170 #[cfg(feature = "profiling")]
171 let profiler = wgpu_profiler::GpuProfiler::new(
172 &ctx.device,
173 wgpu_profiler::GpuProfilerSettings::default(),
174 )
175 .unwrap();
176
177 Self {
178 width,
179 height,
180 resolution_info,
181 pipelines: PipelinesPool::default(),
182 packets: RenderPackets::default(),
183 render_graph,
184 merged_buffer: None,
185 merged_mesh_buffer: None,
186 #[cfg(feature = "profiling")]
187 profiler,
188 }
189 }
190
191 pub fn new_render_textures(&self, ctx: &WgpuContext) -> RenderTextures {
192 RenderTextures::new(ctx, self.width, self.height)
193 }
194
195 pub fn render_store_with_pool(
197 &mut self,
198 ctx: &WgpuContext,
199 render_textures: &mut RenderTextures,
200 clear_color: wgpu::Color,
201 store: &CoreItemStore,
202 pool: &mut RenderPool,
203 ) {
204 let camera_frame = &store.camera_frames[0];
206 let viewport = ViewportUniform::from_camera_frame(camera_frame, self.width, self.height);
207 self.packets.push(pool.alloc_packet(ctx, &viewport));
208
209 let merged = self
211 .merged_buffer
212 .get_or_insert_with(|| VItemsBuffer::new(ctx));
213 merged.update(ctx, &store.vitems);
214
215 let merged_mesh = self
217 .merged_mesh_buffer
218 .get_or_insert_with(|| MeshItemsBuffer::new(ctx));
219 merged_mesh.update(ctx, &store.mesh_items);
220
221 {
223 #[cfg(feature = "profiling")]
224 profiling::scope!("render");
225
226 let mut encoder = ctx
227 .device
228 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
229
230 {
231 #[cfg(feature = "profiling")]
232 let mut scope = self.profiler.scope("render", &mut encoder);
233
234 let render_ctx = RenderContext {
235 pipelines: &self.pipelines,
236 render_textures,
237 render_packets: &self.packets,
238 render_pool: pool,
239 wgpu_ctx: ctx,
240 resolution_info: &self.resolution_info,
241 clear_color,
242 merged_buffer: self.merged_buffer.as_ref(),
243 merged_mesh_buffer: self.merged_mesh_buffer.as_ref(),
244 };
245
246 self.render_graph.exec(
247 #[cfg(not(feature = "profiling"))]
248 &mut encoder,
249 #[cfg(feature = "profiling")]
250 &mut scope,
251 render_ctx,
252 );
253 }
254
255 #[cfg(not(feature = "profiling"))]
256 ctx.queue.submit(Some(encoder.finish()));
257
258 #[cfg(feature = "profiling")]
259 {
260 self.profiler.resolve_queries(&mut encoder);
261 {
262 profiling::scope!("submit");
263 ctx.queue.submit(Some(encoder.finish()));
264 }
265
266 self.profiler.end_frame().unwrap();
267
268 ctx.device
269 .poll(wgpu::PollType::wait_indefinitely())
270 .unwrap();
271 let latest_profiler_results = self
272 .profiler
273 .process_finished_frame(ctx.queue.get_timestamp_period());
274 let mut gpu_profiler = PUFFIN_GPU_PROFILER.lock().unwrap();
275 wgpu_profiler::puffin::output_frame_to_puffin(
276 &mut gpu_profiler,
277 &latest_profiler_results.unwrap(),
278 );
279 gpu_profiler.new_frame();
280 }
281
282 render_textures.mark_dirty();
283 }
284
285 self.packets.clear();
286 }
287}
288
289#[allow(unused)]
290pub struct ResolutionInfo {
291 buffer: WgpuBuffer<UVec3>,
292 pub(crate) pixel_count_buffer: WgpuVecBuffer<u32>,
293 oit_colors_buffer: WgpuVecBuffer<u32>,
294 oit_depths_buffer: WgpuVecBuffer<f32>,
295 bind_group: wgpu::BindGroup,
296}
297
298impl ResolutionInfo {
299 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
300 let buffer = WgpuBuffer::new_init(
301 ctx,
302 Some("ResolutionInfo Buffer"),
303 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
304 uvec3(width, height, oit_layers as u32),
305 );
306
307 let pixel_count = (width * height) as usize;
308 let total_nodes = pixel_count * oit_layers;
309
310 let pixel_count_buffer = WgpuVecBuffer::new(
311 ctx,
312 Some("OIT Pixel Count Buffer"),
313 wgpu::BufferUsages::STORAGE
314 | wgpu::BufferUsages::COPY_DST
315 | wgpu::BufferUsages::COPY_SRC,
316 pixel_count,
317 );
318 let oit_colors_buffer = WgpuVecBuffer::new(
319 ctx,
320 Some("OIT Colors Buffer"),
321 wgpu::BufferUsages::STORAGE
322 | wgpu::BufferUsages::COPY_SRC
323 | wgpu::BufferUsages::COPY_DST,
324 total_nodes,
325 );
326 let oit_depths_buffer = WgpuVecBuffer::new(
327 ctx,
328 Some("OIT Depths Buffer"),
329 wgpu::BufferUsages::STORAGE
330 | wgpu::BufferUsages::COPY_SRC
331 | wgpu::BufferUsages::COPY_DST,
332 total_nodes,
333 );
334
335 let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
336 label: Some("ResolutionInfo BindGroup"),
337 layout: &Self::create_bind_group_layout(ctx),
338 entries: &[
339 wgpu::BindGroupEntry {
340 binding: 0,
341 resource: buffer.as_ref().as_entire_binding(),
342 },
343 wgpu::BindGroupEntry {
344 binding: 1,
345 resource: wgpu::BindingResource::Buffer(
346 pixel_count_buffer.buffer.as_entire_buffer_binding(),
347 ),
348 },
349 wgpu::BindGroupEntry {
350 binding: 2,
351 resource: wgpu::BindingResource::Buffer(
352 oit_colors_buffer.buffer.as_entire_buffer_binding(),
353 ),
354 },
355 wgpu::BindGroupEntry {
356 binding: 3,
357 resource: wgpu::BindingResource::Buffer(
358 oit_depths_buffer.buffer.as_entire_buffer_binding(),
359 ),
360 },
361 ],
362 });
363
364 Self {
365 buffer,
366 bind_group,
367 oit_colors_buffer,
368 oit_depths_buffer,
369 pixel_count_buffer,
370 }
371 }
372 pub fn create_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
408 ctx.device
409 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
410 label: Some("ResolutionInfo BindGroupLayout"),
411 entries: &[
412 wgpu::BindGroupLayoutEntry {
413 binding: 0,
414 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT
415 | wgpu::ShaderStages::COMPUTE,
416 ty: wgpu::BindingType::Buffer {
417 ty: wgpu::BufferBindingType::Uniform,
418 has_dynamic_offset: false,
419 min_binding_size: None,
420 },
421 count: None,
422 },
423 wgpu::BindGroupLayoutEntry {
424 binding: 1,
425 visibility: wgpu::ShaderStages::FRAGMENT,
426 ty: wgpu::BindingType::Buffer {
427 ty: wgpu::BufferBindingType::Storage { read_only: false },
428 has_dynamic_offset: false,
429 min_binding_size: None,
430 },
431 count: None,
432 },
433 wgpu::BindGroupLayoutEntry {
434 binding: 2,
435 visibility: wgpu::ShaderStages::FRAGMENT,
436 ty: wgpu::BindingType::Buffer {
437 ty: wgpu::BufferBindingType::Storage { read_only: false },
438 has_dynamic_offset: false,
439 min_binding_size: None,
440 },
441 count: None,
442 },
443 wgpu::BindGroupLayoutEntry {
444 binding: 3,
445 visibility: wgpu::ShaderStages::FRAGMENT,
446 ty: wgpu::BindingType::Buffer {
447 ty: wgpu::BufferBindingType::Storage { read_only: false },
448 has_dynamic_offset: false,
449 min_binding_size: None,
450 },
451 count: None,
452 },
453 ],
454 })
455 }
456}