1 /* This file is part of the KDE project
2 * Copyright (C) Julian Thijssen <julianthijssen@gmail.com>, (C) 2016
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_opengl_shader_loader.h"
20
21 #include "opengl/kis_opengl.h"
22 #include "kis_config.h"
23
24 #include <QFile>
25 #include <QMessageBox>
26 #include <KLocalizedString>
27
28 #define PROGRAM_VERTEX_ATTRIBUTE 0
29 #define PROGRAM_TEXCOORD_ATTRIBUTE 1
30
31 // Mapping of uniforms to uniform names
32 std::map<Uniform, const char *> KisShaderProgram::names = {
33 {ModelViewProjection, "modelViewProjection"},
34 {TextureMatrix, "textureMatrix"},
35 {ViewportScale, "viewportScale"},
36 {TexelSize, "texelSize"},
37 {Texture0, "texture0"},
38 {Texture1, "texture1"},
39 {FixedLodLevel, "fixedLodLevel"},
40 {FragmentColor, "fragColor"}
41 };
42
43 /**
44 * Generic shader loading function that will compile a shader program given
45 * a vertex shader and fragment shader resource path. Extra code can be prepended
46 * to each shader respectively using the header parameters.
47 *
48 * @param vertPath Resource path to a vertex shader
49 * @param fragPath Resource path to a fragment shader
50 * @param vertHeader Extra code which will be prepended to the vertex shader
51 * @param fragHeader Extra code which will be prepended to the fragment shader
52 */
loadShader(QString vertPath,QString fragPath,QByteArray vertHeader,QByteArray fragHeader)53 KisShaderProgram *KisOpenGLShaderLoader::loadShader(QString vertPath, QString fragPath,
54 QByteArray vertHeader, QByteArray fragHeader)
55 {
56 bool result;
57
58 KisShaderProgram *shader = new KisShaderProgram();
59
60 // Load vertex shader
61 QByteArray vertSource;
62
63 // XXX Check can be removed and set to the MAC version after we move to Qt5.7
64 #ifdef Q_OS_MACOS
65 vertSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
66 // OpenColorIO doesn't support the new GLSL version yet.
67 vertSource.append("#define texture2D texture\n");
68 vertSource.append("#define texture3D texture\n");
69 #else
70 if (KisOpenGL::hasOpenGLES()) {
71 vertSource.append("#version 300 es\n");
72 } else {
73 vertSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
74 }
75 #endif
76 vertSource.append(vertHeader);
77 QFile vertexShaderFile(":/" + vertPath);
78 vertexShaderFile.open(QIODevice::ReadOnly);
79 vertSource.append(vertexShaderFile.readAll());
80
81 result = shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource);
82 if (!result)
83 throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add vertex shader source from file", vertPath, shader->log()));
84
85 // Load fragment shader
86 QByteArray fragSource;
87
88 // XXX Check can be removed and set to the MAC version after we move to Qt5.7
89 #ifdef Q_OS_MACOS
90 fragSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
91 // OpenColorIO doesn't support the new GLSL version yet.
92 fragSource.append("#define texture2D texture\n");
93 fragSource.append("#define texture3D texture\n");
94 #else
95 if (KisOpenGL::hasOpenGLES()) {
96 fragSource.append(
97 "#version 300 es\n"
98 "precision mediump float;\n"
99 "precision mediump sampler3D;\n");
100
101 // OpenColorIO doesn't support the new GLSL version yet.
102 fragSource.append("#define texture2D texture\n");
103 fragSource.append("#define texture3D texture\n");
104 } else {
105 fragSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
106 }
107 #endif
108 fragSource.append(fragHeader);
109 QFile fragmentShaderFile(":/" + fragPath);
110 fragmentShaderFile.open(QIODevice::ReadOnly);
111 fragSource.append(fragmentShaderFile.readAll());
112
113 result = shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource);
114 if (!result)
115 throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add fragment shader source from file", fragPath, shader->log()));
116
117 // Bind attributes
118 shader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
119 shader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE);
120
121 // Link
122 result = shader->link();
123 if (!result)
124 throw ShaderLoaderException(QString("Failed to link shader: ").append(vertPath));
125
126 Q_ASSERT(shader->isLinked());
127
128 return shader;
129 }
130
131 /**
132 * Specific display shader loading function. It adds the appropriate extra code
133 * to the fragment shader depending on what is available on the target machine.
134 * Additionally, it picks the appropriate shader files depending on the availability
135 * of OpenGL3.
136 */
loadDisplayShader(QSharedPointer<KisDisplayFilter> displayFilter,bool useHiQualityFiltering)137 KisShaderProgram *KisOpenGLShaderLoader::loadDisplayShader(QSharedPointer<KisDisplayFilter> displayFilter, bool useHiQualityFiltering)
138 {
139 QByteArray fragHeader;
140
141 if (KisOpenGL::supportsLoD()) {
142 fragHeader.append("#define DIRECT_LOD_FETCH\n");
143 if (useHiQualityFiltering) {
144 fragHeader.append("#define HIGHQ_SCALING\n");
145 }
146 }
147
148 // If we have an OCIO display filter and it contains a function we add
149 // it to our shader header which will sit on top of the fragment code.
150 bool haveDisplayFilter = displayFilter && !displayFilter->program().isEmpty();
151 if (haveDisplayFilter) {
152 fragHeader.append("#define USE_OCIO\n");
153 fragHeader.append(displayFilter->program().toLatin1());
154 }
155
156 QString vertPath, fragPath;
157 // Select appropriate shader files
158 if (KisOpenGL::supportsLoD()) {
159 vertPath = "matrix_transform.vert";
160 fragPath = "highq_downscale.frag";
161 } else {
162 vertPath = "matrix_transform_legacy.vert";
163 fragPath = "simple_texture_legacy.frag";
164 }
165
166 KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), fragHeader);
167
168 return shader;
169 }
170
171 /**
172 * Specific checker shader loading function. It picks the appropriate shader
173 * files depending on the availability of OpenGL3 on the target machine.
174 */
loadCheckerShader()175 KisShaderProgram *KisOpenGLShaderLoader::loadCheckerShader()
176 {
177 QString vertPath, fragPath;
178 // Select appropriate shader files
179 if (KisOpenGL::supportsLoD()) {
180 vertPath = "matrix_transform.vert";
181 fragPath = "simple_texture.frag";
182 } else {
183 vertPath = "matrix_transform_legacy.vert";
184 fragPath = "simple_texture_legacy.frag";
185 }
186
187 KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
188
189 return shader;
190 }
191
192 /**
193 * Specific uniform shader loading function. It picks the appropriate shader
194 * files depending on the availability of OpenGL3 on the target machine.
195 */
loadSolidColorShader()196 KisShaderProgram *KisOpenGLShaderLoader::loadSolidColorShader()
197 {
198 QString vertPath, fragPath;
199 // Select appropriate shader files
200 if (KisOpenGL::supportsLoD()) {
201 vertPath = "matrix_transform.vert";
202 fragPath = "solid_color.frag";
203 } else {
204 vertPath = "matrix_transform_legacy.vert";
205 fragPath = "solid_color_legacy.frag";
206 }
207
208 KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
209
210 return shader;
211 }
212
loadOverlayInvertedShader()213 KisShaderProgram *KisOpenGLShaderLoader::loadOverlayInvertedShader()
214 {
215 QString vertPath, fragPath;
216
217 // Select appropriate shader files
218 if (KisOpenGL::supportsLoD() || KisOpenGL::hasOpenGLES()) {
219 vertPath = "matrix_transform.vert";
220
221 if (KisOpenGL::useFBOForToolOutlineRendering()) {
222 fragPath = "overlay_inverted.frag";
223 } else {
224 fragPath = "solid_color.frag";
225 }
226 } else {
227 vertPath = "matrix_transform_legacy.vert";
228 fragPath = "solid_color_legacy.frag";
229 }
230
231 KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
232
233 return shader;
234 }
235