Panda3D
|
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 }