Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QOpenGlWidget - why would a if-else in a fragment shader cause QPainter to fail?
Forum Updated to NodeBB v4.3 + New Features

QOpenGlWidget - why would a if-else in a fragment shader cause QPainter to fail?

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 5 Posters 2.1k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • N Offline
    N Offline
    NightShadeI
    wrote on 29 May 2023, 10:31 last edited by
    #1

    Spent the last few hours trying to debug why QPainter wasn't drawing after rendering some textures with QOpenGLWidget

    Did all my setup and teardown of logic etc, including unbinding the ShaderProgram

    Turned out if I have some fragment shader with a conditional, it causes QPainter to fail , even if the conditional does the same thing in either true or false branch (this is an if-else)

    Why would this possibly affect QPainter? Very strange bug. Happy to hear any theories

    for what its worth, I know its bad to have the conditionals in a fragment shader, this was just a toy project

    Sample OpenGL fragment code:

    void main()
    {
        vec4 inCol = texture2D(texture, v_texcoord);
        if (someTestValue < 0.5)
        {
            gl_FragColor = inCol;
        }
        else
        {
            gl_FragColor = inCol;
        }
        if (gl_FragColor.a <= 0.0) discard;
    }
    

    If we make that if (true)

    Or even just remove one of those branches, everything works perfectly. Does this sound like a QT bug?

    C 1 Reply Last reply 29 May 2023, 21:27
    0
    • N Offline
      N Offline
      NightShadeI
      wrote on 29 May 2023, 10:49 last edited by
      #2

      Actually it also seems the condition needs to test against some varying type to fail, if its another type then its okay ... as in the QPainter can be seen

      J 1 Reply Last reply 29 May 2023, 11:26
      0
      • N NightShadeI
        29 May 2023, 10:49

        Actually it also seems the condition needs to test against some varying type to fail, if its another type then its okay ... as in the QPainter can be seen

        J Offline
        J Offline
        JonB
        wrote on 29 May 2023, 11:26 last edited by
        #3

        @NightShadeI
        I assume the behaviour you see is "coincidence". Having an if like this cannot affect anything (other than maybe a nanosecond's speed), look at the generated machine code. All this does is assign to a variable named gl_FragColor, which I assume is an address in memory with no side-effects.

        for what its worth, I know its bad to have the conditionals in a fragment shader

        I know nothing about this.

        I suggest you produce a full, standalone, minimal reproducer and start from there.

        1 Reply Last reply
        0
        • S Offline
          S Offline
          SGaist
          Lifetime Qt Champion
          wrote on 29 May 2023, 12:10 last edited by
          #4

          Hi,

          Where does someTestValue come from ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          N 1 Reply Last reply 29 May 2023, 13:11
          0
          • S SGaist
            29 May 2023, 12:10

            Hi,

            Where does someTestValue come from ?

            N Offline
            N Offline
            NightShadeI
            wrote on 29 May 2023, 13:11 last edited by
            #5

            @SGaist

            its a varying

            varying float someTestValue;
            

            Which ultimately is from the vertex shader, simply forwarding on its own test value:

            someTestValue = someVertexTestValue;
            

            That is set like this in the C++

            int testLocation = program->attributeLocation("someVertexTestValue");
            program->enableAttributeArray(testLocation);
            program->setAttributeBuffer(testLocation, GL_FLOAT, offset, 1, sizeof(VertexData));
            

            Obviously this isn't all the code, it might take a while to produce a minimal workable example if its really needed , but I hope it gives some idea, or some way to test if this is just me

            This is Qt 6.4.0

            1 Reply Last reply
            0
            • N NightShadeI
              29 May 2023, 10:31

              Spent the last few hours trying to debug why QPainter wasn't drawing after rendering some textures with QOpenGLWidget

              Did all my setup and teardown of logic etc, including unbinding the ShaderProgram

              Turned out if I have some fragment shader with a conditional, it causes QPainter to fail , even if the conditional does the same thing in either true or false branch (this is an if-else)

              Why would this possibly affect QPainter? Very strange bug. Happy to hear any theories

              for what its worth, I know its bad to have the conditionals in a fragment shader, this was just a toy project

              Sample OpenGL fragment code:

              void main()
              {
                  vec4 inCol = texture2D(texture, v_texcoord);
                  if (someTestValue < 0.5)
                  {
                      gl_FragColor = inCol;
                  }
                  else
                  {
                      gl_FragColor = inCol;
                  }
                  if (gl_FragColor.a <= 0.0) discard;
              }
              

              If we make that if (true)

              Or even just remove one of those branches, everything works perfectly. Does this sound like a QT bug?

              C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 29 May 2023, 21:27 last edited by
              #6

              @NightShadeI said:

              it causes QPainter to fail

              What does that mean? Nothing is drawn? Wrong thing is drawn? You get errors at shader compilation or execution?

              Did you check the output to see if your shader compilation outputs any errors? Did you check if any of your GL calls return errors (e.g. if program->attributeLocation returns -1)? Did you try to run it under Debug context to see if any errors are reported at runtime?

              @JonB said:

              Having an if like this cannot affect anything

              Shader ifs are not your regular CPU ifs. GPUs executes shader programs in groups, usually 2x2 fragments. An if works like a mask. If the conditional diverges in a group you basically execute the whole thing twice with masked output from fragments that don't take the particular branch. Todays hardware does some reordering to mask the hit, but it's very different from branch predicting on CPUs and generally speaking ifs are very costly and should be avoided. One of the tricks GPU drivers do is compile the shader twice, hardcoding each branch and then mixing and matching resulting binary. This can also lead to significant binary growth when a lot of branches are present and can lead to further perf downgrade when all the branches don't fit in the GPU cache.

              All this does is assign to a variable named gl_FragColor, which I assume is an address in memory with no side-effects.

              It's very rarely an address in memory and it has a bunch of side effects :) gl_FragColor is the output of the fragment shader, which gets forwarded to the next pipeline stage, usually via dedicated silicon in the compute unit of the GPU.

              J N 2 Replies Last reply 29 May 2023, 21:42
              2
              • C Chris Kawa
                29 May 2023, 21:27

                @NightShadeI said:

                it causes QPainter to fail

                What does that mean? Nothing is drawn? Wrong thing is drawn? You get errors at shader compilation or execution?

                Did you check the output to see if your shader compilation outputs any errors? Did you check if any of your GL calls return errors (e.g. if program->attributeLocation returns -1)? Did you try to run it under Debug context to see if any errors are reported at runtime?

                @JonB said:

                Having an if like this cannot affect anything

                Shader ifs are not your regular CPU ifs. GPUs executes shader programs in groups, usually 2x2 fragments. An if works like a mask. If the conditional diverges in a group you basically execute the whole thing twice with masked output from fragments that don't take the particular branch. Todays hardware does some reordering to mask the hit, but it's very different from branch predicting on CPUs and generally speaking ifs are very costly and should be avoided. One of the tricks GPU drivers do is compile the shader twice, hardcoding each branch and then mixing and matching resulting binary. This can also lead to significant binary growth when a lot of branches are present and can lead to further perf downgrade when all the branches don't fit in the GPU cache.

                All this does is assign to a variable named gl_FragColor, which I assume is an address in memory with no side-effects.

                It's very rarely an address in memory and it has a bunch of side effects :) gl_FragColor is the output of the fragment shader, which gets forwarded to the next pipeline stage, usually via dedicated silicon in the compute unit of the GPU.

                J Offline
                J Offline
                JonB
                wrote on 29 May 2023, 21:42 last edited by
                #7

                @Chris-Kawa
                Hi, point taken about all the detail. But, excuse my ignorance, how do you know any of this code is to do with GPU?? I see main() and a couple of statements accessing global variables. Clearly out of my depth :)

                C 1 Reply Last reply 29 May 2023, 21:58
                0
                • J JonB
                  29 May 2023, 21:42

                  @Chris-Kawa
                  Hi, point taken about all the detail. But, excuse my ignorance, how do you know any of this code is to do with GPU?? I see main() and a couple of statements accessing global variables. Clearly out of my depth :)

                  C Offline
                  C Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on 29 May 2023, 21:58 last edited by
                  #8

                  @JonB said:

                  how do you know any of this code is to do with GPU??

                  Well, for one OP said it's shader code :)

                  I see main() and a couple of statements accessing global variables

                  The language is GLSL. It's based on cut down C with some domain specific modifications. Basic shader pipeline has a Vertex and Fragment program. Vertex program executes its main for each and every vertex of the geometry and then for each triangle the results from its 3 vertices are interpolated across every fragment and a Fragment program runs its main with those interpolated values as inputs. An attribute is sort of an input data to the vertex shader. They are set up on the CPU from C++ before running the GPU programs. A varying variable is the output from vertex shaders interpolated and passed as input to fragment shaders. Things like gl_Position or gl_FragColor are predefined input/output points of the various stages of the graphics pipeline. Things like texture2D are predefined "functions" that read input texture data using hardware samplers.

                  1 Reply Last reply
                  1
                  • C Chris Kawa
                    29 May 2023, 21:27

                    @NightShadeI said:

                    it causes QPainter to fail

                    What does that mean? Nothing is drawn? Wrong thing is drawn? You get errors at shader compilation or execution?

                    Did you check the output to see if your shader compilation outputs any errors? Did you check if any of your GL calls return errors (e.g. if program->attributeLocation returns -1)? Did you try to run it under Debug context to see if any errors are reported at runtime?

                    @JonB said:

                    Having an if like this cannot affect anything

                    Shader ifs are not your regular CPU ifs. GPUs executes shader programs in groups, usually 2x2 fragments. An if works like a mask. If the conditional diverges in a group you basically execute the whole thing twice with masked output from fragments that don't take the particular branch. Todays hardware does some reordering to mask the hit, but it's very different from branch predicting on CPUs and generally speaking ifs are very costly and should be avoided. One of the tricks GPU drivers do is compile the shader twice, hardcoding each branch and then mixing and matching resulting binary. This can also lead to significant binary growth when a lot of branches are present and can lead to further perf downgrade when all the branches don't fit in the GPU cache.

                    All this does is assign to a variable named gl_FragColor, which I assume is an address in memory with no side-effects.

                    It's very rarely an address in memory and it has a bunch of side effects :) gl_FragColor is the output of the fragment shader, which gets forwarded to the next pipeline stage, usually via dedicated silicon in the compute unit of the GPU.

                    N Offline
                    N Offline
                    NightShadeI
                    wrote on 29 May 2023, 22:18 last edited by NightShadeI
                    #9

                    @Chris-Kawa

                    Ah I think you might be onto something...

                    Indeed attributeLocation is returning -1 , but interesting the conditional still works so that's something ill have to investigate further

                    By not working I mean the QPainter won't draw anything only under these circumstances. Actually it seems more undefined behaviour like ... Since 1 run I saw a line of red just along the top of the screen with everything else normal (im trying to render a red rectangle with QPainter)

                    Certainly no GLSL compilation errors logged though and usually when there are, my textures (from a vertex buffer) don't actually get rendered.

                    No errors I see are currently logged on this seeming UB , except that -1 you now mention, which probably means im doing something silly somewhere

                    N 1 Reply Last reply 29 May 2023, 22:30
                    0
                    • N NightShadeI
                      29 May 2023, 22:18

                      @Chris-Kawa

                      Ah I think you might be onto something...

                      Indeed attributeLocation is returning -1 , but interesting the conditional still works so that's something ill have to investigate further

                      By not working I mean the QPainter won't draw anything only under these circumstances. Actually it seems more undefined behaviour like ... Since 1 run I saw a line of red just along the top of the screen with everything else normal (im trying to render a red rectangle with QPainter)

                      Certainly no GLSL compilation errors logged though and usually when there are, my textures (from a vertex buffer) don't actually get rendered.

                      No errors I see are currently logged on this seeming UB , except that -1 you now mention, which probably means im doing something silly somewhere

                      N Offline
                      N Offline
                      NightShadeI
                      wrote on 29 May 2023, 22:30 last edited by NightShadeI
                      #10

                      Nevermind, unfortunately it does return a valid value when I use it in the conditional. It just returns -1 when I don't use it in the fragment shader , so thus when no errors are returned the problem still persists

                      I.e. the code in my question would correctly return '3' (success) , but if I change someTestValue with true it will return -1

                      C 1 Reply Last reply 29 May 2023, 22:50
                      0
                      • N NightShadeI
                        29 May 2023, 22:30

                        Nevermind, unfortunately it does return a valid value when I use it in the conditional. It just returns -1 when I don't use it in the fragment shader , so thus when no errors are returned the problem still persists

                        I.e. the code in my question would correctly return '3' (success) , but if I change someTestValue with true it will return -1

                        C Offline
                        C Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on 29 May 2023, 22:50 last edited by
                        #11

                        @NightShadeI Shader compilers are extremely aggressive when it comes to unused variables. Anything that is not used in any branch is completely removed from the compiled shader blob, so if you query attributes that don't get used you'll get -1 (invalid location).

                        I would try creating a debug context. If there's no errors from it maybe your shader is just not doing what you think it does?

                        N 1 Reply Last reply 30 May 2023, 11:02
                        0
                        • C Chris Kawa
                          29 May 2023, 22:50

                          @NightShadeI Shader compilers are extremely aggressive when it comes to unused variables. Anything that is not used in any branch is completely removed from the compiled shader blob, so if you query attributes that don't get used you'll get -1 (invalid location).

                          I would try creating a debug context. If there's no errors from it maybe your shader is just not doing what you think it does?

                          N Offline
                          N Offline
                          NightShadeI
                          wrote on 30 May 2023, 11:02 last edited by NightShadeI
                          #12

                          @Chris-Kawa
                          @SGaist
                          @JonB

                          I have produced the most minimal reproducible example that I possibly could, cutting out about 500 lines of code. The example doesn't include everything , like a lot of things I found didn't make a difference (e.g. begin/end native painting). Please find example here:

                          MainWidget.h

                          #pragma once
                          
                          #include <QtGui/QMatrix4x4>
                          #include <QtGui/QOpenGLFunctions>
                          #include <QtGui/QVector2D>
                          #include <QtGui/QVector3D>
                          
                          #include <QtOpenGL/QOpenGLBuffer>
                          #include <QtOpenGL/QOpenGLTexture>
                          #include <QtOpenGL/QOpenGLShader>
                          #include <QtOpenGLWidgets/QOpenGLWidget>
                          
                          struct VertexData {
                              QVector2D theCenter;
                              QVector2D theTextureCoord;
                              QVector2D theOffset{0.f, 0.f};
                              float theExtra{0.f};
                          };
                          
                          class GeometryEngine : protected QOpenGLFunctions {
                              QOpenGLBuffer theGlBuffer;
                          
                              std::array<VertexData, 6> theData{
                                  VertexData{QVector2D{100, 400}, QVector2D{0.f, 1.f}},
                                  VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                  VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}},
                                  VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                  VertexData{QVector2D{400, 100}, QVector2D{1.f, 0.f}},
                                  VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}}
                              };
                          
                          public:
                              GeometryEngine() {
                                  initializeOpenGLFunctions();
                                  initGeometry();
                              }
                          
                              void initGeometry() {
                                  theGlBuffer.create();
                                  theGlBuffer.bind();
                                  theGlBuffer.allocate(theData.data(), theData.size() * sizeof(VertexData));
                              }
                          
                              void drawGeometry(QOpenGLShaderProgram& aProgram) {
                                  theGlBuffer.bind();
                          
                                  const int myCenterLoc = aProgram.attributeLocation("a_center");
                                  aProgram.enableAttributeArray(myCenterLoc);
                                  aProgram.setAttributeBuffer(myCenterLoc, GL_FLOAT, offsetof(VertexData, VertexData::theCenter), 2, sizeof(VertexData));
                                  
                                  const int myTexLoc = aProgram.attributeLocation("a_texcoord");
                                  aProgram.enableAttributeArray(myTexLoc);
                                  aProgram.setAttributeBuffer(myTexLoc, GL_FLOAT, offsetof(VertexData, VertexData::theTextureCoord), 2, sizeof(VertexData));
                          
                          
                                  const int myOffetLoc = aProgram.attributeLocation("a_offset");
                                  aProgram.enableAttributeArray(myOffetLoc);
                                  aProgram.setAttributeBuffer(myOffetLoc, GL_FLOAT, offsetof(VertexData, VertexData::theOffset), 2, sizeof(VertexData));
                          
                                  const int myExtraLoc = aProgram.attributeLocation("a_extra");
                                  aProgram.enableAttributeArray(myExtraLoc);
                                  aProgram.setAttributeBuffer(myExtraLoc, GL_FLOAT, offsetof(VertexData, VertexData::theExtra), 1, sizeof(VertexData));
                          
                                  glDrawArrays(GL_TRIANGLES, 0, theData.size());
                                  theGlBuffer.release();
                              }
                          };
                          
                          class MainWidget : public QOpenGLWidget , protected QOpenGLFunctions {
                              QOpenGLShaderProgram theProgram;
                              GeometryEngine* theGeometries = nullptr;
                              QOpenGLTexture* theTexture    = nullptr;
                          
                              int theWidth{0};
                              int theHeight{0};
                           
                          public:
                              void initShaders() {
                                  if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,   ":/shader/vshader.glsl")) close();
                                  if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader/fshader.glsl")) close();
                                  if (!theProgram.link()) close();
                              }
                          
                              void initializeGL() override {
                                  initializeOpenGLFunctions();
                          
                                  initShaders();
                                  initTextures();
                          
                                  theGeometries = new GeometryEngine{};
                              }
                          
                              void initTextures() {
                                  theTexture = new QOpenGLTexture{QImage{":/tileset.png"}};
                                  theTexture->setMinificationFilter(QOpenGLTexture::Nearest);
                                  theTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
                              }
                          
                              void resizeGL(int aWidth, int aHeight) override {
                                  theWidth  = aWidth;
                                  theHeight = aHeight;
                              }
                          
                              void paintGL() override {
                                  theProgram.bind();
                                  theTexture->bind();
                          
                                  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                          
                                  QMatrix4x4 myMatrix;
                                  myMatrix.ortho(0, theWidth, theHeight, 0, -1.f, 0);
                          
                                  theProgram.setUniformValue("mvp_matrix", myMatrix);
                                  theProgram.setUniformValue("texture", 0);
                          
                                  theGeometries->drawGeometry(theProgram);
                          
                                  QPainter myPainter{this};
                                  myPainter.fillRect(0, 0, 100, 100, Qt::red);
                                  myPainter.setPen(Qt::red);
                                  myPainter.drawLine(rect().topLeft(), rect().bottomRight());
                              }
                          };
                          

                          shader/vshader.glsl

                          #ifdef GL_ES
                          // Set default precision to medium
                          precision mediump int;
                          precision mediump float;
                          #endif
                          
                          uniform mat4 mvp_matrix;
                          
                          attribute vec2 a_center;
                          attribute vec2 a_texcoord;
                          attribute vec2 a_offset;
                          attribute float a_extra;
                          
                          varying vec2 v_texcoord;
                          
                          void main()
                          {
                              vec4 new_pos = vec4(a_center.x + a_offset.x, a_center.y, 1.0, 1.0);
                              gl_Position = mvp_matrix * new_pos;
                              v_texcoord = a_texcoord;
                          }
                          

                          shader/fshader.glsl

                          #ifdef GL_ES
                          // Set default precision to medium
                          precision mediump int;
                          precision mediump float;
                          #endif
                          
                          uniform sampler2D texture;
                          varying vec2 v_texcoord;
                          
                          void main()
                          {
                              vec4 inCol = texture2D(texture, v_texcoord);
                              gl_FragColor = inCol;
                          }
                          

                          main:

                          #include "MainWidget.hpp"
                          
                          int main(int aArgc, char *aArgv[]) {
                              QApplication myApplication{aArgc, aArgv};
                              myApplication.setApplicationName("Playground");
                              myApplication.setApplicationVersion("0.1");
                          
                          #ifndef QT_NO_OPENGL
                              MainWidget widget;
                              widget.show();
                          #else
                              QLabel note("OpenGL Support required");
                              note.show();
                          #endif
                          
                              return myApplication.exec();
                          }
                          

                          Ultimately the issue occurs when we use MORE THAN THREE attributes, so in vshader you have to add a_offset.x + a_extra -- You will notice most your screen becomes red -- Also again, if it matters, my Qt version is 6.4.0 and Windows 10

                          Hope it helps :) You can replace tileset with any image you choose, tried to make it all plug and play

                          Any help would be super appreciated, tried just about anything and can't work this one out

                          C N 2 Replies Last reply 30 May 2023, 20:20
                          0
                          • N NightShadeI
                            30 May 2023, 11:02

                            @Chris-Kawa
                            @SGaist
                            @JonB

                            I have produced the most minimal reproducible example that I possibly could, cutting out about 500 lines of code. The example doesn't include everything , like a lot of things I found didn't make a difference (e.g. begin/end native painting). Please find example here:

                            MainWidget.h

                            #pragma once
                            
                            #include <QtGui/QMatrix4x4>
                            #include <QtGui/QOpenGLFunctions>
                            #include <QtGui/QVector2D>
                            #include <QtGui/QVector3D>
                            
                            #include <QtOpenGL/QOpenGLBuffer>
                            #include <QtOpenGL/QOpenGLTexture>
                            #include <QtOpenGL/QOpenGLShader>
                            #include <QtOpenGLWidgets/QOpenGLWidget>
                            
                            struct VertexData {
                                QVector2D theCenter;
                                QVector2D theTextureCoord;
                                QVector2D theOffset{0.f, 0.f};
                                float theExtra{0.f};
                            };
                            
                            class GeometryEngine : protected QOpenGLFunctions {
                                QOpenGLBuffer theGlBuffer;
                            
                                std::array<VertexData, 6> theData{
                                    VertexData{QVector2D{100, 400}, QVector2D{0.f, 1.f}},
                                    VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                    VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}},
                                    VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                    VertexData{QVector2D{400, 100}, QVector2D{1.f, 0.f}},
                                    VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}}
                                };
                            
                            public:
                                GeometryEngine() {
                                    initializeOpenGLFunctions();
                                    initGeometry();
                                }
                            
                                void initGeometry() {
                                    theGlBuffer.create();
                                    theGlBuffer.bind();
                                    theGlBuffer.allocate(theData.data(), theData.size() * sizeof(VertexData));
                                }
                            
                                void drawGeometry(QOpenGLShaderProgram& aProgram) {
                                    theGlBuffer.bind();
                            
                                    const int myCenterLoc = aProgram.attributeLocation("a_center");
                                    aProgram.enableAttributeArray(myCenterLoc);
                                    aProgram.setAttributeBuffer(myCenterLoc, GL_FLOAT, offsetof(VertexData, VertexData::theCenter), 2, sizeof(VertexData));
                                    
                                    const int myTexLoc = aProgram.attributeLocation("a_texcoord");
                                    aProgram.enableAttributeArray(myTexLoc);
                                    aProgram.setAttributeBuffer(myTexLoc, GL_FLOAT, offsetof(VertexData, VertexData::theTextureCoord), 2, sizeof(VertexData));
                            
                            
                                    const int myOffetLoc = aProgram.attributeLocation("a_offset");
                                    aProgram.enableAttributeArray(myOffetLoc);
                                    aProgram.setAttributeBuffer(myOffetLoc, GL_FLOAT, offsetof(VertexData, VertexData::theOffset), 2, sizeof(VertexData));
                            
                                    const int myExtraLoc = aProgram.attributeLocation("a_extra");
                                    aProgram.enableAttributeArray(myExtraLoc);
                                    aProgram.setAttributeBuffer(myExtraLoc, GL_FLOAT, offsetof(VertexData, VertexData::theExtra), 1, sizeof(VertexData));
                            
                                    glDrawArrays(GL_TRIANGLES, 0, theData.size());
                                    theGlBuffer.release();
                                }
                            };
                            
                            class MainWidget : public QOpenGLWidget , protected QOpenGLFunctions {
                                QOpenGLShaderProgram theProgram;
                                GeometryEngine* theGeometries = nullptr;
                                QOpenGLTexture* theTexture    = nullptr;
                            
                                int theWidth{0};
                                int theHeight{0};
                             
                            public:
                                void initShaders() {
                                    if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,   ":/shader/vshader.glsl")) close();
                                    if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader/fshader.glsl")) close();
                                    if (!theProgram.link()) close();
                                }
                            
                                void initializeGL() override {
                                    initializeOpenGLFunctions();
                            
                                    initShaders();
                                    initTextures();
                            
                                    theGeometries = new GeometryEngine{};
                                }
                            
                                void initTextures() {
                                    theTexture = new QOpenGLTexture{QImage{":/tileset.png"}};
                                    theTexture->setMinificationFilter(QOpenGLTexture::Nearest);
                                    theTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
                                }
                            
                                void resizeGL(int aWidth, int aHeight) override {
                                    theWidth  = aWidth;
                                    theHeight = aHeight;
                                }
                            
                                void paintGL() override {
                                    theProgram.bind();
                                    theTexture->bind();
                            
                                    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                            
                                    QMatrix4x4 myMatrix;
                                    myMatrix.ortho(0, theWidth, theHeight, 0, -1.f, 0);
                            
                                    theProgram.setUniformValue("mvp_matrix", myMatrix);
                                    theProgram.setUniformValue("texture", 0);
                            
                                    theGeometries->drawGeometry(theProgram);
                            
                                    QPainter myPainter{this};
                                    myPainter.fillRect(0, 0, 100, 100, Qt::red);
                                    myPainter.setPen(Qt::red);
                                    myPainter.drawLine(rect().topLeft(), rect().bottomRight());
                                }
                            };
                            

                            shader/vshader.glsl

                            #ifdef GL_ES
                            // Set default precision to medium
                            precision mediump int;
                            precision mediump float;
                            #endif
                            
                            uniform mat4 mvp_matrix;
                            
                            attribute vec2 a_center;
                            attribute vec2 a_texcoord;
                            attribute vec2 a_offset;
                            attribute float a_extra;
                            
                            varying vec2 v_texcoord;
                            
                            void main()
                            {
                                vec4 new_pos = vec4(a_center.x + a_offset.x, a_center.y, 1.0, 1.0);
                                gl_Position = mvp_matrix * new_pos;
                                v_texcoord = a_texcoord;
                            }
                            

                            shader/fshader.glsl

                            #ifdef GL_ES
                            // Set default precision to medium
                            precision mediump int;
                            precision mediump float;
                            #endif
                            
                            uniform sampler2D texture;
                            varying vec2 v_texcoord;
                            
                            void main()
                            {
                                vec4 inCol = texture2D(texture, v_texcoord);
                                gl_FragColor = inCol;
                            }
                            

                            main:

                            #include "MainWidget.hpp"
                            
                            int main(int aArgc, char *aArgv[]) {
                                QApplication myApplication{aArgc, aArgv};
                                myApplication.setApplicationName("Playground");
                                myApplication.setApplicationVersion("0.1");
                            
                            #ifndef QT_NO_OPENGL
                                MainWidget widget;
                                widget.show();
                            #else
                                QLabel note("OpenGL Support required");
                                note.show();
                            #endif
                            
                                return myApplication.exec();
                            }
                            

                            Ultimately the issue occurs when we use MORE THAN THREE attributes, so in vshader you have to add a_offset.x + a_extra -- You will notice most your screen becomes red -- Also again, if it matters, my Qt version is 6.4.0 and Windows 10

                            Hope it helps :) You can replace tileset with any image you choose, tried to make it all plug and play

                            Any help would be super appreciated, tried just about anything and can't work this one out

                            C Offline
                            C Offline
                            Chris Kawa
                            Lifetime Qt Champion
                            wrote on 30 May 2023, 20:20 last edited by Chris Kawa
                            #13

                            @NightShadeI The problem is you're not disabling your attribute arrays after drawing. Qt wrapper API makes it look as if it was a program specific state but it's not. It's a global state that all draw calls share.

                            QPainter explicitly disables attribute arrays at locations 0-2 to use them for something, but then uses locations 3-5 with glVertexAttrib to store some MVP matrix for draws without checking if any arrays are bound at that location.

                            That it doesn't clear those locations first could be considered a bug I guess, but the gist of it is that you're accidentally corrupting attribute 3 (and above) that QPainter uses without properly sanitizing it first.

                            The easy fix is to always disable your attribute arrays after a draw that uses them, or at least before you switch to QPainter calls.

                            As a side note - since you're mixing native OpenGL with QPainter you should be using beginNativePainting() / endNativePainting(). You're sharing state with QPainter and those calls help to restore any state you or QPainter might have changed, so you don't step on each other's feet.

                            N 1 Reply Last reply 30 May 2023, 20:51
                            2
                            • N NightShadeI
                              30 May 2023, 11:02

                              @Chris-Kawa
                              @SGaist
                              @JonB

                              I have produced the most minimal reproducible example that I possibly could, cutting out about 500 lines of code. The example doesn't include everything , like a lot of things I found didn't make a difference (e.g. begin/end native painting). Please find example here:

                              MainWidget.h

                              #pragma once
                              
                              #include <QtGui/QMatrix4x4>
                              #include <QtGui/QOpenGLFunctions>
                              #include <QtGui/QVector2D>
                              #include <QtGui/QVector3D>
                              
                              #include <QtOpenGL/QOpenGLBuffer>
                              #include <QtOpenGL/QOpenGLTexture>
                              #include <QtOpenGL/QOpenGLShader>
                              #include <QtOpenGLWidgets/QOpenGLWidget>
                              
                              struct VertexData {
                                  QVector2D theCenter;
                                  QVector2D theTextureCoord;
                                  QVector2D theOffset{0.f, 0.f};
                                  float theExtra{0.f};
                              };
                              
                              class GeometryEngine : protected QOpenGLFunctions {
                                  QOpenGLBuffer theGlBuffer;
                              
                                  std::array<VertexData, 6> theData{
                                      VertexData{QVector2D{100, 400}, QVector2D{0.f, 1.f}},
                                      VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                      VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}},
                                      VertexData{QVector2D{400, 400}, QVector2D{1.f, 1.f}},
                                      VertexData{QVector2D{400, 100}, QVector2D{1.f, 0.f}},
                                      VertexData{QVector2D{100, 100}, QVector2D{0.f, 0.f}}
                                  };
                              
                              public:
                                  GeometryEngine() {
                                      initializeOpenGLFunctions();
                                      initGeometry();
                                  }
                              
                                  void initGeometry() {
                                      theGlBuffer.create();
                                      theGlBuffer.bind();
                                      theGlBuffer.allocate(theData.data(), theData.size() * sizeof(VertexData));
                                  }
                              
                                  void drawGeometry(QOpenGLShaderProgram& aProgram) {
                                      theGlBuffer.bind();
                              
                                      const int myCenterLoc = aProgram.attributeLocation("a_center");
                                      aProgram.enableAttributeArray(myCenterLoc);
                                      aProgram.setAttributeBuffer(myCenterLoc, GL_FLOAT, offsetof(VertexData, VertexData::theCenter), 2, sizeof(VertexData));
                                      
                                      const int myTexLoc = aProgram.attributeLocation("a_texcoord");
                                      aProgram.enableAttributeArray(myTexLoc);
                                      aProgram.setAttributeBuffer(myTexLoc, GL_FLOAT, offsetof(VertexData, VertexData::theTextureCoord), 2, sizeof(VertexData));
                              
                              
                                      const int myOffetLoc = aProgram.attributeLocation("a_offset");
                                      aProgram.enableAttributeArray(myOffetLoc);
                                      aProgram.setAttributeBuffer(myOffetLoc, GL_FLOAT, offsetof(VertexData, VertexData::theOffset), 2, sizeof(VertexData));
                              
                                      const int myExtraLoc = aProgram.attributeLocation("a_extra");
                                      aProgram.enableAttributeArray(myExtraLoc);
                                      aProgram.setAttributeBuffer(myExtraLoc, GL_FLOAT, offsetof(VertexData, VertexData::theExtra), 1, sizeof(VertexData));
                              
                                      glDrawArrays(GL_TRIANGLES, 0, theData.size());
                                      theGlBuffer.release();
                                  }
                              };
                              
                              class MainWidget : public QOpenGLWidget , protected QOpenGLFunctions {
                                  QOpenGLShaderProgram theProgram;
                                  GeometryEngine* theGeometries = nullptr;
                                  QOpenGLTexture* theTexture    = nullptr;
                              
                                  int theWidth{0};
                                  int theHeight{0};
                               
                              public:
                                  void initShaders() {
                                      if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,   ":/shader/vshader.glsl")) close();
                                      if (!theProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader/fshader.glsl")) close();
                                      if (!theProgram.link()) close();
                                  }
                              
                                  void initializeGL() override {
                                      initializeOpenGLFunctions();
                              
                                      initShaders();
                                      initTextures();
                              
                                      theGeometries = new GeometryEngine{};
                                  }
                              
                                  void initTextures() {
                                      theTexture = new QOpenGLTexture{QImage{":/tileset.png"}};
                                      theTexture->setMinificationFilter(QOpenGLTexture::Nearest);
                                      theTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
                                  }
                              
                                  void resizeGL(int aWidth, int aHeight) override {
                                      theWidth  = aWidth;
                                      theHeight = aHeight;
                                  }
                              
                                  void paintGL() override {
                                      theProgram.bind();
                                      theTexture->bind();
                              
                                      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                              
                                      QMatrix4x4 myMatrix;
                                      myMatrix.ortho(0, theWidth, theHeight, 0, -1.f, 0);
                              
                                      theProgram.setUniformValue("mvp_matrix", myMatrix);
                                      theProgram.setUniformValue("texture", 0);
                              
                                      theGeometries->drawGeometry(theProgram);
                              
                                      QPainter myPainter{this};
                                      myPainter.fillRect(0, 0, 100, 100, Qt::red);
                                      myPainter.setPen(Qt::red);
                                      myPainter.drawLine(rect().topLeft(), rect().bottomRight());
                                  }
                              };
                              

                              shader/vshader.glsl

                              #ifdef GL_ES
                              // Set default precision to medium
                              precision mediump int;
                              precision mediump float;
                              #endif
                              
                              uniform mat4 mvp_matrix;
                              
                              attribute vec2 a_center;
                              attribute vec2 a_texcoord;
                              attribute vec2 a_offset;
                              attribute float a_extra;
                              
                              varying vec2 v_texcoord;
                              
                              void main()
                              {
                                  vec4 new_pos = vec4(a_center.x + a_offset.x, a_center.y, 1.0, 1.0);
                                  gl_Position = mvp_matrix * new_pos;
                                  v_texcoord = a_texcoord;
                              }
                              

                              shader/fshader.glsl

                              #ifdef GL_ES
                              // Set default precision to medium
                              precision mediump int;
                              precision mediump float;
                              #endif
                              
                              uniform sampler2D texture;
                              varying vec2 v_texcoord;
                              
                              void main()
                              {
                                  vec4 inCol = texture2D(texture, v_texcoord);
                                  gl_FragColor = inCol;
                              }
                              

                              main:

                              #include "MainWidget.hpp"
                              
                              int main(int aArgc, char *aArgv[]) {
                                  QApplication myApplication{aArgc, aArgv};
                                  myApplication.setApplicationName("Playground");
                                  myApplication.setApplicationVersion("0.1");
                              
                              #ifndef QT_NO_OPENGL
                                  MainWidget widget;
                                  widget.show();
                              #else
                                  QLabel note("OpenGL Support required");
                                  note.show();
                              #endif
                              
                                  return myApplication.exec();
                              }
                              

                              Ultimately the issue occurs when we use MORE THAN THREE attributes, so in vshader you have to add a_offset.x + a_extra -- You will notice most your screen becomes red -- Also again, if it matters, my Qt version is 6.4.0 and Windows 10

                              Hope it helps :) You can replace tileset with any image you choose, tried to make it all plug and play

                              Any help would be super appreciated, tried just about anything and can't work this one out

                              N Offline
                              N Offline
                              NightShadeI
                              wrote on 30 May 2023, 20:27 last edited by
                              #14
                              This post is deleted!
                              1 Reply Last reply
                              0
                              • C Chris Kawa
                                30 May 2023, 20:20

                                @NightShadeI The problem is you're not disabling your attribute arrays after drawing. Qt wrapper API makes it look as if it was a program specific state but it's not. It's a global state that all draw calls share.

                                QPainter explicitly disables attribute arrays at locations 0-2 to use them for something, but then uses locations 3-5 with glVertexAttrib to store some MVP matrix for draws without checking if any arrays are bound at that location.

                                That it doesn't clear those locations first could be considered a bug I guess, but the gist of it is that you're accidentally corrupting attribute 3 (and above) that QPainter uses without properly sanitizing it first.

                                The easy fix is to always disable your attribute arrays after a draw that uses them, or at least before you switch to QPainter calls.

                                As a side note - since you're mixing native OpenGL with QPainter you should be using beginNativePainting() / endNativePainting(). You're sharing state with QPainter and those calls help to restore any state you or QPainter might have changed, so you don't step on each other's feet.

                                N Offline
                                N Offline
                                NightShadeI
                                wrote on 30 May 2023, 20:51 last edited by
                                #15

                                @Chris-Kawa

                                Thanks so much, funny thing is I just found this also at the same time as you were posting it , and now that makes so much sense with your explanation. Thanks so much for your help in all of this :) Guess can mark as solved. I'm happy with cleaning up my own state at least for now, can probably make my own RAII style wrapper to do it if need be

                                W 1 Reply Last reply 31 May 2023, 03:33
                                1
                                • N NightShadeI has marked this topic as solved on 30 May 2023, 20:53
                                • N NightShadeI
                                  30 May 2023, 20:51

                                  @Chris-Kawa

                                  Thanks so much, funny thing is I just found this also at the same time as you were posting it , and now that makes so much sense with your explanation. Thanks so much for your help in all of this :) Guess can mark as solved. I'm happy with cleaning up my own state at least for now, can probably make my own RAII style wrapper to do it if need be

                                  W Offline
                                  W Offline
                                  wrosecrans
                                  wrote on 31 May 2023, 03:33 last edited by
                                  #16

                                  @NightShadeI Just make sure you've read the OpenGL Wiki's page on gotchas with mixing RAII/OO/ and OpenGL

                                  https://www.khronos.org/opengl/wiki/Common_Mistakes#The_Object_Oriented_Language_Problem

                                  1 Reply Last reply
                                  0

                                  1/16

                                  29 May 2023, 10:31

                                  • Login

                                  • Login or register to search.
                                  1 out of 16
                                  • First post
                                    1/16
                                    Last post
                                  0
                                  • Categories
                                  • Recent
                                  • Tags
                                  • Popular
                                  • Users
                                  • Groups
                                  • Search
                                  • Get Qt Extensions
                                  • Unsolved