rafx_renderer/
renderer.rs

1use rafx_assets::AssetManager;
2use rafx_assets::AssetManagerRenderResource;
3use rafx_assets::AssetResource;
4use rafx_assets::Handle;
5use rafx_framework::render_features::render_features_prelude::*;
6use rafx_framework::visibility::{VisibilityConfig, VisibilityResource};
7use rafx_framework::{ImageViewResource, ResourceArc};
8use rafx_framework::{RenderResources, ResourceLookupSet};
9use std::sync::{Arc, Mutex};
10use std::time::Duration;
11
12use super::*;
13
14use super::{RenderFeaturePlugin, RendererPipelinePlugin, ViewportsResource};
15use crate::main_view_render_resource::PreviousMainViewInfo;
16use rafx_api::extra::upload::{RafxTransferUpload, RafxUploadError};
17use rafx_api::{
18    RafxDeviceContext, RafxError, RafxPresentableFrame, RafxQueue, RafxResourceType, RafxResult,
19    RafxSwapchainHelper,
20};
21use rafx_framework::upload::image_upload::ImageUploadParams;
22use rafx_framework::upload::{image_upload, GpuImageData, GpuImageDataColorSpace};
23
24// Encapsulates a blocking wait to load assets. The load may be blocked by work that plugins need to perform, so they
25// need to be ticked. A caller can request a load context from the renderer and call wait_for_asset_to_load on it,
26// which will ensure that this is done correctly. (Anything that doesn't require borrows can be a member of the struct,
27// and anything that must be borrowed should be passed by the caller)
28pub struct RendererLoadContext {
29    asset_plugins: Arc<Vec<Arc<dyn RendererAssetPlugin>>>,
30}
31
32impl RendererLoadContext {
33    pub fn wait_for_asset_to_load<T>(
34        &self,
35        render_resources: &RenderResources,
36        asset_manager: &mut AssetManager,
37        asset_handle: &Handle<T>,
38        asset_resource: &mut AssetResource,
39        asset_name: &str,
40    ) -> RafxResult<()> {
41        asset_manager.wait_for_asset_to_load(
42            asset_handle,
43            asset_resource,
44            asset_name,
45            |asset_manager, asset_resource| {
46                for plugin in &*self.asset_plugins {
47                    plugin.process_asset_loading(
48                        asset_manager,
49                        asset_resource,
50                        render_resources,
51                    )?;
52                }
53
54                Ok(())
55            },
56        )
57    }
58}
59
60#[derive(Default, Copy, Clone, Debug)]
61pub struct RendererConfigResource {
62    pub visibility_config: VisibilityConfig,
63}
64
65#[derive(Clone)]
66pub struct InvalidResources {
67    pub invalid_image_color: ResourceArc<ImageViewResource>,
68    pub invalid_image_depth: ResourceArc<ImageViewResource>,
69    pub invalid_cube_map_image_color: ResourceArc<ImageViewResource>,
70    pub invalid_cube_map_image_depth: ResourceArc<ImageViewResource>,
71}
72
73pub struct RendererInner {
74    // This is a separate lock
75    pub(super) temporary_work: RenderJobExtractAllocationContext,
76    pub(super) thread_pool: Box<dyn RendererThreadPool>,
77}
78
79pub struct Renderer {
80    pub(super) inner: Arc<Mutex<RendererInner>>,
81    pub(super) render_thread: Option<RenderThread>,
82    pub(super) pipeline_plugin: Arc<dyn RendererPipelinePlugin>,
83    pub(super) asset_plugins: Arc<Vec<Arc<dyn RendererAssetPlugin>>>,
84    pub(super) feature_plugins: Arc<Vec<Arc<dyn RenderFeaturePlugin>>>,
85    pub(super) render_resources: Arc<RenderResources>,
86    pub(super) graphics_queue: RafxQueue,
87    pub(super) transfer_queue: RafxQueue,
88}
89
90impl Drop for Renderer {
91    fn drop(&mut self) {
92        for plugin in &*self.feature_plugins {
93            plugin
94                .prepare_renderer_destroy(&self.render_resources)
95                .unwrap();
96        }
97
98        for plugin in &*self.asset_plugins {
99            plugin
100                .prepare_renderer_destroy(&self.render_resources)
101                .unwrap();
102        }
103
104        self.pipeline_plugin
105            .prepare_renderer_destroy(&self.render_resources)
106            .unwrap();
107    }
108}
109
110impl Renderer {
111    pub fn new(
112        extract_resources: ExtractResources,
113        mut render_resources: RenderResources,
114        asset_resource: &mut AssetResource,
115        asset_manager: &mut AssetManager,
116        graphics_queue: &RafxQueue,
117        transfer_queue: &RafxQueue,
118        feature_plugins: Vec<Arc<dyn RenderFeaturePlugin>>,
119        asset_plugins: Vec<Arc<dyn RendererAssetPlugin>>,
120        pipeline_plugin: Arc<dyn RendererPipelinePlugin>,
121        thread_pool: Box<dyn RendererThreadPool>,
122        allow_use_render_thread: bool,
123    ) -> RafxResult<Self> {
124        let asset_plugins = Arc::new(asset_plugins);
125        let feature_plugins = Arc::new(feature_plugins);
126
127        let device_context = graphics_queue.device_context();
128
129        let mut upload = RafxTransferUpload::new(
130            &device_context,
131            asset_manager.transfer_queue(),
132            asset_manager.graphics_queue(),
133            16 * 1024 * 1024,
134            None,
135        )?;
136
137        let invalid_image_color = Self::upload_image_data(
138            &device_context,
139            &mut upload,
140            &asset_manager.resources(),
141            &GpuImageData::new_1x1_rgba8(255, 0, 255, 255, GpuImageDataColorSpace::Linear),
142            ImageUploadParams::default(),
143        )
144        .map_err(|x| Into::<RafxError>::into(x))?;
145
146        let invalid_image_depth = Self::upload_image_data(
147            &device_context,
148            &mut upload,
149            &asset_manager.resources(),
150            &GpuImageData::new_1x1_d32(0.0),
151            ImageUploadParams::default(),
152        )
153        .map_err(|x| Into::<RafxError>::into(x))?;
154
155        let invalid_cube_map_image_color = Self::upload_image_data(
156            &device_context,
157            &mut upload,
158            &asset_manager.resources(),
159            &GpuImageData::new_1x1_rgba8(255, 0, 255, 255, GpuImageDataColorSpace::Linear),
160            ImageUploadParams {
161                generate_mips: false,
162                resource_type: RafxResourceType::TEXTURE_CUBE,
163                layer_swizzle: Some(&[0, 0, 0, 0, 0, 0]),
164            },
165        )
166        .map_err(|x| Into::<RafxError>::into(x))?;
167
168        let invalid_cube_map_image_depth = Self::upload_image_data(
169            &device_context,
170            &mut upload,
171            &asset_manager.resources(),
172            &GpuImageData::new_1x1_d32(0.0),
173            ImageUploadParams {
174                generate_mips: false,
175                resource_type: RafxResourceType::TEXTURE_CUBE,
176                layer_swizzle: Some(&[0, 0, 0, 0, 0, 0]),
177            },
178        )
179        .map_err(|x| Into::<RafxError>::into(x))?;
180
181        let invalid_resources = InvalidResources {
182            invalid_image_color,
183            invalid_image_depth,
184            invalid_cube_map_image_color,
185            invalid_cube_map_image_depth,
186        };
187
188        render_resources.insert(SwapchainRenderResource::default());
189        render_resources.insert(AssetManagerRenderResource::default());
190        render_resources.insert(TimeRenderResource::default());
191        render_resources.insert(MainViewRenderResource::default());
192
193        let load_context = RendererLoadContext {
194            asset_plugins: asset_plugins.clone(),
195        };
196
197        for plugin in &*asset_plugins {
198            plugin.initialize_static_resources(
199                &load_context,
200                asset_manager,
201                asset_resource,
202                &extract_resources,
203                &mut render_resources,
204                &mut upload,
205            )?;
206        }
207
208        for plugin in &*feature_plugins {
209            plugin.initialize_static_resources(
210                &load_context,
211                asset_manager,
212                asset_resource,
213                &extract_resources,
214                &mut render_resources,
215                &mut upload,
216            )?;
217        }
218
219        pipeline_plugin.initialize_static_resources(
220            &load_context,
221            asset_manager,
222            asset_resource,
223            &extract_resources,
224            &mut render_resources,
225            &mut upload,
226        )?;
227
228        render_resources.insert(invalid_resources.clone());
229
230        upload.block_until_upload_complete()?;
231
232        let use_render_thread =
233            allow_use_render_thread && device_context.device_info().supports_multithreaded_usage;
234        let render_thread = if use_render_thread {
235            Some(RenderThread::start())
236        } else {
237            None
238        };
239
240        let num_features = RenderRegistry::registered_feature_count() as usize;
241        let renderer = RendererInner {
242            thread_pool,
243            temporary_work: RenderJobExtractAllocationContext::new(num_features),
244        };
245
246        Ok(Renderer {
247            inner: Arc::new(Mutex::new(renderer)),
248            render_thread,
249            asset_plugins,
250            feature_plugins,
251            pipeline_plugin,
252            render_resources: Arc::new(render_resources),
253            graphics_queue: graphics_queue.clone(),
254            transfer_queue: transfer_queue.clone(),
255        })
256    }
257
258    pub fn load_context(&self) -> RendererLoadContext {
259        RendererLoadContext {
260            asset_plugins: self.asset_plugins.clone(),
261        }
262    }
263
264    pub fn wait_for_asset_to_load<T>(
265        &self,
266        asset_manager: &mut AssetManager,
267        asset_handle: &Handle<T>,
268        asset_resource: &mut AssetResource,
269        asset_name: &str,
270    ) -> RafxResult<()> {
271        self.load_context().wait_for_asset_to_load(
272            &*self.render_resources,
273            asset_manager,
274            asset_handle,
275            asset_resource,
276            asset_name,
277        )
278    }
279
280    pub fn clear_temporary_work(&mut self) {
281        let mut guard = self.inner.lock().unwrap();
282        let renderer_inner = &mut *guard;
283        renderer_inner.temporary_work.clear();
284    }
285
286    //TODO: We should probably implicitly do this when we do blocking waits for asset loads as the
287    // background processing to do the load may fetch resources that the render thread also fetches.
288    // However implementing this will require RendererLoadContext to have a ref to the render_thread
289    // to block. We probably also need to be the API such that we not only wait for the render
290    // thread to go idle, but also block it from starting again. When we do this we can remove the
291    // explicit wait_for_render_thread_idle() call from the demo.
292    pub fn wait_for_render_thread_idle(&self) {
293        if let Some(render_thread) = &self.render_thread {
294            render_thread.wait_for_render_finish();
295        }
296    }
297
298    pub fn graphics_queue(&self) -> &RafxQueue {
299        &self.graphics_queue
300    }
301
302    pub fn transfer_queue(&self) -> &RafxQueue {
303        &self.transfer_queue
304    }
305
306    fn upload_image_data(
307        device_context: &RafxDeviceContext,
308        upload: &mut RafxTransferUpload,
309        resources: &ResourceLookupSet,
310        image_data: &GpuImageData,
311        params: ImageUploadParams,
312    ) -> Result<ResourceArc<ImageViewResource>, RafxUploadError> {
313        let texture = image_upload::enqueue_load_image(device_context, upload, image_data, params)?;
314
315        let image = resources.insert_image(texture);
316        Ok(resources.get_or_create_image_view(&image, None)?)
317    }
318
319    // This is externally exposed, it checks result of the previous frame (which implicitly also
320    // waits for the previous frame to complete if it hasn't already)
321    #[profiling::function]
322    pub fn start_rendering_next_frame(
323        &self,
324        extract_resources: &mut ExtractResources,
325        previous_update_time: Duration,
326    ) -> RafxResult<()> {
327        //
328        // Block until the previous frame completes being submitted to GPU
329        //
330        let t0 = rafx_base::Instant::now();
331
332        let presentable_frame = {
333            let viewports_resource = extract_resources.fetch::<ViewportsResource>();
334            let mut swapchain_helper = extract_resources.fetch_mut::<RafxSwapchainHelper>();
335            let mut asset_manager = extract_resources.fetch_mut::<AssetManager>();
336            SwapchainHandler::acquire_next_image(
337                &mut *swapchain_helper,
338                &mut *asset_manager,
339                self,
340                viewports_resource.main_window_size.width,
341                viewports_resource.main_window_size.height,
342            )
343        }?;
344
345        if let Some(render_thread) = &self.render_thread {
346            render_thread.wait_for_render_finish();
347        }
348
349        let t1 = rafx_base::Instant::now();
350        log::trace!(
351            "[main] wait for previous frame present {} ms",
352            (t1 - t0).as_secs_f32() * 1000.0
353        );
354
355        Self::create_and_start_render_job(
356            self,
357            extract_resources,
358            presentable_frame,
359            previous_update_time,
360        );
361
362        Ok(())
363    }
364
365    fn create_and_start_render_job(
366        renderer: &Renderer,
367        extract_resources: &mut ExtractResources,
368        presentable_frame: RafxPresentableFrame,
369        previous_update_time: Duration,
370    ) {
371        let result = Self::try_create_render_job(
372            &renderer,
373            extract_resources,
374            &presentable_frame,
375            previous_update_time,
376        );
377
378        match result {
379            Ok(prepared_frame) => {
380                if let Some(render_thread) = &renderer.render_thread {
381                    render_thread.render(prepared_frame, presentable_frame);
382                } else {
383                    // This path is required for backends that do not support multithreaded use
384                    prepared_frame.render_async(presentable_frame);
385                }
386            }
387            Err(e) => {
388                let graphics_queue = renderer.graphics_queue();
389                presentable_frame.present_with_error(graphics_queue, e)
390            }
391        };
392    }
393
394    fn try_create_render_job(
395        renderer: &Renderer,
396        extract_resources: &mut ExtractResources,
397        presentable_frame: &RafxPresentableFrame,
398        previous_update_time: Duration,
399    ) -> RafxResult<RenderFrameJob> {
400        //
401        // Fetch resources
402        //
403        let mut asset_manager_fetch = extract_resources.fetch_mut::<AssetManager>();
404        let asset_manager = &mut *asset_manager_fetch;
405
406        let render_registry = asset_manager.resource_manager().render_registry().clone();
407        let device_context = asset_manager.device_context().clone();
408
409        //
410        // Mark the previous frame as completed
411        //
412        asset_manager.on_frame_complete()?;
413
414        //
415        // Pump asset loading
416        //
417        {
418            let mut asset_resource = extract_resources.fetch_mut::<AssetResource>();
419            for plugin in &*renderer.asset_plugins {
420                plugin.process_asset_loading(
421                    asset_manager,
422                    &mut *asset_resource,
423                    &renderer.render_resources,
424                )?;
425            }
426        }
427
428        let resource_context = asset_manager.resource_manager().resource_context();
429
430        let mut guard = renderer.inner.lock().unwrap();
431        let renderer_inner = &mut *guard;
432        let render_resources = &renderer.render_resources;
433
434        render_resources
435            .fetch_mut::<TimeRenderResource>()
436            .update(previous_update_time);
437
438        for plugin in &*renderer.asset_plugins {
439            plugin.on_frame_complete(asset_manager, extract_resources, &*render_resources)?;
440        }
441
442        let renderer_config = extract_resources
443            .try_fetch::<RendererConfigResource>()
444            .map(|x| *x)
445            .unwrap_or_default();
446
447        //
448        // Swapchain Status
449        //
450        let swapchain_image = {
451            // Temporary hack to jam a swapchain image into the existing resource lookups.. may want
452            // to reconsider this later since the ResourceArc can be held past the lifetime of the
453            // swapchain image
454            let swapchain_image = presentable_frame.swapchain_texture().clone();
455
456            let swapchain_image = resource_context.resources().insert_image(swapchain_image);
457
458            resource_context
459                .resources()
460                .get_or_create_image_view(&swapchain_image, None)?
461        };
462
463        let swapchain_guard = presentable_frame.swapchain().lock().unwrap();
464        let max_color_component_value = match &*swapchain_guard {
465            #[cfg(feature = "rafx-metal")]
466            rafx_api::RafxSwapchain::Metal(swapchain) => {
467                swapchain.edr_info().max_edr_color_component_value
468            }
469            #[allow(unreachable_patterns)]
470            _ => 1.0,
471        };
472
473        render_resources
474            .fetch_mut::<SwapchainRenderResource>()
475            .set_max_color_component_value(max_color_component_value);
476
477        let render_view_set = RenderViewSet::new(presentable_frame.incrementing_frame_index());
478
479        //
480        // Determine Camera Location
481        //
482
483        let viewports_resource = extract_resources.fetch::<ViewportsResource>();
484        let view_meta = viewports_resource.main_view_meta.clone().unwrap();
485
486        let main_window_size = viewports_resource.main_window_size;
487
488        let main_view = render_view_set.create_view(
489            view_meta.view_frustum,
490            view_meta.eye_position,
491            view_meta.view,
492            view_meta.proj,
493            (main_window_size.width, main_window_size.height),
494            view_meta.depth_range,
495            view_meta.render_phase_mask,
496            view_meta.render_feature_mask,
497            view_meta.render_feature_flag_mask,
498            view_meta.debug_name,
499        );
500        {
501            let mut main_view_render_resource =
502                render_resources.fetch_mut::<MainViewRenderResource>();
503
504            if let Some(main_view) = &main_view_render_resource.main_view {
505                main_view_render_resource.previous_main_view_info = Some(PreviousMainViewInfo {
506                    view_matrix: main_view.view_matrix(),
507                    projection_matrix: main_view.projection_matrix(),
508                });
509            }
510            main_view_render_resource.main_view = Some(main_view.clone());
511        }
512
513        //
514        // Compute Views
515        //
516
517        let mut render_views = Vec::default();
518
519        {
520            profiling::scope!("Compute Views");
521            render_views.push(main_view.clone());
522            for plugin in &*renderer.feature_plugins {
523                plugin.add_render_views(
524                    extract_resources,
525                    render_resources,
526                    &render_view_set,
527                    &mut render_views,
528                );
529            }
530        }
531
532        //
533        // Update Resources and flush descriptor set changes
534        //
535
536        asset_manager.on_begin_frame()?;
537
538        //
539        // Build the frame packet - this takes the views and visibility results and creates a
540        // structure that's used during the extract/prepare/write phases
541        //
542
543        unsafe {
544            render_resources
545                .fetch_mut::<AssetManagerRenderResource>()
546                .begin_extract(&asset_manager);
547        }
548
549        let frame_packets = {
550            profiling::scope!("Renderer Extract");
551
552            let extract_context = RenderJobExtractContext::new(
553                &renderer_inner.temporary_work,
554                &extract_resources,
555                &render_resources,
556                &renderer_config.visibility_config,
557            );
558
559            let visibility_results = {
560                profiling::scope!("Calculate View Visibility");
561
562                {
563                    let mut visibility_resource =
564                        extract_resources.fetch_mut::<VisibilityResource>();
565                    visibility_resource.update();
566                }
567
568                let visibility_resource = extract_resources.fetch::<VisibilityResource>();
569                let view_visibility_jobs =
570                    Renderer::create_view_visibility_jobs(&render_views, &visibility_resource);
571
572                renderer_inner.thread_pool.run_view_visibility_jobs(
573                    &view_visibility_jobs,
574                    &extract_context,
575                    &*visibility_resource,
576                )
577            };
578
579            {
580                profiling::scope!("Determine Frame Packet Sizes");
581                renderer_inner
582                    .thread_pool
583                    .count_render_features_render_objects(
584                        &renderer.feature_plugins,
585                        &extract_context,
586                        &visibility_results,
587                    );
588            }
589
590            {
591                profiling::scope!("Allocate Frame Packets");
592                Renderer::create_frame_packets(&renderer.feature_plugins, &extract_context);
593            }
594
595            let extract_jobs = {
596                profiling::scope!("Create Extract Jobs");
597                renderer_inner.thread_pool.create_extract_jobs(
598                    &renderer.feature_plugins,
599                    &extract_context,
600                    visibility_results,
601                )
602            };
603
604            {
605                let visibility_resource = extract_resources.fetch::<VisibilityResource>();
606                profiling::scope!("Run Extract Jobs");
607                renderer_inner
608                    .thread_pool
609                    .run_extract_jobs(&extract_jobs, &*visibility_resource);
610            }
611
612            Renderer::take_frame_packets(extract_jobs)
613        };
614
615        render_resources
616            .fetch_mut::<AssetManagerRenderResource>()
617            .end_extract();
618
619        //TODO: This is now possible to run on the render thread
620        let prepared_render_graph = renderer.pipeline_plugin.generate_render_graph(
621            asset_manager,
622            swapchain_image,
623            presentable_frame.rotating_frame_index(),
624            main_view.clone(),
625            extract_resources,
626            render_resources,
627        )?;
628
629        let graphics_queue = renderer.graphics_queue.clone();
630        let feature_plugins = renderer.feature_plugins.clone();
631        let pipeline_plugin = renderer.pipeline_plugin.clone();
632        let thread_pool = renderer_inner.thread_pool.clone_to_box();
633        let render_resources = renderer.render_resources.clone();
634
635        let prepared_frame = RenderFrameJob {
636            thread_pool,
637            render_resources,
638            prepared_render_graph,
639            resource_context,
640            frame_packets,
641            render_registry,
642            device_context,
643            graphics_queue,
644            feature_plugins,
645            pipeline_plugin,
646            render_views,
647        };
648
649        Ok(prepared_frame)
650    }
651
652    fn create_view_visibility_jobs<'visibility>(
653        render_views: &[RenderView],
654        visibility_resource: &'visibility VisibilityResource,
655    ) -> Vec<Arc<ViewVisibilityJob<'visibility>>> {
656        render_views
657            .iter()
658            .map(|view| {
659                log::trace!("Add visibility job {}", view.debug_name());
660                Arc::new(ViewVisibilityJob::new(view.clone(), visibility_resource))
661            })
662            .collect::<Vec<_>>()
663    }
664
665    pub fn run_view_visibility_job<'extract>(
666        view_visibility_job: &Arc<ViewVisibilityJob>,
667        extract_context: &RenderJobExtractContext<'extract>,
668        visibility_resource: &VisibilityResource,
669    ) -> RenderViewVisibilityQuery {
670        view_visibility_job.query_visibility(extract_context, visibility_resource)
671    }
672
673    pub fn calculate_frame_packet_size(
674        #[allow(unused_variables)] debug_constants: &'static RenderFeatureDebugConstants,
675        feature_index: RenderFeatureIndex,
676        is_relevant: impl Fn(&RenderViewVisibilityQuery) -> bool,
677        visibility_results: &Vec<RenderViewVisibilityQuery>,
678        render_object_instance_object_ids: &mut RenderObjectInstanceObjectIds,
679        frame_packet_size: &mut FramePacketSize,
680    ) {
681        profiling::scope!(debug_constants.feature_name);
682
683        for view_visibility_result in visibility_results
684            .iter()
685            .filter(|result| is_relevant(result))
686        {
687            if let Some(visible_render_objects) =
688                view_visibility_result.render_object_instances_per_view(feature_index)
689            {
690                for render_object in visible_render_objects {
691                    render_object_instance_object_ids.insert(*render_object);
692                }
693
694                frame_packet_size.view_packet_sizes.push(ViewPacketSize {
695                    view: view_visibility_result.view.clone(),
696                    num_render_object_instances: visible_render_objects.len(),
697                    num_volumes: 0, // TODO(dvd): Volumes
698                });
699            } else {
700                frame_packet_size.view_packet_sizes.push(ViewPacketSize {
701                    view: view_visibility_result.view.clone(),
702                    num_render_object_instances: 0,
703                    num_volumes: 0, // TODO(dvd): Volumes
704                });
705            }
706        }
707    }
708
709    pub fn count_render_feature_render_objects<'extract>(
710        feature: &Arc<dyn RenderFeaturePlugin>,
711        extract_context: &RenderJobExtractContext<'extract>,
712        visibility_results: &Vec<RenderViewVisibilityQuery>,
713    ) {
714        profiling::scope!(feature.feature_debug_constants().feature_name);
715
716        let temporary_work = extract_context.allocation_context;
717
718        let mut render_object_instances = temporary_work
719            .render_object_instances
720            .get(feature.feature_index() as usize)
721            .unwrap()
722            .borrow_mut();
723        render_object_instances.clear();
724
725        let mut frame_packet_metadata = temporary_work
726            .frame_packet_metadata
727            .get(feature.feature_index() as usize)
728            .unwrap()
729            .borrow_mut();
730
731        frame_packet_metadata
732            .frame_packet_size
733            .view_packet_sizes
734            .clear();
735
736        // NOTE(dvd): Delegate to the feature for the actual frame packet size.
737        // Most features will just use the default implementation. The default
738        // implementation uses the visibility results to size the frame packet
739        // by filtering each view relevant to the feature to only the entities
740        // with render objects associated with the feature.
741        feature.calculate_frame_packet_size(
742            extract_context,
743            visibility_results,
744            &mut *render_object_instances,
745            &mut frame_packet_metadata.frame_packet_size,
746        );
747
748        frame_packet_metadata
749            .frame_packet_size
750            .num_render_object_instances = render_object_instances.len();
751
752        let num_view_packets = frame_packet_metadata
753            .frame_packet_size
754            .view_packet_sizes
755            .len();
756
757        frame_packet_metadata.is_relevant = num_view_packets > 0;
758    }
759
760    fn create_frame_packets<'extract>(
761        features: &Vec<Arc<dyn RenderFeaturePlugin>>,
762        extract_context: &RenderJobExtractContext<'extract>,
763    ) {
764        let temporary_work = extract_context.allocation_context;
765
766        features.iter().for_each(|feature| {
767            profiling::scope!(feature.feature_debug_constants().feature_name);
768
769            let frame_packet_metadata = temporary_work
770                .frame_packet_metadata
771                .get(feature.feature_index() as usize)
772                .unwrap()
773                .borrow();
774
775            if frame_packet_metadata.is_relevant {
776                let frame_packet =
777                    feature.new_frame_packet(&frame_packet_metadata.frame_packet_size);
778
779                *temporary_work
780                    .frame_packets
781                    .get(feature.feature_index() as usize)
782                    .unwrap()
783                    .borrow_mut() = Some(frame_packet);
784            }
785        });
786    }
787
788    pub fn populate_frame_packet(
789        #[allow(unused_variables)] debug_constants: &'static RenderFeatureDebugConstants,
790        feature_index: RenderFeatureIndex,
791        is_relevant: impl Fn(&RenderViewVisibilityQuery) -> bool,
792        visibility_results: &Vec<RenderViewVisibilityQuery>,
793        frame_packet: &mut Box<dyn RenderFeatureFramePacket>,
794    ) {
795        profiling::scope!(debug_constants.feature_name);
796
797        for view_visibility_result in visibility_results
798            .iter()
799            .filter(|result| is_relevant(result))
800        {
801            let view_frame_index = view_visibility_result
802                .view
803                .view_frame_index(feature_index)
804                .unwrap();
805            if let Some(visible_render_objects) =
806                view_visibility_result.render_object_instances_per_view(feature_index)
807            {
808                for render_object_instance in visible_render_objects {
809                    let render_object_instance_id =
810                        frame_packet.get_or_push_render_object_instance(*render_object_instance);
811
812                    frame_packet.push_render_object_instance_per_view(
813                        view_frame_index,
814                        render_object_instance_id,
815                        *render_object_instance,
816                    );
817                }
818            }
819
820            // TODO(dvd): One could imagine volumes having a bitfield mask and filtering on that.
821            // for per_view_volume in view_visibility_result.per_view_volumes.iter() {
822            //    let object_id = ObjectId::from(per_view_volume.id);
823            //    frame_packet.push_volume(per_frame_view_index, object_id);
824            // }
825        }
826    }
827
828    pub fn create_extract_job<'extract>(
829        feature: &Arc<dyn RenderFeaturePlugin>,
830        extract_context: &RenderJobExtractContext<'extract>,
831        visibility_results: &Vec<RenderViewVisibilityQuery>,
832    ) -> Option<Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>> {
833        profiling::scope!(feature.feature_debug_constants().feature_name);
834
835        let temporary_work = extract_context.allocation_context;
836
837        if !temporary_work
838            .frame_packets
839            .get(feature.feature_index() as usize)
840            .map(|frame_packet| frame_packet.borrow().is_some())
841            .unwrap_or(false)
842        {
843            return None;
844        }
845
846        let frame_packet_metadata = temporary_work
847            .frame_packet_metadata
848            .get(feature.feature_index() as usize)
849            .unwrap()
850            .borrow();
851
852        let mut frame_packet = temporary_work
853            .frame_packets
854            .get(feature.feature_index() as usize)
855            .unwrap()
856            .borrow_mut()
857            .take()
858            .unwrap();
859
860        // NOTE(dvd): Delegate to the feature for populating the frame packet.
861        // Most features will just use the default implementation.
862        feature.populate_frame_packet(
863            extract_context,
864            visibility_results,
865            &frame_packet_metadata.frame_packet_size,
866            &mut frame_packet,
867        );
868
869        Some(feature.new_extract_job(extract_context, frame_packet))
870    }
871
872    pub fn extract_render_object_instance_chunk<'extract>(
873        extract_job: &Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>,
874        visibility_resource: &VisibilityResource,
875        chunk_index: usize,
876        chunk_size: usize,
877    ) {
878        let num_render_object_instances = extract_job.num_render_object_instances();
879        let start_id = chunk_index * chunk_size;
880        let end_id = usize::min(start_id + chunk_size, num_render_object_instances);
881        extract_job.extract_render_object_instance(visibility_resource, start_id..end_id);
882    }
883
884    pub fn extract_render_object_instance_all<'extract>(
885        extract_job: &Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>,
886        visibility_resource: &VisibilityResource,
887    ) {
888        Renderer::extract_render_object_instance_chunk(
889            extract_job,
890            visibility_resource,
891            0,
892            extract_job.num_render_object_instances(),
893        );
894    }
895
896    pub fn extract_render_object_instance_per_view_chunk<'extract>(
897        extract_job: &Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>,
898        visibility_resource: &VisibilityResource,
899        view_packet: &dyn RenderFeatureViewPacket,
900        chunk_index: usize,
901        chunk_size: usize,
902    ) {
903        let num_render_object_instances = view_packet.num_render_object_instances();
904        let start_id = chunk_index * chunk_size;
905        let end_id = usize::min(start_id + chunk_size, num_render_object_instances);
906        extract_job.extract_render_object_instance_per_view(
907            view_packet,
908            visibility_resource,
909            start_id..end_id,
910        );
911    }
912
913    pub fn extract_render_object_instance_per_view_all<'extract>(
914        extract_job: &Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>,
915        visibility_resource: &VisibilityResource,
916        view_packet: &dyn RenderFeatureViewPacket,
917    ) {
918        Renderer::extract_render_object_instance_per_view_chunk(
919            extract_job,
920            visibility_resource,
921            view_packet,
922            0,
923            view_packet.num_render_object_instances(),
924        );
925    }
926
927    fn take_frame_packet<'extract>(
928        extract_job: &mut Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>
929    ) -> Box<dyn RenderFeatureFramePacket> {
930        let extract_job = Arc::get_mut(extract_job).unwrap();
931        extract_job.take_frame_packet()
932    }
933
934    fn take_frame_packets<'extract>(
935        mut finished_extract_jobs: Vec<Arc<dyn RenderFeatureExtractJob<'extract> + 'extract>>
936    ) -> Vec<Box<dyn RenderFeatureFramePacket>> {
937        finished_extract_jobs
938            .iter_mut()
939            .map(|extract_job| Renderer::take_frame_packet(extract_job))
940            .collect()
941    }
942}