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 pipelines;
11pub mod primitives;
13pub mod resource;
14mod schedule;
15pub mod utils;
17pub mod world;
18
19use bevy_ecs::prelude::*;
20use glam::{UVec3, uvec3};
21
22use crate::{
23 primitives::{mesh_items::MeshItemsBuffer, viewport::ViewportUniform, vitems::VItemsBuffer},
24 resource::{PipelinesPool, RenderTextures},
25 schedule::{FrameTarget, RenderDimensions, RenderGraph, RenderPrepare, install_schedules},
26 utils::{WgpuBuffer, WgpuVecBuffer},
27 world::{CoreItemEntities, RenderFrame, reconcile},
28};
29use utils::WgpuContext;
30
31#[cfg(feature = "profiling")]
32pub static PUFFIN_GPU_PROFILER: std::sync::LazyLock<std::sync::Mutex<puffin::GlobalProfiler>> =
36 std::sync::LazyLock::new(|| std::sync::Mutex::new(puffin::GlobalProfiler::default()));
37
38#[allow(unused)]
39#[cfg(feature = "profiling")]
40mod profiling_utils {
41 use wgpu_profiler::GpuTimerQueryResult;
42
43 pub fn scopes_to_console_recursive(results: &[GpuTimerQueryResult], indentation: u32) {
44 for scope in results {
45 if indentation > 0 {
46 print!("{:<width$}", "|", width = 4);
47 }
48
49 if let Some(time) = &scope.time {
50 println!(
51 "{:.3}μs - {}",
52 (time.end - time.start) * 1000.0 * 1000.0,
53 scope.label
54 );
55 } else {
56 println!("n/a - {}", scope.label);
57 }
58
59 if !scope.nested_queries.is_empty() {
60 scopes_to_console_recursive(&scope.nested_queries, indentation + 1);
61 }
62 }
63 }
64
65 pub fn console_output(
66 results: &Option<Vec<GpuTimerQueryResult>>,
67 enabled_features: wgpu::Features,
68 ) {
69 puffin::profile_scope!("console_output");
70 print!("\x1B[2J\x1B[1;1H"); println!("Welcome to wgpu_profiler demo!");
72 println!();
73 println!(
74 "Press space to write out a trace file that can be viewed in chrome's chrome://tracing"
75 );
76 println!();
77 match results {
78 Some(results) => {
79 scopes_to_console_recursive(results, 0);
80 }
81 None => println!("No profiling results available yet!"),
82 }
83 }
84}
85
86pub struct Renderer {
88 width: u32,
89 height: u32,
90 world: World,
91}
92
93impl Renderer {
94 pub fn width(&self) -> u32 {
95 self.width
96 }
97
98 pub fn height(&self) -> u32 {
99 self.height
100 }
101
102 pub fn ratio(&self) -> f32 {
103 self.width as f32 / self.height as f32
104 }
105
106 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
107 let mut world = World::new();
108 world.insert_resource(ctx.clone());
109 world.insert_resource(RenderDimensions { width, height });
110 world.insert_resource(ResolutionInfo::new(ctx, width, height, oit_layers));
111 world.init_resource::<PipelinesPool>();
112 world.insert_resource(VItemsBuffer::new(ctx));
113 world.insert_resource(MeshItemsBuffer::new(ctx));
114 world.insert_resource(primitives::viewport::ViewportGpuPacket::new(
115 ctx,
116 &ViewportUniform::from_camera_frame(&Default::default(), width, height),
117 ));
118 world.init_resource::<CoreItemEntities>();
119
120 #[cfg(feature = "profiling")]
121 world.insert_resource(schedule::RenderProfiler(
122 wgpu_profiler::GpuProfiler::new(
123 &ctx.device,
124 wgpu_profiler::GpuProfilerSettings::default(),
125 )
126 .unwrap(),
127 ));
128 install_schedules(&mut world);
129
130 Self {
131 width,
132 height,
133 world,
134 }
135 }
136
137 pub fn new_render_textures(&self, ctx: &WgpuContext) -> RenderTextures {
138 RenderTextures::new(ctx, self.width, self.height)
139 }
140
141 pub fn render_frame(
143 &mut self,
144 render_textures: &mut RenderTextures,
145 clear_color: wgpu::Color,
146 frame: &RenderFrame,
147 ) {
148 reconcile(&mut self.world, frame);
149 self.world
150 .insert_resource(FrameTarget::new(render_textures, clear_color));
151 self.world.run_schedule(RenderPrepare);
152 self.world.run_schedule(RenderGraph);
153 }
154}
155
156#[allow(unused)]
157#[derive(Resource)]
158pub struct ResolutionInfo {
159 buffer: WgpuBuffer<UVec3>,
160 pub(crate) pixel_count_buffer: WgpuVecBuffer<u32>,
161 oit_colors_buffer: WgpuVecBuffer<u32>,
162 oit_depths_buffer: WgpuVecBuffer<f32>,
163 bind_group: wgpu::BindGroup,
164}
165
166impl ResolutionInfo {
167 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
168 let buffer = WgpuBuffer::new_init(
169 ctx,
170 Some("ResolutionInfo Buffer"),
171 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
172 uvec3(width, height, oit_layers as u32),
173 );
174
175 let pixel_count = (width * height) as usize;
176 let total_nodes = pixel_count * oit_layers;
177
178 let pixel_count_buffer = WgpuVecBuffer::new(
179 ctx,
180 Some("OIT Pixel Count Buffer"),
181 wgpu::BufferUsages::STORAGE
182 | wgpu::BufferUsages::COPY_DST
183 | wgpu::BufferUsages::COPY_SRC,
184 pixel_count,
185 );
186 let oit_colors_buffer = WgpuVecBuffer::new(
187 ctx,
188 Some("OIT Colors Buffer"),
189 wgpu::BufferUsages::STORAGE
190 | wgpu::BufferUsages::COPY_SRC
191 | wgpu::BufferUsages::COPY_DST,
192 total_nodes,
193 );
194 let oit_depths_buffer = WgpuVecBuffer::new(
195 ctx,
196 Some("OIT Depths Buffer"),
197 wgpu::BufferUsages::STORAGE
198 | wgpu::BufferUsages::COPY_SRC
199 | wgpu::BufferUsages::COPY_DST,
200 total_nodes,
201 );
202
203 let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
204 label: Some("ResolutionInfo BindGroup"),
205 layout: &Self::create_bind_group_layout(ctx),
206 entries: &[
207 wgpu::BindGroupEntry {
208 binding: 0,
209 resource: buffer.as_ref().as_entire_binding(),
210 },
211 wgpu::BindGroupEntry {
212 binding: 1,
213 resource: wgpu::BindingResource::Buffer(
214 pixel_count_buffer.buffer.as_entire_buffer_binding(),
215 ),
216 },
217 wgpu::BindGroupEntry {
218 binding: 2,
219 resource: wgpu::BindingResource::Buffer(
220 oit_colors_buffer.buffer.as_entire_buffer_binding(),
221 ),
222 },
223 wgpu::BindGroupEntry {
224 binding: 3,
225 resource: wgpu::BindingResource::Buffer(
226 oit_depths_buffer.buffer.as_entire_buffer_binding(),
227 ),
228 },
229 ],
230 });
231
232 Self {
233 buffer,
234 bind_group,
235 oit_colors_buffer,
236 oit_depths_buffer,
237 pixel_count_buffer,
238 }
239 }
240 pub fn create_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
276 ctx.device
277 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
278 label: Some("ResolutionInfo BindGroupLayout"),
279 entries: &[
280 wgpu::BindGroupLayoutEntry {
281 binding: 0,
282 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT
283 | wgpu::ShaderStages::COMPUTE,
284 ty: wgpu::BindingType::Buffer {
285 ty: wgpu::BufferBindingType::Uniform,
286 has_dynamic_offset: false,
287 min_binding_size: None,
288 },
289 count: None,
290 },
291 wgpu::BindGroupLayoutEntry {
292 binding: 1,
293 visibility: wgpu::ShaderStages::FRAGMENT,
294 ty: wgpu::BindingType::Buffer {
295 ty: wgpu::BufferBindingType::Storage { read_only: false },
296 has_dynamic_offset: false,
297 min_binding_size: None,
298 },
299 count: None,
300 },
301 wgpu::BindGroupLayoutEntry {
302 binding: 2,
303 visibility: wgpu::ShaderStages::FRAGMENT,
304 ty: wgpu::BindingType::Buffer {
305 ty: wgpu::BufferBindingType::Storage { read_only: false },
306 has_dynamic_offset: false,
307 min_binding_size: None,
308 },
309 count: None,
310 },
311 wgpu::BindGroupLayoutEntry {
312 binding: 3,
313 visibility: wgpu::ShaderStages::FRAGMENT,
314 ty: wgpu::BindingType::Buffer {
315 ty: wgpu::BufferBindingType::Storage { read_only: false },
316 has_dynamic_offset: false,
317 min_binding_size: None,
318 },
319 count: None,
320 },
321 ],
322 })
323 }
324}