1 | // Copyright (C) 2024 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 "qopenglvideobuffer_p.h" |
5 | |
6 | #include <qoffscreensurface.h> |
7 | #include <qthread.h> |
8 | #include <private/qimagevideobuffer_p.h> |
9 | |
10 | #include <QtOpenGL/private/qopenglcompositor_p.h> |
11 | #include <QtOpenGL/private/qopenglframebufferobject_p.h> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | static QOpenGLContext *createContext(QOpenGLContext *shareContext) |
16 | { |
17 | // Create an OpenGL context for the current thread. The lifetime of the context is tied to the |
18 | // lifetime of the current thread. |
19 | auto context = std::make_unique<QOpenGLContext>(); |
20 | context->setShareContext(shareContext); |
21 | |
22 | if (!context->create()) { |
23 | qWarning() << "Couldn't create an OpenGL context for QOpenGLVideoBuffer"; |
24 | return nullptr; |
25 | } |
26 | |
27 | QObject::connect(sender: QThread::currentThread(), signal: &QThread::finished, |
28 | context: context.get(), slot: &QOpenGLContext::deleteLater); |
29 | return context.release(); |
30 | } |
31 | |
32 | static bool setCurrentOpenGLContext() |
33 | { |
34 | auto compositorContext = QOpenGLCompositor::instance()->context(); |
35 | |
36 | // A thread-local variable is used to avoid creating a new context if we're called on the same |
37 | // thread. The context lifetime is tied to the current thread lifetime (see createContext()). |
38 | static thread_local QOpenGLContext *context = nullptr; |
39 | static thread_local QOffscreenSurface *surface = nullptr; |
40 | |
41 | if (!context) { |
42 | context = (compositorContext->thread() == QThread::currentThread()) |
43 | ? compositorContext |
44 | : createContext(shareContext: compositorContext); |
45 | |
46 | if (!context) |
47 | return false; |
48 | |
49 | surface = new QOffscreenSurface(nullptr, context); |
50 | surface->setFormat(context->format()); |
51 | surface->create(); |
52 | } |
53 | |
54 | return context->makeCurrent(surface); |
55 | } |
56 | |
57 | QOpenGLVideoBuffer::QOpenGLVideoBuffer(std::unique_ptr<QOpenGLFramebufferObject> fbo) |
58 | : QHwVideoBuffer(QVideoFrame::RhiTextureHandle), m_fbo(std::move(fbo)) |
59 | { |
60 | Q_ASSERT(m_fbo); |
61 | } |
62 | |
63 | QOpenGLVideoBuffer::~QOpenGLVideoBuffer() { } |
64 | |
65 | QAbstractVideoBuffer::MapData QOpenGLVideoBuffer::map(QVideoFrame::MapMode mode) |
66 | { |
67 | return ensureImageBuffer().map(mode); |
68 | } |
69 | |
70 | void QOpenGLVideoBuffer::unmap() |
71 | { |
72 | if (m_imageBuffer) |
73 | m_imageBuffer->unmap(); |
74 | } |
75 | |
76 | quint64 QOpenGLVideoBuffer::textureHandle(QRhi *, int plane) const |
77 | { |
78 | Q_UNUSED(plane); |
79 | return m_fbo->texture(); |
80 | } |
81 | |
82 | QImageVideoBuffer &QOpenGLVideoBuffer::ensureImageBuffer() |
83 | { |
84 | // Create image buffer if not yet created. |
85 | // This is protected by mapMutex in QVideoFrame::map. |
86 | if (!m_imageBuffer) { |
87 | if (!setCurrentOpenGLContext()) |
88 | qWarning() << "Failed to set current OpenGL context"; |
89 | |
90 | m_imageBuffer = std::make_unique<QImageVideoBuffer>(args: m_fbo->toImage(flipped: false)); |
91 | } |
92 | |
93 | return *m_imageBuffer; |
94 | } |
95 | |
96 | QT_END_NAMESPACE |
97 |