Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvivantetextureimporter_p.h"
#include <QtMultimedia/private/qvideotexturehelper_p.h>
#include <QtGui/rhi/qrhi.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qscopeguard.h>
#include <EGL/egl.h>
static_assert(QT_CONFIG(gpu_vivante));
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(qLVivante, "qt.multimedia.vivante");
namespace QtMultimediaPrivate {
QVivanteGLFunctions::QVivanteGLFunctions()
{
m_glTexDirectVIVMap = reinterpret_cast<PFNGLTEXDIRECTVIVMAPPROC>(
eglGetProcAddress("glTexDirectVIVMap"));
m_glTexDirectInvalidateVIV = reinterpret_cast<PFNGLTEXDIRECTINVALIDATEVIVPROC>(
eglGetProcAddress("glTexDirectInvalidateVIV"));
}
const QVivanteGLFunctions &QVivanteGLFunctions::instance()
{
static const QVivanteGLFunctions singleton;
return singleton;
}
bool QVivanteGLFunctions::isValid() const
{
return m_glTexDirectVIVMap && m_glTexDirectInvalidateVIV;
}
void QVivanteGLFunctions::glTexDirectVIVMap(GLenum target, GLsizei width, GLsizei height,
GLenum format, GLvoid **logical,
const GLuint *physical) const
{
m_glTexDirectVIVMap(target, width, height, format, logical, physical);
}
void QVivanteGLFunctions::glTexDirectInvalidateVIV(GLenum target) const
{
m_glTexDirectInvalidateVIV(target);
}
VivanteContext::VivanteContext(QRhi *rhi)
{
if (!rhi || rhi->backend() != QRhi::OpenGLES2) {
qCDebug(qLVivante) << "VivanteContext: no rhi or non OpenGLES2 based RHI";
return;
}
auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
m_glContext = nativeHandles->context;
if (!m_glContext) {
qCDebug(qLVivante) << " no GL context, disabling";
return;
}
if (!QVivanteGLFunctions::instance().isValid()) {
qCDebug(qLVivante)
<< " glTexDirectVIVMap/glTexDirectInvalidateVIV not resolved, disabling";
return;
}
m_valid = true;
}
VivanteTextureHandles::VivanteTextureHandles(QRhi &rhi, QOpenGLContext *glContext, GLuint texture,
std::shared_ptr<void> parentKeepAlive)
: m_parentKeepAlive(std::move(parentKeepAlive)),
m_rhi(rhi),
m_glContext(glContext),
m_texture(texture)
{
rhi.addCleanupCallback(this, [this](QRhi *rhi) {
release(*rhi);
});
}
VivanteTextureHandles::~VivanteTextureHandles()
{
if (!m_released) {
m_rhi.removeCleanupCallback(this);
release(m_rhi);
}
}
void VivanteTextureHandles::release(QRhi &rhi)
{
rhi.makeThreadLocalNativeContextCurrent();
QOpenGLFunctions functions(m_glContext);
functions.glDeleteTextures(1, &m_texture);
m_released = true;
}
q23::expected<QVideoFrameTexturesHandlesUPtr, FailureSeverity>
importVivanteTexture(QRhi &rhi, const VivanteContext &context, quint32 physicalAddress,
void *logicalAddr, QSize size, GLenum vivFormatToken,
std::shared_ptr<void> parentKeepAlive)
{
qCDebug(qLVivante) << "Importing texture from Vivante phys-mem buffer";
QOpenGLContext *glContext = context.glContext();
QOpenGLFunctions functions(glContext);
const auto &vivFunctions = QVivanteGLFunctions::instance();
rhi.makeThreadLocalNativeContextCurrent();
GLuint texture = 0;
functions.glGenTextures(1, &texture);
auto releaseTexture = qScopeGuard([&] {
functions.glDeleteTextures(1, &texture);
});
functions.glActiveTexture(GL_TEXTURE0);
functions.glBindTexture(GL_TEXTURE_2D, texture);
// Each QVideoFrame maps a fresh address, so glTexDirectVIVMap (map the
// address into the texture) and glTexDirectInvalidateVIV (tell the
// driver the content changed) are both called unconditionally on every
// import.
const GLuint physical = physicalAddress;
vivFunctions.glTexDirectVIVMap(GL_TEXTURE_2D, size.width(), size.height(), vivFormatToken,
&logicalAddr, &physical);
if (const GLenum error = functions.glGetError(); error != GL_NO_ERROR) {
qCWarning(qLVivante) << "glTexDirectVIVMap failed with GL error" << Qt::hex << error
<< "for format token" << vivFormatToken << Qt::dec << "size" << size;
functions.glBindTexture(GL_TEXTURE_2D, 0);
return q23::unexpected{ FailureSeverity::recoverable };
}
vivFunctions.glTexDirectInvalidateVIV(GL_TEXTURE_2D);
if (const GLenum error = functions.glGetError(); error != GL_NO_ERROR) {
qCWarning(qLVivante) << "glTexDirectInvalidateVIV failed with GL error" << Qt::hex
<< error;
functions.glBindTexture(GL_TEXTURE_2D, 0);
return q23::unexpected{ FailureSeverity::recoverable };
}
functions.glBindTexture(GL_TEXTURE_2D, 0);
releaseTexture.dismiss();
return std::make_unique<VivanteTextureHandles>(rhi, glContext, texture,
std::move(parentKeepAlive));
}
} // namespace QtMultimediaPrivate
QT_END_NAMESPACE