Ticket #2649: util-opengl.cpp

File util-opengl.cpp, 16.0 KB (added by anonymous, 17 years ago)
Line 
1// N.B. Any locking should be handled by the parent.
2
3using namespace std;
4
5// MythTV General headers
6#include "mythcontext.h"
7
8// MythTV videout headers
9#include "util-opengl.h"
10#include "util-x11.h"
11
12#define LOC QString("OpenglCtx: ")
13#define LOC_ERR QString("OpenglCtx Error: ")
14
15#ifndef GL_TEXTURE_RECTANGLE_ARB
16#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
17#endif
18
19#ifndef GL_TEXTURE_RECTANGLE_EXT
20#define GL_TEXTURE_RECTANGLE_EXT 0x84F5
21#endif
22
23#ifndef GL_TEXTURE_RECTANGLE_NV
24#define GL_TEXTURE_RECTANGLE_NV 0x84F5
25#endif
26
27OpenglContext::OpenglContext() :
28      m_texture( GL_TEXTURE_2D ),
29      m_display(NULL), m_screen_num(0),
30      m_window(0), m_visual(NULL),
31      m_context(NULL), m_visible(true),
32      m_supported(SUPP_NONE), m_max_tex_size(0),
33      m_textures_enabled(FALSE)
34{
35}
36
37OpenglContext::~OpenglContext()
38{
39    if (m_context && m_window)
40    {
41        DeletePrograms();
42        DeleteTextures();
43        DeleteFrameBuffers();
44    }
45    if (m_context)
46        glXDestroyContext(m_display, m_context);
47    if (m_window)
48        XDestroyWindow(m_display, m_window);
49    if (m_visual)
50        XFree(m_visual);
51}
52
53bool OpenglContext::Create(Window window, int width, int height, bool visible)
54{
55    m_visible = visible;
56    m_display = MythXOpenDisplay();
57    if (!m_display)
58    {
59        VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to open display.");
60        return false;
61    }
62    m_screen_num = DefaultScreen(m_display);
63    if (!CheckGlx() ||
64        !GetVisual() ||
65        !CreateWindow(window, width, height) ||
66        !CreateContext())
67        return false;
68
69    VERBOSE(VB_PLAYBACK, LOC + QString("Created window%1 and context.")
70        .arg(m_visible ? "" : " (Offscreen)"));
71    return true;
72}
73
74void OpenglContext::Init(void)
75{
76    MakeCurrent(true);
77    m_extensions= reinterpret_cast<const char *>(glGetString( GL_EXTENSIONS ));
78    glGetIntegerv( GL_MAX_TEXTURE_SIZE, &m_max_tex_size );
79    MakeCurrent(false);
80    if (m_max_tex_size == 0)
81        m_max_tex_size = 512;
82    VERBOSE(VB_PLAYBACK, LOC +
83            QString("Maximum supported texture size: %1 x %2")
84            .arg(m_max_tex_size).arg(m_max_tex_size));
85    m_supported += EnableFrameBufs();
86    m_supported += EnableFragProgs();
87    m_supported += EnableTextureRects();
88}
89
90void OpenglContext::MakeCurrent(bool current)
91{
92    if (current)
93    {
94        bool yes = glXMakeCurrent( m_display, m_window, m_context );
95        if (!yes)
96            VERBOSE(VB_PLAYBACK, LOC + "Could not make context current.");
97    }
98    else
99        glXMakeCurrent( m_display, None, NULL );
100}
101
102void OpenglContext::SwapBuffers(void)
103{
104    if (!m_visible)
105        return;
106    MakeCurrent(true);
107    glXSwapBuffers(m_display, m_window);
108    MakeCurrent(false);
109}
110
111void OpenglContext::EnableTextures(void)
112{
113    if (m_textures_enabled)
114        return;
115    m_textures_enabled = true;
116    MakeCurrent(true);
117    glEnable( m_texture );
118    MakeCurrent(false);
119}
120
121int OpenglContext::EnableTextureRects(void)
122{
123    if (m_extensions.contains("GL_NV_texture_rectangle"))
124    {
125        VERBOSE(VB_PLAYBACK, LOC + "Using NV NPOT texture extension.");
126        m_texture = GL_TEXTURE_RECTANGLE_NV;
127    }
128    else if (m_extensions.contains("GL_ARB_texture_rectangle"))
129    {
130        VERBOSE(VB_PLAYBACK, LOC + "Using ARB NPOT texture extension.");
131        m_texture = GL_TEXTURE_RECTANGLE_ARB;
132    }
133    else if (m_extensions.contains("GL_EXT_texture_rectangle"))
134    {
135        VERBOSE(VB_PLAYBACK, LOC + "Using EXT NPOT texture extension.");
136        m_texture = GL_TEXTURE_RECTANGLE_EXT;
137    }
138    else
139    {
140        VERBOSE(VB_PLAYBACK, LOC + "NPOT textures not available.");
141        return 0;
142    }
143
144    return SUPP_RECT;
145}
146
147int OpenglContext::EnableFragProgs(void)
148{
149    if (!m_extensions.contains("GL_ARB_fragment_program"))
150    {
151        VERBOSE(VB_PLAYBACK, LOC + "GL_ARB_fragment_program unavailable.");
152        return 0;
153    }
154    m_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)
155                glXGetProcAddress("glGenProgramsARB");
156    m_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC)
157                glXGetProcAddress("glBindProgramARB");
158    m_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)
159                glXGetProcAddress("glProgramStringARB");
160    m_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC)
161                glXGetProcAddress("glProgramEnvParameter4fARB");
162    m_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)
163                glXGetProcAddress("glDeleteProgramsARB");
164    m_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC)
165                glXGetProcAddress("glGetProgramivARB");
166
167    if (!m_glGenProgramsARB    ||
168        !m_glBindProgramARB    ||
169        !m_glProgramStringARB  ||
170        !m_glDeleteProgramsARB ||
171        !m_glGetProgramivARB   ||
172        !m_glProgramEnvParameter4fARB)
173    {
174        VERBOSE(VB_PLAYBACK, LOC + "Couldn't get proc address");
175        return 0;
176    }
177    VERBOSE(VB_PLAYBACK, LOC + "Fragment programs supported.");
178    return SUPP_PROG;
179}
180
181int OpenglContext::EnableFrameBufs(void)
182{
183    if (!m_extensions.contains("GL_EXT_framebuffer_object"))
184    {
185        VERBOSE(VB_PLAYBACK, LOC + "GL_EXT_framebuffer_object unavailable.");
186        return 0;
187    }
188    m_glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
189                glXGetProcAddress("glGenFramebuffersEXT");
190    m_glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
191                glXGetProcAddress("glBindFramebufferEXT");
192    m_glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
193                glXGetProcAddress("glFramebufferTexture2DEXT");
194    m_glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
195                glXGetProcAddress("glCheckFramebufferStatusEXT");
196    m_glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
197                glXGetProcAddress("glDeleteFramebuffersEXT");
198
199    if (!m_glGenFramebuffersEXT   ||
200        !m_glBindFramebufferEXT   ||
201        !m_glFramebufferTexture2DEXT ||
202        !m_glDeleteFramebuffersEXT ||
203        !m_glCheckFramebufferStatusEXT)
204    {
205        VERBOSE(VB_PLAYBACK, LOC + "Couldn't get proc address");
206        return 0;
207    }
208    VERBOSE(VB_PLAYBACK, LOC + "Frame Buffer Objects supported.");
209    return SUPP_FBUF;
210}
211
212void OpenglContext::CreateTexture(GLuint *tex)
213{
214    MakeCurrent(true);
215    glGenTextures( 1, tex );
216    SetupTextureFilters(tex, GL_LINEAR);
217    textures.push_back(*tex);
218    MakeCurrent(false);
219}
220
221bool OpenglContext::SetupTexture(QSize size, GLuint *tex, int filt)
222{
223    scratchspace = new unsigned char[(size.width()
224                                        * size.height() * 4) + 4];
225    memset(scratchspace, 0 , size.width() * size.height() * 4);
226 
227    GLint check;
228    MakeCurrent(true);
229
230    SetupTextureFilters(tex, filt);
231    glTexImage2D( m_texture, 0, GL_RGBA8,
232                size.width(), size.height(),
233                0, GL_RGB , GL_UNSIGNED_BYTE, scratchspace );
234    glGetTexLevelParameteriv(m_texture, 0, GL_TEXTURE_WIDTH, &check);
235    MakeCurrent(false);
236    delete scratchspace;
237    if (check !=size.width())
238        return false;
239    return true;
240}
241
242void OpenglContext::SetupTextureFilters(GLuint *tex, int filt)
243{
244    glBindTexture( m_texture, *tex );
245    glTexParameteri (m_texture, GL_TEXTURE_MIN_FILTER, filt);
246    glTexParameteri (m_texture, GL_TEXTURE_MAG_FILTER, filt);
247    glTexParameteri (m_texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
248    glTexParameteri (m_texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
249}
250
251void OpenglContext::DeleteTexture(GLuint *tex)
252{
253    MakeCurrent(true);
254    vector<GLuint>::iterator it;
255    for (it = textures.begin(); it !=textures.end(); it++)
256    {
257        if (*(it) == *tex)
258        {
259            glDeleteTextures( 1, tex);
260            textures.erase(it);
261            break;
262        }
263    }
264    MakeCurrent(false);
265}
266
267void OpenglContext::DeleteTextures(void)
268{
269    MakeCurrent(true);
270    vector<GLuint>::iterator it;
271    for (it = textures.begin(); it !=textures.end(); it++)
272        glDeleteTextures( 1, &(*(it)));
273    textures.clear();
274    MakeCurrent(false);
275}
276
277bool OpenglContext::CreateProgram(QString *program, GLuint *prog)
278{
279    bool success = true;
280    GLint error;
281    MakeCurrent(true);
282    m_glGenProgramsARB   ( 1, prog );
283    m_glBindProgramARB   ( GL_FRAGMENT_PROGRAM_ARB, *prog );
284    m_glProgramStringARB ( GL_FRAGMENT_PROGRAM_ARB,
285                           GL_PROGRAM_FORMAT_ASCII_ARB,
286                           program->length(), program->latin1() );
287
288    glGetIntegerv ( GL_PROGRAM_ERROR_POSITION_ARB, &error );
289    if (error != -1)
290    {
291        VERBOSE(VB_PLAYBACK, LOC_ERR +
292                QString("Fragment Program compile error: position %1:'%2'")
293                .arg(error).arg(program->mid(error)));
294        success = false;
295    }
296    m_glGetProgramivARB( GL_FRAGMENT_PROGRAM_ARB,
297                         GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &error );
298    if (error != 1)
299    {
300        VERBOSE(VB_PLAYBACK, LOC_ERR +
301                QString("Fragment program exceeds hardware capabilities."));
302        success = false;
303    }
304
305    if (success)
306        programs.push_back(*prog);
307    else
308        m_glDeleteProgramsARB( 1, prog );
309    MakeCurrent(false);
310    return success;
311}
312
313void OpenglContext::DeleteProgram(GLuint *prog)
314{
315    MakeCurrent(true);
316    vector<GLuint>::iterator it;
317    for (it = programs.begin(); it !=programs.end(); it++)
318    {
319        if (*(it) == *prog)
320        {
321            m_glDeleteProgramsARB( 1, prog);
322            programs.erase(it);
323            break;
324        }
325    }
326    MakeCurrent(false);
327}
328
329
330void OpenglContext::DeletePrograms(void)
331{
332    MakeCurrent(true);
333    vector<GLuint>::iterator it;
334    for (it = programs.begin(); it !=programs.end(); it++)
335        m_glDeleteProgramsARB( 1, &(*(it)));
336    MakeCurrent(false);
337    programs.clear();
338}
339
340bool OpenglContext::CreateFrameBuffer(GLuint *buf, GLuint *tex,
341                                      int width, int height)
342{
343    SetupTextureFilters(tex, GL_LINEAR);
344    MakeCurrent(true);
345    glPushAttrib (GL_VIEWPORT_BIT);
346    glViewport( 0, 0, width, height);
347    m_glGenFramebuffersEXT( 1, buf );
348    m_glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, *buf );
349    glBindTexture( m_texture, *tex );
350    glTexImage2D( m_texture, 0, GL_RGBA8,
351                  (GLint)width,
352                  (GLint)height, 0,
353                  GL_RGB, GL_UNSIGNED_BYTE, NULL);
354    m_glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT,
355        GL_COLOR_ATTACHMENT0_EXT, m_texture, *tex, 0 );
356    GLenum status;
357    status = m_glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
358    m_glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
359    glPopAttrib ();
360    bool success = false;
361    switch(status)
362    {
363        case GL_FRAMEBUFFER_COMPLETE_EXT:
364            VERBOSE(VB_PLAYBACK, LOC +
365                QString("Created frame buffer object (%1x%2).")
366                .arg(width).arg(height));
367            success = true;
368            break;
369        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
370            VERBOSE(VB_PLAYBACK, LOC + "Frame buffer incomplete_ATTACHMENT");
371            break;
372        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
373            VERBOSE(VB_PLAYBACK, LOC +
374                "Frame buffer incomplete_MISSING_ATTACHMENT");
375            break;
376        case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
377            VERBOSE(VB_PLAYBACK, LOC +
378                "Frame buffer incomplete_DUPLICATE_ATTACHMENT");
379            break;
380        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
381            VERBOSE(VB_PLAYBACK, LOC +
382                "Frame buffer incomplete_DIMENSIONS_EXT");
383            break;
384        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
385            VERBOSE(VB_PLAYBACK, LOC +
386                "Frame buffer incomplete_FORMATS_EXT");
387            break;
388        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
389            VERBOSE(VB_PLAYBACK, LOC +
390                "Frame buffer incomplete_DRAW_BUFFER_EXT");
391            break;
392        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
393            VERBOSE(VB_PLAYBACK, LOC +
394                "Frame buffer incomplete_READ_BUFFER_EXT");
395            break;
396        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
397            VERBOSE(VB_PLAYBACK, LOC + "Frame buffer unsupported.");
398            break;
399        default:
400            VERBOSE(VB_PLAYBACK, LOC +
401                QString("Unknown frame buffer error %1.").arg(status));
402    }
403    if (success)
404        framebuffers.push_back(*buf);
405    else
406        m_glDeleteFramebuffersEXT( 1, buf );
407    MakeCurrent(false);
408    return success;
409}
410
411void OpenglContext::DeleteFrameBuffer(GLuint *buf)
412{
413    MakeCurrent(true);
414    vector<GLuint>::iterator it;
415    for (it = framebuffers.begin(); it !=framebuffers.end(); it++)
416    {
417        if (*(it) == *buf)
418        {
419            m_glDeleteFramebuffersEXT( 1, buf);
420            framebuffers.erase(it);
421            break;
422        }
423    }
424    MakeCurrent(false);
425}
426
427void OpenglContext::DeleteFrameBuffers(void)
428{
429    MakeCurrent(true);
430    vector<GLuint>::iterator it;
431    for (it = framebuffers.begin(); it !=framebuffers.end(); it++)
432        m_glDeleteFramebuffersEXT( 1, &(*(it)));
433    framebuffers.clear();
434    MakeCurrent(false);
435}
436
437bool OpenglContext::CheckGlx(void)
438{
439    int ndummy;
440    if (!glXQueryExtension(m_display, &ndummy, &ndummy))
441    {
442        VERBOSE(VB_PLAYBACK, LOC_ERR + "OpenGL extension not present.");
443        return false;
444    }
445    return true;
446}
447
448bool OpenglContext::GetVisual(void)
449{
450    int attribList[] = {GLX_RGBA, GLX_DEPTH_SIZE, 0, GLX_DOUBLEBUFFER, 1,
451                        GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
452                        GLX_BLUE_SIZE, 1, None};
453    m_visual = glXChooseVisual(m_display, m_screen_num, attribList);
454    if (m_visual == NULL)
455    {
456        // see if we can can get a visual WITH depth buffer
457        int withdepth[] = {GLX_RGBA,
458                        GLX_DOUBLEBUFFER, 1, GLX_RED_SIZE, 1,
459                        GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None};
460        m_visual = glXChooseVisual(m_display, m_screen_num, withdepth);
461        if (m_visual == NULL)
462        {
463            VERBOSE(VB_PLAYBACK, LOC_ERR + "No appropriate visual found");
464            return false;
465        }
466    }
467    return true;
468}
469
470bool OpenglContext::CreateWindow( Window winid, int width, int height)
471{
472    XSetWindowAttributes swa;
473    swa.colormap = XCreateColormap(m_display,
474                                   RootWindow(m_display, m_visual->screen),
475                                   m_visual->visual, AllocNone);
476    if (swa.colormap == 0)
477    {
478        VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create colormap");
479        return false;
480    }
481    m_window = XCreateWindow(m_display, winid, 0, 0,
482                           m_visible ? width : 1, m_visible ? height : 1,
483                           0, m_visual->depth,
484                           InputOutput, m_visual->visual, CWColormap, &swa);
485    if (m_window == 0)
486    {
487        VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create window");
488        return false;
489    }
490    if (m_visible)
491        XMapWindow(m_display, m_window);
492    return true;
493}
494
495bool OpenglContext::CreateContext(void)
496{
497    m_context = glXCreateContext(m_display, m_visual, None, GL_TRUE);
498    if (m_context == NULL)
499    {
500        VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create Glx context");
501        return false;
502    }
503
504    if (!glXMakeCurrent(m_display, m_window, m_context))
505    {
506        VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to make Glx context current.");
507        return false;
508    }
509    glXMakeCurrent(m_display, None, NULL);
510    return true;
511}
512
513__GLXextFuncPtr OpenglContext::glXGetProcAddress(const char * const procName)
514{
515    __GLXextFuncPtr ret = glXGetProcAddressARB((const GLubyte *) procName);
516    if (!ret)
517    {
518        VERBOSE(VB_PLAYBACK,
519                QString("Error: glXGetProcAddressARB unable to find %1")
520                .arg(procName));
521    }
522    return ret;
523}