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