1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#ifdef SDL_VIDEO_RENDER_OGL
24
25#include <SDL3/SDL_opengl.h>
26#include "SDL_shaders_gl.h"
27
28// OpenGL shader implementation
29
30// #define DEBUG_SHADERS
31
32typedef struct
33{
34 GLhandleARB program;
35 GLhandleARB vert_shader;
36 GLhandleARB frag_shader;
37} GL_ShaderData;
38
39struct GL_ShaderContext
40{
41 GLenum (*glGetError)(void);
42
43 PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
44 PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
45 PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
46 PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
47 PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
48 PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
49 PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
50 PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
51 PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
52 PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
53 PFNGLUNIFORM1IARBPROC glUniform1iARB;
54 PFNGLUNIFORM1FARBPROC glUniform1fARB;
55 PFNGLUNIFORM3FARBPROC glUniform3fARB;
56 PFNGLUNIFORM4FARBPROC glUniform4fARB;
57 PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
58
59 bool GL_ARB_texture_rectangle_supported;
60
61 GL_ShaderData shaders[NUM_SHADERS];
62 const float *shader_params[NUM_SHADERS];
63};
64
65/* *INDENT-OFF* */ // clang-format off
66
67#define COLOR_VERTEX_SHADER \
68"varying vec4 v_color;\n" \
69"\n" \
70"void main()\n" \
71"{\n" \
72" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
73" v_color = gl_Color;\n" \
74"}" \
75
76#define TEXTURE_VERTEX_SHADER \
77"varying vec4 v_color;\n" \
78"varying vec2 v_texCoord;\n" \
79"\n" \
80"void main()\n" \
81"{\n" \
82" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
83" v_color = gl_Color;\n" \
84" v_texCoord = vec2(gl_MultiTexCoord0);\n" \
85"}" \
86
87#define YUV_SHADER_PROLOGUE \
88"varying vec4 v_color;\n" \
89"varying vec2 v_texCoord;\n" \
90"uniform sampler2D tex0; // Y \n" \
91"uniform sampler2D tex1; // U \n" \
92"uniform sampler2D tex2; // V \n" \
93"uniform vec3 Yoffset;\n" \
94"uniform vec3 Rcoeff;\n" \
95"uniform vec3 Gcoeff;\n" \
96"uniform vec3 Bcoeff;\n" \
97"\n" \
98
99#define YUV_SHADER_BODY \
100"\n" \
101"void main()\n" \
102"{\n" \
103" vec2 tcoord;\n" \
104" vec3 yuv, rgb;\n" \
105"\n" \
106" // Get the Y value \n" \
107" tcoord = v_texCoord;\n" \
108" yuv.x = texture2D(tex0, tcoord).r;\n" \
109"\n" \
110" // Get the U and V values \n" \
111" tcoord *= UVCoordScale;\n" \
112" yuv.y = texture2D(tex1, tcoord).r;\n" \
113" yuv.z = texture2D(tex2, tcoord).r;\n" \
114"\n" \
115" // Do the color transform \n" \
116" yuv += Yoffset;\n" \
117" rgb.r = dot(yuv, Rcoeff);\n" \
118" rgb.g = dot(yuv, Gcoeff);\n" \
119" rgb.b = dot(yuv, Bcoeff);\n" \
120"\n" \
121" // That was easy. :) \n" \
122" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \
123"}" \
124
125#define NV12_SHADER_PROLOGUE \
126"varying vec4 v_color;\n" \
127"varying vec2 v_texCoord;\n" \
128"uniform sampler2D tex0; // Y \n" \
129"uniform sampler2D tex1; // U/V \n" \
130"uniform vec3 Yoffset;\n" \
131"uniform vec3 Rcoeff;\n" \
132"uniform vec3 Gcoeff;\n" \
133"uniform vec3 Bcoeff;\n" \
134"\n" \
135
136#define NV12_RA_SHADER_BODY \
137"\n" \
138"void main()\n" \
139"{\n" \
140" vec2 tcoord;\n" \
141" vec3 yuv, rgb;\n" \
142"\n" \
143" // Get the Y value \n" \
144" tcoord = v_texCoord;\n" \
145" yuv.x = texture2D(tex0, tcoord).r;\n" \
146"\n" \
147" // Get the U and V values \n" \
148" tcoord *= UVCoordScale;\n" \
149" yuv.yz = texture2D(tex1, tcoord).ra;\n" \
150"\n" \
151" // Do the color transform \n" \
152" yuv += Yoffset;\n" \
153" rgb.r = dot(yuv, Rcoeff);\n" \
154" rgb.g = dot(yuv, Gcoeff);\n" \
155" rgb.b = dot(yuv, Bcoeff);\n" \
156"\n" \
157" // That was easy. :) \n" \
158" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \
159"}" \
160
161#define NV12_RG_SHADER_BODY \
162"\n" \
163"void main()\n" \
164"{\n" \
165" vec2 tcoord;\n" \
166" vec3 yuv, rgb;\n" \
167"\n" \
168" // Get the Y value \n" \
169" tcoord = v_texCoord;\n" \
170" yuv.x = texture2D(tex0, tcoord).r;\n" \
171"\n" \
172" // Get the U and V values \n" \
173" tcoord *= UVCoordScale;\n" \
174" yuv.yz = texture2D(tex1, tcoord).rg;\n" \
175"\n" \
176" // Do the color transform \n" \
177" yuv += Yoffset;\n" \
178" rgb.r = dot(yuv, Rcoeff);\n" \
179" rgb.g = dot(yuv, Gcoeff);\n" \
180" rgb.b = dot(yuv, Bcoeff);\n" \
181"\n" \
182" // That was easy. :) \n" \
183" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \
184"}" \
185
186#define NV21_RA_SHADER_BODY \
187"\n" \
188"void main()\n" \
189"{\n" \
190" vec2 tcoord;\n" \
191" vec3 yuv, rgb;\n" \
192"\n" \
193" // Get the Y value \n" \
194" tcoord = v_texCoord;\n" \
195" yuv.x = texture2D(tex0, tcoord).r;\n" \
196"\n" \
197" // Get the U and V values \n" \
198" tcoord *= UVCoordScale;\n" \
199" yuv.yz = texture2D(tex1, tcoord).ar;\n" \
200"\n" \
201" // Do the color transform \n" \
202" yuv += Yoffset;\n" \
203" rgb.r = dot(yuv, Rcoeff);\n" \
204" rgb.g = dot(yuv, Gcoeff);\n" \
205" rgb.b = dot(yuv, Bcoeff);\n" \
206"\n" \
207" // That was easy. :) \n" \
208" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \
209"}" \
210
211#define NV21_RG_SHADER_BODY \
212"\n" \
213"void main()\n" \
214"{\n" \
215" vec2 tcoord;\n" \
216" vec3 yuv, rgb;\n" \
217"\n" \
218" // Get the Y value \n" \
219" tcoord = v_texCoord;\n" \
220" yuv.x = texture2D(tex0, tcoord).r;\n" \
221"\n" \
222" // Get the U and V values \n" \
223" tcoord *= UVCoordScale;\n" \
224" yuv.yz = texture2D(tex1, tcoord).gr;\n" \
225"\n" \
226" // Do the color transform \n" \
227" yuv += Yoffset;\n" \
228" rgb.r = dot(yuv, Rcoeff);\n" \
229" rgb.g = dot(yuv, Gcoeff);\n" \
230" rgb.b = dot(yuv, Bcoeff);\n" \
231"\n" \
232" // That was easy. :) \n" \
233" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \
234"}" \
235
236/*
237 * NOTE: Always use sampler2D, etc here. We'll #define them to the
238 * texture_rectangle versions if we choose to use that extension.
239 */
240static struct {
241 const char *vertex_shader;
242 const char *fragment_shader;
243 const char *fragment_version;
244} shader_source[NUM_SHADERS] = {
245 // SHADER_NONE
246 { NULL, NULL, NULL },
247
248 // SHADER_SOLID
249 {
250 // vertex shader
251 COLOR_VERTEX_SHADER,
252 // fragment shader
253"varying vec4 v_color;\n"
254"\n"
255"void main()\n"
256"{\n"
257" gl_FragColor = v_color;\n"
258"}",
259 // fragment version
260 NULL
261 },
262
263 // SHADER_RGB
264 {
265 // vertex shader
266 TEXTURE_VERTEX_SHADER,
267 // fragment shader
268"varying vec4 v_color;\n"
269"varying vec2 v_texCoord;\n"
270"uniform sampler2D tex0;\n"
271"uniform vec4 texel_size; // texel size (xy: texel size, zw: texture dimensions)\n"
272"\n"
273"void main()\n"
274"{\n"
275" gl_FragColor = texture2D(tex0, v_texCoord);\n"
276" gl_FragColor.a = 1.0;\n"
277" gl_FragColor *= v_color;\n"
278"}",
279 // fragment version
280 NULL
281 },
282
283 // SHADER_RGBA
284 {
285 // vertex shader
286 TEXTURE_VERTEX_SHADER,
287 // fragment shader
288"varying vec4 v_color;\n"
289"varying vec2 v_texCoord;\n"
290"uniform sampler2D tex0;\n"
291"\n"
292"void main()\n"
293"{\n"
294" gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
295"}",
296 // fragment version
297 NULL
298 },
299
300 // SHADER_RGB_PIXELART
301 {
302 // vertex shader
303 TEXTURE_VERTEX_SHADER,
304 // fragment shader
305"varying vec4 v_color;\n"
306"varying vec2 v_texCoord;\n"
307"uniform sampler2D tex0;\n"
308"uniform vec4 texel_size;\n"
309"\n"
310"void main()\n"
311"{\n"
312" vec2 boxSize = clamp(fwidth(v_texCoord) * texel_size.zw, 1e-5, 1.0);\n"
313" vec2 tx = v_texCoord * texel_size.zw - 0.5 * boxSize;\n"
314" vec2 txOffset = smoothstep(vec2(1.0) - boxSize, vec2(1.0), fract(tx));\n"
315" vec2 uv = (floor(tx) + 0.5 + txOffset) * texel_size.xy;\n"
316" gl_FragColor = textureGrad(tex0, uv, dFdx(v_texCoord), dFdy(v_texCoord));\n"
317" gl_FragColor.a = 1.0;\n"
318" gl_FragColor *= v_color;\n"
319"}",
320 // fragment version
321 "#version 130\n"
322 },
323
324 // SHADER_RGBA_PIXELART
325 {
326 // vertex shader
327 TEXTURE_VERTEX_SHADER,
328 // fragment shader
329"varying vec4 v_color;\n"
330"varying vec2 v_texCoord;\n"
331"uniform sampler2D tex0;\n"
332"uniform vec4 texel_size;\n"
333"\n"
334"void main()\n"
335"{\n"
336" vec2 boxSize = clamp(fwidth(v_texCoord) * texel_size.zw, 1e-5, 1.0);\n"
337" vec2 tx = v_texCoord * texel_size.zw - 0.5 * boxSize;\n"
338" vec2 txOffset = smoothstep(vec2(1.0) - boxSize, vec2(1.0), fract(tx));\n"
339" vec2 uv = (floor(tx) + 0.5 + txOffset) * texel_size.xy;\n"
340" gl_FragColor = textureGrad(tex0, uv, dFdx(v_texCoord), dFdy(v_texCoord));\n"
341" gl_FragColor *= v_color;\n"
342"}",
343 // fragment version
344 "#version 130\n"
345 },
346
347#ifdef SDL_HAVE_YUV
348 // SHADER_YUV
349 {
350 // vertex shader
351 TEXTURE_VERTEX_SHADER,
352 // fragment shader
353 YUV_SHADER_PROLOGUE
354 YUV_SHADER_BODY,
355 // fragment version
356 NULL
357 },
358 // SHADER_NV12_RA
359 {
360 // vertex shader
361 TEXTURE_VERTEX_SHADER,
362 // fragment shader
363 NV12_SHADER_PROLOGUE
364 NV12_RA_SHADER_BODY,
365 // fragment version
366 NULL
367 },
368 // SHADER_NV12_RG
369 {
370 // vertex shader
371 TEXTURE_VERTEX_SHADER,
372 // fragment shader
373 NV12_SHADER_PROLOGUE
374 NV12_RG_SHADER_BODY,
375 // fragment version
376 NULL
377 },
378 // SHADER_NV21_RA
379 {
380 // vertex shader
381 TEXTURE_VERTEX_SHADER,
382 // fragment shader
383 NV12_SHADER_PROLOGUE
384 NV21_RA_SHADER_BODY,
385 // fragment version
386 NULL
387 },
388 // SHADER_NV21_RG
389 {
390 // vertex shader
391 TEXTURE_VERTEX_SHADER,
392 // fragment shader
393 NV12_SHADER_PROLOGUE
394 NV21_RG_SHADER_BODY,
395 // fragment version
396 NULL
397 },
398#endif // SDL_HAVE_YUV
399};
400
401/* *INDENT-ON* */ // clang-format on
402
403static bool CompileShader(GL_ShaderContext *ctx, GLhandleARB shader, const char *version, const char *defines, const char *source)
404{
405 GLint status;
406 const char *sources[3];
407
408 sources[0] = version;
409 sources[1] = defines;
410 sources[2] = source;
411
412 ctx->glShaderSourceARB(shader, SDL_arraysize(sources), sources, NULL);
413 ctx->glCompileShaderARB(shader);
414 ctx->glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
415 if (status == 0) {
416 bool isstack;
417 GLint length;
418 char *info;
419
420 ctx->glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
421 info = SDL_small_alloc(char, length + 1, &isstack);
422 if (info) {
423 ctx->glGetInfoLogARB(shader, length, NULL, info);
424 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to compile shader:");
425 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", defines);
426 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", source);
427 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", info);
428 SDL_small_free(info, isstack);
429 }
430 return false;
431 } else {
432 return true;
433 }
434}
435
436static bool CompileShaderProgram(GL_ShaderContext *ctx, int index, GL_ShaderData *data)
437{
438 const int num_tmus_bound = 4;
439 const char *vert_defines = "";
440 const char *frag_defines = "";
441 const char *frag_version = "";
442 int i;
443 GLint location;
444
445 if (index == SHADER_NONE) {
446 return true;
447 }
448
449 ctx->glGetError();
450
451 // Make sure we use the correct sampler type for our texture type
452 if (ctx->GL_ARB_texture_rectangle_supported) {
453 frag_defines =
454 "#define sampler2D sampler2DRect\n"
455 "#define texture2D texture2DRect\n"
456 "#define UVCoordScale 0.5\n";
457 } else {
458 frag_defines =
459 "#define UVCoordScale 1.0\n";
460 }
461 if (shader_source[index].fragment_version) {
462 frag_version = shader_source[index].fragment_version;
463 }
464
465 // Create one program object to rule them all
466 data->program = ctx->glCreateProgramObjectARB();
467
468 // Create the vertex shader
469 data->vert_shader = ctx->glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
470 if (!CompileShader(ctx, data->vert_shader, "", vert_defines, shader_source[index].vertex_shader)) {
471 return false;
472 }
473
474 // Create the fragment shader
475 data->frag_shader = ctx->glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
476 if (!CompileShader(ctx, data->frag_shader, frag_version, frag_defines, shader_source[index].fragment_shader)) {
477 return false;
478 }
479
480 // ... and in the darkness bind them
481 ctx->glAttachObjectARB(data->program, data->vert_shader);
482 ctx->glAttachObjectARB(data->program, data->frag_shader);
483 ctx->glLinkProgramARB(data->program);
484
485 // Set up some uniform variables
486 ctx->glUseProgramObjectARB(data->program);
487 for (i = 0; i < num_tmus_bound; ++i) {
488 char tex_name[10];
489 (void)SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
490 location = ctx->glGetUniformLocationARB(data->program, tex_name);
491 if (location >= 0) {
492 ctx->glUniform1iARB(location, i);
493 }
494 }
495 ctx->glUseProgramObjectARB(0);
496
497 return ctx->glGetError() == GL_NO_ERROR;
498}
499
500static void DestroyShaderProgram(GL_ShaderContext *ctx, GL_ShaderData *data)
501{
502 ctx->glDeleteObjectARB(data->vert_shader);
503 ctx->glDeleteObjectARB(data->frag_shader);
504 ctx->glDeleteObjectARB(data->program);
505}
506
507GL_ShaderContext *GL_CreateShaderContext(void)
508{
509 GL_ShaderContext *ctx;
510 bool shaders_supported;
511 int i;
512
513 ctx = (GL_ShaderContext *)SDL_calloc(1, sizeof(*ctx));
514 if (!ctx) {
515 return NULL;
516 }
517
518 if (!SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two") &&
519 (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
520 SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle"))) {
521 ctx->GL_ARB_texture_rectangle_supported = true;
522 }
523
524 // Check for shader support
525 shaders_supported = false;
526 if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
527 SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
528 SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
529 SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) {
530 ctx->glGetError = (GLenum(*)(void))SDL_GL_GetProcAddress("glGetError");
531 ctx->glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)SDL_GL_GetProcAddress("glAttachObjectARB");
532 ctx->glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)SDL_GL_GetProcAddress("glCompileShaderARB");
533 ctx->glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glCreateProgramObjectARB");
534 ctx->glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)SDL_GL_GetProcAddress("glCreateShaderObjectARB");
535 ctx->glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)SDL_GL_GetProcAddress("glDeleteObjectARB");
536 ctx->glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)SDL_GL_GetProcAddress("glGetInfoLogARB");
537 ctx->glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)SDL_GL_GetProcAddress("glGetObjectParameterivARB");
538 ctx->glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)SDL_GL_GetProcAddress("glGetUniformLocationARB");
539 ctx->glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)SDL_GL_GetProcAddress("glLinkProgramARB");
540 ctx->glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)SDL_GL_GetProcAddress("glShaderSourceARB");
541 ctx->glUniform1iARB = (PFNGLUNIFORM1IARBPROC)SDL_GL_GetProcAddress("glUniform1iARB");
542 ctx->glUniform1fARB = (PFNGLUNIFORM1FARBPROC)SDL_GL_GetProcAddress("glUniform1fARB");
543 ctx->glUniform3fARB = (PFNGLUNIFORM3FARBPROC)SDL_GL_GetProcAddress("glUniform3fARB");
544 ctx->glUniform4fARB = (PFNGLUNIFORM4FARBPROC)SDL_GL_GetProcAddress("glUniform4fARB");
545 ctx->glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glUseProgramObjectARB");
546 if (ctx->glGetError &&
547 ctx->glAttachObjectARB &&
548 ctx->glCompileShaderARB &&
549 ctx->glCreateProgramObjectARB &&
550 ctx->glCreateShaderObjectARB &&
551 ctx->glDeleteObjectARB &&
552 ctx->glGetInfoLogARB &&
553 ctx->glGetObjectParameterivARB &&
554 ctx->glGetUniformLocationARB &&
555 ctx->glLinkProgramARB &&
556 ctx->glShaderSourceARB &&
557 ctx->glUniform1iARB &&
558 ctx->glUniform1fARB &&
559 ctx->glUniform3fARB &&
560 ctx->glUseProgramObjectARB) {
561 shaders_supported = true;
562 }
563 }
564
565 if (!shaders_supported) {
566 SDL_free(ctx);
567 return NULL;
568 }
569
570 // Compile all the shaders
571 for (i = 0; i < NUM_SHADERS; ++i) {
572 if (!CompileShaderProgram(ctx, i, &ctx->shaders[i])) {
573 GL_DestroyShaderContext(ctx);
574 return NULL;
575 }
576 }
577
578 // We're done!
579 return ctx;
580}
581
582void GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader, const float *shader_params)
583{
584 GLint location;
585 GLhandleARB program = ctx->shaders[shader].program;
586
587 ctx->glUseProgramObjectARB(program);
588
589 if (shader_params && shader_params != ctx->shader_params[shader]) {
590 if (shader == SHADER_RGB_PIXELART ||
591 shader == SHADER_RGBA_PIXELART) {
592 location = ctx->glGetUniformLocationARB(program, "texel_size");
593 if (location >= 0) {
594 ctx->glUniform4fARB(location, shader_params[0], shader_params[1], shader_params[2], shader_params[3]);
595 }
596 }
597
598#ifdef SDL_HAVE_YUV
599 if (shader >= SHADER_YUV) {
600 // YUV shader params are Yoffset, 0, Rcoeff, 0, Gcoeff, 0, Bcoeff, 0
601 location = ctx->glGetUniformLocationARB(program, "Yoffset");
602 if (location >= 0) {
603 ctx->glUniform3fARB(location, shader_params[0], shader_params[1], shader_params[2]);
604 }
605 location = ctx->glGetUniformLocationARB(program, "Rcoeff");
606 if (location >= 0) {
607 ctx->glUniform3fARB(location, shader_params[4], shader_params[5], shader_params[6]);
608 }
609 location = ctx->glGetUniformLocationARB(program, "Gcoeff");
610 if (location >= 0) {
611 ctx->glUniform3fARB(location, shader_params[8], shader_params[9], shader_params[10]);
612 }
613 location = ctx->glGetUniformLocationARB(program, "Bcoeff");
614 if (location >= 0) {
615 ctx->glUniform3fARB(location, shader_params[12], shader_params[13], shader_params[14]);
616 }
617 }
618#endif // SDL_HAVE_YUV
619
620 ctx->shader_params[shader] = shader_params;
621 }
622}
623
624void GL_DestroyShaderContext(GL_ShaderContext *ctx)
625{
626 int i;
627
628 for (i = 0; i < NUM_SHADERS; ++i) {
629 DestroyShaderProgram(ctx, &ctx->shaders[i]);
630 }
631 SDL_free(ctx);
632}
633
634#endif // SDL_VIDEO_RENDER_OGL
635