evan 2026.1.4
Evan
Loading...
Searching...
No Matches
DesktopBackend.cpp
1/*
2** ETIB PROJECT, 2026
3** evan
4** File description:
5** DesktopBackend
6*/
7
8#include "evan/glfw/DesktopBackend.hpp"
9#include "evan/CheckedCast.hpp"
10
11#include <stdexcept>
12
38static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallback(
39 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
40 VkDebugUtilsMessageTypeFlagsEXT messageType,
41 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
42{
43 static std::unique_ptr<utility::logging::Logger> logger = nullptr;
44 std::string_view message;
45 if (!logger) {
46 logger = std::make_unique<utility::logging::DefaultLogger>(
47 "VulkanValidation");
48 }
49
50 switch (messageType) {
51 case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
52 message = "General";
53 break;
54 case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
55 message = "Validation";
56 break;
57 case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
58 message = "Performance";
59 break;
60 default:
61 message = "Unknown";
62 break;
63 }
64
65 switch (messageSeverity) {
66 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
67 logger->error()
68 << "[" << message << "] " << pCallbackData->pMessage;
69 break;
70 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
71 logger->warning()
72 << "[" << message << "] " << pCallbackData->pMessage;
73 break;
74 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
75 logger->info() << "[" << message << "] " << pCallbackData->pMessage;
76 break;
77 default:
78 logger->debug()
79 << "[" << message << "] " << pCallbackData->pMessage;
80 break;
81 }
82 return VK_FALSE;
83}
84
86 : _platform(&platform)
87{
88 this->getLogger().info() << "Initializing DesktopBackend...";
89
90 Version appVersion(0, 1, 0);
91
92 this->getLogger().info()
93 << "Setting up GLFW platform for DesktopBackend...";
96
97 this->createInstance(platform, "Evan", appVersion);
98
99 this->getLogger().info() << "Creating Vulkan surface for DesktopBackend...";
100 _surface = glfwPlatform.createSurface(_VkInstance);
101
102 this->pickPhysicalDevice();
103 this->createLogicalDevice();
104 this->createPresentQueue();
105
106 this->setupCallbackEvent(glfwPlatform);
107}
108
110{
111 this->getLogger().info()
112 << "Destroying DesktopBackend and cleaning up Vulkan resources...";
113 vkDestroyDevice(_device, nullptr);
114
115 this->getLogger().info() << "Destroying Vulkan surface...";
116 vkDestroySurfaceKHR(_VkInstance, _surface, nullptr);
117
118 this->getLogger().info() << "Destroying Vulkan instance...";
119 vkDestroyInstance(_VkInstance, nullptr);
120}
121
123// Public Methods //
125
128{
129 this->getLogger().info() << "Processing frame for DesktopBackend...";
130 VkResult result = vkQueuePresentKHR(_presentQueue, &presentInfo);
131
133 this->getLogger().info() << "Swap chain is out of date or suboptimal.";
134 return mapVkResult(result);
135 } else if (result != VK_SUCCESS) {
136 this->getLogger().error() << "Failed to present swap chain image!";
137 return mapVkResult(result);
138 }
139 this->getLogger().info() << "Frame presented successfully.";
140 return Error::Ok;
141}
142
144{
145 this->getLogger().info()
146 << "Counting available swap chain formats for DesktopBackend...";
147
149 vkGetPhysicalDeviceSurfaceFormatsKHR(_physicalDevice, _surface,
150 &formatCount, nullptr);
151
152 this->getLogger().info()
153 << "Found " << formatCount << " swap chain formats available.";
154 return formatCount;
155}
156
159{
160 this->getLogger().info()
161 << "Enumerating swap chain formats for DesktopBackend...";
162
163 std::vector<VkSurfaceFormatKHR> swapchainFormats(swapchainFormatCount);
164 vkGetPhysicalDeviceSurfaceFormatsKHR(_physicalDevice, _surface,
166 swapchainFormats.data());
167
168 std::vector<int64_t> swapchainFormats64(swapchainFormatCount);
169 for (size_t i = 0; i < swapchainFormatCount; i++) {
171 static_cast<int64_t>(swapchainFormats[i].format);
172 }
173 this->getLogger().info() << "Enumerated " << swapchainFormatCount
174 << " swap chain formats successfully.";
175 return swapchainFormats64;
176}
177
179{
180 this->getLogger().info() << "Finding queue families for DesktopBackend...";
181
183
186 nullptr);
187
188 this->getLogger().info()
189 << "Found " << queueFamilyCount << " queue families available.";
190
191 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
193 queueFamilies.data());
194
195 this->getLogger().info()
196 << "Checking queue families for graphics and presentation support...";
197 for (uint32_t i = 0; i < queueFamilies.size(); i++) {
198 this->getLogger().info() << "Checking queue family " << i << " with "
199 << queueFamilies[i].queueCount << " queues...";
201 indices.graphicsFamily = i;
202 }
203
204 VkBool32 presentSupport = false;
205
206 this->getLogger().info()
207 << "Checking if queue family " << i << " supports presentation...";
208 vkGetPhysicalDeviceSurfaceSupportKHR(_physicalDevice, i, _surface,
210
211 if (presentSupport) {
212 this->getLogger().info()
213 << "Queue family " << i << " supports presentation.";
214 indices.presentFamily = i;
215 }
216
217 if (indices.isComplete()) {
218 this->getLogger().info() << "Found suitable queue families for "
219 "graphics and presentation support.";
220 break;
221 }
222 }
223
224 this->getLogger().info()
225 << "Queue family indices found: Graphics Family = "
226 << (indices.graphicsFamily.has_value()
227 ? std::to_string(indices.graphicsFamily.value())
228 : "None")
229 << ", Present Family = "
230 << (indices.presentFamily.has_value()
231 ? std::to_string(indices.presentFamily.value())
232 : "None");
233 return indices;
234}
235
237{
238 this->getLogger().info()
239 << "Querying swap chain support details for DesktopBackend...";
240
244
245 this->getLogger().info() << "Querying surface capabilities...";
246 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(_physicalDevice, _surface,
247 &details.capabilities);
248 if (vkGetPhysicalDeviceSurfaceFormatsKHR(_physicalDevice, _surface,
249 &formatCount, nullptr)
250 != VK_SUCCESS) {
251 this->getLogger().error() << "Failed to query surface formats!";
252 this->getLogger().info()
253 << "Returning swap chain support details with capabilities but no "
254 "formats or present modes.";
255 return details;
256 }
257 if (formatCount != 0) {
258 this->getLogger().info()
259 << "Found " << formatCount << " surface formats available.";
260 details.formats.resize(formatCount);
261
262 this->getLogger().info() << "Querying surface formats...";
264 _physicalDevice, _surface, &formatCount, details.formats.data());
265 }
266
267 this->getLogger().info() << "Querying surface present modes...";
268 if (vkGetPhysicalDeviceSurfacePresentModesKHR(_physicalDevice, _surface,
269 &presentModeCount, nullptr)
270 != VK_SUCCESS) {
271 this->getLogger().error() << "Failed to query surface present modes!";
272 this->getLogger().info()
273 << "Returning swap chain support details with capabilities and "
274 "formats but no present modes.";
275 return details;
276 }
277 if (presentModeCount != 0) {
278 this->getLogger().info() << "Found " << presentModeCount
279 << " surface present modes available.";
280 details.presentModes.resize(presentModeCount);
281 this->getLogger().info() << "Querying surface present modes...";
282 vkGetPhysicalDeviceSurfacePresentModesKHR(_physicalDevice, _surface,
284 details.presentModes.data());
285 }
286 this->getLogger().info()
287 << "Swap chain support details queried successfully.";
288 return details;
289}
290
292{
293 this->getLogger().info() << "Creating present queue for DesktopBackend...";
294
295 QueueFamilyIndices indices = this->findQueueFamilies();
296
297 if (!indices.presentFamily.has_value()) {
298 throw std::runtime_error(
299 "Failed to find a present queue family for the logical device");
300 }
301
302 this->getLogger().info()
303 << "Retrieving present queue from logical device...";
304
305 vkGetDeviceQueue(_device, indices.presentFamily.value(), 0, &_presentQueue);
306}
307
309// Protected methods //
311
313 const std::string &appName,
315{
316 this->getLogger().info()
317 << "Creating Vulkan instance for DesktopBackend...";
318
319 bool useValidationLayers = enableValidationLayers;
320 if (useValidationLayers && !this->checkValidationLayerSupport()) {
321 this->getLogger().warning()
322 << "Validation layers requested, but not available. Continuing "
323 "without them.";
324 useValidationLayers = false;
325 }
326
329 appInfo.pApplicationName = appName.c_str();
330 appInfo.applicationVersion = appVersion.to_uint32_t();
331 appInfo.pEngineName = "Evan";
332 appInfo.engineVersion = VK_MAKE_VERSION(0, 1, 0);
333 appInfo.apiVersion = VK_API_VERSION_1_1;
334
335 this->getLogger().info()
336 << "Setting up instance creation information using application info:\n"
337 "Application Name = "
338 << appName << ",\nApplication Version = " << appVersion._major << "."
339 << appVersion._minor << "." << appVersion._patch
340 << ",\nEngine Name = Evan,\nEngine Version = 0.1.0,\nAPI Version = 1.1";
341
342 VkInstanceCreateInfo createInfo {};
344 createInfo.pApplicationInfo = &appInfo;
345
346 auto extensions = platform.getRequiredInstanceExtensions();
347
348 this->getLogger().info()
349 << "Required instance extensions for platform: " << extensions.size()
350 << " extensions found.";
351 // Because the extensions are stored as std::string, we need to convert them
352 // to const char* to pass them to the Vulkan API.
353 auto extensionsWrapped = std::vector<const char *>(extensions.size());
354
355 std::transform(extensions.begin(), extensions.end(),
356 extensionsWrapped.begin(), [](const std::string &str) {
357 return str.c_str();
358 });
359 createInfo.enabledExtensionCount =
360 static_cast<uint32_t>(extensionsWrapped.size());
361 createInfo.ppEnabledExtensionNames = extensionsWrapped.data();
362
363 createInfo.flags |= platform.getInstanceCreateFlags();
364
366
368 createInfo.enabledLayerCount =
369 static_cast<uint32_t>(validationLayers.size());
370 createInfo.ppEnabledLayerNames = validationLayers.data();
371 this->getLogger().info()
372 << "Validation layers enabled for instance creation. Setting up "
373 "debug messenger create info...";
374
375 this->populateDebugMessengerCreateInfo(debugCreateInfo,
376 defaultDebugCallback);
377 createInfo.pNext =
379 } else {
380 createInfo.enabledLayerCount = 0;
381 createInfo.ppEnabledLayerNames = nullptr;
382 }
383
384 this->getLogger().info()
385 << "Creating Vulkan instance with the specified application info, "
386 "extensions, and validation layers...";
387 VkResult result = vkCreateInstance(&createInfo, nullptr, &_VkInstance);
388
390 this->getLogger().warning()
391 << "Failed to create Vulkan instance due to incompatible driver. "
392 "This may be caused by running on a platform that requires the "
393 "VK_KHR_portability_subset extension, such as MacOS. Retrying "
394 "instance creation with VK_KHR_portability_subset enabled...";
395
397
398 createInfo.enabledExtensionCount = (uint32_t)extensionsWrapped.size();
399 createInfo.ppEnabledExtensionNames = extensionsWrapped.data();
400
401 result = vkCreateInstance(&createInfo, nullptr, &_VkInstance);
402 }
403
404 if (result != VK_SUCCESS) {
405 throw std::runtime_error("Failed to create Vulkan instance! VkResult: "
406 + std::to_string(result));
407 }
408 this->getLogger().info() << "Vulkan instance created successfully!";
409}
410
412{
413 this->getLogger().info() << "Creating logical device for DesktopBackend...";
414
415 std::vector<const char *> desktopExtensions = deviceExtensions;
416
417 auto platformDeviceExtensions = _platform->getRequiredDeviceExtensions();
418 for (const auto &extension: platformDeviceExtensions) {
419 desktopExtensions.push_back(extension.c_str());
420 }
421
422 this->getLogger().info()
423 << "Required device extensions for logical device creation: "
424 << desktopExtensions.size() << " extensions required.";
425 std::string extensionsList;
426 for (const char *ext: desktopExtensions) {
427 extensionsList += std::string(ext) + ", ";
428 }
429 this->getLogger().info() << "Device extensions: " << extensionsList;
430
431 QueueFamilyIndices indices = this->findQueueFamilies();
432
433 if (!indices.isComplete()) {
434 throw std::runtime_error(
435 "Failed to find suitable queue families for logical device "
436 "creation");
437 }
438
439 this->getLogger().info()
440 << "Setting up queue create infos for logical device creation...";
441
442 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
443 std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(),
444 indices.presentFamily.value() };
445
446 float queuePriority = 1.0f;
447
448 this->getLogger().info()
449 << "Unique queue families identified: " << uniqueQueueFamilies.size()
450 << " unique queue families found.";
452 this->getLogger().info()
453 << "Setting up queue create info for queue family index "
454 << queueFamily << " with priority " << queuePriority << "...";
457 queueCreateInfo.queueFamilyIndex = queueFamily;
458 queueCreateInfo.queueCount = 1;
459 queueCreateInfo.pQueuePriorities = &queuePriority;
461 }
462
465
466 this->getLogger().info()
467 << "Querying supported features of the physical device...";
469
470 deviceFeatures.samplerAnisotropy = supportedFeatures.samplerAnisotropy;
471 deviceFeatures.sampleRateShading = supportedFeatures.sampleRateShading;
472
473#ifdef DEV
474 deviceFeatures.fillModeNonSolid = VK_TRUE;
475#endif
476
477 // The example shader contract uses the ViewIndex input, whose SPIR-V
478 // MultiView capability requires the multiview feature to be enabled even
479 // though desktop render passes keep a zero view mask. Enable it whenever
480 // the device offers it; the query uses the instance's Vulkan 1.1 version.
482 multiviewFeatures.sType =
484
485 const bool multiviewAvailable = multiviewMode() != MultiviewMode::Off
486 && queryMultiviewSupport(_physicalDevice, VK_API_VERSION_1_1);
487 if (multiviewAvailable) {
488 multiviewFeatures.multiview = VK_TRUE;
489 _multiviewSupported = true;
490 this->getLogger().info()
491 << "Enabling the multiview feature on the Vulkan device.";
492 } else if (multiviewMode() == MultiviewMode::On) {
493 this->getLogger().error()
494 << "EVAN_MULTIVIEW=on but the device does not support the "
495 "multiview feature. Continuing without multiview.";
496 }
497
498 VkDeviceCreateInfo createInfo {};
499 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
500
501 createInfo.queueCreateInfoCount =
502 static_cast<uint32_t>(queueCreateInfos.size());
503 createInfo.pQueueCreateInfos = queueCreateInfos.data();
504
505 createInfo.pEnabledFeatures = &deviceFeatures;
506 createInfo.pNext = _multiviewSupported ? &multiviewFeatures : nullptr;
507
508 createInfo.enabledExtensionCount =
509 static_cast<uint32_t>(desktopExtensions.size());
510 createInfo.ppEnabledExtensionNames = desktopExtensions.data();
511
512 this->getLogger().info()
513 << "Device layers are deprecated; skipping validation layer setup for "
514 "logical device creation.";
515 createInfo.enabledLayerCount = 0;
516 createInfo.ppEnabledLayerNames = nullptr;
517
518 this->getLogger().info()
519 << "Creating logical device with the specified queue create infos, "
520 "enabled features, extensions, and validation layers...";
522 vkCreateDevice(_physicalDevice, &createInfo, nullptr, &_device);
523 if (result != VK_SUCCESS) {
524 throw std::runtime_error("Failed to create logical device! VkResult: "
525 + std::to_string(result));
526 }
527 this->getLogger().info() << "Logical device created successfully!";
528}
529
531{
532 this->getLogger().info() << "Picking physical device for DesktopBackend...";
533
535
536 this->getLogger().info() << "Enumerating physical devices...";
537 vkEnumeratePhysicalDevices(_VkInstance, &deviceCount, nullptr);
538
539 if (deviceCount == 0) {
540 throw std::runtime_error("Failed to find GPUs with Vulkan support!");
541 }
542
543 this->getLogger().info()
544 << "Found " << deviceCount
545 << " physical devices with Vulkan support. Evaluating suitability...";
546
547 std::vector<VkPhysicalDevice> devices(deviceCount);
548 vkEnumeratePhysicalDevices(_VkInstance, &deviceCount, devices.data());
549
550 this->getLogger().info()
551 << "Checking each physical device for suitability...";
552 for (const auto &device: devices) {
555
556 this->getLogger().info() << "Evaluating physical device: "
557 << std::string(deviceProperties.deviceName);
558
559 if (this->isDeviceSuitable(device, deviceExtensions)) {
560 this->getLogger().info() << "Physical device "
561 << std::string(deviceProperties.deviceName)
562 << " is suitable. Selecting this device.";
563 _physicalDevice = device;
564 break;
565 }
566 }
567
568 if (_physicalDevice == VK_NULL_HANDLE) {
569 throw std::runtime_error("Failed to find a suitable GPU!");
570 }
571 this->getLogger().info() << "Physical device selected successfully.";
572}
573
575// Private Methods //
577
578bool evan::DesktopBackend::isDeviceSuitable(
579 VkPhysicalDevice device, std::vector<const char *> deviceExtensions)
580{
581 _physicalDevice = device;
582 QueueFamilyIndices indices = this->findQueueFamilies();
583
585 this->checkDeviceExtensionSupport(device, deviceExtensions);
586 bool swapChainAdequate = false;
587
590 this->querySwapChainSupport();
591 swapChainAdequate = !swapChainSupport.formats.empty()
592 && !swapChainSupport.presentModes.empty();
593 }
594 VkPhysicalDeviceFeatures supportedFeatures;
595 vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
596
597 _physicalDevice = VK_NULL_HANDLE;
598
599 return indices.isComplete() && extensionsSupported && swapChainAdequate
600 && supportedFeatures.samplerAnisotropy;
601}
602
603bool evan::DesktopBackend::checkDeviceExtensionSupport(
604 VkPhysicalDevice device, std::vector<const char *> deviceExtensions)
605{
606 uint32_t extensionCount;
607 if (vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
608 nullptr)
609 != VK_SUCCESS)
610 return false;
611
612 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
613 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount,
614 availableExtensions.data());
615
616 std::set<std::string> requiredExtensions(deviceExtensions.begin(),
617 deviceExtensions.end());
618
619 for (const auto &extension: availableExtensions) {
620 requiredExtensions.erase(extension.extensionName);
621 }
622
623 return requiredExtensions.empty();
624}
625
626bool evan::DesktopBackend::checkValidationLayerSupport()
627{
628 auto availableLayers = this->getAvailableLayers();
629
630 for (const char *layerName: validationLayers) {
631 bool layerFound = false;
632
633 for (const auto &layerProperties: availableLayers) {
634 if (strcmp(layerName, layerProperties.layerName) == 0) {
635 layerFound = true;
636 break;
637 }
638 }
639
640 if (!layerFound) {
641 return false;
642 }
643 }
644 return true;
645}
646
647void evan::DesktopBackend::populateDebugMessengerCreateInfo(
648 VkDebugUtilsMessengerCreateInfoEXT &createInfo,
649 debugCallback_t debugCallback)
650{
651 createInfo = {};
652 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
653 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
654 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
655 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
656 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
657 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
658 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
659 createInfo.pfnUserCallback = debugCallback;
660}
661
662void evan::DesktopBackend::setupCallbackEvent(const IPlatform &platform)
663{
664 this->getLogger().info()
665 << "Setting up GLFW input callbacks for DesktopBackend...";
666
667 const IDesktopPlatform &glfwPlatform =
669
670 this->getLogger().info() << "Setting GLFW window user pointer to the "
671 "platform instance for callback access...";
672 glfwSetWindowUserPointer(glfwPlatform._window, (void *)&glfwPlatform);
673
674 this->getLogger().info()
675 << "Setting GLFW key callback for keyboard input events...";
676 glfwSetKeyCallback(
677 glfwPlatform._window,
678 [](GLFWwindow *window, int key, int scancode, int action, int mods) {
679 auto *self = static_cast<evan::IDesktopPlatform *>(
680 glfwGetWindowUserPointer(window));
681
682 (void)window;
683 (void)scancode;
684 auto event = std::make_shared<utility::event::KeyboardEvent>();
685 event->setKeycode(self->convertGlfwKeyToKeyCode(key));
686 event->setIsDownEvent(action == GLFW_PRESS
687 || action == GLFW_REPEAT);
688 event->setModifiers(self->convertGlfwModsToKeyModifiers(mods));
689 self->_keyboardEvents.push_back(std::move(event));
690 });
691
692 this->getLogger().info()
693 << "Setting GLFW mouse button callback for mouse input events...";
694 glfwSetMouseButtonCallback(
695 glfwPlatform._window,
696 [](GLFWwindow *window, int button, int action, int mods) {
697 auto *self = static_cast<evan::IDesktopPlatform *>(
698 glfwGetWindowUserPointer(window));
699
700 (void)window;
701 (void)mods;
702 auto event = std::make_shared<utility::event::MouseButtonEvent>();
703 event->setButton(self->convertGlfwMouseButtonToButton(button));
704 event->setPressed(action == GLFW_PRESS);
705 event->setPosition(self->getMousePosition());
706 self->_mouseButtonEvents.push_back(std::move(event));
707 });
708
709 this->getLogger().info()
710 << "Setting GLFW cursor position callback for mouse motion events...";
711 glfwSetCursorPosCallback(
712 glfwPlatform._window, [](GLFWwindow *window, double xpos, double ypos) {
713 auto *self = static_cast<evan::IDesktopPlatform *>(
714 glfwGetWindowUserPointer(window));
715
716 auto event = std::make_shared<utility::event::MouseMotionEvent>();
717 event->setPosition(self->convertCursorPosition(xpos, ypos));
718 self->_mouseMotionEvents.push_back(std::move(event));
719 });
720
721 this->getLogger().info()
722 << "Setting GLFW scroll callback for mouse wheel events...";
723 glfwSetScrollCallback(
724 glfwPlatform._window,
725 [](GLFWwindow *window, double xoffset, double yoffset) {
726 auto *self = static_cast<evan::IDesktopPlatform *>(
727 glfwGetWindowUserPointer(window));
728
729 auto event = std::make_shared<utility::event::MouseWheelEvent>();
730 event->setOffset(utility::math::Vector2F {
731 static_cast<float>(xoffset), static_cast<float>(yoffset) });
732 self->_mouseWheelEvents.push_back(std::move(event));
733 });
734
735 this->getLogger().info() << "Setting GLFW cursor enter callback for cursor "
736 "enter/leave events...";
737 glfwSetCursorEnterCallback(
738 glfwPlatform._window, [](GLFWwindow *window, int entered) {
739 auto *self = static_cast<evan::IDesktopPlatform *>(
740 glfwGetWindowUserPointer(window));
741
742 auto event = std::make_shared<utility::event::CursorEnterEvent>();
743 event->setEntered(entered == GLFW_TRUE);
744 self->_cursorEnterEvents.push_back(std::move(event));
745 });
746
747 this->getLogger().info()
748 << "Setting GLFW drop callback for file drop events...";
749 glfwSetDropCallback(
750 glfwPlatform._window,
751 [](GLFWwindow *window, int count, const char **paths) {
752 auto *self = static_cast<evan::IDesktopPlatform *>(
753 glfwGetWindowUserPointer(window));
754
755 auto event = std::make_shared<utility::event::FileDropEvent>();
756 std::vector<std::string> pathVector;
757 pathVector.reserve(count);
758 for (int i = 0; i < count; i++) {
759 pathVector.emplace_back(paths[i]);
760 }
761 event->setPaths(pathVector);
762 self->_fileDropEvents.push_back(std::move(event));
763 });
764
765 this->getLogger().info()
766 << "Setting GLFW character callback for text input events...";
767 glfwSetCharCallback(
768 glfwPlatform._window, [](GLFWwindow *window, unsigned int codepoint) {
769 auto *self = static_cast<evan::IDesktopPlatform *>(
770 glfwGetWindowUserPointer(window));
771
772 auto event = std::make_shared<utility::event::TextInputEvent>();
773 // Convert UTF-32 code point to UTF-8 string
774 std::string utf8Text;
775 if (codepoint <= 0x7F) {
776 // ASCII range
777 utf8Text = static_cast<char>(codepoint);
778 } else if (codepoint <= 0x7FF) {
779 // 2-byte UTF-8
780 utf8Text += static_cast<char>(0xC0 | (codepoint >> 6));
781 utf8Text += static_cast<char>(0x80 | (codepoint & 0x3F));
782 } else if (codepoint <= 0xFFFF) {
783 // 3-byte UTF-8
784 utf8Text += static_cast<char>(0xE0 | (codepoint >> 12));
785 utf8Text += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
786 utf8Text += static_cast<char>(0x80 | (codepoint & 0x3F));
787 } else if (codepoint <= 0x10FFFF) {
788 // 4-byte UTF-8
789 utf8Text += static_cast<char>(0xF0 | (codepoint >> 18));
790 utf8Text +=
791 static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F));
792 utf8Text += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
793 utf8Text += static_cast<char>(0x80 | (codepoint & 0x3F));
794 }
795 event->setText(utf8Text);
796 self->_textInputEvents.push_back(std::move(event));
797 });
798}
ASwapchainImage is a class that represents an image in the swapchain of the Evan engine....
DesktopBackend(const IPlatform &platform)
Constructor for DesktopBackend.
evan::SwapChainSupportDetails querySwapChainSupport()
Queries the swap chain support details for a given Vulkan physical device and surface....
uint32_t countSwapchainFormats() const override
Counts the number of supported swapchain formats for the Vulkan surface.
void createPresentQueue()
Creates the presentation queue for the device context.
~DesktopBackend()
Destructor for DesktopBackend.
QueueFamilyIndices findQueueFamilies() override
Finds the queue family indices for the Vulkan physical device.
std::vector< int64_t > enumerateSwapchainFormats(uint32_t swapchainFormatCount) const override
Enumerates the supported swapchain formats for the Vulkan surface.
void createLogicalDevice() override
Creates a Vulkan logical device and retrieves the necessary queues for the Desktop platform.
Error processFrame(VkPresentInfoKHR presentInfo, ASwapchainImage &swapchainImage) override
Processes a frame for rendering.
void createInstance(const IPlatform &platform, const std::string &appName, Version &appVersion) override
Creates a Vulkan instance and initializes the device backend for the Desktop platform.
void pickPhysicalDevice() override
Picks a suitable Vulkan physical device for the Desktop platform.
Desktop platform implementation.
Abstract interface for platform-specific functionality.
Definition IPlatform.hpp:36
Represents a software version using major, minor, and patch numbers.
Definition Version.hpp:35
A structure to represent Vulkan queue family indices.
bool isComplete() const
isComplete
Small result wrapper used by helpers that must return both a status and a value (e....
Definition Error.hpp:89
Struct to encapsulate details about swap chain support.