Skip to main content

ursus_core/vulkan/
renderer.rs

1use crate::assets::storage::GpuAssetServer;
2use crate::render::frame_pipeline::render_pipeline::{PipelineHandles, RenderPipeline};
3use crate::render::graph::RenderGraph;
4use crate::render::resource::ResourcePool;
5use crate::render::world::RWorld;
6use crate::vulkan::core::commands::Commands;
7use crate::vulkan::core::sync::FrameSync;
8use crate::vulkan::core::DeviceContext;
9use crate::vulkan::timestamps::GpuFrameTimes;
10use crate::vulkan::{Device, VulkanContext};
11use ash::vk;
12use std::sync::Arc;
13
14const FRAMES_IN_FLIGHT: u32 = 3;
15
16pub trait DynRenderer: Send {
17    fn draw_frame(
18        &mut self,
19        ctx: &VulkanContext,
20        render_world: &RWorld,
21        gpu_assets: &mut GpuAssetServer,
22    ) -> anyhow::Result<bool>;
23
24    fn resize_output(&mut self, w: u32, h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()>;
25    fn resize_internal(&mut self, w: u32, h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()>;
26
27    fn last_frame_times(&self) -> Option<&GpuFrameTimes>;
28
29    fn exposure(&self) -> f32;
30    fn set_exposure(&mut self, v: f32);
31
32    fn fsr_sharpness(&self) -> f32;
33    fn set_fsr_sharpness(&mut self, v: f32);
34}
35
36pub struct Renderer<P: RenderPipeline> {
37    pub graph: RenderGraph,
38    pub pipeline: P,
39
40    pub commands: Commands,
41    pub(crate) frames: Vec<FrameSync>,
42    pub(crate) acquire_semaphores: Vec<vk::Semaphore>,
43    pub(crate) present_semaphores: Vec<vk::Semaphore>,
44    pub(crate) current_frame: usize,
45    pub(crate) swapchain_loader: ash::khr::swapchain::Device,
46    pub(crate) device: Arc<Device>,
47    pub(crate) handles: PipelineHandles,
48
49    pub exposure: f32,
50    pub fsr_sharpness: f32,
51}
52
53impl<P: RenderPipeline> Renderer<P> {
54    pub fn draw_frame(
55        &mut self,
56        ctx: &VulkanContext,
57        render_world: &RWorld,
58        gpu_assets: &mut GpuAssetServer,
59    ) -> anyhow::Result<bool> {
60        puffin::profile_function!();
61
62        let frame = &self.frames[self.current_frame];
63        let cmd = self.commands.buffers[self.current_frame];
64        let device = &ctx.device.handle;
65        let swapchain = ctx.swapchain.as_ref().unwrap();
66
67        unsafe {
68            puffin::profile_scope!("wait_for_fences");
69            device.wait_for_fences(&[frame.render_fence], true, u64::MAX)?;
70        }
71
72        let acquire_sem = self.acquire_semaphores[self.current_frame];
73        let (image_index, suboptimal) = match unsafe {
74            self.swapchain_loader.acquire_next_image(swapchain.handle, u64::MAX, acquire_sem, vk::Fence::null())
75        } {
76            Ok(r) => r,
77            Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => return Ok(true),
78            Err(e) => return Err(e.into()),
79        };
80
81        self.graph.pool.update_external(
82            self.handles.swapchain,
83            swapchain.images[image_index as usize],
84            swapchain.image_views[image_index as usize],
85            swapchain.extent,
86        );
87        self.graph.reset_external_layouts();
88
89        unsafe { device.reset_fences(&[frame.render_fence])? };
90        unsafe {
91            device.reset_command_buffer(cmd, vk::CommandBufferResetFlags::empty())?;
92            device.begin_command_buffer(
93                cmd,
94                &vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
95            )?;
96        }
97
98        {
99            puffin::profile_scope!("graph_execute");
100            self.graph.execute(device, cmd, render_world, gpu_assets)?;
101        }
102
103        unsafe { device.end_command_buffer(cmd)? };
104
105        let present_sem = self.present_semaphores[image_index as usize];
106
107        unsafe {
108            puffin::profile_scope!("queue_submit");
109            let wait_info = vk::SemaphoreSubmitInfo::default()
110                .semaphore(acquire_sem)
111                .stage_mask(vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT);
112
113            let signal_info = vk::SemaphoreSubmitInfo::default()
114                .semaphore(present_sem)
115                .stage_mask(vk::PipelineStageFlags2::ALL_GRAPHICS);
116
117            let cmd_info = vk::CommandBufferSubmitInfo::default().command_buffer(cmd);
118
119            device.queue_submit2(
120                ctx.device.graphics_queue,
121                &[vk::SubmitInfo2::default()
122                    .wait_semaphore_infos(std::slice::from_ref(&wait_info))
123                    .command_buffer_infos(std::slice::from_ref(&cmd_info))
124                    .signal_semaphore_infos(std::slice::from_ref(&signal_info))],
125                frame.render_fence,
126            )?;
127            self.graph.mark_submitted();
128        }
129
130        let needs_recreate = match unsafe {
131            puffin::profile_scope!("queue_present");
132            let signal_semaphores = [present_sem];
133            self.swapchain_loader.queue_present(
134                ctx.device.present_queue,
135                &vk::PresentInfoKHR::default()
136                    .wait_semaphores(&signal_semaphores)
137                    .swapchains(&[swapchain.handle])
138                    .image_indices(&[image_index]),
139            )
140        } {
141            Ok(false) => false,
142            Ok(true) => true,
143            Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => true,
144            Err(e) => return Err(e.into()),
145        };
146
147        self.current_frame = (self.current_frame + 1) % FRAMES_IN_FLIGHT as usize;
148        Ok(needs_recreate || suboptimal)
149    }
150
151    pub fn resize_output(&mut self, new_w: u32, new_h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()> {
152        unsafe { self.device.handle.device_wait_idle()? };
153        self.graph.resize_output((new_w, new_h), gpu)?;
154        self.pipeline.on_resize(&mut self.graph, new_w, new_h)
155    }
156
157    pub fn resize_internal(&mut self, new_w: u32, new_h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()> {
158        unsafe { self.device.handle.device_wait_idle()? };
159        self.graph.resize_internal((new_w, new_h), gpu)?;
160        self.pipeline.on_resize_internal(&mut self.graph, new_w, new_h)
161    }
162}
163
164impl<P: RenderPipeline> DynRenderer for Renderer<P> {
165    fn draw_frame(
166        &mut self,
167        ctx: &VulkanContext,
168        render_world: &RWorld,
169        gpu_assets: &mut GpuAssetServer,
170    ) -> anyhow::Result<bool> {
171        self.draw_frame(ctx, render_world, gpu_assets)
172    }
173
174    fn resize_output(&mut self, w: u32, h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()> {
175        self.resize_output(w, h, gpu)
176    }
177
178    fn resize_internal(&mut self, w: u32, h: u32, gpu: &GpuAssetServer) -> anyhow::Result<()> {
179        self.resize_internal(w, h, gpu)
180    }
181
182    fn last_frame_times(&self) -> Option<&GpuFrameTimes> {
183        self.graph.last_frame_times.as_ref()
184    }
185
186    fn exposure(&self) -> f32 {
187        self.exposure
188    }
189    fn set_exposure(&mut self, v: f32) {
190        self.exposure = v;
191    }
192
193    fn fsr_sharpness(&self) -> f32 {
194        self.fsr_sharpness
195    }
196    fn set_fsr_sharpness(&mut self, v: f32) {
197        self.fsr_sharpness = v;
198    }
199}
200
201pub fn build_dyn_renderer<P: RenderPipeline + Default + 'static>(
202    ctx: &VulkanContext,
203    gpu_assets: &mut GpuAssetServer,
204    prev_exposure: f32,
205    prev_fsr_sharpness: f32,
206) -> anyhow::Result<Box<dyn DynRenderer>> {
207    let swapchain = ctx.swapchain.as_ref().unwrap();
208    let image_count = swapchain.images.len();
209
210    let acquire_semaphores: Vec<vk::Semaphore> = (0..image_count)
211        .map(|_| {
212            let info = vk::SemaphoreCreateInfo::default();
213            unsafe { ctx.device.handle.create_semaphore(&info, None) }
214        })
215        .collect::<Result<_, _>>()?;
216
217    let present_semaphores: Vec<vk::Semaphore> = (0..image_count)
218        .map(|_| unsafe { ctx.device.handle.create_semaphore(&vk::SemaphoreCreateInfo::default(), None) })
219        .collect::<Result<_, _>>()?;
220
221    let pool = ResourcePool::new(
222        ctx.device.handle.clone(),
223        ctx.device.physical,
224        ctx.instance.handle.clone(),
225        ctx.debug_utils.clone(),
226    );
227
228    let mut graph = RenderGraph::new(
229        pool,
230        ctx.device.handle.clone(),
231        (1280, 720),
232        (swapchain.extent.width, swapchain.extent.height),
233        ctx.debug_utils.clone(),
234    );
235
236    let handles = P::build(ctx, gpu_assets, &mut graph)?;
237    graph.allocate(gpu_assets)?;
238    graph.compile()?;
239
240    let frames: Vec<_> =
241        (0..FRAMES_IN_FLIGHT).map(|_| FrameSync::new(&ctx.device.handle)).collect::<anyhow::Result<_>>()?;
242
243    let commands = Commands::new(&ctx.device.handle, ctx.device.graphics_family, FRAMES_IN_FLIGHT)?;
244
245    graph.enable_timestamps(
246        DeviceContext {
247            device: &ctx.device.handle,
248            physical_device: ctx.device.physical,
249            instance: &ctx.instance.handle,
250        },
251        FRAMES_IN_FLIGHT,
252        commands.pool,
253        ctx.device.graphics_queue,
254    )?;
255
256    let swapchain_loader = ash::khr::swapchain::Device::new(&ctx.instance.handle, &ctx.device.handle);
257
258    Ok(Box::new(Renderer::<P> {
259        graph,
260        pipeline: Default::default(),
261        commands,
262        frames,
263        acquire_semaphores,
264        present_semaphores,
265        current_frame: 0,
266        swapchain_loader,
267        device: ctx.device.clone(),
268        handles,
269        exposure: prev_exposure,
270        fsr_sharpness: prev_fsr_sharpness,
271    }))
272}
273
274impl<P: RenderPipeline> Drop for Renderer<P> {
275    fn drop(&mut self) {
276        unsafe {
277            self.device.handle.device_wait_idle().ok();
278            for &sem in &self.acquire_semaphores {
279                self.device.handle.destroy_semaphore(sem, None);
280            }
281            for &sem in &self.present_semaphores {
282                self.device.handle.destroy_semaphore(sem, None);
283            }
284        }
285    }
286}