Atlas - SDL_render_gl.c

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