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