#include #include #include #include #include #include #include namespace { using namespace DetourNavigator; struct Key { AgentBounds mAgentBounds; TilePosition mTilePosition; RecastMesh mRecastMesh; }; struct Item { Key mKey; PreparedNavMeshData mValue; }; osg::Vec2i generateVec2i(int max, auto& random) { std::uniform_int_distribution distribution(0, max); return osg::Vec2i(distribution(random), distribution(random)); } osg::Vec3f generateAgentHalfExtents(float min, float max, auto& random) { std::uniform_int_distribution distribution(min, max); return osg::Vec3f(distribution(random), distribution(random), distribution(random)); } void generateVertices(std::output_iterator auto out, std::size_t number, auto& random) { std::uniform_real_distribution distribution(0.0, 1.0); std::generate_n(out, 3 * (number - number % 3), [&] { return distribution(random); }); } void generateIndices(std::output_iterator auto out, int max, std::size_t number, auto& random) { std::uniform_int_distribution distribution(0, max); std::generate_n(out, number - number % 3, [&] { return distribution(random); }); } AreaType toAreaType(int index) { switch (index) { case 0: return AreaType_null; case 1: return AreaType_water; case 2: return AreaType_door; case 3: return AreaType_pathgrid; case 4: return AreaType_ground; } return AreaType_null; } AreaType generateAreaType(auto& random) { std::uniform_int_distribution distribution(0, 4); return toAreaType(distribution(random)); } void generateAreaTypes(std::output_iterator auto out, std::size_t triangles, auto& random) { std::generate_n(out, triangles, [&] { return generateAreaType(random); }); } void generateWater(std::output_iterator auto out, std::size_t count, auto& random) { std::uniform_real_distribution distribution(0.0, 1.0); std::generate_n(out, count, [&] { return CellWater{ generateVec2i(1000, random), Water{ ESM::Land::REAL_SIZE, distribution(random) } }; }); } Mesh generateMesh(std::size_t triangles, auto& random) { std::uniform_real_distribution distribution(0.0, 1.0); std::vector vertices; std::vector indices; std::vector areaTypes; if (distribution(random) < 0.939) { generateVertices(std::back_inserter(vertices), triangles * 2.467, random); generateIndices(std::back_inserter(indices), static_cast(vertices.size() / 3) - 1, vertices.size() * 1.279, random); generateAreaTypes(std::back_inserter(areaTypes), indices.size() / 3, random); } return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes)); } Heightfield generateHeightfield(auto& random) { std::uniform_real_distribution distribution(0.0, 1.0); Heightfield result; result.mCellPosition = generateVec2i(1000, random); result.mCellSize = ESM::Land::REAL_SIZE; result.mMinHeight = distribution(random); result.mMaxHeight = result.mMinHeight + 1.0; result.mLength = static_cast(ESM::Land::LAND_SIZE); std::generate_n( std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&] { return distribution(random); }); result.mOriginalSize = ESM::Land::LAND_SIZE; result.mMinX = 0; result.mMinY = 0; return result; } FlatHeightfield generateFlatHeightfield(auto& random) { std::uniform_real_distribution distribution(0.0, 1.0); FlatHeightfield result; result.mCellPosition = generateVec2i(1000, random); result.mCellSize = ESM::Land::REAL_SIZE; result.mHeight = distribution(random); return result; } Key generateKey(std::size_t triangles, auto& random) { const CollisionShapeType agentShapeType = CollisionShapeType::Aabb; const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random); const TilePosition tilePosition = generateVec2i(10000, random); const Version version{ .mGeneration = std::uniform_int_distribution(0, 100)(random), .mRevision = std::uniform_int_distribution(0, 10000)(random), }; Mesh mesh = generateMesh(triangles, random); std::vector water; generateWater(std::back_inserter(water), 1, random); RecastMesh recastMesh(version, std::move(mesh), std::move(water), { generateHeightfield(random) }, { generateFlatHeightfield(random) }, {}); return Key{ AgentBounds{ agentShapeType, agentHalfExtents }, tilePosition, std::move(recastMesh) }; } constexpr std::size_t trianglesPerTile = 239; void generateKeys(std::output_iterator auto out, std::size_t count, auto& random) { std::generate_n(out, count, [&] { return generateKey(trianglesPerTile, random); }); } void fillCache(std::output_iterator auto out, auto& random, NavMeshTilesCache& cache) { std::size_t size = cache.getStats().mNavMeshCacheSize; while (true) { Key key = generateKey(trianglesPerTile, random); cache.set(key.mAgentBounds, key.mTilePosition, key.mRecastMesh, std::make_unique()); *out++ = std::move(key); const std::size_t newSize = cache.getStats().mNavMeshCacheSize; if (size >= newSize) break; size = newSize; } } template void getFromFilledCache(benchmark::State& state) { NavMeshTilesCache cache(maxCacheSize); std::minstd_rand random; std::vector keys; fillCache(std::back_inserter(keys), random, cache); generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random); std::size_t n = 0; for (auto _ : state) { const auto& key = keys[n++ % keys.size()]; auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh); benchmark::DoNotOptimize(result); } } void getFromFilledCache_1m_100hit(benchmark::State& state) { getFromFilledCache<1 * 1024 * 1024, 100>(state); } void getFromFilledCache_4m_100hit(benchmark::State& state) { getFromFilledCache<4 * 1024 * 1024, 100>(state); } void getFromFilledCache_16m_100hit(benchmark::State& state) { getFromFilledCache<16 * 1024 * 1024, 100>(state); } void getFromFilledCache_64m_100hit(benchmark::State& state) { getFromFilledCache<64 * 1024 * 1024, 100>(state); } void getFromFilledCache_1m_70hit(benchmark::State& state) { getFromFilledCache<1 * 1024 * 1024, 70>(state); } void getFromFilledCache_4m_70hit(benchmark::State& state) { getFromFilledCache<4 * 1024 * 1024, 70>(state); } void getFromFilledCache_16m_70hit(benchmark::State& state) { getFromFilledCache<16 * 1024 * 1024, 70>(state); } void getFromFilledCache_64m_70hit(benchmark::State& state) { getFromFilledCache<64 * 1024 * 1024, 70>(state); } template void setToBoundedNonEmptyCache(benchmark::State& state) { NavMeshTilesCache cache(maxCacheSize); std::minstd_rand random; std::vector keys; fillCache(std::back_inserter(keys), random, cache); generateKeys(std::back_inserter(keys), keys.size() * 2, random); std::reverse(keys.begin(), keys.end()); std::size_t n = 0; while (state.KeepRunning()) { const auto& key = keys[n++ % keys.size()]; auto result = cache.set( key.mAgentBounds, key.mTilePosition, key.mRecastMesh, std::make_unique()); benchmark::DoNotOptimize(result); } } void setToBoundedNonEmptyCache_1m(benchmark::State& state) { setToBoundedNonEmptyCache<1 * 1024 * 1024>(state); } void setToBoundedNonEmptyCache_4m(benchmark::State& state) { setToBoundedNonEmptyCache<4 * 1024 * 1024>(state); } void setToBoundedNonEmptyCache_16m(benchmark::State& state) { setToBoundedNonEmptyCache<16 * 1024 * 1024>(state); } void setToBoundedNonEmptyCache_64m(benchmark::State& state) { setToBoundedNonEmptyCache<64 * 1024 * 1024>(state); } } // namespace BENCHMARK(getFromFilledCache_1m_100hit); BENCHMARK(getFromFilledCache_4m_100hit); BENCHMARK(getFromFilledCache_16m_100hit); BENCHMARK(getFromFilledCache_64m_100hit); BENCHMARK(getFromFilledCache_1m_70hit); BENCHMARK(getFromFilledCache_4m_70hit); BENCHMARK(getFromFilledCache_16m_70hit); BENCHMARK(getFromFilledCache_64m_70hit); BENCHMARK(setToBoundedNonEmptyCache_1m); BENCHMARK(setToBoundedNonEmptyCache_4m); BENCHMARK(setToBoundedNonEmptyCache_16m); BENCHMARK(setToBoundedNonEmptyCache_64m); BENCHMARK_MAIN();