diff --git a/.gitignore b/.gitignore index 276f347..a16fda7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ build_*/ .DS_Store /deps/ .directory +.cache +CMakeFiles diff --git a/CMakeLists.txt b/CMakeLists.txt index 29b8a35..24e4cba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,10 @@ if (PORT MATCHES "UltralightLinux") option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF) option(GLFW_INSTALL "Generate installation target" OFF) + option(GLFW_VULKAN_STATIC "Use the Vulkan loader statically linked into application" ON) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGLFW_VULKAN_STATIC=1") + set(GLFW_DIR "src/glfw") add_subdirectory(${GLFW_DIR}) @@ -74,7 +78,7 @@ if (PORT MATCHES "UltralightLinux") include_directories("${GLFW_DIR}/include") include_directories("${GLFW_DIR}/deps") - link_libraries(glfw fontconfig) + link_libraries(glfw fontconfig vulkan) elseif (PORT MATCHES "UltralightMac") link_directories("${ULTRALIGHTCORE_DIR}/bin" "${WEBCORE_DIR}/bin" diff --git a/src/linux/AppGLFW.cpp b/src/linux/AppGLFW.cpp index a5ea9df..4a5eac2 100644 --- a/src/linux/AppGLFW.cpp +++ b/src/linux/AppGLFW.cpp @@ -1,4 +1,6 @@ #include "AppGLFW.h" +#include "vulkan/GPUContextVK.h" +#include "vulkan/GPUDriverVK.h" #include "gl/GPUContextGL.h" #include "gl/GPUDriverGL.h" #include @@ -100,8 +102,11 @@ AppGLFW::AppGLFW(Settings settings, Config config) : settings_(settings) { clipboard_.reset(new ClipboardGLFW()); Platform::instance().set_clipboard(clipboard_.get()); - - gpu_context_.reset(new GPUContextGL(true, false)); + if (glfwVulkanSupported()){ + gpu_context_.reset((GPUContextGL*)new GPUContextVK(true, false)); + }else { + gpu_context_.reset(new GPUContextGL(true, false)); + } Platform::instance().set_gpu_driver(gpu_context_->driver()); // We use the GPUContext's global offscreen window to maintain diff --git a/src/linux/AppGLFW.h b/src/linux/AppGLFW.h index fb3cdac..a5079f2 100644 --- a/src/linux/AppGLFW.h +++ b/src/linux/AppGLFW.h @@ -1,6 +1,7 @@ #pragma once #include #include "gl/GPUContextGL.h" +#include "vulkan/GPUContextVK.h" #include #include "RefCountedImpl.h" #include "MonitorGLFW.h" diff --git a/src/linux/WindowGLFW.cpp b/src/linux/WindowGLFW.cpp index 0d97a75..305d6d0 100644 --- a/src/linux/WindowGLFW.cpp +++ b/src/linux/WindowGLFW.cpp @@ -149,7 +149,9 @@ WindowGLFW::WindowGLFW(Monitor* monitor, uint32_t width, uint32_t height, glfwTerminate(); exit(EXIT_FAILURE); } - + if (glfwVulkanSupported()){ + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // do not create GL context + } // This window will share the GL context of GPUContextGL's offscreen window GLFWwindow* win = glfwCreateWindow(ScreenToPixels(width), ScreenToPixels(height), "", NULL, gpu_context->window()); @@ -161,6 +163,16 @@ WindowGLFW::WindowGLFW(Monitor* monitor, uint32_t width, uint32_t height, exit(EXIT_FAILURE); } + if (glfwVulkanSupported()){ + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkResult ret = glfwCreateWindowSurface(((GPUContextVK*)gpu_context)->instance, window_, nullptr, &surface); + if(ret != VK_SUCCESS) { + glfwTerminate(); + exit(EXIT_FAILURE); + } + this->surface = surface; + } + glfwSetWindowUserPointer(window_, this); // Bind callbacks @@ -201,6 +213,11 @@ WindowGLFW::~WindowGLFW() { glfwDestroyCursor(cursor_hresize_); glfwDestroyCursor(cursor_vresize_); + if(glfwVulkanSupported()){ + GPUContextVK* gpu_context = (GPUContextVK*)static_cast(App::instance())->gpu_context(); + vkDestroySurfaceKHR(gpu_context->instance, this->surface, nullptr); + } + glfwDestroyWindow(window_); static_cast(App::instance())->RemoveWindow(this); } diff --git a/src/linux/WindowGLFW.h b/src/linux/WindowGLFW.h index eaba826..f6268de 100644 --- a/src/linux/WindowGLFW.h +++ b/src/linux/WindowGLFW.h @@ -4,6 +4,7 @@ #include "RefCountedImpl.h" #include "OverlayManager.h" #include +#include typedef struct GLFWwindow GLFWwindow; typedef struct GLFWcursor GLFWcursor; @@ -100,6 +101,9 @@ class WindowGLFW : public Window, GLFWcursor* cursor_hresize_; GLFWcursor* cursor_vresize_; bool window_needs_repaint_ = false; + + // Vulkan + VkSurfaceKHR surface = VK_NULL_HANDLE; }; } // namespace ultralight diff --git a/src/linux/vulkan/GPUContextVK.cpp b/src/linux/vulkan/GPUContextVK.cpp new file mode 100644 index 0000000..eb5632c --- /dev/null +++ b/src/linux/vulkan/GPUContextVK.cpp @@ -0,0 +1,96 @@ +#include "GPUContextVK.h" +#include "GPUDriverVK.h" +#include +#include +#include + +namespace ultralight { + +GPUContextVK::GPUContextVK(bool enable_vsync, bool enable_msaa) : msaa_enabled_(enable_msaa) { + + std::vector instance_layers; + std::vector instance_extensions; + std::vector device_extensions; + + // SUPINE for debug + instance_layers.push_back("VK_LAYER_LUNARG_standard_validation"); + instance_extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + + device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // make sure we can display anything + + uint32_t instance_extension_count = 0; + const char** instance_extensions_buffer = glfwGetRequiredInstanceExtensions(&instance_extension_count); + for (uint32_t i = 0; i < instance_extension_count; ++i) { + instance_extensions.push_back(instance_extensions_buffer[i]); + } + + VkApplicationInfo application_info {}; + application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + application_info.apiVersion = VK_MAKE_VERSION(1, 0, 2); + application_info.applicationVersion = VK_MAKE_VERSION(0, 0, 1); + application_info.engineVersion = VK_MAKE_VERSION(0, 0, 1); + application_info.pApplicationName = "GLFW with Vulkan"; + application_info.pEngineName = "GLFW with Vulkan"; + + VkInstanceCreateInfo instance_create_info {}; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pApplicationInfo = &application_info; + instance_create_info.enabledLayerCount = instance_layers.size(); + instance_create_info.ppEnabledLayerNames = instance_layers.data(); + instance_create_info.enabledExtensionCount = instance_extensions.size(); + instance_create_info.ppEnabledExtensionNames = instance_extensions.data(); + + VkInstance instance = VK_NULL_HANDLE; + vkCreateInstance(&instance_create_info, nullptr, &instance); + this->instance = instance; + + // Get GPUs + uint32_t GPU_count; + vkEnumeratePhysicalDevices(instance, &GPU_count, nullptr); + std::vector GPUs(GPU_count); + vkEnumeratePhysicalDevices(instance, &GPU_count, GPUs.data()); + + uint32_t queue_family_count; + vkGetPhysicalDeviceQueueFamilyProperties(GPUs[0], &queue_family_count, nullptr); + std::vector family_properties(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(GPUs[0], &queue_family_count, family_properties.data()); + + uint32_t graphics_queue_family = UINT32_MAX; + for (uint32_t i = 0; i < queue_family_count; ++i) { + if (family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + graphics_queue_family = i; + } + } + if (graphics_queue_family == UINT32_MAX) { + // queue family not found + glfwTerminate(); + } + // graphics_queue_family now contains queue family ID which supports graphics + + // Create Vulkan device + const float priorities[] { 1.0f }; + VkDeviceQueueCreateInfo queue_create_info {}; + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.queueCount = 1; + queue_create_info.queueFamilyIndex = graphics_queue_family; + queue_create_info.pQueuePriorities = priorities; + + VkDeviceCreateInfo device_create_info {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.queueCreateInfoCount = 1; + device_create_info.pQueueCreateInfos = &queue_create_info; + device_create_info.enabledLayerCount = device_extensions.size(); + device_create_info.ppEnabledExtensionNames = device_extensions.data(); + + VkDevice device = VK_NULL_HANDLE; + vkCreateDevice(GPUs[0], &device_create_info, nullptr, &device); + + this->device = device; +} + +GPUContextVK::~GPUContextVK(){ + vkDestroyDevice( device, nullptr ); + vkDestroyInstance( instance, nullptr ); +} + +} // namespace ultralight \ No newline at end of file diff --git a/src/linux/vulkan/GPUContextVK.h b/src/linux/vulkan/GPUContextVK.h new file mode 100644 index 0000000..15ae44d --- /dev/null +++ b/src/linux/vulkan/GPUContextVK.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include "GPUDriverImpl.h" +#include +#include + +typedef struct GLFWwindow GLFWwindow; + +namespace ultralight { + +class GPUContextVK { +protected: + std::unique_ptr driver_; + GLFWwindow* window_; + GLFWwindow* active_window_ = nullptr; + bool msaa_enabled_; +public: + GPUContextVK(bool enable_vsync, bool enable_msaa); + + virtual ~GPUContextVK(); + + virtual ultralight::GPUDriverImpl* driver() const { return driver_.get(); } + + virtual ultralight::FaceWinding face_winding() const { return ultralight::kFaceWinding_CounterClockwise; } + + virtual void BeginDrawing() {} + + virtual void EndDrawing() {} + + virtual bool msaa_enabled() const { return msaa_enabled_; } + + // An offscreen window dedicated to maintaining the OpenGL context. + // All other windows created during lifetime of the app share this context. + virtual GLFWwindow* window() { return window_; } + + // FBOs are not shared across contexts in OpenGL 3.2 (AFAIK), we luckily + // don't need to share them across multiple windows anyways so we temporarily + // set the active GL context to the "active window" when creating FBOs. + virtual void set_active_window(GLFWwindow* win) { active_window_ = win; } + + virtual GLFWwindow* active_window() { return active_window_; } + + // Vulkan + VkInstance instance; + VkDevice device; +}; + +} // namespace ultralight \ No newline at end of file diff --git a/src/linux/vulkan/GPUDriverVK.cpp b/src/linux/vulkan/GPUDriverVK.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/linux/vulkan/GPUDriverVK.h b/src/linux/vulkan/GPUDriverVK.h new file mode 100644 index 0000000..e69de29