File indexing completed on 2025-01-18 09:12:27
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Definitions/Algebra.hpp"
0010 #include "Acts/Definitions/Units.hpp"
0011 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0012 #include "Acts/Tests/CommonHelpers/BenchmarkTools.hpp"
0013 #include "Acts/Utilities/BoundingBox.hpp"
0014 #include "Acts/Utilities/Frustum.hpp"
0015 #include "Acts/Utilities/Ray.hpp"
0016
0017 #include <algorithm>
0018 #include <chrono>
0019 #include <functional>
0020 #include <iostream>
0021 #include <map>
0022 #include <numbers>
0023 #include <random>
0024 #include <vector>
0025
0026 using namespace Acts;
0027
0028 struct O {};
0029 using Box = Acts::AxisAlignedBoundingBox<O, float, 3>;
0030 using VertexType = Box::VertexType;
0031 using vertex_array_type = Box::vertex_array_type;
0032 using value_type = Box::value_type;
0033 using Frustum3 = Frustum<float, 3, 4>;
0034 using Ray3 = Ray<float, 3>;
0035
0036 int main(int , char** ) {
0037 std::size_t n = 1000;
0038
0039 std::mt19937 rng(42);
0040 std::uniform_real_distribution<float> dir(0, 1);
0041 std::uniform_real_distribution<float> loc(-10, 10);
0042 std::uniform_real_distribution<float> ang(std::numbers::pi / 10.,
0043 std::numbers::pi / 4.);
0044
0045 Box testBox{nullptr, {0, 0, 0}, Box::Size{{1, 2, 3}}};
0046
0047 std::cout << "\n==== RAY ====\n" << std::endl;
0048
0049 std::map<std::string, bool (*)(const Box&, const Ray3&)> rayVariants;
0050
0051 rayVariants["Nominal"] = [](const auto& box, const auto& ray) -> bool {
0052 return box.intersect(ray);
0053 };
0054
0055 rayVariants["Incl. div., unroll"] = [](const Box& box,
0056 const Ray<float, 3>& ray) {
0057 const VertexType& origin = ray.origin();
0058
0059 const vertex_array_type& d = ray.dir();
0060
0061 double tmin = -INFINITY, tmax = INFINITY;
0062 if (d.x() != 0.0) {
0063 double tx1 = (box.min().x() - origin.x()) / d.x();
0064 double tx2 = (box.max().x() - origin.x()) / d.x();
0065
0066 tmin = std::max(tmin, std::min(tx1, tx2));
0067 tmax = std::min(tmax, std::max(tx1, tx2));
0068 }
0069
0070 if (d.y() != 0.0) {
0071 double ty1 = (box.min().y() - origin.y()) / d.y();
0072 double ty2 = (box.max().y() - origin.y()) / d.y();
0073
0074 tmin = std::max(tmin, std::min(ty1, ty2));
0075 tmax = std::min(tmax, std::max(ty1, ty2));
0076 }
0077
0078 if (d.z() != 0.0) {
0079 double tz1 = (box.min().z() - origin.z()) / d.z();
0080 double tz2 = (box.max().z() - origin.z()) / d.z();
0081
0082 tmin = std::max(tmin, std::min(tz1, tz2));
0083 tmax = std::min(tmax, std::max(tz1, tz2));
0084 }
0085
0086 return tmax > tmin && tmax > 0.0;
0087 };
0088
0089 rayVariants["Incl. div., loop"] = [](const Box& box,
0090 const Ray<float, 3>& ray) {
0091 const VertexType& origin = ray.origin();
0092
0093 const vertex_array_type& d = ray.dir();
0094
0095 double tmin = -INFINITY, tmax = INFINITY;
0096
0097 for (std::size_t i = 0; i < 3; i++) {
0098 if (d[i] == 0.0) {
0099 continue;
0100 }
0101 double t1 = (box.min()[i] - origin[i]) / d[i];
0102 double t2 = (box.max()[i] - origin[i]) / d[i];
0103 tmin = std::max(tmin, std::min(t1, t2));
0104 tmax = std::min(tmax, std::max(t1, t2));
0105 }
0106
0107 return tmax > tmin && tmax > 0.0;
0108 };
0109
0110 rayVariants["Incl. div., min/max alt., unroll"] =
0111 [](const Box& box, const Ray<float, 3>& ray) {
0112 const VertexType& origin = ray.origin();
0113 const vertex_array_type& d = ray.dir();
0114
0115 double tx1 = (box.min().x() - origin.x()) / d.x();
0116 double tx2 = (box.max().x() - origin.x()) / d.x();
0117 double tmin = std::min(tx1, tx2);
0118 double tmax = std::max(tx1, tx2);
0119
0120 double ty1 = (box.min().y() - origin.y()) / d.y();
0121 double ty2 = (box.max().y() - origin.y()) / d.y();
0122 tmin = std::max(tmin, std::min(ty1, ty2));
0123 tmax = std::min(tmax, std::max(ty1, ty2));
0124
0125 double tz1 = (box.min().z() - origin.z()) / d.z();
0126 double tz2 = (box.max().z() - origin.z()) / d.z();
0127 tmin = std::max(tmin, std::min(tz1, tz2));
0128 tmax = std::min(tmax, std::max(tz1, tz2));
0129
0130 return tmax > tmin && tmax > 0.0;
0131 };
0132
0133 rayVariants["No div., min/max alt, unroll"] = [](const Box& box,
0134 const Ray<float, 3>& ray) {
0135 const VertexType& origin = ray.origin();
0136 const vertex_array_type& id = ray.idir();
0137
0138 double tx1 = (box.min().x() - origin.x()) * id.x();
0139 double tx2 = (box.max().x() - origin.x()) * id.x();
0140 double tmin = std::min(tx1, tx2);
0141 double tmax = std::max(tx1, tx2);
0142
0143 double ty1 = (box.min().y() - origin.y()) * id.y();
0144 double ty2 = (box.max().y() - origin.y()) * id.y();
0145 tmin = std::max(tmin, std::min(ty1, ty2));
0146 tmax = std::min(tmax, std::max(ty1, ty2));
0147
0148 double tz1 = (box.min().z() - origin.z()) * id.z();
0149 double tz2 = (box.max().z() - origin.z()) * id.z();
0150 tmin = std::max(tmin, std::min(tz1, tz2));
0151 tmax = std::min(tmax, std::max(tz1, tz2));
0152
0153 return tmax > tmin && tmax > 0.0;
0154 };
0155
0156 rayVariants["No div., min/max orig, loop"] = [](const Box& box,
0157 const Ray<float, 3>& ray) {
0158 const VertexType& origin = ray.origin();
0159 const vertex_array_type& id = ray.idir();
0160 double tmin = -INFINITY, tmax = INFINITY;
0161
0162 for (std::size_t i = 0; i < 3; i++) {
0163 double t1 = (box.min()[i] - origin[i]) * id[i];
0164 double t2 = (box.max()[i] - origin[i]) * id[i];
0165 tmin = std::max(tmin, std::min(t1, t2));
0166 tmax = std::min(tmax, std::max(t1, t2));
0167 }
0168
0169 return tmax > tmin && tmax > 0.0;
0170 };
0171
0172 using Vector3F = Eigen::Matrix<float, 3, 1>;
0173
0174 std::vector<Ray3> rays{n, Ray3{Vector3F{0, 0, 0}, Vector3F{1, 0, 0}}};
0175 std::generate(rays.begin(), rays.end(), [&]() {
0176 const Vector3F d{dir(rng), dir(rng), dir(rng)};
0177 const Vector3F l{loc(rng), loc(rng), loc(rng)};
0178 return Ray3{l, d.normalized()};
0179 });
0180
0181 std::cout << "Make sure ray implementations are identical" << std::endl;
0182 for (const auto& ray : rays) {
0183 std::vector<std::pair<std::string, bool>> results;
0184
0185 std::transform(rayVariants.begin(), rayVariants.end(),
0186 std::back_inserter(results),
0187 [&](const auto& p) -> decltype(results)::value_type {
0188 const auto& [name, func] = p;
0189 return {name, func(testBox, ray)};
0190 });
0191
0192 bool all =
0193 std::ranges::all_of(results, [](const auto& r) { return r.second; });
0194 bool none =
0195 std::ranges::none_of(results, [](const auto& r) { return r.second; });
0196
0197 if (!all && !none) {
0198 std::cerr << "Discrepancy: " << std::endl;
0199 for (const auto& [name, result] : results) {
0200 std::cerr << " - " << name << ": " << result << std::endl;
0201 }
0202
0203 testBox.toStream(std::cerr);
0204 std::cerr << std::endl;
0205 std::cerr << "Ray: [" << ray.origin().transpose() << "], ["
0206 << ray.dir().transpose() << "]" << std::endl;
0207 return -1;
0208 }
0209 }
0210 std::cout << "Seems ok" << std::endl;
0211
0212 std::cout << "Run benchmarks: " << std::endl;
0213 for (const auto& p : rayVariants) {
0214
0215 std::cout << "- Benchmarking variant: '" << p.first << "'" << std::endl;
0216 auto bench_result = Acts::Test::microBenchmark(
0217 [&](const auto& ray) { return p.second(testBox, ray); }, rays);
0218 std::cout << " " << bench_result << std::endl;
0219 }
0220
0221 std::cout << "\n==== FRUSTUM ====\n" << std::endl;
0222
0223 std::map<std::string, bool (*)(const Box&, const Frustum3&)> frustumVariants;
0224
0225 frustumVariants["Nominal"] = [](const auto& box,
0226 const auto& frustum) -> bool {
0227 return box.intersect(frustum);
0228 };
0229
0230 frustumVariants["Manual constexpr loop unroll, early ret."] =
0231 [](const Box& box, const Frustum3& fr) {
0232 constexpr std::size_t sides = 4;
0233
0234
0235 const auto& normals = fr.normals();
0236 const vertex_array_type fr_vmin = box.min() - fr.origin();
0237 const vertex_array_type fr_vmax = box.max() - fr.origin();
0238
0239 auto calc = [&](const auto& normal) {
0240 return (normal.array() < 0).template cast<value_type>() * fr_vmin +
0241 (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0242 };
0243
0244 VertexType p_vtx;
0245
0246 p_vtx = calc(normals[0]);
0247 if (p_vtx.dot(normals[0]) < 0) {
0248 return false;
0249 }
0250
0251 p_vtx = calc(normals[1]);
0252 if (p_vtx.dot(normals[1]) < 0) {
0253 return false;
0254 }
0255
0256 p_vtx = calc(normals[2]);
0257 if (p_vtx.dot(normals[2]) < 0) {
0258 return false;
0259 }
0260
0261 if constexpr (sides > 2) {
0262 p_vtx = calc(normals[3]);
0263 if (p_vtx.dot(normals[3]) < 0) {
0264 return false;
0265 }
0266 }
0267
0268 if constexpr (sides > 3) {
0269 p_vtx = calc(normals[4]);
0270 if (p_vtx.dot(normals[4]) < 0) {
0271 return false;
0272 }
0273 }
0274
0275 if constexpr (sides > 4) {
0276 for (std::size_t i = 5; i <= fr.sides; i++) {
0277 const VertexType& normal = normals[i];
0278
0279 p_vtx = calc(normal);
0280 if (p_vtx.dot(normal) < 0) {
0281 return false;
0282 }
0283 }
0284 }
0285
0286 return true;
0287 };
0288
0289 frustumVariants["Nominal, no early ret."] = [](const Box& box,
0290 const Frustum3& fr) {
0291 const auto& normals = fr.normals();
0292 const vertex_array_type fr_vmin = box.min() - fr.origin();
0293 const vertex_array_type fr_vmax = box.max() - fr.origin();
0294
0295 VertexType p_vtx;
0296 bool result = true;
0297 for (std::size_t i = 0; i < fr.sides + 1; i++) {
0298 const VertexType& normal = normals[i];
0299
0300 p_vtx = (normal.array() < 0).template cast<value_type>() * fr_vmin +
0301 (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0302
0303 result = result && (p_vtx.dot(normal) >= 0);
0304 }
0305 return result;
0306 };
0307
0308 frustumVariants["Manual constexpr unroll, early ret."] =
0309 [](const Box& box, const Frustum3& fr) {
0310 constexpr std::size_t sides = 4;
0311
0312
0313 const auto& normals = fr.normals();
0314 const vertex_array_type fr_vmin = box.min() - fr.origin();
0315 const vertex_array_type fr_vmax = box.max() - fr.origin();
0316
0317 auto calc = [&](const auto& normal) {
0318 return (normal.array() < 0).template cast<value_type>() * fr_vmin +
0319 (normal.array() >= 0).template cast<value_type>() * fr_vmax;
0320 };
0321
0322 VertexType p_vtx;
0323 bool result = true;
0324
0325 p_vtx = calc(normals[0]);
0326 result = result && (p_vtx.dot(normals[0]) >= 0);
0327
0328 p_vtx = calc(normals[1]);
0329 result = result && (p_vtx.dot(normals[1]) >= 0);
0330
0331 p_vtx = calc(normals[2]);
0332 result = result && (p_vtx.dot(normals[2]) >= 0);
0333
0334 if constexpr (sides > 2) {
0335 p_vtx = calc(normals[3]);
0336 result = result && (p_vtx.dot(normals[3]) >= 0);
0337 }
0338
0339 if constexpr (sides > 3) {
0340 p_vtx = calc(normals[4]);
0341 result = result && (p_vtx.dot(normals[4]) >= 0);
0342 }
0343
0344 if constexpr (sides > 4) {
0345 for (std::size_t i = 5; i <= fr.sides; i++) {
0346 const VertexType& normal = normals[i];
0347
0348 p_vtx = calc(normal);
0349 result = result && (p_vtx.dot(normal) >= 0);
0350 }
0351 }
0352
0353 return result;
0354 };
0355
0356 std::vector<Frustum3> frustums{
0357 n, Frustum3{{0, 0, 0}, {1, 0, 0}, std::numbers::pi / 2.}};
0358 std::generate(frustums.begin(), frustums.end(), [&]() {
0359 const Vector3F d{dir(rng), dir(rng), dir(rng)};
0360 const Vector3F l{loc(rng), loc(rng), loc(rng)};
0361 return Frustum3{l, d.normalized(), ang(rng)};
0362 });
0363
0364 std::cout << "Make sure frustum implementations are identical" << std::endl;
0365 for (const auto& fr : frustums) {
0366 std::vector<std::pair<std::string, bool>> results;
0367
0368 std::transform(frustumVariants.begin(), frustumVariants.end(),
0369 std::back_inserter(results),
0370 [&](const auto& p) -> decltype(results)::value_type {
0371 const auto& [name, func] = p;
0372 return {name, func(testBox, fr)};
0373 });
0374
0375 bool all =
0376 std::ranges::all_of(results, [](const auto& r) { return r.second; });
0377 bool none =
0378 std::ranges::none_of(results, [](const auto& r) { return r.second; });
0379
0380 if (!all && !none) {
0381 std::cerr << "Discrepancy: " << std::endl;
0382 for (const auto& [name, result] : results) {
0383 std::cerr << " - " << name << ": " << result << std::endl;
0384 }
0385
0386 testBox.toStream(std::cerr);
0387 std::cerr << std::endl;
0388 std::cerr << "Frustum: [" << fr.origin().transpose() << "], ["
0389 << fr.dir().transpose() << "]" << std::endl;
0390 return -1;
0391 }
0392 }
0393 std::cout << "Seems ok" << std::endl;
0394
0395 std::size_t iters_per_run = 1000;
0396
0397 std::vector<std::pair<std::string, Frustum3>> testFrusts = {
0398 {"away", Frustum3{{0, 0, -10}, {0, 0, -1}, std::numbers::pi / 4.}},
0399 {"towards", Frustum3{{0, 0, -10}, {0, 0, 1}, std::numbers::pi / 4.}},
0400 {"left", Frustum3{{0, 0, -10}, {0, 1, 0}, std::numbers::pi / 4.}},
0401 {"right", Frustum3{{0, 0, -10}, {0, -1, 0}, std::numbers::pi / 4.}},
0402 {"up", Frustum3{{0, 0, -10}, {1, 0, 0}, std::numbers::pi / 4.}},
0403 {"down", Frustum3{{0, 0, -10}, {-1, 0, 0}, std::numbers::pi / 4.}},
0404 };
0405
0406 std::cout << "Run benchmarks: " << std::endl;
0407
0408 for (const auto& fr_pair : testFrusts) {
0409 std::cout << "Frustum '" << fr_pair.first << "'" << std::endl;
0410
0411 for (const auto& p : frustumVariants) {
0412
0413 std::cout << "- Benchmarking variant: '" << p.first << "'" << std::endl;
0414 auto bench_result = Acts::Test::microBenchmark(
0415 [&]() { return p.second(testBox, fr_pair.second); }, iters_per_run);
0416 std::cout << " " << bench_result << std::endl;
0417 }
0418
0419 std::cout << std::endl;
0420 }
0421 return 0;
0422 }