Atlas - SDL_render_gl.c
Home / ext / SDL / src / render / opengl Lines: 1 | Size: 78389 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_RENDER_OGL 24#include "../../video/SDL_sysvideo.h" // For SDL_RecreateWindow 25#include <SDL3/SDL_opengl.h> 26#include "../SDL_sysrender.h" 27#include "SDL_shaders_gl.h" 28#include "../../video/SDL_pixels_c.h" 29 30#ifdef SDL_PLATFORM_MACOS 31#include <OpenGL/OpenGL.h> 32#endif 33 34#ifdef SDL_VIDEO_VITA_PVR_OGL 35#include <GL/gl.h> 36#include <GL/glext.h> 37#endif 38 39/* To prevent unnecessary window recreation, 40 * these should match the defaults selected in SDL_GL_ResetAttributes 41 */ 42 43#define RENDERER_CONTEXT_MAJOR 2 44#define RENDERER_CONTEXT_MINOR 1 45 46// This is always the same number between the various EXT/ARB/GLES extensions. 47#ifndef GL_FRAMEBUFFER_SRGB 48#define GL_FRAMEBUFFER_SRGB 0x8DB9 49#endif 50 51// OpenGL renderer implementation 52 53/* Details on optimizing the texture path on macOS: 54 http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html 55*/ 56 57typedef struct GL_FBOList GL_FBOList; 58 59struct GL_FBOList 60{ 61 Uint32 w, h; 62 GLuint FBO; 63 GL_FBOList *next; 64}; 65 66typedef struct 67{ 68 bool viewport_dirty; 69 SDL_Rect viewport; 70 SDL_Texture *texture; 71 SDL_Texture *target; 72 int drawablew; 73 int drawableh; 74 SDL_BlendMode blend; 75 GL_Shader shader; 76 float texel_size[4]; 77 const float *shader_params; 78 bool cliprect_enabled_dirty; 79 bool cliprect_enabled; 80 bool cliprect_dirty; 81 SDL_Rect cliprect; 82 bool texturing; 83 bool texturing_dirty; 84 bool vertex_array; 85 bool color_array; 86 bool texture_array; 87 bool color_dirty; 88 SDL_FColor color; 89 bool clear_color_dirty; 90 SDL_FColor clear_color; 91} GL_DrawStateCache; 92 93typedef struct 94{ 95 SDL_GLContext context; 96 97 bool debug_enabled; 98 bool GL_ARB_debug_output_supported; 99 bool pixelart_supported; 100 int errors; 101 char **error_messages; 102 GLDEBUGPROCARB next_error_callback; 103 GLvoid *next_error_userparam; 104 105 GLenum textype; 106 107 bool GL_ARB_texture_non_power_of_two_supported; 108 bool GL_ARB_texture_rectangle_supported; 109 bool GL_EXT_framebuffer_object_supported; 110 GL_FBOList *framebuffers; 111 112 // OpenGL functions 113#define SDL_PROC(ret, func, params) ret (APIENTRY *func) params; 114#include "SDL_glfuncs.h" 115#undef SDL_PROC 116 117 // Multitexture support 118 bool GL_ARB_multitexture_supported; 119 PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; 120 GLint num_texture_units; 121 122 PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT; 123 PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT; 124 PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT; 125 PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT; 126 PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT; 127 128 // Shader support 129 GL_ShaderContext *shaders; 130 131 GL_DrawStateCache drawstate; 132} GL_RenderData; 133 134typedef struct 135{ 136 GLuint texture; 137} GL_PaletteData; 138 139typedef struct 140{ 141 GLuint texture; 142 bool texture_external; 143 GLfloat texw; 144 GLfloat texh; 145 GLenum format; 146 GLenum formattype; 147 GL_Shader shader; 148 float texel_size[4]; 149 const float *shader_params; 150 void *pixels; 151 int pitch; 152 SDL_Rect locked_rect; 153#ifdef SDL_HAVE_YUV 154 // YUV texture support 155 bool yuv; 156 bool nv12; 157 GLuint utexture; 158 bool utexture_external; 159 GLuint vtexture; 160 bool vtexture_external; 161#endif 162 SDL_ScaleMode texture_scale_mode; 163 SDL_TextureAddressMode texture_address_mode_u; 164 SDL_TextureAddressMode texture_address_mode_v; 165 GL_FBOList *fbo; 166} GL_TextureData; 167 168static const char *GL_TranslateError(GLenum error) 169{ 170#define GL_ERROR_TRANSLATE(e) \ 171 case e: \ 172 return #e; 173 switch (error) { 174 GL_ERROR_TRANSLATE(GL_INVALID_ENUM) 175 GL_ERROR_TRANSLATE(GL_INVALID_VALUE) 176 GL_ERROR_TRANSLATE(GL_INVALID_OPERATION) 177 GL_ERROR_TRANSLATE(GL_OUT_OF_MEMORY) 178 GL_ERROR_TRANSLATE(GL_NO_ERROR) 179 GL_ERROR_TRANSLATE(GL_STACK_OVERFLOW) 180 GL_ERROR_TRANSLATE(GL_STACK_UNDERFLOW) 181 GL_ERROR_TRANSLATE(GL_TABLE_TOO_LARGE) 182 default: 183 return "UNKNOWN"; 184 } 185#undef GL_ERROR_TRANSLATE 186} 187 188static void GL_ClearErrors(SDL_Renderer *renderer) 189{ 190 GL_RenderData *data = (GL_RenderData *)renderer->internal; 191 192 if (!data->debug_enabled) { 193 return; 194 } 195 if (data->GL_ARB_debug_output_supported) { 196 if (data->errors) { 197 int i; 198 for (i = 0; i < data->errors; ++i) { 199 SDL_free(data->error_messages[i]); 200 } 201 SDL_free(data->error_messages); 202 203 data->errors = 0; 204 data->error_messages = NULL; 205 } 206 } else if (data->glGetError) { 207 while (data->glGetError() != GL_NO_ERROR) { 208 // continue; 209 } 210 } 211} 212 213static bool GL_CheckAllErrors(const char *prefix, SDL_Renderer *renderer, const char *file, int line, const char *function) 214{ 215 GL_RenderData *data = (GL_RenderData *)renderer->internal; 216 bool result = true; 217 218 if (!data->debug_enabled) { 219 return true; 220 } 221 if (data->GL_ARB_debug_output_supported) { 222 if (data->errors) { 223 int i; 224 for (i = 0; i < data->errors; ++i) { 225 SDL_SetError("%s: %s (%d): %s %s", prefix, file, line, function, data->error_messages[i]); 226 result = false; 227 } 228 GL_ClearErrors(renderer); 229 } 230 } else { 231 // check gl errors (can return multiple errors) 232 for (;;) { 233 GLenum error = data->glGetError(); 234 if (error != GL_NO_ERROR) { 235 if (prefix == NULL || prefix[0] == '\0') { 236 prefix = "generic"; 237 } 238 SDL_SetError("%s: %s (%d): %s %s (0x%X)", prefix, file, line, function, GL_TranslateError(error), error); 239 result = false; 240 } else { 241 break; 242 } 243 } 244 } 245 return result; 246} 247 248#if 0 249#define GL_CheckError(prefix, renderer) 250#else 251#define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, "SDL_render_gl.c", SDL_LINE, SDL_FUNCTION) 252#endif 253 254static bool GL_LoadFunctions(GL_RenderData *data) 255{ 256#ifdef __SDL_NOGETPROCADDR__ 257#define SDL_PROC(ret, func, params) data->func = func; 258#else 259 bool result = true; 260#define SDL_PROC(ret, func, params) \ 261 do { \ 262 data->func = (ret (APIENTRY *) params)SDL_GL_GetProcAddress(#func); \ 263 if (!data->func) { \ 264 result = SDL_SetError("Couldn't load GL function %s: %s", #func, SDL_GetError()); \ 265 } \ 266 } while (0); 267#endif // __SDL_NOGETPROCADDR__ 268 269#include "SDL_glfuncs.h" 270#undef SDL_PROC 271 return result; 272} 273 274static bool GL_ActivateRenderer(SDL_Renderer *renderer) 275{ 276 GL_RenderData *data = (GL_RenderData *)renderer->internal; 277 278 if (SDL_GL_GetCurrentContext() != data->context) { 279 if (!SDL_GL_MakeCurrent(renderer->window, data->context)) { 280 return false; 281 } 282 } 283 284 GL_ClearErrors(renderer); 285 286 return true; 287} 288 289static void APIENTRY GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam) 290{ 291 SDL_Renderer *renderer = (SDL_Renderer *)userParam; 292 GL_RenderData *data = (GL_RenderData *)renderer->internal; 293 294 if (type == GL_DEBUG_TYPE_ERROR_ARB) { 295 // Record this error 296 int errors = data->errors + 1; 297 char **error_messages = (char **)SDL_realloc(data->error_messages, errors * sizeof(*data->error_messages)); 298 if (error_messages) { 299 data->errors = errors; 300 data->error_messages = error_messages; 301 data->error_messages[data->errors - 1] = SDL_strdup(message); 302 } 303 } 304 305 // If there's another error callback, pass it along, otherwise log it 306 if (data->next_error_callback) { 307 data->next_error_callback(source, type, id, severity, length, message, data->next_error_userparam); 308 } else { 309 if (type == GL_DEBUG_TYPE_ERROR_ARB) { 310 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", message); 311 } else { 312 SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "%s", message); 313 } 314 } 315} 316 317static GL_FBOList *GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h) 318{ 319 GL_FBOList *result = data->framebuffers; 320 321 while (result && ((result->w != w) || (result->h != h))) { 322 result = result->next; 323 } 324 325 if (!result) { 326 result = (GL_FBOList *)SDL_malloc(sizeof(GL_FBOList)); 327 if (result) { 328 result->w = w; 329 result->h = h; 330 data->glGenFramebuffersEXT(1, &result->FBO); 331 result->next = data->framebuffers; 332 data->framebuffers = result; 333 } 334 } 335 return result; 336} 337 338static void GL_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) 339{ 340 /* If the window x/y/w/h changed at all, assume the viewport has been 341 * changed behind our backs. x/y changes might seem weird but viewport 342 * resets have been observed on macOS at minimum! 343 */ 344 if (event->type == SDL_EVENT_WINDOW_RESIZED || 345 event->type == SDL_EVENT_WINDOW_MOVED) { 346 GL_RenderData *data = (GL_RenderData *)renderer->internal; 347 data->drawstate.viewport_dirty = true; 348 } 349} 350 351static GLenum GetBlendFunc(SDL_BlendFactor factor) 352{ 353 switch (factor) { 354 case SDL_BLENDFACTOR_ZERO: 355 return GL_ZERO; 356 case SDL_BLENDFACTOR_ONE: 357 return GL_ONE; 358 case SDL_BLENDFACTOR_SRC_COLOR: 359 return GL_SRC_COLOR; 360 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: 361 return GL_ONE_MINUS_SRC_COLOR; 362 case SDL_BLENDFACTOR_SRC_ALPHA: 363 return GL_SRC_ALPHA; 364 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: 365 return GL_ONE_MINUS_SRC_ALPHA; 366 case SDL_BLENDFACTOR_DST_COLOR: 367 return GL_DST_COLOR; 368 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: 369 return GL_ONE_MINUS_DST_COLOR; 370 case SDL_BLENDFACTOR_DST_ALPHA: 371 return GL_DST_ALPHA; 372 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: 373 return GL_ONE_MINUS_DST_ALPHA; 374 default: 375 return GL_INVALID_ENUM; 376 } 377} 378 379static GLenum GetBlendEquation(SDL_BlendOperation operation) 380{ 381 switch (operation) { 382 case SDL_BLENDOPERATION_ADD: 383 return GL_FUNC_ADD; 384 case SDL_BLENDOPERATION_SUBTRACT: 385 return GL_FUNC_SUBTRACT; 386 case SDL_BLENDOPERATION_REV_SUBTRACT: 387 return GL_FUNC_REVERSE_SUBTRACT; 388 case SDL_BLENDOPERATION_MINIMUM: 389 return GL_MIN; 390 case SDL_BLENDOPERATION_MAXIMUM: 391 return GL_MAX; 392 default: 393 return GL_INVALID_ENUM; 394 } 395} 396 397static bool GL_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) 398{ 399 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); 400 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); 401 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); 402 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); 403 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); 404 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); 405 406 if (GetBlendFunc(srcColorFactor) == GL_INVALID_ENUM || 407 GetBlendFunc(srcAlphaFactor) == GL_INVALID_ENUM || 408 GetBlendEquation(colorOperation) == GL_INVALID_ENUM || 409 GetBlendFunc(dstColorFactor) == GL_INVALID_ENUM || 410 GetBlendFunc(dstAlphaFactor) == GL_INVALID_ENUM || 411 GetBlendEquation(alphaOperation) == GL_INVALID_ENUM) { 412 return false; 413 } 414 if (colorOperation != alphaOperation) { 415 return false; 416 } 417 return true; 418} 419 420static bool convert_format(Uint32 pixel_format, GLint *internalFormat, GLenum *format, GLenum *type) 421{ 422 switch (pixel_format) { 423 case SDL_PIXELFORMAT_BGRA32: 424 case SDL_PIXELFORMAT_BGRX32: 425 *internalFormat = GL_RGBA8; 426 *format = GL_BGRA; 427 *type = GL_UNSIGNED_BYTE; // previously GL_UNSIGNED_INT_8_8_8_8_REV, seeing if this is better in modern times. 428 break; 429 case SDL_PIXELFORMAT_RGBA32: 430 case SDL_PIXELFORMAT_RGBX32: 431 *internalFormat = GL_RGBA8; 432 *format = GL_RGBA; 433 *type = GL_UNSIGNED_BYTE; // previously GL_UNSIGNED_INT_8_8_8_8_REV, seeing if this is better in modern times. 434 break; 435 case SDL_PIXELFORMAT_INDEX8: 436 case SDL_PIXELFORMAT_YV12: 437 case SDL_PIXELFORMAT_IYUV: 438 case SDL_PIXELFORMAT_NV12: 439 case SDL_PIXELFORMAT_NV21: 440 *internalFormat = GL_LUMINANCE; 441 *format = GL_LUMINANCE; 442 *type = GL_UNSIGNED_BYTE; 443 break; 444#ifdef SDL_PLATFORM_MACOS 445 case SDL_PIXELFORMAT_UYVY: 446 *internalFormat = GL_RGB8; 447 *format = GL_YCBCR_422_APPLE; 448 *type = GL_UNSIGNED_SHORT_8_8_APPLE; 449 break; 450#endif 451 default: 452 return false; 453 } 454 return true; 455} 456 457static bool SetTextureScaleMode(GL_RenderData *data, GLenum textype, SDL_PixelFormat format, SDL_ScaleMode scaleMode) 458{ 459 switch (scaleMode) { 460 case SDL_SCALEMODE_NEAREST: 461 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 462 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 463 break; 464 case SDL_SCALEMODE_PIXELART: // Uses linear sampling if supported 465 if (!data->pixelart_supported) { 466 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 467 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 468 break; 469 } 470 SDL_FALLTHROUGH; 471 case SDL_SCALEMODE_LINEAR: 472 if (format == SDL_PIXELFORMAT_INDEX8) { 473 // We'll do linear sampling in the shader 474 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 475 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 476 } else { 477 data->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 478 data->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 479 } 480 break; 481 default: 482 return SDL_SetError("Unknown texture scale mode: %d", scaleMode); 483 } 484 return true; 485} 486 487static GLint TranslateAddressMode(SDL_TextureAddressMode addressMode) 488{ 489 switch (addressMode) { 490 case SDL_TEXTURE_ADDRESS_CLAMP: 491 return GL_CLAMP_TO_EDGE; 492 case SDL_TEXTURE_ADDRESS_WRAP: 493 return GL_REPEAT; 494 default: 495 SDL_assert(!"Unknown texture address mode"); 496 return GL_CLAMP_TO_EDGE; 497 } 498} 499 500static void SetTextureAddressMode(GL_RenderData *data, GLenum textype, SDL_TextureAddressMode addressModeU, SDL_TextureAddressMode addressModeV) 501{ 502 data->glTexParameteri(textype, GL_TEXTURE_WRAP_S, TranslateAddressMode(addressModeU)); 503 data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, TranslateAddressMode(addressModeV)); 504} 505 506static bool GL_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 507{ 508 GL_RenderData *data = (GL_RenderData *)renderer->internal; 509 GL_PaletteData *palettedata = (GL_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); 510 if (!palettedata) { 511 return false; 512 } 513 palette->internal = palettedata; 514 515 data->drawstate.texture = NULL; // we trash this state. 516 517 const GLenum textype = data->textype; 518 data->glGenTextures(1, &palettedata->texture); 519 data->glBindTexture(textype, palettedata->texture); 520 data->glTexImage2D(textype, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 521 if (!GL_CheckError("glTexImage2D()", renderer)) { 522 return false; 523 } 524 SetTextureScaleMode(data, textype, SDL_PIXELFORMAT_UNKNOWN, SDL_SCALEMODE_NEAREST); 525 SetTextureAddressMode(data, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 526 return true; 527} 528 529static bool GL_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) 530{ 531 GL_RenderData *data = (GL_RenderData *)renderer->internal; 532 GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal; 533 534 GL_ActivateRenderer(renderer); 535 536 data->drawstate.texture = NULL; // we trash this state. 537 538 const GLenum textype = data->textype; 539 data->glBindTexture(textype, palettedata->texture); 540 data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 541 data->glPixelStorei(GL_UNPACK_ROW_LENGTH, ncolors); 542 data->glTexSubImage2D(textype, 0, 0, 0, ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, colors); 543 544 return GL_CheckError("glTexSubImage2D()", renderer); 545} 546 547static void GL_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) 548{ 549 GL_RenderData *data = (GL_RenderData *)renderer->internal; 550 GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal; 551 552 if (palettedata) { 553 data->glDeleteTextures(1, &palettedata->texture); 554 SDL_free(palettedata); 555 } 556} 557 558static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) 559{ 560 GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; 561 const GLenum textype = renderdata->textype; 562 GL_TextureData *data; 563 GLint internalFormat; 564 GLenum format, type; 565 int texture_w, texture_h; 566 567 GL_ActivateRenderer(renderer); 568 569 renderdata->drawstate.texture = NULL; // we trash this state. 570 renderdata->drawstate.texturing_dirty = true; // we trash this state. 571 572 if (texture->access == SDL_TEXTUREACCESS_TARGET && 573 !renderdata->GL_EXT_framebuffer_object_supported) { 574 return SDL_SetError("Render targets not supported by OpenGL"); 575 } 576 577 if (!convert_format(texture->format, &internalFormat, &format, &type)) { 578 return SDL_SetError("Texture format %s not supported by OpenGL", 579 SDL_GetPixelFormatName(texture->format)); 580 } 581 582 data = (GL_TextureData *)SDL_calloc(1, sizeof(*data)); 583 if (!data) { 584 return false; 585 } 586 587 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 588 size_t size; 589 data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format); 590 size = (size_t)texture->h * data->pitch; 591 if (texture->format == SDL_PIXELFORMAT_YV12 || 592 texture->format == SDL_PIXELFORMAT_IYUV) { 593 // Need to add size for the U and V planes 594 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); 595 } 596 if (texture->format == SDL_PIXELFORMAT_NV12 || 597 texture->format == SDL_PIXELFORMAT_NV21) { 598 // Need to add size for the U/V plane 599 size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2); 600 } 601 data->pixels = SDL_calloc(1, size); 602 if (!data->pixels) { 603 SDL_free(data); 604 return false; 605 } 606 } 607 608 if (texture->access == SDL_TEXTUREACCESS_TARGET) { 609 data->fbo = GL_GetFBO(renderdata, texture->w, texture->h); 610 } else { 611 data->fbo = NULL; 612 } 613 614 data->texture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER, 0); 615 if (data->texture) { 616 data->texture_external = true; 617 } else { 618 GL_CheckError("", renderer); 619 renderdata->glGenTextures(1, &data->texture); 620 if (!GL_CheckError("glGenTextures()", renderer)) { 621 SDL_free(data->pixels); 622 SDL_free(data); 623 return false; 624 } 625 } 626 texture->internal = data; 627 628 if (renderdata->GL_ARB_texture_non_power_of_two_supported) { 629 texture_w = texture->w; 630 texture_h = texture->h; 631 data->texw = 1.0f; 632 data->texh = 1.0f; 633 } else if (renderdata->GL_ARB_texture_rectangle_supported) { 634 texture_w = texture->w; 635 texture_h = texture->h; 636 data->texw = (GLfloat)texture_w; 637 data->texh = (GLfloat)texture_h; 638 } else { 639 texture_w = SDL_powerof2(texture->w); 640 texture_h = SDL_powerof2(texture->h); 641 data->texw = (GLfloat)(texture->w) / texture_w; 642 data->texh = (GLfloat)texture->h / texture_h; 643 } 644 SDL_PropertiesID props = SDL_GetTextureProperties(texture); 645 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, data->texture); 646 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, (Sint64) textype); 647 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_OPENGL_TEX_W_FLOAT, data->texw); 648 SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_OPENGL_TEX_H_FLOAT, data->texh); 649 650 data->format = format; 651 data->formattype = type; 652 data->texture_scale_mode = texture->scaleMode; 653 data->texture_address_mode_u = SDL_TEXTURE_ADDRESS_CLAMP; 654 data->texture_address_mode_v = SDL_TEXTURE_ADDRESS_CLAMP; 655 renderdata->glEnable(textype); 656 renderdata->glBindTexture(textype, data->texture); 657#ifdef SDL_PLATFORM_MACOS 658#ifndef GL_TEXTURE_STORAGE_HINT_APPLE 659#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC 660#endif 661#ifndef STORAGE_CACHED_APPLE 662#define STORAGE_CACHED_APPLE 0x85BE 663#endif 664#ifndef STORAGE_SHARED_APPLE 665#define STORAGE_SHARED_APPLE 0x85BF 666#endif 667 if (texture->access == SDL_TEXTUREACCESS_STREAMING) { 668 renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE, 669 GL_STORAGE_SHARED_APPLE); 670 } else { 671 renderdata->glTexParameteri(textype, GL_TEXTURE_STORAGE_HINT_APPLE, 672 GL_STORAGE_CACHED_APPLE); 673 } 674 if (texture->access == SDL_TEXTUREACCESS_STREAMING && texture->format == SDL_PIXELFORMAT_ARGB8888 && (texture->w % 8) == 0) { 675 renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); 676 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 677 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, 678 (data->pitch / SDL_BYTESPERPIXEL(texture->format))); 679 renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, 680 texture_h, 0, format, type, data->pixels); 681 renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE); 682 } else 683#endif 684 { 685 renderdata->glTexImage2D(textype, 0, internalFormat, texture_w, 686 texture_h, 0, format, type, NULL); 687 } 688 if (!GL_CheckError("glTexImage2D()", renderer)) { 689 return false; 690 } 691 SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode); 692 SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); 693 694#ifdef SDL_HAVE_YUV 695 if (texture->format == SDL_PIXELFORMAT_YV12 || 696 texture->format == SDL_PIXELFORMAT_IYUV) { 697 data->yuv = true; 698 699 data->utexture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER, 0); 700 if (data->utexture) { 701 data->utexture_external = true; 702 } else { 703 renderdata->glGenTextures(1, &data->utexture); 704 } 705 data->vtexture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER, 0); 706 if (data->vtexture) { 707 data->vtexture_external = true; 708 } else { 709 renderdata->glGenTextures(1, &data->vtexture); 710 } 711 712 renderdata->glBindTexture(textype, data->utexture); 713 renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w + 1) / 2, 714 (texture_h + 1) / 2, 0, format, type, NULL); 715 SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode); 716 SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); 717 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER, data->utexture); 718 719 renderdata->glBindTexture(textype, data->vtexture); 720 renderdata->glTexImage2D(textype, 0, internalFormat, (texture_w + 1) / 2, 721 (texture_h + 1) / 2, 0, format, type, NULL); 722 SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode); 723 SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); 724 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER, data->vtexture); 725 } 726 727 if (texture->format == SDL_PIXELFORMAT_NV12 || 728 texture->format == SDL_PIXELFORMAT_NV21) { 729 data->nv12 = true; 730 731 data->utexture = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER, 0); 732 if (data->utexture) { 733 data->utexture_external = true; 734 } else { 735 renderdata->glGenTextures(1, &data->utexture); 736 } 737 renderdata->glBindTexture(textype, data->utexture); 738 renderdata->glTexImage2D(textype, 0, GL_LUMINANCE_ALPHA, (texture_w + 1) / 2, 739 (texture_h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL); 740 SetTextureScaleMode(renderdata, textype, texture->format, data->texture_scale_mode); 741 SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); 742 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_UV_NUMBER, data->utexture); 743 } 744#endif 745 746 if (texture->format == SDL_PIXELFORMAT_INDEX8) { 747 data->shader = SHADER_PALETTE_NEAREST; 748 } else if (texture->format == SDL_PIXELFORMAT_RGBA32 || texture->format == SDL_PIXELFORMAT_BGRA32) { 749 data->shader = SHADER_RGBA; 750 } else { 751 data->shader = SHADER_RGB; 752 } 753 754 data->texel_size[0] = 1.0f / texture->w; 755 data->texel_size[1] = 1.0f / texture->h; 756 data->texel_size[2] = texture->w; 757 data->texel_size[3] = texture->h; 758 759#ifdef SDL_HAVE_YUV 760 if (data->yuv || data->nv12) { 761 if (data->yuv) { 762 data->shader = SHADER_YUV; 763 } else if (texture->format == SDL_PIXELFORMAT_NV12) { 764 if (SDL_GetHintBoolean("SDL_RENDER_OPENGL_NV12_RG_SHADER", false)) { 765 data->shader = SHADER_NV12_RG; 766 } else { 767 data->shader = SHADER_NV12_RA; 768 } 769 } else { 770 if (SDL_GetHintBoolean("SDL_RENDER_OPENGL_NV12_RG_SHADER", false)) { 771 data->shader = SHADER_NV21_RG; 772 } else { 773 data->shader = SHADER_NV21_RA; 774 } 775 } 776 data->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); 777 if (!data->shader_params) { 778 return SDL_SetError("Unsupported YUV colorspace"); 779 } 780 } 781#endif // SDL_HAVE_YUV 782 783 renderdata->glDisable(textype); 784 785 return GL_CheckError("", renderer); 786} 787 788static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, 789 const SDL_Rect *rect, const void *pixels, int pitch) 790{ 791 GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; 792 const GLenum textype = renderdata->textype; 793 GL_TextureData *data = (GL_TextureData *)texture->internal; 794 const int texturebpp = SDL_BYTESPERPIXEL(texture->format); 795 796 SDL_assert_release(texturebpp != 0); // otherwise, division by zero later. 797 798 GL_ActivateRenderer(renderer); 799 800 renderdata->drawstate.texture = NULL; // we trash this state. 801 802 renderdata->glBindTexture(textype, data->texture); 803 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 804 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp)); 805 renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, 806 rect->h, data->format, data->formattype, 807 pixels); 808#ifdef SDL_HAVE_YUV 809 if (data->yuv) { 810 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2)); 811 812 // Skip to the correct offset into the next texture 813 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); 814 if (texture->format == SDL_PIXELFORMAT_YV12) { 815 renderdata->glBindTexture(textype, data->vtexture); 816 } else { 817 renderdata->glBindTexture(textype, data->utexture); 818 } 819 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 820 (rect->w + 1) / 2, (rect->h + 1) / 2, 821 data->format, data->formattype, pixels); 822 823 // Skip to the correct offset into the next texture 824 pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); 825 if (texture->format == SDL_PIXELFORMAT_YV12) { 826 renderdata->glBindTexture(textype, data->utexture); 827 } else { 828 renderdata->glBindTexture(textype, data->vtexture); 829 } 830 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 831 (rect->w + 1) / 2, (rect->h + 1) / 2, 832 data->format, data->formattype, pixels); 833 } 834 835 if (data->nv12) { 836 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, ((pitch + 1) / 2)); 837 838 // Skip to the correct offset into the next texture 839 pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); 840 renderdata->glBindTexture(textype, data->utexture); 841 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 842 (rect->w + 1) / 2, (rect->h + 1) / 2, 843 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); 844 } 845#endif 846 return GL_CheckError("glTexSubImage2D()", renderer); 847} 848 849#ifdef SDL_HAVE_YUV 850static bool GL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, 851 const SDL_Rect *rect, 852 const Uint8 *Yplane, int Ypitch, 853 const Uint8 *Uplane, int Upitch, 854 const Uint8 *Vplane, int Vpitch) 855{ 856 GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; 857 const GLenum textype = renderdata->textype; 858 GL_TextureData *data = (GL_TextureData *)texture->internal; 859 860 GL_ActivateRenderer(renderer); 861 862 renderdata->drawstate.texture = NULL; // we trash this state. 863 864 renderdata->glBindTexture(textype, data->texture); 865 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 866 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch); 867 renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, 868 rect->h, data->format, data->formattype, 869 Yplane); 870 871 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch); 872 renderdata->glBindTexture(textype, data->utexture); 873 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 874 (rect->w + 1) / 2, (rect->h + 1) / 2, 875 data->format, data->formattype, Uplane); 876 877 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch); 878 renderdata->glBindTexture(textype, data->vtexture); 879 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 880 (rect->w + 1) / 2, (rect->h + 1) / 2, 881 data->format, data->formattype, Vplane); 882 883 return GL_CheckError("glTexSubImage2D()", renderer); 884} 885 886static bool GL_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, 887 const SDL_Rect *rect, 888 const Uint8 *Yplane, int Ypitch, 889 const Uint8 *UVplane, int UVpitch) 890{ 891 GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; 892 const GLenum textype = renderdata->textype; 893 GL_TextureData *data = (GL_TextureData *)texture->internal; 894 895 GL_ActivateRenderer(renderer); 896 897 renderdata->drawstate.texture = NULL; // we trash this state. 898 899 renderdata->glBindTexture(textype, data->texture); 900 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 901 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch); 902 renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w, 903 rect->h, data->format, data->formattype, 904 Yplane); 905 906 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, UVpitch / 2); 907 renderdata->glBindTexture(textype, data->utexture); 908 renderdata->glTexSubImage2D(textype, 0, rect->x / 2, rect->y / 2, 909 (rect->w + 1) / 2, (rect->h + 1) / 2, 910 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, UVplane); 911 912 return GL_CheckError("glTexSubImage2D()", renderer); 913} 914#endif 915 916static bool GL_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, 917 const SDL_Rect *rect, void **pixels, int *pitch) 918{ 919 GL_TextureData *data = (GL_TextureData *)texture->internal; 920 921 data->locked_rect = *rect; 922 *pixels = 923 (void *)((Uint8 *)data->pixels + rect->y * data->pitch + 924 rect->x * SDL_BYTESPERPIXEL(texture->format)); 925 *pitch = data->pitch; 926 return true; 927} 928 929static void GL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) 930{ 931 GL_TextureData *data = (GL_TextureData *)texture->internal; 932 const SDL_Rect *rect; 933 void *pixels; 934 935 rect = &data->locked_rect; 936 pixels = 937 (void *)((Uint8 *)data->pixels + rect->y * data->pitch + 938 rect->x * SDL_BYTESPERPIXEL(texture->format)); 939 GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch); 940} 941 942static bool GL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 943{ 944 GL_RenderData *data = (GL_RenderData *)renderer->internal; 945 GL_TextureData *texturedata; 946 GLenum status; 947 948 GL_ActivateRenderer(renderer); 949 950 if (!data->GL_EXT_framebuffer_object_supported) { 951 return SDL_SetError("Render targets not supported by OpenGL"); 952 } 953 954 data->drawstate.viewport_dirty = true; 955 956 if (!texture) { 957 data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 958 return true; 959 } 960 961 texturedata = (GL_TextureData *)texture->internal; 962 data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO); 963 // TODO: check if texture pixel format allows this operation 964 data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, data->textype, texturedata->texture, 0); 965 // Check FBO status 966 status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 967 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { 968 return SDL_SetError("glFramebufferTexture2DEXT() failed"); 969 } 970 return true; 971} 972 973/* !!! FIXME: all these Queue* calls set up the vertex buffer the way the immediate mode 974 !!! FIXME: renderer wants it, but this might want to operate differently if we move to 975 !!! FIXME: VBOs at some point. */ 976static bool GL_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) 977{ 978 return true; // nothing to do in this backend. 979} 980 981static bool GL_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 982{ 983 GLfloat *verts = (GLfloat *)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(GLfloat), 0, &cmd->data.draw.first); 984 int i; 985 986 if (!verts) { 987 return false; 988 } 989 990 cmd->data.draw.count = count; 991 for (i = 0; i < count; i++) { 992 *(verts++) = 0.5f + points[i].x; 993 *(verts++) = 0.5f + points[i].y; 994 } 995 996 return true; 997} 998 999static bool GL_QueueDrawLines(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) 1000{ 1001 int i; 1002 GLfloat prevx, prevy; 1003 const size_t vertlen = (sizeof(GLfloat) * 2) * count; 1004 GLfloat *verts = (GLfloat *)SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first); 1005 1006 if (!verts) { 1007 return false; 1008 } 1009 cmd->data.draw.count = count; 1010 1011 // 0.5f offset to hit the center of the pixel. 1012 prevx = 0.5f + points->x; 1013 prevy = 0.5f + points->y; 1014 *(verts++) = prevx; 1015 *(verts++) = prevy; 1016 1017 /* bump the end of each line segment out a quarter of a pixel, to provoke 1018 the diamond-exit rule. Without this, you won't just drop the last 1019 pixel of the last line segment, but you might also drop pixels at the 1020 edge of any given line segment along the way too. */ 1021 for (i = 1; i < count; i++) { 1022 const GLfloat xstart = prevx; 1023 const GLfloat ystart = prevy; 1024 const GLfloat xend = points[i].x + 0.5f; // 0.5f to hit pixel center. 1025 const GLfloat yend = points[i].y + 0.5f; 1026 // bump a little in the direction we are moving in. 1027 const GLfloat deltax = xend - xstart; 1028 const GLfloat deltay = yend - ystart; 1029 const GLfloat angle = SDL_atan2f(deltay, deltax); 1030 prevx = xend + (SDL_cosf(angle) * 0.25f); 1031 prevy = yend + (SDL_sinf(angle) * 0.25f); 1032 *(verts++) = prevx; 1033 *(verts++) = prevy; 1034 } 1035 1036 return true; 1037} 1038 1039static bool GL_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, 1040 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, 1041 int num_vertices, const void *indices, int num_indices, int size_indices, 1042 float scale_x, float scale_y) 1043{ 1044 GL_TextureData *texturedata = NULL; 1045 int i; 1046 int count = indices ? num_indices : num_vertices; 1047 GLfloat *verts; 1048 size_t sz = 2 * sizeof(GLfloat) + 4 * sizeof(GLfloat) + (texture ? 2 : 0) * sizeof(GLfloat); 1049 const float color_scale = cmd->data.draw.color_scale; 1050 1051 verts = (GLfloat *)SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first); 1052 if (!verts) { 1053 return false; 1054 } 1055 1056 if (texture) { 1057 texturedata = (GL_TextureData *)texture->internal; 1058 } 1059 1060 cmd->data.draw.count = count; 1061 size_indices = indices ? size_indices : 0; 1062 1063 for (i = 0; i < count; i++) { 1064 int j; 1065 float *xy_; 1066 SDL_FColor *col_; 1067 if (size_indices == 4) { 1068 j = ((const Uint32 *)indices)[i]; 1069 } else if (size_indices == 2) { 1070 j = ((const Uint16 *)indices)[i]; 1071 } else if (size_indices == 1) { 1072 j = ((const Uint8 *)indices)[i]; 1073 } else { 1074 j = i; 1075 } 1076 1077 xy_ = (float *)((char *)xy + j * xy_stride); 1078 1079 *(verts++) = xy_[0] * scale_x; 1080 *(verts++) = xy_[1] * scale_y; 1081 1082 col_ = (SDL_FColor *)((char *)color + j * color_stride); 1083 *(verts++) = col_->r * color_scale; 1084 *(verts++) = col_->g * color_scale; 1085 *(verts++) = col_->b * color_scale; 1086 *(verts++) = col_->a; 1087 1088 if (texture) { 1089 float *uv_ = (float *)((char *)uv + j * uv_stride); 1090 *(verts++) = uv_[0] * texturedata->texw; 1091 *(verts++) = uv_[1] * texturedata->texh; 1092 } 1093 } 1094 return true; 1095} 1096 1097static bool SetDrawState(GL_RenderData *data, const SDL_RenderCommand *cmd, const GL_Shader shader, const float *shader_params) 1098{ 1099 const SDL_BlendMode blend = cmd->data.draw.blend; 1100 bool vertex_array; 1101 bool color_array; 1102 bool texture_array; 1103 1104 if (data->drawstate.viewport_dirty) { 1105 const bool istarget = data->drawstate.target != NULL; 1106 const SDL_Rect *viewport = &data->drawstate.viewport; 1107 data->glMatrixMode(GL_PROJECTION); 1108 data->glLoadIdentity(); 1109 data->glViewport(viewport->x, 1110 istarget ? viewport->y : (data->drawstate.drawableh - viewport->y - viewport->h), 1111 viewport->w, viewport->h); 1112 if (viewport->w && viewport->h) { 1113 data->glOrtho((GLdouble)0, (GLdouble)viewport->w, 1114 (GLdouble)(istarget ? 0 : viewport->h), 1115 (GLdouble)(istarget ? viewport->h : 0), 1116 0.0, 1.0); 1117 } 1118 data->glMatrixMode(GL_MODELVIEW); 1119 data->drawstate.viewport_dirty = false; 1120 } 1121 1122 if (data->drawstate.cliprect_enabled_dirty) { 1123 if (!data->drawstate.cliprect_enabled) { 1124 data->glDisable(GL_SCISSOR_TEST); 1125 } else { 1126 data->glEnable(GL_SCISSOR_TEST); 1127 } 1128 data->drawstate.cliprect_enabled_dirty = false; 1129 } 1130 1131 if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) { 1132 const SDL_Rect *viewport = &data->drawstate.viewport; 1133 const SDL_Rect *rect = &data->drawstate.cliprect; 1134 data->glScissor(viewport->x + rect->x, 1135 data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h, 1136 rect->w, rect->h); 1137 data->drawstate.cliprect_dirty = false; 1138 } 1139 1140 if (blend != data->drawstate.blend) { 1141 if (blend == SDL_BLENDMODE_NONE) { 1142 data->glDisable(GL_BLEND); 1143 } else { 1144 data->glEnable(GL_BLEND); 1145 data->glBlendFuncSeparate(GetBlendFunc(SDL_GetBlendModeSrcColorFactor(blend)), 1146 GetBlendFunc(SDL_GetBlendModeDstColorFactor(blend)), 1147 GetBlendFunc(SDL_GetBlendModeSrcAlphaFactor(blend)), 1148 GetBlendFunc(SDL_GetBlendModeDstAlphaFactor(blend))); 1149 data->glBlendEquation(GetBlendEquation(SDL_GetBlendModeColorOperation(blend))); 1150 } 1151 data->drawstate.blend = blend; 1152 } 1153 1154 if (data->shaders && 1155 (shader != data->drawstate.shader || shader_params != data->drawstate.shader_params)) { 1156 GL_SelectShader(data->shaders, shader, shader_params); 1157 data->drawstate.shader = shader; 1158 data->drawstate.shader_params = shader_params; 1159 } 1160 1161 if (data->drawstate.texturing_dirty || ((cmd->data.draw.texture != NULL) != data->drawstate.texturing)) { 1162 if (!cmd->data.draw.texture) { 1163 data->glDisable(data->textype); 1164 data->drawstate.texturing = false; 1165 } else { 1166 data->glEnable(data->textype); 1167 data->drawstate.texturing = true; 1168 } 1169 data->drawstate.texturing_dirty = false; 1170 } 1171 1172 vertex_array = cmd->command == SDL_RENDERCMD_DRAW_POINTS || cmd->command == SDL_RENDERCMD_DRAW_LINES || cmd->command == SDL_RENDERCMD_GEOMETRY; 1173 color_array = cmd->command == SDL_RENDERCMD_GEOMETRY; 1174 texture_array = cmd->data.draw.texture != NULL; 1175 1176 if (vertex_array != data->drawstate.vertex_array) { 1177 if (vertex_array) { 1178 data->glEnableClientState(GL_VERTEX_ARRAY); 1179 } else { 1180 data->glDisableClientState(GL_VERTEX_ARRAY); 1181 } 1182 data->drawstate.vertex_array = vertex_array; 1183 } 1184 1185 if (color_array != data->drawstate.color_array) { 1186 if (color_array) { 1187 data->glEnableClientState(GL_COLOR_ARRAY); 1188 } else { 1189 data->glDisableClientState(GL_COLOR_ARRAY); 1190 } 1191 data->drawstate.color_array = color_array; 1192 } 1193 1194 /* This is a little awkward but should avoid texcoord arrays getting into 1195 a bad state if the application is manually binding textures */ 1196 if (texture_array != data->drawstate.texture_array) { 1197 if (texture_array) { 1198 data->glEnableClientState(GL_TEXTURE_COORD_ARRAY); 1199 } else { 1200 data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); 1201 } 1202 data->drawstate.texture_array = texture_array; 1203 } 1204 1205 return true; 1206} 1207 1208static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) 1209{ 1210 SDL_Texture *texture = cmd->data.draw.texture; 1211 GL_TextureData *texturedata = (GL_TextureData *)texture->internal; 1212 const GLenum textype = data->textype; 1213 GL_Shader shader = texturedata->shader; 1214 const float *shader_params = texturedata->shader_params; 1215 1216 switch (shader) { 1217 case SHADER_PALETTE_NEAREST: 1218 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_LINEAR) { 1219 shader = SHADER_PALETTE_LINEAR; 1220 shader_params = texturedata->texel_size; 1221 } else if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART && 1222 data->pixelart_supported) { 1223 shader = SHADER_PALETTE_PIXELART; 1224 shader_params = texturedata->texel_size; 1225 } 1226 break; 1227 case SHADER_RGB: 1228 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART && 1229 data->pixelart_supported) { 1230 shader = SHADER_RGB_PIXELART; 1231 shader_params = texturedata->texel_size; 1232 } 1233 break; 1234 case SHADER_RGBA: 1235 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART && 1236 data->pixelart_supported) { 1237 shader = SHADER_RGBA_PIXELART; 1238 shader_params = texturedata->texel_size; 1239 } 1240 break; 1241 default: 1242 break; 1243 } 1244 SetDrawState(data, cmd, shader, shader_params); 1245 1246 if (texture != data->drawstate.texture) { 1247#ifdef SDL_HAVE_YUV 1248 if (texturedata->yuv) { 1249 data->glActiveTextureARB(GL_TEXTURE2_ARB); 1250 data->glBindTexture(textype, texturedata->vtexture); 1251 1252 data->glActiveTextureARB(GL_TEXTURE1_ARB); 1253 data->glBindTexture(textype, texturedata->utexture); 1254 } 1255 if (texturedata->nv12) { 1256 data->glActiveTextureARB(GL_TEXTURE1_ARB); 1257 data->glBindTexture(textype, texturedata->utexture); 1258 } 1259#endif 1260 if (texture->palette) { 1261 GL_PaletteData *palette = (GL_PaletteData *)texture->palette->internal; 1262 data->glActiveTextureARB(GL_TEXTURE1_ARB); 1263 data->glBindTexture(textype, palette->texture); 1264 } 1265 if (data->GL_ARB_multitexture_supported) { 1266 data->glActiveTextureARB(GL_TEXTURE0_ARB); 1267 } 1268 data->glBindTexture(textype, texturedata->texture); 1269 1270 data->drawstate.texture = texture; 1271 } 1272 1273 if (cmd->data.draw.texture_scale_mode != texturedata->texture_scale_mode) { 1274#ifdef SDL_HAVE_YUV 1275 if (texturedata->yuv) { 1276 data->glActiveTextureARB(GL_TEXTURE2); 1277 if (!SetTextureScaleMode(data, textype, texture->format, cmd->data.draw.texture_scale_mode)) { 1278 return false; 1279 } 1280 1281 data->glActiveTextureARB(GL_TEXTURE1); 1282 if (!SetTextureScaleMode(data, textype, texture->format, cmd->data.draw.texture_scale_mode)) { 1283 return false; 1284 } 1285 1286 data->glActiveTextureARB(GL_TEXTURE0); 1287 } else if (texturedata->nv12) { 1288 data->glActiveTextureARB(GL_TEXTURE1); 1289 if (!SetTextureScaleMode(data, textype, texture->format, cmd->data.draw.texture_scale_mode)) { 1290 return false; 1291 } 1292 1293 data->glActiveTextureARB(GL_TEXTURE0); 1294 } 1295#endif 1296 if (texture->palette) { 1297 data->glActiveTextureARB(GL_TEXTURE1); 1298 if (!SetTextureScaleMode(data, textype, SDL_PIXELFORMAT_UNKNOWN, SDL_SCALEMODE_NEAREST)) { 1299 return false; 1300 } 1301 1302 data->glActiveTextureARB(GL_TEXTURE0); 1303 } 1304 if (!SetTextureScaleMode(data, textype, texture->format, cmd->data.draw.texture_scale_mode)) { 1305 return false; 1306 } 1307 1308 texturedata->texture_scale_mode = cmd->data.draw.texture_scale_mode; 1309 } 1310 1311 if (cmd->data.draw.texture_address_mode_u != texturedata->texture_address_mode_u || 1312 cmd->data.draw.texture_address_mode_v != texturedata->texture_address_mode_v) { 1313#ifdef SDL_HAVE_YUV 1314 if (texturedata->yuv) { 1315 data->glActiveTextureARB(GL_TEXTURE2); 1316 SetTextureAddressMode(data, textype, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 1317 1318 data->glActiveTextureARB(GL_TEXTURE1); 1319 SetTextureAddressMode(data, textype, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 1320 1321 data->glActiveTextureARB(GL_TEXTURE0_ARB); 1322 } else if (texturedata->nv12) { 1323 data->glActiveTextureARB(GL_TEXTURE1); 1324 SetTextureAddressMode(data, textype, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 1325 1326 data->glActiveTextureARB(GL_TEXTURE0); 1327 } 1328#endif 1329 if (texture->palette) { 1330 data->glActiveTextureARB(GL_TEXTURE1); 1331 SetTextureAddressMode(data, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); 1332 1333 data->glActiveTextureARB(GL_TEXTURE0); 1334 } 1335 SetTextureAddressMode(data, textype, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v); 1336 1337 texturedata->texture_address_mode_u = cmd->data.draw.texture_address_mode_u; 1338 texturedata->texture_address_mode_v = cmd->data.draw.texture_address_mode_v; 1339 } 1340 1341 return true; 1342} 1343 1344static void GL_InvalidateCachedState(SDL_Renderer *renderer) 1345{ 1346 GL_DrawStateCache *cache = &((GL_RenderData *)renderer->internal)->drawstate; 1347 cache->viewport_dirty = true; 1348 cache->texture = NULL; 1349 cache->drawablew = 0; 1350 cache->drawableh = 0; 1351 cache->blend = SDL_BLENDMODE_INVALID; 1352 cache->shader = SHADER_INVALID; 1353 cache->cliprect_enabled_dirty = true; 1354 cache->cliprect_dirty = true; 1355 cache->texturing_dirty = true; 1356 cache->vertex_array = false; // !!! FIXME: this resets to false at the end of GL_RunCommandQueue, but we could cache this more aggressively. 1357 cache->color_array = false; // !!! FIXME: this resets to false at the end of GL_RunCommandQueue, but we could cache this more aggressively. 1358 cache->texture_array = false; // !!! FIXME: this resets to false at the end of GL_RunCommandQueue, but we could cache this more aggressively. 1359 cache->color_dirty = true; 1360 cache->clear_color_dirty = true; 1361} 1362 1363static bool GL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) 1364{ 1365 // !!! FIXME: it'd be nice to use a vertex buffer instead of immediate mode... 1366 GL_RenderData *data = (GL_RenderData *)renderer->internal; 1367 1368 if (!GL_ActivateRenderer(renderer)) { 1369 return false; 1370 } 1371 1372 data->drawstate.target = renderer->target; 1373 if (!data->drawstate.target) { 1374 int w, h; 1375 SDL_GetWindowSizeInPixels(renderer->window, &w, &h); 1376 if ((w != data->drawstate.drawablew) || (h != data->drawstate.drawableh)) { 1377 data->drawstate.viewport_dirty = true; // if the window dimensions changed, invalidate the current viewport, etc. 1378 data->drawstate.cliprect_dirty = true; 1379 data->drawstate.drawablew = w; 1380 data->drawstate.drawableh = h; 1381 } 1382 } 1383 1384#ifdef SDL_PLATFORM_MACOS 1385 // On macOS on older systems, the OpenGL view change and resize events aren't 1386 // necessarily synchronized, so just always reset it. 1387 // Workaround for: https://discourse.libsdl.org/t/sdl-2-0-22-prerelease/35306/6 1388 data->drawstate.viewport_dirty = true; 1389#endif 1390 1391 while (cmd) { 1392 switch (cmd->command) { 1393 case SDL_RENDERCMD_SETDRAWCOLOR: 1394 { 1395 const float r = cmd->data.color.color.r * cmd->data.color.color_scale; 1396 const float g = cmd->data.color.color.g * cmd->data.color.color_scale; 1397 const float b = cmd->data.color.color.b * cmd->data.color.color_scale; 1398 const float a = cmd->data.color.color.a; 1399 if (data->drawstate.color_dirty || 1400 (r != data->drawstate.color.r) || 1401 (g != data->drawstate.color.g) || 1402 (b != data->drawstate.color.b) || 1403 (a != data->drawstate.color.a)) { 1404 data->glColor4f(r, g, b, a); 1405 data->drawstate.color.r = r; 1406 data->drawstate.color.g = g; 1407 data->drawstate.color.b = b; 1408 data->drawstate.color.a = a; 1409 data->drawstate.color_dirty = false; 1410 } 1411 break; 1412 } 1413 1414 case SDL_RENDERCMD_SETVIEWPORT: 1415 { 1416 SDL_Rect *viewport = &data->drawstate.viewport; 1417 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { 1418 SDL_copyp(viewport, &cmd->data.viewport.rect); 1419 data->drawstate.viewport_dirty = true; 1420 data->drawstate.cliprect_dirty = true; 1421 } 1422 break; 1423 } 1424 1425 case SDL_RENDERCMD_SETCLIPRECT: 1426 { 1427 const SDL_Rect *rect = &cmd->data.cliprect.rect; 1428 if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { 1429 data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; 1430 data->drawstate.cliprect_enabled_dirty = true; 1431 } 1432 1433 if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof(*rect)) != 0) { 1434 SDL_copyp(&data->drawstate.cliprect, rect); 1435 data->drawstate.cliprect_dirty = true; 1436 } 1437 break; 1438 } 1439 1440 case SDL_RENDERCMD_CLEAR: 1441 { 1442 const float r = cmd->data.color.color.r * cmd->data.color.color_scale; 1443 const float g = cmd->data.color.color.g * cmd->data.color.color_scale; 1444 const float b = cmd->data.color.color.b * cmd->data.color.color_scale; 1445 const float a = cmd->data.color.color.a; 1446 if (data->drawstate.clear_color_dirty || 1447 (r != data->drawstate.clear_color.r) || 1448 (g != data->drawstate.clear_color.g) || 1449 (b != data->drawstate.clear_color.b) || 1450 (a != data->drawstate.clear_color.a)) { 1451 data->glClearColor(r, g, b, a); 1452 data->drawstate.clear_color.r = r; 1453 data->drawstate.clear_color.g = g; 1454 data->drawstate.clear_color.b = b; 1455 data->drawstate.clear_color.a = a; 1456 data->drawstate.clear_color_dirty = false; 1457 } 1458 1459 if (data->drawstate.cliprect_enabled || data->drawstate.cliprect_enabled_dirty) { 1460 data->glDisable(GL_SCISSOR_TEST); 1461 data->drawstate.cliprect_enabled_dirty = data->drawstate.cliprect_enabled; 1462 } 1463 1464 data->glClear(GL_COLOR_BUFFER_BIT); 1465 break; 1466 } 1467 1468 case SDL_RENDERCMD_FILL_RECTS: // unused 1469 break; 1470 1471 case SDL_RENDERCMD_COPY: // unused 1472 break; 1473 1474 case SDL_RENDERCMD_COPY_EX: // unused 1475 break; 1476 1477 case SDL_RENDERCMD_DRAW_LINES: 1478 { 1479 if (SetDrawState(data, cmd, SHADER_SOLID, NULL)) { 1480 size_t count = cmd->data.draw.count; 1481 const GLfloat *verts = (GLfloat *)(((Uint8 *)vertices) + cmd->data.draw.first); 1482 1483 // SetDrawState handles glEnableClientState. 1484 data->glVertexPointer(2, GL_FLOAT, sizeof(float) * 2, verts); 1485 1486 if (count > 2) { 1487 // joined lines cannot be grouped 1488 data->glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)count); 1489 } else { 1490 // let's group non joined lines 1491 SDL_RenderCommand *finalcmd = cmd; 1492 SDL_RenderCommand *nextcmd = cmd->next; 1493 SDL_BlendMode thisblend = cmd->data.draw.blend; 1494 1495 while (nextcmd) { 1496 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1497 if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) { 1498 break; // can't go any further on this draw call, different render command up next. 1499 } else if (nextcmd->data.draw.count != 2) { 1500 break; // can't go any further on this draw call, those are joined lines 1501 } else if (nextcmd->data.draw.blend != thisblend) { 1502 break; // can't go any further on this draw call, different blendmode copy up next. 1503 } else { 1504 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1505 count += nextcmd->data.draw.count; 1506 } 1507 nextcmd = nextcmd->next; 1508 } 1509 1510 data->glDrawArrays(GL_LINES, 0, (GLsizei)count); 1511 cmd = finalcmd; // skip any copy commands we just combined in here. 1512 } 1513 } 1514 break; 1515 } 1516 1517 case SDL_RENDERCMD_DRAW_POINTS: 1518 case SDL_RENDERCMD_GEOMETRY: 1519 { 1520 /* as long as we have the same copy command in a row, with the 1521 same texture, we can combine them all into a single draw call. */ 1522 SDL_Texture *thistexture = cmd->data.draw.texture; 1523 SDL_BlendMode thisblend = cmd->data.draw.blend; 1524 SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode; 1525 SDL_TextureAddressMode thisaddressmode_u = cmd->data.draw.texture_address_mode_u; 1526 SDL_TextureAddressMode thisaddressmode_v = cmd->data.draw.texture_address_mode_v; 1527 const SDL_RenderCommandType thiscmdtype = cmd->command; 1528 SDL_RenderCommand *finalcmd = cmd; 1529 SDL_RenderCommand *nextcmd = cmd->next; 1530 size_t count = cmd->data.draw.count; 1531 int ret; 1532 while (nextcmd) { 1533 const SDL_RenderCommandType nextcmdtype = nextcmd->command; 1534 if (nextcmdtype != thiscmdtype) { 1535 break; // can't go any further on this draw call, different render command up next. 1536 } else if (nextcmd->data.draw.texture != thistexture || 1537 nextcmd->data.draw.texture_scale_mode != thisscalemode || 1538 nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u || 1539 nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v || 1540 nextcmd->data.draw.blend != thisblend) { 1541 break; // can't go any further on this draw call, different texture/blendmode copy up next. 1542 } else { 1543 finalcmd = nextcmd; // we can combine copy operations here. Mark this one as the furthest okay command. 1544 count += nextcmd->data.draw.count; 1545 } 1546 nextcmd = nextcmd->next; 1547 } 1548 1549 if (thistexture) { 1550 ret = SetCopyState(data, cmd); 1551 } else { 1552 ret = SetDrawState(data, cmd, SHADER_SOLID, NULL); 1553 } 1554 1555 if (ret) { 1556 const GLfloat *verts = (GLfloat *)(((Uint8 *)vertices) + cmd->data.draw.first); 1557 int op = GL_TRIANGLES; // SDL_RENDERCMD_GEOMETRY 1558 if (thiscmdtype == SDL_RENDERCMD_DRAW_POINTS) { 1559 op = GL_POINTS; 1560 } 1561 1562 if (thiscmdtype == SDL_RENDERCMD_DRAW_POINTS) { 1563 // SetDrawState handles glEnableClientState. 1564 data->glVertexPointer(2, GL_FLOAT, sizeof(float) * 2, verts); 1565 } else { 1566 // SetDrawState handles glEnableClientState. 1567 if (thistexture) { 1568 data->glVertexPointer(2, GL_FLOAT, sizeof(float) * 8, verts + 0); 1569 data->glColorPointer(4, GL_FLOAT, sizeof(float) * 8, verts + 2); 1570 data->glTexCoordPointer(2, GL_FLOAT, sizeof(float) * 8, verts + 6); 1571 } else { 1572 data->glVertexPointer(2, GL_FLOAT, sizeof(float) * 6, verts + 0); 1573 data->glColorPointer(4, GL_FLOAT, sizeof(float) * 6, verts + 2); 1574 } 1575 } 1576 1577 data->glDrawArrays(op, 0, (GLsizei)count); 1578 1579 // Restore previously set color when we're done. 1580 if (thiscmdtype != SDL_RENDERCMD_DRAW_POINTS) { 1581 const float r = data->drawstate.color.r; 1582 const float g = data->drawstate.color.g; 1583 const float b = data->drawstate.color.b; 1584 const float a = data->drawstate.color.a; 1585 data->glColor4f(r, g, b, a); 1586 } 1587 } 1588 1589 cmd = finalcmd; // skip any copy commands we just combined in here. 1590 break; 1591 } 1592 1593 case SDL_RENDERCMD_NO_OP: 1594 break; 1595 } 1596 1597 cmd = cmd->next; 1598 } 1599 1600 /* Turn off vertex array state when we're done, in case external code 1601 relies on it being off. */ 1602 if (data->drawstate.vertex_array) { 1603 data->glDisableClientState(GL_VERTEX_ARRAY); 1604 data->drawstate.vertex_array = false; 1605 } 1606 if (data->drawstate.color_array) { 1607 data->glDisableClientState(GL_COLOR_ARRAY); 1608 data->drawstate.color_array = false; 1609 } 1610 if (data->drawstate.texture_array) { 1611 data->glDisableClientState(GL_TEXTURE_COORD_ARRAY); 1612 data->drawstate.texture_array = false; 1613 } 1614 1615 return GL_CheckError("", renderer); 1616} 1617 1618static SDL_Surface *GL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) 1619{ 1620 GL_RenderData *data = (GL_RenderData *)renderer->internal; 1621 SDL_PixelFormat format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_RGBA32; 1622 GLint internalFormat; 1623 GLenum targetFormat, type; 1624 SDL_Surface *surface; 1625 1626 GL_ActivateRenderer(renderer); 1627 1628 if (!convert_format(format, &internalFormat, &targetFormat, &type)) { 1629 SDL_SetError("Texture format %s not supported by OpenGL", SDL_GetPixelFormatName(format)); 1630 return NULL; 1631 } 1632 1633 surface = SDL_CreateSurface(rect->w, rect->h, format); 1634 if (!surface) { 1635 return NULL; 1636 } 1637 1638 int y = rect->y; 1639 if (!renderer->target) { 1640 int w, h; 1641 SDL_GetRenderOutputSize(renderer, &w, &h); 1642 y = (h - y) - rect->h; 1643 } 1644 1645 data->glPixelStorei(GL_PACK_ALIGNMENT, 1); 1646 data->glPixelStorei(GL_PACK_ROW_LENGTH, (surface->pitch / SDL_BYTESPERPIXEL(format))); 1647 data->glReadPixels(rect->x, y, rect->w, rect->h, targetFormat, type, surface->pixels); 1648 1649 if (!GL_CheckError("glReadPixels()", renderer)) { 1650 SDL_DestroySurface(surface); 1651 return NULL; 1652 } 1653 1654 // Flip the rows to be top-down if necessary 1655 if (!renderer->target) { 1656 SDL_FlipSurface(surface, SDL_FLIP_VERTICAL); 1657 } 1658 return surface; 1659} 1660 1661static bool GL_RenderPresent(SDL_Renderer *renderer) 1662{ 1663 GL_ActivateRenderer(renderer); 1664 1665 return SDL_GL_SwapWindow(renderer->window); 1666} 1667 1668static void GL_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) 1669{ 1670 GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; 1671 GL_TextureData *data = (GL_TextureData *)texture->internal; 1672 1673 GL_ActivateRenderer(renderer); 1674 1675 if (renderdata->drawstate.texture == texture) { 1676 renderdata->drawstate.texture = NULL; 1677 renderdata->drawstate.shader_params = NULL; 1678 } 1679 if (renderdata->drawstate.target == texture) { 1680 renderdata->drawstate.target = NULL; 1681 } 1682 1683 if (!data) { 1684 return; 1685 } 1686 if (data->texture && !data->texture_external) { 1687 renderdata->glDeleteTextures(1, &data->texture); 1688 } 1689#ifdef SDL_HAVE_YUV 1690 if (data->yuv) { 1691 if (!data->utexture_external) { 1692 renderdata->glDeleteTextures(1, &data->utexture); 1693 } 1694 if (!data->vtexture_external) { 1695 renderdata->glDeleteTextures(1, &data->vtexture); 1696 } 1697 } 1698 if (data->nv12) { 1699 if (!data->utexture_external) { 1700 renderdata->glDeleteTextures(1, &data->utexture); 1701 } 1702 } 1703#endif 1704 SDL_free(data->pixels); 1705 SDL_free(data); 1706 texture->internal = NULL; 1707} 1708 1709static void GL_DestroyRenderer(SDL_Renderer *renderer) 1710{ 1711 GL_RenderData *data = (GL_RenderData *)renderer->internal; 1712 1713 if (data) { 1714 if (data->context) { 1715 // make sure we delete the right resources! 1716 GL_ActivateRenderer(renderer); 1717 } 1718 1719 GL_ClearErrors(renderer); 1720 if (data->GL_ARB_debug_output_supported) { 1721 PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC)SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); 1722 1723 // Uh oh, we don't have a safe way of removing ourselves from the callback chain, if it changed after we set our callback. 1724 // For now, just always replace the callback with the original one 1725 glDebugMessageCallbackARBFunc(data->next_error_callback, data->next_error_userparam); 1726 } 1727 if (data->shaders) { 1728 GL_DestroyShaderContext(data->shaders); 1729 } 1730 if (data->context) { 1731 while (data->framebuffers) { 1732 GL_FBOList *nextnode = data->framebuffers->next; 1733 // delete the framebuffer object 1734 data->glDeleteFramebuffersEXT(1, &data->framebuffers->FBO); 1735 GL_CheckError("", renderer); 1736 SDL_free(data->framebuffers); 1737 data->framebuffers = nextnode; 1738 } 1739 SDL_GL_DestroyContext(data->context); 1740 } 1741 SDL_free(data); 1742 } 1743} 1744 1745static bool GL_SetVSync(SDL_Renderer *renderer, const int vsync) 1746{ 1747 int interval = 0; 1748 1749 if (!SDL_GL_SetSwapInterval(vsync)) { 1750 return false; 1751 } 1752 1753 if (!SDL_GL_GetSwapInterval(&interval)) { 1754 return false; 1755 } 1756 1757 if (interval != vsync) { 1758 return SDL_Unsupported(); 1759 } 1760 return true; 1761} 1762 1763static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props) 1764{ 1765 GL_RenderData *data = NULL; 1766 GLint value; 1767 SDL_WindowFlags window_flags = 0; 1768 int profile_mask = 0, major = 0, minor = 0; 1769 int real_major = 0, real_minor = 0; 1770 bool changed_window = false; 1771 const char *hint, *verstr; 1772 bool non_power_of_two_supported = false; 1773 bool bgra_supported = false; 1774 1775 SDL_SetupRendererColorspace(renderer, create_props); 1776 1777 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { 1778 SDL_SetError("Unsupported output colorspace"); 1779 goto error; 1780 } 1781 1782 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask)) { 1783 goto error; 1784 } 1785 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major)) { 1786 goto error; 1787 } 1788 if (!SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor)) { 1789 goto error; 1790 } 1791 1792#ifndef SDL_VIDEO_VITA_PVR_OGL 1793 SDL_SyncWindow(window); 1794 window_flags = SDL_GetWindowFlags(window); 1795 if (!(window_flags & SDL_WINDOW_OPENGL) || 1796 profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) { 1797 1798 changed_window = true; 1799 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); 1800 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR); 1801 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR); 1802 1803 if (!SDL_ReconfigureWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)) | SDL_WINDOW_OPENGL)) { 1804 goto error; 1805 } 1806 } 1807#endif 1808 1809 data = (GL_RenderData *)SDL_calloc(1, sizeof(*data)); 1810 if (!data) { 1811 goto error; 1812 } 1813 1814 renderer->WindowEvent = GL_WindowEvent; 1815 renderer->SupportsBlendMode = GL_SupportsBlendMode; 1816 renderer->CreatePalette = GL_CreatePalette; 1817 renderer->UpdatePalette = GL_UpdatePalette; 1818 renderer->DestroyPalette = GL_DestroyPalette; 1819 renderer->CreateTexture = GL_CreateTexture; 1820 renderer->UpdateTexture = GL_UpdateTexture; 1821#ifdef SDL_HAVE_YUV 1822 renderer->UpdateTextureYUV = GL_UpdateTextureYUV; 1823 renderer->UpdateTextureNV = GL_UpdateTextureNV; 1824#endif 1825 renderer->LockTexture = GL_LockTexture; 1826 renderer->UnlockTexture = GL_UnlockTexture; 1827 renderer->SetRenderTarget = GL_SetRenderTarget; 1828 renderer->QueueSetViewport = GL_QueueNoOp; 1829 renderer->QueueSetDrawColor = GL_QueueNoOp; 1830 renderer->QueueDrawPoints = GL_QueueDrawPoints; 1831 renderer->QueueDrawLines = GL_QueueDrawLines; 1832 renderer->QueueGeometry = GL_QueueGeometry; 1833 renderer->InvalidateCachedState = GL_InvalidateCachedState; 1834 renderer->RunCommandQueue = GL_RunCommandQueue; 1835 renderer->RenderReadPixels = GL_RenderReadPixels; 1836 renderer->RenderPresent = GL_RenderPresent; 1837 renderer->DestroyTexture = GL_DestroyTexture; 1838 renderer->DestroyRenderer = GL_DestroyRenderer; 1839 renderer->SetVSync = GL_SetVSync; 1840 renderer->internal = data; 1841 GL_InvalidateCachedState(renderer); 1842 renderer->window = window; 1843 1844 renderer->name = GL_RenderDriver.name; 1845 1846 SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 0); 1847 data->context = SDL_GL_CreateContext(window); 1848 if (!data->context) { 1849 goto error; 1850 } 1851 if (!SDL_GL_MakeCurrent(window, data->context)) { 1852 goto error; 1853 } 1854 1855 if (!GL_LoadFunctions(data)) { 1856 goto error; 1857 } 1858 1859#ifdef SDL_PLATFORM_MACOS 1860 // Enable multi-threaded rendering 1861 /* Disabled until Ryan finishes his VBO/PBO code... 1862 CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine); 1863 */ 1864#endif 1865 1866 // Check for debug output support 1867 if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) && 1868 (value & SDL_GL_CONTEXT_DEBUG_FLAG)) { 1869 data->debug_enabled = true; 1870 } 1871 if (data->debug_enabled && SDL_GL_ExtensionSupported("GL_ARB_debug_output")) { 1872 PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC)SDL_GL_GetProcAddress("glDebugMessageCallbackARB"); 1873 1874 data->GL_ARB_debug_output_supported = true; 1875 data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)(char *)&data->next_error_callback); 1876 data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam); 1877 glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer); 1878 1879 // Make sure our callback is called when errors actually happen 1880 data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); 1881 } 1882 1883 verstr = (const char *)data->glGetString(GL_VERSION); 1884 if (verstr) { 1885 char verbuf[16]; 1886 char *ptr; 1887 SDL_strlcpy(verbuf, verstr, sizeof(verbuf)); 1888 ptr = SDL_strchr(verbuf, '.'); 1889 if (ptr) { 1890 real_minor = SDL_atoi(ptr + 1); 1891 *ptr = '\0'; 1892 real_major = SDL_atoi(verbuf); 1893 } 1894 } 1895 1896 hint = SDL_GetHint("GL_ARB_texture_non_power_of_two"); 1897 if (!hint || *hint != '0') { 1898 if (real_major >= 2 || SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) { 1899 non_power_of_two_supported = true; 1900 } 1901 } 1902 1903 // texture-rectangle doesn't support GL_REPEAT, it has to be the full NPOT extension (or real OpenGL 2.0+) 1904 renderer->npot_texture_wrap_unsupported = !non_power_of_two_supported; 1905 1906 data->textype = GL_TEXTURE_2D; 1907 if (non_power_of_two_supported) { 1908 data->GL_ARB_texture_non_power_of_two_supported = true; 1909 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); 1910 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, value); 1911 } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") || 1912 SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { 1913 data->GL_ARB_texture_rectangle_supported = true; 1914 data->textype = GL_TEXTURE_RECTANGLE_ARB; 1915 data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value); 1916 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, value); 1917 } else { 1918 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); 1919 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, value); 1920 } 1921 1922 // Check for multitexture support 1923 if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) { 1924 data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)SDL_GL_GetProcAddress("glActiveTextureARB"); 1925 if (data->glActiveTextureARB) { 1926 data->GL_ARB_multitexture_supported = true; 1927 data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units); 1928 } 1929 } 1930 1931 // Check for texture format support 1932 hint = SDL_GetHint("GL_EXT_bgra"); 1933 if (!hint || *hint != '0') { 1934 if (real_major > 1 || (real_major == 1 && real_minor >= 2) || 1935 SDL_GL_ExtensionSupported("GL_EXT_bgra")) { 1936 bgra_supported = true; 1937 } 1938 } 1939 1940 // RGBA32 is always supported with OpenGL 1941 if (bgra_supported) { 1942 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32); // SDL_PIXELFORMAT_ARGB8888 on little endian systems 1943 } 1944 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32); 1945 1946 // Check for shader support 1947 data->shaders = GL_CreateShaderContext(); 1948 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s", 1949 data->shaders ? "ENABLED" : "DISABLED"); 1950 if (GL_SupportsShader(data->shaders, SHADER_RGB)) { 1951 if (bgra_supported) { 1952 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRX32); 1953 } 1954 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBX32); 1955 } else { 1956 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL RGB shaders not supported"); 1957 } 1958 // We support PIXELART mode using a shader 1959 if (GL_SupportsShader(data->shaders, SHADER_RGB_PIXELART) && 1960 GL_SupportsShader(data->shaders, SHADER_RGBA_PIXELART)) { 1961 data->pixelart_supported = true; 1962 } else { 1963 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL PIXELART shaders not supported"); 1964 } 1965 // We support INDEX8 textures using 2 textures and a shader 1966 if (GL_SupportsShader(data->shaders, SHADER_PALETTE_NEAREST) && 1967 GL_SupportsShader(data->shaders, SHADER_PALETTE_LINEAR) && 1968 (!data->pixelart_supported || GL_SupportsShader(data->shaders, SHADER_PALETTE_PIXELART)) && 1969 data->num_texture_units >= 2) { 1970 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); 1971 } else { 1972 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL palette shaders not supported"); 1973 } 1974#ifdef SDL_HAVE_YUV 1975 // We support YV12 textures using 3 textures and a shader 1976 if (GL_SupportsShader(data->shaders, SHADER_YUV) && 1977 data->num_texture_units >= 3) { 1978 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12); 1979 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV); 1980 } else { 1981 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL YUV not supported"); 1982 } 1983 1984 // We support NV12 textures using 2 textures and a shader 1985 if (GL_SupportsShader(data->shaders, SHADER_NV12_RA) && 1986 GL_SupportsShader(data->shaders, SHADER_NV12_RG) && 1987 GL_SupportsShader(data->shaders, SHADER_NV21_RA) && 1988 GL_SupportsShader(data->shaders, SHADER_NV21_RG) && 1989 data->num_texture_units >= 2) { 1990 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12); 1991 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21); 1992 } else { 1993 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL NV12/NV21 not supported"); 1994 } 1995#endif 1996#ifdef SDL_PLATFORM_MACOS 1997 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_UYVY); 1998#endif 1999 2000 if (SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) { 2001 data->GL_EXT_framebuffer_object_supported = true; 2002 data->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) 2003 SDL_GL_GetProcAddress("glGenFramebuffersEXT"); 2004 data->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) 2005 SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"); 2006 data->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) 2007 SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"); 2008 data->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) 2009 SDL_GL_GetProcAddress("glBindFramebufferEXT"); 2010 data->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) 2011 SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"); 2012 } else { 2013 SDL_SetError("Can't create render targets, GL_EXT_framebuffer_object not available"); 2014 goto error; 2015 } 2016 2017 if ((real_major >= 3) || SDL_GL_ExtensionSupported("GL_EXT_framebuffer_sRGB") || SDL_GL_ExtensionSupported("GL_ARB_framebuffer_sRGB")) { 2018 data->glDisable(GL_FRAMEBUFFER_SRGB); 2019 } 2020 2021 // Set up parameters for rendering 2022 data->glMatrixMode(GL_MODELVIEW); 2023 data->glLoadIdentity(); 2024 data->glDisable(GL_DEPTH_TEST); 2025 data->glDisable(GL_CULL_FACE); 2026 data->glDisable(GL_SCISSOR_TEST); 2027 data->glDisable(data->textype); 2028 data->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 2029 data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 2030 // This ended up causing video discrepancies between OpenGL and Direct3D 2031 // data->glEnable(GL_LINE_SMOOTH); 2032 2033 data->drawstate.color.r = 1.0f; 2034 data->drawstate.color.g = 1.0f; 2035 data->drawstate.color.b = 1.0f; 2036 data->drawstate.color.a = 1.0f; 2037 data->drawstate.clear_color.r = 1.0f; 2038 data->drawstate.clear_color.g = 1.0f; 2039 data->drawstate.clear_color.b = 1.0f; 2040 data->drawstate.clear_color.a = 1.0f; 2041 2042 return true; 2043 2044error: 2045 if (changed_window) { 2046 // Uh oh, better try to put it back... 2047 char *error = SDL_strdup(SDL_GetError()); 2048 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); 2049 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); 2050 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); 2051 SDL_RecreateWindow(window, window_flags); 2052 SDL_SetError("%s", error); 2053 SDL_free(error); 2054 } 2055 return false; 2056} 2057 2058SDL_RenderDriver GL_RenderDriver = { 2059 GL_CreateRenderer, "opengl" 2060}; 2061 2062#endif // SDL_VIDEO_RENDER_OGL 2063[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.