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 | |
32 | typedef struct |
33 | { |
34 | GLhandleARB program; |
35 | GLhandleARB vert_shader; |
36 | GLhandleARB frag_shader; |
37 | } GL_ShaderData; |
38 | |
39 | struct 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 | */ |
240 | static 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 | |
403 | static 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 | |
436 | static 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 | |
500 | static 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 | |
507 | GL_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 | |
582 | void 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 | |
624 | void 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 | |