• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 15192227550

22 May 2025 04:45PM UTC coverage: 50.301% (+0.02%) from 50.286%
15192227550

Pull #1772

github

web-flow
Merge 124de9c89 into 0d2a837ba
Pull Request #1772: Use C++17 variable templates for traits

12 of 13 new or added lines in 6 files covered. (92.31%)

3 existing lines in 2 files now uncovered.

22365 of 44462 relevant lines covered (50.3%)

35013.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/libs/s25main/desktops/dskBenchmark.cpp
1
// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "dskBenchmark.h"
6
#include "Game.h"
7
#include "Loader.h"
8
#include "PlayerInfo.h"
9
#include "RttrForeachPt.h"
10
#include "WindowManager.h"
11
#include "buildings/nobMilitary.h"
12
#include "controls/ctrlText.h"
13
#include "drivers/VideoDriverWrapper.h"
14
#include "dskMainMenu.h"
15
#include "factories/BuildingFactory.h"
16
#include "figures/nofPassiveSoldier.h"
17
#include "figures/nofPassiveWorker.h"
18
#include "helpers/mathFuncs.h"
19
#include "helpers/toString.h"
20
#include "lua/GameDataLoader.h"
21
#include "ogl/FontStyle.h"
22
#include "ogl/IRenderer.h"
23
#include "random/Random.h"
24
#include "world/GameWorld.h"
25
#include "world/GameWorldView.h"
26
#include "world/GameWorldViewer.h"
27
#include "world/MapLoader.h"
28
#include "gameTypes/RoadBuildState.h"
29
#include "gameData/GameLoader.h"
30
#include "s25util/Log.h"
31
#include "s25util/strFuncs.h"
32
#include <helpers/chronoIO.h>
33
#include <memory>
34
#include <random>
35

36
namespace {
37
enum
38
{
39
    ID_txtHelp = dskMenuBase::ID_FIRST_FREE,
40
    ID_txtAmount,
41
    ID_first
42
};
43
}
44

45
static const unsigned numTestFrames = 500u;
46

47
struct dskBenchmark::GameView
48
{
49
    GameWorldViewer viewer;
50
    GameWorldView view;
51
    GameView(GameWorldBase& gw, Extent size) : viewer(0u, gw), view(viewer, Position(0, 0), size)
×
52
    {
53
        viewer.InitTerrainRenderer();
×
54
        view.MoveToMapPt(MapPoint(0, 0));
×
55
        view.ToggleShowBQ();
×
56
        view.ToggleShowNames();
×
57
    }
×
58
};
59

60
dskBenchmark::dskBenchmark()
×
61
    : curTest_(Benchmark::None), runAll_(false), numInstances_(1000), frameCtr_(FrameCounter::clock::duration::max())
×
62
{
63
    for(std::chrono::milliseconds& t : testDurations_)
×
64
        t = std::chrono::milliseconds::zero();
×
65
    AddText(ID_txtHelp, DrawPoint(5, 5), "Use F1-F5 to start benchmark, F10 for all, NUM_n to set amount of instances",
×
66
            COLOR_YELLOW, FontStyle::LEFT, LargeFont);
×
67
    AddText(ID_txtAmount, DrawPoint(795, 5), "Instances: default", COLOR_YELLOW, FontStyle::RIGHT, LargeFont);
×
68
}
×
69

70
dskBenchmark::~dskBenchmark()
×
71
{
72
    try
73
    {
74
        printTimes();
×
75
    } catch(...)
×
76
    {}
77
}
×
78

79
bool dskBenchmark::Msg_KeyDown(const KeyEvent& ke)
×
80
{
81
    switch(ke.kt)
×
82
    {
83
        case KeyType::Escape: WINDOWMANAGER.Switch(std::make_unique<dskMainMenu>()); break;
×
84
        case KeyType::F1: startTest(Benchmark::Text); break;
×
85
        case KeyType::F2: startTest(Benchmark::Primitives); break;
×
86
        case KeyType::F3: startTest(Benchmark::EmptyGame); break;
×
87
        case KeyType::F4: startTest(Benchmark::BasicGame); break;
×
88
        case KeyType::F5: startTest(Benchmark::FullGame); break;
×
89
        case KeyType::F10:
×
90
            runAll_ = true;
×
91
            startTest(Benchmark::Text);
×
92
            break;
×
93
        case KeyType::Char:
×
94
            if(ke.c >= '0' && ke.c <= '9')
×
95
            {
96
                numInstances_ = (ke.c - '0') * 100;
×
97
                if(numInstances_ == 0)
×
98
                    numInstances_ = 1000;
×
99
                GetCtrl<ctrlText>(ID_txtAmount)->SetText("Instances: " + helpers::toString(numInstances_));
×
100
                break;
×
101
            } else
102
                return dskMenuBase::Msg_KeyDown(ke);
×
103
        default: return dskMenuBase::Msg_KeyDown(ke);
×
104
    }
105
    return true;
×
106
}
107

108
void dskBenchmark::Msg_PaintAfter()
×
109
{
110
    for(const ColoredRect& rect : rects_)
×
111
        DrawRectangle(rect.rect, rect.clr);
×
112
    for(const ColoredLine& line : lines_)
×
113
        DrawLine(line.p1, line.p2, line.width, line.clr);
×
114
    if(gameView_)
×
115
    {
116
        RoadBuildState roadState;
×
117
        roadState.mode = RoadBuildMode::Disabled;
×
118
        gameView_->view.Draw(roadState, MapPoint::Invalid(), false);
×
119
    }
120
    if(curTest_ != Benchmark::None)
×
121
    {
122
        if(frameCtr_.getCurNumFrames() + 1u >= numTestFrames)
×
123
            VIDEODRIVER.GetRenderer()->synchronize();
×
124
        frameCtr_.update();
×
125
        if(frameCtr_.getCurNumFrames() >= numTestFrames)
×
126
            finishTest();
×
127
    }
128
    dskMenuBase::Msg_PaintAfter();
×
129
}
×
130

131
void dskBenchmark::SetActive(bool activate)
×
132
{
133
    if(!IsActive() && activate)
×
134
        VIDEODRIVER.ResizeScreen(VideoMode(1600, 900), false);
×
135
    dskMenuBase::SetActive(activate);
×
136
}
×
137

138
void dskBenchmark::startTest(Benchmark test)
×
139
{
140
    uint32_t seed = 0x1337;
×
141
    std::mt19937 rng(seed);
×
142
    switch(test)
×
143
    {
144
        case Benchmark::None: return;
×
145
        case Benchmark::Text:
×
146
        {
147
            static const std::string charset =
148
              "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()`~-_=+[{]{\\|;:'\",<.>/? ";
×
149

150
            std::uniform_int_distribution<unsigned> distr(1, 30);
×
151
            std::uniform_int_distribution<unsigned> distr2(0, 100000);
×
152
            std::uniform_int_distribution<unsigned> distrMove(10, 25);
×
153
            DrawPoint pt(0, 0);
×
154
            const glFont* fnt = NormalFont;
×
155
            for(int i = 0; i < numInstances_; i++)
×
156
            {
157
                std::string txt = createRandString(distr(rng), charset, seed);
×
158
                seed += distr2(rng);
×
159
                pt.y += distrMove(rng);
×
160
                if(pt.y >= 580)
×
161
                {
162
                    pt.y = 0;
×
163
                    pt.x += 150 + distrMove(rng) / 3;
×
164
                    if(pt.x >= 780)
×
165
                        pt.x = distrMove(rng);
×
166
                }
167
                AddText(ID_first + i, pt, txt, COLOR_YELLOW, FontStyle::LEFT, fnt);
×
168
            }
169
            break;
×
170
        }
171
        case Benchmark::Primitives:
×
172
        {
173
            Extent screenSize = VIDEODRIVER.GetRenderSize();
×
174
            std::uniform_int_distribution<unsigned> distSize(5, 50);
×
175
            std::uniform_int_distribution<unsigned> distClr(0, 0xFF);
×
176
            std::uniform_int_distribution<unsigned> distrMove(10, 50);
×
177
            std::uniform_int_distribution<unsigned> distrPosX(0, screenSize.x);
×
178
            std::uniform_int_distribution<unsigned> distrPosY(0, screenSize.y);
×
179
            std::uniform_int_distribution<unsigned> distrWidth(1, 10);
×
180
            DrawPoint pt(0, 0);
×
181
            for(int i = 0; i < numInstances_; i++)
×
182
            {
183
                ColoredRect rect;
×
184
                rect.rect.move(pt);
×
185
                rect.rect.setSize(Extent(distSize(rng), distSize(rng)));
×
186
                unsigned clr = distClr(rng);
×
187
                unsigned alpha = std::min((distClr(rng) + 10u) * 10u, 0xFFu);
×
188
                rect.clr = MakeColor(alpha, clr, clr, clr);
×
189
                rects_.push_back(rect);
×
190
                pt.y += distrMove(rng);
×
191
                if(pt.y >= static_cast<int>(screenSize.y) - 20)
×
192
                {
193
                    pt.y = 0;
×
194
                    pt.x += 150 + distrMove(rng) / 3;
×
195
                    if(pt.x >= static_cast<int>(screenSize.x) - 20)
×
196
                        pt.x = distrMove(rng);
×
197
                }
198
                ColoredLine line;
×
199
                line.p1 = Position(distrPosX(rng), distrPosY(rng));
×
200
                line.p2 = Position(distrPosX(rng), distrPosY(rng));
×
201
                line.width = distrWidth(rng);
×
202
                line.clr = MakeColor(alpha, clr, clr, clr);
×
203
                lines_.push_back(line);
×
204
            }
205
            break;
×
206
        }
207
        case Benchmark::EmptyGame:
×
208
            createGame();
×
209
            if(!game_)
×
210
                return;
×
211
            RTTR_FOREACH_PT(MapPoint, game_->world_.GetSize())
×
212
            {
213
                game_->world_.SetVisibility(pt, 0, Visibility::Visible);
×
214
            }
215
            break;
×
216
        case Benchmark::BasicGame:
×
217
        {
218
            createGame();
×
219
            if(!game_)
×
220
                return;
×
221
            std::vector<MapPoint> hqs(2, MapPoint(0, 0));
×
222
            hqs[1].x += 30;
×
223
            MapLoader::PlaceHQs(game_->world_, hqs, false);
×
224
            break;
×
225
        }
226
        case Benchmark::FullGame:
×
227
        {
228
            createGame();
×
229
            if(!game_)
×
230
                return;
×
231
            std::vector<MapPoint> hqs(2, MapPoint(0, 0));
×
232
            hqs[1].x += 30;
×
233
            MapLoader::PlaceHQs(game_->world_, hqs, false);
×
234
            for(unsigned i = 0; i < hqs.size(); i++)
×
235
            {
236
                std::vector<MapPoint> pts = game_->world_.GetPointsInRadius(hqs[i], 15);
×
237
                std::bernoulli_distribution dist(numInstances_ / 1000.f);
×
238
                std::bernoulli_distribution distEqual;
×
239
                std::array<BuildingType, 5> blds = {{BuildingType::Barracks, BuildingType::Mill, BuildingType::IronMine,
×
240
                                                     BuildingType::Slaughterhouse, BuildingType::Bakery}};
241
                std::uniform_int_distribution<unsigned> getBld(0, blds.size() - 1);
×
242
                std::uniform_int_distribution<unsigned> getJob(0, helpers::MaxEnumValue_v<Job>);
×
243
                std::uniform_int_distribution<unsigned> getDir(0, helpers::MaxEnumValue_v<Direction>);
×
244
                for(MapPoint pt : pts)
×
245
                {
246
                    MapPoint flagPt = game_->world_.GetNeighbour(pt, Direction::SouthEast);
×
247
                    if(game_->world_.GetNode(pt).obj || game_->world_.GetNode(flagPt).obj || !dist(rng))
×
248
                        continue;
×
249
                    BuildingType bldType = blds[getBld(rng)];
×
250
                    noBuilding* bld = BuildingFactory::CreateBuilding(
×
251
                      game_->world_, bldType, pt, i, distEqual(rng) ? Nation::Africans : Nation::Japanese);
×
252
                    if(bldType == BuildingType::Barracks)
×
253
                    {
254
                        auto* mil = static_cast<nobMilitary*>(bld);
×
255
                        mil->AddPassiveSoldier(std::make_unique<nofPassiveSoldier>(pt, i, mil, mil, 0));
×
256
                    }
257
                    auto& figure = game_->world_.AddFigure(
×
258
                      flagPt, std::make_unique<nofPassiveWorker>(Job(getJob(rng)), flagPt, i, nullptr));
×
259
                    figure.StartWandering();
×
260
                    figure.StartWalking(Direction(getDir(rng)));
×
261
                }
262
            }
263
            break;
×
264
        }
265
    }
266
    if(game_)
×
267
        gameView_ = std::make_unique<GameView>(game_->world_, VIDEODRIVER.GetRenderSize());
×
268
    VIDEODRIVER.GetRenderer()->synchronize();
×
269
    VIDEODRIVER.setTargetFramerate(-1);
×
270
    curTest_ = test;
×
271
    frameCtr_ = FrameCounter(frameCtr_.getUpdateInterval());
×
272
}
273

274
void dskBenchmark::finishTest()
×
275
{
276
    using namespace std::chrono;
277
    using helpers::withUnit;
278
    LOG.write("Benchmark #%1% took %2%. -> %3%m/frame\n") % rttr::enum_cast(curTest_)
×
279
      % withUnit(duration_cast<duration<float>>(frameCtr_.getCurIntervalLength()))
×
280
      % withUnit(duration_cast<milliseconds>(frameCtr_.getCurIntervalLength() / frameCtr_.getCurNumFrames()));
×
281
    if(testDurations_[curTest_] == milliseconds::zero())
×
282
        testDurations_[curTest_] = duration_cast<milliseconds>(frameCtr_.getCurIntervalLength());
×
283
    else
284
        testDurations_[curTest_] =
×
285
          duration_cast<milliseconds>(testDurations_[curTest_] + frameCtr_.getCurIntervalLength()) / 2;
×
286

287
    std::vector<Window*> ctrls = GetCtrls<Window>();
×
288
    for(Window* ctrl : ctrls)
×
289
    {
290
        if(ctrl->GetID() >= ID_first)
×
291
            DeleteCtrl(ctrl->GetID());
×
292
    }
293
    rects_.clear();
×
294
    lines_.clear();
×
295
    gameView_.reset();
×
296
    game_.reset();
×
297
    SetFpsDisplay(true);
×
298
    VIDEODRIVER.setTargetFramerate(0);
×
299
    if(!runAll_)
×
300
        curTest_ = Benchmark::None;
×
301
    else
302
    {
NEW
303
        if(curTest_ == helpers::MaxEnumerator_v<Benchmark>)
×
304
            curTest_ = Benchmark::None;
×
305
        else
306
        {
307
            curTest_ = Benchmark(rttr::enum_cast(curTest_) + 1);
×
308
            startTest(curTest_);
×
309
        }
310
    }
311
}
×
312

313
void dskBenchmark::createGame()
×
314
{
315
    RANDOM.Init(42);
×
316
    std::vector<PlayerInfo> players;
×
317
    PlayerInfo p;
×
318
    p.ps = PlayerState::Occupied;
×
319
    p.nation = Nation::Africans;
×
320
    p.color = PLAYER_COLORS[0];
×
321
    players.push_back(p);
×
322
    p.nation = Nation::Japanese;
×
323
    p.color = PLAYER_COLORS[1];
×
324
    players.push_back(p);
×
325
    game_ = std::make_shared<Game>(GlobalGameSettings(), 0u, players);
×
326
    GameWorld& world = game_->world_;
×
327
    try
328
    {
329
        loadGameData(world.GetDescriptionWriteable());
×
330
        world.Init(MapExtent(128, 128));
×
331
        const WorldDescription& desc = world.GetDescription();
×
332
        DescIdx<TerrainDesc> lastTerrain(0);
×
333
        int lastHeight = 10;
×
334
        std::mt19937 rng(42);
×
335
        using std::uniform_int_distribution;
336
        uniform_int_distribution<int> percentage(0, 100);
×
337
        uniform_int_distribution<int> randTerrain(0, desc.terrain.size() / 2);
×
338
        RTTR_FOREACH_PT(MapPoint, world.GetSize())
×
339
        {
340
            MapNode& node = world.GetNodeWriteable(pt);
×
341
            DescIdx<TerrainDesc> t;
×
342
            // 90% chance of using the same terrain
343
            if(percentage(rng) <= 90)
×
344
                t = lastTerrain;
×
345
            else
346
                t.value = randTerrain(rng);
×
347
            node.t1 = t;
×
348
            lastTerrain = t;
×
349
            if(percentage(rng) <= 90)
×
350
                t = lastTerrain;
×
351
            else
352
                t.value = randTerrain(rng);
×
353
            node.t2 = t;
×
354
            lastTerrain = t;
×
355
            if(percentage(rng) <= 70)
×
356
                lastHeight = helpers::clamp(lastHeight + uniform_int_distribution<int>(-1, 1)(rng), 8, 13);
×
357
            node.altitude = lastHeight;
×
358
        }
359
        MapLoader::InitShadows(world);
×
360
        MapLoader::SetMapExplored(world);
×
361

362
        GameLoader loader(LOADER, game_);
×
363
        if(!loader.load())
×
364
            throw "GUI";
×
365
    } catch(...)
×
366
    {
367
        game_.reset();
×
368
    }
369
}
×
370

371
void dskBenchmark::printTimes() const
×
372
{
373
    using namespace std::chrono;
374

375
    milliseconds total(0);
×
376
    for(const auto i : helpers::enumRange<Benchmark>())
×
377
    {
378
        if(i == Benchmark::None)
×
379
            continue;
×
380
        LOG.write("Benchmark #%1% took %2% -> %3%/frame\n") % rttr::enum_cast(i)
×
381
          % helpers::withUnit(duration_cast<duration<float>>(testDurations_[i]))
×
382
          % helpers::withUnit(duration_cast<milliseconds>(testDurations_[i] / numTestFrames));
×
383
        total += testDurations_[i];
×
384
    }
385
    LOG.write("Total benchmark time; %1% -> %2%/frame\n") % helpers::withUnit(duration_cast<duration<float>>(total))
×
386
      % helpers::withUnit(duration_cast<milliseconds>(total / numTestFrames));
×
387
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc