8#include "evan/glfw/DesktopBackend.hpp"
9#include "evan/CheckedCast.hpp"
38static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallback(
39 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
40 VkDebugUtilsMessageTypeFlagsEXT messageType,
41 const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData)
43 static std::unique_ptr<utility::logging::Logger> logger =
nullptr;
44 std::string_view message;
46 logger = std::make_unique<utility::logging::DefaultLogger>(
50 switch (messageType) {
51 case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
54 case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
55 message =
"Validation";
57 case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
58 message =
"Performance";
65 switch (messageSeverity) {
66 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
68 <<
"[" << message <<
"] " << pCallbackData->pMessage;
70 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
72 <<
"[" << message <<
"] " << pCallbackData->pMessage;
74 case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
75 logger->info() <<
"[" << message <<
"] " << pCallbackData->pMessage;
79 <<
"[" << message <<
"] " << pCallbackData->pMessage;
88 this->
getLogger().info() <<
"Initializing DesktopBackend...";
93 <<
"Setting up GLFW platform for DesktopBackend...";
99 this->
getLogger().info() <<
"Creating Vulkan surface for DesktopBackend...";
112 <<
"Destroying DesktopBackend and cleaning up Vulkan resources...";
115 this->
getLogger().info() <<
"Destroying Vulkan surface...";
118 this->
getLogger().info() <<
"Destroying Vulkan instance...";
129 this->
getLogger().info() <<
"Processing frame for DesktopBackend...";
133 this->
getLogger().info() <<
"Swap chain is out of date or suboptimal.";
134 return mapVkResult(
result);
136 this->
getLogger().error() <<
"Failed to present swap chain image!";
137 return mapVkResult(
result);
139 this->
getLogger().info() <<
"Frame presented successfully.";
146 <<
"Counting available swap chain formats for DesktopBackend...";
153 <<
"Found " <<
formatCount <<
" swap chain formats available.";
161 <<
"Enumerating swap chain formats for DesktopBackend...";
174 <<
" swap chain formats successfully.";
180 this->
getLogger().info() <<
"Finding queue families for DesktopBackend...";
196 <<
"Checking queue families for graphics and presentation support...";
198 this->
getLogger().info() <<
"Checking queue family " <<
i <<
" with "
207 <<
"Checking if queue family " <<
i <<
" supports presentation...";
213 <<
"Queue family " <<
i <<
" supports presentation.";
218 this->
getLogger().info() <<
"Found suitable queue families for "
219 "graphics and presentation support.";
225 <<
"Queue family indices found: Graphics Family = "
226 << (
indices.graphicsFamily.has_value()
227 ? std::to_string(
indices.graphicsFamily.value())
229 <<
", Present Family = "
230 << (
indices.presentFamily.has_value()
231 ? std::to_string(
indices.presentFamily.value())
239 <<
"Querying swap chain support details for DesktopBackend...";
245 this->
getLogger().info() <<
"Querying surface capabilities...";
251 this->
getLogger().error() <<
"Failed to query surface formats!";
253 <<
"Returning swap chain support details with capabilities but no "
254 "formats or present modes.";
259 <<
"Found " <<
formatCount <<
" surface formats available.";
262 this->
getLogger().info() <<
"Querying surface formats...";
267 this->
getLogger().info() <<
"Querying surface present modes...";
271 this->
getLogger().error() <<
"Failed to query surface present modes!";
273 <<
"Returning swap chain support details with capabilities and "
274 "formats but no present modes.";
279 <<
" surface present modes available.";
281 this->
getLogger().info() <<
"Querying surface present modes...";
287 <<
"Swap chain support details queried successfully.";
293 this->
getLogger().info() <<
"Creating present queue for DesktopBackend...";
297 if (!
indices.presentFamily.has_value()) {
298 throw std::runtime_error(
299 "Failed to find a present queue family for the logical device");
303 <<
"Retrieving present queue from logical device...";
317 <<
"Creating Vulkan instance for DesktopBackend...";
322 <<
"Validation layers requested, but not available. Continuing "
336 <<
"Setting up instance creation information using application info:\n"
337 "Application Name = "
340 <<
",\nEngine Name = Evan,\nEngine Version = 0.1.0,\nAPI Version = 1.1";
344 createInfo.pApplicationInfo = &
appInfo;
349 <<
"Required instance extensions for platform: " <<
extensions.size()
350 <<
" extensions found.";
359 createInfo.enabledExtensionCount =
363 createInfo.flags |=
platform.getInstanceCreateFlags();
368 createInfo.enabledLayerCount =
369 static_cast<uint32_t>(validationLayers.size());
370 createInfo.ppEnabledLayerNames = validationLayers.data();
372 <<
"Validation layers enabled for instance creation. Setting up "
373 "debug messenger create info...";
376 defaultDebugCallback);
380 createInfo.enabledLayerCount = 0;
381 createInfo.ppEnabledLayerNames =
nullptr;
385 <<
"Creating Vulkan instance with the specified application info, "
386 "extensions, and validation layers...";
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...";
405 throw std::runtime_error(
"Failed to create Vulkan instance! VkResult: "
406 + std::to_string(
result));
408 this->
getLogger().info() <<
"Vulkan instance created successfully!";
413 this->
getLogger().info() <<
"Creating logical device for DesktopBackend...";
423 <<
"Required device extensions for logical device creation: "
434 throw std::runtime_error(
435 "Failed to find suitable queue families for logical device "
440 <<
"Setting up queue create infos for logical device creation...";
444 indices.presentFamily.value() };
450 <<
" unique queue families found.";
453 <<
"Setting up queue create info for queue family index "
467 <<
"Querying supported features of the physical device...";
489 _multiviewSupported =
true;
491 <<
"Enabling the multiview feature on the Vulkan device.";
492 }
else if (multiviewMode() == MultiviewMode::On) {
494 <<
"EVAN_MULTIVIEW=on but the device does not support the "
495 "multiview feature. Continuing without multiview.";
501 createInfo.queueCreateInfoCount =
508 createInfo.enabledExtensionCount =
513 <<
"Device layers are deprecated; skipping validation layer setup for "
514 "logical device creation.";
515 createInfo.enabledLayerCount = 0;
516 createInfo.ppEnabledLayerNames =
nullptr;
519 <<
"Creating logical device with the specified queue create infos, "
520 "enabled features, extensions, and validation layers...";
524 throw std::runtime_error(
"Failed to create logical device! VkResult: "
525 + std::to_string(
result));
527 this->
getLogger().info() <<
"Logical device created successfully!";
532 this->
getLogger().info() <<
"Picking physical device for DesktopBackend...";
536 this->
getLogger().info() <<
"Enumerating physical devices...";
540 throw std::runtime_error(
"Failed to find GPUs with Vulkan support!");
545 <<
" physical devices with Vulkan support. Evaluating suitability...";
551 <<
"Checking each physical device for suitability...";
556 this->
getLogger().info() <<
"Evaluating physical device: "
559 if (this->isDeviceSuitable(
device, deviceExtensions)) {
560 this->
getLogger().info() <<
"Physical device "
562 <<
" is suitable. Selecting this device.";
569 throw std::runtime_error(
"Failed to find a suitable GPU!");
571 this->
getLogger().info() <<
"Physical device selected successfully.";
578bool evan::DesktopBackend::isDeviceSuitable(
585 this->checkDeviceExtensionSupport(
device, deviceExtensions);
590 this->querySwapChainSupport();
594 VkPhysicalDeviceFeatures supportedFeatures;
595 vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
597 _physicalDevice = VK_NULL_HANDLE;
599 return indices.
isComplete() && extensionsSupported && swapChainAdequate
600 && supportedFeatures.samplerAnisotropy;
603bool evan::DesktopBackend::checkDeviceExtensionSupport(
604 VkPhysicalDevice device, std::vector<const char *> deviceExtensions)
606 uint32_t extensionCount;
607 if (vkEnumerateDeviceExtensionProperties(device,
nullptr, &extensionCount,
612 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
613 vkEnumerateDeviceExtensionProperties(device,
nullptr, &extensionCount,
614 availableExtensions.data());
616 std::set<std::string> requiredExtensions(deviceExtensions.begin(),
617 deviceExtensions.end());
619 for (
const auto &extension: availableExtensions) {
620 requiredExtensions.erase(extension.extensionName);
623 return requiredExtensions.empty();
626bool evan::DesktopBackend::checkValidationLayerSupport()
628 auto availableLayers = this->getAvailableLayers();
630 for (
const char *layerName: validationLayers) {
631 bool layerFound =
false;
633 for (
const auto &layerProperties: availableLayers) {
634 if (strcmp(layerName, layerProperties.layerName) == 0) {
647void evan::DesktopBackend::populateDebugMessengerCreateInfo(
648 VkDebugUtilsMessengerCreateInfoEXT &createInfo,
649 debugCallback_t debugCallback)
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;
662void evan::DesktopBackend::setupCallbackEvent(
const IPlatform &platform)
664 this->getLogger().info()
665 <<
"Setting up GLFW input callbacks for DesktopBackend...";
667 const IDesktopPlatform &glfwPlatform =
670 this->getLogger().info() <<
"Setting GLFW window user pointer to the "
671 "platform instance for callback access...";
672 glfwSetWindowUserPointer(glfwPlatform._window, (
void *)&glfwPlatform);
674 this->getLogger().info()
675 <<
"Setting GLFW key callback for keyboard input events...";
677 glfwPlatform._window,
678 [](GLFWwindow *window,
int key,
int scancode,
int action,
int mods) {
679 auto *self = static_cast<evan::IDesktopPlatform *>(
680 glfwGetWindowUserPointer(window));
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));
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));
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));
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));
716 auto event = std::make_shared<utility::event::MouseMotionEvent>();
717 event->setPosition(self->convertCursorPosition(xpos, ypos));
718 self->_mouseMotionEvents.push_back(std::move(event));
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));
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));
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));
742 auto event = std::make_shared<utility::event::CursorEnterEvent>();
743 event->setEntered(entered == GLFW_TRUE);
744 self->_cursorEnterEvents.push_back(std::move(event));
747 this->getLogger().info()
748 <<
"Setting GLFW drop callback for file drop events...";
750 glfwPlatform._window,
751 [](GLFWwindow *window,
int count,
const char **paths) {
752 auto *self = static_cast<evan::IDesktopPlatform *>(
753 glfwGetWindowUserPointer(window));
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]);
761 event->setPaths(pathVector);
762 self->_fileDropEvents.push_back(std::move(event));
765 this->getLogger().info()
766 <<
"Setting GLFW character callback for text input events...";
768 glfwPlatform._window, [](GLFWwindow *window,
unsigned int codepoint) {
769 auto *self = static_cast<evan::IDesktopPlatform *>(
770 glfwGetWindowUserPointer(window));
772 auto event = std::make_shared<utility::event::TextInputEvent>();
774 std::string utf8Text;
775 if (codepoint <= 0x7F) {
777 utf8Text = static_cast<char>(codepoint);
778 }
else if (codepoint <= 0x7FF) {
780 utf8Text +=
static_cast<char>(0xC0 | (codepoint >> 6));
781 utf8Text +=
static_cast<char>(0x80 | (codepoint & 0x3F));
782 }
else if (codepoint <= 0xFFFF) {
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) {
789 utf8Text +=
static_cast<char>(0xF0 | (codepoint >> 18));
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));
795 event->setText(utf8Text);
796 self->_textInputEvents.push_back(std::move(event));
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.
Represents a software version using major, minor, and patch numbers.
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....
Struct to encapsulate details about swap chain support.