1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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_log.h"
27 #include "SDL_assert.h"
28 #include "SDL_opengl.h"
29 #include "../SDL_sysrender.h"
30 #include "SDL_shaders_gl.h"
31
32 #ifdef __MACOSX__
33 #include <OpenGL/OpenGL.h>
34 #endif
35
36 /* To prevent unnecessary window recreation,
37 * these should match the defaults selected in SDL_GL_ResetAttributes
38 */
39
40 #define RENDERER_CONTEXT_MAJOR 2
41 #define RENDERER_CONTEXT_MINOR 1
42
43 /* OpenGL renderer implementation */
44
45 /* Details on optimizing the texture path on Mac OS X:
46 http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html
47 */
48
49 /* Used to re-create the window with OpenGL capability */
50 extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
51
52 static const float inv255f = 1.0f / 255.0f;
53
54 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
55 static void GL_WindowEvent(SDL_Renderer * renderer,
56 const SDL_WindowEvent *event);
57 static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
58 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
59 static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
60 const SDL_Rect * rect, const void *pixels,
61 int pitch);
62 static int GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
63 const SDL_Rect * rect,
64 const Uint8 *Yplane, int Ypitch,
65 const Uint8 *Uplane, int Upitch,
66 const Uint8 *Vplane, int Vpitch);
67 static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
68 const SDL_Rect * rect, void **pixels, int *pitch);
69 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
70 static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
71 static int GL_UpdateViewport(SDL_Renderer * renderer);
72 static int GL_UpdateClipRect(SDL_Renderer * renderer);
73 static int GL_RenderClear(SDL_Renderer * renderer);
74 static int GL_RenderDrawPoints(SDL_Renderer * renderer,
75 const SDL_FPoint * points, int count);
76 static int GL_RenderDrawLines(SDL_Renderer * renderer,
77 const SDL_FPoint * points, int count);
78 static int GL_RenderFillRects(SDL_Renderer * renderer,
79 const SDL_FRect * rects, int count);
80 static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
81 const SDL_Rect * srcrect, const SDL_FRect * dstrect);
82 static int GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
83 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
84 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
85 static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
86 Uint32 pixel_format, void * pixels, int pitch);
87 static void GL_RenderPresent(SDL_Renderer * renderer);
88 static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
89 static void GL_DestroyRenderer(SDL_Renderer * renderer);
90 static int GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
91 static int GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture);
92
93 SDL_RenderDriver GL_RenderDriver = {
94 GL_CreateRenderer,
95 {
96 "opengl",
97 (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
98 1,
99 {SDL_PIXELFORMAT_ARGB8888},
100 0,
101 0}
102 };
103
104 typedef struct GL_FBOList GL_FBOList;
105
106 struct GL_FBOList
107 {
108 Uint32 w, h;
109 GLuint FBO;
110 GL_FBOList *next;
111 };
112
113 typedef struct
114 {
115 SDL_GLContext context;
116
117 SDL_bool debug_enabled;
118 SDL_bool GL_ARB_debug_output_supported;
119 int errors;
120 char **error_messages;
121 GLDEBUGPROCARB next_error_callback;
122 GLvoid *next_error_userparam;
123
124 SDL_bool GL_ARB_texture_non_power_of_two_supported;
125 SDL_bool GL_ARB_texture_rectangle_supported;
126 struct {
127 GL_Shader shader;
128 Uint32 color;
129 int blendMode;
130 } current;
131
132 SDL_bool GL_EXT_framebuffer_object_supported;
133 GL_FBOList *framebuffers;
134
135 /* OpenGL functions */
136 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
137 #include "SDL_glfuncs.h"
138 #undef SDL_PROC
139
140 /* Multitexture support */
141 SDL_bool GL_ARB_multitexture_supported;
142 PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
143 GLint num_texture_units;
144
145 PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
146 PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
147 PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
148 PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
149 PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
150
151 /* Shader support */
152 GL_ShaderContext *shaders;
153
154 } GL_RenderData;
155
156 typedef struct
157 {
158 GLuint texture;
159 GLenum type;
160 GLfloat texw;
161 GLfloat texh;
162 GLenum format;
163 GLenum formattype;
164 void *pixels;
165 int pitch;
166 SDL_Rect locked_rect;
167
168 /* YUV texture support */
169 SDL_bool yuv;
170 SDL_bool nv12;
171 GLuint utexture;
172 GLuint vtexture;
173
174 GL_FBOList *fbo;
175 } GL_TextureData;
176
177 SDL_FORCE_INLINE const char*
GL_TranslateError(GLenum error)178 GL_TranslateError (GLenum error)
179 {
180 #define GL_ERROR_TRANSLATE(e) case e: return #e;
181 switch (error) {
182 GL_ERROR_TRANSLATE(GL_INVALID_ENUM)
183 GL_ERROR_TRANSLATE(GL_INVALID_VALUE)
184 GL_ERROR_TRANSLATE(GL_INVALID_OPERATION)
185 GL_ERROR_TRANSLATE(GL_OUT_OF_MEMORY)
186 GL_ERROR_TRANSLATE(GL_NO_ERROR)
187 GL_ERROR_TRANSLATE(GL_STACK_OVERFLOW)
188 GL_ERROR_TRANSLATE(GL_STACK_UNDERFLOW)
189 GL_ERROR_TRANSLATE(GL_TABLE_TOO_LARGE)
190 default:
191 return "UNKNOWN";
192 }
193 #undef GL_ERROR_TRANSLATE
194 }
195
196 SDL_FORCE_INLINE void
GL_ClearErrors(SDL_Renderer * renderer)197 GL_ClearErrors(SDL_Renderer *renderer)
198 {
199 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
200
201 if (!data->debug_enabled)
202 {
203 return;
204 }
205 if (data->GL_ARB_debug_output_supported) {
206 if (data->errors) {
207 int i;
208 for (i = 0; i < data->errors; ++i) {
209 SDL_free(data->error_messages[i]);
210 }
211 SDL_free(data->error_messages);
212
213 data->errors = 0;
214 data->error_messages = NULL;
215 }
216 } else {
217 while (data->glGetError() != GL_NO_ERROR) {
218 continue;
219 }
220 }
221 }
222
223 SDL_FORCE_INLINE int
GL_CheckAllErrors(const char * prefix,SDL_Renderer * renderer,const char * file,int line,const char * function)224 GL_CheckAllErrors (const char *prefix, SDL_Renderer *renderer, const char *file, int line, const char *function)
225 {
226 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
227 int ret = 0;
228
229 if (!data->debug_enabled)
230 {
231 return 0;
232 }
233 if (data->GL_ARB_debug_output_supported) {
234 if (data->errors) {
235 int i;
236 for (i = 0; i < data->errors; ++i) {
237 SDL_SetError("%s: %s (%d): %s %s", prefix, file, line, function, data->error_messages[i]);
238 ret = -1;
239 }
240 GL_ClearErrors(renderer);
241 }
242 } else {
243 /* check gl errors (can return multiple errors) */
244 for (;;) {
245 GLenum error = data->glGetError();
246 if (error != GL_NO_ERROR) {
247 if (prefix == NULL || prefix[0] == '\0') {
248 prefix = "generic";
249 }
250 SDL_SetError("%s: %s (%d): %s %s (0x%X)", prefix, file, line, function, GL_TranslateError(error), error);
251 ret = -1;
252 } else {
253 break;
254 }
255 }
256 }
257 return ret;
258 }
259
260 #if 0
261 #define GL_CheckError(prefix, renderer)
262 #elif defined(_MSC_VER)
263 #define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __FUNCTION__)
264 #else
265 #define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __PRETTY_FUNCTION__)
266 #endif
267
268 static int
GL_LoadFunctions(GL_RenderData * data)269 GL_LoadFunctions(GL_RenderData * data)
270 {
271 #ifdef __SDL_NOGETPROCADDR__
272 #define SDL_PROC(ret,func,params) data->func=func;
273 #else
274 #define SDL_PROC(ret,func,params) \
275 do { \
276 data->func = SDL_GL_GetProcAddress(#func); \
277 if ( ! data->func ) { \
278 return SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \
279 } \
280 } while ( 0 );
281 #endif /* __SDL_NOGETPROCADDR__ */
282
283 #include "SDL_glfuncs.h"
284 #undef SDL_PROC
285 return 0;
286 }
287
288 static SDL_GLContext SDL_CurrentContext = NULL;
289
290 static int
GL_ActivateRenderer(SDL_Renderer * renderer)291 GL_ActivateRenderer(SDL_Renderer * renderer)
292 {
293 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
294
295 if (SDL_CurrentContext != data->context ||
296 SDL_GL_GetCurrentContext() != data->context) {
297 if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
298 return -1;
299 }
300 SDL_CurrentContext = data->context;
301
302 GL_UpdateViewport(renderer);
303 }
304
305 GL_ClearErrors(renderer);
306
307 return 0;
308 }
309
310 /* This is called if we need to invalidate all of the SDL OpenGL state */
311 static void
GL_ResetState(SDL_Renderer * renderer)312 GL_ResetState(SDL_Renderer *renderer)
313 {
314 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
315
316 if (SDL_GL_GetCurrentContext() == data->context) {
317 GL_UpdateViewport(renderer);
318 } else {
319 GL_ActivateRenderer(renderer);
320 }
321
322 data->current.shader = SHADER_NONE;
323 data->current.color = 0;
324 data->current.blendMode = -1;
325
326 data->glDisable(GL_DEPTH_TEST);
327 data->glDisable(GL_CULL_FACE);
328 /* This ended up causing video discrepancies between OpenGL and Direct3D */
329 /* data->glEnable(GL_LINE_SMOOTH); */
330
331 data->glMatrixMode(GL_MODELVIEW);
332 data->glLoadIdentity();
333
334 GL_CheckError("", renderer);
335 }
336
337 static void APIENTRY
GL_HandleDebugMessage(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const char * message,const void * userParam)338 GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam)
339 {
340 SDL_Renderer *renderer = (SDL_Renderer *) userParam;
341 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
342
343 if (type == GL_DEBUG_TYPE_ERROR_ARB) {
344 /* Record this error */
345 int errors = data->errors + 1;
346 char **error_messages = SDL_realloc(data->error_messages, errors * sizeof(*data->error_messages));
347 if (error_messages) {
348 data->errors = errors;
349 data->error_messages = error_messages;
350 data->error_messages[data->errors-1] = SDL_strdup(message);
351 }
352 }
353
354 /* If there's another error callback, pass it along, otherwise log it */
355 if (data->next_error_callback) {
356 data->next_error_callback(source, type, id, severity, length, message, data->next_error_userparam);
357 } else {
358 if (type == GL_DEBUG_TYPE_ERROR_ARB) {
359 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", message);
360 } else {
361 SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "%s", message);
362 }
363 }
364 }
365
366 static GL_FBOList *
GL_GetFBO(GL_RenderData * data,Uint32 w,Uint32 h)367 GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h)
368 {
369 GL_FBOList *result = data->framebuffers;
370
371 while (result && ((result->w != w) || (result->h != h))) {
372 result = result->next;
373 }
374
375 if (!result) {
376 result = SDL_malloc(sizeof(GL_FBOList));
377 if (result) {
378 result->w = w;
379 result->h = h;
380 data->glGenFramebuffersEXT(1, &result->FBO);
381 result->next = data->framebuffers;
382 data->framebuffers = result;
383 }
384 }
385 return result;
386 }
387
388 SDL_Renderer *
GL_CreateRenderer(SDL_Window * window,Uint32 flags)389 GL_CreateRenderer(SDL_Window * window, Uint32 flags)
390 {
391 SDL_Renderer *renderer;
392 GL_RenderData *data;
393 GLint value;
394 Uint32 window_flags;
395 int profile_mask = 0, major = 0, minor = 0;
396 SDL_bool changed_window = SDL_FALSE;
397
398 SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
399 SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
400 SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
401
402 window_flags = SDL_GetWindowFlags(window);
403 if (!(window_flags & SDL_WINDOW_OPENGL) ||
404 profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
405
406 changed_window = SDL_TRUE;
407 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
408 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
409 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
410
411 if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
412 goto error;
413 }
414 }
415
416 renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
417 if (!renderer) {
418 SDL_OutOfMemory();
419 goto error;
420 }
421
422 data = (GL_RenderData *) SDL_calloc(1, sizeof(*data));
423 if (!data) {
424 GL_DestroyRenderer(renderer);
425 SDL_OutOfMemory();
426 goto error;
427 }
428
429 renderer->WindowEvent = GL_WindowEvent;
430 renderer->GetOutputSize = GL_GetOutputSize;
431 renderer->CreateTexture = GL_CreateTexture;
432 renderer->UpdateTexture = GL_UpdateTexture;
433 renderer->UpdateTextureYUV = GL_UpdateTextureYUV;
434 renderer->LockTexture = GL_LockTexture;
435 renderer->UnlockTexture = GL_UnlockTexture;
436 renderer->SetRenderTarget = GL_SetRenderTarget;
437 renderer->UpdateViewport = GL_UpdateViewport;
438 renderer->UpdateClipRect = GL_UpdateClipRect;
439 renderer->RenderClear = GL_RenderClear;
440 renderer->RenderDrawPoints = GL_RenderDrawPoints;
441 renderer->RenderDrawLines = GL_RenderDrawLines;
442 renderer->RenderFillRects = GL_RenderFillRects;
443 renderer->RenderCopy = GL_RenderCopy;
444 renderer->RenderCopyEx = GL_RenderCopyEx;
445 renderer->RenderReadPixels = GL_RenderReadPixels;
446 renderer->RenderPresent = GL_RenderPresent;
447 renderer->DestroyTexture = GL_DestroyTexture;
448 renderer->DestroyRenderer = GL_DestroyRenderer;
449 renderer->GL_BindTexture = GL_BindTexture;
450 renderer->GL_UnbindTexture = GL_UnbindTexture;
451 renderer->info = GL_RenderDriver.info;
452 renderer->info.flags = SDL_RENDERER_ACCELERATED;
453 renderer->driverdata = data;
454 renderer->window = window;
455
456 data->context = SDL_GL_CreateContext(window);
457 if (!data->context) {
458 GL_DestroyRenderer(renderer);
459 goto error;
460 }
461 if (SDL_GL_MakeCurrent(window, data->context) < 0) {
462 GL_DestroyRenderer(renderer);
463 goto error;
464 }
465
466 if (GL_LoadFunctions(data) < 0) {
467 GL_DestroyRenderer(renderer);
468 goto error;
469 }
470
471 #ifdef __MACOSX__
472 /* Enable multi-threaded rendering */
473 /* Disabled until Ryan finishes his VBO/PBO code...
474 CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
475 */
476 #endif
477
478 if (flags & SDL_RENDERER_PRESENTVSYNC) {
479 SDL_GL_SetSwapInterval(1);
480 } else {
481 SDL_GL_SetSwapInterval(0);
482 }
483 if (SDL_GL_GetSwapInterval() > 0) {
484 renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
485 }
486
487 /* Check for debug output support */
488 if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) == 0 &&
489 (value & SDL_GL_CONTEXT_DEBUG_FLAG)) {
490 data->debug_enabled = SDL_TRUE;
491 }
492 if (data->debug_enabled && SDL_GL_ExtensionSupported("GL_ARB_debug_output")) {
493 PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB");
494
495 data->GL_ARB_debug_output_supported = SDL_TRUE;
496 data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)&data->next_error_callback);
497 data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam);
498 glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer);
499
500 /* Make sure our callback is called when errors actually happen */
501 data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
502 }
503
504 if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) {
505 data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE;
506 } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
507 SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
508 data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
509 }
510 if (data->GL_ARB_texture_rectangle_supported) {
511 data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value);
512 renderer->info.max_texture_width = value;
513 renderer->info.max_texture_height = value;
514 } else {
515 data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
516 renderer->info.max_texture_width = value;
517 renderer->info.max_texture_height = value;
518 }
519
520 /* Check for multitexture support */
521 if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) {
522 data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
523 if (data->glActiveTextureARB) {
524 data->GL_ARB_multitexture_supported = SDL_TRUE;
525 data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units);
526 }
527 }
528
529 /* Check for shader support */
530 if (SDL_GetHintBoolean(SDL_HINT_RENDER_OPENGL_SHADERS, SDL_TRUE)) {
531 data->shaders = GL_CreateShaderContext();
532 }
533 SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
534 data->shaders ? "ENABLED" : "DISABLED");
535
536 /* We support YV12 textures using 3 textures and a shader */
537 if (data->shaders && data->num_texture_units >= 3) {
538 renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
539 renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
540 renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV12;
541 renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21;
542 }
543
544 #ifdef __MACOSX__
545 renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_UYVY;
546 #endif
547
548 if (SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) {
549 data->GL_EXT_framebuffer_object_supported = SDL_TRUE;
550 data->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
551 SDL_GL_GetProcAddress("glGenFramebuffersEXT");
552 data->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
553 SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
554 data->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
555 SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
556 data->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
557 SDL_GL_GetProcAddress("glBindFramebufferEXT");
558 data->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
559 SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
560 renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
561 }
562 data->framebuffers = NULL;
563
564 /* Set up parameters for rendering */
565 GL_ResetState(renderer);
566
567 return renderer;
568
569 error:
570 if (changed_window) {
571 /* Uh oh, better try to put it back... */
572 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
573 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
574 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
575 SDL_RecreateWindow(window, window_flags);
576 }
577 return NULL;
578 }
579
580 static void
GL_WindowEvent(SDL_Renderer * renderer,const SDL_WindowEvent * event)581 GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
582 {
583 if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
584 event->event == SDL_WINDOWEVENT_SHOWN ||
585 event->event == SDL_WINDOWEVENT_HIDDEN) {
586 /* Rebind the context to the window area and update matrices */
587 SDL_CurrentContext = NULL;
588 }
589 }
590
591 static int
GL_GetOutputSize(SDL_Renderer * renderer,int * w,int * h)592 GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
593 {
594 SDL_GL_GetDrawableSize(renderer->window, w, h);
595 return 0;
596 }
597
598 SDL_FORCE_INLINE int
power_of_2(int input)599 power_of_2(int input)
600 {
601 int value = 1;
602
603 while (value < input) {
604 value <<= 1;
605 }
606 return value;
607 }
608
609 SDL_FORCE_INLINE SDL_bool
convert_format(GL_RenderData * renderdata,Uint32 pixel_format,GLint * internalFormat,GLenum * format,GLenum * type)610 convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
611 GLint* internalFormat, GLenum* format, GLenum* type)
612 {
613 switch (pixel_format) {
614 case SDL_PIXELFORMAT_ARGB8888:
615 *internalFormat = GL_RGBA8;
616 *format = GL_BGRA;
617 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
618 break;
619 case SDL_PIXELFORMAT_YV12:
620 case SDL_PIXELFORMAT_IYUV:
621 case SDL_PIXELFORMAT_NV12:
622 case SDL_PIXELFORMAT_NV21:
623 *internalFormat = GL_LUMINANCE;
624 *format = GL_LUMINANCE;
625 *type = GL_UNSIGNED_BYTE;
626 break;
627 #ifdef __MACOSX__
628 case SDL_PIXELFORMAT_UYVY:
629 *internalFormat = GL_RGB8;
630 *format = GL_YCBCR_422_APPLE;
631 *type = GL_UNSIGNED_SHORT_8_8_APPLE;
632 break;
633 #endif
634 default:
635 return SDL_FALSE;
636 }
637 return SDL_TRUE;
638 }
639
640 static GLenum
GetScaleQuality(void)641 GetScaleQuality(void)
642 {
643 const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
644
645 if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
646 return GL_NEAREST;
647 } else {
648 return GL_LINEAR;
649 }
650 }
651
652 static int
GL_CreateTexture(SDL_Renderer * renderer,SDL_Texture * texture)653 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
654 {
655 GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
656 GL_TextureData *data;
657 GLint internalFormat;
658 GLenum format, type;
659 int texture_w, texture_h;
660 GLenum scaleMode;
661
662 GL_ActivateRenderer(renderer);
663
664 if (texture->access == SDL_TEXTUREACCESS_TARGET &&
665 !renderdata->GL_EXT_framebuffer_object_supported) {
666 return SDL_SetError("Render targets not supported by OpenGL");
667 }
668
669 if (!convert_format(renderdata, texture->format, &internalFormat,
670 &format, &type)) {
671 return SDL_SetError("Texture format %s not supported by OpenGL",
672 SDL_GetPixelFormatName(texture->format));
673 }
674
675 data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
676 if (!data) {
677 return SDL_OutOfMemory();
678 }
679
680 if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
681 size_t size;
682 data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
683 size = texture->h * data->pitch;
684 if (texture->format == SDL_PIXELFORMAT_YV12 ||
685 texture->format == SDL_PIXELFORMAT_IYUV) {
686 /* Need to add size for the U and V planes */
687 size += (2 * (texture->h * data->pitch) / 4);
688 }
689 if (texture->format == SDL_PIXELFORMAT_NV12 ||
690 texture->format == SDL_PIXELFORMAT_NV21) {
691 /* Need to add size for the U/V plane */
692 size += ((texture->h * data->pitch) / 2);
693 }
694 data->pixels = SDL_calloc(1, size);
695 if (!data->pixels) {
696 SDL_free(data);
697 return SDL_OutOfMemory();
698 }
699 }
700
701 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
702 data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
703 } else {
704 data->fbo = NULL;
705 }
706
707 GL_CheckError("", renderer);
708 renderdata->glGenTextures(1, &data->texture);
709 if (GL_CheckError("glGenTextures()", renderer) < 0) {
710 if (data->pixels) {
711 SDL_free(data->pixels);
712 }
713 SDL_free(data);
714 return -1;
715 }
716 texture->driverdata = data;
717
718 if (renderdata->GL_ARB_texture_non_power_of_two_supported) {
719 data->type = GL_TEXTURE_2D;
720 texture_w = texture->w;
721 texture_h = texture->h;
722 data->texw = 1.0f;
723 data->texh = 1.0f;
724 } else if (renderdata->GL_ARB_texture_rectangle_supported) {
725 data->type = GL_TEXTURE_RECTANGLE_ARB;
726 texture_w = texture->w;
727 texture_h = texture->h;
728 data->texw = (GLfloat) texture_w;
729 data->texh = (GLfloat) texture_h;
730 } else {
731 data->type = GL_TEXTURE_2D;
732 texture_w = power_of_2(texture->w);
733 texture_h = power_of_2(texture->h);
734 data->texw = (GLfloat) (texture->w) / texture_w;
735 data->texh = (GLfloat) texture->h / texture_h;
736 }
737
738 data->format = format;
739 data->formattype = type;
740 scaleMode = GetScaleQuality();
741 renderdata->glEnable(data->type);
742 renderdata->glBindTexture(data->type, data->texture);
743 renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
744 renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
745 /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
746 and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
747 */
748 if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
749 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
750 GL_CLAMP_TO_EDGE);
751 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
752 GL_CLAMP_TO_EDGE);
753 }
754 #ifdef __MACOSX__
755 #ifndef GL_TEXTURE_STORAGE_HINT_APPLE
756 #define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC
757 #endif
758 #ifndef STORAGE_CACHED_APPLE
759 #define STORAGE_CACHED_APPLE 0x85BE
760 #endif
761 #ifndef STORAGE_SHARED_APPLE
762 #define STORAGE_SHARED_APPLE 0x85BF
763 #endif
764 if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
765 renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
766 GL_STORAGE_SHARED_APPLE);
767 } else {
768 renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
769 GL_STORAGE_CACHED_APPLE);
770 }
771 if (texture->access == SDL_TEXTUREACCESS_STREAMING
772 && texture->format == SDL_PIXELFORMAT_ARGB8888
773 && (texture->w % 8) == 0) {
774 renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
775 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
776 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
777 (data->pitch / SDL_BYTESPERPIXEL(texture->format)));
778 renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
779 texture_h, 0, format, type, data->pixels);
780 renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
781 }
782 else
783 #endif
784 {
785 renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
786 texture_h, 0, format, type, NULL);
787 }
788 renderdata->glDisable(data->type);
789 if (GL_CheckError("glTexImage2D()", renderer) < 0) {
790 return -1;
791 }
792
793 if (texture->format == SDL_PIXELFORMAT_YV12 ||
794 texture->format == SDL_PIXELFORMAT_IYUV) {
795 data->yuv = SDL_TRUE;
796
797 renderdata->glGenTextures(1, &data->utexture);
798 renderdata->glGenTextures(1, &data->vtexture);
799 renderdata->glEnable(data->type);
800
801 renderdata->glBindTexture(data->type, data->utexture);
802 renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
803 scaleMode);
804 renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
805 scaleMode);
806 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
807 GL_CLAMP_TO_EDGE);
808 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
809 GL_CLAMP_TO_EDGE);
810 renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
811 texture_h/2, 0, format, type, NULL);
812
813 renderdata->glBindTexture(data->type, data->vtexture);
814 renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
815 scaleMode);
816 renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
817 scaleMode);
818 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
819 GL_CLAMP_TO_EDGE);
820 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
821 GL_CLAMP_TO_EDGE);
822 renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
823 texture_h/2, 0, format, type, NULL);
824
825 renderdata->glDisable(data->type);
826 }
827
828 if (texture->format == SDL_PIXELFORMAT_NV12 ||
829 texture->format == SDL_PIXELFORMAT_NV21) {
830 data->nv12 = SDL_TRUE;
831
832 renderdata->glGenTextures(1, &data->utexture);
833 renderdata->glEnable(data->type);
834
835 renderdata->glBindTexture(data->type, data->utexture);
836 renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
837 scaleMode);
838 renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
839 scaleMode);
840 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
841 GL_CLAMP_TO_EDGE);
842 renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
843 GL_CLAMP_TO_EDGE);
844 renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, texture_w/2,
845 texture_h/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
846 renderdata->glDisable(data->type);
847 }
848
849 return GL_CheckError("", renderer);
850 }
851
852 static int
GL_UpdateTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const void * pixels,int pitch)853 GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
854 const SDL_Rect * rect, const void *pixels, int pitch)
855 {
856 GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
857 GL_TextureData *data = (GL_TextureData *) texture->driverdata;
858 const int texturebpp = SDL_BYTESPERPIXEL(texture->format);
859
860 SDL_assert(texturebpp != 0); /* otherwise, division by zero later. */
861
862 GL_ActivateRenderer(renderer);
863
864 renderdata->glEnable(data->type);
865 renderdata->glBindTexture(data->type, data->texture);
866 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
867 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp));
868 renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
869 rect->h, data->format, data->formattype,
870 pixels);
871 if (data->yuv) {
872 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
873
874 /* Skip to the correct offset into the next texture */
875 pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
876 if (texture->format == SDL_PIXELFORMAT_YV12) {
877 renderdata->glBindTexture(data->type, data->vtexture);
878 } else {
879 renderdata->glBindTexture(data->type, data->utexture);
880 }
881 renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
882 rect->w/2, rect->h/2,
883 data->format, data->formattype, pixels);
884
885 /* Skip to the correct offset into the next texture */
886 pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
887 if (texture->format == SDL_PIXELFORMAT_YV12) {
888 renderdata->glBindTexture(data->type, data->utexture);
889 } else {
890 renderdata->glBindTexture(data->type, data->vtexture);
891 }
892 renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
893 rect->w/2, rect->h/2,
894 data->format, data->formattype, pixels);
895 }
896
897 if (data->nv12) {
898 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
899
900 /* Skip to the correct offset into the next texture */
901 pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
902 renderdata->glBindTexture(data->type, data->utexture);
903 renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
904 rect->w/2, rect->h/2,
905 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels);
906 }
907 renderdata->glDisable(data->type);
908
909 return GL_CheckError("glTexSubImage2D()", renderer);
910 }
911
912 static int
GL_UpdateTextureYUV(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,const Uint8 * Yplane,int Ypitch,const Uint8 * Uplane,int Upitch,const Uint8 * Vplane,int Vpitch)913 GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
914 const SDL_Rect * rect,
915 const Uint8 *Yplane, int Ypitch,
916 const Uint8 *Uplane, int Upitch,
917 const Uint8 *Vplane, int Vpitch)
918 {
919 GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
920 GL_TextureData *data = (GL_TextureData *) texture->driverdata;
921
922 GL_ActivateRenderer(renderer);
923
924 renderdata->glEnable(data->type);
925 renderdata->glBindTexture(data->type, data->texture);
926 renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
927 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch);
928 renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
929 rect->h, data->format, data->formattype,
930 Yplane);
931
932 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
933 renderdata->glBindTexture(data->type, data->utexture);
934 renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
935 rect->w/2, rect->h/2,
936 data->format, data->formattype, Uplane);
937
938 renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
939 renderdata->glBindTexture(data->type, data->vtexture);
940 renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
941 rect->w/2, rect->h/2,
942 data->format, data->formattype, Vplane);
943 renderdata->glDisable(data->type);
944
945 return GL_CheckError("glTexSubImage2D()", renderer);
946 }
947
948 static int
GL_LockTexture(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * rect,void ** pixels,int * pitch)949 GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
950 const SDL_Rect * rect, void **pixels, int *pitch)
951 {
952 GL_TextureData *data = (GL_TextureData *) texture->driverdata;
953
954 data->locked_rect = *rect;
955 *pixels =
956 (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
957 rect->x * SDL_BYTESPERPIXEL(texture->format));
958 *pitch = data->pitch;
959 return 0;
960 }
961
962 static void
GL_UnlockTexture(SDL_Renderer * renderer,SDL_Texture * texture)963 GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
964 {
965 GL_TextureData *data = (GL_TextureData *) texture->driverdata;
966 const SDL_Rect *rect;
967 void *pixels;
968
969 rect = &data->locked_rect;
970 pixels =
971 (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
972 rect->x * SDL_BYTESPERPIXEL(texture->format));
973 GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch);
974 }
975
976 static int
GL_SetRenderTarget(SDL_Renderer * renderer,SDL_Texture * texture)977 GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
978 {
979 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
980 GL_TextureData *texturedata;
981 GLenum status;
982
983 GL_ActivateRenderer(renderer);
984
985 if (!data->GL_EXT_framebuffer_object_supported) {
986 return SDL_SetError("Render targets not supported by OpenGL");
987 }
988
989 if (texture == NULL) {
990 data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
991 return 0;
992 }
993
994 texturedata = (GL_TextureData *) texture->driverdata;
995 data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO);
996 /* TODO: check if texture pixel format allows this operation */
997 data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texturedata->type, texturedata->texture, 0);
998 /* Check FBO status */
999 status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
1000 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1001 return SDL_SetError("glFramebufferTexture2DEXT() failed");
1002 }
1003 return 0;
1004 }
1005
1006 static int
GL_UpdateViewport(SDL_Renderer * renderer)1007 GL_UpdateViewport(SDL_Renderer * renderer)
1008 {
1009 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1010
1011 if (SDL_CurrentContext != data->context) {
1012 /* We'll update the viewport after we rebind the context */
1013 return 0;
1014 }
1015
1016 if (renderer->target) {
1017 data->glViewport(renderer->viewport.x, renderer->viewport.y,
1018 renderer->viewport.w, renderer->viewport.h);
1019 } else {
1020 int w, h;
1021
1022 SDL_GL_GetDrawableSize(renderer->window, &w, &h);
1023 data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h),
1024 renderer->viewport.w, renderer->viewport.h);
1025 }
1026
1027 data->glMatrixMode(GL_PROJECTION);
1028 data->glLoadIdentity();
1029 if (renderer->viewport.w && renderer->viewport.h) {
1030 if (renderer->target) {
1031 data->glOrtho((GLdouble) 0,
1032 (GLdouble) renderer->viewport.w,
1033 (GLdouble) 0,
1034 (GLdouble) renderer->viewport.h,
1035 0.0, 1.0);
1036 } else {
1037 data->glOrtho((GLdouble) 0,
1038 (GLdouble) renderer->viewport.w,
1039 (GLdouble) renderer->viewport.h,
1040 (GLdouble) 0,
1041 0.0, 1.0);
1042 }
1043 }
1044 return GL_CheckError("", renderer);
1045 }
1046
1047 static int
GL_UpdateClipRect(SDL_Renderer * renderer)1048 GL_UpdateClipRect(SDL_Renderer * renderer)
1049 {
1050 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1051
1052 if (renderer->clipping_enabled) {
1053 const SDL_Rect *rect = &renderer->clip_rect;
1054 data->glEnable(GL_SCISSOR_TEST);
1055 if (renderer->target) {
1056 data->glScissor(renderer->viewport.x + rect->x, renderer->viewport.y + rect->y, rect->w, rect->h);
1057 } else {
1058 int w, h;
1059
1060 SDL_GL_GetDrawableSize(renderer->window, &w, &h);
1061 data->glScissor(renderer->viewport.x + rect->x, h - renderer->viewport.y - rect->y - rect->h, rect->w, rect->h);
1062 }
1063 } else {
1064 data->glDisable(GL_SCISSOR_TEST);
1065 }
1066 return 0;
1067 }
1068
1069 static void
GL_SetShader(GL_RenderData * data,GL_Shader shader)1070 GL_SetShader(GL_RenderData * data, GL_Shader shader)
1071 {
1072 if (data->shaders && shader != data->current.shader) {
1073 GL_SelectShader(data->shaders, shader);
1074 data->current.shader = shader;
1075 }
1076 }
1077
1078 static void
GL_SetColor(GL_RenderData * data,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1079 GL_SetColor(GL_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1080 {
1081 Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
1082
1083 if (color != data->current.color) {
1084 data->glColor4f((GLfloat) r * inv255f,
1085 (GLfloat) g * inv255f,
1086 (GLfloat) b * inv255f,
1087 (GLfloat) a * inv255f);
1088 data->current.color = color;
1089 }
1090 }
1091
1092 static void
GL_SetBlendMode(GL_RenderData * data,int blendMode)1093 GL_SetBlendMode(GL_RenderData * data, int blendMode)
1094 {
1095 if (blendMode != data->current.blendMode) {
1096 switch (blendMode) {
1097 case SDL_BLENDMODE_NONE:
1098 data->glDisable(GL_BLEND);
1099 break;
1100 case SDL_BLENDMODE_BLEND:
1101 data->glEnable(GL_BLEND);
1102 data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1103 break;
1104 case SDL_BLENDMODE_ADD:
1105 data->glEnable(GL_BLEND);
1106 data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
1107 break;
1108 case SDL_BLENDMODE_MOD:
1109 data->glEnable(GL_BLEND);
1110 data->glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
1111 break;
1112 }
1113 data->current.blendMode = blendMode;
1114 }
1115 }
1116
1117 static void
GL_SetDrawingState(SDL_Renderer * renderer)1118 GL_SetDrawingState(SDL_Renderer * renderer)
1119 {
1120 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1121
1122 GL_ActivateRenderer(renderer);
1123
1124 GL_SetColor(data, renderer->r,
1125 renderer->g,
1126 renderer->b,
1127 renderer->a);
1128
1129 GL_SetBlendMode(data, renderer->blendMode);
1130
1131 GL_SetShader(data, SHADER_SOLID);
1132 }
1133
1134 static int
GL_RenderClear(SDL_Renderer * renderer)1135 GL_RenderClear(SDL_Renderer * renderer)
1136 {
1137 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1138
1139 GL_ActivateRenderer(renderer);
1140
1141 data->glClearColor((GLfloat) renderer->r * inv255f,
1142 (GLfloat) renderer->g * inv255f,
1143 (GLfloat) renderer->b * inv255f,
1144 (GLfloat) renderer->a * inv255f);
1145
1146 if (renderer->clipping_enabled) {
1147 data->glDisable(GL_SCISSOR_TEST);
1148 }
1149
1150 data->glClear(GL_COLOR_BUFFER_BIT);
1151
1152 if (renderer->clipping_enabled) {
1153 data->glEnable(GL_SCISSOR_TEST);
1154 }
1155
1156 return 0;
1157 }
1158
1159 static int
GL_RenderDrawPoints(SDL_Renderer * renderer,const SDL_FPoint * points,int count)1160 GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
1161 int count)
1162 {
1163 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1164 int i;
1165
1166 GL_SetDrawingState(renderer);
1167
1168 data->glBegin(GL_POINTS);
1169 for (i = 0; i < count; ++i) {
1170 data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
1171 }
1172 data->glEnd();
1173
1174 return 0;
1175 }
1176
1177 static int
GL_RenderDrawLines(SDL_Renderer * renderer,const SDL_FPoint * points,int count)1178 GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
1179 int count)
1180 {
1181 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1182 int i;
1183
1184 GL_SetDrawingState(renderer);
1185
1186 if (count > 2 &&
1187 points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
1188 data->glBegin(GL_LINE_LOOP);
1189 /* GL_LINE_LOOP takes care of the final segment */
1190 --count;
1191 for (i = 0; i < count; ++i) {
1192 data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
1193 }
1194 data->glEnd();
1195 } else {
1196 #if defined(__MACOSX__) || defined(__WIN32__)
1197 #else
1198 int x1, y1, x2, y2;
1199 #endif
1200
1201 data->glBegin(GL_LINE_STRIP);
1202 for (i = 0; i < count; ++i) {
1203 data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
1204 }
1205 data->glEnd();
1206
1207 /* The line is half open, so we need one more point to complete it.
1208 * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
1209 * If we have to, we can use vertical line and horizontal line textures
1210 * for vertical and horizontal lines, and then create custom textures
1211 * for diagonal lines and software render those. It's terrible, but at
1212 * least it would be pixel perfect.
1213 */
1214 data->glBegin(GL_POINTS);
1215 #if defined(__MACOSX__) || defined(__WIN32__)
1216 /* Mac OS X and Windows seem to always leave the last point open */
1217 data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y);
1218 #else
1219 /* Linux seems to leave the right-most or bottom-most point open */
1220 x1 = points[0].x;
1221 y1 = points[0].y;
1222 x2 = points[count-1].x;
1223 y2 = points[count-1].y;
1224
1225 if (x1 > x2) {
1226 data->glVertex2f(0.5f + x1, 0.5f + y1);
1227 } else if (x2 > x1) {
1228 data->glVertex2f(0.5f + x2, 0.5f + y2);
1229 }
1230 if (y1 > y2) {
1231 data->glVertex2f(0.5f + x1, 0.5f + y1);
1232 } else if (y2 > y1) {
1233 data->glVertex2f(0.5f + x2, 0.5f + y2);
1234 }
1235 #endif
1236 data->glEnd();
1237 }
1238 return GL_CheckError("", renderer);
1239 }
1240
1241 static int
GL_RenderFillRects(SDL_Renderer * renderer,const SDL_FRect * rects,int count)1242 GL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
1243 {
1244 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1245 int i;
1246
1247 GL_SetDrawingState(renderer);
1248
1249 for (i = 0; i < count; ++i) {
1250 const SDL_FRect *rect = &rects[i];
1251
1252 data->glRectf(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
1253 }
1254 return GL_CheckError("", renderer);
1255 }
1256
1257 static int
GL_SetupCopy(SDL_Renderer * renderer,SDL_Texture * texture)1258 GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture)
1259 {
1260 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1261 GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
1262
1263 data->glEnable(texturedata->type);
1264 if (texturedata->yuv) {
1265 data->glActiveTextureARB(GL_TEXTURE2_ARB);
1266 data->glBindTexture(texturedata->type, texturedata->vtexture);
1267
1268 data->glActiveTextureARB(GL_TEXTURE1_ARB);
1269 data->glBindTexture(texturedata->type, texturedata->utexture);
1270
1271 data->glActiveTextureARB(GL_TEXTURE0_ARB);
1272 }
1273 if (texturedata->nv12) {
1274 data->glActiveTextureARB(GL_TEXTURE1_ARB);
1275 data->glBindTexture(texturedata->type, texturedata->utexture);
1276
1277 data->glActiveTextureARB(GL_TEXTURE0_ARB);
1278 }
1279 data->glBindTexture(texturedata->type, texturedata->texture);
1280
1281 if (texture->modMode) {
1282 GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
1283 } else {
1284 GL_SetColor(data, 255, 255, 255, 255);
1285 }
1286
1287 GL_SetBlendMode(data, texture->blendMode);
1288
1289 if (texturedata->yuv) {
1290 GL_SetShader(data, SHADER_YUV);
1291 } else if (texturedata->nv12) {
1292 if (texture->format == SDL_PIXELFORMAT_NV12) {
1293 GL_SetShader(data, SHADER_NV12);
1294 } else {
1295 GL_SetShader(data, SHADER_NV21);
1296 }
1297 } else {
1298 GL_SetShader(data, SHADER_RGB);
1299 }
1300 return 0;
1301 }
1302
1303 static int
GL_RenderCopy(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect)1304 GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
1305 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
1306 {
1307 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1308 GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
1309 GLfloat minx, miny, maxx, maxy;
1310 GLfloat minu, maxu, minv, maxv;
1311
1312 GL_ActivateRenderer(renderer);
1313
1314 if (GL_SetupCopy(renderer, texture) < 0) {
1315 return -1;
1316 }
1317
1318 minx = dstrect->x;
1319 miny = dstrect->y;
1320 maxx = dstrect->x + dstrect->w;
1321 maxy = dstrect->y + dstrect->h;
1322
1323 minu = (GLfloat) srcrect->x / texture->w;
1324 minu *= texturedata->texw;
1325 maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
1326 maxu *= texturedata->texw;
1327 minv = (GLfloat) srcrect->y / texture->h;
1328 minv *= texturedata->texh;
1329 maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
1330 maxv *= texturedata->texh;
1331
1332 data->glBegin(GL_TRIANGLE_STRIP);
1333 data->glTexCoord2f(minu, minv);
1334 data->glVertex2f(minx, miny);
1335 data->glTexCoord2f(maxu, minv);
1336 data->glVertex2f(maxx, miny);
1337 data->glTexCoord2f(minu, maxv);
1338 data->glVertex2f(minx, maxy);
1339 data->glTexCoord2f(maxu, maxv);
1340 data->glVertex2f(maxx, maxy);
1341 data->glEnd();
1342
1343 data->glDisable(texturedata->type);
1344
1345 return GL_CheckError("", renderer);
1346 }
1347
1348 static int
GL_RenderCopyEx(SDL_Renderer * renderer,SDL_Texture * texture,const SDL_Rect * srcrect,const SDL_FRect * dstrect,const double angle,const SDL_FPoint * center,const SDL_RendererFlip flip)1349 GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
1350 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
1351 const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
1352 {
1353 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1354 GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
1355 GLfloat minx, miny, maxx, maxy;
1356 GLfloat centerx, centery;
1357 GLfloat minu, maxu, minv, maxv;
1358
1359 GL_ActivateRenderer(renderer);
1360
1361 if (GL_SetupCopy(renderer, texture) < 0) {
1362 return -1;
1363 }
1364
1365 centerx = center->x;
1366 centery = center->y;
1367
1368 if (flip & SDL_FLIP_HORIZONTAL) {
1369 minx = dstrect->w - centerx;
1370 maxx = -centerx;
1371 }
1372 else {
1373 minx = -centerx;
1374 maxx = dstrect->w - centerx;
1375 }
1376
1377 if (flip & SDL_FLIP_VERTICAL) {
1378 miny = dstrect->h - centery;
1379 maxy = -centery;
1380 }
1381 else {
1382 miny = -centery;
1383 maxy = dstrect->h - centery;
1384 }
1385
1386 minu = (GLfloat) srcrect->x / texture->w;
1387 minu *= texturedata->texw;
1388 maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
1389 maxu *= texturedata->texw;
1390 minv = (GLfloat) srcrect->y / texture->h;
1391 minv *= texturedata->texh;
1392 maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
1393 maxv *= texturedata->texh;
1394
1395 /* Translate to flip, rotate, translate to position */
1396 data->glPushMatrix();
1397 data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0);
1398 data->glRotated(angle, (GLdouble)0.0, (GLdouble)0.0, (GLdouble)1.0);
1399
1400 data->glBegin(GL_TRIANGLE_STRIP);
1401 data->glTexCoord2f(minu, minv);
1402 data->glVertex2f(minx, miny);
1403 data->glTexCoord2f(maxu, minv);
1404 data->glVertex2f(maxx, miny);
1405 data->glTexCoord2f(minu, maxv);
1406 data->glVertex2f(minx, maxy);
1407 data->glTexCoord2f(maxu, maxv);
1408 data->glVertex2f(maxx, maxy);
1409 data->glEnd();
1410 data->glPopMatrix();
1411
1412 data->glDisable(texturedata->type);
1413
1414 return GL_CheckError("", renderer);
1415 }
1416
1417 static int
GL_RenderReadPixels(SDL_Renderer * renderer,const SDL_Rect * rect,Uint32 pixel_format,void * pixels,int pitch)1418 GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
1419 Uint32 pixel_format, void * pixels, int pitch)
1420 {
1421 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1422 Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ARGB8888;
1423 void *temp_pixels;
1424 int temp_pitch;
1425 GLint internalFormat;
1426 GLenum format, type;
1427 Uint8 *src, *dst, *tmp;
1428 int w, h, length, rows;
1429 int status;
1430
1431 GL_ActivateRenderer(renderer);
1432
1433 temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
1434 temp_pixels = SDL_malloc(rect->h * temp_pitch);
1435 if (!temp_pixels) {
1436 return SDL_OutOfMemory();
1437 }
1438
1439 if (!convert_format(data, temp_format, &internalFormat, &format, &type)) {
1440 SDL_free(temp_pixels);
1441 return SDL_SetError("Texture format %s not supported by OpenGL",
1442 SDL_GetPixelFormatName(temp_format));
1443 }
1444
1445 SDL_GetRendererOutputSize(renderer, &w, &h);
1446
1447 data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
1448 data->glPixelStorei(GL_PACK_ROW_LENGTH,
1449 (temp_pitch / SDL_BYTESPERPIXEL(temp_format)));
1450
1451 data->glReadPixels(rect->x, renderer->target ? rect->y : (h-rect->y)-rect->h,
1452 rect->w, rect->h, format, type, temp_pixels);
1453
1454 if (GL_CheckError("glReadPixels()", renderer) < 0) {
1455 SDL_free(temp_pixels);
1456 return -1;
1457 }
1458
1459 /* Flip the rows to be top-down if necessary */
1460 if (!renderer->target) {
1461 length = rect->w * SDL_BYTESPERPIXEL(temp_format);
1462 src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
1463 dst = (Uint8*)temp_pixels;
1464 tmp = SDL_stack_alloc(Uint8, length);
1465 rows = rect->h / 2;
1466 while (rows--) {
1467 SDL_memcpy(tmp, dst, length);
1468 SDL_memcpy(dst, src, length);
1469 SDL_memcpy(src, tmp, length);
1470 dst += temp_pitch;
1471 src -= temp_pitch;
1472 }
1473 SDL_stack_free(tmp);
1474 }
1475
1476 status = SDL_ConvertPixels(rect->w, rect->h,
1477 temp_format, temp_pixels, temp_pitch,
1478 pixel_format, pixels, pitch);
1479 SDL_free(temp_pixels);
1480
1481 return status;
1482 }
1483
1484 static void
GL_RenderPresent(SDL_Renderer * renderer)1485 GL_RenderPresent(SDL_Renderer * renderer)
1486 {
1487 GL_ActivateRenderer(renderer);
1488
1489 SDL_GL_SwapWindow(renderer->window);
1490 }
1491
1492 static void
GL_DestroyTexture(SDL_Renderer * renderer,SDL_Texture * texture)1493 GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
1494 {
1495 GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
1496 GL_TextureData *data = (GL_TextureData *) texture->driverdata;
1497
1498 GL_ActivateRenderer(renderer);
1499
1500 if (!data) {
1501 return;
1502 }
1503 if (data->texture) {
1504 renderdata->glDeleteTextures(1, &data->texture);
1505 }
1506 if (data->yuv) {
1507 renderdata->glDeleteTextures(1, &data->utexture);
1508 renderdata->glDeleteTextures(1, &data->vtexture);
1509 }
1510 SDL_free(data->pixels);
1511 SDL_free(data);
1512 texture->driverdata = NULL;
1513 }
1514
1515 static void
GL_DestroyRenderer(SDL_Renderer * renderer)1516 GL_DestroyRenderer(SDL_Renderer * renderer)
1517 {
1518 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1519
1520 if (data) {
1521 GL_ClearErrors(renderer);
1522 if (data->GL_ARB_debug_output_supported) {
1523 PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB");
1524
1525 /* Uh oh, we don't have a safe way of removing ourselves from the callback chain, if it changed after we set our callback. */
1526 /* For now, just always replace the callback with the original one */
1527 glDebugMessageCallbackARBFunc(data->next_error_callback, data->next_error_userparam);
1528 }
1529 if (data->shaders) {
1530 GL_DestroyShaderContext(data->shaders);
1531 }
1532 if (data->context) {
1533 while (data->framebuffers) {
1534 GL_FBOList *nextnode = data->framebuffers->next;
1535 /* delete the framebuffer object */
1536 data->glDeleteFramebuffersEXT(1, &data->framebuffers->FBO);
1537 GL_CheckError("", renderer);
1538 SDL_free(data->framebuffers);
1539 data->framebuffers = nextnode;
1540 }
1541 SDL_GL_DeleteContext(data->context);
1542 }
1543 SDL_free(data);
1544 }
1545 SDL_free(renderer);
1546 }
1547
1548 static int
GL_BindTexture(SDL_Renderer * renderer,SDL_Texture * texture,float * texw,float * texh)1549 GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh)
1550 {
1551 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1552 GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
1553 GL_ActivateRenderer(renderer);
1554
1555 data->glEnable(texturedata->type);
1556 if (texturedata->yuv) {
1557 data->glActiveTextureARB(GL_TEXTURE2_ARB);
1558 data->glBindTexture(texturedata->type, texturedata->vtexture);
1559
1560 data->glActiveTextureARB(GL_TEXTURE1_ARB);
1561 data->glBindTexture(texturedata->type, texturedata->utexture);
1562
1563 data->glActiveTextureARB(GL_TEXTURE0_ARB);
1564 }
1565 data->glBindTexture(texturedata->type, texturedata->texture);
1566
1567 if(texw) *texw = (float)texturedata->texw;
1568 if(texh) *texh = (float)texturedata->texh;
1569
1570 return 0;
1571 }
1572
1573 static int
GL_UnbindTexture(SDL_Renderer * renderer,SDL_Texture * texture)1574 GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
1575 {
1576 GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
1577 GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
1578 GL_ActivateRenderer(renderer);
1579
1580 if (texturedata->yuv) {
1581 data->glActiveTextureARB(GL_TEXTURE2_ARB);
1582 data->glDisable(texturedata->type);
1583
1584 data->glActiveTextureARB(GL_TEXTURE1_ARB);
1585 data->glDisable(texturedata->type);
1586
1587 data->glActiveTextureARB(GL_TEXTURE0_ARB);
1588 }
1589
1590 data->glDisable(texturedata->type);
1591
1592 return 0;
1593 }
1594
1595 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
1596
1597 /* vi: set ts=4 sw=4 expandtab: */
1598