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