1#![recursion_limit = "256"]
3#![cfg_attr(docsrs, feature(doc_cfg))]
5#![allow(rustdoc::private_intra_doc_links)]
6#![doc(
7 html_logo_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg",
8 html_favicon_url = "https://raw.githubusercontent.com/AzurIce/ranim/refs/heads/main/assets/ranim.svg"
9)]
10pub mod cpu_probe;
13pub mod pipelines;
15pub mod primitives;
17pub mod resource;
18mod schedule;
19pub mod upload_probe;
22pub mod utils;
24pub mod world;
25
26use bevy_ecs::prelude::*;
27use glam::{UVec3, uvec3};
28
29use crate::{
30 primitives::{mesh_items::MeshItemsBuffer, viewport::ViewportUniform, vitems::VItemsBuffer},
31 resource::{PipelinesPool, RenderTextures},
32 schedule::{FrameTarget, RenderDimensions, RenderGraph, RenderPrepare, install_schedules},
33 utils::{WgpuBuffer, WgpuVecBuffer},
34 world::{CoreItemEntities, RenderFrame, reconcile},
35};
36use utils::WgpuContext;
37
38pub mod profiling_utils {
39 use wgpu_profiler::GpuTimerQueryResult;
40
41 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
65pub struct Renderer {
67 width: u32,
68 height: u32,
69 world: World,
70}
71
72impl Renderer {
73 pub fn width(&self) -> u32 {
74 self.width
75 }
76
77 pub fn height(&self) -> u32 {
78 self.height
79 }
80
81 pub fn ratio(&self) -> f32 {
82 self.width as f32 / self.height as f32
83 }
84
85 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
86 let mut world = World::new();
87 world.insert_resource(ctx.clone());
88 world.insert_resource(RenderDimensions { width, height });
89 world.insert_resource(ResolutionInfo::new(ctx, width, height, oit_layers));
90 world.init_resource::<PipelinesPool>();
91 world.insert_resource(VItemsBuffer::new(ctx));
92 world.insert_resource(MeshItemsBuffer::new(ctx));
93 world.insert_resource(primitives::viewport::ViewportGpuPacket::new(
94 ctx,
95 &ViewportUniform::from_camera_frame(
96 &Default::default(),
97 width,
98 height,
99 primitives::viewport::DEPTH_ORDER_SPAN,
100 ),
101 ));
102 world.init_resource::<CoreItemEntities>();
103 world.insert_resource(schedule::RenderProfiler::new(ctx));
104 install_schedules(&mut world);
105
106 Self {
107 width,
108 height,
109 world,
110 }
111 }
112
113 pub fn new_render_textures(&self, ctx: &WgpuContext) -> RenderTextures {
114 RenderTextures::new(ctx, self.width, self.height)
115 }
116
117 pub fn render_frame(
119 &mut self,
120 render_textures: &mut RenderTextures,
121 clear_color: wgpu::Color,
122 frame: &RenderFrame,
123 ) {
124 {
125 let _span = cpu_probe::span("reconcile");
126 reconcile(&mut self.world, frame);
127 }
128 self.world
129 .insert_resource(FrameTarget::new(render_textures, clear_color));
130 self.world.run_schedule(RenderPrepare);
131 {
132 let _span = cpu_probe::span("render_graph");
133 self.world.run_schedule(RenderGraph);
134 }
135 }
136
137 pub fn take_last_gpu_scopes(&mut self) -> Option<Vec<wgpu_profiler::GpuTimerQueryResult>> {
141 self.world
142 .resource_mut::<schedule::RenderProfiler>()
143 .last_frame_scopes
144 .take()
145 }
146
147 pub fn gpu_timers_supported(&self) -> bool {
149 self.world
150 .resource::<schedule::RenderProfiler>()
151 .inner
152 .is_some()
153 }
154
155 pub fn gpu_timers_enabled(&self) -> bool {
158 self.world
159 .resource::<schedule::RenderProfiler>()
160 .is_enabled()
161 }
162
163 pub fn set_gpu_timers_enabled(&mut self, on: bool) {
166 self.world
167 .resource_mut::<schedule::RenderProfiler>()
168 .set_enabled(on);
169 }
170}
171
172#[allow(unused)]
173#[derive(Resource)]
174pub struct ResolutionInfo {
175 buffer: WgpuBuffer<UVec3>,
176 pub(crate) pixel_count_buffer: WgpuVecBuffer<u32>,
177 oit_colors_buffer: WgpuVecBuffer<u32>,
178 oit_depths_buffer: WgpuVecBuffer<f32>,
179 bind_group: wgpu::BindGroup,
180}
181
182impl ResolutionInfo {
183 pub fn new(ctx: &WgpuContext, width: u32, height: u32, oit_layers: usize) -> Self {
184 let buffer = WgpuBuffer::new_init(
185 ctx,
186 Some("ResolutionInfo Buffer"),
187 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
188 uvec3(width, height, oit_layers as u32),
189 );
190
191 let pixel_count = (width * height) as usize;
192 let total_nodes = pixel_count * oit_layers;
193
194 let pixel_count_buffer = WgpuVecBuffer::new(
195 ctx,
196 Some("OIT Pixel Count Buffer"),
197 wgpu::BufferUsages::STORAGE
198 | wgpu::BufferUsages::COPY_DST
199 | wgpu::BufferUsages::COPY_SRC,
200 pixel_count,
201 );
202 let oit_colors_buffer = WgpuVecBuffer::new(
203 ctx,
204 Some("OIT Colors Buffer"),
205 wgpu::BufferUsages::STORAGE
206 | wgpu::BufferUsages::COPY_SRC
207 | wgpu::BufferUsages::COPY_DST,
208 total_nodes,
209 );
210 let oit_depths_buffer = WgpuVecBuffer::new(
211 ctx,
212 Some("OIT Depths Buffer"),
213 wgpu::BufferUsages::STORAGE
214 | wgpu::BufferUsages::COPY_SRC
215 | wgpu::BufferUsages::COPY_DST,
216 total_nodes,
217 );
218
219 let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
220 label: Some("ResolutionInfo BindGroup"),
221 layout: &Self::create_bind_group_layout(ctx),
222 entries: &[
223 wgpu::BindGroupEntry {
224 binding: 0,
225 resource: buffer.as_ref().as_entire_binding(),
226 },
227 wgpu::BindGroupEntry {
228 binding: 1,
229 resource: wgpu::BindingResource::Buffer(
230 pixel_count_buffer.buffer.as_entire_buffer_binding(),
231 ),
232 },
233 wgpu::BindGroupEntry {
234 binding: 2,
235 resource: wgpu::BindingResource::Buffer(
236 oit_colors_buffer.buffer.as_entire_buffer_binding(),
237 ),
238 },
239 wgpu::BindGroupEntry {
240 binding: 3,
241 resource: wgpu::BindingResource::Buffer(
242 oit_depths_buffer.buffer.as_entire_buffer_binding(),
243 ),
244 },
245 ],
246 });
247
248 Self {
249 buffer,
250 bind_group,
251 oit_colors_buffer,
252 oit_depths_buffer,
253 pixel_count_buffer,
254 }
255 }
256 pub fn create_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
292 ctx.device
293 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
294 label: Some("ResolutionInfo BindGroupLayout"),
295 entries: &[
296 wgpu::BindGroupLayoutEntry {
297 binding: 0,
298 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT
299 | wgpu::ShaderStages::COMPUTE,
300 ty: wgpu::BindingType::Buffer {
301 ty: wgpu::BufferBindingType::Uniform,
302 has_dynamic_offset: false,
303 min_binding_size: None,
304 },
305 count: None,
306 },
307 wgpu::BindGroupLayoutEntry {
308 binding: 1,
309 visibility: wgpu::ShaderStages::FRAGMENT,
310 ty: wgpu::BindingType::Buffer {
311 ty: wgpu::BufferBindingType::Storage { read_only: false },
312 has_dynamic_offset: false,
313 min_binding_size: None,
314 },
315 count: None,
316 },
317 wgpu::BindGroupLayoutEntry {
318 binding: 2,
319 visibility: wgpu::ShaderStages::FRAGMENT,
320 ty: wgpu::BindingType::Buffer {
321 ty: wgpu::BufferBindingType::Storage { read_only: false },
322 has_dynamic_offset: false,
323 min_binding_size: None,
324 },
325 count: None,
326 },
327 wgpu::BindGroupLayoutEntry {
328 binding: 3,
329 visibility: wgpu::ShaderStages::FRAGMENT,
330 ty: wgpu::BindingType::Buffer {
331 ty: wgpu::BufferBindingType::Storage { read_only: false },
332 has_dynamic_offset: false,
333 min_binding_size: None,
334 },
335 count: None,
336 },
337 ],
338 })
339 }
340}