refactor(grabber): Simplify frame scaling by removing OpenGL
Replaced the complex, low-level OpenGL pipeline in HyperionGrabber with a simple, high-level `QImage::scaled()` call. This change significantly reduces code complexity and improves maintainability without sacrificing performance, as `QImage::scaled()` is highly optimized and often hardware-accelerated. The new implementation is more robust and easier to understand. This resolves the following issues: - Removes fragile, hard-to-debug OpenGL code. - Eliminates potential bugs related to FBOs, shaders, and texture handling. - Corrects the image data pipeline to ensure raw pixel data is processed consistently.
This commit is contained in:
parent
be1342393e
commit
4f8d53fedb
@ -4,15 +4,6 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QBuffer>
|
||||
#include <QImage>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLShader>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOffscreenSurface>
|
||||
|
||||
// public
|
||||
|
||||
@ -61,58 +52,6 @@ HyperionGrabber::HyperionGrabber(QHash<QString, QString> opts)
|
||||
_hclient_p = new HyperionClient(addr, port, _hyperionPriority_m);
|
||||
_hclient_p->ledAdjustments(redAdjust, greenAdjust, blueAdjust, temperature, threshold, transform);
|
||||
|
||||
// Initialize OpenGL for hardware acceleration
|
||||
QSurfaceFormat format;
|
||||
format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
format.setVersion(3, 3); // Or higher, depending on your OpenGL version
|
||||
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
|
||||
format.setRedBufferSize(8);
|
||||
format.setGreenBufferSize(8);
|
||||
format.setBlueBufferSize(8);
|
||||
format.setAlphaBufferSize(8);
|
||||
format.setDepthBufferSize(24);
|
||||
format.setStencilBufferSize(8);
|
||||
|
||||
_offscreenSurface = new QOffscreenSurface();
|
||||
_offscreenSurface->setFormat(format);
|
||||
_offscreenSurface->create();
|
||||
|
||||
_context = new QOpenGLContext(this);
|
||||
_context->setFormat(format);
|
||||
_context->create();
|
||||
_context->makeCurrent(_offscreenSurface);
|
||||
|
||||
_functions = _context->functions();
|
||||
_functions->initializeOpenGLFunctions();
|
||||
|
||||
// Shaders for scaling
|
||||
const char *vertexShaderSource = R"(
|
||||
attribute highp vec4 vertices;
|
||||
attribute highp vec2 texCoords;
|
||||
varying highp vec2 v_texCoords;
|
||||
void main() {
|
||||
gl_Position = vertices;
|
||||
v_texCoords = texCoords;
|
||||
}
|
||||
)";
|
||||
|
||||
const char *fragmentShaderSource = R"(
|
||||
uniform sampler2D textureSampler;
|
||||
varying highp vec2 v_texCoords;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(textureSampler, v_texCoords);
|
||||
}
|
||||
)";
|
||||
|
||||
_shaderProgram = new QOpenGLShaderProgram(this);
|
||||
_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
|
||||
_shaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
|
||||
_shaderProgram->link();
|
||||
|
||||
_fbo = new QOpenGLFramebufferObject(1, 1, QOpenGLFramebufferObject::CombinedDepthStencil); // Placeholder size, will be resized later
|
||||
_texture = new QOpenGLTexture(QOpenGLTexture::Target2D); // Placeholder, will be created from QVideoFrame
|
||||
|
||||
_waylandGrabber_p = new WaylandGrabber(this);
|
||||
connect(_waylandGrabber_p, &WaylandGrabber::frameReady, this, &HyperionGrabber::_processFrame);
|
||||
_waylandGrabber_p->start();
|
||||
@ -140,19 +79,6 @@ HyperionGrabber::~HyperionGrabber()
|
||||
delete _timer_p;
|
||||
}
|
||||
delete _hclient_p;
|
||||
|
||||
// OpenGL cleanup
|
||||
if (_context) {
|
||||
_context->makeCurrent(_offscreenSurface);
|
||||
delete _fbo;
|
||||
delete _shaderProgram;
|
||||
delete _texture;
|
||||
_context->doneCurrent();
|
||||
delete _context;
|
||||
}
|
||||
if (_offscreenSurface) {
|
||||
delete _offscreenSurface;
|
||||
}
|
||||
}
|
||||
|
||||
// private
|
||||
@ -197,95 +123,33 @@ void HyperionGrabber::_processFrame(const QVideoFrame &frame)
|
||||
return;
|
||||
}
|
||||
|
||||
_context->makeCurrent(_offscreenSurface);
|
||||
|
||||
// Convert QVideoFrame to QImage
|
||||
QImage inputImage = frame.toImage();
|
||||
if (inputImage.isNull()) {
|
||||
QImage image = frame.toImage();
|
||||
if (image.isNull()) {
|
||||
qWarning() << "Failed to convert QVideoFrame to QImage.";
|
||||
_context->doneCurrent();
|
||||
return;
|
||||
}
|
||||
// qDebug() << "Input QImage dimensions:" << inputImage.width() << "x" << inputImage.height();
|
||||
// qDebug() << "Input QImage format:" << inputImage.format();
|
||||
|
||||
// Resize FBO if needed
|
||||
QSize targetSize(inputImage.width() / _scale_m, inputImage.height() / _scale_m);
|
||||
if (_fbo->size() != targetSize) {
|
||||
delete _fbo;
|
||||
_fbo = new QOpenGLFramebufferObject(targetSize, QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
// Calculate target size
|
||||
QSize targetSize(image.width() / _scale_m, image.height() / _scale_m);
|
||||
if (!targetSize.isValid()) {
|
||||
qWarning() << "Invalid target size for scaling.";
|
||||
return;
|
||||
}
|
||||
|
||||
_fbo->bind();
|
||||
_functions->glViewport(0, 0, _fbo->width(), _fbo->height());
|
||||
// Scale the image using Qt's optimized scaling
|
||||
QImage scaledImage = image.scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
// qDebug() << "FBO size:" << _fbo->size();
|
||||
|
||||
_functions->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
_functions->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Create texture from QImage
|
||||
if (_texture->isCreated()) {
|
||||
_texture->destroy();
|
||||
// Convert to a format suitable for Hyperion (RGB888)
|
||||
if (scaledImage.format() != QImage::Format_RGB888) {
|
||||
scaledImage = scaledImage.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
_texture->create();
|
||||
_texture->setData(inputImage.flipped(Qt::Vertical)); // Flip vertically
|
||||
|
||||
// qDebug() << "Texture size after setData:" << _texture->width() << "x" << _texture->height();
|
||||
|
||||
_shaderProgram->bind();
|
||||
_shaderProgram->setUniformValue("textureSampler", 0); // Texture unit 0
|
||||
|
||||
// Vertices for a quad
|
||||
GLfloat vertices[] = {
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
};
|
||||
|
||||
// Texture coordinates (flipped vertically for QImage top-left origin)
|
||||
GLfloat texCoords[] = {
|
||||
0.0f, 1.0f, // Top-left of texture (maps to bottom-left of quad)
|
||||
1.0f, 1.0f, // Top-right of texture (maps to bottom-right of quad)
|
||||
0.0f, 0.0f, // Bottom-left of texture (maps to top-left of quad)
|
||||
1.0f, 0.0f, // Bottom-right of texture (maps to top-right of quad)
|
||||
};
|
||||
|
||||
_shaderProgram->setAttributeArray("vertices", vertices, 2);
|
||||
_shaderProgram->enableAttributeArray("vertices");
|
||||
_shaderProgram->setAttributeArray("texCoords", texCoords, 2);
|
||||
_shaderProgram->enableAttributeArray("texCoords");
|
||||
|
||||
_functions->glActiveTexture(GL_TEXTURE0);
|
||||
_texture->bind();
|
||||
|
||||
_functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
_shaderProgram->disableAttributeArray("vertices");
|
||||
_shaderProgram->disableAttributeArray("texCoords");
|
||||
_shaderProgram->release();
|
||||
|
||||
_fbo->release();
|
||||
|
||||
// Read pixels back from FBO
|
||||
QImage scaledImage = _fbo->toImage();
|
||||
scaledImage = scaledImage.flipped(Qt::Vertical); // Flip vertically again if needed
|
||||
|
||||
QByteArray ba;
|
||||
QBuffer buffer(&ba);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
scaledImage.save(&buffer, "PNG"); // Save as PNG for now, can be optimized later
|
||||
int rawDataSize = scaledImage.sizeInBytes();
|
||||
// Update the image size in the client and send
|
||||
_hclient_p->setImgSize(scaledImage.width(), scaledImage.height());
|
||||
_hclient_p->sendImage(scaledImage.constBits(), rawDataSize);
|
||||
|
||||
_context->doneCurrent();
|
||||
_hclient_p->sendImage(scaledImage.constBits(), scaledImage.sizeInBytes());
|
||||
}
|
||||
|
||||
void HyperionGrabber::_setImgSize(int width, int height)
|
||||
{
|
||||
_hclient_p->setImgSize(width, height);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,17 +1,10 @@
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QImage>
|
||||
#include <QVideoFrame>
|
||||
|
||||
#include "hyperionclient.h"
|
||||
#include "WaylandGrabber.h"
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLShader>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QVideoFrame>
|
||||
#include <QOffscreenSurface>
|
||||
|
||||
class HyperionGrabber : public QObject
|
||||
{
|
||||
@ -31,13 +24,6 @@ private:
|
||||
unsigned short _frameskip_m = 0;
|
||||
long long _frameCounter_m = 0;
|
||||
|
||||
QOpenGLContext *_context;
|
||||
QOpenGLFramebufferObject *_fbo;
|
||||
QOpenGLShaderProgram *_shaderProgram;
|
||||
QOpenGLTexture *_texture;
|
||||
QOpenGLFunctions *_functions;
|
||||
QOffscreenSurface *_offscreenSurface;
|
||||
|
||||
QString _parseColorArr(QString, bool);
|
||||
|
||||
private slots:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user