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 | |
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 |
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 |
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 |
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 * |
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 |
523 | GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader) |
524 | { |
525 | ctx->glUseProgramObjectARB(ctx->shaders[shader].program); |
526 | } |
527 | |
528 | void |
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 | |