From 9c11e022f3771f45c446f3c6daebcab61abc587f Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 3 Aug 2020 00:30:13 +0200 Subject: [PATCH] GLShaderTextured: use mutable or immutable storage depending on system capability. Implements #582 --- sdrgui/gui/glshadertextured.cpp | 145 +++++++++++++++++++++++++++++--- sdrgui/gui/glshadertextured.h | 10 ++- 2 files changed, 140 insertions(+), 15 deletions(-) diff --git a/sdrgui/gui/glshadertextured.cpp b/sdrgui/gui/glshadertextured.cpp index 58e737d33..f07e48756 100644 --- a/sdrgui/gui/glshadertextured.cpp +++ b/sdrgui/gui/glshadertextured.cpp @@ -27,10 +27,12 @@ #include "gui/glshadertextured.h" GLShaderTextured::GLShaderTextured() : - m_program(0), - m_texture(0), + m_program(nullptr), + m_texture(nullptr), + m_textureId(0), m_matrixLoc(0), - m_textureLoc(0) + m_textureLoc(0), + m_useImmutableStorage(true) { } GLShaderTextured::~GLShaderTextured() @@ -40,6 +42,10 @@ GLShaderTextured::~GLShaderTextured() void GLShaderTextured::initializeGL() { + initializeOpenGLFunctions(); + m_useImmutableStorage = useImmutableStorage(); + qDebug() << "GLShaderTextured::initializeGL: m_useImmutableStorage: " << m_useImmutableStorage; + m_program = new QOpenGLShaderProgram; if (!m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_vertexShaderSourceTextured)) { @@ -64,6 +70,15 @@ void GLShaderTextured::initializeGL() } void GLShaderTextured::initTexture(const QImage& image, QOpenGLTexture::WrapMode wrapMode) +{ + if (m_useImmutableStorage) { + initTextureImmutable(image, wrapMode); + } else { + initTextureMutable(image, wrapMode); + } +} + +void GLShaderTextured::initTextureImmutable(const QImage& image, QOpenGLTexture::WrapMode wrapMode) { if (m_texture) { delete m_texture; @@ -76,10 +91,39 @@ void GLShaderTextured::initTexture(const QImage& image, QOpenGLTexture::WrapMode m_texture->setWrapMode(wrapMode); } +void GLShaderTextured::initTextureMutable(const QImage& image, QOpenGLTexture::WrapMode wrapMode) +{ + if (m_textureId) + { + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } + + glGenTextures(1, &m_textureId); + glBindTexture(GL_TEXTURE_2D, m_textureId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constScanLine(0)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode); +} + void GLShaderTextured::subTexture(int xOffset, int yOffset, int width, int height, const void *pixels) { - if (!m_texture) { - qDebug("GLShaderTextured::subTexture: no texture defined. Doing nothing"); + if (m_useImmutableStorage) { + subTextureImmutable(xOffset, yOffset, width, height, pixels); + } else { + subTextureMutable(xOffset, yOffset, width, height, pixels); + } +} + +void GLShaderTextured::subTextureImmutable(int xOffset, int yOffset, int width, int height, const void *pixels) +{ + if (!m_texture) + { + qDebug("GLShaderTextured::subTextureImmutable: no texture defined. Doing nothing"); return; } @@ -88,14 +132,31 @@ void GLShaderTextured::subTexture(int xOffset, int yOffset, int width, int heigh f->glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } +void GLShaderTextured::subTextureMutable(int xOffset, int yOffset, int width, int height, const void *pixels) +{ + if (!m_textureId) + { + qDebug("GLShaderTextured::subTextureMutable: no texture defined. Doing nothing"); + return; + } + + glBindTexture(GL_TEXTURE_2D, m_textureId); + glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +} + void GLShaderTextured::drawSurface(const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices) { - draw(GL_TRIANGLE_FAN, transformMatrix, textureCoords, vertices, nbVertices); + if (m_useImmutableStorage) { + draw(GL_TRIANGLE_FAN, transformMatrix, textureCoords, vertices, nbVertices); + } else { + drawMutable(GL_TRIANGLE_FAN, transformMatrix, textureCoords, vertices, nbVertices); + } } void GLShaderTextured::draw(unsigned int mode, const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices) { - if (!m_texture) { + if (!m_texture) + { qDebug("GLShaderTextured::draw: no texture defined. Doing nothing"); return; } @@ -114,17 +175,73 @@ void GLShaderTextured::draw(unsigned int mode, const QMatrix4x4& transformMatrix m_program->release(); } -void GLShaderTextured::cleanup() +void GLShaderTextured::drawMutable(unsigned int mode, const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices) { - if (m_program) { - delete m_program; - m_program = 0; + if (!m_textureId) + { + qDebug("GLShaderTextured::drawMutable: no texture defined. Doing nothing"); + return; } - if (m_texture) { - delete m_texture; - m_texture = 0; + m_program->bind(); + m_program->setUniformValue(m_matrixLoc, transformMatrix); + glBindTexture(GL_TEXTURE_2D, m_textureId); + m_program->setUniformValue(m_textureLoc, 0); // Use texture unit 0 which magically contains our texture + glEnableVertexAttribArray(0); // vertex + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glEnableVertexAttribArray(1); // texture coordinates + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoords); + glDrawArrays(mode, 0, nbVertices); + glDisableVertexAttribArray(0); + m_program->release(); +} + +void GLShaderTextured::cleanup() +{ + if (m_program) + { + delete m_program; + m_program = nullptr; } + + if (m_texture) + { + delete m_texture; + m_texture = nullptr; + } + + if (m_textureId) + { + glDeleteTextures(1, &m_textureId); + m_textureId = 0; + } +} + +bool GLShaderTextured::useImmutableStorage() +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + QSurfaceFormat sf = ctx->format(); + + if (sf.version() >= qMakePair(4, 2) + || ctx->hasExtension(QByteArrayLiteral("GL_ARB_texture_storage")) + || ctx->hasExtension(QByteArrayLiteral("GL_EXT_texture_storage"))) + { + void (QOPENGLF_APIENTRYP glTexStorage2D)( + GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height); + glTexStorage2D = reinterpret_cast(ctx->getProcAddress("glTexStorage2D")); + int data = 0; + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data); + GLenum err = glGetError(); + glDeleteTextures(1, &textureId); + return err == GL_NO_ERROR; + } + + return false; } const QString GLShaderTextured::m_vertexShaderSourceTextured = QString( diff --git a/sdrgui/gui/glshadertextured.h b/sdrgui/gui/glshadertextured.h index 6bbec7d24..d94b432a5 100644 --- a/sdrgui/gui/glshadertextured.h +++ b/sdrgui/gui/glshadertextured.h @@ -32,7 +32,7 @@ class QOpenGLShaderProgram; class QMatrix4x4; class QImage; -class SDRGUI_API GLShaderTextured +class SDRGUI_API GLShaderTextured : protected QOpenGLFunctions { public: GLShaderTextured(); @@ -46,11 +46,19 @@ public: private: void draw(unsigned int mode, const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices); + void drawMutable(unsigned int mode, const QMatrix4x4& transformMatrix, GLfloat *textureCoords, GLfloat *vertices, int nbVertices); + void initTextureImmutable(const QImage& image, QOpenGLTexture::WrapMode wrapMode = QOpenGLTexture::Repeat); + void subTextureImmutable(int xOffset, int yOffset, int width, int height, const void *pixels); + void initTextureMutable(const QImage& image, QOpenGLTexture::WrapMode wrapMode = QOpenGLTexture::Repeat); + void subTextureMutable(int xOffset, int yOffset, int width, int height, const void *pixels); + bool useImmutableStorage(); QOpenGLShaderProgram *m_program; QOpenGLTexture *m_texture; + unsigned int m_textureId; int m_matrixLoc; int m_textureLoc; + bool m_useImmutableStorage; static const QString m_vertexShaderSourceTextured; static const QString m_fragmentShaderSourceTextured; };