Panda3D
osxGraphicsPipe.cxx
1 ////////////////////////////////////////////////////////////////////
2 //
3 // PANDA 3D SOFTWARE
4 // Copyright (c) Carnegie Mellon University. All rights reserved.
5 //
6 // All use of this software is subject to the terms of the revised BSD
7 // license. You should have received a copy of this license along
8 // with this source code in a file named "LICENSE."
9 //
10 ////////////////////////////////////////////////////////////////////
11 
12 #include "osxGraphicsPipe.h"
13 #include "config_osxdisplay.h"
14 #include "osxGraphicsWindow.h"
15 #include "osxGraphicsBuffer.h"
16 #include "osxGraphicsStateGuardian.h"
17 #include "pnmImage.h"
18 #include "subprocessWindow.h"
19 #include "nativeWindowHandle.h"
20 #import <Carbon/Carbon.h>
21 
22 // some macros to make code more readable.
23 #define GetModeWidth(mode) GetDictionaryLong((mode), kCGDisplayWidth)
24 #define GetModeHeight(mode) GetDictionaryLong((mode), kCGDisplayHeight)
25 #define GetModeRefreshRate(mode) GetDictionaryLong((mode), kCGDisplayRefreshRate)
26 #define GetModeBitsPerPixel(mode) GetDictionaryLong((mode), kCGDisplayBitsPerPixel)
27 #define GetModeSafeForHardware(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsSafeForHardware)
28 #define GetModeStretched(mode) GetDictionaryBoolean((mode), kCGDisplayModeIsStretched)
29 #define MAX_DISPLAYS 32
30 
31 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key) {
32  // get a boolean from the dictionary
33  Boolean value = false;
34  CFBooleanRef boolRef;
35  boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
36  if (boolRef != NULL)
37  value = CFBooleanGetValue(boolRef);
38  return value;
39 }
40 
41 long GetDictionaryLong(CFDictionaryRef theDict, const void* key) {
42  // get a long from the dictionary
43  long value = 0;
44  CFNumberRef numRef;
45  numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
46  if (numRef != NULL)
47  CFNumberGetValue(numRef, kCFNumberLongType, &value);
48  return value;
49 }
50 
51 static CFComparisonResult CompareModes (const void *val1,const void *val2,void *context) {
52  // CFArray comparison callback for sorting display modes.
53 #pragma unused(context)
54  CFDictionaryRef thisMode = (CFDictionaryRef)val1;
55  CFDictionaryRef otherMode = (CFDictionaryRef)val2;
56 
57  long width = GetModeWidth(thisMode);
58  long otherWidth = GetModeWidth(otherMode);
59  long height = GetModeHeight(thisMode);
60  long otherHeight = GetModeHeight(otherMode);
61 
62  // sort modes in screen size order
63  if (width * height < otherWidth * otherHeight) {
64  return kCFCompareLessThan;
65  } else if (width * height > otherWidth * otherHeight) {
66  return kCFCompareGreaterThan;
67  }
68 
69  // sort modes by bits per pixel
70  long bitsPerPixel = GetModeBitsPerPixel(thisMode);
71  long otherBitsPerPixel = GetModeBitsPerPixel(otherMode);
72  if (bitsPerPixel < otherBitsPerPixel) {
73  return kCFCompareLessThan;
74  } else if (bitsPerPixel > otherBitsPerPixel) {
75  return kCFCompareGreaterThan;
76  }
77 
78  // sort modes by refresh rate.
79  long refreshRate = GetModeRefreshRate(thisMode);
80  long otherRefreshRate = GetModeRefreshRate(otherMode);
81  if (refreshRate < otherRefreshRate) {
82  return kCFCompareLessThan;
83  } else if (refreshRate > otherRefreshRate) {
84  return kCFCompareGreaterThan;
85  }
86 
87  return kCFCompareEqualTo;
88 }
89 
90 CFArrayRef GSCGDisplayAvailableModesUsefulForOpenGL(CGDirectDisplayID display) {
91  // get a list of all possible display modes for this system.
92  CFArrayRef availableModes = CGDisplayAvailableModes(display);
93  unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
94 
95  // creat mutable array to hold the display modes we are interested int.
96  CFMutableArrayRef usefulModes = CFArrayCreateMutable(kCFAllocatorDefault, numberOfAvailableModes, NULL);
97 
98  // get the current bits per pixel.
99  long currentModeBitsPerPixel = GetModeBitsPerPixel(CGDisplayCurrentMode(display));
100 
101  unsigned int i;
102  for (i= 0; i<numberOfAvailableModes; ++i) {
103  // look at each mode in the available list
104  CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
105 
106  // we are only interested in modes with the same bits per pixel as current.
107  // to allow for switching from fullscreen to windowed modes.
108  // that are safe for this hardward
109  // that are not stretched.
110  long bitsPerPixel = GetModeBitsPerPixel(mode);
111  Boolean safeForHardware = GetModeSafeForHardware(mode);
112  Boolean stretched = GetModeStretched(mode);
113 
114  if ((bitsPerPixel != currentModeBitsPerPixel) || (!safeForHardware) || (stretched)) {
115  continue; // skip this mode
116  }
117 
118  long width = GetModeWidth(mode);
119  long height = GetModeHeight(mode);
120  long refreshRate = GetModeRefreshRate(mode);
121  Boolean replaced = false;
122  Boolean skipped = false;
123 
124  // now check to see if we already added a mode like this one.
125  // we want the highest refresh rate for this width/height
126  unsigned int j;
127  unsigned int currentNumberOfUsefulModes = CFArrayGetCount(usefulModes);
128  for (j = 0; j < currentNumberOfUsefulModes; ++j) {
129  CFDictionaryRef otherMode = (CFDictionaryRef)CFArrayGetValueAtIndex(usefulModes, j);
130  long otherWidth = GetModeWidth(otherMode);
131  long otherHeight = GetModeHeight(otherMode);
132  if ((otherWidth == width) && (otherHeight == height)) {
133  long otherRefreshRate = GetModeRefreshRate(otherMode);
134  if (otherRefreshRate < refreshRate) {
135  // replace lower refresh rate.
136  const void* value = mode;
137  CFArrayReplaceValues(usefulModes, CFRangeMake(j ,1), &value, 1);
138  replaced = true;
139  break;
140  }
141  else if (otherRefreshRate > refreshRate) {
142  skipped = true;
143  break;
144  }
145  }
146  }
147  // this is a useful mode so add it to the array.
148  if (!replaced && !skipped) {
149  CFArrayAppendValue(usefulModes, mode);
150  }
151  }
152  // now sort the useful mode array, using the comparison callback.
153  CFArraySortValues( usefulModes,
154  CFRangeMake(0, CFArrayGetCount(usefulModes)),
155  (CFComparatorFunction) CompareModes, NULL);
156  // return the CFArray of the useful display modes.
157  return usefulModes;
158 }
159 
160 TypeHandle osxGraphicsPipe::_type_handle;
161 
162 ////////////////////////////////////////////////////////////////////
163 // Function: osxGraphicsPipe::Constructor
164 // Access: Public
165 // Description:
166 ////////////////////////////////////////////////////////////////////
167 osxGraphicsPipe::
168 osxGraphicsPipe() {
169  CGRect display_bounds = CGDisplayBounds(kCGDirectMainDisplay);
170  _display_width = CGRectGetWidth(display_bounds);
171  _display_height = CGRectGetHeight(display_bounds);
172 
173  CGDirectDisplayID display, displayArray[MAX_DISPLAYS] ;
174  CGDisplayCount numDisplays;
175  CFDictionaryRef displayMode;
176  CFArrayRef displayModeArray;
177  int number, i;
178  CGGetActiveDisplayList (MAX_DISPLAYS, displayArray, &numDisplays);
179  display = displayArray [numDisplays - 1];
180  displayModeArray = GSCGDisplayAvailableModesUsefulForOpenGL( display );
181  number = CFArrayGetCount( displayModeArray );
182  DisplayMode *displays = new DisplayMode[ number ];
183  for(i = 0; i < number; i++) {
184  displayMode = (CFDictionaryRef) CFArrayGetValueAtIndex (displayModeArray, i);
185  _display_information -> _total_display_modes++;
186  displays[i].width = (signed int)GetModeWidth (displayMode);
187  displays[i].height = (signed int)GetModeHeight (displayMode);
188  displays[i].bits_per_pixel = (signed int)GetModeBitsPerPixel (displayMode);
189  displays[i].refresh_rate = (signed int)GetModeRefreshRate (displayMode);
190  }
191  _display_information -> _display_mode_array = displays;
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: osxGraphicsPipe::Destructor
196 // Access: Public, Virtual
197 // Description:
198 ////////////////////////////////////////////////////////////////////
199 osxGraphicsPipe::
200 ~osxGraphicsPipe() {
201 }
202 
203 ////////////////////////////////////////////////////////////////////
204 // Function: osxGraphicsPipe::get_interface_name
205 // Access: Published, Virtual
206 // Description: Returns the name of the rendering interface
207 // associated with this GraphicsPipe. This is used to
208 // present to the user to allow him/her to choose
209 // between several possible GraphicsPipes available on a
210 // particular platform, so the name should be meaningful
211 // and unique for a given platform.
212 ////////////////////////////////////////////////////////////////////
213 string osxGraphicsPipe::
215  return "OpenGL";
216 }
217 
218 ////////////////////////////////////////////////////////////////////
219 // Function: osxGraphicsPipe::pipe_constructor
220 // Access: Public, Static
221 // Description: This function is passed to the GraphicsPipeSelection
222 // object to allow the user to make a default
223 // osxGraphicsPipe.
224 ////////////////////////////////////////////////////////////////////
225 PT(GraphicsPipe) osxGraphicsPipe::
226 pipe_constructor() {
227  return new osxGraphicsPipe;
228 }
229 
230 ////////////////////////////////////////////////////////////////////
231 // Function: osxGraphicsPipe::get_preferred_window_thread
232 // Access: Public, Virtual
233 // Description: Returns an indication of the thread in which this
234 // GraphicsPipe requires its window processing to be
235 // performed: typically either the app thread (e.g. X)
236 // or the draw thread (Windows).
237 ////////////////////////////////////////////////////////////////////
238 GraphicsPipe::PreferredWindowThread
240  return PWT_app;
241 }
242 
243 ////////////////////////////////////////////////////////////////////
244 // Function: osxGraphicsPipe::create_cg_image
245 // Access: Public, Static
246 // Description: Creates a new Quartz bitmap image with the data in
247 // the indicated PNMImage. The caller should eventually
248 // free this image via CGImageRelease.
249 ////////////////////////////////////////////////////////////////////
250 CGImageRef osxGraphicsPipe::
251 create_cg_image(const PNMImage &pnm_image) {
252  size_t width = pnm_image.get_x_size();
253  size_t height = pnm_image.get_y_size();
254 
255 #ifdef PGM_BIGGRAYS
256  size_t bytes_per_component = 2;
257 #else
258  size_t bytes_per_component = 1;
259 #endif
260  size_t bits_per_component = bytes_per_component * 8;
261  size_t num_components = pnm_image.get_num_channels();
262 
263  size_t bits_per_pixel = num_components * bits_per_component;
264  size_t bytes_per_row = num_components * bytes_per_component * width;
265 
266  size_t num_bytes = bytes_per_row * height;
267  bool has_alpha;
268  bool is_grayscale;
269 
270  CFStringRef color_space_name = NULL;
271  switch (pnm_image.get_color_type()) {
272  case PNMImage::CT_grayscale:
273  color_space_name = kCGColorSpaceGenericGray;
274  has_alpha = false;
275  is_grayscale = true;
276  break;
277 
278  case PNMImage::CT_two_channel:
279  color_space_name = kCGColorSpaceGenericGray;
280  has_alpha = true;
281  is_grayscale = true;
282  break;
283 
284  case PNMImage::CT_color:
285  color_space_name = kCGColorSpaceGenericRGB;
286  has_alpha = false;
287  is_grayscale = false;
288  break;
289 
290  case PNMImage::CT_four_channel:
291  color_space_name = kCGColorSpaceGenericRGB;
292  has_alpha = true;
293  is_grayscale = false;
294  break;
295 
296  case PNMImage::CT_invalid:
297  // Shouldn't get here.
298  nassertr(false, NULL);
299  break;
300  }
301  nassertr(color_space_name != NULL, NULL);
302 
303  CGColorSpaceRef color_space = CGColorSpaceCreateWithName(color_space_name);
304  nassertr(color_space != NULL, NULL);
305 
306  CGBitmapInfo bitmap_info = 0;
307 #ifdef PGM_BIGGRAYS
308  bitmap_info |= kCGBitmapByteOrder16Host;
309 #endif
310  if (has_alpha) {
311  bitmap_info |= kCGImageAlphaLast;
312  }
313 
314  // Now convert the pixel data to a format friendly to
315  // CGImageCreate().
316  char *char_array = (char *)PANDA_MALLOC_ARRAY(num_bytes);
317 
318  xelval *dp = (xelval *)char_array;
319  for (size_t yi = 0; yi < height; ++yi) {
320  for (size_t xi = 0; xi < width; ++xi) {
321  if (is_grayscale) {
322  *dp++ = (xelval)(pnm_image.get_gray(xi, yi) * PGM_MAXMAXVAL);
323  } else {
324  *dp++ = (xelval)(pnm_image.get_red(xi, yi) * PGM_MAXMAXVAL);
325  *dp++ = (xelval)(pnm_image.get_green(xi, yi) * PGM_MAXMAXVAL);
326  *dp++ = (xelval)(pnm_image.get_blue(xi, yi) * PGM_MAXMAXVAL);
327  }
328  if (has_alpha) {
329  *dp++ = (xelval)(pnm_image.get_alpha(xi, yi) * PGM_MAXMAXVAL);
330  }
331  }
332  }
333  nassertr((void *)dp == (void *)(char_array + num_bytes), NULL);
334 
335  CGDataProviderRef provider =
336  CGDataProviderCreateWithData(NULL, char_array, num_bytes, release_data);
337  nassertr(provider != NULL, NULL);
338 
339  CGImageRef image = CGImageCreate
340  (width, height, bits_per_component, bits_per_pixel, bytes_per_row,
341  color_space, bitmap_info, provider,
342  NULL, false, kCGRenderingIntentDefault);
343  nassertr(image != NULL, NULL);
344 
345  CGColorSpaceRelease(color_space);
346  CGDataProviderRelease(provider);
347 
348  return image;
349 }
350 
351 ////////////////////////////////////////////////////////////////////
352 // Function: osxGraphicsPipe::release_data
353 // Access: Private, Static
354 // Description: This callback is assigned to delete the data array
355 // allocated within create_cg_image().
356 ////////////////////////////////////////////////////////////////////
357 void osxGraphicsPipe::
358 release_data(void *info, const void *data, size_t size) {
359  char *char_array = (char *)data;
360  PANDA_FREE_ARRAY(char_array);
361 }
362 
363 ////////////////////////////////////////////////////////////////////
364 // Function: osxGraphicsPipe::make_output
365 // Access: Protected, Virtual
366 // Description: Creates a new window on the pipe, if possible.
367 ////////////////////////////////////////////////////////////////////
368 PT(GraphicsOutput) osxGraphicsPipe::
369 make_output(const string &name,
370  const FrameBufferProperties &fb_prop,
371  const WindowProperties &win_prop,
372  int flags,
373  GraphicsEngine *engine,
375  GraphicsOutput *host,
376  int retry,
377  bool &precertify) {
378  if (!_is_valid) {
379  return NULL;
380  }
381 
382  osxGraphicsStateGuardian *osxgsg = 0;
383  if (gsg != 0) {
384  DCAST_INTO_R(osxgsg, gsg, NULL);
385  }
386 
387  // First thing to try: an osxGraphicsWindow
388 
389  if (retry == 0) {
390  if (((flags&BF_require_parasite)!=0)||
391  ((flags&BF_refuse_window)!=0)||
392  ((flags&BF_resizeable)!=0)||
393  ((flags&BF_size_track_host)!=0)||
394  ((flags&BF_can_bind_color)!=0)||
395  ((flags&BF_can_bind_every)!=0)||
396  ((flags&BF_can_bind_layered)!=0)) {
397  return NULL;
398  }
399  WindowHandle *window_handle = win_prop.get_parent_window();
400  if (window_handle != NULL) {
401  osxdisplay_cat.info()
402  << "Got parent_window " << *window_handle << "\n";
403 #ifdef SUPPORT_SUBPROCESS_WINDOW
404  WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
405  if (os_handle != NULL &&
406  os_handle->is_of_type(NativeWindowHandle::SubprocessHandle::get_class_type())) {
407  return new SubprocessWindow(engine, this, name, fb_prop, win_prop,
408  flags, gsg, host);
409  }
410 #endif // SUPPORT_SUBPROCESS_WINDOW
411  }
412  return new osxGraphicsWindow(engine, this, name, fb_prop, win_prop,
413  flags, gsg, host);
414  }
415 
416  // Second thing to try: a GLGraphicsBuffer
417 
418  if (retry == 1) {
419  if (!osx_support_gl_buffer || !gl_support_fbo || host == NULL ||
420  (flags & (BF_require_parasite | BF_require_window)) != 0) {
421  return NULL;
422  }
423  // Early failure - if we are sure that this buffer WONT
424  // meet specs, we can bail out early.
425  if ((flags & BF_fb_props_optional) == 0) {
426  if (fb_prop.get_indexed_color() ||
427  fb_prop.get_back_buffers() > 0 ||
428  fb_prop.get_accum_bits() > 0) {
429  return NULL;
430  }
431  }
432  if (osxgsg != NULL && osxgsg->is_valid() && !osxgsg->needs_reset()) {
433  if (!osxgsg->_supports_framebuffer_object ||
434  osxgsg->_glDrawBuffers == NULL) {
435  return NULL;
436  } else if (fb_prop.is_basic()) {
437  // Early success - if we are sure that this buffer WILL
438  // meet specs, we can precertify it.
439  precertify = true;
440  }
441  }
442  return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop, flags, gsg, host);
443  }
444 
445  // Third thing to try: an osxGraphicsBuffer
446  if (retry == 2) {
447  if ((!support_render_texture)||
448  ((flags&BF_require_parasite)!=0)||
449  ((flags&BF_require_window)!=0)||
450  ((flags&BF_resizeable)!=0)||
451  ((flags&BF_size_track_host)!=0)||
452  ((flags&BF_can_bind_every)!=0)||
453  ((flags&BF_can_bind_layered)!=0)) {
454  return NULL;
455  }
456  return new osxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
457  flags, gsg, host);
458  }
459 
460  // Nothing else left to try.
461  return NULL;
462 }
463 
464 ////////////////////////////////////////////////////////////////////
465 // Function: osxGraphicsPipe::make_callback_gsg
466 // Access: Protected, Virtual
467 // Description: This is called when make_output() is used to create a
468 // CallbackGraphicsWindow. If the GraphicsPipe can
469 // construct a GSG that's not associated with any
470 // particular window object, do so now, assuming the
471 // correct graphics context has been set up externally.
472 ////////////////////////////////////////////////////////////////////
473 PT(GraphicsStateGuardian) osxGraphicsPipe::
474 make_callback_gsg(GraphicsEngine *engine) {
475  return new osxGraphicsStateGuardian(engine, this, NULL);
476 }
bool is_basic() const
Returns true if the properties are extremely basic.
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:40
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
An offscreen buffer in the OSX environment.
static CGImageRef create_cg_image(const PNMImage &pnm_image)
Creates a new Quartz bitmap image with the data in the indicated PNMImage.
This graphics pipe represents the interface for creating OpenGL graphics windows on the various OSX&#39;s...
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:928
float get_blue(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:913
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
virtual string get_interface_name() const
Returns the name of the rendering interface associated with this GraphicsPipe.
int get_y_size() const
Returns the number of pixels in the Y direction.
int get_x_size() const
Returns the number of pixels in the X direction.
float get_red(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:889
A container for the various kinds of properties we might ask to have on a graphics window before we o...
float get_green(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:901
OSHandle * get_os_handle() const
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.I:41
An interface to the osx/ system for managing GL windows under X.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
This is a base class for the various different classes that represent the result of a frame of render...
int get_num_channels() const
Returns the number of channels in the image.
Encapsulates all the communication with a particular instance of a given rendering backend...
ColorType get_color_type() const
Returns the image type of the image, as an enumerated value.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
This class is the main interface to controlling the render process.
WindowHandle * get_parent_window() const
Returns the parent window specification, or NULL if there is no parent window specified.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:941