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 */ |
48 | extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); |
49 | |
50 | static const float inv255f = 1.0f / 255.0f; |
51 | |
52 | typedef struct GL_FBOList GL_FBOList; |
53 | |
54 | struct GL_FBOList |
55 | { |
56 | Uint32 w, h; |
57 | GLuint FBO; |
58 | GL_FBOList *next; |
59 | }; |
60 | |
61 | typedef 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 | |
80 | typedef 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 | |
120 | typedef 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 | |
142 | SDL_FORCE_INLINE const char* |
143 | GL_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 | |
161 | SDL_FORCE_INLINE void |
162 | GL_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 | |
188 | SDL_FORCE_INLINE int |
189 | GL_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 | |
231 | static int |
232 | GL_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 | |
252 | static int |
253 | GL_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 | |
268 | static void APIENTRY |
269 | GL_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 | |
297 | static GL_FBOList * |
298 | GL_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 | |
319 | static int |
320 | GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) |
321 | { |
322 | SDL_GL_GetDrawableSize(renderer->window, w, h); |
323 | return 0; |
324 | } |
325 | |
326 | static 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 | |
354 | static 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 | |
368 | static SDL_bool |
369 | GL_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 | |
392 | SDL_FORCE_INLINE int |
393 | power_of_2(int input) |
394 | { |
395 | int value = 1; |
396 | |
397 | while (value < input) { |
398 | value <<= 1; |
399 | } |
400 | return value; |
401 | } |
402 | |
403 | SDL_FORCE_INLINE SDL_bool |
404 | convert_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 | |
441 | static int |
442 | GL_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 | |
637 | static int |
638 | GL_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 |
700 | static int |
701 | GL_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 | |
737 | static int |
738 | GL_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 | |
769 | static int |
770 | GL_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 | |
783 | static void |
784 | GL_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 | |
797 | static void |
798 | GL_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 | |
830 | static int |
831 | GL_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. */ |
865 | static int |
866 | GL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) |
867 | { |
868 | return 0; /* nothing to do in this backend. */ |
869 | } |
870 | |
871 | static int |
872 | GL_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 | |
890 | static int |
891 | GL_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 | |
932 | static int |
933 | GL_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 | |
954 | static int |
955 | GL_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 | |
995 | static int |
996 | GL_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 | |
1055 | static void |
1056 | SetDrawState(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 | |
1126 | static void |
1127 | SetCopyState(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 | |
1202 | static int |
1203 | GL_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 | |
1386 | static int |
1387 | GL_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 | |
1457 | static void |
1458 | GL_RenderPresent(SDL_Renderer * renderer) |
1459 | { |
1460 | GL_ActivateRenderer(renderer); |
1461 | |
1462 | SDL_GL_SwapWindow(renderer->window); |
1463 | } |
1464 | |
1465 | static void |
1466 | GL_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 | |
1497 | static void |
1498 | GL_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 | |
1535 | static int |
1536 | GL_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 | |
1567 | static int |
1568 | GL_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 | |
1597 | static SDL_Renderer * |
1598 | GL_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 | |
1803 | error: |
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 | |
1815 | SDL_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 | |