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)