23std::unique_ptr<ShaderManager> ShaderManager::s_shaderManager;
 
   27    if (!s_shaderManager) {
 
   28        s_shaderManager = std::make_unique<ShaderManager>();
 
   30    return s_shaderManager.get();
 
 
   35    s_shaderManager.reset();
 
 
   44    while (!m_boundShaders.isEmpty()) {
 
 
   49QByteArray ShaderManager::generateVertexSource(ShaderTraits traits)
 const 
   52    QTextStream stream(&source);
 
   55    QByteArray attribute, varying;
 
   60        attribute = glsl_140 ? QByteArrayLiteral(
"in") : QByteArrayLiteral(
"attribute");
 
   61        varying = glsl_140 ? QByteArrayLiteral(
"out") : QByteArrayLiteral(
"varying");
 
   64            stream << 
"#version 140\n\n";
 
   67        const bool glsl_es_300 = gl->
glslVersion() >= Version(3, 0);
 
   69        attribute = glsl_es_300 ? QByteArrayLiteral(
"in") : QByteArrayLiteral(
"attribute");
 
   70        varying = glsl_es_300 ? QByteArrayLiteral(
"out") : QByteArrayLiteral(
"varying");
 
   73            stream << 
"#version 300 es\n\n";
 
   77    stream << attribute << 
" vec4 position;\n";
 
   79        stream << attribute << 
" vec4 texcoord;\n\n";
 
   80        stream << varying << 
" vec2 texcoord0;\n\n";
 
   85    stream << 
"uniform mat4 modelViewProjectionMatrix;\n\n";
 
   87    stream << 
"void main()\n{\n";
 
   89        stream << 
"    texcoord0 = texcoord.st;\n";
 
   92    stream << 
"    gl_Position = modelViewProjectionMatrix * position;\n";
 
   99QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits)
 const 
  102    QTextStream stream(&source);
 
  105    QByteArray varying, output, textureLookup;
 
  108        const bool glsl_140 = gl->glslVersion() >= Version(1, 40);
 
  111            stream << 
"#version 140\n\n";
 
  114        varying = glsl_140 ? QByteArrayLiteral(
"in") : QByteArrayLiteral(
"varying");
 
  115        textureLookup = glsl_140 ? QByteArrayLiteral(
"texture") : QByteArrayLiteral(
"texture2D");
 
  116        output = glsl_140 ? QByteArrayLiteral(
"fragColor") : QByteArrayLiteral(
"gl_FragColor");
 
  121            stream << 
"#version 300 es\n\n";
 
  127        stream << 
"precision highp float;\n\n";
 
  129        varying = glsl_es_300 ? QByteArrayLiteral(
"in") : QByteArrayLiteral(
"varying");
 
  130        textureLookup = glsl_es_300 ? QByteArrayLiteral(
"texture") : QByteArrayLiteral(
"texture2D");
 
  131        output = glsl_es_300 ? QByteArrayLiteral(
"fragColor") : QByteArrayLiteral(
"gl_FragColor");
 
  135        stream << 
"uniform sampler2D sampler;\n";
 
  136        stream << 
"uniform sampler2D sampler1;\n";
 
  137        stream << 
"uniform int converter;\n";
 
  138        stream << varying << 
" vec2 texcoord0;\n";
 
  140        stream << 
"#extension GL_OES_EGL_image_external : require\n\n";
 
  141        stream << 
"uniform samplerExternalOES sampler;\n";
 
  142        stream << varying << 
" vec2 texcoord0;\n";
 
  144        stream << 
"uniform vec4 geometryColor;\n";
 
  147        stream << 
"uniform vec4 modulation;\n";
 
  150        stream << 
"#include \"saturation.glsl\"\n";
 
  153        stream << 
"#include \"colormanagement.glsl\"\n";
 
  156    if (output != QByteArrayLiteral(
"gl_FragColor")) {
 
  157        stream << 
"\nout vec4 " << output << 
";\n";
 
  162        stream << 
"vec4 transformY_UV(sampler2D tex0, sampler2D tex1, vec2 texcoord0) {\n";
 
  163        stream << 
"    float y = 1.16438356 * (" << textureLookup << 
"(tex0, texcoord0).x - 0.0625);\n";
 
  164        stream << 
"    float u = " << textureLookup << 
"(tex1, texcoord0).r - 0.5;\n";
 
  165        stream << 
"    float v = " << textureLookup << 
"(tex1, texcoord0).g - 0.5;\n";
 
  166        stream << 
"    return vec4(y + 1.59602678 * v" 
  167                  "              , y - 0.39176229 * u - 0.81296764 * v" 
  168                  "              , y + 2.01723214 * u" 
  174    stream << 
"\nvoid main(void)\n{\n";
 
  175    stream << 
"    vec4 result;\n";
 
  177        stream << 
"    if (converter == 0) {\n";
 
  178        stream << 
"        result = " << textureLookup << 
"(sampler, texcoord0);\n";
 
  179        stream << 
"    } else {\n";
 
  180        stream << 
"        result = transformY_UV(sampler, sampler1, texcoord0);\n";
 
  184        stream << 
"    result = texture2D(sampler, texcoord0);\n";
 
  186        stream << 
"    result = geometryColor;\n";
 
  189        stream << 
"    result = sourceEncodingToNitsInDestinationColorspace(result);\n";
 
  192        stream << 
"    result = adjustSaturation(result);\n";
 
  195        stream << 
"    result *= modulation;\n";
 
  198        stream << 
"    result = nitsToDestinationEncoding(result);\n";
 
  201    stream << 
"    " << output << 
" = result;\n";
 
  207std::unique_ptr<GLShader> ShaderManager::generateShader(ShaderTraits traits)
 
  212std::optional<QByteArray> ShaderManager::preprocess(
const QByteArray &src, 
int recursionDepth)
 const 
  215    if (recursionDepth > 10) {
 
  216        qCWarning(KWIN_OPENGL, 
"shader has too many recursive includes!");
 
  220    ret.reserve(src.size());
 
  221    const auto split = src.split(
'\n');
 
  222    for (
auto it = split.begin(); it != split.end(); it++) {
 
  223        const auto &line = *it;
 
  224        if (line.startsWith(
"#include \"") && line.endsWith(
"\"")) {
 
  225            static constexpr ssize_t includeLength = QByteArrayView(
"#include \"").size();
 
  226            const QByteArray path = 
":/opengl/" + line.mid(includeLength, line.size() - includeLength - 1);
 
  228            if (!file.open(QIODevice::ReadOnly)) {
 
  229                qCWarning(KWIN_OPENGL, 
"failed to read include line %s", qPrintable(line));
 
  232            const auto processed = preprocess(file.readAll(), recursionDepth);
 
  236            ret.append(*processed);
 
  247    const auto vertex = preprocess(vertexSource.isEmpty() ? generateVertexSource(traits) : vertexSource);
 
  248    const auto fragment = preprocess(fragmentSource.isEmpty() ? generateFragmentSource(traits) : fragmentSource);
 
  249    if (!vertex || !fragment) {
 
 
  264static QString resolveShaderFilePath(
const QString &filePath)
 
  271        suffix = QStringLiteral(
"_core");
 
  274    if (filePath.endsWith(QStringLiteral(
".frag"))) {
 
  275        extension = QStringLiteral(
".frag");
 
  276    } 
else if (filePath.endsWith(QStringLiteral(
".vert"))) {
 
  277        extension = QStringLiteral(
".vert");
 
  279        qCWarning(KWIN_OPENGL) << filePath << 
"must end either with .vert or .frag";
 
  283    const QString prefix = filePath.chopped(extension.size());
 
  284    return prefix + suffix + extension;
 
  289    auto loadShaderFile = [](
const QString &filePath) {
 
  290        QFile file(filePath);
 
  291        if (file.open(QIODevice::ReadOnly)) {
 
  292            return file.readAll();
 
  294        qCCritical(KWIN_OPENGL) << 
"Failed to read shader " << filePath;
 
  297    QByteArray vertexSource;
 
  298    QByteArray fragmentSource;
 
  299    if (!vertexFile.isEmpty()) {
 
  300        vertexSource = loadShaderFile(resolveShaderFilePath(vertexFile));
 
  301        if (vertexSource.isEmpty()) {
 
  302            return std::unique_ptr<GLShader>(
new GLShader());
 
  305    if (!fragmentFile.isEmpty()) {
 
  306        fragmentSource = loadShaderFile(resolveShaderFilePath(fragmentFile));
 
  307        if (fragmentSource.isEmpty()) {
 
  308            return std::unique_ptr<GLShader>(
new GLShader());
 
 
  316    std::unique_ptr<GLShader> &
shader = m_shaderHash[traits];
 
  318        shader = generateShader(traits);
 
 
  325    if (m_boundShaders.isEmpty()) {
 
  328        return m_boundShaders.top();
 
 
  334    return !m_boundShaders.isEmpty();
 
 
  350    m_boundShaders.push(
shader);
 
 
  355    if (m_boundShaders.isEmpty()) {
 
  359    if (m_boundShaders.isEmpty()) {
 
  362    } 
else if (
shader != m_boundShaders.top()) {
 
  364        m_boundShaders.top()->bind();
 
 
  368void ShaderManager::bindFragDataLocations(
GLShader *shader)
 
  373void ShaderManager::bindAttributeLocations(GLShader *shader)
 const 
  383    bindAttributeLocations(
shader.get());
 
  384    bindFragDataLocations(
shader.get());
 
 
bool load(const QByteArray &vertexSource, const QByteArray &fragmentSource)
void bindAttributeLocation(const char *name, int index)
void bindFragDataLocation(const char *name, int index)
GLShader * getBoundShader() const
bool isShaderBound() const
std::unique_ptr< GLShader > generateShaderFromFile(ShaderTraits traits, const QString &vertexFile=QString(), const QString &fragmentFile=QString())
GLShader * shader(ShaderTraits traits)
static ShaderManager * instance()
std::unique_ptr< GLShader > generateCustomShader(ShaderTraits traits, const QByteArray &vertexSource=QByteArray(), const QByteArray &fragmentSource=QByteArray())
std::unique_ptr< GLShader > loadShaderFromCode(const QByteArray &vertexSource, const QByteArray &fragmentSource)
GLShader * pushShader(ShaderTraits traits)