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