From 4b611d649ede3bd1e56a73abcbd16aa60db38e3b Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 17 Jan 2020 14:40:36 +0100 Subject: Move QOpenGLShaderProgram from QtGui to QtOpenGL Task-number: QTBUG-74409 Change-Id: I20dfafc0c9bf8e2b68b03e171d70c2cb4ad2bfaf Reviewed-by: Laszlo Agocs --- src/opengl/qopenglshaderprogram.cpp | 3810 +++++++++++++++++++++++++++++++++++ 1 file changed, 3810 insertions(+) create mode 100644 src/opengl/qopenglshaderprogram.cpp (limited to 'src/opengl/qopenglshaderprogram.cpp') diff --git a/src/opengl/qopenglshaderprogram.cpp b/src/opengl/qopenglshaderprogram.cpp new file mode 100644 index 0000000000..c5e61859ee --- /dev/null +++ b/src/opengl/qopenglshaderprogram.cpp @@ -0,0 +1,3810 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglshaderprogram.h" +#include "qopenglextrafunctions.h" +#include "private/qopenglcontext_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(QT_OPENGL_ES_2) +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLShaderProgram + \brief The QOpenGLShaderProgram class allows OpenGL shader programs to be linked and used. + \since 5.0 + \ingroup painting-3D + \inmodule QtOpenGL + + \section1 Introduction + + This class supports shader programs written in the OpenGL Shading + Language (GLSL) and in the OpenGL/ES Shading Language (GLSL/ES). + + QOpenGLShader and QOpenGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + The following example creates a vertex shader program using the + supplied source \c{code}. Once compiled and linked, the shader + program is activated in the current QOpenGLContext by calling + QOpenGLShaderProgram::bind(): + + \snippet code/src_gui_qopenglshaderprogram.cpp 0 + + \section1 Writing Portable Shaders + + Shader programs can be difficult to reuse across OpenGL implementations + because of varying levels of support for standard vertex attributes and + uniform variables. In particular, GLSL/ES lacks all of the + standard variables that are present on desktop OpenGL systems: + \c{gl_Vertex}, \c{gl_Normal}, \c{gl_Color}, and so on. Desktop OpenGL + lacks the variable qualifiers \c{highp}, \c{mediump}, and \c{lowp}. + + The QOpenGLShaderProgram class makes the process of writing portable shaders + easier by prefixing all shader programs with the following lines on + desktop OpenGL: + + \code + #define highp + #define mediump + #define lowp + \endcode + + This makes it possible to run most GLSL/ES shader programs + on desktop systems. The programmer should restrict themselves + to just features that are present in GLSL/ES, and avoid + standard variable names that only work on the desktop. + + \section1 Simple Shader Example + + \snippet code/src_gui_qopenglshaderprogram.cpp 1 + + With the above shader program active, we can draw a green triangle + as follows: + + \snippet code/src_gui_qopenglshaderprogram.cpp 2 + + \section1 Binary Shaders and Programs + + Binary shaders may be specified using \c{glShaderBinary()} on + the return value from QOpenGLShader::shaderId(). The QOpenGLShader instance + containing the binary can then be added to the shader program with + addShader() and linked in the usual fashion with link(). + + Binary programs may be specified using \c{glProgramBinaryOES()} + on the return value from programId(). Then the application should + call link(), which will notice that the program has already been + specified and linked, allowing other operations to be performed + on the shader program. The shader program's id can be explicitly + created using the create() function. + + \section2 Caching Program Binaries + + As of Qt 5.9, support for caching program binaries on disk is built in. To + enable this, switch to using addCacheableShaderFromSourceCode() and + addCacheableShaderFromSourceFile(). With an OpenGL ES 3.x context or support + for \c{GL_ARB_get_program_binary}, this will transparently cache program + binaries under QStandardPaths::GenericCacheLocation or + QStandardPaths::CacheLocation. When support is not available, calling the + cacheable function variants is equivalent to the normal ones. + + \note Some drivers do not have any binary formats available, even though + they advertise the extension or offer OpenGL ES 3.0. In this case program + binary support will be disabled. + + \sa QOpenGLShader +*/ + +/*! + \class QOpenGLShader + \brief The QOpenGLShader class allows OpenGL shaders to be compiled. + \since 5.0 + \ingroup painting-3D + \inmodule QtOpenGL + + This class supports shaders written in the OpenGL Shading Language (GLSL) + and in the OpenGL/ES Shading Language (GLSL/ES). + + QOpenGLShader and QOpenGLShaderProgram shelter the programmer from the details of + compiling and linking vertex and fragment shaders. + + \sa QOpenGLShaderProgram +*/ + +/*! + \enum QOpenGLShader::ShaderTypeBit + This enum specifies the type of QOpenGLShader that is being created. + + \value Vertex Vertex shader written in the OpenGL Shading Language (GLSL). + \value Fragment Fragment shader written in the OpenGL Shading Language (GLSL). + \value Geometry Geometry shaders written in the OpenGL Shading Language (GLSL) + (requires OpenGL >= 3.2 or OpenGL ES >= 3.2). + \value TessellationControl Tessellation control shaders written in the OpenGL + shading language (GLSL) (requires OpenGL >= 4.0 or OpenGL ES >= 3.2). + \value TessellationEvaluation Tessellation evaluation shaders written in the OpenGL + shading language (GLSL) (requires OpenGL >= 4.0 or OpenGL ES >= 3.2). + \value Compute Compute shaders written in the OpenGL shading language (GLSL) + (requires OpenGL >= 4.3 or OpenGL ES >= 3.1). +*/ + +// For GLES 3.1/3.2 +#ifndef GL_GEOMETRY_SHADER +#define GL_GEOMETRY_SHADER 0x8DD9 +#endif +#ifndef GL_TESS_CONTROL_SHADER +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif +#ifndef GL_TESS_EVALUATION_SHADER +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#endif +#ifndef GL_COMPUTE_SHADER +#define GL_COMPUTE_SHADER 0x91B9 +#endif +#ifndef GL_MAX_GEOMETRY_OUTPUT_VERTICES +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#endif +#ifndef GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#endif +#ifndef GL_PATCH_VERTICES +#define GL_PATCH_VERTICES 0x8E72 +#endif +#ifndef GL_PATCH_DEFAULT_OUTER_LEVEL +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#endif +#ifndef GL_PATCH_DEFAULT_INNER_LEVEL +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#endif + +#ifndef QT_OPENGL_ES_2 +static inline bool isFormatGLES(const QSurfaceFormat &f) +{ + return (f.renderableType() == QSurfaceFormat::OpenGLES); +} +#endif + +static inline bool supportsGeometry(const QSurfaceFormat &f) +{ + return f.version() >= qMakePair(3, 2); +} + +static inline bool supportsCompute(const QSurfaceFormat &f) +{ +#ifndef QT_OPENGL_ES_2 + if (!isFormatGLES(f)) + return f.version() >= qMakePair(4, 3); + else + return f.version() >= qMakePair(3, 1); +#else + return f.version() >= qMakePair(3, 1); +#endif +} + +static inline bool supportsTessellation(const QSurfaceFormat &f) +{ +#ifndef QT_OPENGL_ES_2 + if (!isFormatGLES(f)) + return f.version() >= qMakePair(4, 0); + else + return f.version() >= qMakePair(3, 2); +#else + return f.version() >= qMakePair(3, 2); +#endif +} + +class QOpenGLShaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLShader) +public: + QOpenGLShaderPrivate(QOpenGLContext *ctx, QOpenGLShader::ShaderType type) + : shaderGuard(nullptr) + , shaderType(type) + , compiled(false) + , glfuncs(new QOpenGLExtraFunctions(ctx)) + , supportsGeometryShaders(false) + , supportsTessellationShaders(false) + , supportsComputeShaders(false) + { + if (shaderType & QOpenGLShader::Geometry) + supportsGeometryShaders = supportsGeometry(ctx->format()); + else if (shaderType & (QOpenGLShader::TessellationControl | QOpenGLShader::TessellationEvaluation)) + supportsTessellationShaders = supportsTessellation(ctx->format()); + else if (shaderType & QOpenGLShader::Compute) + supportsComputeShaders = supportsCompute(ctx->format()); + } + ~QOpenGLShaderPrivate(); + + QOpenGLSharedResourceGuard *shaderGuard; + QOpenGLShader::ShaderType shaderType; + bool compiled; + QString log; + + QOpenGLExtraFunctions *glfuncs; + + // Support for geometry shaders + bool supportsGeometryShaders; + // Support for tessellation shaders + bool supportsTessellationShaders; + // Support for compute shaders + bool supportsComputeShaders; + + + bool create(); + bool compile(QOpenGLShader *q); + void deleteShader(); +}; + +namespace { + void freeShaderFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteShader(id); + } +} + +QOpenGLShaderPrivate::~QOpenGLShaderPrivate() +{ + delete glfuncs; + if (shaderGuard) + shaderGuard->free(); +} + +bool QOpenGLShaderPrivate::create() +{ + QOpenGLContext *context = const_cast(QOpenGLContext::currentContext()); + if (!context) + return false; + GLuint shader = 0; + if (shaderType == QOpenGLShader::Vertex) { + shader = glfuncs->glCreateShader(GL_VERTEX_SHADER); + } else if (shaderType == QOpenGLShader::Geometry && supportsGeometryShaders) { + shader = glfuncs->glCreateShader(GL_GEOMETRY_SHADER); + } else if (shaderType == QOpenGLShader::TessellationControl && supportsTessellationShaders) { + shader = glfuncs->glCreateShader(GL_TESS_CONTROL_SHADER); + } else if (shaderType == QOpenGLShader::TessellationEvaluation && supportsTessellationShaders) { + shader = glfuncs->glCreateShader(GL_TESS_EVALUATION_SHADER); + } else if (shaderType == QOpenGLShader::Compute && supportsComputeShaders) { + shader = glfuncs->glCreateShader(GL_COMPUTE_SHADER); + } else if (shaderType == QOpenGLShader::Fragment) { + shader = glfuncs->glCreateShader(GL_FRAGMENT_SHADER); + } + if (!shader) { + qWarning("QOpenGLShader: could not create shader"); + return false; + } + shaderGuard = new QOpenGLSharedResourceGuard(context, shader, freeShaderFunc); + return true; +} + +bool QOpenGLShaderPrivate::compile(QOpenGLShader *q) +{ + GLuint shader = shaderGuard ? shaderGuard->id() : 0; + if (!shader) + return false; + + // Try to compile shader + glfuncs->glCompileShader(shader); + GLint value = 0; + + // Get compilation status + glfuncs->glGetShaderiv(shader, GL_COMPILE_STATUS, &value); + compiled = (value != 0); + + if (!compiled) { + // Compilation failed, try to provide some information about the failure + QString name = q->objectName(); + + const char *types[] = { + "Fragment", + "Vertex", + "Geometry", + "Tessellation Control", + "Tessellation Evaluation", + "Compute", + "" + }; + + const char *type = types[6]; + switch (shaderType) { + case QOpenGLShader::Fragment: + type = types[0]; break; + case QOpenGLShader::Vertex: + type = types[1]; break; + case QOpenGLShader::Geometry: + type = types[2]; break; + case QOpenGLShader::TessellationControl: + type = types[3]; break; + case QOpenGLShader::TessellationEvaluation: + type = types[4]; break; + case QOpenGLShader::Compute: + type = types[5]; break; + } + + // Get info and source code lengths + GLint infoLogLength = 0; + GLint sourceCodeLength = 0; + char *logBuffer = nullptr; + char *sourceCodeBuffer = nullptr; + + // Get the compilation info log + glfuncs->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); + + if (infoLogLength > 1) { + GLint temp; + logBuffer = new char [infoLogLength]; + glfuncs->glGetShaderInfoLog(shader, infoLogLength, &temp, logBuffer); + } + + // Get the source code + glfuncs->glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &sourceCodeLength); + + if (sourceCodeLength > 1) { + GLint temp; + sourceCodeBuffer = new char [sourceCodeLength]; + glfuncs->glGetShaderSource(shader, sourceCodeLength, &temp, sourceCodeBuffer); + } + + if (logBuffer) + log = QString::fromLatin1(logBuffer); + else + log = QLatin1String("failed"); + + if (name.isEmpty()) + qWarning("QOpenGLShader::compile(%s): %s", type, qPrintable(log)); + else + qWarning("QOpenGLShader::compile(%s)[%s]: %s", type, qPrintable(name), qPrintable(log)); + + // Dump the source code if we got it + if (sourceCodeBuffer) { + qWarning("*** Problematic %s shader source code ***\n" + "%ls\n" + "***", + type, qUtf16Printable(QString::fromLatin1(sourceCodeBuffer))); + } + + // Cleanup + delete [] logBuffer; + delete [] sourceCodeBuffer; + } + + return compiled; +} + +void QOpenGLShaderPrivate::deleteShader() +{ + if (shaderGuard) { + shaderGuard->free(); + shaderGuard = nullptr; + } +} + +/*! + Constructs a new QOpenGLShader object of the specified \a type + and attaches it to \a parent. If shader programs are not supported, + QOpenGLShaderProgram::hasOpenGLShaderPrograms() will return false. + + This constructor is normally followed by a call to compileSourceCode() + or compileSourceFile(). + + The shader will be associated with the current QOpenGLContext. + + \sa compileSourceCode(), compileSourceFile() +*/ +QOpenGLShader::QOpenGLShader(QOpenGLShader::ShaderType type, QObject *parent) + : QObject(*new QOpenGLShaderPrivate(QOpenGLContext::currentContext(), type), parent) +{ + Q_D(QOpenGLShader); + d->create(); +} + +/*! + Deletes this shader. If the shader has been attached to a + QOpenGLShaderProgram object, then the actual shader will stay around + until the QOpenGLShaderProgram is destroyed. +*/ +QOpenGLShader::~QOpenGLShader() +{ +} + +/*! + Returns the type of this shader. +*/ +QOpenGLShader::ShaderType QOpenGLShader::shaderType() const +{ + Q_D(const QOpenGLShader); + return d->shaderType; +} + +static const char qualifierDefines[] = + "#define lowp\n" + "#define mediump\n" + "#define highp\n"; + +#if defined(QT_OPENGL_ES) && !defined(QT_OPENGL_FORCE_SHADER_DEFINES) +// The "highp" qualifier doesn't exist in fragment shaders +// on all ES platforms. When it doesn't exist, use "mediump". +#define QOpenGL_REDEFINE_HIGHP 1 +static const char redefineHighp[] = + "#ifndef GL_FRAGMENT_PRECISION_HIGH\n" + "#define highp mediump\n" + "#endif\n"; +#endif + +// Boiler-plate header to have the layout attributes available we need later +static const char blendEquationAdvancedHeader[] = + "#ifdef GL_KHR_blend_equation_advanced\n" + "#extension GL_ARB_fragment_coord_conventions : enable\n" + "#extension GL_KHR_blend_equation_advanced : enable\n" + "#endif\n"; + +struct QVersionDirectivePosition +{ + Q_DECL_CONSTEXPR QVersionDirectivePosition(int position = 0, int line = -1) + : position(position) + , line(line) + { + } + + Q_DECL_CONSTEXPR bool hasPosition() const + { + return position > 0; + } + + const int position; + const int line; +}; + +static QVersionDirectivePosition findVersionDirectivePosition(const char *source) +{ + Q_ASSERT(source); + + // According to the GLSL spec the #version directive must not be + // preceded by anything but whitespace and comments. + // In order to not get confused by #version directives within a + // multiline comment, we need to do some minimal comment parsing + // while searching for the directive. + enum { + Normal, + StartOfLine, + PreprocessorDirective, + CommentStarting, + MultiLineComment, + SingleLineComment, + CommentEnding + } state = StartOfLine; + + const char *c = source; + while (*c) { + switch (state) { + case PreprocessorDirective: + if (*c == ' ' || *c == '\t') + break; + if (!strncmp(c, "version", strlen("version"))) { + // Found version directive + c += strlen("version"); + while (*c && *c != '\n') + ++c; + int splitPosition = c - source + 1; + int linePosition = int(std::count(source, c, '\n')) + 1; + return QVersionDirectivePosition(splitPosition, linePosition); + } else if (*c == '/') + state = CommentStarting; + else if (*c == '\n') + state = StartOfLine; + else + state = Normal; + break; + case StartOfLine: + if (*c == ' ' || *c == '\t') + break; + else if (*c == '#') { + state = PreprocessorDirective; + break; + } + state = Normal; + Q_FALLTHROUGH(); + case Normal: + if (*c == '/') + state = CommentStarting; + else if (*c == '\n') + state = StartOfLine; + break; + case CommentStarting: + if (*c == '*') + state = MultiLineComment; + else if (*c == '/') + state = SingleLineComment; + else + state = Normal; + break; + case MultiLineComment: + if (*c == '*') + state = CommentEnding; + break; + case SingleLineComment: + if (*c == '\n') + state = Normal; + break; + case CommentEnding: + if (*c == '/') + state = Normal; + else if (*c != QLatin1Char('*')) + state = MultiLineComment; + break; + } + ++c; + } + + return QVersionDirectivePosition(0, 1); +} + +/*! + Sets the \a source code for this shader and compiles it. + Returns \c true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const char *source) +{ + Q_D(QOpenGLShader); + // This method breaks the shader code into two parts: + // 1. Up to and including an optional #version directive. + // 2. The rest. + // If a #version directive exists, qualifierDefines and redefineHighp + // are inserted after. Otherwise they are inserted right at the start. + // In both cases a #line directive is appended in order to compensate + // for line number changes in case of compiler errors. + + if (d->shaderGuard && d->shaderGuard->id() && source) { + const QVersionDirectivePosition versionDirectivePosition = findVersionDirectivePosition(source); + + QVarLengthArray sourceChunks; + QVarLengthArray sourceChunkLengths; + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + if (versionDirectivePosition.hasPosition()) { + // Append source up to and including the #version directive + sourceChunks.append(source); + sourceChunkLengths.append(GLint(versionDirectivePosition.position)); + } else { + // QTBUG-55733: Intel on Windows with Compatibility profile requires a #version always + if (ctx->format().profile() == QSurfaceFormat::CompatibilityProfile) { + const char *vendor = reinterpret_cast(ctx->functions()->glGetString(GL_VENDOR)); + if (vendor && !strcmp(vendor, "Intel")) { + static const char version110[] = "#version 110\n"; + sourceChunks.append(version110); + sourceChunkLengths.append(GLint(sizeof(version110)) - 1); + } + } + } + if (d->shaderType == Fragment) { + sourceChunks.append(blendEquationAdvancedHeader); + sourceChunkLengths.append(GLint(sizeof(blendEquationAdvancedHeader) - 1)); + } + + // The precision qualifiers are useful on OpenGL/ES systems, + // but usually not present on desktop systems. + const QSurfaceFormat currentSurfaceFormat = ctx->format(); + QOpenGLContextPrivate *ctx_d = QOpenGLContextPrivate::get(QOpenGLContext::currentContext()); + if (currentSurfaceFormat.renderableType() == QSurfaceFormat::OpenGL + || ctx_d->workaround_missingPrecisionQualifiers +#ifdef QT_OPENGL_FORCE_SHADER_DEFINES + || true +#endif + ) { + sourceChunks.append(qualifierDefines); + sourceChunkLengths.append(GLint(sizeof(qualifierDefines) - 1)); + } + +#ifdef QOpenGL_REDEFINE_HIGHP + if (d->shaderType == Fragment && !ctx_d->workaround_missingPrecisionQualifiers + && QOpenGLContext::currentContext()->isOpenGLES()) { + sourceChunks.append(redefineHighp); + sourceChunkLengths.append(GLint(sizeof(redefineHighp) - 1)); + } +#endif + + QByteArray lineDirective; + // #line is rejected by some drivers: + // "2.1 Mesa 8.1-devel (git-48a3d4e)" or "MESA 2.1 Mesa 8.1-devel" + const char *version = reinterpret_cast(ctx->functions()->glGetString(GL_VERSION)); + if (!version || !strstr(version, "2.1 Mesa 8")) { + // Append #line directive in order to compensate for text insertion + lineDirective = QStringLiteral("#line %1\n").arg(versionDirectivePosition.line).toUtf8(); + sourceChunks.append(lineDirective.constData()); + sourceChunkLengths.append(GLint(lineDirective.length())); + } + + // Append rest of shader code + sourceChunks.append(source + versionDirectivePosition.position); + sourceChunkLengths.append(GLint(qstrlen(source + versionDirectivePosition.position))); + + d->glfuncs->glShaderSource(d->shaderGuard->id(), sourceChunks.size(), sourceChunks.data(), sourceChunkLengths.data()); + return d->compile(this); + } else { + return false; + } +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns \c true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const QByteArray& source) +{ + return compileSourceCode(source.constData()); +} + +/*! + \overload + + Sets the \a source code for this shader and compiles it. + Returns \c true if the source was successfully compiled, false otherwise. + + \sa compileSourceFile() +*/ +bool QOpenGLShader::compileSourceCode(const QString& source) +{ + return compileSourceCode(source.toLatin1().constData()); +} + +/*! + Sets the source code for this shader to the contents of \a fileName + and compiles it. Returns \c true if the file could be opened and the + source compiled, false otherwise. + + \sa compileSourceCode() +*/ +bool QOpenGLShader::compileSourceFile(const QString& fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + qWarning() << "QOpenGLShader: Unable to open file" << fileName; + return false; + } + + QByteArray contents = file.readAll(); + return compileSourceCode(contents.constData()); +} + +/*! + Returns the source code for this shader. + + \sa compileSourceCode() +*/ +QByteArray QOpenGLShader::sourceCode() const +{ + Q_D(const QOpenGLShader); + GLuint shader = d->shaderGuard ? d->shaderGuard->id() : 0; + if (!shader) + return QByteArray(); + GLint size = 0; + d->glfuncs->glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &size); + if (size <= 0) + return QByteArray(); + GLint len = 0; + char *source = new char [size]; + d->glfuncs->glGetShaderSource(shader, size, &len, source); + QByteArray src(source); + delete [] source; + return src; +} + +/*! + Returns \c true if this shader has been compiled; false otherwise. + + \sa compileSourceCode(), compileSourceFile() +*/ +bool QOpenGLShader::isCompiled() const +{ + Q_D(const QOpenGLShader); + return d->compiled; +} + +/*! + Returns the errors and warnings that occurred during the last compile. + + \sa compileSourceCode(), compileSourceFile() +*/ +QString QOpenGLShader::log() const +{ + Q_D(const QOpenGLShader); + return d->log; +} + +/*! + Returns the OpenGL identifier associated with this shader. + + \sa QOpenGLShaderProgram::programId() +*/ +GLuint QOpenGLShader::shaderId() const +{ + Q_D(const QOpenGLShader); + return d->shaderGuard ? d->shaderGuard->id() : 0; +} + +class QOpenGLShaderProgramPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QOpenGLShaderProgram) +public: + QOpenGLShaderProgramPrivate() + : programGuard(nullptr) + , linked(false) + , inited(false) + , removingShaders(false) + , glfuncs(new QOpenGLExtraFunctions) +#ifndef QT_OPENGL_ES_2 + , tessellationFuncs(nullptr) +#endif + , linkBinaryRecursion(false) + { + } + ~QOpenGLShaderProgramPrivate(); + + QOpenGLSharedResourceGuard *programGuard; + bool linked; + bool inited; + bool removingShaders; + + QString log; + QList shaders; + QList anonShaders; + + QOpenGLExtraFunctions *glfuncs; +#ifndef QT_OPENGL_ES_2 + // for tessellation features not in GLES 3.2 + QOpenGLFunctions_4_0_Core *tessellationFuncs; +#endif + + bool hasShader(QOpenGLShader::ShaderType type) const; + + QOpenGLProgramBinaryCache::ProgramDesc binaryProgram; + bool isCacheDisabled() const; + bool compileCacheable(); + bool linkBinary(); + + bool linkBinaryRecursion; +}; + +namespace { + void freeProgramFunc(QOpenGLFunctions *funcs, GLuint id) + { + funcs->glDeleteProgram(id); + } +} + + +QOpenGLShaderProgramPrivate::~QOpenGLShaderProgramPrivate() +{ + delete glfuncs; + if (programGuard) + programGuard->free(); +} + +bool QOpenGLShaderProgramPrivate::hasShader(QOpenGLShader::ShaderType type) const +{ + for (QOpenGLShader *shader : shaders) { + if (shader->shaderType() == type) + return true; + } + return false; +} + +/*! + Constructs a new shader program and attaches it to \a parent. + The program will be invalid until addShader() is called. + + The shader program will be associated with the current QOpenGLContext. + + \sa addShader() +*/ +QOpenGLShaderProgram::QOpenGLShaderProgram(QObject *parent) + : QObject(*new QOpenGLShaderProgramPrivate, parent) +{ +} + +/*! + Deletes this shader program. +*/ +QOpenGLShaderProgram::~QOpenGLShaderProgram() +{ +} + +/*! + Requests the shader program's id to be created immediately. Returns \c true + if successful; \c false otherwise. + + This function is primarily useful when combining QOpenGLShaderProgram + with other OpenGL functions that operate directly on the shader + program id, like \c {GL_OES_get_program_binary}. + + When the shader program is used normally, the shader program's id will + be created on demand. + + \sa programId() + + \since 5.3 + */ +bool QOpenGLShaderProgram::create() +{ + return init(); +} + +bool QOpenGLShaderProgram::init() +{ + Q_D(QOpenGLShaderProgram); + if ((d->programGuard && d->programGuard->id()) || d->inited) + return true; + d->inited = true; + QOpenGLContext *context = const_cast(QOpenGLContext::currentContext()); + if (!context) + return false; + d->glfuncs->initializeOpenGLFunctions(); + +#ifndef QT_OPENGL_ES_2 + if (!context->isOpenGLES() && context->format().version() >= qMakePair(4, 0)) { + d->tessellationFuncs = context->versionFunctions(); + d->tessellationFuncs->initializeOpenGLFunctions(); + } +#endif + + GLuint program = d->glfuncs->glCreateProgram(); + if (!program) { + qWarning("QOpenGLShaderProgram: could not create shader program"); + return false; + } + if (d->programGuard) + delete d->programGuard; + d->programGuard = new QOpenGLSharedResourceGuard(context, program, freeProgramFunc); + return true; +} + +/*! + Adds a compiled \a shader to this shader program. Returns \c true + if the shader could be added, or false otherwise. + + Ownership of the \a shader object remains with the caller. + It will not be deleted when this QOpenGLShaderProgram instance + is deleted. This allows the caller to add the same shader + to multiple shader programs. + + \sa addShaderFromSourceCode(), addShaderFromSourceFile() + \sa removeShader(), link(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShader(QOpenGLShader *shader) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->shaders.contains(shader)) + return true; // Already added to this shader program. + if (d->programGuard && d->programGuard->id() && shader) { + if (!shader->d_func()->shaderGuard || !shader->d_func()->shaderGuard->id()) + return false; + if (d->programGuard->group() != shader->d_func()->shaderGuard->group()) { + qWarning("QOpenGLShaderProgram::addShader: Program and shader are not associated with same context."); + return false; + } + d->glfuncs->glAttachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + d->linked = false; // Program needs to be relinked. + d->shaders.append(shader); + connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); + return true; + } else { + return false; + } +} + +/*! + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns \c true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + QOpenGLShader *shader = new QOpenGLShader(type, this); + if (!shader->compileSourceCode(source)) { + d->log = shader->log(); + delete shader; + return false; + } + d->anonShaders.append(shader); + return addShader(shader); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns \c true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray& source) +{ + return addShaderFromSourceCode(type, source.constData()); +} + +/*! + \overload + + Compiles \a source as a shader of the specified \a type and + adds it to this shader program. Returns \c true if compilation + was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceFile() + \sa removeShader(), link(), log(), removeAllShaders() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString& source) +{ + return addShaderFromSourceCode(type, source.toLatin1().constData()); +} + +/*! + Compiles the contents of \a fileName as a shader of the specified + \a type and adds it to this shader program. Returns \c true if + compilation was successful, false otherwise. The compilation errors + and warnings will be made available via log(). + + This function is intended to be a short-cut for quickly + adding vertex and fragment shaders to a shader program without + creating an instance of QOpenGLShader first. + + \sa addShader(), addShaderFromSourceCode() +*/ +bool QOpenGLShaderProgram::addShaderFromSourceFile + (QOpenGLShader::ShaderType type, const QString& fileName) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + QOpenGLShader *shader = new QOpenGLShader(type, this); + if (!shader->compileSourceFile(fileName)) { + d->log = shader->log(); + delete shader; + return false; + } + d->anonShaders.append(shader); + return addShader(shader); +} + +/*! + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the shader has been registered or, in the non-cached case, + compiled successfully; false if there was an error. The compilation error + messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const char *source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + return addCacheableShaderFromSourceCode(type, QByteArray(source)); +} + +static inline QShader::Stage qt_shaderTypeToStage(QOpenGLShader::ShaderType type) +{ + switch (type) { + case QOpenGLShader::Vertex: + return QShader::VertexStage; + case QOpenGLShader::Fragment: + return QShader::FragmentStage; + case QOpenGLShader::Geometry: + return QShader::GeometryStage; + case QOpenGLShader::TessellationControl: + return QShader::TessellationControlStage; + case QOpenGLShader::TessellationEvaluation: + return QShader::TessellationEvaluationStage; + case QOpenGLShader::Compute: + return QShader::ComputeStage; + } + return QShader::VertexStage; +} + +static inline QOpenGLShader::ShaderType qt_shaderStageToType(QShader::Stage stage) +{ + switch (stage) { + case QShader::VertexStage: + return QOpenGLShader::Vertex; + case QShader::TessellationControlStage: + return QOpenGLShader::TessellationControl; + case QShader::TessellationEvaluationStage: + return QOpenGLShader::TessellationEvaluation; + case QShader::GeometryStage: + return QOpenGLShader::Geometry; + case QShader::FragmentStage: + return QOpenGLShader::Fragment; + case QShader::ComputeStage: + return QOpenGLShader::Compute; + } + return QOpenGLShader::Vertex; +} + +/*! + \overload + + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the shader has been registered or, in the non-cached case, + compiled successfully; false if there was an error. The compilation error + messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QByteArray &source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + d->binaryProgram.shaders.append(QOpenGLProgramBinaryCache::ShaderDesc(qt_shaderTypeToStage(type), source)); + return true; +} + +/*! + \overload + + Registers the shader of the specified \a type and \a source to this + program. Unlike addShaderFromSourceCode(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceCode(). + + \since 5.9 + \sa addShaderFromSourceCode(), addCacheableShaderFromSourceFile() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceCode(QOpenGLShader::ShaderType type, const QString &source) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceCode(type, source); + + return addCacheableShaderFromSourceCode(type, source.toUtf8().constData()); +} + +/*! + Registers the shader of the specified \a type and \a fileName to this + program. Unlike addShaderFromSourceFile(), this function does not perform + compilation. Compilation is deferred to link(), and may not happen at all, + because link() may potentially use a program binary from Qt's shader disk + cache. This will typically lead to a significant increase in performance. + + \return true if the file has been read successfully, false if the file could + not be opened or the normal, non-cached compilation of the shader has + failed. The compilation error messages can be retrieved via log(). + + When the disk cache is disabled, via Qt::AA_DisableShaderDiskCache for + example, or the OpenGL context has no support for context binaries, calling + this function is equivalent to addShaderFromSourceFile(). + + \since 5.9 + \sa addShaderFromSourceFile(), addCacheableShaderFromSourceCode() + */ +bool QOpenGLShaderProgram::addCacheableShaderFromSourceFile(QOpenGLShader::ShaderType type, const QString &fileName) +{ + Q_D(QOpenGLShaderProgram); + if (!init()) + return false; + if (d->isCacheDisabled()) + return addShaderFromSourceFile(type, fileName); + + QOpenGLProgramBinaryCache::ShaderDesc shader(qt_shaderTypeToStage(type)); + // NB! It could be tempting to defer reading the file contents and just + // hash the filename as the cache key, perhaps combined with last-modified + // timestamp checks. However, this would raise a number of issues (no + // timestamps for files in the resource system; preference for global, not + // per-application cache items (where filenames may clash); resource-based + // shaders from libraries like Qt Quick; etc.), so just avoid it. + QFile f(fileName); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + shader.source = f.readAll(); + f.close(); + } else { + qWarning("QOpenGLShaderProgram: Unable to open file %s", qPrintable(fileName)); + return false; + } + d->binaryProgram.shaders.append(shader); + return true; +} + +/*! + Removes \a shader from this shader program. The object is not deleted. + + The shader program must be valid in the current QOpenGLContext. + + \sa addShader(), link(), removeAllShaders() +*/ +void QOpenGLShaderProgram::removeShader(QOpenGLShader *shader) +{ + Q_D(QOpenGLShaderProgram); + if (d->programGuard && d->programGuard->id() + && shader && shader->d_func()->shaderGuard) + { + d->glfuncs->glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + } + d->linked = false; // Program needs to be relinked. + if (shader) { + d->shaders.removeAll(shader); + d->anonShaders.removeAll(shader); + disconnect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); + } +} + +/*! + Returns a list of all shaders that have been added to this shader + program using addShader(). + + \sa addShader(), removeShader() +*/ +QList QOpenGLShaderProgram::shaders() const +{ + Q_D(const QOpenGLShaderProgram); + return d->shaders; +} + +/*! + Removes all of the shaders that were added to this program previously. + The QOpenGLShader objects for the shaders will not be deleted if they + were constructed externally. QOpenGLShader objects that are constructed + internally by QOpenGLShaderProgram will be deleted. + + \sa addShader(), removeShader() +*/ +void QOpenGLShaderProgram::removeAllShaders() +{ + Q_D(QOpenGLShaderProgram); + d->removingShaders = true; + for (QOpenGLShader *shader : qAsConst(d->shaders)) { + if (d->programGuard && d->programGuard->id() + && shader && shader->d_func()->shaderGuard) + { + d->glfuncs->glDetachShader(d->programGuard->id(), shader->d_func()->shaderGuard->id()); + } + } + // Delete shader objects that were created anonymously. + qDeleteAll(d->anonShaders); + d->shaders.clear(); + d->anonShaders.clear(); + d->binaryProgram = QOpenGLProgramBinaryCache::ProgramDesc(); + d->linked = false; // Program needs to be relinked. + d->removingShaders = false; +} + +/*! + Links together the shaders that were added to this program with + addShader(). Returns \c true if the link was successful or + false otherwise. If the link failed, the error messages can + be retrieved with log(). + + Subclasses can override this function to initialize attributes + and uniform variables for use in specific shader programs. + + If the shader program was already linked, calling this + function again will force it to be re-linked. + + When shaders were added to this program via + addCacheableShaderFromSourceCode() or addCacheableShaderFromSourceFile(), + program binaries are supported, and a cached binary is available on disk, + actual compilation and linking are skipped. Instead, link() will initialize + the program with the binary blob via glProgramBinary(). If there is no + cached version of the program or it was generated with a different driver + version, the shaders will be compiled from source and the program will get + linked normally. This allows seamless upgrading of the graphics drivers, + without having to worry about potentially incompatible binary formats. + + \sa addShader(), log() +*/ +bool QOpenGLShaderProgram::link() +{ + Q_D(QOpenGLShaderProgram); + GLuint program = d->programGuard ? d->programGuard->id() : 0; + if (!program) + return false; + + if (!d->linkBinaryRecursion && d->shaders.isEmpty() && !d->binaryProgram.shaders.isEmpty()) + return d->linkBinary(); + + GLint value; + if (d->shaders.isEmpty()) { + // If there are no explicit shaders, then it is possible that the + // application added a program binary with glProgramBinaryOES(), or + // otherwise populated the shaders itself. This is also the case when + // we are recursively called back from linkBinary() after a successful + // glProgramBinary(). Check to see if the program is already linked and + // bail out if so. + value = 0; + d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + if (d->linked) + return true; + } + + d->glfuncs->glLinkProgram(program); + value = 0; + d->glfuncs->glGetProgramiv(program, GL_LINK_STATUS, &value); + d->linked = (value != 0); + value = 0; + d->glfuncs->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value); + d->log = QString(); + if (value > 1) { + char *logbuf = new char [value]; + GLint len; + d->glfuncs->glGetProgramInfoLog(program, value, &len, logbuf); + d->log = QString::fromLatin1(logbuf); + if (!d->linked && !d->linkBinaryRecursion) { + QString name = objectName(); + if (name.isEmpty()) + qWarning("QOpenGLShader::link: %ls", qUtf16Printable(d->log)); + else + qWarning("QOpenGLShader::link[%ls]: %ls", qUtf16Printable(name), qUtf16Printable(d->log)); + } + delete [] logbuf; + } + return d->linked; +} + +/*! + Returns \c true if this shader program has been linked; false otherwise. + + \sa link() +*/ +bool QOpenGLShaderProgram::isLinked() const +{ + Q_D(const QOpenGLShaderProgram); + return d->linked; +} + +/*! + Returns the errors and warnings that occurred during the last link() + or addShader() with explicitly specified source code. + + \sa link() +*/ +QString QOpenGLShaderProgram::log() const +{ + Q_D(const QOpenGLShaderProgram); + return d->log; +} + +/*! + Binds this shader program to the active QOpenGLContext and makes + it the current shader program. Any previously bound shader program + is released. This is equivalent to calling \c{glUseProgram()} on + programId(). Returns \c true if the program was successfully bound; + false otherwise. If the shader program has not yet been linked, + or it needs to be re-linked, this function will call link(). + + \sa link(), release() +*/ +bool QOpenGLShaderProgram::bind() +{ + Q_D(QOpenGLShaderProgram); + GLuint program = d->programGuard ? d->programGuard->id() : 0; + if (!program) + return false; + if (!d->linked && !link()) + return false; +#ifndef QT_NO_DEBUG + if (d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) { + qWarning("QOpenGLShaderProgram::bind: program is not valid in the current context."); + return false; + } +#endif + d->glfuncs->glUseProgram(program); + return true; +} + +/*! + Releases the active shader program from the current QOpenGLContext. + This is equivalent to calling \c{glUseProgram(0)}. + + \sa bind() +*/ +void QOpenGLShaderProgram::release() +{ + Q_D(QOpenGLShaderProgram); +#ifndef QT_NO_DEBUG + if (d->programGuard && d->programGuard->group() != QOpenGLContextGroup::currentContextGroup()) + qWarning("QOpenGLShaderProgram::release: program is not valid in the current context."); +#endif + d->glfuncs->glUseProgram(0); +} + +/*! + Returns the OpenGL identifier associated with this shader program. + + \sa QOpenGLShader::shaderId() +*/ +GLuint QOpenGLShaderProgram::programId() const +{ + Q_D(const QOpenGLShaderProgram); + GLuint id = d->programGuard ? d->programGuard->id() : 0; + if (id) + return id; + + // Create the identifier if we don't have one yet. This is for + // applications that want to create the attached shader configuration + // themselves, particularly those using program binaries. + if (!const_cast(this)->init()) + return 0; + return d->programGuard ? d->programGuard->id() : 0; +} + +/*! + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const char *name, int location) +{ + Q_D(QOpenGLShaderProgram); + if (!init() || !d->programGuard || !d->programGuard->id()) + return; + d->glfuncs->glBindAttribLocation(d->programGuard->id(), location, name); + d->linked = false; // Program needs to be relinked. +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const QByteArray& name, int location) +{ + bindAttributeLocation(name.constData(), location); +} + +/*! + \overload + + Binds the attribute \a name to the specified \a location. This + function can be called before or after the program has been linked. + Any attributes that have not been explicitly bound when the program + is linked will be assigned locations automatically. + + When this function is called after the program has been linked, + the program will need to be relinked for the change to take effect. + + \sa attributeLocation() +*/ +void QOpenGLShaderProgram::bindAttributeLocation(const QString& name, int location) +{ + bindAttributeLocation(name.toLatin1().constData(), location); +} + +/*! + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const char *name) const +{ + Q_D(const QOpenGLShaderProgram); + if (d->linked && d->programGuard && d->programGuard->id()) { + return d->glfuncs->glGetAttribLocation(d->programGuard->id(), name); + } else { + qWarning("QOpenGLShaderProgram::attributeLocation(%s): shader program is not linked", name); + return -1; + } +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const QByteArray& name) const +{ + return attributeLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the attribute \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + attribute for this shader program. + + \sa uniformLocation(), bindAttributeLocation() +*/ +int QOpenGLShaderProgram::attributeLocation(const QString& name) const +{ + return attributeLocation(name.toLatin1().constData()); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, GLfloat value) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) + d->glfuncs->glVertexAttrib1fv(location, &value); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, GLfloat value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, GLfloat x, GLfloat y) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) { + GLfloat values[2] = {x, y}; + d->glfuncs->glVertexAttrib2fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, GLfloat x, GLfloat y) +{ + setAttributeValue(attributeLocation(name), x, y); +} + +/*! + Sets the attribute at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[3] = {x, y, z}; + d->glfuncs->glVertexAttrib3fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setAttributeValue(attributeLocation(name), x, y, z); +} + +/*! + Sets the attribute at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + d->glfuncs->glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setAttributeValue(attributeLocation(name), x, y, z, w); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector2D& value) +{ + Q_D(QOpenGLShaderProgram); + if (location != -1) + d->glfuncs->glVertexAttrib2fv(location, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector2D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector3D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glVertexAttrib3fv(location, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector3D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QVector4D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glVertexAttrib4fv(location, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QVector4D& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(int location, const QColor& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(value.redF()), GLfloat(value.greenF()), + GLfloat(value.blueF()), GLfloat(value.alphaF())}; + d->glfuncs->glVertexAttrib4fv(location, values); + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to \a value. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue(const char *name, const QColor& value) +{ + setAttributeValue(attributeLocation(name), value); +} + +/*! + Sets the attribute at \a location in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (int location, const GLfloat *values, int columns, int rows) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (rows < 1 || rows > 4) { + qWarning("QOpenGLShaderProgram::setAttributeValue: rows %d not supported", rows); + return; + } + if (location != -1) { + while (columns-- > 0) { + if (rows == 1) + d->glfuncs->glVertexAttrib1fv(location, values); + else if (rows == 2) + d->glfuncs->glVertexAttrib2fv(location, values); + else if (rows == 3) + d->glfuncs->glVertexAttrib3fv(location, values); + else + d->glfuncs->glVertexAttrib4fv(location, values); + values += rows; + ++location; + } + } +} + +/*! + \overload + + Sets the attribute called \a name in the current context to the + contents of \a values, which contains \a columns elements, each + consisting of \a rows elements. The \a rows value should be + 1, 2, 3, or 4. This function is typically used to set matrix + values and column vectors. + + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::setAttributeValue + (const char *name, const GLfloat *values, int columns, int rows) +{ + setAttributeValue(attributeLocation(name), values, columns, rows); +} + +/*! + Sets an array of vertex \a values on the attribute at \a location + in this shader program. The \a tupleSize indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const GLfloat *values, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 2D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector2D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 3D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector3D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of 4D vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, const QVector4D *values, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, + stride, values); + } +} + +/*! + Sets an array of vertex \a values on the attribute at \a location + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The \a type indicates the type of elements in the \a values array, + usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize + indicates the number of components per vertex: 1, 2, 3, or 4. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + The setAttributeBuffer() function can be used to set the attribute + array to an offset within a vertex buffer. + + \note Normalization will be enabled. If this is not desired, call + glVertexAttribPointer directly through QOpenGLFunctions. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray(), setAttributeBuffer() +*/ +void QOpenGLShaderProgram::setAttributeArray + (int location, GLenum type, const void *values, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, + stride, values); + } +} + +/*! + \overload + + Sets an array of vertex \a values on the attribute called \a name + in this shader program. The \a tupleSize indicates the number of + components per vertex (1, 2, 3, or 4), and the \a stride indicates + the number of bytes between vertices. A default \a stride value + of zero indicates that the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const GLfloat *values, int tupleSize, int stride) +{ + setAttributeArray(attributeLocation(name), values, tupleSize, stride); +} + +/*! + \overload + + Sets an array of 2D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector2D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 3D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector3D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of 4D vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The array will become active when enableAttributeArray() is called + on \a name. Otherwise the value specified with setAttributeValue() + for \a name will be used. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, const QVector4D *values, int stride) +{ + setAttributeArray(attributeLocation(name), values, stride); +} + +/*! + \overload + + Sets an array of vertex \a values on the attribute called \a name + in this shader program. The \a stride indicates the number of bytes + between vertices. A default \a stride value of zero indicates that + the vertices are densely packed in \a values. + + The \a type indicates the type of elements in the \a values array, + usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a tupleSize + indicates the number of components per vertex: 1, 2, 3, or 4. + + The array will become active when enableAttributeArray() is called + on the \a name. Otherwise the value specified with + setAttributeValue() for \a name will be used. + + The setAttributeBuffer() function can be used to set the attribute + array to an offset within a vertex buffer. + + \sa setAttributeValue(), setUniformValue(), enableAttributeArray() + \sa disableAttributeArray(), setAttributeBuffer() +*/ +void QOpenGLShaderProgram::setAttributeArray + (const char *name, GLenum type, const void *values, int tupleSize, int stride) +{ + setAttributeArray(attributeLocation(name), type, values, tupleSize, stride); +} + +/*! + Sets an array of vertex values on the attribute at \a location in + this shader program, starting at a specific \a offset in the + currently bound vertex buffer. The \a stride indicates the number + of bytes between vertices. A default \a stride value of zero + indicates that the vertices are densely packed in the value array. + + The \a type indicates the type of elements in the vertex value + array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a + tupleSize indicates the number of components per vertex: 1, 2, 3, + or 4. + + The array will become active when enableAttributeArray() is called + on the \a location. Otherwise the value specified with + setAttributeValue() for \a location will be used. + + \note Normalization will be enabled. If this is not desired, call + glVertexAttribPointer directly through QOpenGLFunctions. + + \sa setAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeBuffer + (int location, GLenum type, int offset, int tupleSize, int stride) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + d->glfuncs->glVertexAttribPointer(location, tupleSize, type, GL_TRUE, stride, + reinterpret_cast(qintptr(offset))); + } +} + +/*! + \overload + + Sets an array of vertex values on the attribute called \a name + in this shader program, starting at a specific \a offset in the + currently bound vertex buffer. The \a stride indicates the number + of bytes between vertices. A default \a stride value of zero + indicates that the vertices are densely packed in the value array. + + The \a type indicates the type of elements in the vertex value + array, usually \c{GL_FLOAT}, \c{GL_UNSIGNED_BYTE}, etc. The \a + tupleSize indicates the number of components per vertex: 1, 2, 3, + or 4. + + The array will become active when enableAttributeArray() is called + on the \a name. Otherwise the value specified with + setAttributeValue() for \a name will be used. + + \sa setAttributeArray() +*/ +void QOpenGLShaderProgram::setAttributeBuffer + (const char *name, GLenum type, int offset, int tupleSize, int stride) +{ + setAttributeBuffer(attributeLocation(name), type, offset, tupleSize, stride); +} + +/*! + Enables the vertex array at \a location in this shader program + so that the value set by setAttributeArray() on \a location + will be used by the shader program. + + \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::enableAttributeArray(int location) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glEnableVertexAttribArray(location); +} + +/*! + \overload + + Enables the vertex array called \a name in this shader program + so that the value set by setAttributeArray() on \a name + will be used by the shader program. + + \sa disableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::enableAttributeArray(const char *name) +{ + enableAttributeArray(attributeLocation(name)); +} + +/*! + Disables the vertex array at \a location in this shader program + that was enabled by a previous call to enableAttributeArray(). + + \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::disableAttributeArray(int location) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glDisableVertexAttribArray(location); +} + +/*! + \overload + + Disables the vertex array called \a name in this shader program + that was enabled by a previous call to enableAttributeArray(). + + \sa enableAttributeArray(), setAttributeArray(), setAttributeValue() + \sa setUniformValue() +*/ +void QOpenGLShaderProgram::disableAttributeArray(const char *name) +{ + disableAttributeArray(attributeLocation(name)); +} + +/*! + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const char *name) const +{ + Q_D(const QOpenGLShaderProgram); + Q_UNUSED(d); + if (d->linked && d->programGuard && d->programGuard->id()) { + return d->glfuncs->glGetUniformLocation(d->programGuard->id(), name); + } else { + qWarning("QOpenGLShaderProgram::uniformLocation(%s): shader program is not linked", name); + return -1; + } +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const QByteArray& name) const +{ + return uniformLocation(name.constData()); +} + +/*! + \overload + + Returns the location of the uniform variable \a name within this shader + program's parameter list. Returns -1 if \a name is not a valid + uniform variable for this shader program. + + \sa attributeLocation() +*/ +int QOpenGLShaderProgram::uniformLocation(const QString& name) const +{ + return uniformLocation(name.toLatin1().constData()); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLfloat value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1fv(location, 1, &value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLfloat value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLint value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + This function should be used when setting sampler values. + + \note This function is not aware of unsigned int support in modern OpenGL + versions and therefore treats \a value as a GLint and calls glUniform1i. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLuint value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1i(location, value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. This function should be used when setting sampler values. + + \note This function is not aware of unsigned int support in modern OpenGL + versions and therefore treats \a value as a GLint and calls glUniform1i. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLuint value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, GLfloat x, GLfloat y) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[2] = {x, y}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 2D vector (\a x, \a y). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, GLfloat x, GLfloat y) +{ + setUniformValue(uniformLocation(name), x, y); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[3] = {x, y, z}; + d->glfuncs->glUniform3fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 3D vector (\a x, \a y, \a z). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z) +{ + setUniformValue(uniformLocation(name), x, y, z); +} + +/*! + Sets the uniform variable at \a location in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (int location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {x, y, z, w}; + d->glfuncs->glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the 4D vector (\a x, \a y, \a z, \a w). + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + setUniformValue(uniformLocation(name), x, y, z, w); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector2D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform2fv(location, 1, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector2D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector3D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform3fv(location, 1, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector3D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QVector4D& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform4fv(location, 1, reinterpret_cast(&value)); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QVector4D& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QColor& color) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(color.redF()), GLfloat(color.greenF()), + GLfloat(color.blueF()), GLfloat(color.alphaF())}; + d->glfuncs->glUniform4fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to + the red, green, blue, and alpha components of \a color. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QColor& color) +{ + setUniformValue(uniformLocation(name), color); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QPoint& point) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QPoint& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QPointF& point) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(point.x()), GLfloat(point.y())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the x and y coordinates of \a point. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QPointF& point) +{ + setUniformValue(uniformLocation(name), point); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QSize& size) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QSize& size) +{ + setUniformValue(uniformLocation(name), size); +} + +/*! + Sets the uniform variable at \a location in the current context to + the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QSizeF& size) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat values[4] = {GLfloat(size.width()), GLfloat(size.height())}; + d->glfuncs->glUniform2fv(location, 1, values); + } +} + +/*! + \overload + + Sets the uniform variable associated with \a name in the current + context to the width and height of the given \a size. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QSizeF& size) +{ + setUniformValue(uniformLocation(name), size); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniformMatrix2fv(location, 1, GL_FALSE, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x2 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x3 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat2x3, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec3. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform3fv(location, 2, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x3 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat2x3, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec3. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 2x4 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat2x4, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix2x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform4fv(location, 2, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x4 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat2x4, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix2x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x2 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat3x2, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec2. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform2fv(location, 3, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x2 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat3x2, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec2. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x3 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 3x4 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat3x4, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix3x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform4fv(location, 3, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x4 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat3x4, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix3x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x2 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat4x2, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec2. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x2& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform2fv(location, 4, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x2 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat4x2, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec2. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x2& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x3 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat4x3, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec3. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x3& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniform3fv(location, 4, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x3 matrix \a value. + + \note This function is not aware of non square matrix support, + that is, GLSL types like mat4x3, that is present in modern OpenGL + versions. Instead, it treats the uniform as an array of vec3. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x3& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QMatrix4x4& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + d->glfuncs->glUniformMatrix4fv(location, 1, GL_FALSE, value.constData()); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 2x2 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix2fv(location, 1, GL_FALSE, value[0]); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 3x3 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, value[0]); +} + +/*! + \overload + + Sets the uniform variable at \a location in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4]) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniformMatrix4fv(location, 1, GL_FALSE, value[0]); +} + + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 2x2 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[2][2]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 3x3 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[3][3]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context + to a 4x4 matrix \a value. The matrix elements must be specified + in column-major order. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValue(const char *name, const GLfloat value[4][4]) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable at \a location in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(location, QMatrix4x4(value))}. +*/ +void QOpenGLShaderProgram::setUniformValue(int location, const QTransform& value) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + GLfloat mat[3][3] = { + {GLfloat(value.m11()), GLfloat(value.m12()), GLfloat(value.m13())}, + {GLfloat(value.m21()), GLfloat(value.m22()), GLfloat(value.m23())}, + {GLfloat(value.m31()), GLfloat(value.m32()), GLfloat(value.m33())} + }; + d->glfuncs->glUniformMatrix3fv(location, 1, GL_FALSE, mat[0]); + } +} + +/*! + \overload + + Sets the uniform variable called \a name in the current context to a + 3x3 transformation matrix \a value that is specified as a QTransform value. + + To set a QTransform value as a 4x4 matrix in a shader, use + \c{setUniformValue(name, QMatrix4x4(value))}. +*/ +void QOpenGLShaderProgram::setUniformValue + (const char *name, const QTransform& value) +{ + setUniformValue(uniformLocation(name), value); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLint *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1iv(location, count, values); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \note This function is not aware of unsigned int support in modern OpenGL + versions and therefore treats \a values as a GLint and calls glUniform1iv. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLuint *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform1iv(location, count, reinterpret_cast(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. This overload + should be used when setting an array of sampler values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLuint *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count elements of \a values. Each element + has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const GLfloat *values, int count, int tupleSize) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) { + if (tupleSize == 1) + d->glfuncs->glUniform1fv(location, count, values); + else if (tupleSize == 2) + d->glfuncs->glUniform2fv(location, count, values); + else if (tupleSize == 3) + d->glfuncs->glUniform3fv(location, count, values); + else if (tupleSize == 4) + d->glfuncs->glUniform4fv(location, count, values); + else + qWarning("QOpenGLShaderProgram::setUniformValue: size %d not supported", tupleSize); + } +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count elements of \a values. Each element + has \a tupleSize components. The \a tupleSize must be 1, 2, 3, or 4. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray + (const char *name, const GLfloat *values, int count, int tupleSize) +{ + setUniformValueArray(uniformLocation(name), values, count, tupleSize); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector2D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform2fv(location, count, reinterpret_cast(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector2D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector3D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform3fv(location, count, reinterpret_cast(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector3D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QVector4D *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + if (location != -1) + d->glfuncs->glUniform4fv(location, count, reinterpret_cast(values)); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4D vector elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QVector4D *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +// We have to repack matrix arrays from qreal to GLfloat. +#define setUniformMatrixArray(func,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ + func(location, count, GL_FALSE, \ + reinterpret_cast(values[0].constData())); \ + } else { \ + QVarLengthArray temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + for (int index2 = 0; index2 < (cols * rows); ++index2) { \ + temp.data()[cols * rows * index + index2] = \ + values[index].constData()[index2]; \ + } \ + } \ + func(location, count, GL_FALSE, temp.constData()); \ + } +#define setUniformGenericMatrixArray(colfunc,location,values,count,type,cols,rows) \ + if (location == -1 || count <= 0) \ + return; \ + if (sizeof(type) == sizeof(GLfloat) * cols * rows) { \ + const GLfloat *data = reinterpret_cast \ + (values[0].constData()); \ + colfunc(location, count * cols, data); \ + } else { \ + QVarLengthArray temp(cols * rows * count); \ + for (int index = 0; index < count; ++index) { \ + for (int index2 = 0; index2 < (cols * rows); ++index2) { \ + temp.data()[cols * rows * index + index2] = \ + values[index].constData()[index2]; \ + } \ + } \ + colfunc(location, count * cols, temp.constData()); \ + } + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix2fv, location, values, count, QMatrix2x2, 2, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform3fv, location, values, count, + QMatrix2x3, 2, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix2x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform4fv, location, values, count, + QMatrix2x4, 2, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 2x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix2x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform2fv, location, values, count, + QMatrix3x2, 3, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix3fv, location, values, count, QMatrix3x3, 3, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix3x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform4fv, location, values, count, + QMatrix3x4, 3, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 3x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix3x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x2 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform2fv, location, values, count, + QMatrix4x2, 4, 2); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x2 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x2 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x3 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformGenericMatrixArray + (d->glfuncs->glUniform3fv, location, values, count, + QMatrix4x3, 4, 3); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x3 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x3 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Sets the uniform variable array at \a location in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(int location, const QMatrix4x4 *values, int count) +{ + Q_D(QOpenGLShaderProgram); + Q_UNUSED(d); + setUniformMatrixArray + (d->glfuncs->glUniformMatrix4fv, location, values, count, QMatrix4x4, 4, 4); +} + +/*! + \overload + + Sets the uniform variable array called \a name in the current + context to the \a count 4x4 matrix elements of \a values. + + \sa setAttributeValue() +*/ +void QOpenGLShaderProgram::setUniformValueArray(const char *name, const QMatrix4x4 *values, int count) +{ + setUniformValueArray(uniformLocation(name), values, count); +} + +/*! + Returns the hardware limit for how many vertices a geometry shader + can output. +*/ +int QOpenGLShaderProgram::maxGeometryOutputVertices() const +{ + GLint n = 0; + Q_D(const QOpenGLShaderProgram); + d->glfuncs->glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &n); + return n; +} + +/*! + Use this function to specify to OpenGL the number of vertices in + a patch to \a count. A patch is a custom OpenGL primitive whose interpretation + is entirely defined by the tessellation shader stages. Therefore, calling + this function only makes sense when using a QOpenGLShaderProgram + containing tessellation stage shaders. When using OpenGL tessellation, + the only primitive that can be rendered with \c{glDraw*()} functions is + \c{GL_PATCHES}. + + This is equivalent to calling glPatchParameteri(GL_PATCH_VERTICES, count). + + \note This modifies global OpenGL state and is not specific to this + QOpenGLShaderProgram instance. You should call this in your render + function when needed, as QOpenGLShaderProgram will not apply this for + you. This is purely a convenience function. + + \sa patchVertexCount() +*/ +void QOpenGLShaderProgram::setPatchVertexCount(int count) +{ + Q_D(QOpenGLShaderProgram); + d->glfuncs->glPatchParameteri(GL_PATCH_VERTICES, count); +} + +/*! + Returns the number of vertices per-patch to be used when rendering. + + \note This returns the global OpenGL state value. It is not specific to + this QOpenGLShaderProgram instance. + + \sa setPatchVertexCount() +*/ +int QOpenGLShaderProgram::patchVertexCount() const +{ + int patchVertices = 0; + Q_D(const QOpenGLShaderProgram); + d->glfuncs->glGetIntegerv(GL_PATCH_VERTICES, &patchVertices); + return patchVertices; +} + +/*! + Sets the default outer tessellation levels to be used by the tessellation + primitive generator in the event that the tessellation control shader + does not output them to \a levels. For more details on OpenGL and Tessellation + shaders see \l{OpenGL Tessellation Shaders}. + + The \a levels argument should be a QVector consisting of 4 floats. Not all + of the values make sense for all tessellation modes. If you specify a vector with + fewer than 4 elements, the remaining elements will be given a default value of 1. + + \note This modifies global OpenGL state and is not specific to this + QOpenGLShaderProgram instance. You should call this in your render + function when needed, as QOpenGLShaderProgram will not apply this for + you. This is purely a convenience function. + + \note This function is only available with OpenGL >= 4.0 and is not supported + with OpenGL ES 3.2. + + \sa defaultOuterTessellationLevels(), setDefaultInnerTessellationLevels() +*/ +void QOpenGLShaderProgram::setDefaultOuterTessellationLevels(const QVector &levels) +{ +#ifndef QT_OPENGL_ES_2 + Q_D(QOpenGLShaderProgram); + if (d->tessellationFuncs) { + QVector tessLevels = levels; + + // Ensure we have the required 4 outer tessellation levels + // Use default of 1 for missing entries (same as spec) + const int argCount = 4; + if (tessLevels.size() < argCount) { + tessLevels.reserve(argCount); + for (int i = tessLevels.size(); i < argCount; ++i) + tessLevels.append(1.0f); + } + d->tessellationFuncs->glPatchParameterfv(GL_PATCH_DEFAULT_OUTER_LEVEL, tessLevels.data()); + } +#else + Q_UNUSED(levels); +#endif +} + +/*! + Returns the default outer tessellation levels to be used by the tessellation + primitive generator in the event that the tessellation control shader + does not output them. For more details on OpenGL and Tessellation shaders see + \l{OpenGL Tessellation Shaders}. + + Returns a QVector of floats describing the outer tessellation levels. The vector + will always have four elements but not all of them make sense for every mode + of tessellation. + + \note This returns the global OpenGL state value. It is not specific to + this QOpenGLShaderProgram instance. + + \note This function is only supported with OpenGL >= 4.0 and will not + return valid results with OpenGL ES 3.2. + + \sa setDefaultOuterTessellationLevels(), defaultInnerTessellationLevels() +*/ +QVector QOpenGLShaderProgram::defaultOuterTessellationLevels() const +{ +#ifndef QT_OPENGL_ES_2 + QVector tessLevels(4, 1.0f); + Q_D(const QOpenGLShaderProgram); + if (d->tessellationFuncs) + d->tessellationFuncs->glGetFloatv(GL_PATCH_DEFAULT_OUTER_LEVEL, tessLevels.data()); + return tessLevels; +#else + return QVector(); +#endif +} + +/*! + Sets the default outer tessellation levels to be used by the tessellation + primitive generator in the event that the tessellation control shader + does not output them to \a levels. For more details on OpenGL and Tessellation shaders see + \l{OpenGL Tessellation Shaders}. + + The \a levels argument should be a QVector consisting of 2 floats. Not all + of the values make sense for all tessellation modes. If you specify a vector with + fewer than 2 elements, the remaining elements will be given a default value of 1. + + \note This modifies global OpenGL state and is not specific to this + QOpenGLShaderProgram instance. You should call this in your render + function when needed, as QOpenGLShaderProgram will not apply this for + you. This is purely a convenience function. + + \note This function is only available with OpenGL >= 4.0 and is not supported + with OpenGL ES 3.2. + + \sa defaultInnerTessellationLevels(), setDefaultOuterTessellationLevels() +*/ +void QOpenGLShaderProgram::setDefaultInnerTessellationLevels(const QVector &levels) +{ +#ifndef QT_OPENGL_ES_2 + Q_D(QOpenGLShaderProgram); + if (d->tessellationFuncs) { + QVector tessLevels = levels; + + // Ensure we have the required 2 inner tessellation levels + // Use default of 1 for missing entries (same as spec) + const int argCount = 2; + if (tessLevels.size() < argCount) { + tessLevels.reserve(argCount); + for (int i = tessLevels.size(); i < argCount; ++i) + tessLevels.append(1.0f); + } + d->tessellationFuncs->glPatchParameterfv(GL_PATCH_DEFAULT_INNER_LEVEL, tessLevels.data()); + } +#else + Q_UNUSED(levels); +#endif +} + +/*! + Returns the default inner tessellation levels to be used by the tessellation + primitive generator in the event that the tessellation control shader + does not output them. For more details on OpenGL and Tessellation shaders see + \l{OpenGL Tessellation Shaders}. + + Returns a QVector of floats describing the inner tessellation levels. The vector + will always have two elements but not all of them make sense for every mode + of tessellation. + + \note This returns the global OpenGL state value. It is not specific to + this QOpenGLShaderProgram instance. + + \note This function is only supported with OpenGL >= 4.0 and will not + return valid results with OpenGL ES 3.2. + + \sa setDefaultInnerTessellationLevels(), defaultOuterTessellationLevels() +*/ +QVector QOpenGLShaderProgram::defaultInnerTessellationLevels() const +{ +#ifndef QT_OPENGL_ES_2 + QVector tessLevels(2, 1.0f); + Q_D(const QOpenGLShaderProgram); + if (d->tessellationFuncs) + d->tessellationFuncs->glGetFloatv(GL_PATCH_DEFAULT_INNER_LEVEL, tessLevels.data()); + return tessLevels; +#else + return QVector(); +#endif +} + + +/*! + Returns \c true if shader programs written in the OpenGL Shading + Language (GLSL) are supported on this system; false otherwise. + + The \a context is used to resolve the GLSL extensions. + If \a context is \nullptr, then QOpenGLContext::currentContext() + is used. +*/ +bool QOpenGLShaderProgram::hasOpenGLShaderPrograms(QOpenGLContext *context) +{ + if (!context) + context = QOpenGLContext::currentContext(); + if (!context) + return false; + return QOpenGLFunctions(context).hasOpenGLFeature(QOpenGLFunctions::Shaders); +} + +/*! + \internal +*/ +void QOpenGLShaderProgram::shaderDestroyed() +{ + Q_D(QOpenGLShaderProgram); + QOpenGLShader *shader = qobject_cast(sender()); + if (shader && !d->removingShaders) + removeShader(shader); +} + +/*! + Returns \c true if shader programs of type \a type are supported on + this system; false otherwise. + + The \a context is used to resolve the GLSL extensions. + If \a context is \nullptr, then QOpenGLContext::currentContext() + is used. +*/ +bool QOpenGLShader::hasOpenGLShaders(ShaderType type, QOpenGLContext *context) +{ + if (!context) + context = QOpenGLContext::currentContext(); + if (!context) + return false; + + if ((type & ~(Geometry | Vertex | Fragment | TessellationControl | TessellationEvaluation | Compute)) || type == 0) + return false; + + if (type & QOpenGLShader::Geometry) + return supportsGeometry(context->format()); + else if (type & (QOpenGLShader::TessellationControl | QOpenGLShader::TessellationEvaluation)) + return supportsTessellation(context->format()); + else if (type & QOpenGLShader::Compute) + return supportsCompute(context->format()); + + // Unconditional support of vertex and fragment shaders implicitly assumes + // a minimum OpenGL version of 2.0 + return true; +} + +bool QOpenGLShaderProgramPrivate::isCacheDisabled() const +{ + static QOpenGLProgramBinarySupportCheckWrapper binSupportCheck; + return !binSupportCheck.get(QOpenGLContext::currentContext())->isSupported(); +} + +bool QOpenGLShaderProgramPrivate::compileCacheable() +{ + Q_Q(QOpenGLShaderProgram); + for (const QOpenGLProgramBinaryCache::ShaderDesc &shader : qAsConst(binaryProgram.shaders)) { + QScopedPointer s(new QOpenGLShader(qt_shaderStageToType(shader.stage), q)); + if (!s->compileSourceCode(shader.source)) { + log = s->log(); + return false; + } + anonShaders.append(s.take()); + if (!q->addShader(anonShaders.last())) + return false; + } + return true; +} + +bool QOpenGLShaderProgramPrivate::linkBinary() +{ + static QOpenGLProgramBinaryCache binCache; + + Q_Q(QOpenGLShaderProgram); + + const QByteArray cacheKey = binaryProgram.cacheKey(); + if (lcOpenGLProgramDiskCache().isEnabled(QtDebugMsg)) + qCDebug(lcOpenGLProgramDiskCache, "program with %d shaders, cache key %s", + binaryProgram.shaders.count(), cacheKey.constData()); + + bool needsCompile = true; + if (binCache.load(cacheKey, q->programId())) { + qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache"); + needsCompile = false; + } + + bool needsSave = false; + if (needsCompile) { + qCDebug(lcOpenGLProgramDiskCache, "Program binary not in cache, compiling"); + if (compileCacheable()) + needsSave = true; + else + return false; + } + + linkBinaryRecursion = true; + bool ok = q->link(); + linkBinaryRecursion = false; + if (ok && needsSave) + binCache.save(cacheKey, q->programId()); + + return ok; +} + +QT_END_NAMESPACE -- cgit v1.2.3