Panda3D
Loading...
Searching...
No Matches
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
32Boolean 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
42long 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
52static 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
91CFArrayRef 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
160TypeHandle osxGraphicsPipe::_type_handle;
161
162/**
163 *
164 */
165osxGraphicsPipe::
166osxGraphicsPipe() {
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 */
195osxGraphicsPipe::
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 */
206get_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 */
214PT(GraphicsPipe) osxGraphicsPipe::
215pipe_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 */
224GraphicsPipe::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 */
234create_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 */
337void osxGraphicsPipe::
338release_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 */
346PT(GraphicsOutput) osxGraphicsPipe::
347make_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 */
448PT(GraphicsStateGuardian) osxGraphicsPipe::
449make_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.
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.
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
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.
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.