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.
  • SGaistS SGaist

    Hi,

    Where does someTestValue come from ?

    N Offline
    N Offline
    NightShadeI
    wrote on 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

      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?

      Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on 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.

      JonBJ N 2 Replies Last reply
      2
      • Chris KawaC Chris Kawa

        @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.

        JonBJ Online
        JonBJ Online
        JonB
        wrote on 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 :)

        Chris KawaC 1 Reply Last reply
        0
        • JonBJ JonB

          @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 :)

          Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on 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
          • Chris KawaC Chris Kawa

            @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 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
            0
            • N NightShadeI

              @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 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

              Chris KawaC 1 Reply Last reply
              0
              • N NightShadeI

                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

                Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on 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
                0
                • Chris KawaC Chris Kawa

                  @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 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

                  Chris KawaC N 2 Replies Last reply
                  0
                  • N NightShadeI

                    @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

                    Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on 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
                    2
                    • N NightShadeI

                      @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 last edited by
                      #14
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • Chris KawaC Chris Kawa

                        @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 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
                        1
                        • N NightShadeI has marked this topic as solved on
                        • N NightShadeI

                          @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 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

                          • Login

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