File indexing completed on 2024-05-19 05:31:55

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
0006     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
0007     SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 #include "glshader.h"
0012 #include "glplatform.h"
0013 #include "glutils.h"
0014 #include "utils/common.h"
0015 
0016 #include <QFile>
0017 
0018 namespace KWin
0019 {
0020 
0021 GLShader::GLShader(unsigned int flags)
0022     : m_valid(false)
0023     , m_locationsResolved(false)
0024     , m_explicitLinking(flags & ExplicitLinking)
0025 {
0026     m_program = glCreateProgram();
0027 }
0028 
0029 GLShader::GLShader(const QString &vertexfile, const QString &fragmentfile, unsigned int flags)
0030     : m_valid(false)
0031     , m_locationsResolved(false)
0032     , m_explicitLinking(flags & ExplicitLinking)
0033 {
0034     m_program = glCreateProgram();
0035     loadFromFiles(vertexfile, fragmentfile);
0036 }
0037 
0038 GLShader::~GLShader()
0039 {
0040     if (m_program) {
0041         glDeleteProgram(m_program);
0042     }
0043 }
0044 
0045 bool GLShader::loadFromFiles(const QString &vertexFile, const QString &fragmentFile)
0046 {
0047     QFile vf(vertexFile);
0048     if (!vf.open(QIODevice::ReadOnly)) {
0049         qCCritical(KWIN_OPENGL) << "Couldn't open" << vertexFile << "for reading!";
0050         return false;
0051     }
0052     const QByteArray vertexSource = vf.readAll();
0053 
0054     QFile ff(fragmentFile);
0055     if (!ff.open(QIODevice::ReadOnly)) {
0056         qCCritical(KWIN_OPENGL) << "Couldn't open" << fragmentFile << "for reading!";
0057         return false;
0058     }
0059     const QByteArray fragmentSource = ff.readAll();
0060 
0061     return load(vertexSource, fragmentSource);
0062 }
0063 
0064 bool GLShader::link()
0065 {
0066     // Be optimistic
0067     m_valid = true;
0068 
0069     glLinkProgram(m_program);
0070 
0071     // Get the program info log
0072     int maxLength, length;
0073     glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &maxLength);
0074 
0075     QByteArray log(maxLength, 0);
0076     glGetProgramInfoLog(m_program, maxLength, &length, log.data());
0077 
0078     // Make sure the program linked successfully
0079     int status;
0080     glGetProgramiv(m_program, GL_LINK_STATUS, &status);
0081 
0082     if (status == 0) {
0083         qCCritical(KWIN_OPENGL) << "Failed to link shader:"
0084                                 << "\n"
0085                                 << log;
0086         m_valid = false;
0087     } else if (length > 0) {
0088         qCDebug(KWIN_OPENGL) << "Shader link log:" << log;
0089     }
0090 
0091     return m_valid;
0092 }
0093 
0094 const QByteArray GLShader::prepareSource(GLenum shaderType, const QByteArray &source) const
0095 {
0096     // Prepare the source code
0097     QByteArray ba;
0098     if (GLPlatform::instance()->isGLES() && GLPlatform::instance()->glslVersion() < Version(3, 0)) {
0099         ba.append("precision highp float;\n");
0100     }
0101     ba.append(source);
0102     if (GLPlatform::instance()->isGLES() && GLPlatform::instance()->glslVersion() >= Version(3, 0)) {
0103         ba.replace("#version 140", "#version 300 es\n\nprecision highp float;\n");
0104     }
0105 
0106     return ba;
0107 }
0108 
0109 bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &source) const
0110 {
0111     GLuint shader = glCreateShader(shaderType);
0112 
0113     QByteArray preparedSource = prepareSource(shaderType, source);
0114     const char *src = preparedSource.constData();
0115     glShaderSource(shader, 1, &src, nullptr);
0116 
0117     // Compile the shader
0118     glCompileShader(shader);
0119 
0120     // Get the shader info log
0121     int maxLength, length;
0122     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
0123 
0124     QByteArray log(maxLength, 0);
0125     glGetShaderInfoLog(shader, maxLength, &length, log.data());
0126 
0127     // Check the status
0128     int status;
0129     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
0130 
0131     if (status == 0) {
0132         const char *typeName = (shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment");
0133         qCCritical(KWIN_OPENGL) << "Failed to compile" << typeName << "shader:"
0134                                 << "\n"
0135                                 << log;
0136         size_t line = 0;
0137         const auto split = source.split('\n');
0138         for (const auto &l : split) {
0139             qCCritical(KWIN_OPENGL).nospace() << "line " << line++ << ":" << l;
0140         }
0141     } else if (length > 0) {
0142         qCDebug(KWIN_OPENGL) << "Shader compile log:" << log;
0143     }
0144 
0145     if (status != 0) {
0146         glAttachShader(program, shader);
0147     }
0148 
0149     glDeleteShader(shader);
0150     return status != 0;
0151 }
0152 
0153 bool GLShader::load(const QByteArray &vertexSource, const QByteArray &fragmentSource)
0154 {
0155     m_valid = false;
0156 
0157     // Compile the vertex shader
0158     if (!vertexSource.isEmpty()) {
0159         bool success = compile(m_program, GL_VERTEX_SHADER, vertexSource);
0160 
0161         if (!success) {
0162             return false;
0163         }
0164     }
0165 
0166     // Compile the fragment shader
0167     if (!fragmentSource.isEmpty()) {
0168         bool success = compile(m_program, GL_FRAGMENT_SHADER, fragmentSource);
0169 
0170         if (!success) {
0171             return false;
0172         }
0173     }
0174 
0175     if (m_explicitLinking) {
0176         return true;
0177     }
0178 
0179     // link() sets mValid
0180     return link();
0181 }
0182 
0183 void GLShader::bindAttributeLocation(const char *name, int index)
0184 {
0185     glBindAttribLocation(m_program, index, name);
0186 }
0187 
0188 void GLShader::bindFragDataLocation(const char *name, int index)
0189 {
0190     if (!GLPlatform::instance()->isGLES() && (hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_gpu_shader4")))) {
0191         glBindFragDataLocation(m_program, index, name);
0192     }
0193 }
0194 
0195 void GLShader::bind()
0196 {
0197     glUseProgram(m_program);
0198 }
0199 
0200 void GLShader::unbind()
0201 {
0202     glUseProgram(0);
0203 }
0204 
0205 void GLShader::resolveLocations()
0206 {
0207     if (m_locationsResolved) {
0208         return;
0209     }
0210 
0211     m_matrix4Locations[Mat4Uniform::TextureMatrix] = uniformLocation("textureMatrix");
0212     m_matrix4Locations[Mat4Uniform::ProjectionMatrix] = uniformLocation("projection");
0213     m_matrix4Locations[Mat4Uniform::ModelViewMatrix] = uniformLocation("modelview");
0214     m_matrix4Locations[Mat4Uniform::ModelViewProjectionMatrix] = uniformLocation("modelViewProjectionMatrix");
0215     m_matrix4Locations[Mat4Uniform::WindowTransformation] = uniformLocation("windowTransformation");
0216     m_matrix4Locations[Mat4Uniform::ScreenTransformation] = uniformLocation("screenTransformation");
0217     m_matrix4Locations[Mat4Uniform::ColorimetryTransformation] = uniformLocation("colorimetryTransform");
0218 
0219     m_vec2Locations[Vec2Uniform::Offset] = uniformLocation("offset");
0220 
0221     m_vec3Locations[Vec3Uniform::PrimaryBrightness] = uniformLocation("primaryBrightness");
0222 
0223     m_vec4Locations[Vec4Uniform::ModulationConstant] = uniformLocation("modulation");
0224 
0225     m_floatLocations[FloatUniform::Saturation] = uniformLocation("saturation");
0226     m_floatLocations[FloatUniform::MaxHdrBrightness] = uniformLocation("maxHdrBrightness");
0227     m_floatLocations[FloatUniform::SdrBrightness] = uniformLocation("sdrBrightness");
0228 
0229     m_colorLocations[ColorUniform::Color] = uniformLocation("geometryColor");
0230 
0231     m_intLocations[IntUniform::TextureWidth] = uniformLocation("textureWidth");
0232     m_intLocations[IntUniform::TextureHeight] = uniformLocation("textureHeight");
0233     m_intLocations[IntUniform::Sampler] = uniformLocation("sampler");
0234     m_intLocations[IntUniform::Sampler1] = uniformLocation("sampler1");
0235     m_intLocations[IntUniform::SourceNamedTransferFunction] = uniformLocation("sourceNamedTransferFunction");
0236     m_intLocations[IntUniform::DestinationNamedTransferFunction] = uniformLocation("destinationNamedTransferFunction");
0237 
0238     m_locationsResolved = true;
0239 }
0240 
0241 int GLShader::uniformLocation(const char *name)
0242 {
0243     const int location = glGetUniformLocation(m_program, name);
0244     return location;
0245 }
0246 
0247 bool GLShader::setUniform(Mat3Uniform uniform, const QMatrix3x3 &value)
0248 {
0249     resolveLocations();
0250     return setUniform(m_matrix3Locations[uniform], value);
0251 }
0252 
0253 bool GLShader::setUniform(Mat4Uniform uniform, const QMatrix4x4 &matrix)
0254 {
0255     resolveLocations();
0256     return setUniform(m_matrix4Locations[uniform], matrix);
0257 }
0258 
0259 bool GLShader::setUniform(Vec2Uniform uniform, const QVector2D &value)
0260 {
0261     resolveLocations();
0262     return setUniform(m_vec2Locations[uniform], value);
0263 }
0264 
0265 bool GLShader::setUniform(Vec3Uniform uniform, const QVector3D &value)
0266 {
0267     resolveLocations();
0268     return setUniform(m_vec3Locations[uniform], value);
0269 }
0270 
0271 bool GLShader::setUniform(Vec4Uniform uniform, const QVector4D &value)
0272 {
0273     resolveLocations();
0274     return setUniform(m_vec4Locations[uniform], value);
0275 }
0276 
0277 bool GLShader::setUniform(FloatUniform uniform, float value)
0278 {
0279     resolveLocations();
0280     return setUniform(m_floatLocations[uniform], value);
0281 }
0282 
0283 bool GLShader::setUniform(IntUniform uniform, int value)
0284 {
0285     resolveLocations();
0286     return setUniform(m_intLocations[uniform], value);
0287 }
0288 
0289 bool GLShader::setUniform(ColorUniform uniform, const QVector4D &value)
0290 {
0291     resolveLocations();
0292     return setUniform(m_colorLocations[uniform], value);
0293 }
0294 
0295 bool GLShader::setUniform(ColorUniform uniform, const QColor &value)
0296 {
0297     resolveLocations();
0298     return setUniform(m_colorLocations[uniform], value);
0299 }
0300 
0301 bool GLShader::setUniform(const char *name, float value)
0302 {
0303     const int location = uniformLocation(name);
0304     return setUniform(location, value);
0305 }
0306 
0307 bool GLShader::setUniform(const char *name, int value)
0308 {
0309     const int location = uniformLocation(name);
0310     return setUniform(location, value);
0311 }
0312 
0313 bool GLShader::setUniform(const char *name, const QVector2D &value)
0314 {
0315     const int location = uniformLocation(name);
0316     return setUniform(location, value);
0317 }
0318 
0319 bool GLShader::setUniform(const char *name, const QVector3D &value)
0320 {
0321     const int location = uniformLocation(name);
0322     return setUniform(location, value);
0323 }
0324 
0325 bool GLShader::setUniform(const char *name, const QVector4D &value)
0326 {
0327     const int location = uniformLocation(name);
0328     return setUniform(location, value);
0329 }
0330 
0331 bool GLShader::setUniform(const char *name, const QMatrix4x4 &value)
0332 {
0333     const int location = uniformLocation(name);
0334     return setUniform(location, value);
0335 }
0336 
0337 bool GLShader::setUniform(const char *name, const QColor &color)
0338 {
0339     const int location = uniformLocation(name);
0340     return setUniform(location, color);
0341 }
0342 
0343 bool GLShader::setUniform(int location, float value)
0344 {
0345     if (location >= 0) {
0346         glUniform1f(location, value);
0347     }
0348     return (location >= 0);
0349 }
0350 
0351 bool GLShader::setUniform(int location, int value)
0352 {
0353     if (location >= 0) {
0354         glUniform1i(location, value);
0355     }
0356     return (location >= 0);
0357 }
0358 
0359 bool GLShader::setUniform(int location, int xValue, int yValue, int zValue)
0360 {
0361     if (location >= 0) {
0362         glUniform3i(location, xValue, yValue, zValue);
0363     }
0364     return location >= 0;
0365 }
0366 
0367 bool GLShader::setUniform(int location, const QVector2D &value)
0368 {
0369     if (location >= 0) {
0370         glUniform2fv(location, 1, (const GLfloat *)&value);
0371     }
0372     return (location >= 0);
0373 }
0374 
0375 bool GLShader::setUniform(int location, const QVector3D &value)
0376 {
0377     if (location >= 0) {
0378         glUniform3fv(location, 1, (const GLfloat *)&value);
0379     }
0380     return (location >= 0);
0381 }
0382 
0383 bool GLShader::setUniform(int location, const QVector4D &value)
0384 {
0385     if (location >= 0) {
0386         glUniform4fv(location, 1, (const GLfloat *)&value);
0387     }
0388     return (location >= 0);
0389 }
0390 
0391 bool GLShader::setUniform(int location, const QMatrix3x3 &value)
0392 {
0393     if (location >= 0) {
0394         glUniformMatrix3fv(location, 1, GL_FALSE, value.constData());
0395     }
0396     return location >= 0;
0397 }
0398 
0399 bool GLShader::setUniform(int location, const QMatrix4x4 &value)
0400 {
0401     if (location >= 0) {
0402         glUniformMatrix4fv(location, 1, GL_FALSE, value.constData());
0403     }
0404     return (location >= 0);
0405 }
0406 
0407 bool GLShader::setUniform(int location, const QColor &color)
0408 {
0409     if (location >= 0) {
0410         glUniform4f(location, color.redF(), color.greenF(), color.blueF(), color.alphaF());
0411     }
0412     return (location >= 0);
0413 }
0414 
0415 int GLShader::attributeLocation(const char *name)
0416 {
0417     int location = glGetAttribLocation(m_program, name);
0418     return location;
0419 }
0420 
0421 bool GLShader::setAttribute(const char *name, float value)
0422 {
0423     int location = attributeLocation(name);
0424     if (location >= 0) {
0425         glVertexAttrib1f(location, value);
0426     }
0427     return (location >= 0);
0428 }
0429 
0430 QMatrix4x4 GLShader::getUniformMatrix4x4(const char *name)
0431 {
0432     int location = uniformLocation(name);
0433     if (location >= 0) {
0434         GLfloat m[16];
0435         glGetnUniformfv(m_program, location, sizeof(m), m);
0436         QMatrix4x4 matrix(m[0], m[4], m[8], m[12],
0437                           m[1], m[5], m[9], m[13],
0438                           m[2], m[6], m[10], m[14],
0439                           m[3], m[7], m[11], m[15]);
0440         matrix.optimize();
0441         return matrix;
0442     } else {
0443         return QMatrix4x4();
0444     }
0445 }
0446 
0447 bool GLShader::setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst)
0448 {
0449     const auto &srcColorimetry = src.colorimetry() == NamedColorimetry::BT709 ? dst.sdrColorimetry() : src.colorimetry();
0450     return setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, srcColorimetry.toOther(dst.colorimetry()))
0451         && setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(src.transferFunction()))
0452         && setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(dst.transferFunction()))
0453         && setUniform(FloatUniform::SdrBrightness, dst.sdrBrightness())
0454         && setUniform(FloatUniform::MaxHdrBrightness, dst.maxHdrHighlightBrightness());
0455 }
0456 
0457 bool GLShader::setColorspaceUniformsFromSRGB(const ColorDescription &dst)
0458 {
0459     return setColorspaceUniforms(ColorDescription::sRGB, dst);
0460 }
0461 
0462 bool GLShader::setColorspaceUniformsToSRGB(const ColorDescription &src)
0463 {
0464     return setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, src.colorimetry().toOther(src.sdrColorimetry()))
0465         && setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(src.transferFunction()))
0466         && setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(NamedTransferFunction::gamma22))
0467         && setUniform(FloatUniform::SdrBrightness, src.sdrBrightness())
0468         && setUniform(FloatUniform::MaxHdrBrightness, src.sdrBrightness());
0469 }
0470 }