Insert #line statements in shader program.
This makes the GLSL compiler issue correct line numbers on errors even in the presence of the other preprocessor lines inserted by QOpenGLShaderProgram/QGLShaderProgram. Change-Id: Ief4fc1dd1e94bb2b3a1ad13fbaf62186910a4994 Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com> Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
This commit is contained in:
parent
294c914eb6
commit
92ce89c1ef
@ -39,6 +39,7 @@
|
|||||||
#include <QtCore/qfile.h>
|
#include <QtCore/qfile.h>
|
||||||
#include <QtCore/qvarlengtharray.h>
|
#include <QtCore/qvarlengtharray.h>
|
||||||
#include <QtCore/qvector.h>
|
#include <QtCore/qvector.h>
|
||||||
|
#include <QtCore/qregularexpression.h>
|
||||||
#include <QtGui/qtransform.h>
|
#include <QtGui/qtransform.h>
|
||||||
#include <QtGui/QColor>
|
#include <QtGui/QColor>
|
||||||
#include <QtGui/QSurfaceFormat>
|
#include <QtGui/QSurfaceFormat>
|
||||||
@ -47,6 +48,8 @@
|
|||||||
#include <QtGui/qopenglfunctions_4_0_core.h>
|
#include <QtGui/qopenglfunctions_4_0_core.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -401,6 +404,95 @@ static const char redefineHighp[] =
|
|||||||
"#endif\n";
|
"#endif\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
QString working = QString::fromUtf8(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 run a minimal preprocessor first.
|
||||||
|
enum {
|
||||||
|
Normal,
|
||||||
|
CommentStarting,
|
||||||
|
MultiLineComment,
|
||||||
|
SingleLineComment,
|
||||||
|
CommentEnding
|
||||||
|
} state = Normal;
|
||||||
|
|
||||||
|
for (QChar *c = working.begin(); c != working.end(); ++c) {
|
||||||
|
switch (state) {
|
||||||
|
case Normal:
|
||||||
|
if (*c == QLatin1Char('/'))
|
||||||
|
state = CommentStarting;
|
||||||
|
break;
|
||||||
|
case CommentStarting:
|
||||||
|
if (*c == QLatin1Char('*'))
|
||||||
|
state = MultiLineComment;
|
||||||
|
else if (*c == QLatin1Char('/'))
|
||||||
|
state = SingleLineComment;
|
||||||
|
else
|
||||||
|
state = Normal;
|
||||||
|
break;
|
||||||
|
case MultiLineComment:
|
||||||
|
if (*c == QLatin1Char('*'))
|
||||||
|
state = CommentEnding;
|
||||||
|
else if (*c == QLatin1Char('#'))
|
||||||
|
*c = QLatin1Char('_');
|
||||||
|
break;
|
||||||
|
case SingleLineComment:
|
||||||
|
if (*c == QLatin1Char('\n'))
|
||||||
|
state = Normal;
|
||||||
|
else if (*c == QLatin1Char('#'))
|
||||||
|
*c = QLatin1Char('_');
|
||||||
|
break;
|
||||||
|
case CommentEnding:
|
||||||
|
if (*c == QLatin1Char('/')) {
|
||||||
|
state = Normal;
|
||||||
|
} else {
|
||||||
|
if (*c == QLatin1Char('#'))
|
||||||
|
*c = QLatin1Char('_');
|
||||||
|
state = MultiLineComment;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for #version directive
|
||||||
|
int splitPosition = 0;
|
||||||
|
int linePosition = 1;
|
||||||
|
|
||||||
|
static const QRegularExpression pattern(QStringLiteral("^#\\s*version.*(\\n)?"),
|
||||||
|
QRegularExpression::MultilineOption
|
||||||
|
| QRegularExpression::OptimizeOnFirstUsageOption);
|
||||||
|
QRegularExpressionMatch match = pattern.match(working);
|
||||||
|
if (match.hasMatch()) {
|
||||||
|
splitPosition = match.capturedEnd();
|
||||||
|
linePosition += int(std::count(working.begin(), working.begin() + splitPosition, QLatin1Char('\n')));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVersionDirectivePosition(splitPosition, linePosition);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Sets the \a source code for this shader and compiles it.
|
Sets the \a source code for this shader and compiles it.
|
||||||
Returns \c true if the source was successfully compiled, false otherwise.
|
Returns \c true if the source was successfully compiled, false otherwise.
|
||||||
@ -410,26 +502,24 @@ static const char redefineHighp[] =
|
|||||||
bool QOpenGLShader::compileSourceCode(const char *source)
|
bool QOpenGLShader::compileSourceCode(const char *source)
|
||||||
{
|
{
|
||||||
Q_D(QOpenGLShader);
|
Q_D(QOpenGLShader);
|
||||||
if (d->shaderGuard && d->shaderGuard->id()) {
|
// This method breaks the shader code into two parts:
|
||||||
QVarLengthArray<const char *, 4> src;
|
// 1. Up to and including an optional #version directive.
|
||||||
QVarLengthArray<GLint, 4> srclen;
|
// 2. The rest.
|
||||||
int headerLen = 0;
|
// If a #version directive exists, qualifierDefines and redefineHighp
|
||||||
while (source && source[headerLen] == '#') {
|
// are inserted after. Otherwise they are inserted right at the start.
|
||||||
// Skip #version and #extension directives at the start of
|
// In both cases a #line directive is appended in order to compensate
|
||||||
// the shader code. We need to insert the qualifierDefines
|
// for line number changes in case of compiler errors.
|
||||||
// and redefineHighp just after them.
|
|
||||||
if (qstrncmp(source + headerLen, "#version", 8) != 0 &&
|
if (d->shaderGuard && d->shaderGuard->id() && source) {
|
||||||
qstrncmp(source + headerLen, "#extension", 10) != 0) {
|
const QVersionDirectivePosition versionDirectivePosition = findVersionDirectivePosition(source);
|
||||||
break;
|
|
||||||
}
|
QVarLengthArray<const char *, 5> sourceChunks;
|
||||||
while (source[headerLen] != '\0' && source[headerLen] != '\n')
|
QVarLengthArray<GLint, 5> sourceChunkLengths;
|
||||||
++headerLen;
|
|
||||||
if (source[headerLen] == '\n')
|
if (versionDirectivePosition.hasPosition()) {
|
||||||
++headerLen;
|
// Append source up to #version directive
|
||||||
}
|
sourceChunks.append(source);
|
||||||
if (headerLen > 0) {
|
sourceChunkLengths.append(GLint(versionDirectivePosition.position));
|
||||||
src.append(source);
|
|
||||||
srclen.append(GLint(headerLen));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The precision qualifiers are useful on OpenGL/ES systems,
|
// The precision qualifiers are useful on OpenGL/ES systems,
|
||||||
@ -442,20 +532,28 @@ bool QOpenGLShader::compileSourceCode(const char *source)
|
|||||||
|| true
|
|| true
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
src.append(qualifierDefines);
|
sourceChunks.append(qualifierDefines);
|
||||||
srclen.append(GLint(sizeof(qualifierDefines) - 1));
|
sourceChunkLengths.append(GLint(sizeof(qualifierDefines) - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QOpenGL_REDEFINE_HIGHP
|
#ifdef QOpenGL_REDEFINE_HIGHP
|
||||||
if (d->shaderType == Fragment && !ctx_d->workaround_missingPrecisionQualifiers
|
if (d->shaderType == Fragment && !ctx_d->workaround_missingPrecisionQualifiers
|
||||||
&& QOpenGLContext::currentContext()->isOpenGLES()) {
|
&& QOpenGLContext::currentContext()->isOpenGLES()) {
|
||||||
src.append(redefineHighp);
|
sourceChunks.append(redefineHighp);
|
||||||
srclen.append(GLint(sizeof(redefineHighp) - 1));
|
sourceChunkLengths.append(GLint(sizeof(redefineHighp) - 1));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
src.append(source + headerLen);
|
|
||||||
srclen.append(GLint(qstrlen(source + headerLen)));
|
// Append #line directive in order to compensate for text insertion
|
||||||
d->glfuncs->glShaderSource(d->shaderGuard->id(), src.size(), src.data(), srclen.data());
|
QByteArray 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);
|
return d->compile(this);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user