Panda3D

osxGraphicsPipe.cxx

00001 ////////////////////////////////////////////////////////////////////
00002 //
00003 // PANDA 3D SOFTWARE
00004 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00005 //
00006 // All use of this software is subject to the terms of the revised BSD
00007 // license.  You should have received a copy of this license along
00008 // with this source code in a file named "LICENSE."
00009 //
00010 ////////////////////////////////////////////////////////////////////
00011 
00012 #include "osxGraphicsPipe.h"
00013 #include "config_osxdisplay.h"
00014 #include "osxGraphicsWindow.h"
00015 #include "osxGraphicsBuffer.h"
00016 #include "osxGraphicsStateGuardian.h"
00017 #include "pnmImage.h"
00018 #include "subprocessWindow.h"
00019 #include "nativeWindowHandle.h"
00020 #import <Carbon/Carbon.h>
00021 
00022 // some macros to make code more readable.
00023 #define GetModeWidth(mode) GetDictionaryLong((mode), kCGDisplayWidth)
00024 #define GetModeHeight(mode) GetDictionaryLong((mode), kCGDisplayHeight)
00025 #define GetModeRefreshRate(mode) GetDictionaryLong((mode), kCGDisplayRefreshRate)
00026 #define GetModeBitsPerPixel(mode) GetDictionaryLong((mode), kCGDisplayBitsPerPixel)
00027 #define GetModeSafeForHardware(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsSafeForHardware)
00028 #define GetModeStretched(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsStretched)
00029 #define MAX_DISPLAYS 32
00030 
00031 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key) {
00032   // get a boolean from the dictionary
00033   Boolean value = false;
00034   CFBooleanRef boolRef;
00035   boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
00036   if (boolRef != NULL)
00037     value = CFBooleanGetValue(boolRef);   
00038   return value;
00039 }
00040 
00041 long GetDictionaryLong(CFDictionaryRef theDict, const void* key) {
00042   // get a long from the dictionary
00043   long value = 0;
00044   CFNumberRef numRef;
00045   numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
00046   if (numRef != NULL)
00047     CFNumberGetValue(numRef, kCFNumberLongType, &value);   
00048   return value;
00049 }
00050 
00051 static CFComparisonResult CompareModes (const void *val1,const void *val2,void *context) {
00052   // CFArray comparison callback for sorting display modes.
00053 #pragma unused(context)
00054   CFDictionaryRef thisMode = (CFDictionaryRef)val1;
00055   CFDictionaryRef otherMode = (CFDictionaryRef)val2;
00056    
00057   long width = GetModeWidth(thisMode);
00058   long otherWidth = GetModeWidth(otherMode);
00059   long height = GetModeHeight(thisMode);
00060   long otherHeight = GetModeHeight(otherMode);
00061    
00062   // sort modes in screen size order
00063   if (width * height < otherWidth * otherHeight) {
00064     return kCFCompareLessThan;
00065   } else if (width * height > otherWidth * otherHeight) {
00066     return kCFCompareGreaterThan;
00067   }
00068    
00069   // sort modes by bits per pixel
00070   long bitsPerPixel = GetModeBitsPerPixel(thisMode);
00071   long otherBitsPerPixel = GetModeBitsPerPixel(otherMode);   
00072   if (bitsPerPixel < otherBitsPerPixel) {
00073     return kCFCompareLessThan;
00074   } else if (bitsPerPixel > otherBitsPerPixel) {
00075     return kCFCompareGreaterThan;
00076   }
00077    
00078   // sort modes by refresh rate.
00079   long refreshRate = GetModeRefreshRate(thisMode);
00080   long otherRefreshRate = GetModeRefreshRate(otherMode);   
00081   if (refreshRate < otherRefreshRate) {
00082     return kCFCompareLessThan;
00083   } else if (refreshRate > otherRefreshRate) {
00084     return kCFCompareGreaterThan;
00085   }
00086      
00087   return kCFCompareEqualTo;
00088 }
00089 
00090 CFArrayRef GSCGDisplayAvailableModesUsefulForOpenGL(CGDirectDisplayID display) {
00091   // get a list of all possible display modes for this system.
00092   CFArrayRef availableModes = CGDisplayAvailableModes(display);
00093   unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
00094  
00095   // creat mutable array to hold the display modes we are interested int.
00096   CFMutableArrayRef usefulModes = CFArrayCreateMutable(kCFAllocatorDefault, numberOfAvailableModes, NULL);
00097  
00098   // get the current bits per pixel.
00099   long currentModeBitsPerPixel = GetModeBitsPerPixel(CGDisplayCurrentMode(display));
00100  
00101   unsigned int i;
00102   for (i= 0; i<numberOfAvailableModes; ++i) {
00103     // look at each mode in the available list
00104     CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
00105      
00106     // we are only interested in modes with the same bits per pixel as current.
00107     //   to allow for switching from fullscreen to windowed modes.
00108     // that are safe for this hardward
00109     // that are not stretched.
00110     long bitsPerPixel = GetModeBitsPerPixel(mode);
00111     Boolean safeForHardware = GetModeSafeForHardware(mode);
00112     Boolean stretched = GetModeStretched(mode);
00113    
00114     if ((bitsPerPixel != currentModeBitsPerPixel) || (!safeForHardware) || (stretched)) {
00115       continue; // skip this mode
00116     }
00117      
00118     long width = GetModeWidth(mode);
00119     long height = GetModeHeight(mode);
00120     long refreshRate = GetModeRefreshRate(mode);     
00121     Boolean replaced = false;
00122     Boolean skipped = false;
00123      
00124     // now check to see if we already added a mode like this one.
00125     //   we want the highest refresh rate for this width/height
00126     unsigned int j;
00127     unsigned int currentNumberOfUsefulModes =  CFArrayGetCount(usefulModes);
00128     for (j = 0; j < currentNumberOfUsefulModes; ++j) {
00129       CFDictionaryRef otherMode = (CFDictionaryRef)CFArrayGetValueAtIndex(usefulModes, j);       
00130       long otherWidth = GetModeWidth(otherMode);
00131       long otherHeight = GetModeHeight(otherMode);       
00132       if ((otherWidth == width) && (otherHeight == height)) {
00133         long otherRefreshRate = GetModeRefreshRate(otherMode);         
00134         if (otherRefreshRate < refreshRate) {
00135           // replace lower refresh rate.
00136           const void* value = mode;
00137           CFArrayReplaceValues(usefulModes, CFRangeMake(j ,1), &value, 1);
00138           replaced = true;
00139           break;
00140         }
00141         else if (otherRefreshRate > refreshRate) {
00142           skipped = true;
00143           break;
00144         }
00145       }
00146     }     
00147     // this is a useful mode so add it to the array.
00148     if (!replaced && !skipped) {
00149       CFArrayAppendValue(usefulModes, mode);
00150     }     
00151   }   
00152   // now sort the useful mode array, using the comparison callback.
00153   CFArraySortValues( usefulModes,
00154     CFRangeMake(0, CFArrayGetCount(usefulModes)),
00155     (CFComparatorFunction) CompareModes, NULL); 
00156   // return the CFArray of the useful display modes.
00157   return usefulModes;
00158 }
00159 
00160 TypeHandle osxGraphicsPipe::_type_handle;
00161   
00162 ////////////////////////////////////////////////////////////////////
00163 //     Function: osxGraphicsPipe::Constructor
00164 //       Access: Public
00165 //  Description: 
00166 ////////////////////////////////////////////////////////////////////
00167 osxGraphicsPipe::
00168 osxGraphicsPipe() {
00169   CGRect display_bounds = CGDisplayBounds(kCGDirectMainDisplay);
00170   _display_width = CGRectGetWidth(display_bounds);
00171   _display_height = CGRectGetHeight(display_bounds);
00172  
00173   CGDirectDisplayID display, displayArray[MAX_DISPLAYS] ;
00174   CGDisplayCount numDisplays;
00175   CFDictionaryRef displayMode; 
00176   CFArrayRef displayModeArray;
00177   int number, i; 
00178   CGGetActiveDisplayList (MAX_DISPLAYS, displayArray, &numDisplays);
00179   display = displayArray [numDisplays - 1];
00180   displayModeArray = GSCGDisplayAvailableModesUsefulForOpenGL( display );   
00181   number = CFArrayGetCount( displayModeArray );
00182   DisplayMode *displays = new DisplayMode[ number ]; 
00183   for(i = 0; i < number; i++) {     
00184      displayMode = (CFDictionaryRef) CFArrayGetValueAtIndex (displayModeArray, i);
00185      _display_information -> _total_display_modes++;     
00186      displays[i].width = (signed int)GetModeWidth (displayMode);
00187      displays[i].height = (signed int)GetModeHeight (displayMode);
00188      displays[i].bits_per_pixel = (signed int)GetModeBitsPerPixel (displayMode);
00189      displays[i].refresh_rate = (signed int)GetModeRefreshRate (displayMode);
00190   }   
00191   _display_information -> _display_mode_array = displays;
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: osxGraphicsPipe::Destructor
00196 //       Access: Public, Virtual
00197 //  Description: 
00198 ////////////////////////////////////////////////////////////////////
00199 osxGraphicsPipe::
00200 ~osxGraphicsPipe() {
00201 }
00202 
00203 ////////////////////////////////////////////////////////////////////
00204 //     Function: osxGraphicsPipe::get_interface_name
00205 //       Access: Published, Virtual
00206 //  Description: Returns the name of the rendering interface
00207 //               associated with this GraphicsPipe.  This is used to
00208 //               present to the user to allow him/her to choose
00209 //               between several possible GraphicsPipes available on a
00210 //               particular platform, so the name should be meaningful
00211 //               and unique for a given platform.
00212 ////////////////////////////////////////////////////////////////////
00213 string osxGraphicsPipe::
00214 get_interface_name() const {
00215   return "OpenGL";
00216 }
00217 
00218 ////////////////////////////////////////////////////////////////////
00219 //     Function: osxGraphicsPipe::pipe_constructor
00220 //       Access: Public, Static
00221 //  Description: This function is passed to the GraphicsPipeSelection
00222 //               object to allow the user to make a default
00223 //               osxGraphicsPipe.
00224 ////////////////////////////////////////////////////////////////////
00225 PT(GraphicsPipe) osxGraphicsPipe::
00226 pipe_constructor() {
00227   return new osxGraphicsPipe;
00228 }
00229 
00230 ////////////////////////////////////////////////////////////////////
00231 //     Function: osxGraphicsPipe::get_preferred_window_thread
00232 //       Access: Public, Virtual
00233 //  Description: Returns an indication of the thread in which this
00234 //               GraphicsPipe requires its window processing to be
00235 //               performed: typically either the app thread (e.g. X)
00236 //               or the draw thread (Windows).
00237 ////////////////////////////////////////////////////////////////////
00238 GraphicsPipe::PreferredWindowThread 
00239 osxGraphicsPipe::get_preferred_window_thread() const {
00240   return PWT_app;
00241 }
00242 
00243 ////////////////////////////////////////////////////////////////////
00244 //     Function: osxGraphicsPipe::create_cg_image
00245 //       Access: Public, Static
00246 //  Description: Creates a new Quartz bitmap image with the data in
00247 //               the indicated PNMImage.  The caller should eventually
00248 //               free this image via CGImageRelease.
00249 ////////////////////////////////////////////////////////////////////
00250 CGImageRef osxGraphicsPipe::
00251 create_cg_image(const PNMImage &pnm_image) {
00252   size_t width = pnm_image.get_x_size();
00253   size_t height = pnm_image.get_y_size();
00254 
00255 #ifdef PGM_BIGGRAYS
00256   size_t bytes_per_component = 2;
00257 #else
00258   size_t bytes_per_component = 1;
00259 #endif
00260   size_t bits_per_component = bytes_per_component * 8;
00261   size_t num_components = pnm_image.get_num_channels();
00262 
00263   size_t bits_per_pixel = num_components * bits_per_component;
00264   size_t bytes_per_row = num_components * bytes_per_component * width;
00265 
00266   size_t num_bytes = bytes_per_row * height;
00267   bool has_alpha;
00268   bool is_grayscale;
00269 
00270   CFStringRef color_space_name = NULL;
00271   switch (pnm_image.get_color_type()) {
00272   case PNMImage::CT_grayscale:
00273     color_space_name = kCGColorSpaceGenericGray;
00274     has_alpha = false;
00275     is_grayscale = true;
00276     break;
00277 
00278   case PNMImage::CT_two_channel:
00279     color_space_name = kCGColorSpaceGenericGray;
00280     has_alpha = true;
00281     is_grayscale = true;
00282     break;
00283 
00284   case PNMImage::CT_color:
00285     color_space_name = kCGColorSpaceGenericRGB;
00286     has_alpha = false;
00287     is_grayscale = false;
00288     break;
00289 
00290   case PNMImage::CT_four_channel:
00291     color_space_name = kCGColorSpaceGenericRGB;
00292     has_alpha = true;
00293     is_grayscale = false;
00294     break;
00295 
00296   case PNMImage::CT_invalid:
00297     // Shouldn't get here.
00298     nassertr(false, NULL);
00299     break;
00300   }
00301   nassertr(color_space_name != NULL, NULL);
00302 
00303   CGColorSpaceRef color_space = CGColorSpaceCreateWithName(color_space_name);
00304   nassertr(color_space != NULL, NULL);
00305 
00306   CGBitmapInfo bitmap_info = 0;
00307 #ifdef PGM_BIGGRAYS
00308   bitmap_info |= kCGBitmapByteOrder16Host;
00309 #endif
00310   if (has_alpha) {
00311     bitmap_info |= kCGImageAlphaLast;
00312   }
00313 
00314   // Now convert the pixel data to a format friendly to
00315   // CGImageCreate().
00316   char *char_array = (char *)PANDA_MALLOC_ARRAY(num_bytes);
00317 
00318   xelval *dp = (xelval *)char_array;
00319   for (size_t yi = 0; yi < height; ++yi) {
00320     for (size_t xi = 0; xi < width; ++xi) {
00321       if (is_grayscale) {
00322         *dp++ = (xelval)(pnm_image.get_gray(xi, yi) * PGM_MAXMAXVAL);
00323       } else {
00324         *dp++ = (xelval)(pnm_image.get_red(xi, yi) * PGM_MAXMAXVAL);
00325         *dp++ = (xelval)(pnm_image.get_green(xi, yi) * PGM_MAXMAXVAL);
00326         *dp++ = (xelval)(pnm_image.get_blue(xi, yi) * PGM_MAXMAXVAL);
00327       }
00328       if (has_alpha) {
00329         *dp++ = (xelval)(pnm_image.get_alpha(xi, yi) * PGM_MAXMAXVAL);
00330       }
00331     }
00332   }
00333   nassertr((void *)dp == (void *)(char_array + num_bytes), NULL);
00334 
00335   CGDataProviderRef provider = 
00336     CGDataProviderCreateWithData(NULL, char_array, num_bytes, release_data);
00337   nassertr(provider != NULL, NULL);
00338 
00339   CGImageRef image = CGImageCreate
00340     (width, height, bits_per_component, bits_per_pixel, bytes_per_row,
00341      color_space, bitmap_info, provider,
00342      NULL, false, kCGRenderingIntentDefault);
00343   nassertr(image != NULL, NULL);
00344 
00345   CGColorSpaceRelease(color_space);
00346   CGDataProviderRelease(provider);
00347 
00348   return image;
00349 }
00350 
00351 ////////////////////////////////////////////////////////////////////
00352 //     Function: osxGraphicsPipe::release_data
00353 //       Access: Private, Static
00354 //  Description: This callback is assigned to delete the data array
00355 //               allocated within create_cg_image().
00356 ////////////////////////////////////////////////////////////////////
00357 void osxGraphicsPipe::
00358 release_data(void *info, const void *data, size_t size) {
00359   char *char_array = (char *)data;
00360   PANDA_FREE_ARRAY(char_array);
00361 }
00362 
00363 ////////////////////////////////////////////////////////////////////
00364 //     Function: osxGraphicsPipe::make_output
00365 //       Access: Protected, Virtual
00366 //  Description: Creates a new window on the pipe, if possible.
00367 ////////////////////////////////////////////////////////////////////
00368 PT(GraphicsOutput) osxGraphicsPipe::
00369 make_output(const string &name,
00370             const FrameBufferProperties &fb_prop,
00371             const WindowProperties &win_prop,
00372             int flags,
00373             GraphicsEngine *engine,
00374             GraphicsStateGuardian *gsg,
00375             GraphicsOutput *host,
00376             int retry,
00377             bool &precertify) {
00378   if (!_is_valid) {
00379     return NULL;
00380   }
00381   
00382   osxGraphicsStateGuardian *osxgsg = 0;
00383   if (gsg != 0) {
00384     DCAST_INTO_R(osxgsg, gsg, NULL);
00385   }
00386 
00387   // First thing to try: an osxGraphicsWindow
00388 
00389   if (retry == 0) {
00390     if (((flags&BF_require_parasite)!=0)||
00391         ((flags&BF_refuse_window)!=0)||
00392         ((flags&BF_resizeable)!=0)||
00393         ((flags&BF_size_track_host)!=0)||
00394         ((flags&BF_can_bind_color)!=0)||
00395         ((flags&BF_can_bind_every)!=0)) {
00396       return NULL;
00397     }
00398     WindowHandle *window_handle = win_prop.get_parent_window();
00399     if (window_handle != NULL) {
00400       osxdisplay_cat.info()
00401         << "Got parent_window " << *window_handle << "\n";
00402 #ifdef SUPPORT_SUBPROCESS_WINDOW
00403       WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
00404       if (os_handle != NULL && 
00405           os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) {
00406         return new SubprocessWindow(engine, this, name, fb_prop, win_prop,
00407                                     flags, gsg, host);
00408       }
00409 #endif  // SUPPORT_SUBPROCESS_WINDOW
00410     }
00411     return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop,
00412                                  flags, gsg, host);
00413   }
00414   
00415   // Second thing to try: a glGraphicsBuffer
00416   
00417   if (retry == 1) {
00418     if (!osx_support_gl_buffer) {
00419       return NULL;
00420     }
00421     if ((host==0)||
00422         ((flags&BF_require_parasite)!=0)||
00423         ((flags&BF_require_window)!=0)) {
00424       return NULL;
00425     }
00426     // Early failure - if we are sure that this buffer WONT
00427     // meet specs, we can bail out early.
00428     if ((flags & BF_fb_props_optional)==0) {
00429       if ((fb_prop.get_indexed_color() > 0)||
00430           (fb_prop.get_back_buffers() > 0)||
00431           (fb_prop.get_accum_bits() > 0)||
00432           (fb_prop.get_multisamples() > 0)) {
00433         return NULL;
00434       }
00435     }
00436     // Early success - if we are sure that this buffer WILL
00437     // meet specs, we can precertify it.
00438     if ((osxgsg != 0) &&
00439         (osxgsg->is_valid()) &&
00440         (!osxgsg->needs_reset()) &&
00441         (osxgsg->_supports_framebuffer_object) &&
00442         (osxgsg->_glDrawBuffers != 0)&&
00443         (fb_prop.is_basic())) {
00444       precertify = true;
00445     }
00446     return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host);
00447   }
00448   
00449   // Third thing to try: an osxGraphicsBuffer
00450   if (retry == 2) {
00451     if ((!support_render_texture)||
00452         ((flags&BF_require_parasite)!=0)||
00453         ((flags&BF_require_window)!=0)||
00454         ((flags&BF_resizeable)!=0)||
00455         ((flags&BF_size_track_host)!=0)||
00456         ((flags&BF_can_bind_every)!=0)) {
00457       return NULL;
00458     }
00459     return new osxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
00460                                  flags, gsg, host);
00461   }
00462 
00463   // Nothing else left to try.
00464   return NULL;
00465 }
00466 
00467 ////////////////////////////////////////////////////////////////////
00468 //     Function: osxGraphicsPipe::make_callback_gsg
00469 //       Access: Protected, Virtual
00470 //  Description: This is called when make_output() is used to create a
00471 //               CallbackGraphicsWindow.  If the GraphicsPipe can
00472 //               construct a GSG that's not associated with any
00473 //               particular window object, do so now, assuming the
00474 //               correct graphics context has been set up externally.
00475 ////////////////////////////////////////////////////////////////////
00476 PT(GraphicsStateGuardian) osxGraphicsPipe::
00477 make_callback_gsg(GraphicsEngine *engine) {
00478   return new osxGraphicsStateGuardian(engine, this, NULL);
00479 }
 All Classes Functions Variables Enumerations