1 | // Copyright (C) 2024 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qopenxrgraphics_opengl_p.h" |
5 | #include "qopenxrhelpers_p.h" |
6 | |
7 | #include <QtGui/QOpenGLContext> |
8 | #include <QtQuick/QQuickWindow> |
9 | #include <QtQuick/private/qquickrendertarget_p.h> |
10 | |
11 | #include <rhi/qrhi.h> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | #ifndef GL_RGBA8 |
16 | #define GL_RGBA8 0x8058 |
17 | #endif |
18 | |
19 | #ifndef GL_SRGB8_ALPHA8_EXT |
20 | #define GL_SRGB8_ALPHA8_EXT 0x8C43 |
21 | #endif |
22 | |
23 | #ifndef GL_DEPTH_COMPONENT16 |
24 | #define GL_DEPTH_COMPONENT16 0x81A5 |
25 | #endif |
26 | |
27 | #ifndef GL_DEPTH_COMPONENT24 |
28 | #define GL_DEPTH_COMPONENT24 0x81A6 |
29 | #endif |
30 | |
31 | #ifndef GL_DEPTH_COMPONENT32F |
32 | #define GL_DEPTH_COMPONENT32F 0x8CAC |
33 | #endif |
34 | |
35 | #ifndef GL_DEPTH24_STENCIL8 |
36 | #define GL_DEPTH24_STENCIL8 0x88F0 |
37 | #endif |
38 | |
39 | QOpenXRGraphicsOpenGL::QOpenXRGraphicsOpenGL() |
40 | { |
41 | #ifdef XR_USE_PLATFORM_WIN32 |
42 | m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; |
43 | #elif defined(XR_USE_PLATFORM_XLIB) |
44 | m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; |
45 | #elif defined(XR_USE_PLATFORM_XCB) |
46 | m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XCB_KHR; |
47 | #elif defined(XR_USE_PLATFORM_WAYLAND) |
48 | m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WAYLAND_KHR; |
49 | #endif |
50 | |
51 | m_graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR; |
52 | } |
53 | |
54 | |
55 | bool QOpenXRGraphicsOpenGL::isExtensionSupported(const QVector<XrExtensionProperties> &extensions) const |
56 | { |
57 | for (const auto &extension : extensions) { |
58 | if (!strcmp(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, |
59 | s2: extension.extensionName)) |
60 | return true; |
61 | } |
62 | return false; |
63 | } |
64 | |
65 | |
66 | const char *QOpenXRGraphicsOpenGL::extensionName() const |
67 | { |
68 | return XR_KHR_OPENGL_ENABLE_EXTENSION_NAME; |
69 | } |
70 | |
71 | |
72 | const XrBaseInStructure *QOpenXRGraphicsOpenGL::handle() const |
73 | { |
74 | return reinterpret_cast<const XrBaseInStructure*>(&m_graphicsBinding); |
75 | } |
76 | |
77 | |
78 | bool QOpenXRGraphicsOpenGL::setupGraphics(const XrInstance &instance, XrSystemId &systemId, const QQuickGraphicsConfiguration &) |
79 | { |
80 | // Extension function must be loaded by name |
81 | PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = nullptr; |
82 | OpenXRHelpers::checkXrResult(result: xrGetInstanceProcAddr(instance, name: "xrGetOpenGLGraphicsRequirementsKHR" , |
83 | function: reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetOpenGLGraphicsRequirementsKHR)), |
84 | instance); |
85 | if (!pfnGetOpenGLGraphicsRequirementsKHR) { |
86 | qWarning(msg: "Could not resolve xrGetOpenGLGraphicsRequirementsKHR; perhaps the OpenXR implementation does not support OpenGL?" ); |
87 | return false; |
88 | } |
89 | OpenXRHelpers::checkXrResult(result: pfnGetOpenGLGraphicsRequirementsKHR(instance, systemId, &m_graphicsRequirements), |
90 | instance); |
91 | return true; |
92 | } |
93 | |
94 | bool QOpenXRGraphicsOpenGL::finializeGraphics(QRhi *rhi) |
95 | { |
96 | const QRhiGles2NativeHandles *openglRhi = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); |
97 | |
98 | auto context = openglRhi->context; |
99 | |
100 | const XrVersion desiredApiVersion = XR_MAKE_VERSION(context->format().majorVersion(), context->format().minorVersion(), 0); |
101 | if (m_graphicsRequirements.minApiVersionSupported > desiredApiVersion) { |
102 | qWarning(msg: "Qt Quick 3D XR (OpenGL): Runtime does not support desired graphics API and/or version" ); |
103 | return false; |
104 | } |
105 | |
106 | # ifdef XR_USE_PLATFORM_WIN32 |
107 | auto nativeContext = context->nativeInterface<QNativeInterface::QWGLContext>(); |
108 | if (nativeContext) { |
109 | m_graphicsBinding.hGLRC = nativeContext->nativeContext(); |
110 | m_graphicsBinding.hDC = GetDC(reinterpret_cast<HWND>(m_window->winId())); |
111 | } |
112 | # endif |
113 | |
114 | m_rhi = rhi; |
115 | |
116 | return true; |
117 | } |
118 | |
119 | |
120 | int64_t QOpenXRGraphicsOpenGL::colorSwapchainFormat(const QVector<int64_t> &swapchainFormats) const |
121 | { |
122 | // List of supported color swapchain formats. |
123 | constexpr int64_t supportedColorSwapchainFormats[] = { |
124 | GL_RGBA8, |
125 | GL_RGBA8_SNORM |
126 | }; |
127 | |
128 | auto swapchainFormatIt = std::find_first_of(first1: std::begin(arr: supportedColorSwapchainFormats), |
129 | last1: std::end(arr: supportedColorSwapchainFormats), |
130 | first2: swapchainFormats.begin(), |
131 | last2: swapchainFormats.end()); |
132 | return *swapchainFormatIt; |
133 | } |
134 | |
135 | int64_t QOpenXRGraphicsOpenGL::depthSwapchainFormat(const QVector<int64_t> &swapchainFormats) const |
136 | { |
137 | // in order of preference |
138 | constexpr int64_t supportedDepthSwapchainFormats[] = { |
139 | GL_DEPTH24_STENCIL8, |
140 | GL_DEPTH_COMPONENT32F, |
141 | GL_DEPTH_COMPONENT24, |
142 | GL_DEPTH_COMPONENT16 |
143 | }; |
144 | |
145 | return *std::find_first_of(first1: std::begin(arr: supportedDepthSwapchainFormats), |
146 | last1: std::end(arr: supportedDepthSwapchainFormats), |
147 | first2: swapchainFormats.begin(), |
148 | last2: swapchainFormats.end()); |
149 | } |
150 | |
151 | QVector<XrSwapchainImageBaseHeader*> QOpenXRGraphicsOpenGL::allocateSwapchainImages(int count, XrSwapchain swapchain) |
152 | { |
153 | QVector<XrSwapchainImageBaseHeader*> swapchainImages; |
154 | QVector<XrSwapchainImageOpenGLKHR> swapchainImageBuffer(count); |
155 | for (XrSwapchainImageOpenGLKHR& image : swapchainImageBuffer) { |
156 | image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; |
157 | swapchainImages.push_back(t: reinterpret_cast<XrSwapchainImageBaseHeader*>(&image)); |
158 | } |
159 | m_swapchainImageBuffer.insert(key: swapchain, value: swapchainImageBuffer); |
160 | return swapchainImages; |
161 | } |
162 | |
163 | |
164 | QQuickRenderTarget QOpenXRGraphicsOpenGL::(const XrSwapchainSubImage &subImage, |
165 | const XrSwapchainImageBaseHeader *swapchainImage, |
166 | quint64 swapchainFormat, |
167 | int samples, |
168 | int arraySize, |
169 | const XrSwapchainImageBaseHeader *depthSwapchainImage, |
170 | quint64 depthSwapchainFormat) const |
171 | { |
172 | const uint32_t colorTexture = reinterpret_cast<const XrSwapchainImageOpenGLKHR*>(swapchainImage)->image; |
173 | |
174 | switch (swapchainFormat) { |
175 | case GL_SRGB8_ALPHA8_EXT: |
176 | swapchainFormat = GL_RGBA8; |
177 | break; |
178 | default: |
179 | break; |
180 | } |
181 | |
182 | QQuickRenderTarget::Flags flags; |
183 | if (samples > 1) |
184 | flags |= QQuickRenderTarget::Flag::MultisampleResolve; |
185 | |
186 | const QSize pixelSize(subImage.imageRect.extent.width, subImage.imageRect.extent.height); |
187 | QQuickRenderTarget rt = QQuickRenderTarget::fromOpenGLTexture(textureId: colorTexture, |
188 | format: swapchainFormat, |
189 | pixelSize, |
190 | sampleCount: samples, |
191 | arraySize, |
192 | flags); |
193 | if (depthSwapchainImage) { |
194 | QRhiTexture::Format format = QRhiTexture::D24S8; |
195 | switch (depthSwapchainFormat) { |
196 | case GL_DEPTH_COMPONENT32F: |
197 | format = QRhiTexture::D32F; |
198 | break; |
199 | case GL_DEPTH_COMPONENT24: |
200 | format = QRhiTexture::D24; |
201 | break; |
202 | case GL_DEPTH_COMPONENT16: |
203 | format = QRhiTexture::D16; |
204 | break; |
205 | } |
206 | GLuint depthImage = reinterpret_cast<const XrSwapchainImageOpenGLKHR*>(depthSwapchainImage)->image; |
207 | if (m_depthTexture && (m_depthTexture->format() != format || m_depthTexture->pixelSize() != pixelSize || m_depthTexture->arraySize() != arraySize)) { |
208 | delete m_depthTexture; |
209 | m_depthTexture = nullptr; |
210 | } |
211 | if (!m_depthTexture) { |
212 | // this is never multisample, QQuickRt takes care of resolving depth-stencil |
213 | if (arraySize > 1) |
214 | m_depthTexture = m_rhi->newTextureArray(format, arraySize, pixelSize, sampleCount: 1, flags: QRhiTexture::RenderTarget); |
215 | else |
216 | m_depthTexture = m_rhi->newTexture(format, pixelSize, sampleCount: 1, flags: QRhiTexture::RenderTarget); |
217 | } |
218 | m_depthTexture->createFrom(src: { .object: depthImage, .layout: 0 }); |
219 | rt.setDepthTexture(m_depthTexture); |
220 | } |
221 | return rt; |
222 | } |
223 | |
224 | void QOpenXRGraphicsOpenGL::setupWindow(QQuickWindow *window) |
225 | { |
226 | m_window = window; |
227 | } |
228 | |
229 | void QOpenXRGraphicsOpenGL::releaseResources() |
230 | { |
231 | delete m_depthTexture; |
232 | m_depthTexture = nullptr; |
233 | } |
234 | |
235 | QT_END_NAMESPACE |
236 | |