Logo Search packages:      
Sourcecode: timfx version File versions  Download package

kino_opengl_utility.cc

// timfx 
// Copyright 2002, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "kino_opengl_utility.h"

#include <iterator>
#include <sstream>

namespace
{

int XErrorFlag = 0;

int HandleXError( Display *dpy, XErrorEvent *event )
{
    XErrorFlag = 1;
    return 0;
}

void print(std::ostream& Stream, const GLXFBConfig& RHS)
{
/*
   int pbAttribs[] = {GLX_LARGEST_PBUFFER_SGIX, True,
                  GLX_PRESERVED_CONTENTS_SGIX, False,
                  None};
   GLXPbufferSGIX pBuffer;
*/
   int width=2, height=2;
   int bufferSize, level, doubleBuffer, stereo, auxBuffers;
   int redSize, greenSize, blueSize, alphaSize;
   int depthSize, stencilSize;
   int accumRedSize, accumBlueSize, accumGreenSize, accumAlphaSize;
   int sampleBuffers, samples;
   int drawableType, renderType, xRenderable, xVisual, id;
   int maxWidth, maxHeight, maxPixels;
   int optWidth, optHeight;

   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_BUFFER_SIZE, &bufferSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_LEVEL, &level);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_DOUBLEBUFFER, &doubleBuffer);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_STEREO, &stereo);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_AUX_BUFFERS, &auxBuffers);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_RED_SIZE, &redSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_GREEN_SIZE, &greenSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_BLUE_SIZE, &blueSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_ALPHA_SIZE, &alphaSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_DEPTH_SIZE, &depthSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_STENCIL_SIZE, &stencilSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_ACCUM_RED_SIZE, &accumRedSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_ACCUM_GREEN_SIZE, &accumGreenSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_ACCUM_BLUE_SIZE, &accumBlueSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_ACCUM_ALPHA_SIZE, &accumAlphaSize);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_FBCONFIG_ID, &id);
/*
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_SAMPLE_BUFFERS_SGIS, &sampleBuffers);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_SAMPLES_SGIS, &samples);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_DRAWABLE_TYPE_SGIX, &drawableType);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_RENDER_TYPE_SGIX, &renderType);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_X_RENDERABLE_SGIX, &xRenderable);
   glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_X_VISUAL_TYPE_EXT, &xVisual);
   if (!xRenderable || !(drawableType & GLX_WINDOW_BIT_SGIX))
      xVisual = -1;
*/

      glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_MAX_PBUFFER_WIDTH, &maxWidth);
      glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_MAX_PBUFFER_HEIGHT, &maxHeight);
      glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_MAX_PBUFFER_PIXELS, &maxPixels);
//    glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_OPTIMAL_PBUFFER_WIDTH_SGIX, &optWidth);
//    glXGetFBConfigAttrib(GDK_DISPLAY(), RHS, GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX, &optHeight);

//   pBuffer = CreatePbuffer(GDK_DISPLAY(), RHS, width, height, pbAttribs);

      Stream << "Id:" <<  id << std::endl;
      Stream << "    Buffer Size: " << bufferSize << std::endl;
      Stream << "    Level: " << level << std::endl;
      Stream << "    Double Buffer: " << (doubleBuffer ? "yes" : "no") << std::endl;
      Stream << "    Stereo: " << (stereo ? "yes" : "no") << std::endl;
      Stream << "    Aux Buffers: " << auxBuffers << std::endl;
      Stream << "    Red Size: " << redSize << std::endl;
      Stream << "    Green Size: " << greenSize << std::endl;
      Stream << "    Blue Size: " << blueSize << std::endl;
      Stream << "    Alpha Size: " << alphaSize << std::endl;
      Stream << "    Depth Size: " << depthSize << std::endl;
      Stream << "    Stencil Size: " << stencilSize << std::endl;
      Stream << "    Accum Red Size: " << accumRedSize << std::endl;
      Stream << "    Accum Green Size: " << accumGreenSize << std::endl;
      Stream << "    Accum Blue Size: " << accumBlueSize << std::endl;
      Stream << "    Accum Alpha Size: " << accumAlphaSize << std::endl;
      Stream << "    Sample Buffers: " << sampleBuffers << std::endl;
      Stream << "    Samples/Pixel: " << samples << std::endl;
      Stream << "    Drawable Types: " << std::endl;
      
/*
      if (drawableType & GLX_WINDOW_BIT_SGIX)  Stream << "  Window " << std::endl;
      if (drawableType & GLX_PIXMAP_BIT_SGIX)  Stream << "  Pixmap " << std::endl;
      if (drawableType & GLX_PBUFFER_BIT_SGIX)  Stream << "  PBuffer" << std::endl;
      Stream << std::endl;
      Stream << "    Render Types: " << std::endl;
      if (renderType & GLX_RGBA_BIT_SGIX)  Stream << "  RGBA " << std::endl;
      if (renderType & GLX_COLOR_INDEX_BIT_SGIX)  Stream << "  CI " << std::endl;
      Stream << std::endl;
      Stream << "    X Renderable: %s\n", xRenderable ? "yes" : "no" << std::endl;
*/      

      Stream << "    Max width: " << maxWidth << std::endl;
      Stream << "    Max height: " << maxHeight << std::endl;
      Stream << "    Max pixels: " << maxPixels << std::endl;
//    Stream << "    Optimum width: " << optWidth << std::endl;
//    Stream << "    Optimum height: " << optHeight << std::endl;

/*
      Stream << "    Pbuffer: %s\n", pBuffer ? "yes" : "no");

   if (pBuffer) {
      glXDestroyGLXPbufferSGIX(GDK_DISPLAY(), pBuffer);
   }
*/
}

/// Set to true iff render_buffers should attempt to use the GLX pbuffers extension
bool g_use_pbuffers = true;

} // namespace

namespace kino
{

namespace gl
{

///////////////////////////////////////////////////////////////
// push_matrix

push_matrix::push_matrix(const GLenum MatrixType) :
      m_matrix_type(MatrixType)
{
      glMatrixMode(m_matrix_type);
      glPushMatrix();
}

push_matrix::~push_matrix()
{
      glMatrixMode(m_matrix_type);
      glPopMatrix();
}

///////////////////////////////////////////////////////////////
// push_attributes

push_attributes::push_attributes(const GLenum Attributes)
{
      glPushAttrib(Attributes);
}

push_attributes::~push_attributes()
{
      glPopAttrib();
}

/*
///////////////////////////////////////////////////////////////
// texture::implementation

class texture::implementation
{
public:
      implementation() :
            m_name(0)
      {
            // Generate a texture name ...
            glGenTextures(1, const_cast<GLuint*>(&m_name));
      }
      
      ~implementation()
      {
            glDeleteTextures(1, &m_name);
      }
      
      const GLuint m_name;
};

///////////////////////////////////////////////////////////////
// texture

texture::texture() :
      m_implementation(new texture::implementation())
{
      // Setup defaults for the texture ...
      glBindTexture(GL_TEXTURE_2D, m_implementation->m_name);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

texture::~texture()
{
      delete m_implementation;
}

void texture::bind()
{
      glBindTexture(GL_TEXTURE_2D, m_implementation->m_name);
}
*/

///////////////////////////////////////////////////////////////
// render_buffer::implementation

class render_buffer::implementation
{
public:
      virtual ~implementation()
      {
      }

      virtual void make_current() = 0;

      const kino::pixel_size_type m_width;
      const kino::pixel_size_type m_height;

protected:
      implementation(const kino::pixel_size_type Width, const kino::pixel_size_type Height) :
            m_width(Width),
            m_height(Height)
      {
            // Sanity checks ...
            if(!m_width)
                  throw "Invalid (zero) width";
                  
            if(!m_height)
                  throw "Invalid (zero) height";
      }
};

namespace
{

////////////////////////////////////////////////////////////////////////////////////////////////////
// glx_buffer

/// render_buffer implementation that renders to an offscreen GLXPixmap, which is typically portable, but typically unaccelerated
class glx_buffer :
      public render_buffer::implementation
{
public:
      glx_buffer(const kino::pixel_size_type Width, const kino::pixel_size_type Height) :
            render_buffer::implementation(Width, Height)
      {
            // Sanity checks ...
            if(!glXQueryExtension(GDK_DISPLAY(), 0, 0))
                  throw "glx_buffer: X server does not support GLX";
                  
            int configuration[] = { GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_RGBA, None };
            XVisualInfo* const visual = glXChooseVisual(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()), &configuration[0]);
            if(!visual)
                  throw "glx_buffer: No appropriate OpenGL visual available";
                  
            m_context = glXCreateContext(GDK_DISPLAY(), visual, 0, False);
            if(!m_context)
                  throw "glx_buffer: Could not create OpenGL render context";
                  
            m_pixmap = XCreatePixmap(GDK_DISPLAY(), RootWindow(GDK_DISPLAY(), visual->screen), m_width, m_height, visual->depth);
            if(!m_pixmap)
                  throw "glx_buffer: Could not create render pixmap";
                  
            m_glxpixmap = glXCreateGLXPixmap(GDK_DISPLAY(), visual, m_pixmap);
            if(!m_glxpixmap)
                  throw "glx_buffer: Could not create GLX pixmap";
      }

      ~glx_buffer()
      {
            glXDestroyGLXPixmap(GDK_DISPLAY(), m_glxpixmap);
            XFreePixmap(GDK_DISPLAY(), m_pixmap);
            glXDestroyContext(GDK_DISPLAY(), m_context);
      }

      void make_current()
      {
            glXMakeCurrent(GDK_DISPLAY(), m_glxpixmap, m_context);
      }

private:
      GLXContext m_context;
      Pixmap m_pixmap;
      GLXPixmap m_glxpixmap;
};

#ifdef GLX_VERSION_1_3

/// render_buffer implementation that renders to an offscreen pbuffer, which is not-so-portable, but should be hardware-accelerated
class pbuffer_buffer :
      public render_buffer::implementation
{
public:
      pbuffer_buffer(const kino::pixel_size_type Width, const kino::pixel_size_type Height) :
            render_buffer::implementation(Width, Height)
      {
            // Sanity checks ...
            if(!glXQueryExtension(GDK_DISPLAY(), 0, 0))
                  throw "pbuffer_buffer: X server does not support GLX";
            
            int fbconfig_configuration[] = { GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_RGBA, None };
            int fbconfig_count = 0;
            GLXFBConfig* const fbconfigs = glXChooseFBConfig(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()), 0, &fbconfig_count);
            if(!fbconfig_count)
                  throw "pbuffer_buffer: No appropriate fbconfig available";

            GLXFBConfig* fbconfig = 0;
            for(fbconfig = fbconfigs; fbconfig != fbconfigs + fbconfig_count; ++fbconfig)
                  {
                        int (*old_error_handler)( Display *, XErrorEvent * ) = XSetErrorHandler(HandleXError);
                        XErrorFlag = 0;
                        
                        std::vector<int> pbuffer_configuration;
                        pbuffer_configuration.push_back(GLX_PBUFFER_WIDTH);
                        pbuffer_configuration.push_back(Width);
                        pbuffer_configuration.push_back(GLX_PBUFFER_HEIGHT);
                        pbuffer_configuration.push_back(Height);
                        pbuffer_configuration.push_back(None);
                        m_pbuffer = glXCreatePbuffer(GDK_DISPLAY(), *fbconfig, &pbuffer_configuration[0]);
                        
                        XSetErrorHandler( old_error_handler );

                        if(m_pbuffer != None && XErrorFlag == 0)
                              break;
                  }
            if(None == m_pbuffer)
                  throw "pbuffer_buffer: Could not create pbuffer";

            m_context = glXCreateNewContext(GDK_DISPLAY(), *fbconfig, GLX_RGBA_TYPE, 0, False);
            if(!m_context)
                  throw "pbuffer_buffer: Could not create OpenGL render context";
      }

      ~pbuffer_buffer()
      {
            glXDestroyPbuffer(GDK_DISPLAY(), m_pbuffer);
      }

      void make_current()
      {
            if(!glXMakeContextCurrent(GDK_DISPLAY(), m_pbuffer, m_pbuffer, m_context))
                  throw ("Could not set current OpenGL context");
      }

private:
      GLXContext m_context;
      GLXPbuffer m_pbuffer;
};

#endif // GLX_VERSION_1_3

render_buffer::implementation* render_buffer_implementation_factory(const kino::pixel_size_type Width, const kino::pixel_size_type Height)
{
#ifdef GLX_VERSION_1_3

      // See if we can support pbuffer rendering ...
      if(g_use_pbuffers)
            {
                  try
                        {
                              // Check for suitable client extensions ...
                              std::istringstream client_extensions_text(glXGetClientString(GDK_DISPLAY(), GLX_EXTENSIONS));
                              std::vector<std::string> client_extensions;
                              client_extensions.assign(std::istream_iterator<std::string>(client_extensions_text), std::istream_iterator<std::string>());
      
                              if(std::count(client_extensions.begin(), client_extensions.end(), "GLX_SGIX_pbuffer") && std::count(client_extensions.begin(), client_extensions.end(), "GLX_SGIX_fbconfig"))
                                    {
                                          // Check for suitable server extensions ...
                                          std::istringstream server_extensions_text(glXQueryServerString(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()), GLX_EXTENSIONS));
                                          std::vector<std::string> server_extensions;
                                          server_extensions.assign(std::istream_iterator<std::string>(server_extensions_text), std::istream_iterator<std::string>());
                  
                                          if(std::count(server_extensions.begin(), server_extensions.end(), "GLX_SGIX_pbuffer") && std::count(server_extensions.begin(), server_extensions.end(), "GLX_SGIX_fbconfig"))
                                                {
                                                      std::cout << "Trying pbuffers for offscreen rendering" << std::endl;
                                                      return new pbuffer_buffer(Width, Height);
                                                }
                                    }
                        }
                  catch(const char* e)
                        {
                              std::cerr << e << std::endl;
                        }
                  catch(...)
                        {
                              std::cerr << "Unknown error creating pbuffer implementation" << std::endl;
                        }
            }
      
#endif // GLX_VERSION_1_3

      // No pbuffers, so fall back on GLXPixbuf rendering ...
      std::cout << "Trying GLXPixbuf for offscreen rendering" << std::endl;
      return new glx_buffer(Width, Height);
}

} // namespace

///////////////////////////////////////////////////////////////
// render_buffer

00419 render_buffer::render_buffer(const kino::pixel_size_type Width, const kino::pixel_size_type Height) :
      m_implementation(render_buffer_implementation_factory(Width, Height))
{
}

render_buffer::~render_buffer()
{
      delete m_implementation;
}

00429 void render_buffer::start_render()
{
      m_implementation->make_current();
      glViewport(0, 0, m_implementation->m_width, m_implementation->m_height);

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();

      glMatrixMode(GL_TEXTURE);
      glLoadIdentity();
      
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
}

00444 void render_buffer::draw_background(const kino::pixel_size_type Width, const kino::pixel_size_type Height, void* Pixels)
{
      // Sanity checks ...
      if(Width != m_implementation->m_width)
            throw "Invalid background width";
            
      if(Height != m_implementation->m_height)
            throw "Invalid background height";
            
      push_attributes attributes(GL_ALL_ATTRIB_BITS);

      push_matrix projection_matrix(GL_PROJECTION);   
      glLoadIdentity();
      glOrtho(0, 1, 1, 0, -1, 1);

      push_matrix modelview_matrix(GL_MODELVIEW);
      glLoadIdentity();
      
      glPixelZoom(1, -1);
      glRasterPos2d(0, 0);
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
      glDisable(GL_TEXTURE_1D);
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_TEXTURE_3D);
      glDrawPixels(Width, Height, GL_RGB, GL_UNSIGNED_BYTE, Pixels);
}

00472 void render_buffer::render_frame()
{
      glDisable(GL_POLYGON_STIPPLE);
}

00477 void render_buffer::render_field(const unsigned int Field)
{
      static const GLubyte even_mask[] =
      {
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
      };

      static const GLubyte odd_mask[] =
      {
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
            0x00, 0x00, 0x00, 0x00,
            0xff, 0xff, 0xff, 0xff,
      };

      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
      glPolygonStipple(Field ? odd_mask : even_mask);
      glEnable(GL_POLYGON_STIPPLE);
}

00557 void render_buffer::finish_render()
{
      glFlush();
}

00562 void render_buffer::read_pixels(const kino::pixel_size_type Width, const kino::pixel_size_type Height, uint8_t* Pixels, int Format)
{
      // Sanity checks ...
      if(Width != m_implementation->m_width)
            throw "Invalid buffer width";
            
      if(Height != m_implementation->m_height)
            throw "Invalid buffer height";
      
      glPixelStorei(GL_PACK_ALIGNMENT, 1);
      for(int row = 0; row < Height; ++row)
            glReadPixels(0, row, Width, 1, Format, GL_UNSIGNED_BYTE, Pixels + (Height - row - 1) * Width * 3);
}

////////////////////////////////////////////////////////////////////////////////////////////
// use_pbuffers

void use_pbuffers(const bool Enable)
{
      g_use_pbuffers = Enable;      
}
      
} // namespace gl

} // namespace kino


Generated by  Doxygen 1.6.0   Back to index