diff options
author | Peter Varga <pvarga@inf.u-szeged.hu> | 2022-10-27 14:33:16 +0200 |
---|---|---|
committer | Peter Varga <pvarga@inf.u-szeged.hu> | 2022-11-10 09:42:16 +0100 |
commit | b32f5e5b495ef73f25d3156d24a83fffd33f02be (patch) | |
tree | 5651504ad55f3609163c85879e0ac6d726c6db27 | |
parent | 6fb80f47941b555f1a9455d3bd3d189b90092b60 (diff) |
Add Vulkan rendering support
Updates 3rdparty:
* 8b7ce4ef70d Make GrVkImage external
Task-number: QTBUG-107669
Change-Id: If7fbe1f20538598dd1d4f3a67be17c9f7d06a3cd
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
m--------- | src/3rdparty | 0 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/core/api/configure.cmake | 10 | ||||
-rw-r--r-- | src/core/api/qtwebenginecoreglobal.cpp | 33 | ||||
-rw-r--r-- | src/core/chromium_overrides.cpp | 43 | ||||
-rw-r--r-- | src/core/compositor/compositor.cpp | 20 | ||||
-rw-r--r-- | src/core/compositor/compositor.h | 18 | ||||
-rw-r--r-- | src/core/compositor/display_skia_output_device.cpp | 278 | ||||
-rw-r--r-- | src/core/compositor/display_skia_output_device.h | 14 | ||||
-rw-r--r-- | src/core/compositor/vulkan_implementation_qt.cpp | 153 | ||||
-rw-r--r-- | src/core/compositor/vulkan_implementation_qt.h | 47 | ||||
-rw-r--r-- | src/core/ozone/ozone_platform_qt.cpp | 17 | ||||
-rw-r--r-- | src/core/ozone/surface_factory_qt.cpp | 19 | ||||
-rw-r--r-- | src/core/ozone/surface_factory_qt.h | 4 | ||||
-rw-r--r-- | src/core/render_widget_host_view_qt_delegate_item.cpp | 32 | ||||
-rw-r--r-- | src/core/render_widget_host_view_qt_delegate_item.h | 1 | ||||
-rw-r--r-- | src/core/web_engine_context.cpp | 13 | ||||
-rw-r--r-- | src/webenginequick/api/qtwebenginequickglobal.cpp | 5 |
18 files changed, 681 insertions, 31 deletions
diff --git a/src/3rdparty b/src/3rdparty -Subproject 9457651ead9b7034edb37532c2f33a558b1700b +Subproject 8b7ce4ef70d2e3588f3ae4143356b77edd5caae diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c0a2441d3..f50b48807 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -204,6 +204,11 @@ foreach(arch ${archs}) ozone/gl_surface_glx_qt.cpp ozone/gl_surface_glx_qt.h ) + extend_gn_target(${buildGn} CONDITION QT_FEATURE_webengine_vulkan + SOURCES + compositor/vulkan_implementation_qt.cpp compositor/vulkan_implementation_qt.h + ) + extend_gn_target(${buildGn} CONDITION QT_FEATURE_opengl SOURCES compositor/compositor_resource_fence.cpp compositor/compositor_resource_fence.h diff --git a/src/core/api/configure.cmake b/src/core/api/configure.cmake index 4e54ad2a9..333039a42 100644 --- a/src/core/api/configure.cmake +++ b/src/core/api/configure.cmake @@ -150,6 +150,12 @@ qt_feature("webengine-sanitizer" PRIVATE AUTODETECT CLANG CONDITION CLANG AND ECM_ENABLE_SANITIZERS ) +qt_feature("webengine-vulkan" PRIVATE + SECTION "WebEngine" + LABEL "Vulkan support" + PURPOSE "Enables support for Vulkan rendering" + CONDITION QT_FEATURE_vulkan +) # internal testing feature qt_feature("webengine-system-poppler" PRIVATE LABEL "popler" @@ -178,6 +184,10 @@ qt_configure_add_summary_entry( CONDITION UNIX ) qt_configure_add_summary_entry( + ARGS "webengine-vulkan" + CONDITION QT_FEATURE_vulkan +) +qt_configure_add_summary_entry( ARGS "webengine-v8-snapshot-support" CONDITION UNIX AND cross_compile ) diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp index 3c2eefdf5..1dded1086 100644 --- a/src/core/api/qtwebenginecoreglobal.cpp +++ b/src/core/api/qtwebenginecoreglobal.cpp @@ -195,22 +195,25 @@ sandbox::SandboxInterfaceInfo *staticSandboxInterfaceInfo(sandbox::SandboxInterf static void initialize() { #if QT_CONFIG(opengl) - if (QCoreApplication::instance()) { - // On window/ANGLE, calling QtWebEngineQuick::initialize from DllMain will result in a crash. - if (!qt_gl_global_share_context() && - !(QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts) && - QQuickWindow::graphicsApi() == QSGRendererInterface::OpenGLRhi) - ) { - qWarning("Qt WebEngine seems to be initialized from a plugin. Please " - "set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute and " - "QSGRendererInterface::OpenGLRhi using QQuickWindow::setGraphicsApi " - "before constructing QGuiApplication."); + if (QCoreApplication::instance()) { + // On Windows/ANGLE, calling QtWebEngineQuick::initialize from DllMain will result in a + // crash. + if (!qt_gl_global_share_context() + && !(QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts) + && QQuickWindow::graphicsApi() == QSGRendererInterface::OpenGLRhi)) { + qWarning("Qt WebEngine seems to be initialized from a plugin. Please " + "set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute and " + "QSGRendererInterface::OpenGLRhi using QQuickWindow::setGraphicsApi " + "before constructing QGuiApplication."); + } + return; } - return; - } - // QCoreApplication is not yet instantiated, ensuring the call will be deferred - qAddPreRoutine(QtWebEngineCore::initialize); - QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi); + + // QCoreApplication is not yet instantiated, ensuring the call will be deferred + qAddPreRoutine(QtWebEngineCore::initialize); + auto api = QQuickWindow::graphicsApi(); + if (api != QSGRendererInterface::OpenGLRhi && api != QSGRendererInterface::VulkanRhi) + QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi); #endif // QT_CONFIG(opengl) } diff --git a/src/core/chromium_overrides.cpp b/src/core/chromium_overrides.cpp index 3a183d0d8..7462b4157 100644 --- a/src/core/chromium_overrides.cpp +++ b/src/core/chromium_overrides.cpp @@ -12,6 +12,7 @@ #include "content/common/font_list.h" #include "extensions/buildflags/buildflags.h" #include "extensions/common/constants.h" +#include "gpu/vulkan/buildflags.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_factory.h" @@ -23,6 +24,17 @@ #include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h" #endif +#if BUILDFLAG(ENABLE_VULKAN) +#include "compositor/vulkan_implementation_qt.h" + +#include "gpu/vulkan/init/vulkan_factory.h" + +#if defined(USE_OZONE) +#include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/surface_factory_ozone.h" +#endif // defined(USE_OZONE) +#endif // defined(ENABLE_VULKAN) + void *GetQtXDisplay() { return GLContextHelper::getXDisplay(); @@ -93,6 +105,37 @@ ActivationClient *GetActivationClient(aura::Window *) } // namespace wm #endif // defined(USE_AURA) || defined(USE_OZONE) +#if BUILDFLAG(ENABLE_VULKAN) +namespace gpu { +std::unique_ptr<VulkanImplementation> CreateVulkanImplementation(bool use_swiftshader, + bool allow_protected_memory) +{ +#if BUILDFLAG(IS_APPLE) + // TODO: Investigate if we can support MoltenVK. + NOTIMPLEMENTED(); + return nullptr; +#else +#if defined(USE_OZONE) + return ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone()->CreateVulkanImplementation( + use_swiftshader, allow_protected_memory); +#endif + +#if !BUILDFLAG(IS_WIN) + // TODO(samans): Support Swiftshader on more platforms. + // https://crbug.com/963988 + DCHECK(!use_swiftshader) << "Vulkan Swiftshader is not supported on this platform."; +#endif // !BUILDFLAG(IS_WIN) + + // Protected memory is supported only on Fuchsia, which uses Ozone, i.e. + // VulkanImplementation is initialized above. + DCHECK(!allow_protected_memory) << "Protected memory is not supported on this platform."; + + return std::make_unique<VulkanImplementationQt>(); +#endif // BUILDFLAG(IS_APPLE) +} +} // namespace gpu +#endif // BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr<ui::OSExchangeDataProvider> ui::OSExchangeDataProviderFactory::CreateProvider() { return nullptr; diff --git a/src/core/compositor/compositor.cpp b/src/core/compositor/compositor.cpp index ada039afc..fed395cb1 100644 --- a/src/core/compositor/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -9,6 +9,7 @@ #include <QHash> #include <QImage> #include <QMutex> +#include <QQuickWindow> namespace QtWebEngineCore { @@ -153,6 +154,25 @@ int Compositor::textureId() return 0; } +#if QT_CONFIG(webengine_vulkan) +VkImage Compositor::vkImage(QQuickWindow *) +{ + Q_UNREACHABLE(); + return {}; +} + +VkImageLayout Compositor::vkImageLayout() +{ + Q_UNREACHABLE(); + return {}; +} + +void Compositor::releaseVulkanResources(QQuickWindow *) +{ + Q_UNREACHABLE(); +} +#endif + // static void Compositor::unlockBindings() { diff --git a/src/core/compositor/compositor.h b/src/core/compositor/compositor.h index 9cadab4d4..7c6590134 100644 --- a/src/core/compositor/compositor.h +++ b/src/core/compositor/compositor.h @@ -4,10 +4,16 @@ #ifndef COMPOSITOR_H #define COMPOSITOR_H +#include <QtGui/qtguiglobal.h> #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#if QT_CONFIG(webengine_vulkan) +#include <QVulkanInstance> +#endif + QT_BEGIN_NAMESPACE class QImage; +class QQuickWindow; class QSize; QT_END_NAMESPACE @@ -31,6 +37,7 @@ public: enum class Type { Software, OpenGL, + Vulkan, }; // Identifies a compositor. @@ -139,6 +146,17 @@ public: // (OpenGL) Texture of the frame. virtual int textureId(); +#if QT_CONFIG(webengine_vulkan) + // (Vulkan) VkImage of the frame. + virtual VkImage vkImage(QQuickWindow *win); + + // (Vulkan) Layout for vkImage(). + virtual VkImageLayout vkImageLayout(); + + // (Vulkan) Release Vulkan resources created by Qt's Vulkan instance. + virtual void releaseVulkanResources(QQuickWindow *win); +#endif + protected: Compositor(Type type) : m_type(type) { } virtual ~Compositor() { if (m_binding) unbind(); } diff --git a/src/core/compositor/display_skia_output_device.cpp b/src/core/compositor/display_skia_output_device.cpp index ee693ed81..7f42d61de 100644 --- a/src/core/compositor/display_skia_output_device.cpp +++ b/src/core/compositor/display_skia_output_device.cpp @@ -8,6 +8,30 @@ #include "gpu/command_buffer/service/skia_utils.h" #include "third_party/skia/include/core/SkSurfaceProps.h" +#if QT_CONFIG(webengine_vulkan) +#if defined(USE_OZONE) +#include "ui/ozone/buildflags.h" +#if BUILDFLAG(OZONE_PLATFORM_X11) +// We need to define USE_VULKAN_XCB for proper vulkan function pointers. +// Avoiding it may lead to call wrong vulkan functions. +// This is originally defined in chromium/gpu/vulkan/BUILD.gn. +#define USE_VULKAN_XCB +#endif // BUILDFLAG(OZONE_PLATFORM_X11) +#endif // defined(USE_OZONE) +#include "gpu/vulkan/vulkan_function_pointers.h" + +#include "components/viz/common/gpu/vulkan_context_provider.h" +#include "compositor/display_skia_output_device.h" +#include "gpu/vulkan/vma_wrapper.h" +#include "gpu/vulkan/vulkan_device_queue.h" +#include "third_party/vulkan_memory_allocator/include/vk_mem_alloc.h" + +#include <QQuickWindow> +#include <QVulkanInstance> +#include <QVulkanFunctions> +#include <QVulkanDeviceFunctions> +#endif // QT_CONFIG(webengine_vulkan) + namespace QtWebEngineCore { class DisplaySkiaOutputDevice::Buffer @@ -26,11 +50,8 @@ public: DCHECK(m_texture.isValid()); if (m_texture.backend() == GrBackendApi::kVulkan) { -#if BUILDFLAG(ENABLE_VULKAN) - GrVkImageInfo info; - bool result = m_texture.getVkImageInfo(&info); - DCHECK(result); - m_estimatedSize = info.fAlloc.fSize; +#if QT_CONFIG(webengine_vulkan) + initVulkan(); #else NOTREACHED(); #endif @@ -53,6 +74,9 @@ public: ~Buffer() { +#if QT_CONFIG(webengine_vulkan) && defined(Q_OS_WIN) + CloseHandle(m_win32Handle); +#endif DeleteGrBackendTexture(m_parent->m_contextState.get(), &m_texture); m_parent->memory_type_tracker_->TrackMemFree(m_estimatedSize); } @@ -74,6 +98,42 @@ public: const GrBackendTexture &texture() const { return m_texture; } SkSurface *surface() const { return m_surface.get(); } +#if QT_CONFIG(webengine_vulkan) + const VkImageCreateInfo *imageCreateInfo() const { return &m_imageCreateInfo; } + uint64_t allocationSize() const { return m_estimatedSize; } + VkImageLayout imageLayout() const { return m_imageLayout; } + uint32_t memoryTypeIndex() const { return m_memoryTypeIndex.value(); } + +#if defined(Q_OS_WIN) + const VkExternalMemoryHandleTypeFlagBits externalMemoryHandleType() const + { + return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR; + } + + HANDLE externalMemoryHandle() const { return m_win32Handle; } +#else + const VkExternalMemoryHandleTypeFlagBits externalMemoryHandleType() const + { + return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + } + + int externalMemoryHandle() const + { + VkMemoryGetFdInfoKHR exportInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR }; + exportInfo.pNext = nullptr; + exportInfo.memory = m_imageInfo.fAlloc.fMemory; + exportInfo.handleType = externalMemoryHandleType(); + + // Importing Vulkan memory object closes the file descriptor. + int fd = -1; + if (m_vfp->vkGetMemoryFdKHR(m_vulkanDevice, &exportInfo, &fd) != VK_SUCCESS) + qFatal("VULKAN: Unable to extract file descriptor out of external VkImage!"); + + return fd; + } +#endif // defined(Q_OS_WIN) +#endif // QT_CONFIG(webengine_vulkan) + private: DisplaySkiaOutputDevice *m_parent; Shape m_shape; @@ -81,17 +141,125 @@ private: sk_sp<SkSurface> m_surface; uint64_t m_estimatedSize = 0; scoped_refptr<CompositorResourceFence> m_fence; + +#if QT_CONFIG(webengine_vulkan) + static VkSampleCountFlagBits vkSampleCount(uint32_t sampleCount) + { + Q_ASSERT(sampleCount >= 1); + switch (sampleCount) { + case 1: + return VK_SAMPLE_COUNT_1_BIT; + case 2: + return VK_SAMPLE_COUNT_2_BIT; + case 4: + return VK_SAMPLE_COUNT_4_BIT; + case 8: + return VK_SAMPLE_COUNT_8_BIT; + case 16: + return VK_SAMPLE_COUNT_16_BIT; + default: + Q_UNREACHABLE(); + } + + return VK_SAMPLE_COUNT_1_BIT; + } + + void initVulkan() + { + bool success = m_texture.getVkImageInfo(&m_imageInfo); + if (!success) + qFatal("VULKAN: Failed to get external Vulkan resources from Skia!"); + + m_vfp = gpu::GetVulkanFunctionPointers(); + gpu::VulkanDeviceQueue *vulkanDeviceQueue = + m_parent->m_contextState->vk_context_provider()->GetDeviceQueue(); + m_vulkanDevice = vulkanDeviceQueue->GetVulkanDevice(); + + // Store allocation size for the external VkImage. + m_estimatedSize = m_imageInfo.fAlloc.fSize; + + // Store initial layout for the imported image. + if (vulkanDeviceQueue->vk_physical_device_properties().vendorID == 0x10DE) { + // FIXME: This is a workaround for Nvidia driver. + // The imported image is empty if the initialLayout is not PREINITIALIZED. + m_imageLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + } else { + // The initialLayout should be undefined for the external image. + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html#VUID-VkImageCreateInfo-pNext-01443 + m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + // Specify VkCreateImageInfo for the imported VkImage. + // The specification should match with the texture's VkImage. + m_externalMemoryImageCreateInfo.sType = + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; + m_externalMemoryImageCreateInfo.pNext = nullptr; + m_externalMemoryImageCreateInfo.handleTypes = externalMemoryHandleType(); + + m_imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + m_imageCreateInfo.pNext = &m_externalMemoryImageCreateInfo; + m_imageCreateInfo.flags = + m_imageInfo.fProtected == GrProtected::kYes ? VK_IMAGE_CREATE_PROTECTED_BIT : 0; + m_imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + m_imageCreateInfo.format = m_imageInfo.fFormat; + m_imageCreateInfo.extent.width = static_cast<uint32_t>(m_shape.characterization.width()); + m_imageCreateInfo.extent.height = static_cast<uint32_t>(m_shape.characterization.height()); + m_imageCreateInfo.extent.depth = 1; + m_imageCreateInfo.mipLevels = m_imageInfo.fLevelCount; + m_imageCreateInfo.arrayLayers = 1; + m_imageCreateInfo.samples = vkSampleCount(m_imageInfo.fSampleCount); + m_imageCreateInfo.tiling = m_imageInfo.fImageTiling; + m_imageCreateInfo.usage = m_imageInfo.fImageUsageFlags; + m_imageCreateInfo.sharingMode = m_imageInfo.fSharingMode; + m_imageCreateInfo.queueFamilyIndexCount = 0; + m_imageCreateInfo.pQueueFamilyIndices = nullptr; + m_imageCreateInfo.initialLayout = m_imageLayout; + +#if defined(Q_OS_WIN) + // Extract Windows handle for the memory of the external VkImage. + // Ownership should be released at the destruction of the Buffer object. + // Multiple handles for the same memory cause issues on Windows even if one is + // released before extracting the other. + VkMemoryGetWin32HandleInfoKHR exportInfo = { + VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR + }; + exportInfo.pNext = nullptr; + exportInfo.memory = m_imageInfo.fAlloc.fMemory; + exportInfo.handleType = externalMemoryHandleType(); + + if (m_vfp->vkGetMemoryWin32HandleKHR(m_vulkanDevice, &exportInfo, &m_win32Handle) + != VK_SUCCESS) { + qFatal("VULKAN: Unable to extract handle out of external VkImage!"); + } +#endif // defined(Q_OS_WIN) + + VmaAllocator vmaAllocator = vulkanDeviceQueue->vma_allocator(); + const VmaAllocation vmaAllocation = + reinterpret_cast<const VmaAllocation>(m_imageInfo.fAlloc.fBackendMemory); + VmaAllocationInfo vmaInfo; + gpu::vma::GetAllocationInfo(vmaAllocator, vmaAllocation, &vmaInfo); + m_memoryTypeIndex = vmaInfo.memoryType; + } + + gpu::VulkanFunctionPointers *m_vfp = nullptr; + VkDevice m_vulkanDevice = VK_NULL_HANDLE; + VkImageLayout m_imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + GrVkImageInfo m_imageInfo; + VkExternalMemoryImageCreateInfoKHR m_externalMemoryImageCreateInfo = {}; + VkImageCreateInfo m_imageCreateInfo = {}; +#if defined(Q_OS_WIN) + HANDLE m_win32Handle = nullptr; +#endif + absl::optional<uint32_t> m_memoryTypeIndex; +#endif // QT_CONFIG(webengine_vulkan) }; DisplaySkiaOutputDevice::DisplaySkiaOutputDevice( - scoped_refptr<gpu::SharedContextState> contextState, - gpu::MemoryTracker *memoryTracker, + scoped_refptr<gpu::SharedContextState> contextState, gpu::MemoryTracker *memoryTracker, DidSwapBufferCompleteCallback didSwapBufferCompleteCallback) - : SkiaOutputDevice( - contextState->gr_context(), - memoryTracker, - didSwapBufferCompleteCallback) - , Compositor(Compositor::Type::OpenGL) + : SkiaOutputDevice(contextState->gr_context(), memoryTracker, didSwapBufferCompleteCallback) + , Compositor(contextState->GrContextIsVulkan() ? Compositor::Type::Vulkan + : Compositor::Type::OpenGL) , m_contextState(contextState) { capabilities_.uses_default_gl_framebuffer = false; @@ -211,6 +379,92 @@ float DisplaySkiaOutputDevice::devicePixelRatio() return m_frontBuffer ? m_frontBuffer->shape().devicePixelRatio : 1; } +#if QT_CONFIG(webengine_vulkan) +VkImage DisplaySkiaOutputDevice::vkImage(QQuickWindow *win) +{ + if (!m_frontBuffer) + return VK_NULL_HANDLE; + + QSGRendererInterface *ri = win->rendererInterface(); + VkDevice qtVulkanDevice = + *static_cast<VkDevice *>(ri->getResource(win, QSGRendererInterface::DeviceResource)); + QVulkanDeviceFunctions *df = win->vulkanInstance()->deviceFunctions(qtVulkanDevice); + + df->vkDestroyImage(qtVulkanDevice, m_importedImage, nullptr); + df->vkFreeMemory(qtVulkanDevice, m_importedImageMemory, nullptr); + + if (df->vkCreateImage(qtVulkanDevice, m_frontBuffer->imageCreateInfo(), nullptr, + &m_importedImage) + != VK_SUCCESS) { + qFatal("VULKAN: Failed to create imported image!"); + } + + VkMemoryDedicatedAllocateInfoKHR dedicatedAllocateInfo = { + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR + }; + dedicatedAllocateInfo.pNext = nullptr; + dedicatedAllocateInfo.image = m_importedImage; + dedicatedAllocateInfo.buffer = VK_NULL_HANDLE; + +#if defined(Q_OS_WIN) + VkImportMemoryWin32HandleInfoKHR importInfo = { + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR + }; + importInfo.handle = m_frontBuffer->externalMemoryHandle(); +#else + VkImportMemoryFdInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR }; + importInfo.fd = m_frontBuffer->externalMemoryHandle(); +#endif // defined(Q_OS_WIN) + importInfo.pNext = &dedicatedAllocateInfo; + importInfo.handleType = m_frontBuffer->externalMemoryHandleType(); + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.pNext = &importInfo; + allocateInfo.allocationSize = m_frontBuffer->allocationSize(); + allocateInfo.memoryTypeIndex = m_frontBuffer->memoryTypeIndex(); + + if (df->vkAllocateMemory(qtVulkanDevice, &allocateInfo, nullptr, &m_importedImageMemory) + != VK_SUCCESS) { + qFatal("VULKAN: Failed to allocate memory for imported VkImage!"); + } + + df->vkBindImageMemory(qtVulkanDevice, m_importedImage, m_importedImageMemory, 0); + + return m_importedImage; +} + +VkImageLayout DisplaySkiaOutputDevice::vkImageLayout() +{ + if (!m_frontBuffer) + return VK_IMAGE_LAYOUT_UNDEFINED; + + return m_frontBuffer->imageLayout(); +} + +void DisplaySkiaOutputDevice::releaseVulkanResources(QQuickWindow *win) +{ + VkDevice *vkDevicePtr = static_cast<VkDevice *>( + win->rendererInterface()->getResource(win, QSGRendererInterface::DeviceResource)); + + if (!vkDevicePtr) { + Q_ASSERT(m_importedImage == VK_NULL_HANDLE && m_importedImageMemory == VK_NULL_HANDLE); + return; + } + + QVulkanDeviceFunctions *df = win->vulkanInstance()->deviceFunctions(*vkDevicePtr); + + if (m_importedImage != VK_NULL_HANDLE) { + df->vkDestroyImage(*vkDevicePtr, m_importedImage, nullptr); + m_importedImage = VK_NULL_HANDLE; + } + + if (m_importedImageMemory != VK_NULL_HANDLE) { + df->vkFreeMemory(*vkDevicePtr, m_importedImageMemory, nullptr); + m_importedImageMemory = VK_NULL_HANDLE; + } +} +#endif // QT_CONFIG(webengine_vulkan) + void DisplaySkiaOutputDevice::SwapBuffersFinished() { { diff --git a/src/core/compositor/display_skia_output_device.h b/src/core/compositor/display_skia_output_device.h index 943d37214..e2818d604 100644 --- a/src/core/compositor/display_skia_output_device.h +++ b/src/core/compositor/display_skia_output_device.h @@ -13,6 +13,10 @@ #include <QMutex> +QT_BEGIN_NAMESPACE +class QQuickWindow; +QT_END_NAMESPACE + namespace QtWebEngineCore { class DisplaySkiaOutputDevice final : public viz::SkiaOutputDevice, public Compositor @@ -43,6 +47,11 @@ public: QSize size() override; bool hasAlphaChannel() override; float devicePixelRatio() override; +#if QT_CONFIG(webengine_vulkan) + VkImage vkImage(QQuickWindow *win) override; + VkImageLayout vkImageLayout() override; + void releaseVulkanResources(QQuickWindow *win) override; +#endif private: struct Shape @@ -73,6 +82,11 @@ private: viz::OutputSurfaceFrame m_frame; bool m_readyToUpdate = false; scoped_refptr<base::SingleThreadTaskRunner> m_taskRunner; + +#if QT_CONFIG(webengine_vulkan) + VkImage m_importedImage = VK_NULL_HANDLE; + VkDeviceMemory m_importedImageMemory = VK_NULL_HANDLE; +#endif // QT_CONFIG(webengine_vulkan) }; } // namespace QtWebEngineCore diff --git a/src/core/compositor/vulkan_implementation_qt.cpp b/src/core/compositor/vulkan_implementation_qt.cpp new file mode 100644 index 000000000..713e5d7a1 --- /dev/null +++ b/src/core/compositor/vulkan_implementation_qt.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "vulkan_implementation_qt.h" + +#include "base/environment.h" +#include "base/logging.h" +#include "gpu/vulkan/vulkan_image.h" +#include "gpu/vulkan/vulkan_surface.h" +#include "gpu/vulkan/vulkan_util.h" +#include "ui/gfx/gpu_fence.h" + +namespace gpu { + +VulkanImplementationQt::VulkanImplementationQt() : VulkanImplementation(false) { } + +VulkanImplementationQt::~VulkanImplementationQt() = default; + +bool VulkanImplementationQt::InitializeVulkanInstance(bool /*using_surface*/) +{ + std::vector<const char *> required_extensions = { + VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + }; + + auto env = base::Environment::Create(); + std::string vulkan_path; + if (!env->GetVar("QT_VULKAN_LIB", &vulkan_path)) +#ifdef Q_OS_WIN + vulkan_path = "vulkan-1.dll"; +#else + vulkan_path = "libvulkan.so.1"; +#endif + + if (!vulkan_instance_.Initialize(base::FilePath::FromUTF8Unsafe(vulkan_path), + required_extensions, {})) { + LOG(ERROR) << "Failed to initialize vulkan instance"; + return false; + } + + return true; +} + +VulkanInstance *VulkanImplementationQt::GetVulkanInstance() +{ + return &vulkan_instance_; +} + +std::unique_ptr<VulkanSurface> +VulkanImplementationQt::CreateViewSurface(gfx::AcceleratedWidget /*window*/) +{ + NOTREACHED(); + return nullptr; +} + +bool VulkanImplementationQt::GetPhysicalDevicePresentationSupport( + VkPhysicalDevice /*device*/, + const std::vector<VkQueueFamilyProperties> & /*queue_family_properties*/, + uint32_t /*queue_family_index*/) +{ + NOTREACHED(); + return true; +} + +std::vector<const char *> VulkanImplementationQt::GetRequiredDeviceExtensions() +{ + return { + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, +#ifdef Q_OS_WIN + VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, +#else + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, +#endif + }; +} + +std::vector<const char *> VulkanImplementationQt::GetOptionalDeviceExtensions() +{ + return { + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, +#ifdef Q_OS_WIN + VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, +#else + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, +#endif + }; +} + +VkFence VulkanImplementationQt::CreateVkFenceForGpuFence(VkDevice /*vk_device*/) +{ + NOTREACHED(); + return VK_NULL_HANDLE; +} + +std::unique_ptr<gfx::GpuFence> +VulkanImplementationQt::ExportVkFenceToGpuFence(VkDevice /*vk_device*/, VkFence /*vk_fence*/) +{ + NOTREACHED(); + return nullptr; +} + +VkSemaphore VulkanImplementationQt::CreateExternalSemaphore(VkDevice vk_device) +{ + return CreateExternalVkSemaphore( +#ifdef Q_OS_WIN + vk_device, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT); +#else + vk_device, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT); +#endif +} + +VkSemaphore VulkanImplementationQt::ImportSemaphoreHandle(VkDevice vk_device, + SemaphoreHandle sync_handle) +{ + return ImportVkSemaphoreHandle(vk_device, std::move(sync_handle)); +} + +SemaphoreHandle VulkanImplementationQt::GetSemaphoreHandle(VkDevice vk_device, + VkSemaphore vk_semaphore) +{ + return GetVkSemaphoreHandle(vk_device, vk_semaphore, +#ifdef Q_OS_WIN + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT); +#else + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT); +#endif +} + +VkExternalMemoryHandleTypeFlagBits VulkanImplementationQt::GetExternalImageHandleType() +{ +#ifdef Q_OS_WIN + return VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT; +#else + return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; +#endif +} + +bool VulkanImplementationQt::CanImportGpuMemoryBuffer( + gfx::GpuMemoryBufferType /*memory_buffer_type*/) +{ + return false; +} + +std::unique_ptr<VulkanImage> +VulkanImplementationQt::CreateImageFromGpuMemoryHandle(VulkanDeviceQueue * /*device_queue*/, + gfx::GpuMemoryBufferHandle /*gmb_handle*/, + gfx::Size /*size*/, VkFormat /*vk_format*/) +{ + NOTREACHED(); + return nullptr; +} + +} // namespace gpu diff --git a/src/core/compositor/vulkan_implementation_qt.h b/src/core/compositor/vulkan_implementation_qt.h new file mode 100644 index 000000000..6e7a68b49 --- /dev/null +++ b/src/core/compositor/vulkan_implementation_qt.h @@ -0,0 +1,47 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef VULKAN_IMPLEMENTATION_QT_H +#define VULKAN_IMPLEMENTATION_QT_H + +#include "gpu/vulkan/vulkan_implementation.h" +#include "gpu/vulkan/vulkan_instance.h" + +namespace gpu { + +class VulkanImplementationQt : public VulkanImplementation +{ +public: + VulkanImplementationQt(); + ~VulkanImplementationQt() override; + + // Overridden from VulkanImplementation. + bool InitializeVulkanInstance(bool using_surface) override; + VulkanInstance *GetVulkanInstance() override; + std::unique_ptr<VulkanSurface> CreateViewSurface(gfx::AcceleratedWidget window) override; + bool GetPhysicalDevicePresentationSupport( + VkPhysicalDevice device, + const std::vector<VkQueueFamilyProperties> &queue_family_properties, + uint32_t queue_family_index) override; + std::vector<const char *> GetRequiredDeviceExtensions() override; + std::vector<const char *> GetOptionalDeviceExtensions() override; + VkFence CreateVkFenceForGpuFence(VkDevice vk_device) override; + std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence(VkDevice vk_device, + VkFence vk_fence) override; + VkSemaphore CreateExternalSemaphore(VkDevice vk_device) override; + VkSemaphore ImportSemaphoreHandle(VkDevice vk_device, SemaphoreHandle handle) override; + SemaphoreHandle GetSemaphoreHandle(VkDevice vk_device, VkSemaphore vk_semaphore) override; + VkExternalMemoryHandleTypeFlagBits GetExternalImageHandleType() override; + bool CanImportGpuMemoryBuffer(gfx::GpuMemoryBufferType memory_buffer_type) override; + std::unique_ptr<VulkanImage> + CreateImageFromGpuMemoryHandle(VulkanDeviceQueue *device_queue, + gfx::GpuMemoryBufferHandle gmb_handle, gfx::Size size, + VkFormat vk_format) override; + +private: + VulkanInstance vulkan_instance_; +}; + +} // namespace gpu + +#endif // VULKAN_IMPLEMENTATION_QT_H diff --git a/src/core/ozone/ozone_platform_qt.cpp b/src/core/ozone/ozone_platform_qt.cpp index 8ab3aa1a7..71315a049 100644 --- a/src/core/ozone/ozone_platform_qt.cpp +++ b/src/core/ozone/ozone_platform_qt.cpp @@ -4,6 +4,7 @@ #include "ozone_platform_qt.h" #if defined(USE_OZONE) +#include "base/no_destructor.h" #include "ui/base/buildflags.h" #include "ui/base/ime/input_method.h" #include "ui/display/types/native_display_delegate.h" @@ -52,6 +53,8 @@ public: ui::OverlayManagerOzone* GetOverlayManager() override; std::unique_ptr<InputMethod> CreateInputMethod(internal::InputMethodDelegate *delegate, gfx::AcceleratedWidget widget) override; std::unique_ptr<ui::PlatformScreen> CreateScreen() override { return nullptr; } + const PlatformProperties &GetPlatformProperties() override; + private: bool InitializeUI(const ui::OzonePlatform::InitParams &) override; void InitializeGPU(const ui::OzonePlatform::InitParams &) override; @@ -76,6 +79,20 @@ OzonePlatformQt::OzonePlatformQt() {} OzonePlatformQt::~OzonePlatformQt() {} +const ui::OzonePlatform::PlatformProperties &OzonePlatformQt::GetPlatformProperties() +{ + static base::NoDestructor<ui::OzonePlatform::PlatformProperties> properties; + static bool initialized = false; + if (!initialized) { + properties->uses_external_vulkan_image_factory = true; + properties->fetch_buffer_formats_for_gmb_on_gpu = true; + + initialized = true; + } + + return *properties; +} + ui::SurfaceFactoryOzone* OzonePlatformQt::GetSurfaceFactoryOzone() { return surface_factory_ozone_.get(); diff --git a/src/core/ozone/surface_factory_qt.cpp b/src/core/ozone/surface_factory_qt.cpp index 33164d076..13c1a7b11 100644 --- a/src/core/ozone/surface_factory_qt.cpp +++ b/src/core/ozone/surface_factory_qt.cpp @@ -10,6 +10,12 @@ #include "ozone/gl_ozone_glx_qt.h" #endif +#include "qtwebenginecoreglobal_p.h" + +#if QT_CONFIG(webengine_vulkan) +#include "compositor/vulkan_implementation_qt.h" +#endif + namespace QtWebEngineCore { SurfaceFactoryQt::SurfaceFactoryQt() @@ -27,7 +33,6 @@ SurfaceFactoryQt::SurfaceFactoryQt() gl::GLImplementationParts(gl::kGLImplementationDisabled) }; m_ozone.reset(new ui::GLOzoneEGLQt()); } else { - qWarning("No suitable graphics backend found\n"); m_impl = { gl::GLImplementationParts(gl::kGLImplementationDisabled) }; } } @@ -41,6 +46,18 @@ ui::GLOzone *SurfaceFactoryQt::GetGLOzone(const gl::GLImplementationParts &imple { return m_ozone.get(); } +#if BUILDFLAG(ENABLE_VULKAN) +std::unique_ptr<gpu::VulkanImplementation> +SurfaceFactoryQt::CreateVulkanImplementation(bool /*allow_protected_memory*/, + bool /*enforce_protected_memory*/) +{ +#if QT_CONFIG(webengine_vulkan) + return std::make_unique<gpu::VulkanImplementationQt>(); +#else + return nullptr; +#endif +} +#endif } // namespace QtWebEngineCore #endif // defined(USE_OZONE) diff --git a/src/core/ozone/surface_factory_qt.h b/src/core/ozone/surface_factory_qt.h index 767b69b85..bfcfa014b 100644 --- a/src/core/ozone/surface_factory_qt.h +++ b/src/core/ozone/surface_factory_qt.h @@ -16,6 +16,10 @@ public: SurfaceFactoryQt(); std::vector<gl::GLImplementationParts> GetAllowedGLImplementations() override; ui::GLOzone *GetGLOzone(const gl::GLImplementationParts &implementation) override; +#if BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr<gpu::VulkanImplementation> + CreateVulkanImplementation(bool allow_protected_memory, bool enforce_protected_memory) override; +#endif private: std::vector<gl::GLImplementationParts> m_impl; std::unique_ptr<ui::GLOzone> m_ozone; diff --git a/src/core/render_widget_host_view_qt_delegate_item.cpp b/src/core/render_widget_host_view_qt_delegate_item.cpp index a44046aac..f90a201b6 100644 --- a/src/core/render_widget_host_view_qt_delegate_item.cpp +++ b/src/core/render_widget_host_view_qt_delegate_item.cpp @@ -29,6 +29,7 @@ RenderWidgetHostViewQtDelegateItem::RenderWidgetHostViewQtDelegateItem(RenderWid RenderWidgetHostViewQtDelegateItem::~RenderWidgetHostViewQtDelegateItem() { + releaseVulkanResources(); if (m_widgetDelegate) { m_widgetDelegate->Unbind(); m_widgetDelegate->Destroy(); @@ -315,6 +316,12 @@ void RenderWidgetHostViewQtDelegateItem::itemChange(ItemChange change, const Ite this, &RenderWidgetHostViewQtDelegateItem::onBeforeRendering, Qt::DirectConnection)); m_windowConnections.append(connect(value.window, SIGNAL(xChanged(int)), SLOT(onWindowPosChanged()))); m_windowConnections.append(connect(value.window, SIGNAL(yChanged(int)), SLOT(onWindowPosChanged()))); +#if QT_CONFIG(webengine_vulkan) + m_windowConnections.append( + connect(value.window, &QQuickWindow::sceneGraphAboutToStop, this, + &RenderWidgetHostViewQtDelegateItem::releaseVulkanResources, + Qt::DirectConnection)); +#endif if (!m_isPopup) m_windowConnections.append(connect(value.window, SIGNAL(closing(QQuickCloseEvent *)), SLOT(onHide()))); } @@ -362,6 +369,18 @@ QSGNode *RenderWidgetHostViewQtDelegateItem::updatePaintNode(QSGNode *oldNode, U node->setTexture(QNativeInterface::QSGOpenGLTexture::fromNative(texId, win, texSize, texOpts)); node->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); #endif +#if QT_CONFIG(webengine_vulkan) + } else if (comp->type() == Compositor::Type::Vulkan) { + QQuickWindow::CreateTextureOptions texOpts; + if (comp->hasAlphaChannel()) + texOpts.setFlag(QQuickWindow::TextureHasAlphaChannel); + + VkImage image = comp->vkImage(win); + VkImageLayout layout = comp->vkImageLayout(); + node->setTexture(QNativeInterface::QSGVulkanTexture::fromNative(image, layout, win, texSize, + texOpts)); + node->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); +#endif // QT_CONFIG(webengine_vulkan) } else { Q_UNREACHABLE(); } @@ -372,7 +391,7 @@ QSGNode *RenderWidgetHostViewQtDelegateItem::updatePaintNode(QSGNode *oldNode, U void RenderWidgetHostViewQtDelegateItem::onBeforeRendering() { auto comp = compositor(); - if (!comp || comp->type() != Compositor::Type::OpenGL) + if (!comp || comp->type() == Compositor::Type::Software) return; comp->waitForTexture(); } @@ -388,6 +407,17 @@ void RenderWidgetHostViewQtDelegateItem::onHide() m_client->forwardEvent(&event); } +void RenderWidgetHostViewQtDelegateItem::releaseVulkanResources() +{ +#if QT_CONFIG(webengine_vulkan) + auto comp = compositor(); + if (!comp || comp->type() != Compositor::Type::Vulkan) + return; + + comp->releaseVulkanResources(QQuickItem::window()); +#endif +} + void RenderWidgetHostViewQtDelegateItem::adapterClientChanged(WebContentsAdapterClient *client) { m_adapterClient = client; diff --git a/src/core/render_widget_host_view_qt_delegate_item.h b/src/core/render_widget_host_view_qt_delegate_item.h index e057d37d9..6f3289157 100644 --- a/src/core/render_widget_host_view_qt_delegate_item.h +++ b/src/core/render_widget_host_view_qt_delegate_item.h @@ -101,6 +101,7 @@ protected: private Q_SLOTS: void onBeforeRendering(); void onWindowPosChanged(); + void releaseVulkanResources(); void onHide(); private: diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index 94ff5f7b2..f6c60daf3 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -55,6 +55,7 @@ #include "content/public/common/network_service_util.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/sync_point_manager.h" +#include "gpu/config/gpu_finch_features.h" #include "media/audio/audio_manager.h" #include "media/base/media_switches.h" #include "mojo/core/embedder/embedder.h" @@ -123,7 +124,8 @@ namespace QtWebEngineCore { static bool usingSupportedSGBackend() { - if (QQuickWindow::graphicsApi() != QSGRendererInterface::OpenGL) + if (QQuickWindow::graphicsApi() != QSGRendererInterface::OpenGL + && QQuickWindow::graphicsApi() != QSGRendererInterface::Vulkan) return false; const QStringList args = QGuiApplication::arguments(); @@ -683,6 +685,15 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(cc::switches::kDisableCompositedAntialiasing); } +#if QT_CONFIG(webengine_vulkan) + if (QQuickWindow::graphicsApi() == QSGRendererInterface::Vulkan) { + enableFeatures.push_back(features::kVulkan.name); + enableFeatures.push_back(features::kUseSkiaRenderer.name); + parsedCommandLine->AppendSwitchASCII(switches::kUseVulkan, + switches::kVulkanImplementationNameNative); + } +#endif + initializeFeatureList(parsedCommandLine, enableFeatures, disableFeatures); GLContextHelper::initialize(); diff --git a/src/webenginequick/api/qtwebenginequickglobal.cpp b/src/webenginequick/api/qtwebenginequickglobal.cpp index 607777e55..b16305dae 100644 --- a/src/webenginequick/api/qtwebenginequickglobal.cpp +++ b/src/webenginequick/api/qtwebenginequickglobal.cpp @@ -43,9 +43,12 @@ void initialize() QtWebEngineCore::initialize(); return; } + // call initialize the same way as widgets do qAddPreRoutine(QtWebEngineCore::initialize); - QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi); + auto api = QQuickWindow::graphicsApi(); + if (api != QSGRendererInterface::OpenGLRhi && api != QSGRendererInterface::VulkanRhi) + QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi); } } // namespace QtWebEngineQuick |