evan 2026.1.4
Evan
Loading...
Searching...
No Matches
EvanPlatform.hpp
1/*
2 Copyright (c) 2025 ETIB Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23#pragma once
24
25#include <utility/system_io/android_system_io.hpp>
26#include <utility/system_io/default_system_io.hpp>
27
28// GLM configuration
29#include <glm/glm.hpp>
30#include <glm/gtc/matrix_transform.hpp>
31#include <glm/gtx/hash.hpp>
32
33#include <array>
34#include <cstddef>
35#include <cstdint>
36#include <cstdlib>
37#include <cstring>
38#include <limits>
39
40#ifdef NDEBUG
41const bool enableValidationLayers = false;
42#else
43const bool enableValidationLayers = true;
44#endif
45
46/*
47 * @brief Validation layers for Vulkan debugging.
48 *
49 * This constant defines the validation layers used for debugging Vulkan
50 * applications. These layers provide additional checks and validation
51 * during development to help identify issues and improve code quality.
52 *
53 */
54const std::vector<const char *> validationLayers = {
55 "VK_LAYER_KHRONOS_validation" // Khronos validation layer
56};
57
58/*
59 * @brief Maximum number of frames in flight.
60 *
61 * This constant defines the maximum number of frames that can be in flight
62 * at any given time. It is used to manage synchronization and resource
63 * allocation for rendering operations in Vulkan.
64 *
65 */
66const int MAX_FRAMES_IN_FLIGHT = 2;
67
68/*
69 * @brief Maximum number of swapchain image sets per frame.
70 *
71 * Used to size per-swapchain synchronization resources (fences and
72 * semaphores) for a frame. Current backends use one (desktop) or two
73 * (OpenXR, one per eye); a larger bound leaves room for multi-view.
74 *
75 */
76const int MAX_SWAPCHAINS = 8;
77
78/*
79 * @brief Maximum number of views renderable in a single multiview pass.
80 *
81 * Stereo view configurations have two views; the multiview uniform layout and
82 * the render-pass view mask are sized for this bound. View configurations with
83 * more views fall back to the per-view recording path.
84 */
85constexpr uint32_t MAX_MULTIVIEW_VIEWS = 2;
86
93enum class MultiviewMode {
94 Auto,
95 On,
96 Off
97};
98
107inline MultiviewMode multiviewMode()
108{
109 static const MultiviewMode mode = [] {
110 const char *value = std::getenv("EVAN_MULTIVIEW");
111 if (value == nullptr || value[0] == '\0') {
112 return MultiviewMode::Auto;
113 }
114 if (std::strcmp(value, "1") == 0 || std::strcmp(value, "on") == 0
115 || std::strcmp(value, "true") == 0
116 || std::strcmp(value, "yes") == 0) {
117 return MultiviewMode::On;
118 }
119 if (std::strcmp(value, "0") == 0 || std::strcmp(value, "off") == 0
120 || std::strcmp(value, "false") == 0
121 || std::strcmp(value, "no") == 0) {
122 return MultiviewMode::Off;
123 }
124 return MultiviewMode::Auto;
125 }();
126 return mode;
127}
128
139inline bool statsFlagEnabled(const char *value)
140{
141 return value != nullptr && value[0] != '\0' && value[0] != '0';
142}
143
154inline unsigned statsIntervalFromValue(const char *value)
155{
156 constexpr unsigned defaultInterval = 120u;
157 constexpr unsigned long maxInterval = 1000000ul;
158
159 if (value == nullptr || value[0] == '\0') {
160 return defaultInterval;
161 }
162
163 char *end = nullptr;
164 const unsigned long parsed = std::strtoul(value, &end, 10);
165 if (end == value || parsed == 0 || parsed > maxInterval) {
166 return defaultInterval;
167 }
168 return static_cast<unsigned>(parsed);
169}
170
180inline bool statsEnabled()
181{
182 static const bool enabled = statsFlagEnabled(std::getenv("EVAN_STATS"));
183 return enabled;
184}
185
194inline unsigned statsLogInterval()
195{
196 static const unsigned interval =
197 statsIntervalFromValue(std::getenv("EVAN_STATS_INTERVAL"));
198 return interval;
199}
200
210inline bool statsGpuEnabled()
211{
212 static const bool enabled = [] {
213 const char *value = std::getenv("EVAN_STATS_GPU");
214 if (value == nullptr || value[0] == '\0') {
215 return true;
216 }
217 return value[0] != '0';
218 }();
219 return enabled;
220}
221
232constexpr uint32_t multiviewViewMask(std::size_t viewCount)
233{
234 if (viewCount < 2
235 || viewCount > static_cast<std::size_t>(MAX_MULTIVIEW_VIEWS)) {
236 return 0u;
237 }
238 return (1u << viewCount) - 1u;
239}
240
259constexpr std::size_t frameSlotCount(std::size_t viewCount,
260 std::size_t swapchainCount)
261{
262 const std::size_t requested =
263 viewCount > swapchainCount ? viewCount : swapchainCount;
264
265 if (requested == 0) {
266 return 1;
267 }
268 return requested > static_cast<std::size_t>(MAX_SWAPCHAINS)
269 ? static_cast<std::size_t>(MAX_SWAPCHAINS)
270 : requested;
271}
272
273/*
274 * @brief Initial number of per-instance transforms per view slot.
275 *
276 * Floor and initial allocation of the instancing vertex buffer: each frame
277 * keeps one slot per view, each slot holding 4x4 model matrices fed to the
278 * vertex shader as per-instance attributes. The buffer grows past this bound
279 * on demand (see nextInstanceCapacity) so the constant is not a hard cap and
280 * no mesh is ever dropped for lack of a transform slot.
281 */
282const int MAX_INSTANCES_PER_VIEW = 1024;
283
297constexpr std::size_t nextInstanceCapacity(std::size_t required,
298 std::size_t current)
299{
300 const std::size_t minimum =
301 static_cast<std::size_t>(MAX_INSTANCES_PER_VIEW);
302 std::size_t capacity = current > minimum ? current : minimum;
303
304 while (capacity < required) {
305 // Saturate instead of overflowing when a request is absurdly large.
306 if (capacity > std::numeric_limits<std::size_t>::max() / 2) {
307 return required;
308 }
309 capacity *= 2;
310 }
311 return capacity;
312}
313
314/*
315 * @brief Maximum number of indirect draw commands per view slot.
316 *
317 * Bounds the indirect draw buffer: each frame keeps one slot per view, each
318 * slot holding up to this many VkDrawIndexedIndirectCommand entries used to
319 * batch the sorted, surviving draws.
320 */
321const int MAX_INDIRECT_COMMANDS_PER_VIEW = 4096;
322
323// Vulkan headers - needed for both GLFW and OpenXR
324#if defined(__GLFW__) || defined(__OPENXR__)
325 #include <vulkan/vulkan.h>
326#endif
327
328#include <vulkan/vulkan.h>
329
330#ifdef __GLFW__
331 #include <vulkan/vulkan.hpp>
332 #define GLFW_INCLUDE_VULKAN
333 #include <GLFW/glfw3.h>
334 #include <GLFW/glfw3native.h>
335#endif
336
337#ifdef __OPENXR__
338 #ifdef _WIN32
339 #define NOMINMAX
340 #include <windows.h>
341 #endif
342 #include <openxr/openxr.h>
343 #ifdef __ANDROID__
344 #include <jni.h>
345 #endif
346 #include <openxr/openxr_platform.h>
347#endif
348
349/*
350 ** @brief Callback type for the debug messenger.
351 **
352 ** This callback is used to handle debug messages from the Vulkan
353 * API.
354 ** It is used to log the debug messages from the Vulkan API.
355 **
356 ** @param severity the severity of the message
357 ** @param type the type of the message
358 ** @param pCallbackData the callback data
359 ** @param pUserData the user data
360 **
361 ** @return true if the message is handled, false otherwise
362 */
363typedef VkBool32 debugCallback_t(VkDebugUtilsMessageSeverityFlagBitsEXT,
364 VkDebugUtilsMessageTypeFlagsEXT,
365 const VkDebugUtilsMessengerCallbackDataEXT *,
366 void *);
bool enabled() noexcept
Whether the global counters currently record.