Unravel Engine C++ Reference
Loading...
Searching...
No Matches
batch_collector.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <chrono>
5
6namespace unravel
7{
8
9template<typename Key>
11 : key(key)
12{
13}
14
15template<typename Key>
17{
18 instances.add_instance(instance);
19}
20
21template<typename Key>
22void batch_group_t<Key>::calculate_camera_distance(const math::vec3& camera_pos)
23{
24 if(instances.empty())
25 {
26 camera_distance = 0.0f;
27 return;
28 }
29
30 math::vec3 average_position(0.0f);
31 for(const auto& instance : instances)
32 {
33 if(!instance.world_transform_ptr)
34 {
35 continue;
36 }
37 const auto& transform = *instance.world_transform_ptr;
38 average_position += math::vec3(transform[3][0], transform[3][1], transform[3][2]);
39 }
40
41 average_position /= static_cast<float>(instances.size());
42 camera_distance = math::length(average_position - camera_pos);
43}
44
45template<typename Key>
46auto batch_group_t<Key>::is_valid() const -> bool
47{
48 return key.is_valid() && !instances.empty();
49}
50
51template<typename Key>
53{
54 return instances.get_gpu_memory_size();
55}
56
57template<typename Key>
59
60template<typename Key>
62{
63 if(!key.is_valid() || !instance.is_valid())
64 {
65 return;
66 }
67
68 const auto start_time = std::chrono::high_resolution_clock::now();
69 get_or_create_batch_group(key).add_instance(instance);
70
71 if(profiling_enabled_)
72 {
73 const auto end_time = std::chrono::high_resolution_clock::now();
74 const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
75 stats_.collection_time_ms += static_cast<float>(duration.count()) / 1000.0f;
76 }
77}
78
79template<typename Key>
80void batch_collector_t<Key>::collect_renderable(const Key& key, const math::mat4& world_transform)
81{
82 collect_renderable(key, batch_instance(&world_transform));
83}
84
85template<typename Key>
87{
88 if(batch_groups_.empty())
89 {
90 return;
91 }
92
93 const auto start_time = std::chrono::high_resolution_clock::now();
94 prepared_batches_.clear();
95 prepared_batches_.reserve(batch_groups_.size());
97 for(auto& [key, group] : batch_groups_)
98 {
99 if(group.is_valid())
101 prepared_batches_.push_back(&group);
105 split_large_batches(context);
106
107 if(context.enable_distance_sorting)
108 {
109 calculate_camera_distances(context.camera_position);
110 }
111
112 sort_batches(context);
113 update_statistics();
114
115 if(profiling_enabled_ && context.enable_profiling)
116 {
117 const auto end_time = std::chrono::high_resolution_clock::now();
118 const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
119 stats_.preparation_time_ms = static_cast<float>(duration.count()) / 1000.0f;
120 }
121}
122
123template<typename Key>
125{
126 return prepared_batches_;
127}
128
129template<typename Key>
131{
132 batch_groups_.clear();
133 prepared_batches_.clear();
134 stats_.reset();
135}
136
137template<typename Key>
139{
140 return stats_;
141}
142
143template<typename Key>
145{
146 return batch_groups_.size();
147}
148
149template<typename Key>
151{
152 size_t total = 0;
153 for(const auto& [key, group] : batch_groups_)
154 {
155 total += group.instances.size();
156 }
157 return total;
158}
159
160template<typename Key>
162{
163 return !batch_groups_.empty();
164}
165
166template<typename Key>
168{
169 max_instances_per_batch_ = max_instances;
170}
171
172template<typename Key>
174{
175 profiling_enabled_ = enabled;
176}
177
178template<typename Key>
180{
181 if constexpr(std::is_same_v<Key, batch_key>)
182 {
183 std::sort(prepared_batches_.begin(),
184 prepared_batches_.end(),
185 [&context](const batch_group_t<Key>* a, const batch_group_t<Key>* b) -> bool
186 {
187 if(a->key.material_ptr.get() != b->key.material_ptr.get())
188 {
189 return a->key.material_ptr.get() < b->key.material_ptr.get();
190 }
191 if(a->key.mesh_ptr.get() != b->key.mesh_ptr.get())
192 {
193 return a->key.mesh_ptr.get() < b->key.mesh_ptr.get();
194 }
195 if(a->key.lod_index != b->key.lod_index)
196 {
197 return a->key.lod_index < b->key.lod_index;
198 }
199 if(a->key.submesh_index != b->key.submesh_index)
200 {
201 return a->key.submesh_index < b->key.submesh_index;
202 }
203 if(context.enable_distance_sorting)
204 {
205 return a->camera_distance > b->camera_distance;
206 }
207 return false;
208 });
209 }
210 else
211 {
212 std::sort(prepared_batches_.begin(),
213 prepared_batches_.end(),
214 [&context](const batch_group_t<Key>* a, const batch_group_t<Key>* b) -> bool
215 {
216 if(a->key != b->key)
217 {
218 return a->key < b->key;
219 }
220 if(context.enable_distance_sorting)
221 {
222 return a->camera_distance > b->camera_distance;
223 }
224 return false;
225 });
226 }
227}
228
229template<typename Key>
230void batch_collector_t<Key>::split_large_batches(const submit_context& context)
231{
232 if(context.max_instances_per_batch == 0)
233 {
234 return;
235 }
236
237 batch_list_t new_batches;
238 for(auto* batch : prepared_batches_)
239 {
240 if(batch->instances.size() <= context.max_instances_per_batch)
241 {
242 new_batches.push_back(batch);
243 continue;
244 }
245
246 stats_.split_batches++;
247 new_batches.push_back(batch);
248 }
249 prepared_batches_ = std::move(new_batches);
250}
251
252template<typename Key>
253void batch_collector_t<Key>::calculate_camera_distances(const math::vec3& camera_pos)
254{
255 for(auto* batch : prepared_batches_)
256 {
257 batch->calculate_camera_distance(camera_pos);
258 }
259}
260
261template<typename Key>
262void batch_collector_t<Key>::update_statistics()
263{
264 stats_.total_batches = static_cast<uint32_t>(prepared_batches_.size());
265 stats_.total_instances = static_cast<uint32_t>(get_instance_count());
266
267 stats_.instance_buffer_memory_used = 0;
268 for(const auto* batch : prepared_batches_)
269 {
270 stats_.instance_buffer_memory_used += batch->get_gpu_memory_size();
271 }
272 stats_.calculate_derived_stats();
273}
274
275template<typename Key>
276auto batch_collector_t<Key>::get_or_create_batch_group(const Key& key) -> batch_group_t<Key>&
277{
278 auto it = batch_groups_.find(key);
279 if(it != batch_groups_.end())
280 {
281 return it->second;
282 }
283 auto [inserted_it, success] = batch_groups_.emplace(key, batch_group_t<Key>(key));
284 return inserted_it->second;
285}
286
287} // namespace unravel
entt::handle b
entt::handle a
auto get_stats() const -> const batch_stats &
std::vector< batch_group_t< Key > * > batch_list_t
void set_max_instances_per_batch(uint32_t max_instances)
auto has_batches() const -> bool
void prepare_batches(const submit_context &context)
auto get_instance_count() const -> size_t
void set_profiling_enabled(bool enabled)
auto get_batch_count() const -> size_t
void collect_renderable(const Key &key, const batch_instance &instance)
auto get_prepared_batches() const -> const batch_list_t &
void calculate_camera_distance(const math::vec3 &camera_pos)
auto is_valid() const -> bool
auto get_gpu_memory_size() const -> size_t
void add_instance(const batch_instance &instance)
Instance data for a single object in a batch.
auto is_valid() const -> bool
Check if this instance has valid data.
bool enabled