1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "vulkanserverbufferintegration.h"
5
6#include "vulkanwrapper.h"
7
8#include <QtOpenGL/QOpenGLTexture>
9#include <QtGui/QOpenGLContext>
10#include <QtGui/QOffscreenSurface>
11#include <QtGui/qopengl.h>
12
13#include <unistd.h>
14#include <fcntl.h>
15
16#include <QtCore/QDebug>
17
18QT_BEGIN_NAMESPACE
19static constexpr bool vsbiExtraDebug = false;
20
21#define DECL_GL_FUNCTION(name, type) \
22 type name
23
24#define FIND_GL_FUNCTION(name, type) \
25 do { \
26 name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \
27 if (!name) { \
28 qWarning() << "ERROR in GL proc lookup. Could not find " #name; \
29 return false; \
30 } \
31 } while (0)
32
33struct VulkanServerBufferGlFunctions
34{
35 DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
36 DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
37 //DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
38 DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
39 DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
40
41 bool init(QOpenGLContext *glContext)
42 {
43 FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC);
44 FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC);
45 //FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC);
46 FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC);
47 FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC);
48
49 return true;
50 }
51 static bool create(QOpenGLContext *glContext);
52};
53
54static VulkanServerBufferGlFunctions *funcs = nullptr;
55
56//RAII
57class CurrentContext
58{
59public:
60 CurrentContext()
61 {
62 if (!QOpenGLContext::currentContext()) {
63 if (QOpenGLContext::globalShareContext()) {
64 if (!localContext) {
65 localContext = new QOpenGLContext;
66 localContext->setShareContext(QOpenGLContext::globalShareContext());
67 localContext->create();
68 }
69 if (!offscreenSurface) {
70 offscreenSurface = new QOffscreenSurface;
71 offscreenSurface->setFormat(localContext->format());
72 offscreenSurface->create();
73 }
74 localContext->makeCurrent(surface: offscreenSurface);
75 localContextInUse = true;
76 } else {
77 qCritical(msg: "VulkanServerBufferIntegration: no globalShareContext");
78 }
79 }
80 }
81 ~CurrentContext()
82 {
83 if (localContextInUse)
84 localContext->doneCurrent();
85 }
86 QOpenGLContext *context() { return localContextInUse ? localContext : QOpenGLContext::currentContext(); }
87private:
88 static QOpenGLContext *localContext;
89 static QOffscreenSurface *offscreenSurface;
90 bool localContextInUse = false;
91};
92
93QOpenGLContext *CurrentContext::localContext = nullptr;
94QOffscreenSurface *CurrentContext::offscreenSurface = nullptr;
95
96bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext)
97{
98 if (funcs)
99 return true;
100 funcs = new VulkanServerBufferGlFunctions;
101 if (!funcs->init(glContext)) {
102 delete funcs;
103 funcs = nullptr;
104 return false;
105 }
106 return true;
107}
108
109VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format)
110 : QtWayland::ServerBuffer(qimage.size(),format)
111 , m_integration(integration)
112 , m_width(qimage.width())
113 , m_height(qimage.height())
114{
115 m_format = format;
116 switch (m_format) {
117 case RGBA32:
118 m_glInternalFormat = GL_RGBA8;
119 break;
120 // case A8:
121 // m_glInternalFormat = GL_R8;
122 // break;
123 default:
124 qWarning(msg: "VulkanServerBuffer: unsupported format");
125 m_glInternalFormat = GL_RGBA8;
126 break;
127 }
128
129 auto vulkanWrapper = m_integration->vulkanWrapper();
130 m_vImage = vulkanWrapper->createTextureImage(img: qimage);
131 if (m_vImage)
132 m_fd = vulkanWrapper->getImageInfo(imgWrapper: m_vImage, memSize: &m_memorySize);
133}
134
135VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, VulkanImageWrapper *vImage, uint glInternalFormat, const QSize &size)
136 : QtWayland::ServerBuffer(size, QtWayland::ServerBuffer::Custom)
137 , m_integration(integration)
138 , m_width(size.width())
139 , m_height(size.height())
140 , m_vImage(vImage)
141 , m_glInternalFormat(glInternalFormat)
142{
143 auto vulkanWrapper = m_integration->vulkanWrapper();
144 m_fd = vulkanWrapper->getImageInfo(imgWrapper: m_vImage, memSize: &m_memorySize);
145}
146
147VulkanServerBuffer::~VulkanServerBuffer()
148{
149 delete m_texture; //this is always nullptr for now
150 auto vulkanWrapper = m_integration->vulkanWrapper();
151 vulkanWrapper->freeTextureImage(imageWrapper: m_vImage);
152}
153
154struct ::wl_resource *VulkanServerBuffer::resourceForClient(struct ::wl_client *client)
155{
156 auto *bufferResource = resourceMap().value(client);
157 if (!bufferResource) {
158 auto integrationResource = m_integration->resourceMap().value(client);
159 if (!integrationResource) {
160 qWarning(msg: "VulkanServerBuffer::resourceForClient: Trying to get resource for ServerBuffer. But client is not bound to the vulkan interface");
161 return nullptr;
162 }
163 struct ::wl_resource *shm_integration_resource = integrationResource->handle;
164 Resource *resource = add(client, 1);
165 m_integration->send_server_buffer_created(shm_integration_resource, resource->handle, m_fd, m_width, m_height, m_memorySize, m_glInternalFormat);
166 return resource->handle;
167 }
168 return bufferResource->handle;
169}
170
171QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture()
172{
173 if (m_texture && m_texture->isCreated())
174 return m_texture;
175
176 CurrentContext current;
177
178 if (!funcs && !VulkanServerBufferGlFunctions::create(glContext: current.context()))
179 return nullptr;
180
181 funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject);
182 if (vsbiExtraDebug) qDebug() << "glCreateMemoryObjectsEXT" << Qt::hex << glGetError();
183
184
185 int dupfd = fcntl(fd: m_fd, F_DUPFD_CLOEXEC, 0);
186 if (dupfd < 0) {
187 perror(s: "VulkanServerBuffer::toOpenGlTexture() Could not dup fd:");
188 return nullptr;
189 }
190
191 funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, dupfd);
192 if (vsbiExtraDebug) qDebug() << "glImportMemoryFdEXT" << Qt::hex << glGetError();
193
194
195 if (!m_texture)
196 m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
197 m_texture->create();
198
199 GLuint texId = m_texture->textureId();
200 if (vsbiExtraDebug) qDebug() << "created texture" << texId << Qt::hex << glGetError();
201
202 m_texture->bind();
203 if (vsbiExtraDebug) qDebug() << "bound texture" << texId << Qt::hex << glGetError();
204 funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_glInternalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 );
205 if (vsbiExtraDebug) qDebug() << "glTexStorageMem2DEXT" << Qt::hex << glGetError();
206 if (vsbiExtraDebug) qDebug() << "format" << Qt::hex << m_glInternalFormat << GL_RGBA8;
207
208
209 return m_texture;
210}
211
212void VulkanServerBuffer::releaseOpenGlTexture()
213{
214 if (!m_texture || !m_texture->isCreated())
215 return;
216
217 CurrentContext current;
218 m_texture->destroy();
219 funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject);
220}
221
222
223bool VulkanServerBuffer::bufferInUse()
224{
225 return (m_texture && m_texture->isCreated()) || resourceMap().size() > 0;
226}
227
228void VulkanServerBuffer::server_buffer_release(Resource *resource)
229{
230 qCDebug(qLcWaylandCompositorHardwareIntegration) << "server_buffer RELEASE resource" << resource->handle << wl_resource_get_id(resource->handle) << "for client" << resource->client();
231 wl_resource_destroy(resource->handle);
232}
233
234VulkanServerBufferIntegration::VulkanServerBufferIntegration()
235{
236}
237
238VulkanServerBufferIntegration::~VulkanServerBufferIntegration()
239{
240}
241
242bool VulkanServerBufferIntegration::initializeHardware(QWaylandCompositor *compositor)
243{
244 Q_ASSERT(QGuiApplication::platformNativeInterface());
245
246 QtWaylandServer::zqt_vulkan_server_buffer_v1::init(compositor->display(), 1);
247 return true;
248}
249
250bool VulkanServerBufferIntegration::supportsFormat(QtWayland::ServerBuffer::Format format) const
251{
252 switch (format) {
253 case QtWayland::ServerBuffer::RGBA32:
254 return true;
255 case QtWayland::ServerBuffer::A8:
256 return false;
257 default:
258 return false;
259 }
260}
261
262QtWayland::ServerBuffer *VulkanServerBufferIntegration::createServerBufferFromImage(const QImage &qimage, QtWayland::ServerBuffer::Format format)
263{
264 if (!m_vulkanWrapper) {
265 CurrentContext current;
266 m_vulkanWrapper = new VulkanWrapper(current.context());
267 }
268 return new VulkanServerBuffer(this, qimage, format);
269}
270
271QtWayland::ServerBuffer *
272VulkanServerBufferIntegration::createServerBufferFromData(QByteArrayView view, const QSize &size,
273 uint glInternalFormat)
274{
275 if (!m_vulkanWrapper) {
276 CurrentContext current;
277 m_vulkanWrapper = new VulkanWrapper(current.context());
278 }
279
280 auto *vImage = m_vulkanWrapper->createTextureImageFromData(
281 pixels: reinterpret_cast<const uchar *>(view.constData()), bufferSize: view.size(), size, glInternalFormat);
282
283 if (vImage)
284 return new VulkanServerBuffer(this, vImage, glInternalFormat, size);
285
286 qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not load compressed texture";
287 return nullptr;
288}
289
290QT_END_NAMESPACE
291

source code of qtwayland/src/hardwareintegration/compositor/vulkan-server/vulkanserverbufferintegration.cpp