39 std::uniform_real_distribution<float> dir(0, 1);
40 std::uniform_real_distribution<float> loc(-10, 10);
41 std::uniform_real_distribution<float> ang(
M_PI / 10.,
M_PI / 4.);
45 std::cout <<
"\n==== RAY ====\n" << std::endl;
47 std::map<std::string, bool (*)(const Box&, const Ray3&)> rayVariants;
49 rayVariants[
"Nominal"] = [](
const auto&
box,
const auto& ray) ->
bool {
50 return box.intersect(ray);
53 rayVariants[
"Incl. div., unroll"] = [](
const Box&
box,
59 double tmin = -INFINITY, tmax = INFINITY;
61 double tx1 = (box.
min().x() - origin.x()) / d.x();
62 double tx2 = (box.
max().x() - origin.x()) / d.x();
69 double ty1 = (box.
min().y() - origin.y()) / d.y();
70 double ty2 = (box.
max().y() - origin.y()) / d.y();
77 double tz1 = (box.
min().z() - origin.z()) / d.z();
78 double tz2 = (box.
max().z() - origin.z()) / d.z();
84 return tmax > tmin && tmax > 0.0;
87 rayVariants[
"Incl. div., loop"] = [](
const Box&
box,
93 double tmin = -INFINITY, tmax = INFINITY;
95 for (
size_t i = 0; i < 3; i++) {
98 double t1 = (box.
min()[i] - origin[i]) / d[i];
99 double t2 = (box.
max()[i] - origin[i]) / d[i];
104 return tmax > tmin && tmax > 0.0;
107 rayVariants[
"Incl. div., min/max alt., unroll"] =
112 double tx1 = (box.
min().x() - origin.x()) / d.x();
113 double tx2 = (box.
max().x() - origin.x()) / d.x();
117 double ty1 = (box.
min().y() - origin.y()) / d.y();
118 double ty2 = (box.
max().y() - origin.y()) / d.y();
122 double tz1 = (box.
min().z() - origin.z()) / d.z();
123 double tz2 = (box.
max().z() - origin.z()) / d.z();
127 return tmax > tmin && tmax > 0.0;
130 rayVariants[
"No div., min/max alt, unroll"] = [](
const Box&
box,
135 double tx1 = (box.
min().x() - origin.x()) *
id.
x();
136 double tx2 = (box.
max().x() - origin.x()) *
id.
x();
140 double ty1 = (box.
min().y() - origin.y()) *
id.
y();
141 double ty2 = (box.
max().y() - origin.y()) *
id.
y();
145 double tz1 = (box.
min().z() - origin.z()) *
id.
z();
146 double tz2 = (box.
max().z() - origin.z()) *
id.
z();
150 return tmax > tmin && tmax > 0.0;
153 rayVariants[
"No div., min/max orig, loop"] = [](
const Box&
box,
157 double tmin = -INFINITY, tmax = INFINITY;
159 for (
size_t i = 0; i < 3; i++) {
160 double t1 = (box.
min()[i] - origin[i]) *
id[i];
161 double t2 = (box.
max()[i] - origin[i]) *
id[i];
166 return tmax > tmin && tmax > 0.0;
170 std::generate(rays.begin(), rays.end(), [&]() {
171 const Vector3F d{dir(rng), dir(rng), dir(rng)};
172 const Vector3F l{loc(rng), loc(rng), loc(rng)};
173 return Ray3{l,
d.normalized()};
176 std::cout <<
"Make sure ray implementations are identical" << std::endl;
177 for (
const auto& ray : rays) {
178 std::vector<std::pair<std::string, bool>> results;
181 std::back_inserter(results),
187 bool all = std::all_of(results.begin(), results.end(),
188 [](
const auto& r) {
return r.second; });
189 bool none = std::none_of(results.begin(), results.end(),
190 [](
const auto& r) {
return r.second; });
193 std::cerr <<
"Discrepancy: " << std::endl;
194 for (
const auto& [
name, result] : results) {
195 std::cerr <<
" - " <<
name <<
": " << result << std::endl;
198 testBox.toStream(std::cerr);
199 std::cerr << std::endl;
200 std::cerr <<
"Ray: [" << ray.origin().transpose() <<
"], ["
201 << ray.dir().transpose() <<
"]" << std::endl;
205 std::cout <<
"Seems ok" << std::endl;
207 std::cout <<
"Run benchmarks: " << std::endl;
208 for (
const auto&
p : rayVariants) {
210 std::cout <<
"- Benchmarking variant: '" <<
p.first <<
"'" << std::endl;
212 [&](
const auto& ray) {
return p.second(testBox, ray); }, rays);
213 std::cout <<
" " << bench_result << std::endl;
216 std::cout <<
"\n==== FRUSTUM ====\n" << std::endl;
218 std::map<std::string, bool (*)(const Box&, const Frustum3&)> frustumVariants;
220 frustumVariants[
"Nominal"] = [](
const auto&
box,
221 const auto& frustum) ->
bool {
225 frustumVariants[
"Manual constexpr loop unroll, early ret."] =
227 constexpr
size_t sides = 4;
230 const auto& normals = fr.normals();
234 auto calc = [&](
const auto& normal) {
235 return (normal.array() < 0).
template cast<value_type>() * fr_vmin +
236 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
241 p_vtx = calc(normals[0]);
242 if (p_vtx.dot(normals[0]) < 0) {
246 p_vtx = calc(normals[1]);
247 if (p_vtx.dot(normals[1]) < 0) {
251 p_vtx = calc(normals[2]);
252 if (p_vtx.dot(normals[2]) < 0) {
256 if constexpr (sides > 2) {
257 p_vtx = calc(normals[3]);
258 if (p_vtx.dot(normals[3]) < 0) {
263 if constexpr (sides > 3) {
264 p_vtx = calc(normals[4]);
265 if (p_vtx.dot(normals[4]) < 0) {
270 if constexpr (sides > 4) {
271 for (
size_t i = 5; i <= fr.sides; i++) {
274 p_vtx = calc(normal);
275 if (p_vtx.dot(normal) < 0) {
284 frustumVariants[
"Nominal, no early ret."] = [](
const Box&
box,
286 const auto& normals = fr.normals();
292 for (
size_t i = 0; i < fr.sides + 1; i++) {
295 p_vtx = (normal.array() < 0).
template cast<value_type>() * fr_vmin +
296 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
298 result = result && (p_vtx.dot(normal) >= 0);
303 frustumVariants[
"Manual constexpr unroll, early ret."] =
305 constexpr
size_t sides = 4;
308 const auto& normals = fr.normals();
312 auto calc = [&](
const auto& normal) {
313 return (normal.array() < 0).
template cast<value_type>() * fr_vmin +
314 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
320 p_vtx = calc(normals[0]);
321 result = result && (p_vtx.dot(normals[0]) >= 0);
323 p_vtx = calc(normals[1]);
324 result = result && (p_vtx.dot(normals[1]) >= 0);
326 p_vtx = calc(normals[2]);
327 result = result && (p_vtx.dot(normals[2]) >= 0);
329 if constexpr (sides > 2) {
330 p_vtx = calc(normals[3]);
331 result = result && (p_vtx.dot(normals[3]) >= 0);
334 if constexpr (sides > 3) {
335 p_vtx = calc(normals[4]);
336 result = result && (p_vtx.dot(normals[4]) >= 0);
339 if constexpr (sides > 4) {
340 for (
size_t i = 5; i <= fr.sides; i++) {
343 p_vtx = calc(normal);
344 result = result && (p_vtx.dot(normal) >= 0);
351 std::vector<Frustum3> frustums{
n,
Frustum3{{0, 0, 0}, {1, 0, 0},
M_PI / 2.}};
352 std::generate(frustums.begin(), frustums.end(), [&]() {
353 const Vector3F d{dir(rng), dir(rng), dir(rng)};
354 const Vector3F l{loc(rng), loc(rng), loc(rng)};
355 return Frustum3{l,
d.normalized(), ang(rng)};
358 std::cout <<
"Make sure frustum implementations are identical" << std::endl;
359 for (
const auto& fr : frustums) {
360 std::vector<std::pair<std::string, bool>> results;
363 std::back_inserter(results),
369 bool all = std::all_of(results.begin(), results.end(),
370 [](
const auto& r) {
return r.second; });
371 bool none = std::none_of(results.begin(), results.end(),
372 [](
const auto& r) {
return r.second; });
375 std::cerr <<
"Discrepancy: " << std::endl;
376 for (
const auto& [
name, result] : results) {
377 std::cerr <<
" - " <<
name <<
": " << result << std::endl;
380 testBox.toStream(std::cerr);
381 std::cerr << std::endl;
382 std::cerr <<
"Frustum: [" << fr.origin().transpose() <<
"], ["
383 << fr.dir().transpose() <<
"]" << std::endl;
387 std::cout <<
"Seems ok" << std::endl;
389 size_t iters_per_run = 1000;
391 std::vector<std::pair<std::string, Frustum3>> testFrusts = {
393 {
"towards",
Frustum3{{0, 0, -10}, {0, 0, 1},
M_PI / 4.}},
395 {
"right",
Frustum3{{0, 0, -10}, {0, -1, 0},
M_PI / 4.}},
400 std::cout <<
"Run benchmarks: " << std::endl;
402 for (
const auto& fr_pair : testFrusts) {
403 std::cout <<
"Frustum '" << fr_pair.first <<
"'" << std::endl;
405 for (
const auto&
p : frustumVariants) {
407 std::cout <<
"- Benchmarking variant: '" <<
p.first <<
"'" << std::endl;
409 [&]() {
return p.second(testBox, fr_pair.second); }, iters_per_run);
410 std::cout <<
" " << bench_result << std::endl;
413 std::cout << std::endl;