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