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