// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qopenxrgraphics_opengles_p.h" #include "qopenxrhelpers_p.h" #include #include #include QT_BEGIN_NAMESPACE #ifndef GL_DEPTH_COMPONENT32F #define GL_DEPTH_COMPONENT32F 0x8CAC #endif QOpenXRGraphicsOpenGLES::QOpenXRGraphicsOpenGLES() { #ifdef XR_USE_PLATFORM_ANDROID m_graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR; #endif m_graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR; } bool QOpenXRGraphicsOpenGLES::isExtensionSupported(const QVector &extensions) const { for (const auto &extension : extensions) { if (!strcmp(XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, extension.extensionName)) return true; } return false; } const char *QOpenXRGraphicsOpenGLES::extensionName() const { return XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME; } const XrBaseInStructure *QOpenXRGraphicsOpenGLES::handle() const { return reinterpret_cast(&m_graphicsBinding); } bool QOpenXRGraphicsOpenGLES::setupGraphics(const XrInstance &instance, XrSystemId &systemId, const QQuickGraphicsConfiguration &) { // Extension function must be loaded by name PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = nullptr; OpenXRHelpers::checkXrResult(xrGetInstanceProcAddr(instance, "xrGetOpenGLESGraphicsRequirementsKHR", reinterpret_cast(&pfnGetOpenGLESGraphicsRequirementsKHR)), instance); if (!pfnGetOpenGLESGraphicsRequirementsKHR) { qWarning("Could not resolve xrGetOpenGLESGraphicsRequirementsKHR; perhaps the OpenXR implementation does not support OpenGL ES?"); return false; } OpenXRHelpers::checkXrResult(pfnGetOpenGLESGraphicsRequirementsKHR(instance, systemId, &m_graphicsRequirements), instance); return true; } bool QOpenXRGraphicsOpenGLES::finializeGraphics(QRhi *rhi) { const QRhiGles2NativeHandles *openglRhi = static_cast(rhi->nativeHandles()); auto context = openglRhi->context; const XrVersion desiredApiVersion = XR_MAKE_VERSION(context->format().majorVersion(), context->format().minorVersion(), 0); if (m_graphicsRequirements.minApiVersionSupported > desiredApiVersion) { qWarning("Qt Quick 3D XR (Open GL ES): Runtime does not support desired graphics API and/or version"); return false; } #ifdef XR_USE_PLATFORM_ANDROID auto nativeContext = context->nativeInterface(); if (nativeContext) { m_graphicsBinding.display = nativeContext->display(); m_graphicsBinding.config = nativeContext->config(); m_graphicsBinding.context = nativeContext->nativeContext(); } #endif m_rhi = rhi; return true; } int64_t QOpenXRGraphicsOpenGLES::colorSwapchainFormat(const QVector &swapchainFormats) const { // List of supported color swapchain formats. constexpr int64_t supportedColorSwapchainFormats[] = { GL_SRGB8_ALPHA8_EXT, GL_RGBA8_SNORM }; auto swapchainFormatIt = std::find_first_of(std::begin(supportedColorSwapchainFormats), std::end(supportedColorSwapchainFormats), swapchainFormats.begin(), swapchainFormats.end()); return *swapchainFormatIt; } int64_t QOpenXRGraphicsOpenGLES::depthSwapchainFormat(const QVector &swapchainFormats) const { // in order of preference constexpr int64_t supportedDepthSwapchainFormats[] = { GL_DEPTH24_STENCIL8_OES, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT24_OES, GL_DEPTH_COMPONENT16_OES }; return *std::find_first_of(std::begin(supportedDepthSwapchainFormats), std::end(supportedDepthSwapchainFormats), swapchainFormats.begin(), swapchainFormats.end()); } QVector QOpenXRGraphicsOpenGLES::allocateSwapchainImages(int count, XrSwapchain swapchain) { QVector swapchainImages; QVector swapchainImageBuffer(count); for (XrSwapchainImageOpenGLESKHR& image : swapchainImageBuffer) { image.type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; swapchainImages.push_back(reinterpret_cast(&image)); } m_swapchainImageBuffer.insert(swapchain, swapchainImageBuffer); return swapchainImages; } QQuickRenderTarget QOpenXRGraphicsOpenGLES::renderTarget(const XrSwapchainSubImage &subImage, const XrSwapchainImageBaseHeader *swapchainImage, quint64 swapchainFormat, int samples, int arraySize, const XrSwapchainImageBaseHeader *depthSwapchainImage, quint64 depthSwapchainFormat) const { const uint32_t colorTexture = reinterpret_cast(swapchainImage)->image; switch (swapchainFormat) { case GL_SRGB8_ALPHA8_EXT: swapchainFormat = GL_RGBA8_OES; break; default: break; } QQuickRenderTarget::Flags flags; if (samples > 1) flags |= QQuickRenderTarget::Flag::MultisampleResolve; const QSize pixelSize(subImage.imageRect.extent.width, subImage.imageRect.extent.height); QQuickRenderTarget rt = QQuickRenderTarget::fromOpenGLTexture(colorTexture, swapchainFormat, pixelSize, samples, arraySize, flags); if (depthSwapchainImage) { QRhiTexture::Format format = QRhiTexture::D24S8; switch (depthSwapchainFormat) { case GL_DEPTH_COMPONENT32F: format = QRhiTexture::D32F; break; case GL_DEPTH_COMPONENT24_OES: format = QRhiTexture::D24; break; case GL_DEPTH_COMPONENT16_OES: format = QRhiTexture::D16; break; } GLuint depthImage = reinterpret_cast(depthSwapchainImage)->image; if (m_depthTexture && (m_depthTexture->format() != format || m_depthTexture->pixelSize() != pixelSize || m_depthTexture->arraySize() != arraySize)) { delete m_depthTexture; m_depthTexture = nullptr; } if (!m_depthTexture) { // this is never multisample, QQuickRt takes care of resolving depth-stencil if (arraySize > 1) m_depthTexture = m_rhi->newTextureArray(format, arraySize, pixelSize, 1, QRhiTexture::RenderTarget); else m_depthTexture = m_rhi->newTexture(format, pixelSize, 1, QRhiTexture::RenderTarget); } m_depthTexture->createFrom({ depthImage, 0 }); rt.setDepthTexture(m_depthTexture); } return rt; } void QOpenXRGraphicsOpenGLES::releaseResources() { delete m_depthTexture; m_depthTexture = nullptr; } QT_END_NAMESPACE