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