00001
00002 #include "mythcontext.h"
00003 #include "openglcontext.h"
00004
00005 #include "util-opengl.h"
00006
00007 #define LOC QString("GLCtx: ")
00008 #define LOC_ERR QString("GLCtx, Error: ")
00009
00010 class PrivateContext
00011 {
00012 public:
00013 PrivateContext() :
00014 m_glx_fbconfig(0), m_gl_window(0), m_glx_window(0),
00015 m_glx_context(NULL),
00016 m_texture_type(GL_TEXTURE_2D), m_textures_enabled(false),
00017 m_vis_info(NULL), m_attr_list(NULL)
00018 {
00019 }
00020
00021 ~PrivateContext()
00022 {
00023 }
00024
00025 GLXFBConfig m_glx_fbconfig;
00026 Window m_gl_window;
00027 GLXWindow m_glx_window;
00028 GLXContext m_glx_context;
00029 int m_texture_type;
00030 bool m_textures_enabled;
00031 XVisualInfo *m_vis_info;
00032 int const *m_attr_list;
00033
00034 vector<GLuint> m_textures;
00035 vector<GLuint> m_programs;
00036 vector<GLuint> m_framebuffers;
00037 };
00038
00039 OpenGLContext::OpenGLContext() :
00040 m_priv(new PrivateContext()),
00041 m_display(NULL), m_screen_num(0),
00042 m_major_ver(1), m_minor_ver(2),
00043 m_extensions(QString::null), m_ext_supported(0),
00044 m_visible(true), m_max_tex_size(0)
00045 {
00046 if (!init_opengl())
00047 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to initialize OpenGL support.");
00048 }
00049
00050 OpenGLContext::~OpenGLContext()
00051 {
00052 MakeCurrent(true);
00053
00054 if (m_priv->m_glx_context)
00055 {
00056 DeletePrograms();
00057 DeleteTextures();
00058 DeleteFrameBuffers();
00059 }
00060
00061 glFlush();
00062
00063 MakeCurrent(false);
00064
00065 if (m_priv->m_glx_window)
00066 {
00067 X11S(glXDestroyWindow(m_display, m_priv->m_glx_window));
00068 m_priv->m_glx_window = 0;
00069 }
00070
00071 if (m_priv->m_gl_window)
00072 {
00073 X11S(XDestroyWindow(m_display, m_priv->m_gl_window));
00074 m_priv->m_gl_window = 0;
00075 }
00076
00077 if (m_priv->m_glx_context)
00078 {
00079 X11S(glXDestroyContext(m_display, m_priv->m_glx_context));
00080 m_priv->m_glx_context = 0;
00081 }
00082
00083 if (m_priv)
00084 {
00085 delete m_priv;
00086 m_priv = NULL;
00087 }
00088 }
00089
00090 void OpenGLContext::Hide(void)
00091 {
00092 X11S(XUnmapWindow(m_display, m_priv->m_gl_window));
00093 }
00094
00095 void OpenGLContext::Show(void)
00096 {
00097 X11S(XMapWindow(m_display, m_priv->m_gl_window));
00098 }
00099
00100
00101 bool OpenGLContext::Create(
00102 Display *XJ_disp, Window XJ_curwin, uint screen_num,
00103 const QSize &display_visible_size, bool visible)
00104 {
00105 static bool debugged = false;
00106
00107 m_visible = visible;
00108 m_display = XJ_disp;
00109 m_screen_num = screen_num;
00110 uint major, minor;
00111
00112 if (!get_glx_version(XJ_disp, major, minor))
00113 {
00114 VERBOSE(VB_PLAYBACK, LOC_ERR + "GLX extension not present.");
00115
00116 return false;
00117 }
00118
00119 m_major_ver = major;
00120 m_minor_ver = minor;
00121
00122 if ((1 == major) && (minor < 2))
00123 {
00124 VERBOSE(VB_PLAYBACK, LOC_ERR + QString(
00125 "Need GLX 1.2 or better, have %1.%2")
00126 .arg(major).arg(minor));
00127
00128 return false;
00129 }
00130 else if ((1 == major) && (minor == 2))
00131 {
00132 m_priv->m_attr_list = get_attr_cfg(kRenderRGBA);
00133 m_priv->m_vis_info = glXChooseVisual(
00134 XJ_disp, m_screen_num, (int*) m_priv->m_attr_list);
00135 if (!m_priv->m_vis_info)
00136 {
00137 m_priv->m_attr_list = get_attr_cfg(kSimpleRGBA);
00138 m_priv->m_vis_info = glXChooseVisual(
00139 XJ_disp, m_screen_num, (int*) m_priv->m_attr_list);
00140 }
00141 if (!m_priv->m_vis_info)
00142 {
00143 VERBOSE(VB_IMPORTANT, LOC_ERR + "No appropriate visual found");
00144 return false;
00145 }
00146 X11S(m_priv->m_glx_context = glXCreateContext(
00147 XJ_disp, m_priv->m_vis_info, None, GL_TRUE));
00148 }
00149 else
00150 {
00151 m_priv->m_attr_list = get_attr_cfg(kRenderRGBA);
00152 m_priv->m_glx_fbconfig = get_fbuffer_cfg(
00153 XJ_disp, m_screen_num, m_priv->m_attr_list);
00154
00155 if (!m_priv->m_glx_fbconfig)
00156 {
00157 VERBOSE(VB_IMPORTANT, LOC_ERR + "No framebuffer "
00158 "with needed OpenGL attributes.");
00159
00160 return false;
00161 }
00162
00163
00164 X11S(m_priv->m_glx_context = glXCreateNewContext(
00165 m_display, m_priv->m_glx_fbconfig,
00166 GLX_RGBA_TYPE, NULL, GL_TRUE));
00167 }
00168
00169 if (!m_priv->m_glx_context)
00170 {
00171 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create GLX context");
00172
00173 return false;
00174 }
00175
00176 if ((1 == major) && (minor > 2))
00177 {
00178 m_priv->m_vis_info =
00179 glXGetVisualFromFBConfig(XJ_disp, m_priv->m_glx_fbconfig);
00180 }
00181
00182 m_priv->m_gl_window = get_gl_window(
00183 XJ_disp, XJ_curwin, m_priv->m_vis_info, display_visible_size, visible);
00184
00185 if (!m_priv->m_gl_window)
00186 {
00187 VERBOSE(VB_IMPORTANT, LOC_ERR + "Couldn't create OpenGL Window");
00188
00189 return false;
00190 }
00191
00192 if ((1 == major) && (minor > 2))
00193 {
00194 X11S(m_priv->m_glx_window = glXCreateWindow(
00195 XJ_disp, m_priv->m_glx_fbconfig, m_priv->m_gl_window, NULL));
00196
00197 if (!m_priv->m_glx_window)
00198 {
00199 VERBOSE(VB_IMPORTANT, LOC_ERR + "Couldn't create GLX Window");
00200
00201 return false;
00202 }
00203 }
00204
00205 VERBOSE(VB_PLAYBACK, LOC + QString("Created window%1 and context.")
00206 .arg(m_visible ? "" : " (Offscreen)"));
00207
00208 {
00209 MakeCurrent(true);
00210
00211 GLint maxtexsz = 0;
00212 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsz);
00213 m_max_tex_size = (maxtexsz) ? maxtexsz : 512;
00214 m_extensions = (const char*) glGetString(GL_EXTENSIONS);
00215
00216 if (!debugged)
00217 {
00218 debugged = true;
00219
00220 bool direct = false;
00221 X11S(direct = glXIsDirect(XJ_disp, m_priv->m_glx_context));
00222
00223 VERBOSE(VB_PLAYBACK, LOC + QString("GLX Version: %1.%2")
00224 .arg(major).arg(minor));
00225 VERBOSE(VB_PLAYBACK, LOC + QString("Direct rendering: %1")
00226 .arg(direct ? "Yes" : "No"));
00227 VERBOSE(VB_PLAYBACK, LOC + QString("OpenGL vendor : %1")
00228 .arg((const char*) glGetString(GL_VENDOR)));
00229 VERBOSE(VB_PLAYBACK, LOC + QString("OpenGL renderer: %1")
00230 .arg((const char*) glGetString(GL_RENDERER)));
00231 VERBOSE(VB_PLAYBACK, LOC + QString("OpenGL version : %1")
00232 .arg((const char*) glGetString(GL_VERSION)));
00233 VERBOSE(VB_PLAYBACK, LOC + QString("Max texture size: %1 x %2")
00234 .arg(m_max_tex_size).arg(m_max_tex_size));
00235 }
00236
00237 MakeCurrent(false);
00238 }
00239
00240 int tex_type = get_gl_texture_rect_type(m_extensions);
00241 m_priv->m_texture_type = (tex_type) ? tex_type : m_priv->m_texture_type;
00242
00243 m_ext_supported =
00244 ((tex_type) ? kGLExtRect : 0) |
00245 ((has_gl_fragment_program_support(m_extensions)) ?
00246 kGLExtFragProg : 0) |
00247 ((has_gl_fbuffer_object_support(m_extensions)) ? kGLExtFBufObj : 0) |
00248 ((minor >= 3) ? kGLXPBuffer : 0);
00249
00250 return true;
00251 }
00252
00253
00254 bool OpenGLContext::MakeCurrent(bool current)
00255 {
00256 bool ok;
00257
00258 if (current)
00259 {
00260 if (IsGLXSupported(1,3))
00261 {
00262 X11S(ok = glXMakeCurrent(m_display,
00263 m_priv->m_glx_window,
00264 m_priv->m_glx_context));
00265 }
00266 else
00267 {
00268 X11S(ok = glXMakeCurrent(m_display,
00269 m_priv->m_gl_window,
00270 m_priv->m_glx_context));
00271 }
00272 }
00273 else
00274 {
00275 X11S(ok = glXMakeCurrent(m_display, None, NULL));
00276 }
00277
00278 if (!ok)
00279 VERBOSE(VB_PLAYBACK, LOC + "Could not make context current.");
00280
00281 return ok;
00282 }
00283
00284
00285 void OpenGLContext::SwapBuffers(void)
00286 {
00287 if (m_visible)
00288 {
00289 MakeCurrent(true);
00290
00291 glFinish();
00292 if (IsGLXSupported(1,3))
00293 X11S(glXSwapBuffers(m_display, m_priv->m_glx_window));
00294 else
00295 X11S(glXSwapBuffers(m_display, m_priv->m_gl_window));
00296
00297 MakeCurrent(false);
00298 }
00299 }
00300
00301
00302 void OpenGLContext::Flush(void)
00303 {
00304 glFlush();
00305 }
00306
00307
00308 void OpenGLContext::EnableTextures(void)
00309 {
00310 if (!m_priv->m_textures_enabled)
00311 {
00312 m_priv->m_textures_enabled = true;
00313
00314 MakeCurrent(true);
00315 glEnable(GetTextureType());
00316 MakeCurrent(false);
00317 }
00318 }
00319
00320
00321 uint OpenGLContext::CreateTexture(void)
00322 {
00323 MakeCurrent(true);
00324
00325 GLuint tex;
00326 glGenTextures(1, &tex);
00327 SetupTextureFilters(tex, GL_LINEAR);
00328 m_priv->m_textures.push_back(tex);
00329
00330 MakeCurrent(false);
00331
00332 return tex;
00333 }
00334
00335
00336 bool OpenGLContext::SetupTexture(const QSize &size, uint tex, int filt)
00337 {
00338 unsigned char *scratch =
00339 new unsigned char[(size.width() * size.height() * 4) + 128];
00340
00341 bzero(scratch, size.width() * size.height() * 4);
00342
00343 GLint check;
00344
00345 MakeCurrent(true);
00346 SetupTextureFilters(tex, filt);
00347 glTexImage2D(GetTextureType(), 0, GL_RGBA8, size.width(), size.height(),
00348 0, GL_RGB , GL_UNSIGNED_BYTE, scratch);
00349 glGetTexLevelParameteriv(GetTextureType(), 0, GL_TEXTURE_WIDTH, &check);
00350 MakeCurrent(false);
00351
00352 if (scratch)
00353 {
00354 delete scratch;
00355 scratch = NULL;
00356 }
00357
00358 return (check == size.width());
00359 }
00360
00361
00362 void OpenGLContext::SetupTextureFilters(uint tex, int filt)
00363 {
00364 glBindTexture(GetTextureType(), tex);
00365 glTexParameteri(GetTextureType(), GL_TEXTURE_MIN_FILTER, filt);
00366 glTexParameteri(GetTextureType(), GL_TEXTURE_MAG_FILTER, filt);
00367 glTexParameteri(GetTextureType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00368 glTexParameteri(GetTextureType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00369 }
00370
00371
00372 void OpenGLContext::DeleteTexture(uint tex)
00373 {
00374 MakeCurrent(true);
00375
00376 vector<GLuint>::iterator it;
00377 for (it = m_priv->m_textures.begin(); it !=m_priv->m_textures.end(); it++)
00378 {
00379 if (*(it) == tex)
00380 {
00381 GLuint gltex = tex;
00382 glDeleteTextures(1, &gltex);
00383 m_priv->m_textures.erase(it);
00384 break;
00385 }
00386 }
00387
00388 MakeCurrent(false);
00389 }
00390
00391
00392 void OpenGLContext::DeleteTextures(void)
00393 {
00394 MakeCurrent(true);
00395
00396 vector<GLuint>::iterator it;
00397 for (it = m_priv->m_textures.begin(); it !=m_priv->m_textures.end(); it++)
00398 glDeleteTextures(1, &(*(it)));
00399 m_priv->m_textures.clear();
00400
00401 MakeCurrent(false);
00402 }
00403
00404 int OpenGLContext::GetTextureType(void) const
00405 {
00406 return m_priv->m_texture_type;
00407 }
00408
00409
00410 bool OpenGLContext::CreateFragmentProgram(const QString &program, uint &fp)
00411 {
00412 bool success = true;
00413 GLint error;
00414
00415 MakeCurrent(true);
00416
00417 GLuint glfp;
00418 gMythGLGenProgramsARB(1, &glfp);
00419 gMythGLBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, glfp);
00420 gMythGLProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
00421 GL_PROGRAM_FORMAT_ASCII_ARB,
00422 program.length(), program.latin1());
00423
00424 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error);
00425 if (error != -1)
00426 {
00427 VERBOSE(VB_PLAYBACK, LOC_ERR +
00428 QString("Fragment Program compile error: position %1:'%2'")
00429 .arg(error).arg(program.mid(error)));
00430
00431 success = false;
00432 }
00433 gMythGLGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
00434 GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &error);
00435 if (error != 1)
00436 {
00437 VERBOSE(VB_PLAYBACK, LOC_ERR +
00438 "Fragment program exceeds hardware capabilities.");
00439
00440 success = false;
00441 }
00442
00443 if (success)
00444 {
00445 m_priv->m_programs.push_back(glfp);
00446 }
00447 else
00448 {
00449 gMythGLDeleteProgramsARB(1, &glfp);
00450 }
00451
00452 MakeCurrent(false);
00453
00454 fp = glfp;
00455
00456 return success;
00457 }
00458
00459
00460 void OpenGLContext::DeleteFragmentProgram(uint fp)
00461 {
00462 MakeCurrent(true);
00463
00464 vector<GLuint>::iterator it;
00465 for (it = m_priv->m_programs.begin(); it != m_priv->m_programs.end(); it++)
00466 {
00467 if (*(it) == fp)
00468 {
00469 GLuint glfp = fp;
00470 gMythGLDeleteProgramsARB(1, &glfp);
00471 m_priv->m_programs.erase(it);
00472 break;
00473 }
00474 }
00475
00476 MakeCurrent(false);
00477 }
00478
00479 void OpenGLContext::BindFragmentProgram(uint fp)
00480 {
00481 gMythGLBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fp);
00482 }
00483
00484 void OpenGLContext::InitFragmentParams(
00485 uint fp, float a, float b, float c, float d)
00486 {
00487 gMythGLProgramEnvParameter4fARB(
00488 GL_FRAGMENT_PROGRAM_ARB, fp, a, b, c, d);
00489 }
00490
00491 void OpenGLContext::DeletePrograms(void)
00492 {
00493 MakeCurrent(true);
00494
00495 vector<GLuint>::iterator it;
00496 for (it = m_priv->m_programs.begin(); it != m_priv->m_programs.end(); it++)
00497 gMythGLDeleteProgramsARB(1, &(*(it)));
00498 m_priv->m_programs.clear();
00499
00500 MakeCurrent(false);
00501 }
00502
00503
00504 bool OpenGLContext::CreateFrameBuffer(uint &fb, uint tex, const QSize &size)
00505 {
00506 GLuint glfb;
00507
00508 MakeCurrent(true);
00509
00510 SetupTextureFilters(tex, GL_LINEAR);
00511
00512 glPushAttrib(GL_VIEWPORT_BIT);
00513 glViewport(0, 0, size.width(), size.height());
00514 gMythGLGenFramebuffersEXT(1, &glfb);
00515 gMythGLBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glfb);
00516 glBindTexture(GetTextureType(), tex);
00517 glTexImage2D(GetTextureType(), 0, GL_RGBA8,
00518 (GLint) size.width(), (GLint) size.height(), 0,
00519 GL_RGB, GL_UNSIGNED_BYTE, NULL);
00520 gMythGLFramebufferTexture2DEXT(
00521 GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
00522 GetTextureType(), tex, 0);
00523
00524 GLenum status;
00525 status = gMythGLCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
00526 gMythGLBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
00527 glPopAttrib();
00528
00529 bool success = false;
00530 switch (status)
00531 {
00532 case GL_FRAMEBUFFER_COMPLETE_EXT:
00533 VERBOSE(VB_PLAYBACK, LOC +
00534 QString("Created frame buffer object (%1x%2).")
00535 .arg(size.width()).arg(size.height()));
00536 success = true;
00537 break;
00538 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
00539 VERBOSE(VB_PLAYBACK, LOC + "Frame buffer incomplete_ATTACHMENT");
00540 break;
00541 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
00542 VERBOSE(VB_PLAYBACK, LOC +
00543 "Frame buffer incomplete_MISSING_ATTACHMENT");
00544 break;
00545 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
00546 VERBOSE(VB_PLAYBACK, LOC +
00547 "Frame buffer incomplete_DUPLICATE_ATTACHMENT");
00548 break;
00549 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
00550 VERBOSE(VB_PLAYBACK, LOC +
00551 "Frame buffer incomplete_DIMENSIONS_EXT");
00552 break;
00553 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
00554 VERBOSE(VB_PLAYBACK, LOC +
00555 "Frame buffer incomplete_FORMATS_EXT");
00556 break;
00557 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
00558 VERBOSE(VB_PLAYBACK, LOC +
00559 "Frame buffer incomplete_DRAW_BUFFER_EXT");
00560 break;
00561 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
00562 VERBOSE(VB_PLAYBACK, LOC +
00563 "Frame buffer incomplete_READ_BUFFER_EXT");
00564 break;
00565 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
00566 VERBOSE(VB_PLAYBACK, LOC + "Frame buffer unsupported.");
00567 break;
00568 default:
00569 VERBOSE(VB_PLAYBACK, LOC +
00570 QString("Unknown frame buffer error %1.").arg(status));
00571 }
00572
00573 if (success)
00574 m_priv->m_framebuffers.push_back(glfb);
00575 else
00576 gMythGLDeleteFramebuffersEXT(1, &glfb);
00577
00578 MakeCurrent(false);
00579
00580 fb = glfb;
00581
00582 return success;
00583 }
00584
00585
00586 void OpenGLContext::DeleteFrameBuffer(uint fb)
00587 {
00588 MakeCurrent(true);
00589
00590 vector<GLuint>::iterator it;
00591 for (it = m_priv->m_framebuffers.begin();
00592 it != m_priv->m_framebuffers.end(); it++)
00593 {
00594 if (*(it) == fb)
00595 {
00596 GLuint glfb = fb;
00597 gMythGLDeleteFramebuffersEXT(1, &glfb);
00598 m_priv->m_framebuffers.erase(it);
00599 break;
00600 }
00601 }
00602
00603 MakeCurrent(false);
00604 }
00605
00606 void OpenGLContext::DeleteFrameBuffers(void)
00607 {
00608 MakeCurrent(true);
00609
00610 vector<GLuint>::iterator it;
00611 for (it = m_priv->m_framebuffers.begin();
00612 it != m_priv->m_framebuffers.end(); it++)
00613 {
00614 gMythGLDeleteFramebuffersEXT(1, &(*(it)));
00615 }
00616 m_priv->m_framebuffers.clear();
00617
00618 MakeCurrent(false);
00619 }
00620
00621
00622 void OpenGLContext::BindFramebuffer(uint fb)
00623 {
00624 gMythGLBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
00625 }
00626
00627 bool OpenGLContext::IsGLXSupported(
00628 Display *display, uint min_major, uint min_minor)
00629 {
00630 uint major, minor;
00631 if (init_opengl() && get_glx_version(display, major, minor))
00632 {
00633 return ((major > min_major) ||
00634 (major == min_major && (minor >= min_minor)));
00635 }
00636
00637 return false;
00638 }