1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::render_features::render_features_prelude::*;
use crate::render_features::VisibilityVecs;
use crate::visibility::VisibilityObjectId;
use rafx_base::owned_pool::Pooled;
use rafx_visibility::{VisibilityObjectHandle, VisibilityResult};
use slotmap::KeyData;

pub type VisibleRenderObjects = Pooled<VisibilityVecs>;

/// The `RenderObject`s visible to a specific `RenderView` for the current frame. Each `RenderObject`
/// is represented by a `RenderViewObject` with the `ObjectId` returned by the `VisibilityObject`
/// and a `RenderObjectId`. If a `VisibilityObject` has multiple `RenderObject`s associated with it,
/// the results will be returned as 0 or more `RenderViewObject`s. The visible `RenderObject`s will
/// only contain `RenderObject`s associated with a `RenderFeature` included by the `RenderView`'s
/// `RenderFeatureMask`.
pub struct RenderViewVisibilityQuery {
    pub view: RenderView,
    pub per_view_render_objects: VisibleRenderObjects,
}

impl RenderViewVisibilityQuery {
    pub fn render_object_instances_per_view(
        &self,
        feature_index: RenderFeatureIndex,
    ) -> Option<&Vec<RenderObjectInstance>> {
        self.per_view_render_objects
            .get(feature_index as usize)
            .and_then(|feature| {
                if feature.is_empty() {
                    None
                } else {
                    Some(feature)
                }
            })
    }
}

/// Determines the visible `RenderObject`s in a given `RenderView` and returns the results in the
/// `RenderViewVisibilityQuery`. It is thread-safe to call `query_visibility` on multiple `RenderView`s
/// simultaneously.
pub struct ViewVisibilityJob<'a> {
    pub view: RenderView,
    pub visibility_resource: &'a VisibilityResource,
}

impl<'a> ViewVisibilityJob<'a> {
    pub fn new(
        view: RenderView,
        visibility_resource: &'a VisibilityResource,
    ) -> Self {
        Self {
            view,
            visibility_resource,
        }
    }

    pub fn view(&self) -> &RenderView {
        &self.view
    }

    #[profiling::function]
    pub fn query_visibility<'extract>(
        &self,
        extract_context: &RenderJobExtractContext<'extract>,
        visibility_resource: &VisibilityResource,
    ) -> RenderViewVisibilityQuery {
        let view_frustum = self.view.view_frustum();
        let visibility_query = view_frustum
            .query_visibility(visibility_resource, extract_context.visibility_config)
            .unwrap();

        let render_feature_mask = self.view.render_feature_mask();

        let mut all_render_objects = extract_context
            .allocation_context
            .query_visibility_vecs(&self.view);

        let visible_objects = &visibility_query.objects;
        for visibility_handle in visible_objects {
            let visibility_object_id = self.visibility_object_id(visibility_handle);
            let visibility_object_arc = self
                .visibility_resource
                .visibility_object_arc(visibility_object_id)
                .unwrap();
            let object_id = visibility_object_arc.object_id();
            let render_objects = visibility_object_arc.render_objects();

            for render_object_id in render_objects {
                // TODO(dvd): Should this use a render phase bitmask as another culling option?
                let render_feature_index = render_object_id.render_feature_index();
                if !render_feature_mask.is_included_index(render_feature_index) {
                    continue;
                }

                all_render_objects[render_feature_index as usize].push(RenderObjectInstance::new(
                    object_id,
                    render_object_id.as_id(),
                    visibility_object_id,
                ));
            }
        }

        // Sort the results.
        for feature in all_render_objects.iter_mut() {
            if feature.is_empty() {
                continue;
            }

            profiling::scope!("sort visible render objects");
            feature.sort_unstable_by_key(|render_object| render_object.render_object_id);
        }

        let per_view_render_objects = all_render_objects;
        RenderViewVisibilityQuery {
            view: self.view().clone(),
            per_view_render_objects,
        }
    }

    fn visibility_object_id(
        &self,
        visibility_result: &VisibilityResult<VisibilityObjectHandle>,
    ) -> VisibilityObjectId {
        VisibilityObjectId::from(KeyData::from_ffi(visibility_result.id))
    }
}