1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
24 
25 #include "SDL_stdinc.h"
26 #include "SDL_opengl.h"
27 #include "SDL_video.h"
28 #include "SDL_shaders_gl.h"
29 
30 /* OpenGL shader implementation */
31 
32 /* #define DEBUG_SHADERS */
33 
34 typedef struct
35 {
36     GLhandleARB program;
37     GLhandleARB vert_shader;
38     GLhandleARB frag_shader;
39 } GL_ShaderData;
40 
41 struct GL_ShaderContext
42 {
43     GLenum (*glGetError)(void);
44 
45     PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
46     PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
47     PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
48     PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
49     PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
50     PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
51     PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
52     PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
53     PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
54     PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
55     PFNGLUNIFORM1IARBPROC glUniform1iARB;
56     PFNGLUNIFORM1FARBPROC glUniform1fARB;
57     PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
58 
59     SDL_bool GL_ARB_texture_rectangle_supported;
60 
61     GL_ShaderData shaders[NUM_SHADERS];
62 };
63 
64 #define COLOR_VERTEX_SHADER                                     \
65 "varying vec4 v_color;\n"                                       \
66 "\n"                                                            \
67 "void main()\n"                                                 \
68 "{\n"                                                           \
69 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
70 "    v_color = gl_Color;\n"                                     \
71 "}"                                                             \
72 
73 #define TEXTURE_VERTEX_SHADER                                   \
74 "varying vec4 v_color;\n"                                       \
75 "varying vec2 v_texCoord;\n"                                    \
76 "\n"                                                            \
77 "void main()\n"                                                 \
78 "{\n"                                                           \
79 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
80 "    v_color = gl_Color;\n"                                     \
81 "    v_texCoord = vec2(gl_MultiTexCoord0);\n"                   \
82 "}"                                                             \
83 
84 #define JPEG_SHADER_CONSTANTS                                   \
85 "// YUV offset \n"                                              \
86 "const vec3 offset = vec3(0, -0.501960814, -0.501960814);\n"    \
87 "\n"                                                            \
88 "// RGB coefficients \n"                                        \
89 "const vec3 Rcoeff = vec3(1,  0.000,  1.402);\n"                \
90 "const vec3 Gcoeff = vec3(1, -0.3441, -0.7141);\n"              \
91 "const vec3 Bcoeff = vec3(1,  1.772,  0.000);\n"                \
92 
93 #define BT601_SHADER_CONSTANTS                                  \
94 "// YUV offset \n"                                              \
95 "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \
96 "\n"                                                            \
97 "// RGB coefficients \n"                                        \
98 "const vec3 Rcoeff = vec3(1.1644,  0.000,  1.596);\n"           \
99 "const vec3 Gcoeff = vec3(1.1644, -0.3918, -0.813);\n"          \
100 "const vec3 Bcoeff = vec3(1.1644,  2.0172,  0.000);\n"          \
101 
102 #define BT709_SHADER_CONSTANTS                                  \
103 "// YUV offset \n"                                              \
104 "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \
105 "\n"                                                            \
106 "// RGB coefficients \n"                                        \
107 "const vec3 Rcoeff = vec3(1.1644,  0.000,  1.7927);\n"          \
108 "const vec3 Gcoeff = vec3(1.1644, -0.2132, -0.5329);\n"         \
109 "const vec3 Bcoeff = vec3(1.1644,  2.1124,  0.000);\n"          \
110 
111 #define YUV_SHADER_PROLOGUE                                     \
112 "varying vec4 v_color;\n"                                       \
113 "varying vec2 v_texCoord;\n"                                    \
114 "uniform sampler2D tex0; // Y \n"                               \
115 "uniform sampler2D tex1; // U \n"                               \
116 "uniform sampler2D tex2; // V \n"                               \
117 "\n"                                                            \
118 
119 #define YUV_SHADER_BODY                                         \
120 "\n"                                                            \
121 "void main()\n"                                                 \
122 "{\n"                                                           \
123 "    vec2 tcoord;\n"                                            \
124 "    vec3 yuv, rgb;\n"                                          \
125 "\n"                                                            \
126 "    // Get the Y value \n"                                     \
127 "    tcoord = v_texCoord;\n"                                    \
128 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
129 "\n"                                                            \
130 "    // Get the U and V values \n"                              \
131 "    tcoord *= UVCoordScale;\n"                                 \
132 "    yuv.y = texture2D(tex1, tcoord).r;\n"                      \
133 "    yuv.z = texture2D(tex2, tcoord).r;\n"                      \
134 "\n"                                                            \
135 "    // Do the color transform \n"                              \
136 "    yuv += offset;\n"                                          \
137 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
138 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
139 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
140 "\n"                                                            \
141 "    // That was easy. :) \n"                                   \
142 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
143 "}"                                                             \
144 
145 #define NV12_SHADER_PROLOGUE                                    \
146 "varying vec4 v_color;\n"                                       \
147 "varying vec2 v_texCoord;\n"                                    \
148 "uniform sampler2D tex0; // Y \n"                               \
149 "uniform sampler2D tex1; // U/V \n"                             \
150 "\n"                                                            \
151 
152 #define NV12_SHADER_BODY                                        \
153 "\n"                                                            \
154 "void main()\n"                                                 \
155 "{\n"                                                           \
156 "    vec2 tcoord;\n"                                            \
157 "    vec3 yuv, rgb;\n"                                          \
158 "\n"                                                            \
159 "    // Get the Y value \n"                                     \
160 "    tcoord = v_texCoord;\n"                                    \
161 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
162 "\n"                                                            \
163 "    // Get the U and V values \n"                              \
164 "    tcoord *= UVCoordScale;\n"                                 \
165 "    yuv.yz = texture2D(tex1, tcoord).ra;\n"                    \
166 "\n"                                                            \
167 "    // Do the color transform \n"                              \
168 "    yuv += offset;\n"                                          \
169 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
170 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
171 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
172 "\n"                                                            \
173 "    // That was easy. :) \n"                                   \
174 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
175 "}"                                                             \
176 
177 #define NV21_SHADER_PROLOGUE                                    \
178 "varying vec4 v_color;\n"                                       \
179 "varying vec2 v_texCoord;\n"                                    \
180 "uniform sampler2D tex0; // Y \n"                               \
181 "uniform sampler2D tex1; // U/V \n"                             \
182 "\n"                                                            \
183 
184 #define NV21_SHADER_BODY                                        \
185 "\n"                                                            \
186 "void main()\n"                                                 \
187 "{\n"                                                           \
188 "    vec2 tcoord;\n"                                            \
189 "    vec3 yuv, rgb;\n"                                          \
190 "\n"                                                            \
191 "    // Get the Y value \n"                                     \
192 "    tcoord = v_texCoord;\n"                                    \
193 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
194 "\n"                                                            \
195 "    // Get the U and V values \n"                              \
196 "    tcoord *= UVCoordScale;\n"                                 \
197 "    yuv.yz = texture2D(tex1, tcoord).ar;\n"                    \
198 "\n"                                                            \
199 "    // Do the color transform \n"                              \
200 "    yuv += offset;\n"                                          \
201 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
202 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
203 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
204 "\n"                                                            \
205 "    // That was easy. :) \n"                                   \
206 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
207 "}"                                                             \
208 
209 /*
210  * NOTE: Always use sampler2D, etc here. We'll #define them to the
211  *  texture_rectangle versions if we choose to use that extension.
212  */
213 static const char *shader_source[NUM_SHADERS][2] =
214 {
215     /* SHADER_NONE */
216     { NULL, NULL },
217 
218     /* SHADER_SOLID */
219     {
220         /* vertex shader */
221         COLOR_VERTEX_SHADER,
222         /* fragment shader */
223 "varying vec4 v_color;\n"
224 "\n"
225 "void main()\n"
226 "{\n"
227 "    gl_FragColor = v_color;\n"
228 "}"
229     },
230 
231     /* SHADER_RGB */
232     {
233         /* vertex shader */
234         TEXTURE_VERTEX_SHADER,
235         /* fragment shader */
236 "varying vec4 v_color;\n"
237 "varying vec2 v_texCoord;\n"
238 "uniform sampler2D tex0;\n"
239 "\n"
240 "void main()\n"
241 "{\n"
242 "    gl_FragColor = texture2D(tex0, v_texCoord);\n"
243 "    gl_FragColor.a = 1.0;\n"
244 "    gl_FragColor *= v_color;\n"
245 "}"
246     },
247 
248     /* SHADER_RGBA */
249     {
250         /* vertex shader */
251         TEXTURE_VERTEX_SHADER,
252         /* fragment shader */
253 "varying vec4 v_color;\n"
254 "varying vec2 v_texCoord;\n"
255 "uniform sampler2D tex0;\n"
256 "\n"
257 "void main()\n"
258 "{\n"
259 "    gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
260 "}"
261     },
262 
263     /* SHADER_YUV_JPEG */
264     {
265         /* vertex shader */
266         TEXTURE_VERTEX_SHADER,
267         /* fragment shader */
268         YUV_SHADER_PROLOGUE
269         JPEG_SHADER_CONSTANTS
270         YUV_SHADER_BODY
271     },
272     /* SHADER_YUV_BT601 */
273     {
274         /* vertex shader */
275         TEXTURE_VERTEX_SHADER,
276         /* fragment shader */
277         YUV_SHADER_PROLOGUE
278         BT601_SHADER_CONSTANTS
279         YUV_SHADER_BODY
280     },
281     /* SHADER_YUV_BT709 */
282     {
283         /* vertex shader */
284         TEXTURE_VERTEX_SHADER,
285         /* fragment shader */
286         YUV_SHADER_PROLOGUE
287         BT709_SHADER_CONSTANTS
288         YUV_SHADER_BODY
289     },
290     /* SHADER_NV12_JPEG */
291     {
292         /* vertex shader */
293         TEXTURE_VERTEX_SHADER,
294         /* fragment shader */
295         NV12_SHADER_PROLOGUE
296         JPEG_SHADER_CONSTANTS
297         NV12_SHADER_BODY
298     },
299     /* SHADER_NV12_BT601 */
300     {
301         /* vertex shader */
302         TEXTURE_VERTEX_SHADER,
303         /* fragment shader */
304         NV12_SHADER_PROLOGUE
305         BT601_SHADER_CONSTANTS
306         NV12_SHADER_BODY
307     },
308     /* SHADER_NV12_BT709 */
309     {
310         /* vertex shader */
311         TEXTURE_VERTEX_SHADER,
312         /* fragment shader */
313         NV12_SHADER_PROLOGUE
314         BT709_SHADER_CONSTANTS
315         NV12_SHADER_BODY
316     },
317     /* SHADER_NV21_JPEG */
318     {
319         /* vertex shader */
320         TEXTURE_VERTEX_SHADER,
321         /* fragment shader */
322         NV21_SHADER_PROLOGUE
323         JPEG_SHADER_CONSTANTS
324         NV21_SHADER_BODY
325     },
326     /* SHADER_NV21_BT601 */
327     {
328         /* vertex shader */
329         TEXTURE_VERTEX_SHADER,
330         /* fragment shader */
331         NV21_SHADER_PROLOGUE
332         BT601_SHADER_CONSTANTS
333         NV21_SHADER_BODY
334     },
335     /* SHADER_NV21_BT709 */
336     {
337         /* vertex shader */
338         TEXTURE_VERTEX_SHADER,
339         /* fragment shader */
340         NV21_SHADER_PROLOGUE
341         BT709_SHADER_CONSTANTS
342         NV21_SHADER_BODY
343     },
344 };
345 
346 static SDL_bool
CompileShader(GL_ShaderContext * ctx,GLhandleARB shader,const char * defines,const char * source)347 CompileShader(GL_ShaderContext *ctx, GLhandleARB shader, const char *defines, const char *source)
348 {
349     GLint status;
350     const char *sources[2];
351 
352     sources[0] = defines;
353     sources[1] = source;
354 
355     ctx->glShaderSourceARB(shader, SDL_arraysize(sources), sources, NULL);
356     ctx->glCompileShaderARB(shader);
357     ctx->glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
358     if (status == 0) {
359         SDL_bool isstack;
360         GLint length;
361         char *info;
362 
363         ctx->glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
364         info = SDL_small_alloc(char, length+1, &isstack);
365         ctx->glGetInfoLogARB(shader, length, NULL, info);
366         SDL_LogError(SDL_LOG_CATEGORY_RENDER,
367             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
368 #ifdef DEBUG_SHADERS
369         fprintf(stderr,
370             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
371 #endif
372         SDL_small_free(info, isstack);
373 
374         return SDL_FALSE;
375     } else {
376         return SDL_TRUE;
377     }
378 }
379 
380 static SDL_bool
CompileShaderProgram(GL_ShaderContext * ctx,int index,GL_ShaderData * data)381 CompileShaderProgram(GL_ShaderContext *ctx, int index, GL_ShaderData *data)
382 {
383     const int num_tmus_bound = 4;
384     const char *vert_defines = "";
385     const char *frag_defines = "";
386     int i;
387     GLint location;
388 
389     if (index == SHADER_NONE) {
390         return SDL_TRUE;
391     }
392 
393     ctx->glGetError();
394 
395     /* Make sure we use the correct sampler type for our texture type */
396     if (ctx->GL_ARB_texture_rectangle_supported) {
397         frag_defines =
398 "#define sampler2D sampler2DRect\n"
399 "#define texture2D texture2DRect\n"
400 "#define UVCoordScale 0.5\n";
401     } else {
402         frag_defines =
403 "#define UVCoordScale 1.0\n";
404     }
405 
406     /* Create one program object to rule them all */
407     data->program = ctx->glCreateProgramObjectARB();
408 
409     /* Create the vertex shader */
410     data->vert_shader = ctx->glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
411     if (!CompileShader(ctx, data->vert_shader, vert_defines, shader_source[index][0])) {
412         return SDL_FALSE;
413     }
414 
415     /* Create the fragment shader */
416     data->frag_shader = ctx->glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
417     if (!CompileShader(ctx, data->frag_shader, frag_defines, shader_source[index][1])) {
418         return SDL_FALSE;
419     }
420 
421     /* ... and in the darkness bind them */
422     ctx->glAttachObjectARB(data->program, data->vert_shader);
423     ctx->glAttachObjectARB(data->program, data->frag_shader);
424     ctx->glLinkProgramARB(data->program);
425 
426     /* Set up some uniform variables */
427     ctx->glUseProgramObjectARB(data->program);
428     for (i = 0; i < num_tmus_bound; ++i) {
429         char tex_name[10];
430         SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
431         location = ctx->glGetUniformLocationARB(data->program, tex_name);
432         if (location >= 0) {
433             ctx->glUniform1iARB(location, i);
434         }
435     }
436     ctx->glUseProgramObjectARB(0);
437 
438     return (ctx->glGetError() == GL_NO_ERROR);
439 }
440 
441 static void
DestroyShaderProgram(GL_ShaderContext * ctx,GL_ShaderData * data)442 DestroyShaderProgram(GL_ShaderContext *ctx, GL_ShaderData *data)
443 {
444     ctx->glDeleteObjectARB(data->vert_shader);
445     ctx->glDeleteObjectARB(data->frag_shader);
446     ctx->glDeleteObjectARB(data->program);
447 }
448 
449 GL_ShaderContext *
GL_CreateShaderContext(void)450 GL_CreateShaderContext(void)
451 {
452     GL_ShaderContext *ctx;
453     SDL_bool shaders_supported;
454     int i;
455 
456     ctx = (GL_ShaderContext *)SDL_calloc(1, sizeof(*ctx));
457     if (!ctx) {
458         return NULL;
459     }
460 
461     if (!SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two") &&
462         (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
463          SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle"))) {
464         ctx->GL_ARB_texture_rectangle_supported = SDL_TRUE;
465     }
466 
467     /* Check for shader support */
468     shaders_supported = SDL_FALSE;
469     if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
470         SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
471         SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
472         SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) {
473         ctx->glGetError = (GLenum (*)(void)) SDL_GL_GetProcAddress("glGetError");
474         ctx->glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
475         ctx->glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
476         ctx->glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
477         ctx->glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
478         ctx->glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
479         ctx->glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
480         ctx->glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
481         ctx->glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
482         ctx->glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
483         ctx->glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
484         ctx->glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
485         ctx->glUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB");
486         ctx->glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
487         if (ctx->glGetError &&
488             ctx->glAttachObjectARB &&
489             ctx->glCompileShaderARB &&
490             ctx->glCreateProgramObjectARB &&
491             ctx->glCreateShaderObjectARB &&
492             ctx->glDeleteObjectARB &&
493             ctx->glGetInfoLogARB &&
494             ctx->glGetObjectParameterivARB &&
495             ctx->glGetUniformLocationARB &&
496             ctx->glLinkProgramARB &&
497             ctx->glShaderSourceARB &&
498             ctx->glUniform1iARB &&
499             ctx->glUniform1fARB &&
500             ctx->glUseProgramObjectARB) {
501             shaders_supported = SDL_TRUE;
502         }
503     }
504 
505     if (!shaders_supported) {
506         SDL_free(ctx);
507         return NULL;
508     }
509 
510     /* Compile all the shaders */
511     for (i = 0; i < NUM_SHADERS; ++i) {
512         if (!CompileShaderProgram(ctx, i, &ctx->shaders[i])) {
513             GL_DestroyShaderContext(ctx);
514             return NULL;
515         }
516     }
517 
518     /* We're done! */
519     return ctx;
520 }
521 
522 void
GL_SelectShader(GL_ShaderContext * ctx,GL_Shader shader)523 GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader)
524 {
525     ctx->glUseProgramObjectARB(ctx->shaders[shader].program);
526 }
527 
528 void
GL_DestroyShaderContext(GL_ShaderContext * ctx)529 GL_DestroyShaderContext(GL_ShaderContext *ctx)
530 {
531     int i;
532 
533     for (i = 0; i < NUM_SHADERS; ++i) {
534         DestroyShaderProgram(ctx, &ctx->shaders[i]);
535     }
536     SDL_free(ctx);
537 }
538 
539 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
540 
541 /* vi: set ts=4 sw=4 expandtab: */
542