Skip to main content

ranim_render/
lib.rs

1//! Rendering stuff in ranim
2// #![warn(missing_docs)]
3#![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)]
9/// The pipelines
10pub mod pipelines;
11/// The basic renderable structs
12pub mod primitives;
13pub mod resource;
14mod schedule;
15/// Rendering related utils
16pub 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")]
32// Since the timing information we get from WGPU may be several frames behind the CPU, we can't report these frames to
33// the singleton returned by `puffin::GlobalProfiler::lock`. Instead, we need our own `puffin::GlobalProfiler` that we
34// can be several frames behind puffin's main global profiler singleton.
35pub 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"); // Clear terminal and put cursor to first row first column
71        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
86// MARK: Renderer
87pub 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    /// Reconcile and render one evaluated frame.
142    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    // This may never be used?
241    // pub fn update(&mut self, ctx: &WgpuContext, resolution: UVec2) {
242    //     self.buffer.set(ctx, resolution);
243
244    //     let pixel_count = (data.screen_size[0] * data.screen_size[1]) as usize;
245    //     let layers = data.oit_layers as usize;
246    //     let total_nodes = pixel_count * layers;
247
248    //     let mut bind_group_dirty = false;
249
250    //     if self.pixel_count_buffer.len() != pixel_count {
251    //         self.pixel_count_buffer.resize(ctx, pixel_count);
252    //         bind_group_dirty = true;
253    //     }
254
255    //     if self.oit_colors_buffer.len() != total_nodes {
256    //         self.oit_colors_buffer.resize(ctx, total_nodes);
257    //         bind_group_dirty = true;
258    //     }
259
260    //     if self.oit_depths_buffer.len() != total_nodes {
261    //         self.oit_depths_buffer.resize(ctx, total_nodes);
262    //         bind_group_dirty = true;
263    //     }
264
265    //     if bind_group_dirty {
266    //         self.uniforms_bind_group = ViewportBindGroup::new(
267    //             ctx,
268    //             &self.uniforms_buffer,
269    //             &self.pixel_count_buffer,
270    //             &self.oit_colors_buffer,
271    //             &self.oit_depths_buffer,
272    //         );
273    //     }
274    // }
275    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}