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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use super::RenderFeatureSubmitNode;
use crate::render_features::SubmitNodeSortFunction;
use fnv::FnvHashMap;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;

/// The `ID` of a registered `RenderFeature`.
pub type RenderFeatureIndex = u32;

/// The `ID` of a registered `RenderFeatureFlag`.
pub type RenderFeatureFlagIndex = u32;

/// The `ID` of a registered `RenderPhase`.
pub type RenderPhaseIndex = u32;

pub type RenderFeatureMaskInnerType = u64;
pub const MAX_RENDER_FEATURE_COUNT: u32 = 64;

pub type RenderFeatureFlagMaskInnerType = u128;
pub const MAX_RENDER_FEATURE_FLAG_COUNT: u32 = 128;

pub type RenderPhaseMaskInnerType = u32;
pub const MAX_RENDER_PHASE_COUNT: u32 = 32;

pub struct RenderFeatureDebugConstants {
    pub feature_name: &'static str,

    pub begin_per_frame_extract: &'static str,
    pub extract_render_object_instance: &'static str,
    pub extract_render_object_instance_per_view: &'static str,
    pub end_per_view_extract: &'static str,
    pub end_per_frame_extract: &'static str,

    pub begin_per_frame_prepare: &'static str,
    pub prepare_render_object_instance: &'static str,
    pub prepare_render_object_instance_per_view: &'static str,
    pub end_per_view_prepare: &'static str,
    pub end_per_frame_prepare: &'static str,

    pub on_begin_execute_graph: &'static str,
    pub render_submit_node: &'static str,
    pub begin_submit_node_batch: &'static str,
}

pub trait RenderFeature {
    fn set_feature_index(index: RenderFeatureIndex);
    fn feature_index() -> RenderFeatureIndex;

    fn feature_debug_name() -> &'static str;
    fn feature_debug_constants() -> &'static RenderFeatureDebugConstants;
}

pub trait RenderFeatureFlag {
    fn set_feature_flag_index(index: RenderFeatureIndex);
    fn feature_flag_index() -> RenderFeatureIndex;

    fn feature_flag_debug_name() -> &'static str;
}

pub trait RenderPhase {
    fn set_render_phase_index(index: RenderPhaseIndex);
    fn render_phase_index() -> RenderPhaseIndex;

    fn sort_submit_nodes(submit_nodes: &mut Vec<RenderFeatureSubmitNode>);

    fn render_phase_debug_name() -> &'static str;
}

pub struct RegisteredPhase {
    sort_submit_nodes_callback: SubmitNodeSortFunction,
}

impl RegisteredPhase {
    fn new<T: RenderPhase>() -> Self {
        RegisteredPhase {
            sort_submit_nodes_callback: T::sort_submit_nodes,
        }
    }
}

static RENDER_REGISTRY_FEATURE_COUNT: AtomicU32 = AtomicU32::new(0);
static RENDER_REGISTRY_FEATURE_FLAG_COUNT: AtomicU32 = AtomicU32::new(0);
static RENDER_REGISTRY_PHASE_COUNT: AtomicU32 = AtomicU32::new(0);

#[derive(Default)]
pub struct RenderRegistryBuilder {
    registered_phases: FnvHashMap<RenderPhaseIndex, RegisteredPhase>,
    phase_name_to_index: FnvHashMap<String, RenderPhaseIndex>,
}

impl RenderRegistryBuilder {
    pub fn register_feature<T>(self) -> Self
    where
        T: RenderFeature,
    {
        let feature_index = RENDER_REGISTRY_FEATURE_COUNT.fetch_add(1, Ordering::AcqRel);
        T::set_feature_index(feature_index);
        self
    }

    pub fn register_feature_flag<T>(self) -> Self
    where
        T: RenderFeatureFlag,
    {
        let feature_flag_index = RENDER_REGISTRY_FEATURE_FLAG_COUNT.fetch_add(1, Ordering::AcqRel);
        T::set_feature_flag_index(feature_flag_index);
        self
    }

    pub fn register_render_phase<T>(
        mut self,
        name: &str,
    ) -> Self
    where
        T: RenderPhase,
    {
        let render_phase_index = RENDER_REGISTRY_PHASE_COUNT.fetch_add(1, Ordering::AcqRel);
        assert!(render_phase_index < MAX_RENDER_PHASE_COUNT);
        T::set_render_phase_index(render_phase_index);
        let old = self
            .registered_phases
            .insert(T::render_phase_index(), RegisteredPhase::new::<T>());
        assert!(old.is_none());
        let old = self
            .phase_name_to_index
            .insert(name.to_string(), render_phase_index);
        assert!(old.is_none());
        self
    }

    pub fn build(self) -> RenderRegistry {
        let inner = RenderRegistryInner {
            registered_phases: self.registered_phases,
            phase_name_to_index: self.phase_name_to_index,
        };

        RenderRegistry {
            inner: Arc::new(inner),
        }
    }
}

struct RenderRegistryInner {
    registered_phases: FnvHashMap<RenderPhaseIndex, RegisteredPhase>,
    phase_name_to_index: FnvHashMap<String, RenderPhaseIndex>,
}

#[derive(Clone)]
pub struct RenderRegistry {
    inner: Arc<RenderRegistryInner>,
}

impl RenderRegistry {
    pub fn registered_feature_count() -> RenderFeatureIndex {
        RENDER_REGISTRY_FEATURE_COUNT.load(Ordering::Acquire)
    }

    pub fn registered_render_phase_count() -> RenderPhaseIndex {
        RENDER_REGISTRY_PHASE_COUNT.load(Ordering::Acquire)
    }

    pub fn render_phase_index_from_name(
        &self,
        name: &str,
    ) -> Option<RenderPhaseIndex> {
        self.inner.phase_name_to_index.get(name).copied()
    }

    pub fn submit_node_sort_function(
        &self,
        render_phase_index: RenderPhaseIndex,
    ) -> SubmitNodeSortFunction {
        self.inner.registered_phases[&render_phase_index].sort_submit_nodes_callback
    }
}