From e0e7c42a19316c53fd3ab21133223b5ee788ba38 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 17 Jan 2020 15:06:47 +0100 Subject: [PATCH] Introduce VK_KHR_display support for i.MX8 devices The eglfs_viv backend has Vulkan support now. While the support code is common (lives in api/vulkan), we will not expose this for any other integration yet, without the appropriate testing. While putting this to eglfs seems unintuitive at first, it turns out that for Vivante in particular this is very useful, since we can rely on the existing framebuffer device infrastructure to solve certain problems (like the lack of vsync) The VK_KHR_display implementation of Vivante currently exhibits all the known issues of the old, fbdev-style EGL plumbing (presumably since it lives on top of that): - No vsync. This can be fixed by setting QT_QPA_EGLFS_FORCEVSYNC. - May need a manual call to fbset to set the correct resolution before launching the Qt app. - And of course it lacks all the multi-screen features provided by drm. - Plus, it seems the swapchain only supports a min/max buffer count of 1. This needs special handling in QRhi since until now we assumed that there was always at least 2 buffers available. [ChangeLog][Platform Specific Changes][Linux] Vulkan is now supported by eglfs (eglfs_viv backend) on i.MX8 devices with the Vivante graphics stack. This is done via VK_KHR_display so no windowing system is required. Task-number: QTBUG-78754 Change-Id: I7530aa026d4b904b9de83f9bdbdc4897ae770e71 Reviewed-by: Paul Olav Tvete --- src/gui/rhi/qrhivulkan.cpp | 17 +- src/plugins/platforms/eglfs/api/api.pri | 9 + .../eglfs/api/qeglfsdeviceintegration.cpp | 8 + .../eglfs/api/qeglfsdeviceintegration_p.h | 4 + .../platforms/eglfs/api/qeglfsintegration.cpp | 19 +- .../platforms/eglfs/api/qeglfsintegration_p.h | 3 + .../platforms/eglfs/api/qeglfswindow_p.h | 4 + .../eglfs/api/vulkan/qeglfsvulkaninstance.cpp | 280 ++++++++++++++++++ .../eglfs/api/vulkan/qeglfsvulkaninstance_p.h | 88 ++++++ .../eglfs/api/vulkan/qeglfsvulkanwindow.cpp | 75 +++++ .../eglfs/api/vulkan/qeglfsvulkanwindow_p.h | 74 +++++ .../eglfs_viv/qeglfsvivintegration.cpp | 21 ++ .../eglfs_viv/qeglfsvivintegration.h | 6 + .../eglfs/eglfsdeviceintegration.pro | 3 + 14 files changed, 602 insertions(+), 9 deletions(-) create mode 100644 src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance.cpp create mode 100644 src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance_p.h create mode 100644 src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow.cpp create mode 100644 src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow_p.h diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index f4c72d2cca..e7faa0cadd 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -1351,8 +1351,8 @@ bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain) quint32 actualSwapChainBufferCount = 0; err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr); - if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) { - qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount); + if (err != VK_SUCCESS || actualSwapChainBufferCount == 0) { + qWarning("Failed to get swapchain images: %d", err); return false; } @@ -1528,7 +1528,8 @@ void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain) QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); - QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); + const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0; + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); if (!frame.imageAcquired) { @@ -1571,7 +1572,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin // will make B wait for A's frame 0 commands, so if a resource is written // in B's frame or when B checks for pending resource releases, that won't // mess up A's in-flight commands (as they are not in flight anymore). - waitCommandCompletion(int(swapChainD->currentFrameSlot)); + waitCommandCompletion(frameResIndex); // Now is the time to read the timestamps for the previous frame for this slot. if (frame.timestampQueryIndex >= 0) { @@ -1606,7 +1607,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin // when profiling is enabled, pick a free query (pair) from the pool int timestampQueryIdx = -1; - if (profilerPrivateOrNull()) { + if (profilerPrivateOrNull() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight for (int i = 0; i < timestampQueryPoolMap.count(); ++i) { if (!timestampQueryPoolMap.testBit(i)) { timestampQueryPoolMap.setBit(i); @@ -1648,7 +1649,8 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram recordPrimaryCommandBuffer(&swapChainD->cbWrapper); - QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); + int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0; + QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]); QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) { @@ -1860,7 +1862,8 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer void QRhiVulkan::waitCommandCompletion(int frameSlot) { for (QVkSwapChain *sc : qAsConst(swapchains)) { - QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]); + const int frameResIndex = sc->bufferCount > 1 ? frameSlot : 0; + QVkSwapChain::FrameResources &frame(sc->frameRes[frameResIndex]); if (frame.cmdFenceWaitable) { df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX); df->vkResetFences(dev, 1, &frame.cmdFence); diff --git a/src/plugins/platforms/eglfs/api/api.pri b/src/plugins/platforms/eglfs/api/api.pri index a6d81016b6..68965b58d8 100644 --- a/src/plugins/platforms/eglfs/api/api.pri +++ b/src/plugins/platforms/eglfs/api/api.pri @@ -23,4 +23,13 @@ qtConfig(opengl) { $$PWD/qeglfscontext_p.h } +qtConfig(vulkan) { + SOURCES += \ + $$PWD/vulkan/qeglfsvulkaninstance.cpp \ + $$PWD/vulkan/qeglfsvulkanwindow.cpp + HEADERS += \ + $$PWD/vulkan/qeglfsvulkaninstance_p.h \ + $$PWD/vulkan/qeglfsvulkanwindow_p.h +} + INCLUDEPATH += $$PWD diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp index dbfb0e6058..e3145aa0b0 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp @@ -381,6 +381,14 @@ void *QEglFSDeviceIntegration::wlDisplay() const return nullptr; } +#if QT_CONFIG(vulkan) +QPlatformVulkanInstance *QEglFSDeviceIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) +{ + Q_UNUSED(instance); + return nullptr; +} +#endif + EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) { class Chooser : public QEglConfigChooser { diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h index 71ffb4c69a..08447a40ea 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h +++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h @@ -108,6 +108,10 @@ public: virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen); virtual void *wlDisplay() const; +#if QT_CONFIG(vulkan) + virtual QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance); +#endif + static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); }; diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp index d9a3545a95..e26d984cc1 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp @@ -247,6 +247,13 @@ QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOf } #endif // QT_NO_OPENGL +#if QT_CONFIG(vulkan) +QPlatformVulkanInstance *QEglFSIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const +{ + return qt_egl_device_integration()->createPlatformVulkanInstance(instance); +} +#endif + bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const { // We assume that devices will have more and not less capabilities @@ -283,7 +290,8 @@ enum ResourceType { NativeDisplay, XlibDisplay, WaylandDisplay, - EglSurface + EglSurface, + VkSurface }; static int resourceType(const QByteArray &key) @@ -296,7 +304,8 @@ static int resourceType(const QByteArray &key) QByteArrayLiteral("nativedisplay"), QByteArrayLiteral("display"), QByteArrayLiteral("server_wl_display"), - QByteArrayLiteral("eglsurface") + QByteArrayLiteral("eglsurface"), + QByteArrayLiteral("vksurface") }; const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *result = std::find(names, end, key); @@ -364,6 +373,12 @@ void *QEglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWi if (window && window->handle()) result = reinterpret_cast(static_cast(window->handle())->surface()); break; +#if QT_CONFIG(vulkan) + case VkSurface: + if (window && window->handle() && window->surfaceType() == QSurface::VulkanSurface) + result = static_cast(window->handle())->vulkanSurfacePtr(); + break; +#endif default: break; } diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h index 898b322834..b293651ce7 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h @@ -85,6 +85,9 @@ public: #ifndef QT_NO_OPENGL QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override; QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; +#endif +#if QT_CONFIG(vulkan) + QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override; #endif bool hasCapability(QPlatformIntegration::Capability cap) const override; diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h index be2a0630d3..7bf74c25ee 100644 --- a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h +++ b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h @@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE class QOpenGLCompositorBackingStore; class QPlatformTextureList; + #ifndef QT_NO_OPENGL class Q_EGLFS_EXPORT QEglFSWindow : public QPlatformWindow, public QOpenGLCompositorWindow #else @@ -96,6 +97,9 @@ public: EGLNativeWindowType eglWindow() const; EGLSurface surface() const; QEglFSScreen *screen() const override; +#if QT_CONFIG(vulkan) + virtual void *vulkanSurfacePtr() { return nullptr; } +#endif bool hasNativeWindow() const { return m_flags.testFlag(HasNativeWindow); } diff --git a/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance.cpp b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance.cpp new file mode 100644 index 0000000000..a75251ca5f --- /dev/null +++ b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsvulkaninstance_p.h" +#include "qeglfswindow_p.h" +#include "qeglfshooks_p.h" +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglDevDebug) + +QEglFSVulkanInstance::QEglFSVulkanInstance(QVulkanInstance *instance) + : m_instance(instance) +{ + loadVulkanLibrary(QStringLiteral("vulkan")); +} + +void QEglFSVulkanInstance::createOrAdoptInstance() +{ + qCDebug(qLcEglDevDebug, "Creating Vulkan instance for VK_KHR_display"); + + const QByteArray extName = QByteArrayLiteral("VK_KHR_display"); + initInstance(m_instance, { extName }); + if (!m_vkInst) + return; + if (!enabledExtensions().contains(extName)) { + qWarning("Failed to enable VK_KHR_display extension"); + return; + } + +#if VK_KHR_display + m_getPhysicalDeviceDisplayPropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPropertiesKHR"); + m_getDisplayModePropertiesKHR = (PFN_vkGetDisplayModePropertiesKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayModePropertiesKHR"); + m_getPhysicalDeviceDisplayPlanePropertiesKHR = (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR"); + + m_getDisplayPlaneSupportedDisplaysKHR = (PFN_vkGetDisplayPlaneSupportedDisplaysKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneSupportedDisplaysKHR"); + m_getDisplayPlaneCapabilitiesKHR = (PFN_vkGetDisplayPlaneCapabilitiesKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkGetDisplayPlaneCapabilitiesKHR"); + + m_createDisplayPlaneSurfaceKHR = (PFN_vkCreateDisplayPlaneSurfaceKHR) + m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDisplayPlaneSurfaceKHR"); +#endif + + m_enumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) + m_vkGetInstanceProcAddr(m_vkInst, "vkEnumeratePhysicalDevices"); + + // Use for first physical device, unless overridden by QT_VK_PHYSICAL_DEVICE_INDEX. + // This behavior matches what the Vulkan backend of QRhi would do. + + uint32_t physDevCount = 0; + m_enumeratePhysicalDevices(m_vkInst, &physDevCount, nullptr); + if (!physDevCount) { + qWarning("No physical devices"); + return; + } + QVarLengthArray physDevs(physDevCount); + VkResult err = m_enumeratePhysicalDevices(m_vkInst, &physDevCount, physDevs.data()); + if (err != VK_SUCCESS || !physDevCount) { + qWarning("Failed to enumerate physical devices: %d", err); + return; + } + + if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX")) { + int requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX"); + if (requestedPhysDevIndex >= 0 && uint32_t(requestedPhysDevIndex) < physDevCount) + m_physDev = physDevs[requestedPhysDevIndex]; + } + + if (m_physDev == VK_NULL_HANDLE) + m_physDev = physDevs[0]; +} + +bool QEglFSVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + QWindow *window) +{ + Q_UNUSED(physicalDevice); + Q_UNUSED(queueFamilyIndex); + Q_UNUSED(window); + return true; +} + +VkSurfaceKHR QEglFSVulkanInstance::createSurface(QEglFSWindow *window) +{ +#if VK_KHR_display + qCDebug(qLcEglDevDebug, "Creating VkSurfaceKHR via VK_KHR_display for window %p", (void *) window); + + if (!m_physDev) { + qWarning("No physical device, cannot create surface"); + return VK_NULL_HANDLE; + } + + uint32_t displayCount = 0; + VkResult err = m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to get display properties: %d", err); + return VK_NULL_HANDLE; + } + + qCDebug(qLcEglDevDebug, "Display count: %u", displayCount); + + QVarLengthArray displayProps(displayCount); + m_getPhysicalDeviceDisplayPropertiesKHR(m_physDev, &displayCount, displayProps.data()); + + VkDisplayKHR display = VK_NULL_HANDLE; + VkDisplayModeKHR displayMode = VK_NULL_HANDLE; + uint32_t width = 0; + uint32_t height = 0; + + for (uint32_t i = 0; i < displayCount; ++i) { + const VkDisplayPropertiesKHR &disp(displayProps[i]); + qCDebug(qLcEglDevDebug, "Display #%u:\n display: %p\n name: %s\n dimensions: %ux%u\n resolution: %ux%u", + i, (void *) disp.display, disp.displayName, + disp.physicalDimensions.width, disp.physicalDimensions.height, + disp.physicalResolution.width, disp.physicalResolution.height); + + // Just pick the first display and the first mode. + if (i == 0) + display = disp.display; + + uint32_t modeCount = 0; + if (m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, nullptr) != VK_SUCCESS) { + qWarning("Failed to get modes for display"); + continue; + } + QVarLengthArray modeProps(modeCount); + m_getDisplayModePropertiesKHR(m_physDev, disp.display, &modeCount, modeProps.data()); + for (uint32_t j = 0; j < modeCount; ++j) { + const VkDisplayModePropertiesKHR &mode(modeProps[j]); + qCDebug(qLcEglDevDebug, " Mode #%u:\n mode: %p\n visibleRegion: %ux%u\n refreshRate: %u", + j, (void *) mode.displayMode, + mode.parameters.visibleRegion.width, mode.parameters.visibleRegion.height, + mode.parameters.refreshRate); + if (j == 0) { + displayMode = mode.displayMode; + width = mode.parameters.visibleRegion.width; + height = mode.parameters.visibleRegion.height; + } + } + } + + if (display == VK_NULL_HANDLE || displayMode == VK_NULL_HANDLE) { + qWarning("Failed to choose display and mode"); + return VK_NULL_HANDLE; + } + uint32_t planeCount = 0; + err = m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to get plane properties: %d", err); + return VK_NULL_HANDLE; + } + + qCDebug(qLcEglDevDebug, "Plane count: %u", planeCount); + + QVarLengthArray planeProps(planeCount); + m_getPhysicalDeviceDisplayPlanePropertiesKHR(m_physDev, &planeCount, planeProps.data()); + + uint32_t planeIndex = UINT_MAX; + for (uint32_t i = 0; i < planeCount; ++i) { + uint32_t supportedDisplayCount = 0; + err = m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, nullptr); + if (err != VK_SUCCESS) { + qWarning("Failed to query supported displays for plane: %d", err); + return VK_NULL_HANDLE; + } + + QVarLengthArray supportedDisplays(supportedDisplayCount); + m_getDisplayPlaneSupportedDisplaysKHR(m_physDev, i, &supportedDisplayCount, supportedDisplays.data()); + qCDebug(qLcEglDevDebug, "Plane #%u supports %u displays, currently bound to display %p", + i, supportedDisplayCount, (void *) planeProps[i].currentDisplay); + + VkDisplayPlaneCapabilitiesKHR caps; + err = m_getDisplayPlaneCapabilitiesKHR(m_physDev, displayMode, i, &caps); + if (err != VK_SUCCESS) { + qWarning("Failed to query plane capabilities: %d", err); + return VK_NULL_HANDLE; + } + + qCDebug(qLcEglDevDebug, " supportedAlpha: %d (1=no, 2=global, 4=per pixel, 8=per pixel premul)\n" + " minSrc=%d, %d %ux%u\n" + " maxSrc=%d, %d %ux%u\n" + " minDst=%d, %d %ux%u\n" + " maxDst=%d, %d %ux%u", + int(caps.supportedAlpha), + caps.minSrcPosition.x, caps.minSrcPosition.y, caps.minSrcExtent.width, caps.minSrcExtent.height, + caps.maxSrcPosition.x, caps.maxSrcPosition.y, caps.maxSrcExtent.width, caps.maxSrcExtent.height, + caps.minDstPosition.x, caps.minDstPosition.y, caps.minDstExtent.width, caps.minDstExtent.height, + caps.maxDstPosition.x, caps.maxDstPosition.y, caps.maxDstExtent.width, caps.maxDstExtent.height); + + // if the plane is not in use and supports our chosen display, use that plane + if (supportedDisplays.contains(display) + && (planeProps[i].currentDisplay == VK_NULL_HANDLE || planeProps[i].currentDisplay == display)) + { + planeIndex = i; + } + } + + if (planeIndex == UINT_MAX) { + qWarning("Failed to find a suitable plane"); + return VK_NULL_HANDLE; + } + + qCDebug(qLcEglDevDebug, "Using plane #%u", planeIndex); + + VkDisplaySurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.displayMode = displayMode; + surfaceCreateInfo.planeIndex = planeIndex; + surfaceCreateInfo.planeStackIndex = planeProps[planeIndex].currentStackIndex; + surfaceCreateInfo.transform = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; + surfaceCreateInfo.globalAlpha = 1.0f; + surfaceCreateInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + surfaceCreateInfo.imageExtent = { width, height }; + + VkSurfaceKHR surface = VK_NULL_HANDLE; + err = m_createDisplayPlaneSurfaceKHR(m_vkInst, &surfaceCreateInfo, nullptr, &surface); + if (err != VK_SUCCESS || surface == VK_NULL_HANDLE) { + qWarning("Failed to create surface: %d", err); + return VK_NULL_HANDLE; + } + + qCDebug(qLcEglDevDebug, "Created surface %p", (void *) surface); + + return surface; + +#else + Q_UNUSED(window); + qWarning("VK_KHR_display support was not compiled in, cannot create surface"); + return VK_NULL_HANDLE; +#endif +} + +void QEglFSVulkanInstance::presentAboutToBeQueued(QWindow *window) +{ + // support QT_QPA_EGLFS_FORCEVSYNC (i.MX8 with eglfs_viv) + qt_egl_device_integration()->waitForVSync(window->handle()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance_p.h b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance_p.h new file mode 100644 index 0000000000..9d6d47f439 --- /dev/null +++ b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkaninstance_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSVULKANINSTANCE_H +#define QEGLFSVULKANINSTANCE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qeglfsglobal_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QEglFSWindow; + +class Q_EGLFS_EXPORT QEglFSVulkanInstance : public QBasicPlatformVulkanInstance +{ +public: + QEglFSVulkanInstance(QVulkanInstance *instance); + + void createOrAdoptInstance() override; + bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override; + void presentAboutToBeQueued(QWindow *window) override; + + VkSurfaceKHR createSurface(QEglFSWindow *window); + +private: + QVulkanInstance *m_instance; + VkPhysicalDevice m_physDev = VK_NULL_HANDLE; + PFN_vkEnumeratePhysicalDevices m_enumeratePhysicalDevices = nullptr; +#if VK_KHR_display + PFN_vkGetPhysicalDeviceDisplayPropertiesKHR m_getPhysicalDeviceDisplayPropertiesKHR = nullptr; + PFN_vkGetDisplayModePropertiesKHR m_getDisplayModePropertiesKHR = nullptr; + PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR m_getPhysicalDeviceDisplayPlanePropertiesKHR = nullptr; + PFN_vkGetDisplayPlaneSupportedDisplaysKHR m_getDisplayPlaneSupportedDisplaysKHR = nullptr; + PFN_vkGetDisplayPlaneCapabilitiesKHR m_getDisplayPlaneCapabilitiesKHR = nullptr; + PFN_vkCreateDisplayPlaneSurfaceKHR m_createDisplayPlaneSurfaceKHR = nullptr; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow.cpp b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow.cpp new file mode 100644 index 0000000000..ae41ca00b6 --- /dev/null +++ b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsvulkanwindow_p.h" + +QT_BEGIN_NAMESPACE + +QEglFSVulkanWindow::QEglFSVulkanWindow(QWindow *window) + : QEglFSWindow(window), + m_surface(VK_NULL_HANDLE) +{ +} + +QEglFSVulkanWindow::~QEglFSVulkanWindow() +{ + if (m_surface) { + QVulkanInstance *inst = window()->vulkanInstance(); + if (inst) + static_cast(inst->handle())->destroySurface(m_surface); + } +} + +void *QEglFSVulkanWindow::vulkanSurfacePtr() +{ + if (m_surface) + return &m_surface; + + QVulkanInstance *inst = window()->vulkanInstance(); + if (!inst) { + qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?"); + return nullptr; + } + QEglFSVulkanInstance *eglfsInst = static_cast(inst->handle()); + m_surface = eglfsInst->createSurface(this); + + return &m_surface; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow_p.h b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow_p.h new file mode 100644 index 0000000000..492fb41ca4 --- /dev/null +++ b/src/plugins/platforms/eglfs/api/vulkan/qeglfsvulkanwindow_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSVULKANWINDOW_H +#define QEGLFSVULKANWINDOW_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qeglfsglobal_p.h" +#include "qeglfswindow_p.h" +#include "qeglfsvulkaninstance_p.h" + +QT_BEGIN_NAMESPACE + +class Q_EGLFS_EXPORT QEglFSVulkanWindow : public QEglFSWindow +{ +public: + QEglFSVulkanWindow(QWindow *window); + ~QEglFSVulkanWindow(); + + void *vulkanSurfacePtr() override; + +private: + VkSurfaceKHR m_surface; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp index 2fc076ad0c..5e1d28a353 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.cpp @@ -41,6 +41,11 @@ #include #include +#if QT_CONFIG(vulkan) +#include "private/qeglfsvulkaninstance_p.h" +#include "private/qeglfsvulkanwindow_p.h" +#endif + #ifdef Q_OS_INTEGRITY extern "C" void VivanteInit(void); #endif @@ -97,4 +102,20 @@ void QEglFSVivIntegration::destroyNativeWindow(EGLNativeWindowType window) fbDestroyWindow(window); } +#if QT_CONFIG(vulkan) + +QEglFSWindow *QEglFSVivIntegration::createWindow(QWindow *window) const +{ + if (window->surfaceType() == QSurface::VulkanSurface) + return new QEglFSVulkanWindow(window); + return QEglFSDeviceIntegration::createWindow(window); +} + +QPlatformVulkanInstance *QEglFSVivIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) +{ + return new QEglFSVulkanInstance(instance); +} + +#endif // vulkan + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.h index 4d1718afcf..02b59c16b5 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/qeglfsvivintegration.h @@ -53,6 +53,12 @@ public: void destroyNativeWindow(EGLNativeWindowType window) override; EGLNativeDisplayType platformDisplay() const override; + // Vulkan support with VK_KHR_display +#if QT_CONFIG(vulkan) + QEglFSWindow *createWindow(QWindow *window) const override; + QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) override; +#endif + private: QSize mScreenSize; EGLNativeDisplayType mNativeDisplay; diff --git a/src/plugins/platforms/eglfs/eglfsdeviceintegration.pro b/src/plugins/platforms/eglfs/eglfsdeviceintegration.pro index 8bb7b614f1..bd02aea4d3 100644 --- a/src/plugins/platforms/eglfs/eglfsdeviceintegration.pro +++ b/src/plugins/platforms/eglfs/eglfsdeviceintegration.pro @@ -20,6 +20,9 @@ qtHaveModule(input_support-private): \ qtHaveModule(platformcompositor_support-private): \ QT += platformcompositor_support-private +qtConfig(vulkan): \ + QT += vulkan_support-private + # Avoid X11 header collision, use generic EGL native types DEFINES += QT_EGL_NO_X11