Panda3D
osxGraphicsStateGuardian.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 osxGraphicsStateGuardian.cxx
10  */
11 
13 #include "osxGraphicsBuffer.h"
14 #include "string_utils.h"
15 #include "config_osxdisplay.h"
16 #include "depthWriteAttrib.h"
17 #include "depthTestAttrib.h"
18 #include "textureAttrib.h"
19 #include "pnmImage.h"
20 
21 #include <OpenGL/gl.h>
22 #import <mach-o/dyld.h>
23 
24 // This is generated data for the standard texture we use for drawing the
25 // resize box in the window corner.
26 #include "resize_box.rgb.c"
27 
28 TypeHandle osxGraphicsStateGuardian::_type_handle;
29 
30 /**
31  * Returns the pointer to the GL extension function with the indicated name.
32  * It is the responsibility of the caller to ensure that the required
33  * extension is defined in the OpenGL runtime prior to calling this; it is an
34  * error to call this for a function that is not defined.
35  */
36 void *osxGraphicsStateGuardian::
37 do_get_extension_func(const char *name) {
38  std::string fullname = "_" + std::string(name);
39  NSSymbol symbol = nullptr;
40 
41  if (NSIsSymbolNameDefined(fullname.c_str())) {
42  symbol = NSLookupAndBindSymbol(fullname.c_str());
43  }
44 
45  return symbol ? NSAddressOfSymbol(symbol) : nullptr;
46 }
47 
48 /**
49  *
50  */
51 osxGraphicsStateGuardian::
52 osxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
53  osxGraphicsStateGuardian *share_with) :
54  GLGraphicsStateGuardian(engine, pipe),
55  _share_with(share_with),
56  _aglPixFmt(nullptr),
57  _aglcontext(nullptr)
58 {
59  _shared_buffer = 1011;
60  get_gamma_table();
61 }
62 
63 /**
64  *
65  */
66 osxGraphicsStateGuardian::
67 ~osxGraphicsStateGuardian() {
68  if (_aglcontext != (AGLContext)nullptr) {
69  aglSetCurrentContext(nullptr);
70  aglDestroyContext(_aglcontext);
71  report_agl_error("aglDestroyContext");
72  _aglcontext = (AGLContext)nullptr;
73  }
74 }
75 
76 /**
77  * Resets all internal state as if the gsg were newly created.
78  */
80 {
81 /*
82  if(_aglcontext != (AGLContext)NULL)
83  {
84  aglDestroyContext(_aglcontext);
85  report_agl_error();
86  _aglcontext = (AGLContext)NULL;
87  }
88  */
89 
90  GLGraphicsStateGuardian::reset();
91 
92  if (_aglcontext != (AGLContext)nullptr) {
93  // Apply the video-sync setting.
94  GLint value = sync_video ? 1 : 0;
95  aglSetInteger(_aglcontext, AGL_SWAP_INTERVAL, &value);
96  }
97 }
98 
99 /**
100  * Draws an OSX-style resize icon in the bottom right corner of the current
101  * display region. This is normally done automatically at the end of each
102  * frame when the window is indicated as resizable, since the 3-D graphics
103  * overlay the normal, OS-drawn resize icon and the user won't be able see it.
104  */
107  // This state is created, once, and never freed.
108  static CPT(RenderState) state;
109  if (state == nullptr) {
110  state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
111  DepthWriteAttrib::make(DepthWriteAttrib::M_off),
112  DepthTestAttrib::make(DepthTestAttrib::M_none));
113 
114  // Get the default texture to apply to the resize box; it's compiled into
115  // the code.
116  std::string resize_box_string((const char *)resize_box, resize_box_len);
117  std::istringstream resize_box_strm(resize_box_string);
118  PNMImage resize_box_pnm;
119  if (resize_box_pnm.read(resize_box_strm, "resize_box.rgb")) {
120  PT(Texture) tex = new Texture;
121  tex->set_name("resize_box.rgb");
122  tex->load(resize_box_pnm);
123  tex->set_minfilter(SamplerState::FT_linear);
124  tex->set_magfilter(SamplerState::FT_linear);
125  state = state->add_attrib(TextureAttrib::make(tex));
126  }
127  }
128 
129  // Clear out the lens.
130  _projection_mat_inv = _projection_mat = TransformState::make_identity();
131  prepare_lens();
132 
133  // Set the state to our specific, known state for drawing the icon.
134  set_state_and_transform(state, TransformState::make_identity());
135 
136  // Now determine the inner corner of the quad, choosing a 15x15 pixel square
137  // in the lower-right corner, computed from the viewport size.
138  PN_stdfloat inner_x = 1.0f - (15.0f * 2.0f / _viewport_width);
139  PN_stdfloat inner_y = (15.0f * 2.0f / _viewport_height) - 1.0f;
140 
141  // Draw the quad. We just use the slow, simple immediate mode calls here.
142  // It's just one quad, after all.
143  glBegin(GL_QUADS);
144 
145  glColor4f(1.0, 1.0, 1.0, 1.0);
146  glTexCoord2f(0.0, 0.0);
147  glVertex2f(inner_x, -1.0);
148 
149  glTexCoord2f(0.9375, 0.0);
150  glVertex2f(1.0, -1.0);
151 
152  glTexCoord2f(0.9375, 0.9375);
153  glVertex2f(1.0, inner_y);
154 
155  glTexCoord2f(0.0, 0.9375);
156  glVertex2f(inner_x, inner_y);
157 
158  glEnd();
159 }
160 
161 /**
162  * This function will build up a context for a gsg..
163  */
165 build_gl(bool full_screen, bool pbuffer, FrameBufferProperties &fb_props) {
166  if (_aglcontext) {
167  describe_pixel_format(fb_props);
168  return noErr; // already built
169  }
170 
171  OSStatus err = noErr;
172 
173  GDHandle display = GetMainDevice();
174 
175  pvector<GLint> attrib;
176  if (!fb_props.get_indexed_color()) {
177  attrib.push_back(AGL_RGBA);
178  int color_bits = fb_props.get_color_bits();
179  int alpha_bits = fb_props.get_alpha_bits();
180  attrib.push_back(AGL_BUFFER_SIZE);
181  attrib.push_back(color_bits + alpha_bits);
182  attrib.push_back(AGL_PIXEL_SIZE);
183  attrib.push_back(color_bits);
184  attrib.push_back(AGL_RED_SIZE);
185  attrib.push_back(fb_props.get_red_bits());
186  attrib.push_back(AGL_GREEN_SIZE);
187  attrib.push_back(fb_props.get_green_bits());
188  attrib.push_back(AGL_BLUE_SIZE);
189  attrib.push_back(fb_props.get_blue_bits());
190  attrib.push_back(AGL_ALPHA_SIZE);
191  attrib.push_back(alpha_bits);
192  }
193  attrib.push_back(AGL_DEPTH_SIZE);
194  attrib.push_back(fb_props.get_depth_bits());
195  attrib.push_back(AGL_STENCIL_SIZE);
196  attrib.push_back(fb_props.get_stencil_bits());
197  if (fb_props.get_multisamples() != 0) {
198  attrib.push_back(AGL_MULTISAMPLE);
199  attrib.push_back(AGL_SAMPLE_BUFFERS_ARB);
200  attrib.push_back(1);
201  attrib.push_back(AGL_SAMPLES_ARB);
202  attrib.push_back(fb_props.get_multisamples());
203  }
204 
205  if (fb_props.is_stereo()) {
206  attrib.push_back(AGL_STEREO);
207  }
208 
209  if (!fb_props.is_single_buffered()) {
210  attrib.push_back(AGL_DOUBLEBUFFER);
211  }
212  if (full_screen) {
213  attrib.push_back(AGL_FULLSCREEN);
214  }
215  if (pbuffer) {
216  attrib.push_back(AGL_PBUFFER);
217  }
218 
219  if (fb_props.get_force_hardware()) {
220  attrib.push_back(AGL_ACCELERATED);
221  attrib.push_back(AGL_NO_RECOVERY);
222  }
223 
224  // Allow the system to choose the largest buffers requested that meets all
225  // our selections.
226  attrib.push_back(AGL_MAXIMUM_POLICY);
227 
228  // Terminate the list.
229  attrib.push_back(AGL_NONE);
230 
231  // build context
232  _aglcontext = nullptr;
233  _aglPixFmt = aglChoosePixelFormat(&display, 1, &attrib[0]);
234  err = report_agl_error("aglChoosePixelFormat");
235  if (_aglPixFmt) {
236  if(_share_with == nullptr) {
237  _aglcontext = aglCreateContext(_aglPixFmt, nullptr);
238  } else {
239  _aglcontext = aglCreateContext(_aglPixFmt, ((osxGraphicsStateGuardian *)_share_with)->_aglcontext);
240  }
241  err = report_agl_error("aglCreateContext");
242 
243  if (_aglcontext == nullptr) {
244  osxdisplay_cat.error()
245  << "osxGraphicsStateGuardian::build_gl Error Getting GL Context \n" ;
246  if(err == noErr) {
247  err = -1;
248  }
249  } else {
250  aglSetInteger(_aglcontext, AGL_BUFFER_NAME, &_shared_buffer);
251  err = report_agl_error("aglSetInteger AGL_BUFFER_NAME");
252  }
253 
254  } else {
255  osxdisplay_cat.error()
256  << "osxGraphicsStateGuardian::build_gl Error Getting Pixel Format\n" ;
257  osxdisplay_cat.error()
258  << fb_props << "\n";
259  if(err == noErr) {
260  err = -1;
261  }
262  }
263 
264  if (err == noErr) {
265  describe_pixel_format(fb_props);
266  }
267 
268  if (osxdisplay_cat.is_debug()) {
269  osxdisplay_cat.debug()
270  << "osxGraphicsStateGuardian::build_gl Returning :" << err << "\n";
271  osxdisplay_cat.debug()
272  << fb_props << "\n";
273  }
274 
275  return err;
276 }
277 
278 
279 /**
280  * Fills in the fb_props member with the appropriate values according to the
281  * chosen pixel format.
282  */
283 void osxGraphicsStateGuardian::
284 describe_pixel_format(FrameBufferProperties &fb_props) {
285  fb_props.clear();
286  GLint value;
287 
288  if (aglDescribePixelFormat(_aglPixFmt, AGL_RGBA, &value)) {
289  fb_props.set_indexed_color(!value);
290  fb_props.set_rgb_color(value);
291  }
292  if (aglDescribePixelFormat(_aglPixFmt, AGL_DEPTH_SIZE, &value)) {
293  fb_props.set_depth_bits(value);
294  }
295  int color_bits = 0;
296  if (aglDescribePixelFormat(_aglPixFmt, AGL_RED_SIZE, &value)) {
297  fb_props.set_red_bits(value);
298  color_bits += value;
299  }
300  if (aglDescribePixelFormat(_aglPixFmt, AGL_GREEN_SIZE, &value)) {
301  fb_props.set_green_bits(value);
302  color_bits += value;
303  }
304  if (aglDescribePixelFormat(_aglPixFmt, AGL_BLUE_SIZE, &value)) {
305  fb_props.set_blue_bits(value);
306  color_bits += value;
307  }
308  fb_props.set_color_bits(color_bits);
309  if (aglDescribePixelFormat(_aglPixFmt, AGL_ALPHA_SIZE, &value)) {
310  fb_props.set_alpha_bits(value);
311  }
312 
313  if (aglDescribePixelFormat(_aglPixFmt, AGL_STENCIL_SIZE, &value)) {
314  fb_props.set_stencil_bits(value);
315  }
316 
317  int accum_bits = 0;
318  if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_RED_SIZE, &value)) {
319  accum_bits += value;
320  }
321  if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_GREEN_SIZE, &value)) {
322  accum_bits += value;
323  }
324  if (aglDescribePixelFormat(_aglPixFmt, AGL_ACCUM_BLUE_SIZE, &value)) {
325  accum_bits += value;
326  }
327 
328  if (aglDescribePixelFormat(_aglPixFmt, AGL_SAMPLES_ARB, &value)) {
329  fb_props.set_multisamples(value);
330  }
331 
332  if (aglDescribePixelFormat(_aglPixFmt, AGL_DOUBLEBUFFER, &value)) {
333  if (value) {
334  fb_props.set_back_buffers(1);
335  } else {
336  fb_props.set_back_buffers(0);
337  }
338  }
339 
340  if (aglDescribePixelFormat(_aglPixFmt, AGL_STEREO, &value)) {
341  fb_props.set_stereo(value);
342  }
343 
344  // Until we query the renderer, we don't know whether it's hardware or
345  // software based, so set both flags to indicate we don't know.
346  fb_props.set_force_hardware(true);
347  fb_props.set_force_software(true);
348 
349  GLint ndevs;
350  AGLDevice *gdevs = aglDevicesOfPixelFormat(_aglPixFmt, &ndevs);
351  if (gdevs != nullptr) {
352  AGLRendererInfo rinfo = aglQueryRendererInfo(gdevs, ndevs);
353  if (rinfo != nullptr) {
354  if (aglDescribeRenderer(rinfo, AGL_ACCELERATED, &value)) {
355  // Now we know whether it's hardware or software.
356  fb_props.set_force_hardware(value);
357  fb_props.set_force_software(!value);
358  }
359  if (aglDescribeRenderer(rinfo, AGL_VIDEO_MEMORY, &value)) {
360  osxdisplay_cat.debug()
361  << "Reported video memory is " << value << "\n";
362  }
363  if (aglDescribeRenderer(rinfo, AGL_TEXTURE_MEMORY, &value)) {
364  osxdisplay_cat.debug()
365  << "Reported texture memory is " << value << "\n";
366  }
367  }
368  }
369 }
370 
371 /**
372  * Static function for getting the orig gamma tables
373  */
376  CGDisplayRestoreColorSyncSettings();
377  _cgErr = CGGetDisplayTransferByTable( 0, 256, _gOriginalRedTable, _gOriginalGreenTable, _gOriginalBlueTable, &_sampleCount);
378 }
379 
380 /**
381  * Static function for setting gamma which is needed for atexit.
382  */
384 static_set_gamma(bool restore, PN_stdfloat gamma) {
385  bool set;
386 
387  set = false;
388 
389  if (restore) {
390  CGDisplayRestoreColorSyncSettings();
391  set = true;
392  return set;
393  }
394  // CGDisplayRestoreColorSyncSettings();
395 
396  // CGGammaValue gOriginalRedTable[ 256 ]; CGGammaValue gOriginalGreenTable[
397  // 256 ]; CGGammaValue gOriginalBlueTable[ 256 ];
398 
399  // CGTableCount sampleCount; CGDisplayErr cgErr;
400 
401  // cgErr = CGGetDisplayTransferByTable( 0, 256, _gOriginalRedTable,
402  // _gOriginalGreenTable, _gOriginalBlueTable, &_sampleCount);
403 
404  CGGammaValue redTable[ 256 ];
405  CGGammaValue greenTable[ 256 ];
406  CGGammaValue blueTable[ 256 ];
407 
408  short j, i;
409  short y[3];
410 
411  for (j = 0; j < 3; j++) {
412  y[j] = 255;
413  }
414 
415  y[0] = 256 * gamma;
416  y[1] = 256 * gamma;
417  y[2] = 256 * gamma;
418 
419  for (i = 0; i < 256; i++) {
420  redTable[i] = _gOriginalRedTable[ i ] * (y[ 0 ] ) / 256;
421  greenTable[ i ] = _gOriginalGreenTable[ i ] * (y[ 1 ] ) / 256;
422  blueTable[ i ] = _gOriginalBlueTable[ i ] * (y[ 2 ] ) / 256;
423  }
424  _cgErr = CGSetDisplayTransferByTable( 0, 256, redTable, greenTable, blueTable);
425 
426  if (_cgErr == 0) {
427  set = true;
428  }
429 
430  return set;
431 }
432 
433 /**
434  * Non static version of setting gamma. Returns true on success.
435  */
437 set_gamma(PN_stdfloat gamma) {
438  bool set;
439 
440  set = static_set_gamma(false, gamma);
441 
442  return set;
443 }
444 
445 /**
446  * Restore original gamma.
447  */
450  static_set_gamma(true, 1.0f);
451 }
452 
453 /**
454  * This function is passed to the atexit function.
455  */
458  static_set_gamma(true, 1.0);
459 }
bool set_gamma(PN_stdfloat gamma)
Non static version of setting gamma.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
void draw_resize_box()
Draws an OSX-style resize icon in the bottom right corner of the current display region.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
void atexit_function()
This function is passed to the atexit function.
bool static_set_gamma(bool restore, PN_stdfloat gamma)
Static function for setting gamma which is needed for atexit.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void restore_gamma()
Restore original gamma.
OSStatus build_gl(bool full_screen, bool pbuffer, FrameBufferProperties &fb_props)
This function will build up a context for a gsg.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
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 void reset()
Resets all internal state as if the gsg were newly created.
bool get_gamma_table()
Static function for getting the orig gamma tables.