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