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();
64 tmin = std::max(tmin,
std::min(tx1, tx2));
65 tmax =
std::min(tmax, std::max(tx1, tx2));
69 double ty1 = (box.
min().y() - origin.y()) / d.y();
70 double ty2 = (box.
max().y() - origin.y()) / d.y();
72 tmin = std::max(tmin,
std::min(ty1, ty2));
73 tmax =
std::min(tmax, std::max(ty1, ty2));
77 double tz1 = (box.
min().z() - origin.z()) / d.z();
78 double tz2 = (box.
max().z() - origin.z()) / d.z();
80 tmin = std::max(tmin,
std::min(tz1, tz2));
81 tmax =
std::min(tmax, std::max(tz1, tz2));
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++) {
99 double t1 = (box.
min()[
i] - origin[
i]) / d[
i];
100 double t2 = (box.
max()[
i] - origin[
i]) / d[
i];
101 tmin = std::max(tmin,
std::min(t1, t2));
102 tmax =
std::min(tmax, std::max(t1, t2));
105 return tmax > tmin && tmax > 0.0;
108 rayVariants[
"Incl. div., min/max alt., unroll"] =
113 double tx1 = (box.
min().x() - origin.x()) / d.x();
114 double tx2 = (box.
max().x() - origin.x()) / d.x();
116 double tmax = std::max(tx1, tx2);
118 double ty1 = (box.
min().y() - origin.y()) / d.y();
119 double ty2 = (box.
max().y() - origin.y()) / d.y();
120 tmin = std::max(tmin,
std::min(ty1, ty2));
121 tmax =
std::min(tmax, std::max(ty1, ty2));
123 double tz1 = (box.
min().z() - origin.z()) / d.z();
124 double tz2 = (box.
max().z() - origin.z()) / d.z();
125 tmin = std::max(tmin,
std::min(tz1, tz2));
126 tmax =
std::min(tmax, std::max(tz1, tz2));
128 return tmax > tmin && tmax > 0.0;
131 rayVariants[
"No div., min/max alt, unroll"] = [](
const Box&
box,
136 double tx1 = (box.
min().x() - origin.x()) *
id.
x();
137 double tx2 = (box.
max().x() - origin.x()) *
id.
x();
139 double tmax = std::max(tx1, tx2);
141 double ty1 = (box.
min().y() - origin.y()) *
id.
y();
142 double ty2 = (box.
max().y() - origin.y()) *
id.
y();
143 tmin = std::max(tmin,
std::min(ty1, ty2));
144 tmax =
std::min(tmax, std::max(ty1, ty2));
146 double tz1 = (box.
min().z() - origin.z()) *
id.
z();
147 double tz2 = (box.
max().z() - origin.z()) *
id.
z();
148 tmin = std::max(tmin,
std::min(tz1, tz2));
149 tmax =
std::min(tmax, std::max(tz1, tz2));
151 return tmax > tmin && tmax > 0.0;
154 rayVariants[
"No div., min/max orig, loop"] = [](
const Box&
box,
158 double tmin = -INFINITY, tmax = INFINITY;
160 for (
size_t i = 0;
i < 3;
i++) {
161 double t1 = (box.
min()[
i] - origin[
i]) *
id[
i];
162 double t2 = (box.
max()[
i] - origin[
i]) *
id[
i];
163 tmin = std::max(tmin,
std::min(t1, t2));
164 tmax =
std::min(tmax, std::max(t1, t2));
167 return tmax > tmin && tmax > 0.0;
170 using Vector3F = Eigen::Matrix<float, 3, 1>;
173 std::generate(rays.begin(), rays.end(), [&]() {
174 const Vector3F d{dir(rng), dir(rng), dir(rng)};
176 return Ray3{l, d.normalized()};
179 std::cout <<
"Make sure ray implementations are identical" << std::endl;
180 for (
const auto& ray : rays) {
181 std::vector<std::pair<std::string, bool>>
results;
184 std::back_inserter(results),
186 const auto& [
name, func] =
p;
187 return {
name, func(testBox, ray)};
190 bool all = std::all_of(results.begin(), results.end(),
191 [](
const auto&
r) {
return r.second; });
192 bool none = std::none_of(results.begin(), results.end(),
193 [](
const auto&
r) {
return r.second; });
196 std::cerr <<
"Discrepancy: " << std::endl;
197 for (
const auto& [
name, result] : results) {
198 std::cerr <<
" - " <<
name <<
": " << result << std::endl;
201 testBox.toStream(std::cerr);
202 std::cerr << std::endl;
203 std::cerr <<
"Ray: [" << ray.origin().transpose() <<
"], ["
204 << ray.dir().transpose() <<
"]" << std::endl;
208 std::cout <<
"Seems ok" << std::endl;
210 std::cout <<
"Run benchmarks: " << std::endl;
211 for (
const auto&
p : rayVariants) {
213 std::cout <<
"- Benchmarking variant: '" <<
p.first <<
"'" << std::endl;
215 [&](
const auto& ray) {
return p.second(testBox, ray); }, rays);
216 std::cout <<
" " << bench_result << std::endl;
219 std::cout <<
"\n==== FRUSTUM ====\n" << std::endl;
221 std::map<std::string, bool (*)(const Box&, const Frustum3&)> frustumVariants;
223 frustumVariants[
"Nominal"] = [](
const auto&
box,
224 const auto& frustum) ->
bool {
228 frustumVariants[
"Manual constexpr loop unroll, early ret."] =
230 constexpr
size_t sides = 4;
233 const auto& normals = fr.normals();
237 auto calc = [&](
const auto& normal) {
238 return (normal.array() < 0).
template cast<value_type>() * fr_vmin +
239 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
244 p_vtx = calc(normals[0]);
245 if (p_vtx.dot(normals[0]) < 0) {
249 p_vtx = calc(normals[1]);
250 if (p_vtx.dot(normals[1]) < 0) {
254 p_vtx = calc(normals[2]);
255 if (p_vtx.dot(normals[2]) < 0) {
259 if constexpr (sides > 2) {
260 p_vtx = calc(normals[3]);
261 if (p_vtx.dot(normals[3]) < 0) {
266 if constexpr (sides > 3) {
267 p_vtx = calc(normals[4]);
268 if (p_vtx.dot(normals[4]) < 0) {
273 if constexpr (sides > 4) {
274 for (
size_t i = 5;
i <= fr.sides;
i++) {
277 p_vtx = calc(normal);
278 if (p_vtx.dot(normal) < 0) {
287 frustumVariants[
"Nominal, no early ret."] = [](
const Box&
box,
289 const auto& normals = fr.normals();
295 for (
size_t i = 0;
i < fr.sides + 1;
i++) {
298 p_vtx = (normal.array() < 0).
template cast<value_type>() * fr_vmin +
299 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
301 result = result && (p_vtx.dot(normal) >= 0);
306 frustumVariants[
"Manual constexpr unroll, early ret."] =
308 constexpr
size_t sides = 4;
311 const auto& normals = fr.normals();
315 auto calc = [&](
const auto& normal) {
316 return (normal.array() < 0).
template cast<value_type>() * fr_vmin +
317 (normal.array() >= 0).
template cast<value_type>() * fr_vmax;
323 p_vtx = calc(normals[0]);
324 result = result && (p_vtx.dot(normals[0]) >= 0);
326 p_vtx = calc(normals[1]);
327 result = result && (p_vtx.dot(normals[1]) >= 0);
329 p_vtx = calc(normals[2]);
330 result = result && (p_vtx.dot(normals[2]) >= 0);
332 if constexpr (sides > 2) {
333 p_vtx = calc(normals[3]);
334 result = result && (p_vtx.dot(normals[3]) >= 0);
337 if constexpr (sides > 3) {
338 p_vtx = calc(normals[4]);
339 result = result && (p_vtx.dot(normals[4]) >= 0);
342 if constexpr (sides > 4) {
343 for (
size_t i = 5;
i <= fr.sides;
i++) {
346 p_vtx = calc(normal);
347 result = result && (p_vtx.dot(normal) >= 0);
354 std::vector<Frustum3> frustums{
n,
Frustum3{{0, 0, 0}, {1, 0, 0}, M_PI / 2.}};
355 std::generate(frustums.begin(), frustums.end(), [&]() {
356 const Vector3F d{dir(rng), dir(rng), dir(rng)};
358 return Frustum3{l, d.normalized(), ang(rng)};
361 std::cout <<
"Make sure frustum implementations are identical" << std::endl;
362 for (
const auto& fr : frustums) {
363 std::vector<std::pair<std::string, bool>>
results;
366 std::back_inserter(results),
368 const auto& [
name, func] =
p;
369 return {
name, func(testBox, fr)};
372 bool all = std::all_of(results.begin(), results.end(),
373 [](
const auto&
r) {
return r.second; });
374 bool none = std::none_of(results.begin(), results.end(),
375 [](
const auto&
r) {
return r.second; });
378 std::cerr <<
"Discrepancy: " << std::endl;
379 for (
const auto& [
name, result] : results) {
380 std::cerr <<
" - " <<
name <<
": " << result << std::endl;
383 testBox.toStream(std::cerr);
384 std::cerr << std::endl;
385 std::cerr <<
"Frustum: [" << fr.origin().transpose() <<
"], ["
386 << fr.dir().transpose() <<
"]" << std::endl;
390 std::cout <<
"Seems ok" << std::endl;
392 size_t iters_per_run = 1000;
394 std::vector<std::pair<std::string, Frustum3>> testFrusts = {
395 {
"away",
Frustum3{{0, 0, -10}, {0, 0, -1}, M_PI / 4.}},
396 {
"towards",
Frustum3{{0, 0, -10}, {0, 0, 1}, M_PI / 4.}},
397 {
"left",
Frustum3{{0, 0, -10}, {0, 1, 0}, M_PI / 4.}},
398 {
"right",
Frustum3{{0, 0, -10}, {0, -1, 0}, M_PI / 4.}},
399 {
"up",
Frustum3{{0, 0, -10}, {1, 0, 0}, M_PI / 4.}},
400 {
"down",
Frustum3{{0, 0, -10}, {-1, 0, 0}, M_PI / 4.}},
403 std::cout <<
"Run benchmarks: " << std::endl;
405 for (
const auto& fr_pair : testFrusts) {
406 std::cout <<
"Frustum '" << fr_pair.first <<
"'" << std::endl;
408 for (
const auto&
p : frustumVariants) {
410 std::cout <<
"- Benchmarking variant: '" <<
p.first <<
"'" << std::endl;
412 [&]() {
return p.second(testBox, fr_pair.second); }, iters_per_run);
413 std::cout <<
" " << bench_result << std::endl;
416 std::cout << std::endl;