1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qandroidplatformvulkanwindow.h"
41 #include "qandroidplatformscreen.h"
42 #include "androidjnimain.h"
43 #include "qandroideventdispatcher.h"
44 #include "androiddeadlockprotector.h"
45 
46 #include <QSurfaceFormat>
47 #include <qpa/qwindowsysteminterface.h>
48 #include <qpa/qplatformscreen.h>
49 
50 #include <android/native_window.h>
51 #include <android/native_window_jni.h>
52 
53 QT_BEGIN_NAMESPACE
54 
QAndroidPlatformVulkanWindow(QWindow * window)55 QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
56     : QAndroidPlatformWindow(window),
57       m_nativeSurfaceId(-1),
58       m_nativeWindow(nullptr),
59       m_vkSurface(0),
60       m_createVkSurface(nullptr),
61       m_destroyVkSurface(nullptr)
62 {
63 }
64 
~QAndroidPlatformVulkanWindow()65 QAndroidPlatformVulkanWindow::~QAndroidPlatformVulkanWindow()
66 {
67     m_surfaceWaitCondition.wakeOne();
68     lockSurface();
69     if (m_nativeSurfaceId != -1)
70         QtAndroid::destroySurface(m_nativeSurfaceId);
71     clearSurface();
72     unlockSurface();
73 }
74 
setGeometry(const QRect & rect)75 void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
76 {
77     if (rect == geometry())
78         return;
79 
80     m_oldGeometry = geometry();
81 
82     QAndroidPlatformWindow::setGeometry(rect);
83     if (m_nativeSurfaceId != -1)
84         QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
85 
86     QRect availableGeometry = screen()->availableGeometry();
87     if (rect.width() > 0
88             && rect.height() > 0
89             && availableGeometry.width() > 0
90             && availableGeometry.height() > 0) {
91         QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
92     }
93 
94     if (rect.topLeft() != m_oldGeometry.topLeft())
95         repaint(QRegion(rect));
96 }
97 
applicationStateChanged(Qt::ApplicationState state)98 void QAndroidPlatformVulkanWindow::applicationStateChanged(Qt::ApplicationState state)
99 {
100     QAndroidPlatformWindow::applicationStateChanged(state);
101     if (state <= Qt::ApplicationHidden) {
102         lockSurface();
103         if (m_nativeSurfaceId != -1) {
104             QtAndroid::destroySurface(m_nativeSurfaceId);
105             m_nativeSurfaceId = -1;
106         }
107         clearSurface();
108         unlockSurface();
109     }
110 }
111 
format() const112 QSurfaceFormat QAndroidPlatformVulkanWindow::format() const
113 {
114     return window()->requestedFormat();
115 }
116 
clearSurface()117 void QAndroidPlatformVulkanWindow::clearSurface()
118 {
119     if (m_vkSurface && m_destroyVkSurface) {
120         m_destroyVkSurface(window()->vulkanInstance()->vkInstance(), m_vkSurface, nullptr);
121         m_vkSurface = 0;
122     }
123 
124     if (m_nativeWindow) {
125         ANativeWindow_release(m_nativeWindow);
126         m_nativeWindow = nullptr;
127     }
128 }
129 
sendExpose()130 void QAndroidPlatformVulkanWindow::sendExpose()
131 {
132     QRect availableGeometry = screen()->availableGeometry();
133     if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
134         QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
135 }
136 
surfaceChanged(JNIEnv * jniEnv,jobject surface,int w,int h)137 void QAndroidPlatformVulkanWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
138 {
139     Q_UNUSED(jniEnv);
140     Q_UNUSED(w);
141     Q_UNUSED(h);
142 
143     lockSurface();
144     m_androidSurfaceObject = surface;
145     if (surface)
146         m_surfaceWaitCondition.wakeOne();
147     unlockSurface();
148 
149     if (surface)
150         sendExpose();
151 }
152 
vkSurface()153 VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
154 {
155     if (QAndroidEventDispatcherStopper::stopped())
156         return &m_vkSurface;
157 
158     bool needsExpose = false;
159     if (!m_vkSurface) {
160         clearSurface();
161 
162         QMutexLocker lock(&m_surfaceMutex);
163         if (m_nativeSurfaceId == -1) {
164             AndroidDeadlockProtector protector;
165             if (!protector.acquire())
166                 return &m_vkSurface;
167             const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
168             m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
169             m_surfaceWaitCondition.wait(&m_surfaceMutex);
170         }
171 
172         if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
173             return &m_vkSurface;
174 
175         QJNIEnvironmentPrivate env;
176         m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object());
177 
178         VkAndroidSurfaceCreateInfoKHR surfaceInfo;
179         memset(&surfaceInfo, 0, sizeof(surfaceInfo));
180         surfaceInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
181         surfaceInfo.window = m_nativeWindow;
182         QVulkanInstance *inst = window()->vulkanInstance();
183         if (!inst) {
184             qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
185             return &m_vkSurface;
186         }
187         if (!m_createVkSurface) {
188             m_createVkSurface = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
189                         inst->getInstanceProcAddr("vkCreateAndroidSurfaceKHR"));
190         }
191         if (!m_destroyVkSurface) {
192             m_destroyVkSurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
193                         inst->getInstanceProcAddr("vkDestroySurfaceKHR"));
194         }
195         VkResult err = m_createVkSurface(inst->vkInstance(), &surfaceInfo, nullptr, &m_vkSurface);
196         if (err != VK_SUCCESS)
197             qWarning("Failed to create Android VkSurface: %d", err);
198 
199         needsExpose = true;
200     }
201 
202     if (needsExpose)
203         sendExpose();
204 
205     return &m_vkSurface;
206 }
207 
208 QT_END_NAMESPACE
209