diff options
Diffstat (limited to 'src/core/ozone/gl_context_qt.cpp')
-rw-r--r-- | src/core/ozone/gl_context_qt.cpp | 243 |
1 files changed, 225 insertions, 18 deletions
diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp index ab6c5ca8c..0042f2bce 100644 --- a/src/core/ozone/gl_context_qt.cpp +++ b/src/core/ozone/gl_context_qt.cpp @@ -8,10 +8,21 @@ #include <QThread> #include <QtGui/private/qtgui-config_p.h> #include <qpa/qplatformnativeinterface.h> -#include "ui/gl/gl_context_egl.h" + #include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface.h" + +#if defined(USE_OZONE) +#include "ui/gl/egl_util.h" -#if defined(OS_WIN) +#include <QOpenGLFunctions> +#include <QOffscreenSurface> + +#include <vector> +#endif + +#if BUILDFLAG(IS_WIN) +#include "ui/gl/gl_context_egl.h" #include "ui/gl/gl_context_wgl.h" #endif @@ -27,7 +38,8 @@ inline void *resourceForContext(const QByteArray &resource) #if QT_CONFIG(opengl) QOpenGLContext *shareContext = qt_gl_global_share_context(); if (!shareContext) { - qFatal("QWebEngine: OpenGL resource sharing is not set up in QtQuick. Please make sure to call QtWebEngineCore::initialize() in your main() function."); + qFatal("QWebEngine: OpenGL resource sharing is not set up in QtQuick. Please make sure to " + "call QtWebEngineQuick::initialize() in your main() function."); } return qApp->platformNativeInterface()->nativeResourceForContext(resource, shareContext); #else @@ -94,7 +106,7 @@ void* GLContextHelper::getGlXConfig() void* GLContextHelper::getEGLDisplay() { -#ifdef Q_OS_WIN +#if BUILDFLAG(IS_WIN) // Windows QPA plugin does not implement resourceForIntegration for "egldisplay". // Use resourceForContext instead. return resourceForContext(QByteArrayLiteral("egldisplay")); @@ -163,33 +175,228 @@ bool GLContextHelper::isCreateContextRobustnessSupported() return contextHelper->m_robustness; } +#if QT_CONFIG(opengl) && defined(USE_OZONE) +class ScopedGLContext +{ +public: + ScopedGLContext() + : m_context(new QOpenGLContext()) + , m_previousContext(gl::GLContext::GetCurrent()) + , m_previousSurface(gl::GLSurface::GetCurrent()) + { + if (!m_context->create()) { + qWarning("Failed to create OpenGL context."); + return; + } + + QOffscreenSurface *surface = new QOffscreenSurface(m_context->screen(), m_context.get()); + surface->create(); + Q_ASSERT(surface->isValid()); + + if (!m_context->makeCurrent(surface)) { + qWarning("Failed to make OpenGL context current."); + return; + } + } + + ~ScopedGLContext() + { + if (!m_textures.empty()) { + auto glFun = m_context->functions(); + glFun->glDeleteTextures(m_textures.size(), m_textures.data()); + } + + if (m_previousContext) + m_previousContext->MakeCurrent(m_previousSurface); + } + + bool isValid() const { return m_context->isValid() && (m_context->surface() != nullptr); } + + EGLDisplay eglDisplay() const + { + QNativeInterface::QEGLContext *nativeInterface = + m_context->nativeInterface<QNativeInterface::QEGLContext>(); + return nativeInterface->display(); + } + + EGLContext eglContext() const + { + QNativeInterface::QEGLContext *nativeInterface = + m_context->nativeInterface<QNativeInterface::QEGLContext>(); + return nativeInterface->nativeContext(); + } + + uint createTexture(int width, int height) + { + auto glFun = m_context->functions(); + + uint glTexture; + glFun->glGenTextures(1, &glTexture); + glFun->glBindTexture(GL_TEXTURE_2D, glTexture); + glFun->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + NULL); + glFun->glBindTexture(GL_TEXTURE_2D, 0); + + m_textures.push_back(glTexture); + return glTexture; + } + +private: + QScopedPointer<QOpenGLContext> m_context; + gl::GLContext *m_previousContext; + gl::GLSurface *m_previousSurface; + std::vector<uint> m_textures; +}; + +EGLHelper::EGLFunctions::EGLFunctions() +{ + const static auto getProcAddress = + reinterpret_cast<gl::GLGetProcAddressProc>(GLContextHelper::getEglGetProcAddress()); + + eglCreateImage = reinterpret_cast<PFNEGLCREATEIMAGEPROC>(getProcAddress("eglCreateImage")); + eglDestroyImage = reinterpret_cast<PFNEGLDESTROYIMAGEPROC>(getProcAddress("eglDestroyImage")); + eglGetError = reinterpret_cast<PFNEGLGETERRORPROC>(getProcAddress("eglGetError")); + eglExportDMABUFImageMESA = reinterpret_cast<PFNEGLEXPORTDMABUFIMAGEMESAPROC>( + getProcAddress("eglExportDMABUFImageMESA")); + eglExportDMABUFImageQueryMESA = reinterpret_cast<PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC>( + getProcAddress("eglExportDMABUFImageQueryMESA")); + eglQueryString = reinterpret_cast<PFNEGLQUERYSTRINGPROC>(getProcAddress("eglQueryString")); +} + +EGLHelper *EGLHelper::instance() +{ + static EGLHelper eglHelper; + return &eglHelper; +} + +EGLHelper::EGLHelper() : m_functions(new EGLHelper::EGLFunctions()) +{ + const char *extensions = m_functions->eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!extensions) { + qWarning("EGL: Failed to query EGL extensions."); + return; + } + + if (strstr(extensions, "EGL_KHR_base_image")) { + qWarning("EGL: EGL_KHR_base_image extension is not supported."); + return; + } + + auto eglDisplay = GLContextHelper::getEGLDisplay(); + if (!eglDisplay) { + qWarning("EGL: No EGL display."); + return; + } + + const char *displayExtensions = m_functions->eglQueryString(eglDisplay, EGL_EXTENSIONS); + m_isDmaBufSupported = strstr(displayExtensions, "EGL_EXT_image_dma_buf_import") + && strstr(displayExtensions, "EGL_EXT_image_dma_buf_import_modifiers") + && strstr(displayExtensions, "EGL_MESA_image_dma_buf_export"); + + if (m_isDmaBufSupported) { + // FIXME: This disables GBM for nvidia. Remove this when nvidia fixes its GBM support. + // + // "Buffer allocation and submission to DRM KMS using gbm is not currently supported." + // See: https://download.nvidia.com/XFree86/Linux-x86_64/550.40.07/README/kms.html + // + // Chromium uses GBM to allocate scanout buffers. Scanout requires DRM KMS. If KMS is + // enabled, gbm_device and gbm_buffer are created without any issues but rendering to the + // buffer will malfunction. It is not known how to detect this problem before rendering + // so we just disable GBM for nvidia. + const char *displayVendor = m_functions->eglQueryString(eglDisplay, EGL_VENDOR); + m_isDmaBufSupported = !strstr(displayVendor, "NVIDIA"); + } +} + +void EGLHelper::queryDmaBuf(const int width, const int height, int *fd, int *stride, int *offset, + uint64_t *modifiers) +{ + if (!m_isDmaBufSupported) + return; + + ScopedGLContext context; + if (!context.isValid()) + return; + + EGLDisplay eglDisplay = context.eglDisplay(); + EGLContext eglContext = context.eglContext(); + if (!eglContext) { + qWarning("EGL: No EGLContext."); + return; + } + + uint64_t textureId = context.createTexture(width, height); + EGLImage eglImage = m_functions->eglCreateImage(eglDisplay, eglContext, EGL_GL_TEXTURE_2D, + (EGLClientBuffer)textureId, NULL); + if (eglImage == EGL_NO_IMAGE) { + qWarning() << "EGL: Failed to create EGLImage:" + << ui::GetEGLErrorString(m_functions->eglGetError()); + return; + } + + int numPlanes = 0; + if (!m_functions->eglExportDMABUFImageQueryMESA(eglDisplay, eglImage, nullptr, &numPlanes, + modifiers)) + qWarning() << "EGL: Failed to retrieve the pixel format of the buffer:" + << ui::GetEGLErrorString(m_functions->eglGetError()); + Q_ASSERT(numPlanes == 1); + + if (!m_functions->eglExportDMABUFImageMESA(eglDisplay, eglImage, fd, stride, offset)) + qWarning() << "EGL: Failed to retrieve the dma_buf file descriptor:" + << ui::GetEGLErrorString(m_functions->eglGetError()); + + m_functions->eglDestroyImage(eglDisplay, eglImage); +} + +bool EGLHelper::isDmaBufSupported() +{ + if (!m_isDmaBufSupported) + return false; + + int fd = -1; + queryDmaBuf(2, 2, &fd, nullptr, nullptr, nullptr); + if (fd == -1) { + m_isDmaBufSupported = false; + return false; + } + + close(fd); + return true; +} +#endif // QT_CONFIG(opengl) && defined(USE_OZONE) + QT_END_NAMESPACE -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) namespace gl { namespace init { -scoped_refptr<GLContext> CreateGLContext(GLShareGroup* share_group, - GLSurface* compatible_surface, - const GLContextAttribs& attribs) +scoped_refptr<GLContext> CreateGLContext(GLShareGroup *share_group, + GLSurface *compatible_surface, + const GLContextAttribs &attribs) { - scoped_refptr<GLContext> context; - if (GetGLImplementation() == kGLImplementationDesktopGL) { - context = new GLContextWGL(share_group); + switch (GetGLImplementation()) { + case kGLImplementationDesktopGLCoreProfile: + case kGLImplementationDesktopGL: { + scoped_refptr<GLContext> context = new GLContextWGL(share_group); if (!context->Initialize(compatible_surface, attribs)) return nullptr; return context; - } else { - context = new GLContextEGL(share_group); } - - if (!GLContextHelper::initializeContext(context.get(), compatible_surface, attribs)) + case kGLImplementationEGLANGLE: + case kGLImplementationEGLGLES2: + return InitializeGLContext(new GLContextEGL(share_group), + compatible_surface, attribs); + case kGLImplementationDisabled: return nullptr; - - return context; + default: + break; + } + Q_UNREACHABLE(); + return nullptr; } } // namespace init } // namespace gl -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) |