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::
206 get_interface_name() const {
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  */
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 }
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
bool is_basic() const
Returns true if the properties are extremely basic.
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Encapsulates all the communication with a particular instance of a given rendering backend.
int get_x_size() const
Returns the number of pixels in the X direction.
get_num_channels
Returns the number of channels in the image.
int get_y_size() const
Returns the number of pixels in the Y direction.
ColorType get_color_type() const
Returns the image type of the image, as an enumerated value.
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
float get_blue(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:788
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:809
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:799
float get_green(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:779
float get_red(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:770
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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 object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
A container for the various kinds of properties we might ask to have on a graphics window before we o...
get_parent_window
Returns the parent window specification, or NULL if there is no parent window specified.
An offscreen buffer in the OSX environment.
This graphics pipe represents the interface for creating OpenGL graphics windows on the various OSX's...
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
static CGImageRef create_cg_image(const PNMImage &pnm_image)
Creates a new Quartz bitmap image with the data in the indicated PNMImage.
virtual std::string get_interface_name() const
Returns the name of the rendering interface associated with this GraphicsPipe.
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
An interface to the osx/ system for managing GL windows under X.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(GraphicsPipe) osxGraphicsPipe
This function is passed to the GraphicsPipeSelection object to allow the user to make a default osxGr...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.