1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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_ES && !SDL_RENDER_DISABLED
24
25 #include "SDL_hints.h"
26 #include "SDL_opengles.h"
27 #include "../SDL_sysrender.h"
28
29 /* To prevent unnecessary window recreation,
30 * these should match the defaults selected in SDL_GL_ResetAttributes
31 */
32
33 #define RENDERER_CONTEXT_MAJOR 1
34 #define RENDERER_CONTEXT_MINOR 1
35
36 #if defined(SDL_VIDEO_DRIVER_PANDORA)
37
38 /* Empty function stub to get OpenGL ES 1.x support without */
39 /* OpenGL ES extension GL_OES_draw_texture supported */
40 GL_API void GL_APIENTRY
glDrawTexiOES(GLint x,GLint y,GLint z,GLint width,GLint height)41 glDrawTexiOES(GLint x, GLint y, GLint z, GLint width, GLint height)
42 {
43 return;
44 }
45
46 #endif /* SDL_VIDEO_DRIVER_PANDORA */
47
48 /* OpenGL ES 1.1 renderer implementation, based on the OpenGL renderer */
49
50 /* Used to re-create the window with OpenGL ES capability */
51 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
52
53 static const float inv255f = 1.0f / 255.0f;
54
55 static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags);
56 static void GLES_WindowEvent(SDL_Renderer * renderer,
57 const SDL_WindowEvent *event);
58 static int GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
59 static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
60 static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
61 const SDL_Rect * rect, const void *pixels,
62 int pitch);
63 static int GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
64 const SDL_Rect * rect, void **pixels, int *pitch);
65 static void GLES_UnlockTexture(SDL_Renderer * renderer,
66 SDL_Texture * texture);
67 static int GLES_SetRenderTarget(SDL_Renderer * renderer,
68 SDL_Texture * texture);
69 static int GLES_UpdateViewport(SDL_Renderer * renderer);
70 static int GLES_UpdateClipRect(SDL_Renderer * renderer);
71 static int GLES_RenderClear(SDL_Renderer * renderer);
72 static int GLES_RenderDrawPoints(SDL_Renderer * renderer,
73 const SDL_FPoint * points, int count);
74 static int GLES_RenderDrawLines(SDL_Renderer * renderer,
75 const SDL_FPoint * points, int count);
76 static int GLES_RenderFillRects(SDL_Renderer * renderer,
77 const SDL_FRect * rects, int count);
78 static int GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
79 const SDL_Rect * srcrect,
80 const SDL_FRect * dstrect);
81 static int GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
82 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
83 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
84 static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
85 Uint32 pixel_format, void * pixels, int pitch);
86 static void GLES_RenderPresent(SDL_Renderer * renderer);
87 static void GLES_DestroyTexture(SDL_Renderer * renderer,
88 SDL_Texture * texture);
89 static void GLES_DestroyRenderer(SDL_Renderer * renderer);
90 static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
91 static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture);
92
93 typedef struct GLES_FBOList GLES_FBOList;
94
95 struct GLES_FBOList
96 {
97 Uint32 w, h;
98 GLuint FBO;
99 GLES_FBOList *next;
100 };
101
102
103 SDL_RenderDriver GLES_RenderDriver = {
104 GLES_CreateRenderer,
105 {
106 "opengles",
107 (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
108 1,
109 {SDL_PIXELFORMAT_ABGR8888},
110 0,
111 0}
112 };
113
114 typedef struct
115 {
116 SDL_GLContext context;
117 struct {
118 Uint32 color;
119 int blendMode;
120 SDL_bool tex_coords;
121 } current;
122
123 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
124 #define SDL_PROC_OES SDL_PROC
125 #include "SDL_glesfuncs.h"
126 #undef SDL_PROC
127 #undef SDL_PROC_OES
128 SDL_bool GL_OES_framebuffer_object_supported;
129 GLES_FBOList *framebuffers;
130 GLuint window_framebuffer;
131
132 SDL_bool GL_OES_blend_func_separate_supported;
133 } GLES_RenderData;
134
135 typedef struct
136 {
137 GLuint texture;
138 GLenum type;
139 GLfloat texw;
140 GLfloat texh;
141 GLenum format;
142 GLenum formattype;
143 void *pixels;
144 int pitch;
145 GLES_FBOList *fbo;
146 } GLES_TextureData;
147
148 static int
GLES_SetError(const char * prefix,GLenum result)149 GLES_SetError(const char *prefix, GLenum result)
150 {
151 const char *error;
152
153 switch (result) {
154 case GL_NO_ERROR:
155 error = "GL_NO_ERROR";
156 break;
157 case GL_INVALID_ENUM:
158 error = "GL_INVALID_ENUM";
159 break;
160 case GL_INVALID_VALUE:
161 error = "GL_INVALID_VALUE";
162 break;
163 case GL_INVALID_OPERATION:
164 error = "GL_INVALID_OPERATION";
165 break;
166 case GL_STACK_OVERFLOW:
167 error = "GL_STACK_OVERFLOW";
168 break;
169 case GL_STACK_UNDERFLOW:
170 error = "GL_STACK_UNDERFLOW";
171 break;
172 case GL_OUT_OF_MEMORY:
173 error = "GL_OUT_OF_MEMORY";
174 break;
175 default:
176 error = "UNKNOWN";
177 break;
178 }
179 return SDL_SetError("%s: %s", prefix, error);
180 }
181
GLES_LoadFunctions(GLES_RenderData * data)182 static int GLES_LoadFunctions(GLES_RenderData * data)
183 {
184 #if SDL_VIDEO_DRIVER_UIKIT
185 #define __SDL_NOGETPROCADDR__
186 #elif SDL_VIDEO_DRIVER_ANDROID
187 #define __SDL_NOGETPROCADDR__
188 #elif SDL_VIDEO_DRIVER_PANDORA
189 #define __SDL_NOGETPROCADDR__
190 #endif
191
192 #ifdef __SDL_NOGETPROCADDR__
193 #define SDL_PROC(ret,func,params) data->func=func;
194 #define SDL_PROC_OES(ret,func,params) data->func=func;
195 #else
196 #define SDL_PROC(ret,func,params) \
197 do { \
198 data->func = SDL_GL_GetProcAddress(#func); \
199 if ( ! data->func ) { \
200 return SDL_SetError("Couldn't load GLES function %s: %s\n", #func, SDL_GetError()); \
201 } \
202 } while ( 0 );
203 #define SDL_PROC_OES(ret,func,params) \
204 do { \
205 data->func = SDL_GL_GetProcAddress(#func); \
206 } while ( 0 );
207 #endif /* __SDL_NOGETPROCADDR__ */
208
209 #include "SDL_glesfuncs.h"
210 #undef SDL_PROC
211 #undef SDL_PROC_OES
212 return 0;
213 }
214
215 static SDL_GLContext SDL_CurrentContext = NULL;
216
217 GLES_FBOList *
GLES_GetFBO(GLES_RenderData * data,Uint32 w,Uint32 h)218 GLES_GetFBO(GLES_RenderData *data, Uint32 w, Uint32 h)
219 {
220 GLES_FBOList *result = data->framebuffers;
221 while ((result) && ((result->w != w) || (result->h != h)) ) {
222 result = result->next;
223 }
224 if (result == NULL) {
225 result = SDL_malloc(sizeof(GLES_FBOList));
226 result->w = w;
227 result->h = h;
228 data->glGenFramebuffersOES(1, &result->FBO);
229 result->next = data->framebuffers;
230 data->framebuffers = result;
231 }
232 return result;
233 }
234
235
236 static int
GLES_ActivateRenderer(SDL_Renderer * renderer)237 GLES_ActivateRenderer(SDL_Renderer * renderer)
238 {
239 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
240
241 if (SDL_CurrentContext != data->context) {
242 if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
243 return -1;
244 }
245 SDL_CurrentContext = data->context;
246
247 GLES_UpdateViewport(renderer);
248 }
249 return 0;
250 }
251
252 /* This is called if we need to invalidate all of the SDL OpenGL state */
253 static void
GLES_ResetState(SDL_Renderer * renderer)254 GLES_ResetState(SDL_Renderer *renderer)
255 {
256 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
257
258 if (SDL_CurrentContext == data->context) {
259 GLES_UpdateViewport(renderer);
260 } else {
261 GLES_ActivateRenderer(renderer);
262 }
263
264 data->current.color = 0;
265 data->current.blendMode = -1;
266 data->current.tex_coords = SDL_FALSE;
267
268 data->glDisable(GL_DEPTH_TEST);
269 data->glDisable(GL_CULL_FACE);
270
271 data->glMatrixMode(GL_MODELVIEW);
272 data->glLoadIdentity();
273
274 data->glEnableClientState(GL_VERTEX_ARRAY);
275 data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
276 }
277
278 SDL_Renderer *
GLES_CreateRenderer(SDL_Window * window,Uint32 flags)279 GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
280 {
281
282 SDL_Renderer *renderer;
283 GLES_RenderData *data;
284 GLint value;
285 Uint32 window_flags;
286 int profile_mask = 0, major = 0, minor = 0;
287 SDL_bool changed_window = SDL_FALSE;
288
289 SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
290 SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
291 SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
292
293 window_flags = SDL_GetWindowFlags(window);
294 if (!(window_flags & SDL_WINDOW_OPENGL) ||
295 profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
296
297 changed_window = SDL_TRUE;
298 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
299 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
300 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
301
302 if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
303 goto error;
304 }
305 }
306
307 renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
308 if (!renderer) {
309 SDL_OutOfMemory();
310 goto error;
311 }
312
313 data = (GLES_RenderData *) SDL_calloc(1, sizeof(*data));
314 if (!data) {
315 GLES_DestroyRenderer(renderer);
316 SDL_OutOfMemory();
317 goto error;
318 }
319
320 renderer->WindowEvent = GLES_WindowEvent;
321 renderer->GetOutputSize = GLES_GetOutputSize;
322 renderer->CreateTexture = GLES_CreateTexture;
323 renderer->UpdateTexture = GLES_UpdateTexture;
324 renderer->LockTexture = GLES_LockTexture;
325 renderer->UnlockTexture = GLES_UnlockTexture;
326 renderer->SetRenderTarget = GLES_SetRenderTarget;
327 renderer->UpdateViewport = GLES_UpdateViewport;
328 renderer->UpdateClipRect = GLES_UpdateClipRect;
329 renderer->RenderClear = GLES_RenderClear;
330 renderer->RenderDrawPoints = GLES_RenderDrawPoints;
331 renderer->RenderDrawLines = GLES_RenderDrawLines;
332 renderer->RenderFillRects = GLES_RenderFillRects;
333 renderer->RenderCopy = GLES_RenderCopy;
334 renderer->RenderCopyEx = GLES_RenderCopyEx;
335 renderer->RenderReadPixels = GLES_RenderReadPixels;
336 renderer->RenderPresent = GLES_RenderPresent;
337 renderer->DestroyTexture = GLES_DestroyTexture;
338 renderer->DestroyRenderer = GLES_DestroyRenderer;
339 renderer->GL_BindTexture = GLES_BindTexture;
340 renderer->GL_UnbindTexture = GLES_UnbindTexture;
341 renderer->info = GLES_RenderDriver.info;
342 renderer->info.flags = SDL_RENDERER_ACCELERATED;
343 renderer->driverdata = data;
344 renderer->window = window;
345
346 data->context = SDL_GL_CreateContext(window);
347 if (!data->context) {
348 GLES_DestroyRenderer(renderer);
349 goto error;
350 }
351 if (SDL_GL_MakeCurrent(window, data->context) < 0) {
352 GLES_DestroyRenderer(renderer);
353 goto error;
354 }
355
356 if (GLES_LoadFunctions(data) < 0) {
357 GLES_DestroyRenderer(renderer);
358 goto error;
359 }
360
361 if (flags & SDL_RENDERER_PRESENTVSYNC) {
362 SDL_GL_SetSwapInterval(1);
363 } else {
364 SDL_GL_SetSwapInterval(0);
365 }
366 if (SDL_GL_GetSwapInterval() > 0) {
367 renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
368 }
369
370 value = 0;
371 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
372 renderer->info.max_texture_width = value;
373 value = 0;
374 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
375 renderer->info.max_texture_height = value;
376
377 /* Android does not report GL_OES_framebuffer_object but the functionality seems to be there anyway */
378 if (SDL_GL_ExtensionSupported("GL_OES_framebuffer_object") || data->glGenFramebuffersOES) {
379 data->GL_OES_framebuffer_object_supported = SDL_TRUE;
380 renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
381
382 value = 0;
383 data->glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &value);
384 data->window_framebuffer = (GLuint)value;
385 }
386 data->framebuffers = NULL;
387
388 if (SDL_GL_ExtensionSupported("GL_OES_blend_func_separate")) {
389 data->GL_OES_blend_func_separate_supported = SDL_TRUE;
390 }
391
392 /* Set up parameters for rendering */
393 GLES_ResetState(renderer);
394
395 return renderer;
396
397 error:
398 if (changed_window) {
399 /* Uh oh, better try to put it back... */
400 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
401 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
402 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
403 SDL_RecreateWindow(window, window_flags);
404 }
405 return NULL;
406 }
407
408 static void
GLES_WindowEvent(SDL_Renderer * renderer,const SDL_WindowEvent * event)409 GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
410 {
411 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
412
413 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
414 event->event == SDL_WINDOWEVENT_SHOWN ||
415 event->event == SDL_WINDOWEVENT_HIDDEN) {
416 /* Rebind the context to the window area and update matrices */
417 SDL_CurrentContext = NULL;
418 }
419
420 if (event->event == SDL_WINDOWEVENT_MINIMIZED) {
421 /* According to Apple documentation, we need to finish drawing NOW! */
422 data->glFinish();
423 }
424 }
425
426 static int
GLES_GetOutputSize(SDL_Renderer * renderer,int * w,int * h)427 GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
428 {
429 SDL_GL_GetDrawableSize(renderer->window, w, h);
430 return 0;
431 }
432
433 static SDL_INLINE int
power_of_2(int input)434 power_of_2(int input)
435 {
436 int value = 1;
437
438 while (value < input) {
439 value <<= 1;
440 }
441 return value;
442 }
443
444 static GLenum
GetScaleQuality(void)445 GetScaleQuality(void)
446 {
447 const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
448
449 if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
450 return GL_NEAREST;
451 } else {
452 return GL_LINEAR;
453 }
454 }
455
456 static int
GLES_CreateTexture(SDL_Renderer * renderer,SDL_Texture * texture)457 GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
458 {
459 GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
460 GLES_TextureData *data;
461 GLint internalFormat;
462 GLenum format, type;
463 int texture_w, texture_h;
464 GLenum scaleMode;
465 GLenum result;
466
467 GLES_ActivateRenderer(renderer);
468
469 switch (texture->format) {
470 case SDL_PIXELFORMAT_ABGR8888:
471 internalFormat = GL_RGBA;
472 format = GL_RGBA;
473 type = GL_UNSIGNED_BYTE;
474 break;
475 default:
476 return SDL_SetError("Texture format not supported");
477 }
478
479 data = (GLES_TextureData *) SDL_calloc(1, sizeof(*data));
480 if (!data) {
481 return SDL_OutOfMemory();
482 }
483
484 if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
485 data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
486 data->pixels = SDL_calloc(1, texture->h * data->pitch);
487 if (!data->pixels) {
488 SDL_free(data);
489 return SDL_OutOfMemory();
490 }
491 }
492
493
494 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
495 if (!renderdata->GL_OES_framebuffer_object_supported) {
496 SDL_free(data);
497 return SDL_SetError("GL_OES_framebuffer_object not supported");
498 }
499 data->fbo = GLES_GetFBO(renderer->driverdata, texture->w, texture->h);
500 } else {
501 data->fbo = NULL;
502 }
503
504
505 renderdata->glGetError();
506 renderdata->glEnable(GL_TEXTURE_2D);
507 renderdata->glGenTextures(1, &data->texture);
508 result = renderdata->glGetError();
509 if (result != GL_NO_ERROR) {
510 SDL_free(data);
511 return GLES_SetError("glGenTextures()", result);
512 }
513
514 data->type = GL_TEXTURE_2D;
515 /* no NPOV textures allowed in OpenGL ES (yet) */
516 texture_w = power_of_2(texture->w);
517 texture_h = power_of_2(texture->h);
518 data->texw = (GLfloat) texture->w / texture_w;
519 data->texh = (GLfloat) texture->h / texture_h;
520
521 data->format = format;
522 data->formattype = type;
523 scaleMode = GetScaleQuality();
524 renderdata->glBindTexture(data->type, data->texture);
525 renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
526 renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
527 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
528 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
529
530 renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
531 texture_h, 0, format, type, NULL);
532 renderdata->glDisable(GL_TEXTURE_2D);
533
534 result = renderdata->glGetError();
535 if (result != GL_NO_ERROR) {
536 SDL_free(data);
537 return GLES_SetError("glTexImage2D()", result);
538 }
539
540 texture->driverdata = data;
541 return 0;
542 }
543
544 static int
GLES_UpdateTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const void * pixels,int pitch)545 GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
546 const SDL_Rect * rect, const void *pixels, int pitch)
547 {
548 GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
549 GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
550 Uint8 *blob = NULL;
551 Uint8 *src;
552 int srcPitch;
553 int y;
554
555 GLES_ActivateRenderer(renderer);
556
557 /* Bail out if we're supposed to update an empty rectangle */
558 if (rect->w <= 0 || rect->h <= 0) {
559 return 0;
560 }
561
562 /* Reformat the texture data into a tightly packed array */
563 srcPitch = rect->w * SDL_BYTESPERPIXEL(texture->format);
564 src = (Uint8 *)pixels;
565 if (pitch != srcPitch) {
566 blob = (Uint8 *)SDL_malloc(srcPitch * rect->h);
567 if (!blob) {
568 return SDL_OutOfMemory();
569 }
570 src = blob;
571 for (y = 0; y < rect->h; ++y) {
572 SDL_memcpy(src, pixels, srcPitch);
573 src += srcPitch;
574 pixels = (Uint8 *)pixels + pitch;
575 }
576 src = blob;
577 }
578
579 /* Create a texture subimage with the supplied data */
580 renderdata->glGetError();
581 renderdata->glEnable(data->type);
582 renderdata->glBindTexture(data->type, data->texture);
583 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
584 renderdata->glTexSubImage2D(data->type,
585 0,
586 rect->x,
587 rect->y,
588 rect->w,
589 rect->h,
590 data->format,
591 data->formattype,
592 src);
593 renderdata->glDisable(data->type);
594 SDL_free(blob);
595
596 if (renderdata->glGetError() != GL_NO_ERROR) {
597 return SDL_SetError("Failed to update texture");
598 }
599 return 0;
600 }
601
602 static int
GLES_LockTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,void ** pixels,int * pitch)603 GLES_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
604 const SDL_Rect * rect, void **pixels, int *pitch)
605 {
606 GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
607
608 *pixels =
609 (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
610 rect->x * SDL_BYTESPERPIXEL(texture->format));
611 *pitch = data->pitch;
612 return 0;
613 }
614
615 static void
GLES_UnlockTexture(SDL_Renderer * renderer,SDL_Texture * texture)616 GLES_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
617 {
618 GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
619 SDL_Rect rect;
620
621 /* We do whole texture updates, at least for now */
622 rect.x = 0;
623 rect.y = 0;
624 rect.w = texture->w;
625 rect.h = texture->h;
626 GLES_UpdateTexture(renderer, texture, &rect, data->pixels, data->pitch);
627 }
628
629 static int
GLES_SetRenderTarget(SDL_Renderer * renderer,SDL_Texture * texture)630 GLES_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
631 {
632 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
633 GLES_TextureData *texturedata = NULL;
634 GLenum status;
635
636 GLES_ActivateRenderer(renderer);
637
638 if (!data->GL_OES_framebuffer_object_supported) {
639 return SDL_SetError("Can't enable render target support in this renderer");
640 }
641
642 if (texture == NULL) {
643 data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, data->window_framebuffer);
644 return 0;
645 }
646
647 texturedata = (GLES_TextureData *) texture->driverdata;
648 data->glBindFramebufferOES(GL_FRAMEBUFFER_OES, texturedata->fbo->FBO);
649 /* TODO: check if texture pixel format allows this operation */
650 data->glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, texturedata->type, texturedata->texture, 0);
651 /* Check FBO status */
652 status = data->glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
653 if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
654 return SDL_SetError("glFramebufferTexture2DOES() failed");
655 }
656 return 0;
657 }
658
659 static int
GLES_UpdateViewport(SDL_Renderer * renderer)660 GLES_UpdateViewport(SDL_Renderer * renderer)
661 {
662 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
663
664 if (SDL_CurrentContext != data->context) {
665 /* We'll update the viewport after we rebind the context */
666 return 0;
667 }
668
669 if (renderer->target) {
670 data->glViewport(renderer->viewport.x, renderer->viewport.y,
671 renderer->viewport.w, renderer->viewport.h);
672 } else {
673 int w, h;
674
675 SDL_GL_GetDrawableSize(renderer->window, &w, &h);
676 data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h),
677 renderer->viewport.w, renderer->viewport.h);
678 }
679
680 data->glMatrixMode(GL_PROJECTION);
681 data->glLoadIdentity();
682 if (renderer->viewport.w && renderer->viewport.h) {
683 if (renderer->target) {
684 data->glOrthof((GLfloat) 0,
685 (GLfloat) renderer->viewport.w,
686 (GLfloat) 0,
687 (GLfloat) renderer->viewport.h,
688 0.0, 1.0);
689 } else {
690 data->glOrthof((GLfloat) 0,
691 (GLfloat) renderer->viewport.w,
692 (GLfloat) renderer->viewport.h,
693 (GLfloat) 0,
694 0.0, 1.0);
695 }
696 }
697 return 0;
698 }
699
700 static int
GLES_UpdateClipRect(SDL_Renderer * renderer)701 GLES_UpdateClipRect(SDL_Renderer * renderer)
702 {
703 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
704
705 if (SDL_CurrentContext != data->context) {
706 /* We'll update the clip rect after we rebind the context */
707 return 0;
708 }
709
710 if (renderer->clipping_enabled) {
711 const SDL_Rect *rect = &renderer->clip_rect;
712 data->glEnable(GL_SCISSOR_TEST);
713 if (renderer->target) {
714 data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h);
715 } else {
716 int w, h;
717
718 SDL_GL_GetDrawableSize(renderer->window, &w, &h);
719 data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h);
720 }
721 } else {
722 data->glDisable(GL_SCISSOR_TEST);
723 }
724 return 0;
725 }
726
727 static void
GLES_SetColor(GLES_RenderData * data,Uint8 r,Uint8 g,Uint8 b,Uint8 a)728 GLES_SetColor(GLES_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
729 {
730 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
731
732 if (color != data->current.color) {
733 data->glColor4f((GLfloat) r * inv255f,
734 (GLfloat) g * inv255f,
735 (GLfloat) b * inv255f,
736 (GLfloat) a * inv255f);
737 data->current.color = color;
738 }
739 }
740
741 static void
GLES_SetBlendMode(GLES_RenderData * data,int blendMode)742 GLES_SetBlendMode(GLES_RenderData * data, int blendMode)
743 {
744 if (blendMode != data->current.blendMode) {
745 switch (blendMode) {
746 case SDL_BLENDMODE_NONE:
747 data->glDisable(GL_BLEND);
748 break;
749 case SDL_BLENDMODE_BLEND:
750 data->glEnable(GL_BLEND);
751 if (data->GL_OES_blend_func_separate_supported) {
752 data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
753 } else {
754 data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
755 }
756 break;
757 case SDL_BLENDMODE_ADD:
758 data->glEnable(GL_BLEND);
759 if (data->GL_OES_blend_func_separate_supported) {
760 data->glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
761 } else {
762 data->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
763 }
764 break;
765 case SDL_BLENDMODE_MOD:
766 data->glEnable(GL_BLEND);
767 if (data->GL_OES_blend_func_separate_supported) {
768 data->glBlendFuncSeparateOES(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
769 } else {
770 data->glBlendFunc(GL_ZERO, GL_SRC_COLOR);
771 }
772 break;
773 }
774 data->current.blendMode = blendMode;
775 }
776 }
777
778 static void
GLES_SetTexCoords(GLES_RenderData * data,SDL_bool enabled)779 GLES_SetTexCoords(GLES_RenderData * data, SDL_bool enabled)
780 {
781 if (enabled != data->current.tex_coords) {
782 if (enabled) {
783 data->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
784 } else {
785 data->glDisableClientState(GL_TEXTURE_COORD_ARRAY);
786 }
787 data->current.tex_coords = enabled;
788 }
789 }
790
791 static void
GLES_SetDrawingState(SDL_Renderer * renderer)792 GLES_SetDrawingState(SDL_Renderer * renderer)
793 {
794 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
795
796 GLES_ActivateRenderer(renderer);
797
798 GLES_SetColor(data, (GLfloat) renderer->r,
799 (GLfloat) renderer->g,
800 (GLfloat) renderer->b,
801 (GLfloat) renderer->a);
802
803 GLES_SetBlendMode(data, renderer->blendMode);
804
805 GLES_SetTexCoords(data, SDL_FALSE);
806 }
807
808 static int
GLES_RenderClear(SDL_Renderer * renderer)809 GLES_RenderClear(SDL_Renderer * renderer)
810 {
811 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
812
813 GLES_ActivateRenderer(renderer);
814
815 data->glClearColor((GLfloat) renderer->r * inv255f,
816 (GLfloat) renderer->g * inv255f,
817 (GLfloat) renderer->b * inv255f,
818 (GLfloat) renderer->a * inv255f);
819
820 if (renderer->clipping_enabled) {
821 data->glDisable(GL_SCISSOR_TEST);
822 }
823
824 data->glClear(GL_COLOR_BUFFER_BIT);
825
826 if (renderer->clipping_enabled) {
827 data->glEnable(GL_SCISSOR_TEST);
828 }
829
830 return 0;
831 }
832
833 static int
GLES_RenderDrawPoints(SDL_Renderer * renderer,const SDL_FPoint * points,int count)834 GLES_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
835 int count)
836 {
837 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
838 GLfloat *vertices;
839 int idx;
840
841 GLES_SetDrawingState(renderer);
842
843 /* Emit the specified vertices as points */
844 vertices = SDL_stack_alloc(GLfloat, count * 2);
845 for (idx = 0; idx < count; ++idx) {
846 GLfloat x = points[idx].x + 0.5f;
847 GLfloat y = points[idx].y + 0.5f;
848
849 vertices[idx * 2] = x;
850 vertices[(idx * 2) + 1] = y;
851 }
852
853 data->glVertexPointer(2, GL_FLOAT, 0, vertices);
854 data->glDrawArrays(GL_POINTS, 0, count);
855 SDL_stack_free(vertices);
856 return 0;
857 }
858
859 static int
GLES_RenderDrawLines(SDL_Renderer * renderer,const SDL_FPoint * points,int count)860 GLES_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
861 int count)
862 {
863 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
864 GLfloat *vertices;
865 int idx;
866
867 GLES_SetDrawingState(renderer);
868
869 /* Emit a line strip including the specified vertices */
870 vertices = SDL_stack_alloc(GLfloat, count * 2);
871 for (idx = 0; idx < count; ++idx) {
872 GLfloat x = points[idx].x + 0.5f;
873 GLfloat y = points[idx].y + 0.5f;
874
875 vertices[idx * 2] = x;
876 vertices[(idx * 2) + 1] = y;
877 }
878
879 data->glVertexPointer(2, GL_FLOAT, 0, vertices);
880 if (count > 2 &&
881 points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
882 /* GL_LINE_LOOP takes care of the final segment */
883 --count;
884 data->glDrawArrays(GL_LINE_LOOP, 0, count);
885 } else {
886 data->glDrawArrays(GL_LINE_STRIP, 0, count);
887 /* We need to close the endpoint of the line */
888 data->glDrawArrays(GL_POINTS, count-1, 1);
889 }
890 SDL_stack_free(vertices);
891
892 return 0;
893 }
894
895 static int
GLES_RenderFillRects(SDL_Renderer * renderer,const SDL_FRect * rects,int count)896 GLES_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects,
897 int count)
898 {
899 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
900 int i;
901
902 GLES_SetDrawingState(renderer);
903
904 for (i = 0; i < count; ++i) {
905 const SDL_FRect *rect = &rects[i];
906 GLfloat minx = rect->x;
907 GLfloat maxx = rect->x + rect->w;
908 GLfloat miny = rect->y;
909 GLfloat maxy = rect->y + rect->h;
910 GLfloat vertices[8];
911 vertices[0] = minx;
912 vertices[1] = miny;
913 vertices[2] = maxx;
914 vertices[3] = miny;
915 vertices[4] = minx;
916 vertices[5] = maxy;
917 vertices[6] = maxx;
918 vertices[7] = maxy;
919
920 data->glVertexPointer(2, GL_FLOAT, 0, vertices);
921 data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
922 }
923
924 return 0;
925 }
926
927 static int
GLES_RenderCopy(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect)928 GLES_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
929 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
930 {
931 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
932 GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
933 GLfloat minx, miny, maxx, maxy;
934 GLfloat minu, maxu, minv, maxv;
935 GLfloat vertices[8];
936 GLfloat texCoords[8];
937
938 GLES_ActivateRenderer(renderer);
939
940 data->glEnable(GL_TEXTURE_2D);
941
942 data->glBindTexture(texturedata->type, texturedata->texture);
943
944 if (texture->modMode) {
945 GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
946 } else {
947 GLES_SetColor(data, 255, 255, 255, 255);
948 }
949
950 GLES_SetBlendMode(data, texture->blendMode);
951
952 GLES_SetTexCoords(data, SDL_TRUE);
953
954 minx = dstrect->x;
955 miny = dstrect->y;
956 maxx = dstrect->x + dstrect->w;
957 maxy = dstrect->y + dstrect->h;
958
959 minu = (GLfloat) srcrect->x / texture->w;
960 minu *= texturedata->texw;
961 maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
962 maxu *= texturedata->texw;
963 minv = (GLfloat) srcrect->y / texture->h;
964 minv *= texturedata->texh;
965 maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
966 maxv *= texturedata->texh;
967
968 vertices[0] = minx;
969 vertices[1] = miny;
970 vertices[2] = maxx;
971 vertices[3] = miny;
972 vertices[4] = minx;
973 vertices[5] = maxy;
974 vertices[6] = maxx;
975 vertices[7] = maxy;
976
977 texCoords[0] = minu;
978 texCoords[1] = minv;
979 texCoords[2] = maxu;
980 texCoords[3] = minv;
981 texCoords[4] = minu;
982 texCoords[5] = maxv;
983 texCoords[6] = maxu;
984 texCoords[7] = maxv;
985
986 data->glVertexPointer(2, GL_FLOAT, 0, vertices);
987 data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
988 data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
989
990 data->glDisable(GL_TEXTURE_2D);
991
992 return 0;
993 }
994
995 static int
GLES_RenderCopyEx(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)996 GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
997 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
998 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
999 {
1000
1001 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1002 GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1003 GLfloat minx, miny, maxx, maxy;
1004 GLfloat minu, maxu, minv, maxv;
1005 GLfloat centerx, centery;
1006 GLfloat vertices[8];
1007 GLfloat texCoords[8];
1008
1009
1010 GLES_ActivateRenderer(renderer);
1011
1012 data->glEnable(GL_TEXTURE_2D);
1013
1014 data->glBindTexture(texturedata->type, texturedata->texture);
1015
1016 if (texture->modMode) {
1017 GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
1018 } else {
1019 GLES_SetColor(data, 255, 255, 255, 255);
1020 }
1021
1022 GLES_SetBlendMode(data, texture->blendMode);
1023
1024 GLES_SetTexCoords(data, SDL_TRUE);
1025
1026 centerx = center->x;
1027 centery = center->y;
1028
1029 /* Rotate and translate */
1030 data->glPushMatrix();
1031 data->glTranslatef(dstrect->x + centerx, dstrect->y + centery, 0.0f);
1032 data->glRotatef((GLfloat)angle, 0.0f, 0.0f, 1.0f);
1033
1034 if (flip & SDL_FLIP_HORIZONTAL) {
1035 minx = dstrect->w - centerx;
1036 maxx = -centerx;
1037 } else {
1038 minx = -centerx;
1039 maxx = dstrect->w - centerx;
1040 }
1041
1042 if (flip & SDL_FLIP_VERTICAL) {
1043 miny = dstrect->h - centery;
1044 maxy = -centery;
1045 } else {
1046 miny = -centery;
1047 maxy = dstrect->h - centery;
1048 }
1049
1050 minu = (GLfloat) srcrect->x / texture->w;
1051 minu *= texturedata->texw;
1052 maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
1053 maxu *= texturedata->texw;
1054 minv = (GLfloat) srcrect->y / texture->h;
1055 minv *= texturedata->texh;
1056 maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
1057 maxv *= texturedata->texh;
1058
1059 vertices[0] = minx;
1060 vertices[1] = miny;
1061 vertices[2] = maxx;
1062 vertices[3] = miny;
1063 vertices[4] = minx;
1064 vertices[5] = maxy;
1065 vertices[6] = maxx;
1066 vertices[7] = maxy;
1067
1068 texCoords[0] = minu;
1069 texCoords[1] = minv;
1070 texCoords[2] = maxu;
1071 texCoords[3] = minv;
1072 texCoords[4] = minu;
1073 texCoords[5] = maxv;
1074 texCoords[6] = maxu;
1075 texCoords[7] = maxv;
1076 data->glVertexPointer(2, GL_FLOAT, 0, vertices);
1077 data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
1078 data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1079 data->glPopMatrix();
1080 data->glDisable(GL_TEXTURE_2D);
1081
1082 return 0;
1083 }
1084
1085 static int
GLES_RenderReadPixels(SDL_Renderer * renderer,const SDL_Rect * rect,Uint32 pixel_format,void * pixels,int pitch)1086 GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
1087 Uint32 pixel_format, void * pixels, int pitch)
1088 {
1089 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1090 Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888;
1091 void *temp_pixels;
1092 int temp_pitch;
1093 Uint8 *src, *dst, *tmp;
1094 int w, h, length, rows;
1095 int status;
1096
1097 GLES_ActivateRenderer(renderer);
1098
1099 temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
1100 temp_pixels = SDL_malloc(rect->h * temp_pitch);
1101 if (!temp_pixels) {
1102 return SDL_OutOfMemory();
1103 }
1104
1105 SDL_GetRendererOutputSize(renderer, &w, &h);
1106
1107 data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
1108
1109 data->glReadPixels(rect->x, renderer->target ? rect->y : (h-rect->y)-rect->h,
1110 rect->w, rect->h, GL_RGBA, GL_UNSIGNED_BYTE, temp_pixels);
1111
1112 /* Flip the rows to be top-down if necessary */
1113 if (!renderer->target) {
1114 length = rect->w * SDL_BYTESPERPIXEL(temp_format);
1115 src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
1116 dst = (Uint8*)temp_pixels;
1117 tmp = SDL_stack_alloc(Uint8, length);
1118 rows = rect->h / 2;
1119 while (rows--) {
1120 SDL_memcpy(tmp, dst, length);
1121 SDL_memcpy(dst, src, length);
1122 SDL_memcpy(src, tmp, length);
1123 dst += temp_pitch;
1124 src -= temp_pitch;
1125 }
1126 SDL_stack_free(tmp);
1127 }
1128
1129 status = SDL_ConvertPixels(rect->w, rect->h,
1130 temp_format, temp_pixels, temp_pitch,
1131 pixel_format, pixels, pitch);
1132 SDL_free(temp_pixels);
1133
1134 return status;
1135 }
1136
1137 static void
GLES_RenderPresent(SDL_Renderer * renderer)1138 GLES_RenderPresent(SDL_Renderer * renderer)
1139 {
1140 GLES_ActivateRenderer(renderer);
1141
1142 SDL_GL_SwapWindow(renderer->window);
1143 }
1144
1145 static void
GLES_DestroyTexture(SDL_Renderer * renderer,SDL_Texture * texture)1146 GLES_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
1147 {
1148 GLES_RenderData *renderdata = (GLES_RenderData *) renderer->driverdata;
1149
1150 GLES_TextureData *data = (GLES_TextureData *) texture->driverdata;
1151
1152 GLES_ActivateRenderer(renderer);
1153
1154 if (!data) {
1155 return;
1156 }
1157 if (data->texture) {
1158 renderdata->glDeleteTextures(1, &data->texture);
1159 }
1160 SDL_free(data->pixels);
1161 SDL_free(data);
1162 texture->driverdata = NULL;
1163 }
1164
1165 static void
GLES_DestroyRenderer(SDL_Renderer * renderer)1166 GLES_DestroyRenderer(SDL_Renderer * renderer)
1167 {
1168 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1169
1170 if (data) {
1171 if (data->context) {
1172 while (data->framebuffers) {
1173 GLES_FBOList *nextnode = data->framebuffers->next;
1174 data->glDeleteFramebuffersOES(1, &data->framebuffers->FBO);
1175 SDL_free(data->framebuffers);
1176 data->framebuffers = nextnode;
1177 }
1178 SDL_GL_DeleteContext(data->context);
1179 }
1180 SDL_free(data);
1181 }
1182 SDL_free(renderer);
1183 }
1184
GLES_BindTexture(SDL_Renderer * renderer,SDL_Texture * texture,float * texw,float * texh)1185 static int GLES_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh)
1186 {
1187 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1188 GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1189 GLES_ActivateRenderer(renderer);
1190
1191 data->glEnable(GL_TEXTURE_2D);
1192 data->glBindTexture(texturedata->type, texturedata->texture);
1193
1194 if (texw) {
1195 *texw = (float)texturedata->texw;
1196 }
1197 if (texh) {
1198 *texh = (float)texturedata->texh;
1199 }
1200
1201 return 0;
1202 }
1203
GLES_UnbindTexture(SDL_Renderer * renderer,SDL_Texture * texture)1204 static int GLES_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
1205 {
1206 GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
1207 GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
1208 GLES_ActivateRenderer(renderer);
1209 data->glDisable(texturedata->type);
1210
1211 return 0;
1212 }
1213
1214 #endif /* SDL_VIDEO_RENDER_OGL_ES && !SDL_RENDER_DISABLED */
1215
1216 /* vi: set ts=4 sw=4 expandtab: */
1217