Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
Presentation.cpp
Go to the documentation of this file.
1#include "Presentation.hpp"
2
3#include <charconv>
4#include <cstdio>
5#include <optional>
6#include <utility>
7
9{
10 namespace
11 {
12 [[nodiscard]] std::expected<std::pair<int, int>, std::string>
13 parseResolution(std::string_view value)
14 {
15 const auto separator = value.find_first_of("xX");
16 if (separator == std::string_view::npos ||
17 separator == 0 || separator + 1 >= value.size())
18 return std::unexpected(
19 "--resolution expects WIDTHxHEIGHT");
20
21 int width{};
22 int height{};
23 const auto widthResult = std::from_chars(
24 value.data(), value.data() + separator, width);
25 const auto heightResult = std::from_chars(
26 value.data() + separator + 1,
27 value.data() + value.size(),
28 height);
29 if (widthResult.ec != std::errc{} ||
30 widthResult.ptr != value.data() + separator ||
31 heightResult.ec != std::errc{} ||
32 heightResult.ptr != value.data() + value.size() ||
33 width < 640 || height < 360 ||
34 width > 16384 || height > 16384)
35 return std::unexpected(
36 "--resolution must be between 640x360 and 16384x16384");
37
38 return std::pair{width, height};
39 }
40
41
42 [[nodiscard]] std::optional<PresentMode>
43 parsePresentMode(std::string_view value) noexcept
44 {
45 if (value == "vsync") return PresentMode::Vsync;
46 if (value == "mailbox") return PresentMode::Mailbox;
47 if (value == "immediate") return PresentMode::Immediate;
48 return std::nullopt;
49 }
50 }
51
52
53 std::expected<Options, std::string>
54 parseArguments(std::span<const std::string_view> arguments)
55 {
56 Options result;
57 bool modeExplicit = false;
58 bool presentExplicit = false;
59
60 for (std::size_t index = 0; index < arguments.size(); ++index)
61 {
62 const auto argument = arguments[index];
63
64 const auto setMode = [&](WindowMode mode)
65 -> std::expected<void, std::string>
66 {
67 if (modeExplicit)
68 return std::unexpected(
69 "window mode was specified more than once");
70 result.windowMode = mode;
71 modeExplicit = true;
72 return {};
73 };
74
75 if (argument == "--windowed")
76 {
77 if (auto changed = setMode(WindowMode::Windowed); !changed)
78 return std::unexpected(changed.error());
79 }
80 else if (argument == "--fullscreen")
81 {
82 if (auto changed =
84 return std::unexpected(changed.error());
85 }
86 else if (argument == "--exclusive-fullscreen")
87 {
88 if (auto changed =
90 return std::unexpected(changed.error());
91 }
92 else if (argument == "--resolution")
93 {
94 if (result.resolutionExplicit ||
95 index + 1 == arguments.size())
96 return std::unexpected(
97 "--resolution requires one WIDTHxHEIGHT value");
98 const auto parsed = parseResolution(arguments[++index]);
99 if (!parsed)
100 return std::unexpected(parsed.error());
101 result.width = parsed->first;
102 result.height = parsed->second;
103 result.resolutionExplicit = true;
104 }
105 else if (argument == "--present-mode")
106 {
107 if (presentExplicit ||
108 index + 1 == arguments.size())
109 return std::unexpected(
110 "--present-mode requires vsync, mailbox or immediate");
111 const auto parsed = parsePresentMode(arguments[++index]);
112 if (!parsed)
113 return std::unexpected(
114 "--present-mode requires vsync, mailbox or immediate");
115 result.presentMode = *parsed;
116 presentExplicit = true;
117 }
118 else
119 {
120 result.remaining.push_back(argument);
121 }
122 }
123
124 return result;
125 }
126
127
128 SDL_WindowFlags windowFlags(const Options& options) noexcept
129 {
130 SDL_WindowFlags flags =
133
134 if (options.windowMode != WindowMode::Windowed)
135 flags |= SDL_WINDOW_FULLSCREEN;
136
137 return flags;
138 }
139
140
141 std::pair<int, int> initialWindowSize(
142 const Options& options) noexcept
143 {
144 return {options.width, options.height};
145 }
146
147
148 std::expected<void, std::string>
150 {
151 if (!window)
152 return std::unexpected("presentation window is null");
153
154 if (options.windowMode == WindowMode::Windowed)
155 {
156 if (!SDL_SetWindowFullscreen(window, false))
157 return std::unexpected(SDL_GetError());
158 if (!SDL_SetWindowSize(window, options.width, options.height))
159 return std::unexpected(SDL_GetError());
161 window,
164 return {};
165 }
166
168 {
169 if (!SDL_SetWindowFullscreenMode(window, nullptr) ||
171 return std::unexpected(SDL_GetError());
172 return {};
173 }
174
176 if (display == 0)
177 display = SDL_GetPrimaryDisplay();
178 if (display == 0)
179 return std::unexpected("no display is available for fullscreen");
180
181 int width = options.width;
182 int height = options.height;
183 float refreshRate = 0.0F;
184 if (!options.resolutionExplicit)
185 {
186 const auto* desktop = SDL_GetDesktopDisplayMode(display);
187 if (!desktop)
188 return std::unexpected(SDL_GetError());
189 width = desktop->w;
190 height = desktop->h;
191 refreshRate = desktop->refresh_rate;
192 }
193
196 display, width, height, refreshRate, true, &closest))
197 return std::unexpected(SDL_GetError());
198
201 return std::unexpected(SDL_GetError());
202
203 return {};
204 }
205
206
207 std::expected<void, std::string>
210 const Options& options)
211 {
212 if (!window)
213 return std::unexpected("presentation window is null");
214
215 const bool fullscreen =
217 if (fullscreen)
218 {
219 if (!SDL_SetWindowFullscreen(window, false))
220 return std::unexpected(SDL_GetError());
221 if (!SDL_SetWindowFullscreenMode(window, nullptr))
222 return std::unexpected(SDL_GetError());
223 if (!SDL_SetWindowSize(window, options.width, options.height))
224 return std::unexpected(SDL_GetError());
226 window,
229 return {};
230 }
231
232 if (!SDL_SetWindowFullscreenMode(window, nullptr) ||
234 return std::unexpected(SDL_GetError());
235 return {};
236 }
237
238
240 {
241 switch (mode)
242 {
248 default:
250 }
251 }
252
253
254 std::string_view presentModeName(PresentMode mode) noexcept
255 {
256 switch (mode)
257 {
258 case PresentMode::Mailbox: return "mailbox";
259 case PresentMode::Immediate: return "immediate";
261 default: return "vsync";
262 }
263 }
264
265
266 std::string_view windowModeName(WindowMode mode) noexcept
267 {
268 switch (mode)
269 {
270 case WindowMode::Windowed: return "windowed";
271 case WindowMode::ExclusiveFullscreen: return "exclusive-fullscreen";
273 default: return "borderless-fullscreen";
274 }
275 }
276
277
278 std::expected<SDL_GPUPresentMode, std::string>
280 SDL_GPUDevice* device,
283 {
284 if (!device || !window)
285 return std::unexpected("GPU presentation owner is missing");
286
287 auto selected = toSDLPresentMode(requested);
289 device, window, selected))
290 selected = SDL_GPU_PRESENTMODE_VSYNC;
291
293 device,
294 window,
296 selected))
297 return std::unexpected(
298 std::string("SDL_SetGPUSwapchainParameters: ") +
299 SDL_GetError());
300
301 // Two frames in flight preserves throughput while avoiding a deep
302 // presentation queue. The fixed 60 Hz game clock remains independent.
303 if (!SDL_SetGPUAllowedFramesInFlight(device, 2))
304 return std::unexpected(
305 std::string("SDL_SetGPUAllowedFramesInFlight: ") +
306 SDL_GetError());
307
308 return selected;
309 }
310
311
313 : window_(window),
314 sampleStartNanoseconds_(SDL_GetTicksNS())
315 {
316 }
317
318
320 {
321 ++sampleFrames_;
322 const Uint64 now = SDL_GetTicksNS();
323 const Uint64 elapsed = now - sampleStartNanoseconds_;
324 if (elapsed < 1'000'000'000ULL)
325 return;
326
327 lastFramesPerSecond_ =
328 static_cast<double>(sampleFrames_) *
329 1'000'000'000.0 /
330 static_cast<double>(elapsed);
331
332 if (window_)
333 {
334 int width{};
335 int height{};
336 if (SDL_GetWindowSizeInPixels(window_, &width, &height))
337 {
338 char title[128]{};
339 std::snprintf(
340 title,
341 sizeof(title),
342 "Monopoly Modern - %dx%d - %.1f FPS",
343 width,
344 height,
345 lastFramesPerSecond_);
346 (void)SDL_SetWindowTitle(window_, title);
347 }
348 }
349
350 sampleFrames_ = 0;
351 sampleStartNanoseconds_ = now;
352 }
353
354
356 {
357 return lastFramesPerSecond_;
358 }
359}
std::uint32_t height
std::uint32_t width
unsigned index
std::uint16_t value
Definition Timers.cpp:16
double lastFramesPerSecond() const noexcept
FrameTelemetry(SDL_Window *window) noexcept
std::expected< Options, std::string > parseArguments(std::span< const std::string_view > arguments)
std::expected< void, std::string > configureWindow(SDL_Window *window, const Options &options)
std::string_view presentModeName(PresentMode mode) noexcept
std::string_view windowModeName(WindowMode mode) noexcept
std::expected< SDL_GPUPresentMode, std::string > configureSwapchain(SDL_GPUDevice *device, SDL_Window *window, PresentMode requested)
std::pair< int, int > initialWindowSize(const Options &options) noexcept
SDL_WindowFlags windowFlags(const Options &options) noexcept
SDL_GPUPresentMode toSDLPresentMode(PresentMode mode) noexcept
std::expected< void, std::string > toggleBorderlessFullscreen(SDL_Window *window, const Options &options)
std::vector< std::string_view > remaining