core/vgui/internals/vgui_accelerate_x11.cxx
Go to the documentation of this file.
00001 // This is core/vgui/internals/vgui_accelerate_x11.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author David Capel
00008 // \date   28 Mar 2000
00009 // \brief  See vgui_accelerate_x11.h for a description of this file.
00010 
00011 #include "vgui_accelerate_x11.h"
00012 
00013 //capes : there's no point starting this accelerator unless you're running Mesa at the moment
00014 #if VGUI_MESA
00015 
00016 // This file uses only the following names from xmesa.h :
00017 //   XMesaBuffer (type)
00018 //   XMesaGetCurrentBuffer (function)
00019 //   XMesaGetBackBuffer (function)
00020 
00021 #include <vcl_iostream.h>
00022 #include <vcl_cmath.h>
00023 #include <vcl_cassert.h>
00024 #include <vcl_cstring.h>
00025 #include <vcl_algorithm.h>
00026 
00027 #include <X11/Xlib.h>
00028 #include <X11/Xutil.h>
00029 
00030 // These accelerated functions take advantage of the fact the Mesa-X11 uses
00031 // an XImage for its backbuffer. This allows us to determine the optimal
00032 // pixel-format for image caching, and also to use the Hermes library, with
00033 // its MMX/PII optimized code, to perform surface clearing, pixel conversion
00034 // and stretching/blitting.
00035 
00036 #ifdef HAS_HERMES
00037 // These are the source packing formats that vgui_glDrawPixels will support
00038 struct gl_to_hermes_format_map
00039 {
00040   GLenum gl_format;
00041   GLenum gl_type;
00042   int32 bits_per_pixel;
00043   int32 red_mask;
00044   int32 green_mask;
00045   int32 blue_mask;
00046   int32 alpha_mask;
00047 };
00048 
00049 // swap between little and big endian 32bit words.
00050 #define endian_swap32(x) (((x&0x000000ff)<<24) | ((x&0x0000ff00)<<8) | ((x&0x00ff0000)>>8) | ((x&0xff000000)>>24))
00051 
00052 // To reduce maintenance, the table is written as if for a little-endian
00053 // machine, but with silly macro calls to make it work for bit-endian too.
00054 // If you can't think little-endian, just image bytes of memory laid out
00055 // linearly, but with addresses increasing to the left.
00056 #if VXL_LITTLE_ENDIAN
00057 # define s(x) x
00058 #else
00059 # define s(x) endian_swap32(x)
00060 #endif
00061 gl_to_hermes_format_map gl_to_hermes_formats[] =
00062 {
00063   {GL_RGBA,     GL_UNSIGNED_BYTE,        32, s(0x000000ff), s(0x0000ff00), s(0x00ff0000), 0},
00064   {GL_BGRA,     GL_UNSIGNED_BYTE,        32, s(0x00ff0000), s(0x0000ff00), s(0x000000ff), 0},
00065   {GL_ABGR_EXT, GL_UNSIGNED_BYTE,        32, s(0xff000000), s(0x00ff0000), s(0x0000ff00), 0},
00066   {GL_BGR,      GL_UNSIGNED_BYTE,        24, s(0x00ff0000), s(0x0000ff00), s(0x000000ff), 0},
00067   {GL_RGB,      GL_UNSIGNED_SHORT_5_6_5, 16,  (0x0000f800),  (0x000007e0),  (0x0000001f), 0}
00068   // capes - this last format assumes whole word read/writes
00069 };
00070 #undef s
00071 const int number_of_accelerated_formats = sizeof(gl_to_hermes_formats) / sizeof(gl_to_hermes_formats[0]);
00072 #endif
00073 
00074 vgui_accelerate_x11::vgui_accelerate_x11()
00075 {
00076   vcl_cerr << __FILE__ ": Initializing Mesa/X11 accelerator\n";
00077 
00078 #ifdef HAS_HERMES
00079   vcl_cerr << __FILE__ ": Initializing Hermes\n";
00080   Hermes_Init();
00081   hermes_clearer = Hermes_ClearerInstance();
00082   hermes_converter = Hermes_ConverterInstance(HERMES_CONVERT_NORMAL);
00083 #endif
00084 
00085   aux_buffer = 0;
00086   aux_buffer_size = 0;
00087 }
00088 
00089 vgui_accelerate_x11::~vgui_accelerate_x11()
00090 {
00091   vcl_cerr << __FILE__ ": Destroying Mesa/X11 accelerator\n";
00092 
00093   delete[] aux_buffer;
00094 
00095 #ifdef HAS_HERMES
00096   Hermes_ClearerReturn(hermes_clearer);
00097   Hermes_ConverterReturn(hermes_converter);
00098   Hermes_Done();
00099 #endif
00100 }
00101 
00102 bool vgui_accelerate_x11::vgui_glClear( GLbitfield mask )
00103 {
00104   if (!vgui_no_acceleration)
00105   {
00106 #if defined(VGUI_MESA) && defined(HAS_HERMES)
00107     GLint render_mode;
00108     GLboolean rgba_mode;
00109     GLint draw_buffer;
00110     glGetIntegerv (GL_RENDER_MODE, &render_mode);
00111     glGetBooleanv (GL_RGBA_MODE, &rgba_mode);
00112     glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
00113     if (render_mode == GL_RENDER && (draw_buffer == GL_BACK || draw_buffer == GL_BACK_LEFT) && rgba_mode == GL_TRUE)
00114     {
00115       if (mask & GL_COLOR_BUFFER_BIT)
00116       {
00117         Pixmap p_dummy;
00118         XImage* backbuffer;
00119         XMesaBuffer mesabuf = XMesaGetCurrentBuffer();
00120         XMesaGetBackBuffer(mesabuf, &p_dummy, &backbuffer);
00121 
00122         int x_min, x_max, y_min, y_max;
00123         GLboolean scissor_enabled;
00124         glGetBooleanv(GL_SCISSOR_TEST, &scissor_enabled);
00125         if (scissor_enabled) {
00126           GLint scissor_box[4];
00127           glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
00128           x_min = vcl_max(scissor_box[0], 0);
00129           y_min = vcl_max(scissor_box[1], 0);
00130           x_max = vcl_min(scissor_box[0] + scissor_box[2], backbuffer->width);
00131           y_max = vcl_min(scissor_box[1] + scissor_box[3], backbuffer->height);
00132         } else {
00133           x_min = 0;
00134           y_min = 0;
00135           x_max = backbuffer->width;
00136           y_max = backbuffer->height;
00137         }
00138 
00139         GLfloat clear_color[4];
00140         glGetFloatv(GL_COLOR_CLEAR_VALUE, clear_color);
00141 
00142         HermesFormat* dest_format =
00143            Hermes_FormatNew(backbuffer->bits_per_pixel,
00144                             backbuffer->red_mask, backbuffer->green_mask, backbuffer->blue_mask, 0, 0);
00145         Hermes_ClearerRequest (hermes_clearer, dest_format);
00146         Hermes_ClearerClear(hermes_clearer,backbuffer->data,
00147                             x_min, backbuffer->height - y_max,
00148                             x_max - x_min, y_max - y_min,
00149                             backbuffer->bytes_per_line,
00150                             (int32)(clear_color[0]*255.0F),
00151                             (int32)(clear_color[1]*255.0F),
00152                             (int32)(clear_color[2]*255.0F),
00153                             (int32)(clear_color[3]*255.0F));
00154         Hermes_FormatFree(dest_format);
00155       }
00156       GLbitfield leftover = mask & ~(GL_COLOR_BUFFER_BIT);
00157       if (leftover != 0) glClear( leftover );
00158       return true;
00159     }
00160 #endif  // If we have been successful then we should never reach this point!!
00161   }
00162 
00163   // Call the baseclass method as a fallback
00164   return vgui_accelerate::vgui_glClear( mask );
00165 }
00166 
00167 bool vgui_accelerate_x11::vgui_glDrawPixels( GLsizei width, GLsizei height,
00168                                              GLenum format, GLenum type,
00169                                              const GLvoid *pixels )
00170 {
00171   if (!vgui_no_acceleration)
00172   {
00173 #if defined(VGUI_MESA) && defined(HAS_HERMES)
00174     GLint render_mode;
00175     GLboolean rgba_mode;
00176     GLint draw_buffer;
00177     GLboolean raster_pos_valid;
00178     GLboolean depth_test_enabled;
00179     GLfloat pixel_zoom_x;
00180     GLfloat pixel_zoom_y;
00181     glGetBooleanv (GL_CURRENT_RASTER_POSITION_VALID, &raster_pos_valid);
00182     if (!raster_pos_valid) return true;
00183     glGetIntegerv (GL_RENDER_MODE, &render_mode);
00184     glGetBooleanv (GL_RGBA_MODE, &rgba_mode);
00185     glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
00186     glGetBooleanv (GL_DEPTH_TEST, &depth_test_enabled);
00187     glGetFloatv (GL_ZOOM_X, &pixel_zoom_x);
00188     glGetFloatv (GL_ZOOM_Y, &pixel_zoom_y);
00189     if (render_mode == GL_RENDER && (draw_buffer == GL_BACK || draw_buffer == GL_BACK_LEFT) && rgba_mode == GL_TRUE &&
00190         !depth_test_enabled && pixel_zoom_x > 0 && pixel_zoom_y < 0)
00191     {
00192       // See if there is a Hermes renderer for the specified GL format
00193       bool found_match = false;
00194       int format_index;
00195       for (int i=0; i < number_of_accelerated_formats && !found_match; ++i) {
00196         if (gl_to_hermes_formats[i].gl_format == format &&  gl_to_hermes_formats[i].gl_type == type) {
00197           found_match = true;
00198           format_index = i;
00199         }
00200       }
00201       if (found_match)
00202       {
00203         HermesFormat* src_format =
00204              Hermes_FormatNew(gl_to_hermes_formats[format_index].bits_per_pixel,
00205                               gl_to_hermes_formats[format_index].red_mask,
00206                               gl_to_hermes_formats[format_index].green_mask,
00207                               gl_to_hermes_formats[format_index].blue_mask,
00208                               0, 0);
00209         Pixmap p_dummy;
00210         XImage* backbuffer;
00211         XMesaBuffer mesabuf = XMesaGetCurrentBuffer();
00212         XMesaGetBackBuffer(mesabuf, &p_dummy, &backbuffer);
00213 
00214         HermesFormat* dest_format =
00215               Hermes_FormatNew(backbuffer->bits_per_pixel,
00216                                backbuffer->red_mask,
00217                                backbuffer->green_mask,
00218                                backbuffer->blue_mask,
00219                                0, 0);
00220 
00221         // 1 Get RasterPos, unpack_skip_pixels, unpack_skip_rows and pixel_zoom
00222         GLint raster_pos[4];
00223         GLint skip_pixels;
00224         GLint skip_rows;
00225         glGetIntegerv (GL_CURRENT_RASTER_POSITION, raster_pos);
00226         glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skip_pixels);
00227         glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skip_rows);
00228 
00229 #if 0 // commented out
00230         vcl_cerr << "bb width " << backbuffer->width << vcl_endl
00231                  << "bb height " << backbuffer->height << vcl_endl
00232                  << "raspos " << raster_pos[0] << ' ' << raster_pos[1]
00233                  << ' ' << raster_pos[2] << ' ' << raster_pos[3] << vcl_endl
00234                  << "pz x " << pixel_zoom_x << vcl_endl
00235                  << "pz y " << pixel_zoom_y << vcl_endl
00236                  << "skip x " << skip_pixels << vcl_endl
00237                  << "skip y " << skip_rows << vcl_endl
00238                  << "width " << width << vcl_endl
00239                  << "height " << height << vcl_endl;
00240 #endif
00241 
00242         // Find the OUTERMOST boundary of the scaled src image in window coords
00243         float dest_x_min, dest_y_min, dest_x_max, dest_y_max;
00244         if (pixel_zoom_x > 0) {
00245           dest_x_min = raster_pos[0];
00246           dest_x_max = raster_pos[0] + pixel_zoom_x * width;
00247         } else {
00248           dest_x_min = raster_pos[0] + 1 + pixel_zoom_x * width;
00249           dest_x_max = raster_pos[0] + 1;
00250         }
00251         if (pixel_zoom_y > 0) {
00252           dest_y_min = raster_pos[1];
00253           dest_y_max = raster_pos[1] + pixel_zoom_y * height;
00254         } else {
00255           dest_y_min = raster_pos[1] + 1 + pixel_zoom_y * height;
00256           dest_y_max = raster_pos[1] + 1;
00257         }
00258 
00259 #if 0 // commented out
00260         vcl_cerr << dest_x_min << ' ' << dest_x_max << vcl_endl
00261                  << dest_y_min << ' ' << dest_y_max << vcl_endl;
00262 #endif
00263 
00264         float window_x_min, window_y_min, window_x_max, window_y_max;
00265         GLboolean scissor_enabled;
00266         glGetBooleanv (GL_SCISSOR_TEST, &scissor_enabled);
00267         if (scissor_enabled) {
00268           GLint scissor_box[4];
00269           glGetIntegerv (GL_SCISSOR_BOX, scissor_box);
00270           window_x_min = vcl_max(scissor_box[0], 0);
00271           window_y_min = vcl_max(scissor_box[1], 0);
00272           window_x_max = vcl_min(scissor_box[0] + scissor_box[2], backbuffer->width);
00273           window_y_max = vcl_min(scissor_box[1] + scissor_box[3], backbuffer->height);
00274         } else {
00275           window_x_min = 0.0;
00276           window_y_min = 0.0;
00277           window_x_max = backbuffer->width;
00278           window_y_max = backbuffer->height;
00279         }
00280 
00281         int src_x_min = 0;
00282         int src_y_min = 0;
00283         int src_x_max = width;
00284         int src_y_max = height;
00285 
00286         float abs_px_x = vcl_fabs(pixel_zoom_x);
00287         float abs_px_y = vcl_fabs(pixel_zoom_y);
00288 
00289         if (dest_x_min < window_x_min) {
00290           int dw = (int)vcl_ceil((window_x_min - dest_x_min)/abs_px_x);
00291           src_x_min += dw;
00292           dest_x_min += dw * abs_px_x;
00293         }
00294         if (dest_x_max > window_x_max) {
00295           int dw = (int)vcl_ceil((dest_x_max - window_x_max)/abs_px_x);
00296           src_x_max -= dw;
00297           dest_x_max -= dw * abs_px_x;
00298         }
00299         if (dest_y_min < window_y_min) {
00300           int dh = (int)vcl_ceil((window_y_min - dest_y_min)/abs_px_y);
00301           src_y_min += dh;
00302           dest_y_min += dh * abs_px_y;
00303         }
00304         if (dest_y_max > window_y_max) {
00305           int dh = (int)vcl_ceil((dest_y_max - window_y_max)/abs_px_y);
00306           src_y_max -= dh;
00307           dest_y_max -= dh * abs_px_y;
00308         }
00309 
00310 #if 0 // commented out
00311         vcl_cerr << "clipped dest -\n"
00312                  << dest_x_min << ' ' << dest_x_max << vcl_endl
00313                  << dest_y_min << ' ' << dest_y_max << vcl_endl
00314                  << "clipped src -\n"
00315                  << src_x_min << ' ' << src_x_max << vcl_endl
00316                  << src_y_min << ' ' << src_y_max << vcl_endl;
00317 #endif
00318 
00319         // Okay, the destination rectangle should be correct. Now lets adjust skip_pixels and skip_rows,
00320         // based on the signs of pixel zoom_x/y
00321         if (pixel_zoom_x > 0)
00322           skip_pixels += src_x_min;
00323         else
00324           skip_pixels += (width - src_x_max);
00325 
00326         if (pixel_zoom_y > 0)
00327           skip_rows += src_y_min;
00328         else
00329           skip_rows += (height - src_y_max);
00330 
00331         // Get the pixel store parameters
00332         GLint row_length;
00333         glGetIntegerv (GL_UNPACK_ROW_LENGTH, &row_length);
00334         if (row_length == 0) row_length = width;
00335         GLint unpack_alignment;
00336         glGetIntegerv (GL_UNPACK_ALIGNMENT, &unpack_alignment);
00337         int src_pitch = (int)vcl_ceil(double(row_length) * (src_format->bits >> 3) / unpack_alignment);
00338 
00339         // Now choose a renderer depending on the sign of pixel_zoom_y
00340         if (pixel_zoom_y > 0) {
00341 
00342           // ***  Use Hermes to do a span-based render in bottom-to-top direction ***
00343 
00344         } else {
00345           Hermes_ConverterRequest(hermes_converter, src_format, dest_format);
00346           Hermes_ConverterCopy(hermes_converter,
00347                                (void *)pixels,
00348                                skip_pixels, skip_rows,
00349                                src_x_max - src_x_min, src_y_max - src_y_min,
00350                                src_pitch,
00351                                backbuffer->data,
00352                                (int)(dest_x_min + 0.5F), (int)(backbuffer->height - dest_y_max + 0.5F),
00353                                (int)(dest_x_max - dest_x_min + 0.5F), (int)(dest_y_max - dest_y_min + 0.5F),
00354                                backbuffer->bytes_per_line);
00355 
00356           Hermes_FormatFree(src_format);
00357           Hermes_FormatFree(dest_format);
00358         }
00359         return true;
00360       }
00361     }
00362 #endif // If we have been successful then we should never reach this point!!
00363   }
00364 
00365   // Call the baseclass method as a fallback
00366   return vgui_accelerate::vgui_glDrawPixels( width, height, format, type, pixels );
00367 }
00368 
00369 // If it seems likely that the cached section will be rendered using the Hermes implementation
00370 // of vgui_glDrawPixels, then we should cache the section in the same format as the Mesa backbuffer
00371 // XImage. The mapping between XImage/Hermes pixel formats and GL formats is given at the top of
00372 // file.
00373 bool vgui_accelerate_x11::vgui_choose_cache_format(GLenum* format, GLenum* type)
00374 {
00375   if (!vgui_no_acceleration)
00376   {
00377 #if defined(VGUI_MESA) && defined(HAS_HERMES)
00378     // This function may be called before a valid GL context is in place, so we must get the
00379     // cache format from the current X display and assume that Mesa is going to create an XImage
00380     // backbuffer with the same format.
00381     XMesaBuffer mesabuf = XMesaGetCurrentBuffer();
00382     // There must be a valid GL context initialized before this call can be made
00383     assert(mesabuf != 0);
00384     Pixmap p_dummy;
00385     GLint render_mode;
00386     GLboolean rgba_mode;
00387     GLint draw_buffer;
00388     GLboolean depth_test_enabled;
00389     glGetIntegerv (GL_RENDER_MODE, &render_mode);
00390     glGetBooleanv (GL_RGBA_MODE, &rgba_mode);
00391     glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
00392     glGetBooleanv (GL_DEPTH_TEST, &depth_test_enabled);
00393     if (render_mode == GL_RENDER
00394         && (draw_buffer == GL_BACK || draw_buffer == GL_BACK_LEFT)
00395         && rgba_mode == GL_TRUE
00396         && !depth_test_enabled)
00397     {
00398       XImage* backbuffer;
00399       XMesaGetBackBuffer(mesabuf, &p_dummy, &backbuffer);
00400       // See if we can find a GL format renderable by Hermes which is consistent with the XImage pixel format
00401       bool found_match = false;
00402       for (int i=0; i < number_of_accelerated_formats && !found_match; ++i) {
00403         if (gl_to_hermes_formats[i].bits_per_pixel == backbuffer->bits_per_pixel &&
00404             gl_to_hermes_formats[i].red_mask == backbuffer->red_mask &&
00405             gl_to_hermes_formats[i].green_mask == backbuffer->green_mask &&
00406             gl_to_hermes_formats[i].blue_mask == backbuffer->blue_mask) {
00407           (*format) = gl_to_hermes_formats[i].gl_format;
00408           (*type) = gl_to_hermes_formats[i].gl_type;
00409           found_match = true;
00410         }
00411       }
00412       if (found_match) return true;
00413     }
00414 #endif
00415   }
00416 
00417   // Call the baseclass method as a fallback
00418   return vgui_accelerate::vgui_choose_cache_format( format, type );
00419 }
00420 
00421 bool vgui_accelerate_x11::vgui_copy_back_to_aux ()
00422 {
00423   if (!vgui_no_acceleration)
00424   {
00425 #if defined(VGUI_MESA)
00426     XMesaBuffer mesabuf = XMesaGetCurrentBuffer();
00427     assert(mesabuf != 0);   // There must be a valid GL context initialized before this call can be made
00428     Pixmap p_dummy;
00429     GLint render_mode;
00430     GLint draw_buffer;
00431     glGetIntegerv (GL_RENDER_MODE, &render_mode);
00432     glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
00433     if (render_mode == GL_RENDER && (draw_buffer == GL_BACK || draw_buffer == GL_BACK_LEFT)) {
00434       XImage* backbuffer;
00435       XMesaGetBackBuffer(mesabuf, &p_dummy, &backbuffer);
00436       int blit_size = backbuffer->bytes_per_line * backbuffer->height;
00437       // resize the aux_buffer if necessary
00438 #ifdef DEBUG
00439       vcl_cerr << "blit_size = " << blit_size << '\n';
00440 #endif
00441       if (blit_size != aux_buffer_size) {
00442         delete[] aux_buffer;
00443         aux_buffer = new char[blit_size];
00444         aux_buffer_size = blit_size;
00445       }
00446       vcl_memcpy(aux_buffer, backbuffer->data, blit_size);
00447       return true;
00448     }
00449 #endif
00450   }
00451   return false;
00452 }
00453 
00454 bool vgui_accelerate_x11::vgui_copy_aux_to_back ()
00455 {
00456 #if defined(VGUI_MESA)
00457   if (!vgui_no_acceleration) {
00458     XMesaBuffer mesabuf = XMesaGetCurrentBuffer();
00459     assert(mesabuf != 0);   // There must be a valid GL context initialized before this call can be made
00460     Pixmap p_dummy;
00461     GLint render_mode;
00462     GLint draw_buffer;
00463     glGetIntegerv (GL_RENDER_MODE, &render_mode);
00464     glGetIntegerv (GL_DRAW_BUFFER, &draw_buffer);
00465     if (render_mode == GL_RENDER && (draw_buffer == GL_BACK || draw_buffer == GL_BACK_LEFT)) {
00466       XImage* backbuffer;
00467       XMesaGetBackBuffer(mesabuf, &p_dummy, &backbuffer);
00468       int blit_size = backbuffer->bytes_per_line * backbuffer->height;
00469       assert(aux_buffer_size > 0);
00470       vcl_memcpy(backbuffer->data, aux_buffer, blit_size);
00471     }
00472     return true;
00473   }
00474 #endif
00475   return false;
00476 }
00477 
00478 #endif // matches #if VGUI_MESA