1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_OGL_ES2
24
25#include "../../video/SDL_sysvideo.h" // For SDL_RecreateWindow
26#include <SDL3/SDL_opengles2.h>
27#include "../SDL_sysrender.h"
28#include "../../video/SDL_pixels_c.h"
29#include "SDL_shaders_gles2.h"
30
31/* WebGL doesn't offer client-side arrays, so use Vertex Buffer Objects
32 on Emscripten, which converts GLES2 into WebGL calls.
33 In all other cases, attempt to use client-side arrays, as they tend to
34 be dramatically faster when not batching, and about the same when
35 we are. */
36#ifdef SDL_PLATFORM_EMSCRIPTEN
37#define USE_VERTEX_BUFFER_OBJECTS 1
38#else
39#define USE_VERTEX_BUFFER_OBJECTS 0
40#endif
41
42/* To prevent unnecessary window recreation,
43 * these should match the defaults selected in SDL_GL_ResetAttributes
44 */
45#define RENDERER_CONTEXT_MAJOR 2
46#define RENDERER_CONTEXT_MINOR 0
47
48/*************************************************************************************************
49 * Context structures *
50 *************************************************************************************************/
51
52typedef struct GLES2_FBOList GLES2_FBOList;
53
54struct GLES2_FBOList
55{
56 Uint32 w, h;
57 GLuint FBO;
58 GLES2_FBOList *next;
59};
60
61typedef struct GLES2_TextureData
62{
63 GLuint texture;
64 bool texture_external;
65 GLenum texture_type;
66 GLenum pixel_format;
67 GLenum pixel_type;
68 void *pixel_data;
69 int pitch;
70#ifdef SDL_HAVE_YUV
71 // YUV texture support
72 bool yuv;
73 bool nv12;
74 GLuint texture_v;
75 GLuint texture_v_external;
76 GLuint texture_u;
77 GLuint texture_u_external;
78#endif
79 GLfloat texel_size[4];
80 SDL_ScaleMode texture_scale_mode;
81 SDL_TextureAddressMode texture_address_mode;
82 GLES2_FBOList *fbo;
83} GLES2_TextureData;
84
85typedef enum
86{
87 GLES2_ATTRIBUTE_POSITION = 0,
88 GLES2_ATTRIBUTE_COLOR = 1,
89 GLES2_ATTRIBUTE_TEXCOORD = 2,
90} GLES2_Attribute;
91
92typedef enum
93{
94 GLES2_UNIFORM_PROJECTION,
95 GLES2_UNIFORM_TEXTURE,
96 GLES2_UNIFORM_TEXTURE_U,
97 GLES2_UNIFORM_TEXTURE_V,
98 GLES2_UNIFORM_TEXEL_SIZE,
99 GLES2_UNIFORM_OFFSET,
100 GLES2_UNIFORM_MATRIX,
101 NUM_GLES2_UNIFORMS
102} GLES2_Uniform;
103
104static const char *GLES2_UniformNames[] = {
105 "u_projection",
106 "u_texture",
107 "u_texture_u",
108 "u_texture_v",
109 "u_texel_size",
110 "u_offset",
111 "u_matrix"
112};
113SDL_COMPILE_TIME_ASSERT(GLES2_UniformNames, SDL_arraysize(GLES2_UniformNames) == NUM_GLES2_UNIFORMS);
114
115typedef struct GLES2_ProgramCacheEntry
116{
117 GLuint id;
118 GLuint vertex_shader;
119 GLuint fragment_shader;
120 GLuint uniform_locations[NUM_GLES2_UNIFORMS];
121 GLfloat projection[4][4];
122 const float *shader_params;
123 struct GLES2_ProgramCacheEntry *prev;
124 struct GLES2_ProgramCacheEntry *next;
125} GLES2_ProgramCacheEntry;
126
127typedef struct GLES2_ProgramCache
128{
129 int count;
130 GLES2_ProgramCacheEntry *head;
131 GLES2_ProgramCacheEntry *tail;
132} GLES2_ProgramCache;
133
134typedef enum
135{
136 GLES2_IMAGESOURCE_INVALID,
137 GLES2_IMAGESOURCE_SOLID,
138 GLES2_IMAGESOURCE_TEXTURE_ABGR,
139 GLES2_IMAGESOURCE_TEXTURE_ARGB,
140 GLES2_IMAGESOURCE_TEXTURE_RGB,
141 GLES2_IMAGESOURCE_TEXTURE_BGR,
142 GLES2_IMAGESOURCE_TEXTURE_YUV,
143 GLES2_IMAGESOURCE_TEXTURE_NV12,
144 GLES2_IMAGESOURCE_TEXTURE_NV21,
145 GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES
146} GLES2_ImageSource;
147
148typedef struct
149{
150 SDL_Rect viewport;
151 bool viewport_dirty;
152 SDL_Texture *texture;
153 SDL_Texture *target;
154 SDL_BlendMode blend;
155 bool cliprect_enabled_dirty;
156 bool cliprect_enabled;
157 bool cliprect_dirty;
158 SDL_Rect cliprect;
159 bool texturing;
160 bool texturing_dirty;
161 SDL_FColor clear_color;
162 bool clear_color_dirty;
163 int drawablew;
164 int drawableh;
165 GLES2_ProgramCacheEntry *program;
166 const float *shader_params;
167 GLfloat projection[4][4];
168} GLES2_DrawStateCache;
169
170typedef struct GLES2_RenderData
171{
172 SDL_GLContext context;
173
174 bool debug_enabled;
175
176 bool GL_EXT_blend_minmax_supported;
177
178#define SDL_PROC(ret, func, params) ret (APIENTRY *func) params;
179#include "SDL_gles2funcs.h"
180#undef SDL_PROC
181 GLES2_FBOList *framebuffers;
182 GLuint window_framebuffer;
183
184 GLuint shader_id_cache[GLES2_SHADER_COUNT];
185
186 GLES2_ProgramCache program_cache;
187 Uint8 clear_r, clear_g, clear_b, clear_a;
188
189#if USE_VERTEX_BUFFER_OBJECTS
190 GLuint vertex_buffers[8];
191 size_t vertex_buffer_size[8];
192 int current_vertex_buffer;
193#endif
194
195 GLES2_DrawStateCache drawstate;
196 GLES2_ShaderIncludeType texcoord_precision_hint;
197} GLES2_RenderData;
198
199#define GLES2_MAX_CACHED_PROGRAMS 8
200
201static const char *GL_TranslateError(GLenum error)
202{
203#define GL_ERROR_TRANSLATE(e) \
204 case e: \
205 return #e;
206 switch (error) {
207 GL_ERROR_TRANSLATE(GL_INVALID_ENUM)
208 GL_ERROR_TRANSLATE(GL_INVALID_VALUE)
209 GL_ERROR_TRANSLATE(GL_INVALID_OPERATION)
210 GL_ERROR_TRANSLATE(GL_OUT_OF_MEMORY)
211 GL_ERROR_TRANSLATE(GL_NO_ERROR)
212 default:
213 return "UNKNOWN";
214 }
215#undef GL_ERROR_TRANSLATE
216}
217
218static void GL_ClearErrors(SDL_Renderer *renderer)
219{
220 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
221
222 if (!data->debug_enabled) {
223 return;
224 }
225 while (data->glGetError() != GL_NO_ERROR) {
226 // continue;
227 }
228}
229
230static bool GL_CheckAllErrors(const char *prefix, SDL_Renderer *renderer, const char *file, int line, const char *function)
231{
232 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
233 bool result = true;
234
235 if (!data->debug_enabled) {
236 return true;
237 }
238 // check gl errors (can return multiple errors)
239 for (;;) {
240 GLenum error = data->glGetError();
241 if (error != GL_NO_ERROR) {
242 if (!prefix || prefix[0] == '\0') {
243 prefix = "generic";
244 }
245 SDL_SetError("%s: %s (%d): %s %s (0x%X)", prefix, file, line, function, GL_TranslateError(error), error);
246 result = false;
247 } else {
248 break;
249 }
250 }
251 return result;
252}
253
254#if 0
255#define GL_CheckError(prefix, renderer)
256#else
257#define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, SDL_FILE, SDL_LINE, SDL_FUNCTION)
258#endif
259
260/*************************************************************************************************
261 * Renderer state APIs *
262 *************************************************************************************************/
263
264static bool GLES2_LoadFunctions(GLES2_RenderData *data)
265{
266#ifdef SDL_VIDEO_DRIVER_UIKIT
267#define __SDL_NOGETPROCADDR__
268#elif defined(SDL_VIDEO_DRIVER_ANDROID)
269#define __SDL_NOGETPROCADDR__
270#endif
271
272#if defined __SDL_NOGETPROCADDR__
273#define SDL_PROC(ret, func, params) data->func = func;
274#else
275#define SDL_PROC(ret, func, params) \
276 do { \
277 data->func = (ret (APIENTRY *) params)SDL_GL_GetProcAddress(#func); \
278 if (!data->func) { \
279 return SDL_SetError("Couldn't load GLES2 function %s: %s", #func, SDL_GetError()); \
280 } \
281 } while (0);
282#endif // __SDL_NOGETPROCADDR__
283
284#include "SDL_gles2funcs.h"
285#undef SDL_PROC
286 return true;
287}
288
289static GLES2_FBOList *GLES2_GetFBO(GLES2_RenderData *data, Uint32 w, Uint32 h)
290{
291 GLES2_FBOList *result = data->framebuffers;
292 while ((result) && ((result->w != w) || (result->h != h))) {
293 result = result->next;
294 }
295 if (!result) {
296 result = (GLES2_FBOList *)SDL_malloc(sizeof(GLES2_FBOList));
297 result->w = w;
298 result->h = h;
299 data->glGenFramebuffers(1, &result->FBO);
300 result->next = data->framebuffers;
301 data->framebuffers = result;
302 }
303 return result;
304}
305
306static bool GLES2_ActivateRenderer(SDL_Renderer *renderer)
307{
308 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
309
310 if (SDL_GL_GetCurrentContext() != data->context) {
311 // Null out the current program to ensure we set it again
312 data->drawstate.program = NULL;
313
314 if (!SDL_GL_MakeCurrent(renderer->window, data->context)) {
315 return false;
316 }
317 }
318
319 GL_ClearErrors(renderer);
320
321 return true;
322}
323
324static void GLES2_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
325{
326 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
327
328 if (event->type == SDL_EVENT_WINDOW_MINIMIZED) {
329 // According to Apple documentation, we need to finish drawing NOW!
330 data->glFinish();
331 }
332}
333
334static GLenum GetBlendFunc(SDL_BlendFactor factor)
335{
336 switch (factor) {
337 case SDL_BLENDFACTOR_ZERO:
338 return GL_ZERO;
339 case SDL_BLENDFACTOR_ONE:
340 return GL_ONE;
341 case SDL_BLENDFACTOR_SRC_COLOR:
342 return GL_SRC_COLOR;
343 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
344 return GL_ONE_MINUS_SRC_COLOR;
345 case SDL_BLENDFACTOR_SRC_ALPHA:
346 return GL_SRC_ALPHA;
347 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
348 return GL_ONE_MINUS_SRC_ALPHA;
349 case SDL_BLENDFACTOR_DST_COLOR:
350 return GL_DST_COLOR;
351 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
352 return GL_ONE_MINUS_DST_COLOR;
353 case SDL_BLENDFACTOR_DST_ALPHA:
354 return GL_DST_ALPHA;
355 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
356 return GL_ONE_MINUS_DST_ALPHA;
357 default:
358 return GL_INVALID_ENUM;
359 }
360}
361
362static GLenum GetBlendEquation(SDL_BlendOperation operation)
363{
364 switch (operation) {
365 case SDL_BLENDOPERATION_ADD:
366 return GL_FUNC_ADD;
367 case SDL_BLENDOPERATION_SUBTRACT:
368 return GL_FUNC_SUBTRACT;
369 case SDL_BLENDOPERATION_REV_SUBTRACT:
370 return GL_FUNC_REVERSE_SUBTRACT;
371 case SDL_BLENDOPERATION_MINIMUM:
372 return GL_MIN_EXT;
373 case SDL_BLENDOPERATION_MAXIMUM:
374 return GL_MAX_EXT;
375 default:
376 return GL_INVALID_ENUM;
377 }
378}
379
380static bool GLES2_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
381{
382 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
383
384 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
385 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
386 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
387 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
388 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
389 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
390
391 if (GetBlendFunc(srcColorFactor) == GL_INVALID_ENUM ||
392 GetBlendFunc(srcAlphaFactor) == GL_INVALID_ENUM ||
393 GetBlendEquation(colorOperation) == GL_INVALID_ENUM ||
394 GetBlendFunc(dstColorFactor) == GL_INVALID_ENUM ||
395 GetBlendFunc(dstAlphaFactor) == GL_INVALID_ENUM ||
396 GetBlendEquation(alphaOperation) == GL_INVALID_ENUM) {
397 return false;
398 }
399
400 if (colorOperation == SDL_BLENDOPERATION_MINIMUM && !data->GL_EXT_blend_minmax_supported) {
401 return false;
402 }
403 if (colorOperation == SDL_BLENDOPERATION_MAXIMUM && !data->GL_EXT_blend_minmax_supported) {
404 return false;
405 }
406
407 return true;
408}
409
410static GLES2_ProgramCacheEntry *GLES2_CacheProgram(GLES2_RenderData *data, GLuint vertex, GLuint fragment)
411{
412 GLES2_ProgramCacheEntry *entry;
413 GLint linkSuccessful;
414 int i;
415
416 // Check if we've already cached this program
417 entry = data->program_cache.head;
418 while (entry) {
419 if (entry->vertex_shader == vertex && entry->fragment_shader == fragment) {
420 break;
421 }
422 entry = entry->next;
423 }
424 if (entry) {
425 if (data->program_cache.head != entry) {
426 if (entry->next) {
427 entry->next->prev = entry->prev;
428 }
429 if (entry->prev) {
430 entry->prev->next = entry->next;
431 }
432 entry->prev = NULL;
433 entry->next = data->program_cache.head;
434 data->program_cache.head->prev = entry;
435 data->program_cache.head = entry;
436 }
437 return entry;
438 }
439
440 // Create a program cache entry
441 entry = (GLES2_ProgramCacheEntry *)SDL_calloc(1, sizeof(GLES2_ProgramCacheEntry));
442 if (!entry) {
443 return NULL;
444 }
445 entry->vertex_shader = vertex;
446 entry->fragment_shader = fragment;
447
448 // Create the program and link it
449 entry->id = data->glCreateProgram();
450 data->glAttachShader(entry->id, vertex);
451 data->glAttachShader(entry->id, fragment);
452 data->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_POSITION, "a_position");
453 data->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_COLOR, "a_color");
454 data->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_TEXCOORD, "a_texCoord");
455 data->glLinkProgram(entry->id);
456 data->glGetProgramiv(entry->id, GL_LINK_STATUS, &linkSuccessful);
457 if (!linkSuccessful) {
458 data->glDeleteProgram(entry->id);
459 SDL_free(entry);
460 SDL_SetError("Failed to link shader program");
461 return NULL;
462 }
463
464 // Predetermine locations of uniform variables
465 for (i = 0; i < NUM_GLES2_UNIFORMS; ++i) {
466 entry->uniform_locations[i] = data->glGetUniformLocation(entry->id, GLES2_UniformNames[i]);
467 }
468
469 data->glUseProgram(entry->id);
470 if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V] != -1) {
471 data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V], 2); // always texture unit 2.
472 }
473 if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U] != -1) {
474 data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U], 1); // always texture unit 1.
475 }
476 if (entry->uniform_locations[GLES2_UNIFORM_TEXTURE] != -1) {
477 data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0); // always texture unit 0.
478 }
479 if (entry->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) {
480 data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection);
481 }
482
483 // Cache the linked program
484 if (data->program_cache.head) {
485 entry->next = data->program_cache.head;
486 data->program_cache.head->prev = entry;
487 } else {
488 data->program_cache.tail = entry;
489 }
490 data->program_cache.head = entry;
491 ++data->program_cache.count;
492
493 // Evict the last entry from the cache if we exceed the limit
494 if (data->program_cache.count > GLES2_MAX_CACHED_PROGRAMS) {
495 data->glDeleteProgram(data->program_cache.tail->id);
496 data->program_cache.tail = data->program_cache.tail->prev;
497 if (data->program_cache.tail) {
498 SDL_free(data->program_cache.tail->next);
499 data->program_cache.tail->next = NULL;
500 }
501 --data->program_cache.count;
502 }
503 return entry;
504}
505
506static bool GLES2_CacheShader(GLES2_RenderData *data, GLES2_ShaderType type, GLenum shader_type)
507{
508 GLuint id = 0;
509 GLint compileSuccessful = GL_FALSE;
510 int attempt, num_src;
511 const GLchar *shader_src_list[3];
512 const GLchar *shader_body = GLES2_GetShader(type);
513
514 if (!shader_body) {
515 return SDL_SetError("No shader body src");
516 }
517
518 for (attempt = 0; attempt < 2 && !compileSuccessful; ++attempt) {
519 num_src = 0;
520
521 shader_src_list[num_src++] = GLES2_GetShaderPrologue(type);
522
523 if (shader_type == GL_FRAGMENT_SHADER) {
524 if (attempt == 0) {
525 shader_src_list[num_src++] = GLES2_GetShaderInclude(data->texcoord_precision_hint);
526 } else {
527 shader_src_list[num_src++] = GLES2_GetShaderInclude(GLES2_SHADER_FRAGMENT_INCLUDE_UNDEF_PRECISION);
528 }
529 }
530
531 shader_src_list[num_src++] = shader_body;
532
533 SDL_assert(num_src <= SDL_arraysize(shader_src_list));
534
535#ifdef DEBUG_PRINT_SHADERS
536 {
537 int i;
538 char *message = NULL;
539
540 SDL_asprintf(&message, "Compiling shader:\n");
541 for (i = 0; i < num_src; ++i) {
542 char *last_message = message;
543 SDL_asprintf(&message, "%s%s", last_message, shader_src_list[i]);
544 SDL_free(last_message);
545 }
546 SDL_Log("%s", message);
547 SDL_free(message);
548 }
549#endif
550
551 // Compile
552 id = data->glCreateShader(shader_type);
553 data->glShaderSource(id, num_src, shader_src_list, NULL);
554 data->glCompileShader(id);
555 data->glGetShaderiv(id, GL_COMPILE_STATUS, &compileSuccessful);
556 }
557
558 if (!compileSuccessful) {
559 char *info = NULL;
560 int length = 0;
561
562 data->glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
563 if (length > 0) {
564 info = (char *)SDL_malloc(length);
565 if (info) {
566 data->glGetShaderInfoLog(id, length, &length, info);
567 }
568 }
569 if (info) {
570 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to load the shader %d: %s", type, info);
571 SDL_free(info);
572 } else {
573 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to load the shader %d", type);
574
575 }
576 data->glDeleteShader(id);
577
578 return SDL_SetError("Failed to load the shader %d", type);
579 }
580
581 // Cache
582 data->shader_id_cache[(Uint32)type] = id;
583
584 return true;
585}
586
587static bool GLES2_CacheShaders(GLES2_RenderData *data)
588{
589 int shader;
590
591 data->texcoord_precision_hint = GLES2_GetTexCoordPrecisionEnumFromHint();
592
593 for (shader = 0; shader < GLES2_SHADER_FRAGMENT_TEXTURE_EXTERNAL_OES; ++shader) {
594 GLenum shader_type;
595
596 if (shader == GLES2_SHADER_VERTEX_DEFAULT) {
597 shader_type = GL_VERTEX_SHADER;
598 } else {
599 shader_type = GL_FRAGMENT_SHADER;
600 }
601 if (!GLES2_CacheShader(data, (GLES2_ShaderType)shader, shader_type)) {
602 return false;
603 }
604 }
605 return true;
606}
607
608static bool GLES2_SelectProgram(GLES2_RenderData *data, SDL_Texture *texture, GLES2_ImageSource source, SDL_ScaleMode scale_mode, SDL_Colorspace colorspace)
609{
610 GLuint vertex;
611 GLuint fragment;
612 GLES2_ShaderType vtype, ftype;
613 GLES2_ProgramCacheEntry *program;
614 GLES2_TextureData *tdata = texture ? (GLES2_TextureData *)texture->internal : NULL;
615 const float *shader_params = NULL;
616
617 // Select an appropriate shader pair for the specified modes
618 vtype = GLES2_SHADER_VERTEX_DEFAULT;
619 switch (source) {
620 case GLES2_IMAGESOURCE_SOLID:
621 ftype = GLES2_SHADER_FRAGMENT_SOLID;
622 break;
623 case GLES2_IMAGESOURCE_TEXTURE_ABGR:
624 if (scale_mode == SDL_SCALEMODE_PIXELART) {
625 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_ABGR_PIXELART;
626 shader_params = tdata->texel_size;
627 } else {
628 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_ABGR;
629 }
630 break;
631 case GLES2_IMAGESOURCE_TEXTURE_ARGB:
632 if (scale_mode == SDL_SCALEMODE_PIXELART) {
633 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_ARGB_PIXELART;
634 shader_params = tdata->texel_size;
635 } else {
636 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_ARGB;
637 }
638 break;
639 case GLES2_IMAGESOURCE_TEXTURE_RGB:
640 if (scale_mode == SDL_SCALEMODE_PIXELART) {
641 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_RGB_PIXELART;
642 shader_params = tdata->texel_size;
643 } else {
644 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_RGB;
645 }
646 break;
647 case GLES2_IMAGESOURCE_TEXTURE_BGR:
648 if (scale_mode == SDL_SCALEMODE_PIXELART) {
649 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_BGR_PIXELART;
650 shader_params = tdata->texel_size;
651 } else {
652 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_BGR;
653 }
654 break;
655#ifdef SDL_HAVE_YUV
656 case GLES2_IMAGESOURCE_TEXTURE_YUV:
657 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV;
658 shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8);
659 if (!shader_params) {
660 SDL_SetError("Unsupported YUV colorspace");
661 goto fault;
662 }
663 break;
664 case GLES2_IMAGESOURCE_TEXTURE_NV12:
665 if (SDL_GetHintBoolean("SDL_RENDER_OPENGL_NV12_RG_SHADER", false)) {
666 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RG;
667 } else {
668 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RA;
669 }
670 shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8);
671 if (!shader_params) {
672 SDL_SetError("Unsupported YUV colorspace");
673 goto fault;
674 }
675 break;
676 case GLES2_IMAGESOURCE_TEXTURE_NV21:
677 if (SDL_GetHintBoolean("SDL_RENDER_OPENGL_NV12_RG_SHADER", false)) {
678 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_RG;
679 } else {
680 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_RA;
681 }
682 shader_params = SDL_GetYCbCRtoRGBConversionMatrix(colorspace, 0, 0, 8);
683 if (!shader_params) {
684 SDL_SetError("Unsupported YUV colorspace");
685 goto fault;
686 }
687 break;
688#endif // SDL_HAVE_YUV
689 case GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES:
690 ftype = GLES2_SHADER_FRAGMENT_TEXTURE_EXTERNAL_OES;
691 break;
692 default:
693 goto fault;
694 }
695
696 // Load the requested shaders
697 vertex = data->shader_id_cache[(Uint32)vtype];
698 if (!vertex) {
699 vertex = GLES2_CacheShader(data, vtype, GL_VERTEX_SHADER);
700 if (!vertex) {
701 goto fault;
702 }
703 }
704
705 fragment = data->shader_id_cache[(Uint32)ftype];
706 if (!fragment) {
707 fragment = GLES2_CacheShader(data, ftype, GL_FRAGMENT_SHADER);
708 if (!fragment) {
709 goto fault;
710 }
711 }
712
713 // Check if we need to change programs at all
714 if (data->drawstate.program &&
715 data->drawstate.program->vertex_shader == vertex &&
716 data->drawstate.program->fragment_shader == fragment &&
717 data->drawstate.program->shader_params == shader_params) {
718 return true;
719 }
720
721 // Generate a matching program
722 program = GLES2_CacheProgram(data, vertex, fragment);
723 if (!program) {
724 goto fault;
725 }
726
727 // Select that program in OpenGL
728 data->glUseProgram(program->id);
729
730 if (shader_params && shader_params != program->shader_params) {
731#ifdef SDL_HAVE_YUV
732 if (ftype >= GLES2_SHADER_FRAGMENT_TEXTURE_YUV) {
733 // YUV shader params are Yoffset, 0, Rcoeff, 0, Gcoeff, 0, Bcoeff, 0
734 if (program->uniform_locations[GLES2_UNIFORM_OFFSET] != -1) {
735 data->glUniform3f(program->uniform_locations[GLES2_UNIFORM_OFFSET], shader_params[0], shader_params[1], shader_params[2]);
736 }
737 if (program->uniform_locations[GLES2_UNIFORM_MATRIX] != -1) {
738 GLfloat matrix[3 * 3];
739
740 matrix[0 * 3 + 0] = shader_params[4];
741 matrix[0 * 3 + 1] = shader_params[5];
742 matrix[0 * 3 + 2] = shader_params[6];
743 matrix[1 * 3 + 0] = shader_params[8];
744 matrix[1 * 3 + 1] = shader_params[9];
745 matrix[1 * 3 + 2] = shader_params[10];
746 matrix[2 * 3 + 0] = shader_params[12];
747 matrix[2 * 3 + 1] = shader_params[13];
748 matrix[2 * 3 + 2] = shader_params[14];
749 data->glUniformMatrix3fv(program->uniform_locations[GLES2_UNIFORM_MATRIX], 1, GL_FALSE, matrix);
750 }
751 }
752 else
753#endif
754 if (ftype >= GLES2_SHADER_FRAGMENT_TEXTURE_ABGR_PIXELART) {
755 data->glUniform4f(program->uniform_locations[GLES2_UNIFORM_TEXEL_SIZE], shader_params[0], shader_params[1], shader_params[2], shader_params[3]);
756 }
757 program->shader_params = shader_params;
758 }
759
760 // Set the current program
761 data->drawstate.program = program;
762
763 // Clean up and return
764 return true;
765fault:
766 data->drawstate.program = NULL;
767 return false;
768}
769
770static bool GLES2_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
771{
772 return true; // nothing to do in this backend.
773}
774
775static bool GLES2_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
776{
777 const bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_BGRA32 || renderer->target->format == SDL_PIXELFORMAT_BGRX32));
778 SDL_VertexSolid *verts = (SDL_VertexSolid *)SDL_AllocateRenderVertices(renderer, count * sizeof(*verts), 0, &cmd->data.draw.first);
779 int i;
780 SDL_FColor color = cmd->data.draw.color;
781 const float color_scale = cmd->data.draw.color_scale;
782
783 if (!verts) {
784 return false;
785 }
786
787 color.r *= color_scale;
788 color.g *= color_scale;
789 color.b *= color_scale;
790
791 if (colorswap) {
792 float r = color.r;
793 color.r = color.b;
794 color.b = r;
795 }
796
797 cmd->data.draw.count = count;
798 for (i = 0; i < count; i++) {
799 verts->position.x = 0.5f + points[i].x;
800 verts->position.y = 0.5f + points[i].y;
801 verts->color = color;
802 verts++;
803 }
804
805 return true;
806}
807
808static bool GLES2_QueueDrawLines(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
809{
810 const bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_BGRA32 || renderer->target->format == SDL_PIXELFORMAT_BGRX32));
811 int i;
812 GLfloat prevx, prevy;
813 SDL_VertexSolid *verts = (SDL_VertexSolid *)SDL_AllocateRenderVertices(renderer, count * sizeof(*verts), 0, &cmd->data.draw.first);
814 SDL_FColor color = cmd->data.draw.color;
815 const float color_scale = cmd->data.draw.color_scale;
816
817 if (!verts) {
818 return false;
819 }
820
821 color.r *= color_scale;
822 color.g *= color_scale;
823 color.b *= color_scale;
824
825 if (colorswap) {
826 float r = color.r;
827 color.r = color.b;
828 color.b = r;
829 }
830
831 cmd->data.draw.count = count;
832
833 // 0.5f offset to hit the center of the pixel.
834 prevx = 0.5f + points->x;
835 prevy = 0.5f + points->y;
836 verts->position.x = prevx;
837 verts->position.y = prevy;
838 verts->color = color;
839 verts++;
840
841 /* bump the end of each line segment out a quarter of a pixel, to provoke
842 the diamond-exit rule. Without this, you won't just drop the last
843 pixel of the last line segment, but you might also drop pixels at the
844 edge of any given line segment along the way too. */
845 for (i = 1; i < count; i++) {
846 const GLfloat xstart = prevx;
847 const GLfloat ystart = prevy;
848 const GLfloat xend = points[i].x + 0.5f; // 0.5f to hit pixel center.
849 const GLfloat yend = points[i].y + 0.5f;
850 // bump a little in the direction we are moving in.
851 const GLfloat deltax = xend - xstart;
852 const GLfloat deltay = yend - ystart;
853 const GLfloat angle = SDL_atan2f(deltay, deltax);
854 prevx = xend + (SDL_cosf(angle) * 0.25f);
855 prevy = yend + (SDL_sinf(angle) * 0.25f);
856 verts->position.x = prevx;
857 verts->position.y = prevy;
858 verts->color = color;
859 verts++;
860 }
861
862 return true;
863}
864
865static bool GLES2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
866 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
867 int num_vertices, const void *indices, int num_indices, int size_indices,
868 float scale_x, float scale_y)
869{
870 int i;
871 const bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_BGRA32 || renderer->target->format == SDL_PIXELFORMAT_BGRX32));
872 int count = indices ? num_indices : num_vertices;
873 const float color_scale = cmd->data.draw.color_scale;
874
875 cmd->data.draw.count = count;
876 size_indices = indices ? size_indices : 0;
877
878 if (texture) {
879 SDL_Vertex *verts = (SDL_Vertex *)SDL_AllocateRenderVertices(renderer, count * sizeof(*verts), 0, &cmd->data.draw.first);
880 if (!verts) {
881 return false;
882 }
883
884 for (i = 0; i < count; i++) {
885 int j;
886 float *xy_;
887 SDL_FColor col_;
888 float *uv_;
889 if (size_indices == 4) {
890 j = ((const Uint32 *)indices)[i];
891 } else if (size_indices == 2) {
892 j = ((const Uint16 *)indices)[i];
893 } else if (size_indices == 1) {
894 j = ((const Uint8 *)indices)[i];
895 } else {
896 j = i;
897 }
898
899 xy_ = (float *)((char *)xy + j * xy_stride);
900 col_ = *(SDL_FColor *)((char *)color + j * color_stride);
901 uv_ = (float *)((char *)uv + j * uv_stride);
902
903 verts->position.x = xy_[0] * scale_x;
904 verts->position.y = xy_[1] * scale_y;
905
906 col_.r *= color_scale;
907 col_.g *= color_scale;
908 col_.b *= color_scale;
909
910 if (colorswap) {
911 float r = col_.r;
912 col_.r = col_.b;
913 col_.b = r;
914 }
915
916 verts->color = col_;
917 verts->tex_coord.x = uv_[0];
918 verts->tex_coord.y = uv_[1];
919 verts++;
920 }
921
922 } else {
923 SDL_VertexSolid *verts = (SDL_VertexSolid *)SDL_AllocateRenderVertices(renderer, count * sizeof(*verts), 0, &cmd->data.draw.first);
924 if (!verts) {
925 return false;
926 }
927
928 for (i = 0; i < count; i++) {
929 int j;
930 float *xy_;
931 SDL_FColor col_;
932
933 if (size_indices == 4) {
934 j = ((const Uint32 *)indices)[i];
935 } else if (size_indices == 2) {
936 j = ((const Uint16 *)indices)[i];
937 } else if (size_indices == 1) {
938 j = ((const Uint8 *)indices)[i];
939 } else {
940 j = i;
941 }
942
943 xy_ = (float *)((char *)xy + j * xy_stride);
944 col_ = *(SDL_FColor *)((char *)color + j * color_stride);
945
946 verts->position.x = xy_[0] * scale_x;
947 verts->position.y = xy_[1] * scale_y;
948
949 col_.r *= color_scale;
950 col_.g *= color_scale;
951 col_.b *= color_scale;
952
953 if (colorswap) {
954 float r = col_.r;
955 col_.r = col_.b;
956 col_.b = r;
957 }
958
959 verts->color = col_;
960 verts++;
961 }
962 }
963
964 return true;
965}
966
967static bool SetDrawState(GLES2_RenderData *data, const SDL_RenderCommand *cmd, const GLES2_ImageSource imgsrc, void *vertices)
968{
969 SDL_Texture *texture = cmd->data.draw.texture;
970 const SDL_BlendMode blend = cmd->data.draw.blend;
971 GLES2_ProgramCacheEntry *program;
972 int stride;
973
974 SDL_assert((texture != NULL) == (imgsrc != GLES2_IMAGESOURCE_SOLID));
975
976 if (data->drawstate.viewport_dirty) {
977 const SDL_Rect *viewport = &data->drawstate.viewport;
978 data->glViewport(viewport->x,
979 data->drawstate.target ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h),
980 viewport->w, viewport->h);
981 if (viewport->w && viewport->h) {
982 data->drawstate.projection[0][0] = 2.0f / viewport->w;
983 data->drawstate.projection[1][1] = (data->drawstate.target ? 2.0f : -2.0f) / viewport->h;
984 data->drawstate.projection[3][1] = data->drawstate.target ? -1.0f : 1.0f;
985 }
986 data->drawstate.viewport_dirty = false;
987 }
988
989 if (data->drawstate.cliprect_enabled_dirty) {
990 if (!data->drawstate.cliprect_enabled) {
991 data->glDisable(GL_SCISSOR_TEST);
992 } else {
993 data->glEnable(GL_SCISSOR_TEST);
994 }
995 data->drawstate.cliprect_enabled_dirty = false;
996 }
997
998 if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) {
999 const SDL_Rect *viewport = &data->drawstate.viewport;
1000 const SDL_Rect *rect = &data->drawstate.cliprect;
1001 data->glScissor(viewport->x + rect->x,
1002 data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h,
1003 rect->w, rect->h);
1004 data->drawstate.cliprect_dirty = false;
1005 }
1006
1007 if (data->drawstate.texturing_dirty || ((texture != NULL) != data->drawstate.texturing)) {
1008 if (!texture) {
1009 data->glDisableVertexAttribArray((GLenum)GLES2_ATTRIBUTE_TEXCOORD);
1010 data->drawstate.texturing = false;
1011 } else {
1012 data->glEnableVertexAttribArray((GLenum)GLES2_ATTRIBUTE_TEXCOORD);
1013 data->drawstate.texturing = true;
1014 }
1015 data->drawstate.texturing_dirty = false;
1016 }
1017
1018 if (texture) {
1019 stride = sizeof(SDL_Vertex);
1020 } else {
1021 stride = sizeof(SDL_VertexSolid);
1022 }
1023
1024 if (texture) {
1025 uintptr_t base = (uintptr_t)vertices + cmd->data.draw.first; // address of first vertex, or base offset when using VBOs.
1026 data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *)(base + offsetof(SDL_Vertex, tex_coord)));
1027 }
1028
1029 SDL_Colorspace colorspace = texture ? texture->colorspace : SDL_COLORSPACE_SRGB;
1030 if (!GLES2_SelectProgram(data, texture, imgsrc, cmd->data.draw.texture_scale_mode, colorspace)) {
1031 return false;
1032 }
1033
1034 program = data->drawstate.program;
1035
1036 if (program->uniform_locations[GLES2_UNIFORM_PROJECTION] != -1) {
1037 if (SDL_memcmp(program->projection, data->drawstate.projection, sizeof(data->drawstate.projection)) != 0) {
1038 data->glUniformMatrix4fv(program->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)data->drawstate.projection);
1039 SDL_memcpy(program->projection, data->drawstate.projection, sizeof(data->drawstate.projection));
1040 }
1041 }
1042
1043 if (blend != data->drawstate.blend) {
1044 if (blend == SDL_BLENDMODE_NONE) {
1045 data->glDisable(GL_BLEND);
1046 } else {
1047 data->glEnable(GL_BLEND);
1048 data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)),
1049 GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)),
1050 GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)),
1051 GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend)));
1052 data->glBlendEquationSeparate(GetBlendEquation(SDL_GetBlendModeColorOperation(blend)),
1053 GetBlendEquation(SDL_GetBlendModeAlphaOperation(blend)));
1054 }
1055 data->drawstate.blend = blend;
1056 }
1057
1058 // all drawing commands use this
1059 {
1060 uintptr_t base = (uintptr_t)vertices + cmd->data.draw.first; // address of first vertex, or base offset when using VBOs.
1061 data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *)(base + offsetof(SDL_VertexSolid, position)));
1062 data->glVertexAttribPointer(GLES2_ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_TRUE /* Normalized */, stride, (const GLvoid *)(base + offsetof(SDL_VertexSolid, color)));
1063 }
1064
1065 return true;
1066}
1067
1068static bool SetTextureScaleMode(GLES2_RenderData *data, GLenum textype, SDL_ScaleMode scaleMode)
1069{
1070 switch (scaleMode) {
1071 case SDL_SCALEMODE_NEAREST:
1072 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1073 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1074 break;
1075 case SDL_SCALEMODE_LINEAR:
1076 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1077 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1078 break;
1079 case SDL_SCALEMODE_PIXELART:
1080#ifdef OPENGLES_300 // Required for the pixel art shader
1081 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1082 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1083#else
1084 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1085 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1086#endif
1087 break;
1088 default:
1089 return SDL_SetError("Unknown texture scale mode: %d", scaleMode);
1090 }
1091 return true;
1092}
1093
1094static bool SetTextureAddressMode(GLES2_RenderData *data, GLenum textype, SDL_TextureAddressMode addressMode)
1095{
1096 switch (addressMode) {
1097 case SDL_TEXTURE_ADDRESS_CLAMP:
1098 data->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1099 data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1100 break;
1101 case SDL_TEXTURE_ADDRESS_WRAP:
1102 data->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_REPEAT);
1103 data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_REPEAT);
1104 break;
1105 default:
1106 return SDL_SetError("Unknown texture address mode: %d", addressMode);
1107 }
1108 return true;
1109}
1110
1111static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, void *vertices)
1112{
1113 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1114 GLES2_ImageSource sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
1115 SDL_Texture *texture = cmd->data.draw.texture;
1116 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
1117 int ret;
1118
1119 // Pick an appropriate shader
1120 if (renderer->target) {
1121 // Check if we need to do color mapping between the source and render target textures
1122 if (renderer->target->format != texture->format) {
1123 switch (texture->format) {
1124 case SDL_PIXELFORMAT_BGRA32:
1125 switch (renderer->target->format) {
1126 case SDL_PIXELFORMAT_RGBA32:
1127 case SDL_PIXELFORMAT_RGBX32:
1128 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1129 break;
1130 case SDL_PIXELFORMAT_BGRX32:
1131 sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
1132 break;
1133 default:
1134 break;
1135 }
1136 break;
1137 case SDL_PIXELFORMAT_RGBA32:
1138 switch (renderer->target->format) {
1139 case SDL_PIXELFORMAT_BGRA32:
1140 case SDL_PIXELFORMAT_BGRX32:
1141 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1142 break;
1143 case SDL_PIXELFORMAT_RGBX32:
1144 sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
1145 break;
1146 default:
1147 break;
1148 }
1149 break;
1150 case SDL_PIXELFORMAT_BGRX32:
1151 switch (renderer->target->format) {
1152 case SDL_PIXELFORMAT_RGBA32:
1153 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1154 break;
1155 case SDL_PIXELFORMAT_BGRA32:
1156 sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
1157 break;
1158 case SDL_PIXELFORMAT_RGBX32:
1159 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1160 break;
1161 default:
1162 break;
1163 }
1164 break;
1165 case SDL_PIXELFORMAT_RGBX32:
1166 switch (renderer->target->format) {
1167 case SDL_PIXELFORMAT_RGBA32:
1168 sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
1169 break;
1170 case SDL_PIXELFORMAT_BGRA32:
1171 sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB;
1172 break;
1173 case SDL_PIXELFORMAT_BGRX32:
1174 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1175 break;
1176 default:
1177 break;
1178 }
1179 break;
1180#ifdef SDL_HAVE_YUV
1181 case SDL_PIXELFORMAT_IYUV:
1182 case SDL_PIXELFORMAT_YV12:
1183 sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
1184 break;
1185 case SDL_PIXELFORMAT_NV12:
1186 sourceType = GLES2_IMAGESOURCE_TEXTURE_NV12;
1187 break;
1188 case SDL_PIXELFORMAT_NV21:
1189 sourceType = GLES2_IMAGESOURCE_TEXTURE_NV21;
1190 break;
1191#endif
1192 case SDL_PIXELFORMAT_EXTERNAL_OES:
1193 sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES;
1194 break;
1195 default:
1196 return SDL_SetError("Unsupported texture format");
1197 }
1198 } else {
1199 sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR; // Texture formats match, use the non color mapping shader (even if the formats are not ABGR)
1200 }
1201 } else {
1202 switch (texture->format) {
1203 case SDL_PIXELFORMAT_BGRA32:
1204 sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
1205 break;
1206 case SDL_PIXELFORMAT_RGBA32:
1207 sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
1208 break;
1209 case SDL_PIXELFORMAT_BGRX32:
1210 sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB;
1211 break;
1212 case SDL_PIXELFORMAT_RGBX32:
1213 sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
1214 break;
1215#ifdef SDL_HAVE_YUV
1216 case SDL_PIXELFORMAT_IYUV:
1217 case SDL_PIXELFORMAT_YV12:
1218 sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
1219 break;
1220 case SDL_PIXELFORMAT_NV12:
1221 sourceType = GLES2_IMAGESOURCE_TEXTURE_NV12;
1222 break;
1223 case SDL_PIXELFORMAT_NV21:
1224 sourceType = GLES2_IMAGESOURCE_TEXTURE_NV21;
1225 break;
1226#endif
1227 case SDL_PIXELFORMAT_EXTERNAL_OES:
1228 sourceType = GLES2_IMAGESOURCE_TEXTURE_EXTERNAL_OES;
1229 break;
1230 default:
1231 return SDL_SetError("Unsupported texture format");
1232 }
1233 }
1234
1235 ret = SetDrawState(data, cmd, sourceType, vertices);
1236
1237 if (texture != data->drawstate.texture) {
1238#ifdef SDL_HAVE_YUV
1239 if (tdata->yuv) {
1240 data->glActiveTexture(GL_TEXTURE2);
1241 data->glBindTexture(tdata->texture_type, tdata->texture_v);
1242
1243 data->glActiveTexture(GL_TEXTURE1);
1244 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1245
1246 data->glActiveTexture(GL_TEXTURE0);
1247 } else if (tdata->nv12) {
1248 data->glActiveTexture(GL_TEXTURE1);
1249 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1250
1251 data->glActiveTexture(GL_TEXTURE0);
1252 }
1253#endif
1254 data->glBindTexture(tdata->texture_type, tdata->texture);
1255
1256 data->drawstate.texture = texture;
1257 }
1258
1259 if (cmd->data.draw.texture_scale_mode != tdata->texture_scale_mode) {
1260#ifdef SDL_HAVE_YUV
1261 if (tdata->yuv) {
1262 data->glActiveTexture(GL_TEXTURE2);
1263 if (!SetTextureScaleMode(data, tdata->texture_type, cmd->data.draw.texture_scale_mode)) {
1264 return false;
1265 }
1266
1267 data->glActiveTexture(GL_TEXTURE1);
1268 if (!SetTextureScaleMode(data, tdata->texture_type, cmd->data.draw.texture_scale_mode)) {
1269 return false;
1270 }
1271
1272 data->glActiveTexture(GL_TEXTURE0);
1273 } else if (tdata->nv12) {
1274 data->glActiveTexture(GL_TEXTURE1);
1275 if (!SetTextureScaleMode(data, tdata->texture_type, cmd->data.draw.texture_scale_mode)) {
1276 return false;
1277 }
1278
1279 data->glActiveTexture(GL_TEXTURE0);
1280 }
1281#endif
1282 if (!SetTextureScaleMode(data, tdata->texture_type, cmd->data.draw.texture_scale_mode)) {
1283 return false;
1284 }
1285
1286 tdata->texture_scale_mode = cmd->data.draw.texture_scale_mode;
1287 }
1288
1289 if (cmd->data.draw.texture_address_mode != tdata->texture_address_mode) {
1290#ifdef SDL_HAVE_YUV
1291 if (tdata->yuv) {
1292 data->glActiveTexture(GL_TEXTURE2);
1293 if (!SetTextureAddressMode(data, tdata->texture_type, cmd->data.draw.texture_address_mode)) {
1294 return false;
1295 }
1296
1297 data->glActiveTexture(GL_TEXTURE1);
1298 if (!SetTextureAddressMode(data, tdata->texture_type, cmd->data.draw.texture_address_mode)) {
1299 return false;
1300 }
1301
1302 data->glActiveTexture(GL_TEXTURE0);
1303 } else if (tdata->nv12) {
1304 data->glActiveTexture(GL_TEXTURE1);
1305 if (!SetTextureAddressMode(data, tdata->texture_type, cmd->data.draw.texture_address_mode)) {
1306 return false;
1307 }
1308
1309 data->glActiveTexture(GL_TEXTURE0);
1310 }
1311#endif
1312 if (!SetTextureAddressMode(data, tdata->texture_type, cmd->data.draw.texture_address_mode)) {
1313 return false;
1314 }
1315
1316 tdata->texture_address_mode = cmd->data.draw.texture_address_mode;
1317 }
1318
1319 return ret;
1320}
1321
1322static void GLES2_InvalidateCachedState(SDL_Renderer *renderer)
1323{
1324 GLES2_DrawStateCache *cache = &((GLES2_RenderData *)renderer->internal)->drawstate;
1325 cache->viewport_dirty = true;
1326 cache->texture = NULL;
1327 cache->blend = SDL_BLENDMODE_INVALID;
1328 cache->cliprect_enabled_dirty = true;
1329 cache->cliprect_dirty = true;
1330 cache->texturing_dirty = true;
1331 cache->clear_color_dirty = true;
1332 cache->drawablew = 0;
1333 cache->drawableh = 0;
1334 cache->program = NULL;
1335}
1336
1337static bool GLES2_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
1338{
1339 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1340 const bool colorswap = (renderer->target && (renderer->target->format == SDL_PIXELFORMAT_BGRA32 || renderer->target->format == SDL_PIXELFORMAT_BGRX32));
1341
1342#if USE_VERTEX_BUFFER_OBJECTS
1343 const int vboidx = data->current_vertex_buffer;
1344 const GLuint vbo = data->vertex_buffers[vboidx];
1345#endif
1346
1347 if (!GLES2_ActivateRenderer(renderer)) {
1348 return false;
1349 }
1350
1351 data->drawstate.target = renderer->target;
1352 if (!data->drawstate.target) {
1353 int w, h;
1354 SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
1355 if ((w != data->drawstate.drawablew) || (h != data->drawstate.drawableh)) {
1356 data->drawstate.viewport_dirty = true; // if the window dimensions changed, invalidate the current viewport, etc.
1357 data->drawstate.cliprect_dirty = true;
1358 data->drawstate.drawablew = w;
1359 data->drawstate.drawableh = h;
1360 }
1361 }
1362
1363#if USE_VERTEX_BUFFER_OBJECTS
1364 // upload the new VBO data for this set of commands.
1365 data->glBindBuffer(GL_ARRAY_BUFFER, vbo);
1366 if (data->vertex_buffer_size[vboidx] < vertsize) {
1367 data->glBufferData(GL_ARRAY_BUFFER, vertsize, vertices, GL_STREAM_DRAW);
1368 data->vertex_buffer_size[vboidx] = vertsize;
1369 } else {
1370 data->glBufferSubData(GL_ARRAY_BUFFER, 0, vertsize, vertices);
1371 }
1372
1373 // cycle through a few VBOs so the GL has some time with the data before we replace it.
1374 data->current_vertex_buffer++;
1375 if (data->current_vertex_buffer >= SDL_arraysize(data->vertex_buffers)) {
1376 data->current_vertex_buffer = 0;
1377 }
1378 // attrib pointers will be offsets into the VBO.
1379 vertices = (void *)(uintptr_t)0; // must be the exact value 0, not NULL (the representation of NULL is not guaranteed to be 0).
1380#endif
1381
1382 while (cmd) {
1383 switch (cmd->command) {
1384 case SDL_RENDERCMD_SETDRAWCOLOR:
1385 {
1386 break;
1387 }
1388
1389 case SDL_RENDERCMD_SETVIEWPORT:
1390 {
1391 SDL_Rect *viewport = &data->drawstate.viewport;
1392 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) {
1393 SDL_copyp(viewport, &cmd->data.viewport.rect);
1394 data->drawstate.viewport_dirty = true;
1395 data->drawstate.cliprect_dirty = true;
1396 }
1397 break;
1398 }
1399
1400 case SDL_RENDERCMD_SETCLIPRECT:
1401 {
1402 const SDL_Rect *rect = &cmd->data.cliprect.rect;
1403 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
1404 data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
1405 data->drawstate.cliprect_enabled_dirty = true;
1406 }
1407
1408 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof(*rect)) != 0) {
1409 SDL_copyp(&data->drawstate.cliprect, rect);
1410 data->drawstate.cliprect_dirty = true;
1411 }
1412 break;
1413 }
1414
1415 case SDL_RENDERCMD_CLEAR:
1416 {
1417 const float r = (colorswap ? cmd->data.color.color.b : cmd->data.color.color.r) * cmd->data.color.color_scale;
1418 const float g = cmd->data.color.color.g * cmd->data.color.color_scale;
1419 const float b = (colorswap ? cmd->data.color.color.r : cmd->data.color.color.b) * cmd->data.color.color_scale;
1420 const float a = cmd->data.color.color.a;
1421 if (data->drawstate.clear_color_dirty ||
1422 (r != data->drawstate.clear_color.r) ||
1423 (g != data->drawstate.clear_color.g) ||
1424 (b != data->drawstate.clear_color.b) ||
1425 (a != data->drawstate.clear_color.a)) {
1426 data->glClearColor(r, g, b, a);
1427 data->drawstate.clear_color.r = r;
1428 data->drawstate.clear_color.g = g;
1429 data->drawstate.clear_color.b = b;
1430 data->drawstate.clear_color.a = a;
1431 data->drawstate.clear_color_dirty = false;
1432 }
1433
1434 if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) {
1435 data->glDisable(GL_SCISSOR_TEST);
1436 data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled;
1437 }
1438
1439 data->glClear(GL_COLOR_BUFFER_BIT);
1440 break;
1441 }
1442
1443 case SDL_RENDERCMD_FILL_RECTS: // unused
1444 break;
1445
1446 case SDL_RENDERCMD_COPY: // unused
1447 break;
1448
1449 case SDL_RENDERCMD_COPY_EX: // unused
1450 break;
1451
1452 case SDL_RENDERCMD_DRAW_LINES:
1453 {
1454 if (SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID, vertices)) {
1455 size_t count = cmd->data.draw.count;
1456 if (count > 2) {
1457 // joined lines cannot be grouped
1458 data->glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)count);
1459 } else {
1460 // let's group non joined lines
1461 SDL_RenderCommand *finalcmd = cmd;
1462 SDL_RenderCommand *nextcmd = cmd->next;
1463 SDL_BlendMode thisblend = cmd->data.draw.blend;
1464
1465 while (nextcmd) {
1466 const SDL_RenderCommandType nextcmdtype = nextcmd->command;
1467 if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) {
1468 break; // can't go any further on this draw call, different render command up next.
1469 } else if (nextcmd->data.draw.count != 2) {
1470 break; // can't go any further on this draw call, those are joined lines
1471 } else if (nextcmd->data.draw.blend != thisblend) {
1472 break; // can't go any further on this draw call, different blendmode copy up next.
1473 } else {
1474 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
1475 count += nextcmd->data.draw.count;
1476 }
1477 nextcmd = nextcmd->next;
1478 }
1479
1480 data->glDrawArrays(GL_LINES, 0, (GLsizei)count);
1481 cmd = finalcmd; // skip any copy commands we just combined in here.
1482 }
1483 }
1484 break;
1485 }
1486
1487 case SDL_RENDERCMD_DRAW_POINTS:
1488 case SDL_RENDERCMD_GEOMETRY:
1489 {
1490 /* as long as we have the same copy command in a row, with the
1491 same texture, we can combine them all into a single draw call. */
1492 SDL_Texture *thistexture = cmd->data.draw.texture;
1493 SDL_BlendMode thisblend = cmd->data.draw.blend;
1494 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
1495 SDL_TextureAddressMode thisaddressmode = cmd->data.draw.texture_address_mode;
1496 const SDL_RenderCommandType thiscmdtype = cmd->command;
1497 SDL_RenderCommand *finalcmd = cmd;
1498 SDL_RenderCommand *nextcmd = cmd->next;
1499 size_t count = cmd->data.draw.count;
1500 int ret;
1501 while (nextcmd) {
1502 const SDL_RenderCommandType nextcmdtype = nextcmd->command;
1503 if (nextcmdtype != thiscmdtype) {
1504 break; // can't go any further on this draw call, different render command up next.
1505 } else if (nextcmd->data.draw.texture != thistexture ||
1506 nextcmd->data.draw.texture_scale_mode != thisscalemode ||
1507 nextcmd->data.draw.texture_address_mode != thisaddressmode ||
1508 nextcmd->data.draw.blend != thisblend) {
1509 break; // can't go any further on this draw call, different texture/blendmode copy up next.
1510 } else {
1511 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command.
1512 count += nextcmd->data.draw.count;
1513 }
1514 nextcmd = nextcmd->next;
1515 }
1516
1517 if (thistexture) {
1518 ret = SetCopyState(renderer, cmd, vertices);
1519 } else {
1520 ret = SetDrawState(data, cmd, GLES2_IMAGESOURCE_SOLID, vertices);
1521 }
1522
1523 if (ret) {
1524 int op = GL_TRIANGLES; // SDL_RENDERCMD_GEOMETRY
1525 if (thiscmdtype == SDL_RENDERCMD_DRAW_POINTS) {
1526 op = GL_POINTS;
1527 }
1528 data->glDrawArrays(op, 0, (GLsizei)count);
1529 }
1530
1531 cmd = finalcmd; // skip any copy commands we just combined in here.
1532 break;
1533 }
1534
1535 case SDL_RENDERCMD_NO_OP:
1536 break;
1537 }
1538
1539 cmd = cmd->next;
1540 }
1541
1542 return GL_CheckError("", renderer);
1543}
1544
1545static void GLES2_DestroyRenderer(SDL_Renderer *renderer)
1546{
1547 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1548
1549 // Deallocate everything
1550 if (data) {
1551 GLES2_ActivateRenderer(renderer);
1552
1553 {
1554 int i;
1555 for (i = 0; i < GLES2_SHADER_COUNT; i++) {
1556 GLuint id = data->shader_id_cache[i];
1557 if (id) {
1558 data->glDeleteShader(id);
1559 }
1560 }
1561 }
1562 {
1563 GLES2_ProgramCacheEntry *entry;
1564 GLES2_ProgramCacheEntry *next;
1565 entry = data->program_cache.head;
1566 while (entry) {
1567 data->glDeleteProgram(entry->id);
1568 next = entry->next;
1569 SDL_free(entry);
1570 entry = next;
1571 }
1572 }
1573
1574 if (data->context) {
1575 while (data->framebuffers) {
1576 GLES2_FBOList *nextnode = data->framebuffers->next;
1577 data->glDeleteFramebuffers(1, &data->framebuffers->FBO);
1578 GL_CheckError("", renderer);
1579 SDL_free(data->framebuffers);
1580 data->framebuffers = nextnode;
1581 }
1582
1583#if USE_VERTEX_BUFFER_OBJECTS
1584 data->glDeleteBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers);
1585 GL_CheckError("", renderer);
1586#endif
1587
1588 SDL_GL_DestroyContext(data->context);
1589 }
1590
1591 SDL_free(data);
1592 }
1593}
1594
1595static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
1596{
1597 GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->internal;
1598 GLES2_TextureData *data;
1599 GLenum format;
1600 GLenum type;
1601
1602 GLES2_ActivateRenderer(renderer);
1603
1604 renderdata->drawstate.texture = NULL; // we trash this state.
1605
1606 // Determine the corresponding GLES texture format params
1607 switch (texture->format) {
1608 case SDL_PIXELFORMAT_BGRA32:
1609 case SDL_PIXELFORMAT_RGBA32:
1610 case SDL_PIXELFORMAT_BGRX32:
1611 case SDL_PIXELFORMAT_RGBX32:
1612 format = GL_RGBA;
1613 type = GL_UNSIGNED_BYTE;
1614 break;
1615#ifdef SDL_HAVE_YUV
1616 case SDL_PIXELFORMAT_IYUV:
1617 case SDL_PIXELFORMAT_YV12:
1618 case SDL_PIXELFORMAT_NV12:
1619 case SDL_PIXELFORMAT_NV21:
1620 format = GL_LUMINANCE;
1621 type = GL_UNSIGNED_BYTE;
1622 break;
1623#endif
1624#ifdef GL_TEXTURE_EXTERNAL_OES
1625 case SDL_PIXELFORMAT_EXTERNAL_OES:
1626 format = GL_NONE;
1627 type = GL_NONE;
1628 break;
1629#endif
1630 default:
1631 return SDL_SetError("Texture format not supported");
1632 }
1633
1634 if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES &&
1635 texture->access != SDL_TEXTUREACCESS_STATIC) {
1636 return SDL_SetError("Unsupported texture access for SDL_PIXELFORMAT_EXTERNAL_OES");
1637 }
1638
1639 // Allocate a texture struct
1640 data = (GLES2_TextureData *)SDL_calloc(1, sizeof(GLES2_TextureData));
1641 if (!data) {
1642 return false;
1643 }
1644 data->texture = 0;
1645#ifdef GL_TEXTURE_EXTERNAL_OES
1646 data->texture_type = (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
1647#else
1648 data->texture_type = GL_TEXTURE_2D;
1649#endif
1650 data->pixel_format = format;
1651 data->pixel_type = type;
1652#ifdef SDL_HAVE_YUV
1653 data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12));
1654 data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21));
1655 data->texture_u = 0;
1656 data->texture_v = 0;
1657#endif
1658 data->texture_scale_mode = SDL_SCALEMODE_INVALID;
1659 data->texture_address_mode = SDL_TEXTURE_ADDRESS_INVALID;
1660
1661 // Allocate a blob for image renderdata
1662 if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
1663 size_t size;
1664 data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
1665 size = (size_t)texture->h * data->pitch;
1666#ifdef SDL_HAVE_YUV
1667 if (data->yuv) {
1668 // Need to add size for the U and V planes
1669 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
1670 } else if (data->nv12) {
1671 // Need to add size for the U/V plane
1672 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);
1673 }
1674#endif
1675 data->pixel_data = SDL_calloc(1, size);
1676 if (!data->pixel_data) {
1677 SDL_free(data);
1678 return false;
1679 }
1680 }
1681
1682 // Allocate the texture
1683 GL_CheckError("", renderer);
1684
1685 data->texel_size[2] = texture->w;
1686 data->texel_size[3] = texture->h;
1687 data->texel_size[0] = 1.0f / data->texel_size[2];
1688 data->texel_size[1] = 1.0f / data->texel_size[3];
1689
1690#ifdef SDL_HAVE_YUV
1691 if (data->yuv) {
1692 data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0);
1693 if (data->texture_v) {
1694 data->texture_v_external = true;
1695 } else {
1696 renderdata->glGenTextures(1, &data->texture_v);
1697 if (!GL_CheckError("glGenTexures()", renderer)) {
1698 return false;
1699 }
1700 }
1701 renderdata->glActiveTexture(GL_TEXTURE2);
1702 renderdata->glBindTexture(data->texture_type, data->texture_v);
1703 renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
1704 SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER, data->texture_v);
1705
1706 data->texture_u = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER, 0);
1707 if (data->texture_u) {
1708 data->texture_u_external = true;
1709 } else {
1710 renderdata->glGenTextures(1, &data->texture_u);
1711 if (!GL_CheckError("glGenTexures()", renderer)) {
1712 return false;
1713 }
1714 }
1715 renderdata->glActiveTexture(GL_TEXTURE1);
1716 renderdata->glBindTexture(data->texture_type, data->texture_u);
1717 renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);
1718 if (!GL_CheckError("glTexImage2D()", renderer)) {
1719 return false;
1720 }
1721 SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER, data->texture_u);
1722
1723 if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) {
1724 return SDL_SetError("Unsupported YUV colorspace");
1725 }
1726 } else if (data->nv12) {
1727 data->texture_u = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER, 0);
1728 if (data->texture_u) {
1729 data->texture_u_external = true;
1730 } else {
1731 renderdata->glGenTextures(1, &data->texture_u);
1732 if (!GL_CheckError("glGenTexures()", renderer)) {
1733 return false;
1734 }
1735 }
1736 renderdata->glActiveTexture(GL_TEXTURE1);
1737 renderdata->glBindTexture(data->texture_type, data->texture_u);
1738 renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
1739 if (!GL_CheckError("glTexImage2D()", renderer)) {
1740 return false;
1741 }
1742 SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER, data->texture_u);
1743
1744 if (!SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8)) {
1745 return SDL_SetError("Unsupported YUV colorspace");
1746 }
1747 }
1748#endif
1749
1750 data->texture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER, 0);
1751 if (data->texture) {
1752 data->texture_external = true;
1753 } else {
1754 renderdata->glGenTextures(1, &data->texture);
1755 if (!GL_CheckError("glGenTexures()", renderer)) {
1756 return false;
1757 }
1758 }
1759 texture->internal = data;
1760 renderdata->glActiveTexture(GL_TEXTURE0);
1761 renderdata->glBindTexture(data->texture_type, data->texture);
1762 if (texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) {
1763 renderdata->glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL);
1764 if (!GL_CheckError("glTexImage2D()", renderer)) {
1765 return false;
1766 }
1767 }
1768 SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER, data->texture);
1769 SDL_SetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER, data->texture_type);
1770
1771 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
1772 data->fbo = GLES2_GetFBO((GLES2_RenderData *)renderer->internal, texture->w, texture->h);
1773 } else {
1774 data->fbo = NULL;
1775 }
1776
1777 return GL_CheckError("", renderer);
1778}
1779
1780static bool GLES2_TexSubImage2D(GLES2_RenderData *data, GLenum target, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, GLint pitch, GLint bpp)
1781{
1782 Uint8 *blob = NULL;
1783 Uint8 *src;
1784 size_t src_pitch;
1785 int y;
1786
1787 if ((width == 0) || (height == 0) || (bpp == 0)) {
1788 return true; // nothing to do
1789 }
1790
1791 // Reformat the texture data into a tightly packed array
1792 src_pitch = (size_t)width * bpp;
1793 src = (Uint8 *)pixels;
1794 if ((size_t)pitch != src_pitch) {
1795 blob = (Uint8 *)SDL_malloc(src_pitch * height);
1796 if (!blob) {
1797 return false;
1798 }
1799 src = blob;
1800 for (y = 0; y < height; ++y) {
1801 SDL_memcpy(src, pixels, src_pitch);
1802 src += src_pitch;
1803 pixels = (Uint8 *)pixels + pitch;
1804 }
1805 src = blob;
1806 }
1807
1808 data->glTexSubImage2D(target, 0, xoffset, yoffset, width, height, format, type, src);
1809 if (blob) {
1810 SDL_free(blob);
1811 }
1812 return true;
1813}
1814
1815static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
1816 const void *pixels, int pitch)
1817{
1818 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1819 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
1820
1821 GLES2_ActivateRenderer(renderer);
1822
1823 // Bail out if we're supposed to update an empty rectangle
1824 if (rect->w <= 0 || rect->h <= 0) {
1825 return true;
1826 }
1827
1828 data->drawstate.texture = NULL; // we trash this state.
1829
1830 // Create a texture subimage with the supplied data
1831 data->glBindTexture(tdata->texture_type, tdata->texture);
1832 GLES2_TexSubImage2D(data, tdata->texture_type,
1833 rect->x,
1834 rect->y,
1835 rect->w,
1836 rect->h,
1837 tdata->pixel_format,
1838 tdata->pixel_type,
1839 pixels, pitch, SDL_BYTESPERPIXEL(texture->format));
1840
1841#ifdef SDL_HAVE_YUV
1842 if (tdata->yuv) {
1843 // Skip to the correct offset into the next texture
1844 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
1845 if (texture->format == SDL_PIXELFORMAT_YV12) {
1846 data->glBindTexture(tdata->texture_type, tdata->texture_v);
1847 } else {
1848 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1849 }
1850 GLES2_TexSubImage2D(data, tdata->texture_type,
1851 rect->x / 2,
1852 rect->y / 2,
1853 (rect->w + 1) / 2,
1854 (rect->h + 1) / 2,
1855 tdata->pixel_format,
1856 tdata->pixel_type,
1857 pixels, (pitch + 1) / 2, 1);
1858
1859 // Skip to the correct offset into the next texture
1860 pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
1861 if (texture->format == SDL_PIXELFORMAT_YV12) {
1862 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1863 } else {
1864 data->glBindTexture(tdata->texture_type, tdata->texture_v);
1865 }
1866 GLES2_TexSubImage2D(data, tdata->texture_type,
1867 rect->x / 2,
1868 rect->y / 2,
1869 (rect->w + 1) / 2,
1870 (rect->h + 1) / 2,
1871 tdata->pixel_format,
1872 tdata->pixel_type,
1873 pixels, (pitch + 1) / 2, 1);
1874 } else if (tdata->nv12) {
1875 // Skip to the correct offset into the next texture
1876 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
1877 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1878 GLES2_TexSubImage2D(data, tdata->texture_type,
1879 rect->x / 2,
1880 rect->y / 2,
1881 (rect->w + 1) / 2,
1882 (rect->h + 1) / 2,
1883 GL_LUMINANCE_ALPHA,
1884 GL_UNSIGNED_BYTE,
1885 pixels, 2 * ((pitch + 1) / 2), 2);
1886 }
1887#endif
1888
1889 return GL_CheckError("glTexSubImage2D()", renderer);
1890}
1891
1892#ifdef SDL_HAVE_YUV
1893static bool GLES2_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
1894 const SDL_Rect *rect,
1895 const Uint8 *Yplane, int Ypitch,
1896 const Uint8 *Uplane, int Upitch,
1897 const Uint8 *Vplane, int Vpitch)
1898{
1899 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1900 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
1901
1902 GLES2_ActivateRenderer(renderer);
1903
1904 // Bail out if we're supposed to update an empty rectangle
1905 if (rect->w <= 0 || rect->h <= 0) {
1906 return true;
1907 }
1908
1909 data->drawstate.texture = NULL; // we trash this state.
1910
1911 data->glBindTexture(tdata->texture_type, tdata->texture_v);
1912 GLES2_TexSubImage2D(data, tdata->texture_type,
1913 rect->x / 2,
1914 rect->y / 2,
1915 (rect->w + 1) / 2,
1916 (rect->h + 1) / 2,
1917 tdata->pixel_format,
1918 tdata->pixel_type,
1919 Vplane, Vpitch, 1);
1920
1921 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1922 GLES2_TexSubImage2D(data, tdata->texture_type,
1923 rect->x / 2,
1924 rect->y / 2,
1925 (rect->w + 1) / 2,
1926 (rect->h + 1) / 2,
1927 tdata->pixel_format,
1928 tdata->pixel_type,
1929 Uplane, Upitch, 1);
1930
1931 data->glBindTexture(tdata->texture_type, tdata->texture);
1932 GLES2_TexSubImage2D(data, tdata->texture_type,
1933 rect->x,
1934 rect->y,
1935 rect->w,
1936 rect->h,
1937 tdata->pixel_format,
1938 tdata->pixel_type,
1939 Yplane, Ypitch, 1);
1940
1941 return GL_CheckError("glTexSubImage2D()", renderer);
1942}
1943
1944static bool GLES2_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
1945 const SDL_Rect *rect,
1946 const Uint8 *Yplane, int Ypitch,
1947 const Uint8 *UVplane, int UVpitch)
1948{
1949 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
1950 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
1951
1952 GLES2_ActivateRenderer(renderer);
1953
1954 // Bail out if we're supposed to update an empty rectangle
1955 if (rect->w <= 0 || rect->h <= 0) {
1956 return true;
1957 }
1958
1959 data->drawstate.texture = NULL; // we trash this state.
1960
1961 data->glBindTexture(tdata->texture_type, tdata->texture_u);
1962 GLES2_TexSubImage2D(data, tdata->texture_type,
1963 rect->x / 2,
1964 rect->y / 2,
1965 (rect->w + 1) / 2,
1966 (rect->h + 1) / 2,
1967 GL_LUMINANCE_ALPHA,
1968 GL_UNSIGNED_BYTE,
1969 UVplane, UVpitch, 2);
1970
1971 data->glBindTexture(tdata->texture_type, tdata->texture);
1972 GLES2_TexSubImage2D(data, tdata->texture_type,
1973 rect->x,
1974 rect->y,
1975 rect->w,
1976 rect->h,
1977 tdata->pixel_format,
1978 tdata->pixel_type,
1979 Yplane, Ypitch, 1);
1980
1981 return GL_CheckError("glTexSubImage2D()", renderer);
1982}
1983#endif
1984
1985static bool GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
1986 void **pixels, int *pitch)
1987{
1988 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
1989
1990 // Retrieve the buffer/pitch for the specified region
1991 *pixels = (Uint8 *)tdata->pixel_data +
1992 (tdata->pitch * rect->y) +
1993 (rect->x * SDL_BYTESPERPIXEL(texture->format));
1994 *pitch = tdata->pitch;
1995
1996 return true;
1997}
1998
1999static void GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
2000{
2001 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
2002 SDL_Rect rect;
2003
2004 // We do whole texture updates, at least for now
2005 rect.x = 0;
2006 rect.y = 0;
2007 rect.w = texture->w;
2008 rect.h = texture->h;
2009 GLES2_UpdateTexture(renderer, texture, &rect, tdata->pixel_data, tdata->pitch);
2010}
2011
2012static bool GLES2_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
2013{
2014 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
2015 GLES2_TextureData *texturedata = NULL;
2016 GLenum status;
2017
2018 data->drawstate.viewport_dirty = true;
2019
2020 if (!texture) {
2021 data->glBindFramebuffer(GL_FRAMEBUFFER, data->window_framebuffer);
2022 } else {
2023 texturedata = (GLES2_TextureData *)texture->internal;
2024 data->glBindFramebuffer(GL_FRAMEBUFFER, texturedata->fbo->FBO);
2025 // TODO: check if texture pixel format allows this operation
2026 data->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texturedata->texture_type, texturedata->texture, 0);
2027 // Check FBO status
2028 status = data->glCheckFramebufferStatus(GL_FRAMEBUFFER);
2029 if (status != GL_FRAMEBUFFER_COMPLETE) {
2030 return SDL_SetError("glFramebufferTexture2D() failed");
2031 }
2032 }
2033 return true;
2034}
2035
2036static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
2037{
2038 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
2039 GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
2040
2041 GLES2_ActivateRenderer(renderer);
2042
2043 if (data->drawstate.texture == texture) {
2044 data->drawstate.texture = NULL;
2045 }
2046 if (data->drawstate.target == texture) {
2047 data->drawstate.target = NULL;
2048 }
2049
2050 // Destroy the texture
2051 if (tdata) {
2052 if (tdata->texture && !tdata->texture_external) {
2053 data->glDeleteTextures(1, &tdata->texture);
2054 }
2055#ifdef SDL_HAVE_YUV
2056 if (tdata->texture_v && !tdata->texture_v_external) {
2057 data->glDeleteTextures(1, &tdata->texture_v);
2058 }
2059 if (tdata->texture_u && !tdata->texture_u_external) {
2060 data->glDeleteTextures(1, &tdata->texture_u);
2061 }
2062#endif
2063 SDL_free(tdata->pixel_data);
2064 SDL_free(tdata);
2065 texture->internal = NULL;
2066 }
2067}
2068
2069static SDL_Surface *GLES2_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
2070{
2071 GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
2072 SDL_PixelFormat format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_RGBA32;
2073 SDL_Surface *surface;
2074
2075 surface = SDL_CreateSurface(rect->w, rect->h, format);
2076 if (!surface) {
2077 return NULL;
2078 }
2079
2080 int y = rect->y;
2081 if (!renderer->target) {
2082 int w, h;
2083 SDL_GetRenderOutputSize(renderer, &w, &h);
2084 y = (h - y) - rect->h;
2085 }
2086
2087 data->glReadPixels(rect->x, y, rect->w, rect->h, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
2088 if (!GL_CheckError("glReadPixels()", renderer)) {
2089 SDL_DestroySurface(surface);
2090 return NULL;
2091 }
2092
2093 // Flip the rows to be top-down if necessary
2094 if (!renderer->target) {
2095 SDL_FlipSurface(surface, SDL_FLIP_VERTICAL);
2096 }
2097 return surface;
2098}
2099
2100static bool GLES2_RenderPresent(SDL_Renderer *renderer)
2101{
2102 // Tell the video driver to swap buffers
2103 return SDL_GL_SwapWindow(renderer->window);
2104}
2105
2106static bool GLES2_SetVSync(SDL_Renderer *renderer, const int vsync)
2107{
2108 int interval = 0;
2109
2110 if (!SDL_GL_SetSwapInterval(vsync)) {
2111 return false;
2112 }
2113
2114 if (!SDL_GL_GetSwapInterval(&interval)) {
2115 return false;
2116 }
2117
2118 if (interval != vsync) {
2119 return SDL_Unsupported();
2120 }
2121 return true;
2122}
2123
2124/*************************************************************************************************
2125 * Renderer instantiation *
2126 *************************************************************************************************/
2127
2128static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
2129{
2130 GLES2_RenderData *data = NULL;
2131 SDL_WindowFlags window_flags = 0; // -Wconditional-uninitialized
2132 GLint window_framebuffer;
2133 GLint value;
2134 int profile_mask = 0, major = 0, minor = 0;
2135 bool changed_window = false;
2136
2137 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask)) {
2138 goto error;
2139 }
2140 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major)) {
2141 goto error;
2142 }
2143 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor)) {
2144 goto error;
2145 }
2146
2147 SDL_SyncWindow(window);
2148 window_flags = SDL_GetWindowFlags(window);
2149
2150 // OpenGL ES 3.0 is a superset of OpenGL ES 2.0
2151 if (!(window_flags & SDL_WINDOW_OPENGL) ||
2152 profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major < RENDERER_CONTEXT_MAJOR) {
2153
2154 changed_window = true;
2155 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
2156 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
2157 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
2158
2159 if (!SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) {
2160 goto error;
2161 }
2162 }
2163
2164 SDL_SetupRendererColorspace(renderer, create_props);
2165
2166 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
2167 SDL_SetError("Unsupported output colorspace");
2168 goto error;
2169 }
2170
2171 data = (GLES2_RenderData *)SDL_calloc(1, sizeof(GLES2_RenderData));
2172 if (!data) {
2173 goto error;
2174 }
2175 renderer->internal = data;
2176 GLES2_InvalidateCachedState(renderer);
2177 renderer->window = window;
2178
2179 renderer->name = GLES2_RenderDriver.name;
2180 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32);
2181 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32);
2182 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX32);
2183 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX32);
2184
2185 // Create an OpenGL ES 2.0 context
2186 data->context = SDL_GL_CreateContext(window);
2187 if (!data->context) {
2188 goto error;
2189 }
2190 if (!SDL_GL_MakeCurrent(window, data->context)) {
2191 goto error;
2192 }
2193
2194 if (!GLES2_LoadFunctions(data)) {
2195 goto error;
2196 }
2197
2198 if (!GLES2_CacheShaders(data)) {
2199 goto error;
2200 }
2201
2202 // Check for debug output support
2203 if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) &&
2204 (value & SDL_GL_CONTEXT_DEBUG_FLAG)) {
2205 data->debug_enabled = true;
2206 }
2207
2208 value = 0;
2209 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
2210 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, value);
2211
2212#if USE_VERTEX_BUFFER_OBJECTS
2213 // we keep a few of these and cycle through them, so data can live for a few frames.
2214 data->glGenBuffers(SDL_arraysize(data->vertex_buffers), data->vertex_buffers);
2215#endif
2216
2217 data->framebuffers = NULL;
2218 data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer);
2219 data->window_framebuffer = (GLuint)window_framebuffer;
2220
2221 // Populate the function pointers for the module
2222 renderer->WindowEvent = GLES2_WindowEvent;
2223 renderer->SupportsBlendMode = GLES2_SupportsBlendMode;
2224 renderer->CreateTexture = GLES2_CreateTexture;
2225 renderer->UpdateTexture = GLES2_UpdateTexture;
2226#ifdef SDL_HAVE_YUV
2227 renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV;
2228 renderer->UpdateTextureNV = GLES2_UpdateTextureNV;
2229#endif
2230 renderer->LockTexture = GLES2_LockTexture;
2231 renderer->UnlockTexture = GLES2_UnlockTexture;
2232 renderer->SetRenderTarget = GLES2_SetRenderTarget;
2233 renderer->QueueSetViewport = GLES2_QueueNoOp;
2234 renderer->QueueSetDrawColor = GLES2_QueueNoOp;
2235 renderer->QueueDrawPoints = GLES2_QueueDrawPoints;
2236 renderer->QueueDrawLines = GLES2_QueueDrawLines;
2237 renderer->QueueGeometry = GLES2_QueueGeometry;
2238 renderer->InvalidateCachedState = GLES2_InvalidateCachedState;
2239 renderer->RunCommandQueue = GLES2_RunCommandQueue;
2240 renderer->RenderReadPixels = GLES2_RenderReadPixels;
2241 renderer->RenderPresent = GLES2_RenderPresent;
2242 renderer->DestroyTexture = GLES2_DestroyTexture;
2243 renderer->DestroyRenderer = GLES2_DestroyRenderer;
2244 renderer->SetVSync = GLES2_SetVSync;
2245#ifdef SDL_HAVE_YUV
2246 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
2247 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
2248 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
2249 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
2250#endif
2251#ifdef GL_TEXTURE_EXTERNAL_OES
2252 if (GLES2_CacheShader(data, GLES2_SHADER_FRAGMENT_TEXTURE_EXTERNAL_OES, GL_FRAGMENT_SHADER)) {
2253 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_EXTERNAL_OES);
2254 }
2255#endif
2256
2257 if (SDL_GL_ExtensionSupported("GL_EXT_blend_minmax")) {
2258 data->GL_EXT_blend_minmax_supported = true;
2259 }
2260
2261 // Set up parameters for rendering
2262 data->glDisable(GL_DEPTH_TEST);
2263 data->glDisable(GL_CULL_FACE);
2264 data->glActiveTexture(GL_TEXTURE0);
2265 data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
2266 data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
2267
2268 data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_POSITION);
2269 data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_COLOR);
2270 data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_TEXCOORD);
2271
2272 data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
2273
2274 data->drawstate.clear_color.r = 1.0f;
2275 data->drawstate.clear_color.g = 1.0f;
2276 data->drawstate.clear_color.b = 1.0f;
2277 data->drawstate.clear_color.a = 1.0f;
2278 data->drawstate.projection[3][0] = -1.0f;
2279 data->drawstate.projection[3][3] = 1.0f;
2280
2281 GL_CheckError("", renderer);
2282
2283 return true;
2284
2285error:
2286 if (changed_window) {
2287 // Uh oh, better try to put it back...
2288 char *error = SDL_strdup(SDL_GetError());
2289 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
2290 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
2291 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
2292 SDL_RecreateWindow(window, window_flags);
2293 SDL_SetError("%s", error);
2294 SDL_free(error);
2295 }
2296 return false;
2297}
2298
2299SDL_RenderDriver GLES2_RenderDriver = {
2300 GLES2_CreateRenderer, "opengles2"
2301};
2302
2303#endif // SDL_VIDEO_RENDER_OGL_ES2
2304