How to make OpenGL viewport have the exact size and position of my QML item which renders into it?

Viewed 1587

I'm trying to render a 640x360 red square inside a 1280x720 window. The problem is that OpenGL's viewport won't automatically be inside the area occupied by the square into the Qt window system. That is, if my video object have 640x360 dimensions, the OpenGl viewport still have the 1280x720 dimensions (WHY?).

Anyways, I can fix that by using glViewport(this->x, this->y, this->width, this->height);. The problem is that in OpenGL, the coordinate system has origin at the left low corner, and this->x, this->y, this->width, this->height are from the object's coordinates in QML syntax, where the origin is in the upper left corner. The result is this:

main.qml:

import QtQuick 2.0

import OpenGlVideoQtQuick2 1.0


Grid {
    columns: 2
    spacing: 2    
    width: 1280
    height: 720
    OpenGlVideoQtQuick2 {
        width: 640
        height: 360
    }

}

but the red square occupies the 3rd grid instead of the 1st one:

enter image description here

I could easily fix that by doing glViewport(this->x, WINDOW_HEIGHT-this->y, this->width, this->height); where WINDOW_HEIGHT would have the value 720, however I don't think I should trust the OpenGL viewport to always have the size of the window, so instead I should get the OpenGl viewport dimensions, but I don't think that that is possible.

I tried

GLint dims[2] = {9999, 9999};
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims);
std::cout << "dimenson x:" << dims[0] << " dimenson y:" << dims[1] << std::endl;

which gives me dimenson x:8192 dimenson y:8192 which makes no sense to me.

Here is the OpenGL rendering code: https://github.com/lucaszanella/QQuickPaintedItemBug/blob/0a8ef6b5229afa7113ec1e4e3838981042792329/OpenGlVideoQtQuick2.cpp

You can view an entire buildable project here: https://github.com/lucaszanella/QQuickPaintedItemBug/tree/0a8ef6b5229afa7113ec1e4e3838981042792329

1 Answers

So here is the solution in pseudo code:

int w = item->width() * window->devicePixelRatio();
int h = item->height() * window->devicePixelRatio();
int x = item->x();
int y = window->height()* window->devicePixelRatio() - h - item->y()

glViewport(x, y, w, h);

I have tested with Qt Scene Graph - OpenGL Under QML example. Here are the changes I made:

main.qml

Squircle {
    x: 80
    y: 120
    width: 160
    height: 240
    [...]
}

squircle.cpp

void Squircle::sync()
{
    if (!m_renderer) {
        m_renderer = new SquircleRenderer();
        connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::paint, Qt::DirectConnection);
    }
    m_renderer->setViewportSize(this->size().toSize() * window()->devicePixelRatio());
    m_renderer->setPosition(this->position().toPoint());
    m_renderer->setT(m_t);
    m_renderer->setWindow(window());
}

void SquircleRenderer::paint()
{
    [...]
    int y = (m_window->size()* m_window->devicePixelRatio()).height() - m_viewportSize.height() -  m_position.y();
    glViewport(m_position.x(), y, m_viewportSize.width(), m_viewportSize.height());
    [...]
}

squircle.h

class SquircleRenderer : public QObject, protected QOpenGLFunctions
{
    Q_OBJECT
public:

    void setPosition(const QPoint &position) { m_position = position; }

    [...]

private:
    [...]
    QPoint m_position;
};

And here is the result:

enter image description here

Related