1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
34typedef struct
35{
36 GLhandleARB program;
37 GLhandleARB vert_shader;
38 GLhandleARB frag_shader;
39} GL_ShaderData;
40
41struct 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 */
213static 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
346static SDL_bool
347CompileShader(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
380static SDL_bool
381CompileShaderProgram(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
441static void
442DestroyShaderProgram(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
449GL_ShaderContext *
450GL_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
522void
523GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader)
524{
525 ctx->glUseProgramObjectARB(ctx->shaders[shader].program);
526}
527
528void
529GL_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