1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "vulkanserverbufferintegration.h" |
5 | #include <QtWaylandClient/private/qwaylanddisplay_p.h> |
6 | #include <QDebug> |
7 | #include <QtOpenGL/QOpenGLTexture> |
8 | #include <QtGui/QOpenGLContext> |
9 | #include <QtGui/qopengl.h> |
10 | #include <QtGui/QImage> |
11 | #include <QtCore/QCoreApplication> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | namespace QtWaylandClient { |
16 | |
17 | static constexpr bool sbiExtraDebug = |
18 | #ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG |
19 | true; |
20 | #else |
21 | false; |
22 | #endif |
23 | |
24 | #define DECL_GL_FUNCTION(name, type) \ |
25 | type name |
26 | |
27 | #define FIND_GL_FUNCTION(name, type) \ |
28 | do { \ |
29 | name = reinterpret_cast<type>(glContext->getProcAddress(#name)); \ |
30 | if (!name) { \ |
31 | qWarning() << "ERROR in GL proc lookup. Could not find " #name; \ |
32 | return false; \ |
33 | } \ |
34 | } while (0) |
35 | |
36 | struct VulkanServerBufferGlFunctions |
37 | { |
38 | DECL_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC); |
39 | DECL_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC); |
40 | DECL_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC); |
41 | DECL_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC); |
42 | DECL_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC); |
43 | |
44 | bool init(QOpenGLContext *glContext) |
45 | { |
46 | FIND_GL_FUNCTION(glCreateMemoryObjectsEXT, PFNGLCREATEMEMORYOBJECTSEXTPROC); |
47 | FIND_GL_FUNCTION(glImportMemoryFdEXT, PFNGLIMPORTMEMORYFDEXTPROC); |
48 | FIND_GL_FUNCTION(glTextureStorageMem2DEXT, PFNGLTEXTURESTORAGEMEM2DEXTPROC); |
49 | FIND_GL_FUNCTION(glTexStorageMem2DEXT, PFNGLTEXSTORAGEMEM2DEXTPROC); |
50 | FIND_GL_FUNCTION(glDeleteMemoryObjectsEXT, PFNGLDELETEMEMORYOBJECTSEXTPROC); |
51 | |
52 | return true; |
53 | } |
54 | static bool create(QOpenGLContext *glContext); |
55 | }; |
56 | |
57 | static VulkanServerBufferGlFunctions *funcs = nullptr; |
58 | |
59 | bool VulkanServerBufferGlFunctions::create(QOpenGLContext *glContext) |
60 | { |
61 | if (funcs) |
62 | return true; |
63 | funcs = new VulkanServerBufferGlFunctions; |
64 | if (!funcs->init(glContext)) { |
65 | delete funcs; |
66 | funcs = nullptr; |
67 | return false; |
68 | } |
69 | return true; |
70 | } |
71 | |
72 | VulkanServerBuffer::VulkanServerBuffer(VulkanServerBufferIntegration *integration, struct ::qt_server_buffer *id, |
73 | int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) |
74 | : m_integration(integration) |
75 | , m_server_buffer(id) |
76 | , m_fd(fd) |
77 | , m_memorySize(memory_size) |
78 | , m_internalFormat(format) |
79 | { |
80 | m_size = QSize(width, height); |
81 | } |
82 | |
83 | VulkanServerBuffer::~VulkanServerBuffer() |
84 | { |
85 | if (QCoreApplication::closingDown()) |
86 | return; // can't trust anything at this point |
87 | |
88 | if (m_texture) { //only do gl cleanup if import has been called |
89 | m_integration->deleteGLTextureWhenPossible(texture: m_texture); |
90 | |
91 | if (sbiExtraDebug) qDebug() << "glDeleteMemoryObjectsEXT" << m_memoryObject; |
92 | funcs->glDeleteMemoryObjectsEXT(1, &m_memoryObject); |
93 | } |
94 | qt_server_buffer_release(m_server_buffer); |
95 | qt_server_buffer_destroy(m_server_buffer); |
96 | } |
97 | |
98 | void VulkanServerBuffer::import() |
99 | { |
100 | if (m_texture) |
101 | return; |
102 | |
103 | if (sbiExtraDebug) qDebug() << "importing" << m_fd << Qt::hex << glGetError(); |
104 | |
105 | auto *glContext = QOpenGLContext::currentContext(); |
106 | if (!glContext) |
107 | return; |
108 | |
109 | if (!funcs && !VulkanServerBufferGlFunctions::create(glContext)) |
110 | return; |
111 | |
112 | funcs->glCreateMemoryObjectsEXT(1, &m_memoryObject); |
113 | if (sbiExtraDebug) qDebug() << "glCreateMemoryObjectsEXT" << Qt::hex << glGetError(); |
114 | funcs->glImportMemoryFdEXT(m_memoryObject, m_memorySize, GL_HANDLE_TYPE_OPAQUE_FD_EXT, m_fd); |
115 | if (sbiExtraDebug) qDebug() << "glImportMemoryFdEXT" << Qt::hex << glGetError(); |
116 | |
117 | |
118 | m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); |
119 | m_texture->create(); |
120 | |
121 | if (sbiExtraDebug) qDebug() << "created texture" << m_texture->textureId() << Qt::hex << glGetError(); |
122 | |
123 | m_texture->bind(); |
124 | if (sbiExtraDebug) qDebug() << "bound texture" << Qt::hex << glGetError(); |
125 | funcs->glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, m_internalFormat, m_size.width(), m_size.height(), m_memoryObject, 0 ); |
126 | if (sbiExtraDebug) qDebug() << "glTexStorageMem2DEXT" << Qt::hex << glGetError(); |
127 | if (sbiExtraDebug) qDebug() << "format" << Qt::hex << m_internalFormat << GL_RGBA8; |
128 | } |
129 | |
130 | QOpenGLTexture *VulkanServerBuffer::toOpenGlTexture() |
131 | { |
132 | m_integration->deleteOrphanedTextures(); |
133 | if (!m_texture) |
134 | import(); |
135 | return m_texture; |
136 | } |
137 | |
138 | void VulkanServerBufferIntegration::initialize(QWaylandDisplay *display) |
139 | { |
140 | m_display = display; |
141 | display->addRegistryListener(listener: &wlDisplayHandleGlobal, data: this); |
142 | } |
143 | |
144 | QWaylandServerBuffer *VulkanServerBufferIntegration::serverBuffer(struct qt_server_buffer *buffer) |
145 | { |
146 | return static_cast<QWaylandServerBuffer *>(qt_server_buffer_get_user_data(buffer)); |
147 | } |
148 | |
149 | void VulkanServerBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) |
150 | { |
151 | Q_UNUSED(version); |
152 | if (interface == "zqt_vulkan_server_buffer_v1" ) { |
153 | auto *integration = static_cast<VulkanServerBufferIntegration *>(data); |
154 | integration->QtWayland::zqt_vulkan_server_buffer_v1::init(registry, id, 1); |
155 | } |
156 | } |
157 | |
158 | void VulkanServerBufferIntegration::zqt_vulkan_server_buffer_v1_server_buffer_created(qt_server_buffer *id, int32_t fd, uint32_t width, uint32_t height, uint32_t memory_size, uint32_t format) |
159 | { |
160 | if (sbiExtraDebug) qDebug() << "vulkan_server_buffer_server_buffer_created" << fd; |
161 | auto *server_buffer = new VulkanServerBuffer(this, id, fd, width, height, memory_size, format); |
162 | qt_server_buffer_set_user_data(id, server_buffer); |
163 | } |
164 | |
165 | void VulkanServerBufferIntegration::deleteOrphanedTextures() |
166 | { |
167 | if (!QOpenGLContext::currentContext()) { |
168 | qWarning(msg: "VulkanServerBufferIntegration::deleteOrphanedTextures with no current context!" ); |
169 | return; |
170 | } |
171 | qDeleteAll(c: orphanedTextures); |
172 | orphanedTextures.clear(); |
173 | } |
174 | |
175 | } |
176 | |
177 | QT_END_NAMESPACE |
178 | |