Panda3D
wglGraphicsStateGuardian.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 wglGraphicsStateGuardian.cxx
10  * @author drose
11  * @date 2003-01-27
12  */
13 
15 #include "config_wgldisplay.h"
16 #include "wglGraphicsBuffer.h"
17 #include "wglGraphicsPipe.h"
18 #include "string_utils.h"
19 #include <atomic>
20 
21 TypeHandle wglGraphicsStateGuardian::_type_handle;
22 
23 const char * const wglGraphicsStateGuardian::_twindow_class_name = "wglGraphicsStateGuardian";
24 bool wglGraphicsStateGuardian::_twindow_class_registered = false;
25 
26 /**
27  *
28  */
29 wglGraphicsStateGuardian::
30 wglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
31  wglGraphicsStateGuardian *share_with) :
32  GLGraphicsStateGuardian(engine, pipe),
33  _share_with(share_with)
34 {
35  _made_context = false;
36  _context = (HGLRC)nullptr;
37 
38  _twindow = (HWND)0;
39  _twindow_dc = (HDC)0;
40 
41  _pfnum = -1;
42  _pfnum_supports_pbuffer = false;
43  _pfnum_properties.clear();
44 
45  _supports_pbuffer = false;
46  _supports_pixel_format = false;
47  _supports_wgl_multisample = false;
48  _supports_wgl_render_texture = false;
49 
50  _wglCreateContextAttribsARB = nullptr;
51 
53 }
54 
55 /**
56  *
57  */
58 wglGraphicsStateGuardian::
59 ~wglGraphicsStateGuardian() {
60  release_twindow();
61  if (_context != (HGLRC)nullptr) {
62  wglDeleteContext(_context);
63  _context = (HGLRC)nullptr;
64  }
65 }
66 
67 /**
68  * This is called by wglGraphicsWindow when it finds it cannot use the pfnum
69  * determined by the GSG. Assuming this pfnum corresponds to an "advanced"
70  * frame buffer determined by wglChoosePixelFormatARB, this asks the GSG to
71  * swap out that pfnum for the earlier, "preliminary" pfnum determined via
72  * DescribePixelFormat().
73  *
74  * This is a one-way operation. Once called, you can never go back to the
75  * advanced pfnum.
76  *
77  * This method returns true if a change was successfully made, or false if
78  * there was no second tier to fall back to.
79  */
81 fail_pfnum() {
82  if (_pfnum == _pre_pfnum) {
83  return false;
84  }
85 
86  _pfnum = _pre_pfnum;
87  _pfnum_supports_pbuffer = false;
88  _pfnum_properties = _pre_pfnum_properties;
89  return true;
90 }
91 
92 /**
93  * Gets the FrameBufferProperties to match the indicated pixel format
94  * descriptor.
95  */
97 get_properties(FrameBufferProperties &properties, HDC hdc, int pfnum) {
98 
99  PIXELFORMATDESCRIPTOR pfd;
100  ZeroMemory(&pfd,sizeof(PIXELFORMATDESCRIPTOR));
101  pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
102  pfd.nVersion = 1;
103 
104  DescribePixelFormat(hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
105 
106  properties.clear();
107  properties.set_all_specified();
108 
109  if (((pfd.dwFlags & PFD_SUPPORT_OPENGL) == 0)||
110  ((pfd.dwFlags & PFD_DRAW_TO_WINDOW) == 0)) {
111  // Return without setting either RGB or Indexed Color. This indicates a
112  // window that can't do anything at all.
113  return;
114  }
115 
116  if (pfd.iPixelType == PFD_TYPE_COLORINDEX) {
117  properties.set_indexed_color(1);
118  properties.set_color_bits(pfd.cColorBits);
119  properties.set_alpha_bits(pfd.cAlphaBits);
120  } else {
121  properties.set_rgb_color(1);
122  properties.set_rgba_bits(pfd.cRedBits, pfd.cGreenBits,
123  pfd.cBlueBits, pfd.cAlphaBits);
124  }
125 
126  if (pfd.dwFlags & PFD_DOUBLEBUFFER) {
127  properties.set_back_buffers(1);
128  }
129  if (pfd.dwFlags & PFD_STEREO) {
130  properties.set_stereo(1);
131  }
132  if (pfd.dwFlags & PFD_GENERIC_FORMAT) {
133  properties.set_force_software(1);
134  } else {
135  properties.set_force_hardware(1);
136  }
137 
138  if (pfd.cDepthBits != 0) {
139  properties.set_depth_bits(pfd.cDepthBits);
140  }
141  if (pfd.cStencilBits != 0) {
142  properties.set_stencil_bits(pfd.cStencilBits);
143  }
144 
145  // The basic API doesn't do accum or multisample.
146 }
147 
148 /**
149  * Gets the FrameBufferProperties to match the indicated pixel format
150  * descriptor, using the WGL extensions.
151  */
154  HDC window_dc, int pfnum) {
155 
156  static const int max_attrib_list = 32;
157  int iattrib_list[max_attrib_list];
158  int ivalue_list[max_attrib_list];
159  int ni = 0;
160 
161  int acceleration_i, pixel_type_i, double_buffer_i, stereo_i,
162  color_bits_i, red_bits_i, green_bits_i, blue_bits_i, alpha_bits_i,
163  accum_bits_i, depth_bits_i, stencil_bits_i, multisamples_i,
164  srgb_capable_i;
165 
166  iattrib_list[acceleration_i = ni++] = WGL_ACCELERATION_ARB;
167  iattrib_list[pixel_type_i = ni++] = WGL_PIXEL_TYPE_ARB;
168  iattrib_list[double_buffer_i = ni++] = WGL_DOUBLE_BUFFER_ARB;
169  iattrib_list[stereo_i = ni++] = WGL_STEREO_ARB;
170  iattrib_list[color_bits_i = ni++] = WGL_COLOR_BITS_ARB;
171  iattrib_list[red_bits_i = ni++] = WGL_RED_BITS_ARB;
172  iattrib_list[green_bits_i = ni++] = WGL_GREEN_BITS_ARB;
173  iattrib_list[blue_bits_i = ni++] = WGL_BLUE_BITS_ARB;
174  iattrib_list[alpha_bits_i = ni++] = WGL_ALPHA_BITS_ARB;
175  iattrib_list[accum_bits_i = ni++] = WGL_ACCUM_BITS_ARB;
176  iattrib_list[depth_bits_i = ni++] = WGL_DEPTH_BITS_ARB;
177  iattrib_list[stencil_bits_i = ni++] = WGL_STENCIL_BITS_ARB;
178  iattrib_list[srgb_capable_i = ni++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;
179 
180  if (_supports_wgl_multisample) {
181  iattrib_list[multisamples_i = ni++] = WGL_SAMPLES_ARB;
182  }
183 
184  // Terminate the list.
185  nassertr(ni <= max_attrib_list, false);
186 
187  if (!_wglGetPixelFormatAttribivARB(window_dc, pfnum, 0,
188  ni, iattrib_list, ivalue_list)) {
189  return false;
190  }
191 
192  properties.clear();
193  properties.set_all_specified();
194 
195  if (ivalue_list[acceleration_i] == WGL_NO_ACCELERATION_ARB) {
196  properties.set_force_software(true);
197  } else {
198  properties.set_force_hardware(true);
199  }
200 
201  if (ivalue_list[pixel_type_i] == WGL_TYPE_COLORINDEX_ARB) {
202  properties.set_indexed_color(true);
203  properties.set_color_bits(ivalue_list[color_bits_i]);
204  properties.set_alpha_bits(ivalue_list[alpha_bits_i]);
205  } else {
206  properties.set_rgb_color(true);
207  properties.set_rgba_bits(ivalue_list[red_bits_i],
208  ivalue_list[green_bits_i],
209  ivalue_list[blue_bits_i],
210  ivalue_list[alpha_bits_i]);
211  }
212 
213  if (ivalue_list[double_buffer_i]) {
214  properties.set_back_buffers(1);
215  }
216 
217  if (ivalue_list[stereo_i]) {
218  properties.set_stereo(true);
219  }
220 
221  if (ivalue_list[srgb_capable_i]) {
222  properties.set_srgb_color(true);
223  }
224 
225  if (ivalue_list[accum_bits_i] != 0) {
226  properties.set_accum_bits(ivalue_list[accum_bits_i]);
227  }
228 
229  if (ivalue_list[depth_bits_i] != 0) {
230  properties.set_depth_bits(ivalue_list[depth_bits_i]);
231  }
232 
233  if (ivalue_list[stencil_bits_i] != 0) {
234  properties.set_stencil_bits(ivalue_list[stencil_bits_i]);
235  }
236 
237  if (_supports_wgl_multisample) {
238  if (ivalue_list[multisamples_i] != 0) {
239  properties.set_multisamples(ivalue_list[multisamples_i]);
240  }
241  }
242 
243  return true;
244 }
245 
246 /**
247  * Selects a pixel format for all the windows and buffers that use this gsg.
248  */
251  bool need_pbuffer) {
252 
253  // Choose best format available using DescribePixelFormat. In the process,
254  // we need a DC to examine the available pixel formats. We'll use the
255  // screen DC.
256 
257  if (gl_force_pixfmt.has_value()) {
258  wgldisplay_cat.info()
259  << "overriding pixfmt choice with gl-force-pixfmt("
260  << gl_force_pixfmt << ")\n";
261  _pfnum = gl_force_pixfmt;
262  _pfnum_properties = properties;
263  _pfnum_supports_pbuffer = true;
264  return;
265  }
266 
267  int best_pfnum = 0;
268  int best_quality = 0;
269  FrameBufferProperties best_prop;
270 
271  HDC hdc = GetDC(nullptr);
272 
273  int max_pfnum = DescribePixelFormat(hdc, 1, 0, nullptr);
274 
275  for (int pfnum = 0; pfnum<max_pfnum; ++pfnum) {
276  FrameBufferProperties pfprop;
277  get_properties(pfprop, hdc, pfnum);
278  int quality = pfprop.get_quality(properties);
279  if (quality > best_quality) {
280  best_pfnum = pfnum;
281  best_quality = quality;
282  best_prop = pfprop;
283  }
284  }
285 
286  ReleaseDC(nullptr, hdc);
287 
288  _pfnum = best_pfnum;
289  _pfnum_supports_pbuffer = false;
290  _pfnum_properties = best_prop;
291  _pre_pfnum = _pfnum;
292  _pre_pfnum_properties = _pfnum_properties;
293 
294  if (best_quality == 0) {
295  wgldisplay_cat.error()
296  << "Could not find a usable pixel format.\n";
297  return;
298  }
299 
300  if (wgldisplay_cat.is_debug()) {
301  wgldisplay_cat.debug()
302  << "Preliminary pixfmt #" << _pfnum << " = "
303  << _pfnum_properties << "\n";
304  }
305 
306  // See whether or not the wgl extensions are available. This routine is
307  // called before "reset". So the extensions list is empty. We need to
308  // create a twindow, make it current, fetch the extensions temporarily, get
309  // the few extensions we need, then clear the extensions list again in
310  // preparation for the reset.
311 
312  HDC twindow_dc = get_twindow_dc();
313  if (twindow_dc == 0) {
314  return;
315  }
316 
317  HGLRC twindow_ctx = wglCreateContext(twindow_dc);
318  if (twindow_ctx == 0) {
319  return;
320  }
321 
322  if (!wglGraphicsPipe::wgl_make_current(twindow_dc, twindow_ctx, nullptr)) {
323  wgldisplay_cat.error()
324  << "Failed to make WGL context current.\n";
325  wglDeleteContext(twindow_ctx);
326  return;
327  }
328 
329  _extensions.clear();
330  save_extensions((const char *)GLP(GetString)(GL_EXTENSIONS));
331  get_extra_extensions();
332  _supports_pixel_format = has_extension("WGL_ARB_pixel_format");
333  _supports_wgl_multisample = has_extension("WGL_ARB_multisample");
334 
335  if (has_extension("WGL_ARB_create_context")) {
336  _wglCreateContextAttribsARB =
337  (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
338  } else {
339  _wglCreateContextAttribsARB = nullptr;
340  }
341 
342  _extensions.clear();
343 
344  if (!_supports_pixel_format) {
345  wglDeleteContext(twindow_ctx);
346  return;
347  }
348 
349  _wglGetPixelFormatAttribivARB =
350  (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribivARB");
351  _wglGetPixelFormatAttribfvARB =
352  (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribfvARB");
353  _wglChoosePixelFormatARB =
354  (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
355 
356  if (_wglGetPixelFormatAttribivARB == nullptr ||
357  _wglGetPixelFormatAttribfvARB == nullptr ||
358  _wglChoosePixelFormatARB == nullptr) {
359  wgldisplay_cat.error()
360  << "Driver claims to support WGL_ARB_pixel_format extension, but does not define all functions.\n";
361  wglDeleteContext(twindow_ctx);
362  return;
363  }
364 
365  // Use the wgl extensions to find a better format.
366 
367  static const int max_attrib_list = 64;
368  int iattrib_list[max_attrib_list];
369  float fattrib_list[max_attrib_list];
370  int ni = 0;
371  int nf = 0;
372 
373  iattrib_list[ni++] = WGL_SUPPORT_OPENGL_ARB;
374  iattrib_list[ni++] = true;
375  iattrib_list[ni++] = WGL_PIXEL_TYPE_ARB;
376  iattrib_list[ni++] = WGL_TYPE_RGBA_ARB;
377 
378  if (need_pbuffer) {
379  iattrib_list[ni++] = WGL_DRAW_TO_PBUFFER_ARB;
380  iattrib_list[ni++] = true;
381  if (_pfnum_properties.get_alpha_bits()) {
382  iattrib_list[ni++] = WGL_BIND_TO_TEXTURE_RGBA_ARB;
383  iattrib_list[ni++] = true;
384  } else {
385  iattrib_list[ni++] = WGL_BIND_TO_TEXTURE_RGB_ARB;
386  iattrib_list[ni++] = true;
387  }
388  }
389 
390  nassertv(ni < max_attrib_list && nf < max_attrib_list);
391  iattrib_list[ni] = 0;
392  fattrib_list[nf] = 0;
393 
394  static const int max_pformats = 1024;
395  int pformat[max_pformats];
396  memset(pformat, 0, sizeof(pformat));
397  int nformats = 0;
398 
399  if (!_wglChoosePixelFormatARB(twindow_dc, iattrib_list, fattrib_list,
400  max_pformats, pformat, (unsigned int *)&nformats)) {
401  nformats = 0;
402  }
403  nformats = std::min(nformats, max_pformats);
404 
405  if (wgldisplay_cat.is_debug()) {
406  wgldisplay_cat.debug()
407  << "Found " << nformats << " advanced formats: [";
408  for (int i = 0; i < nformats; i++) {
409  wgldisplay_cat.debug(false)
410  << " " << pformat[i];
411  }
412  wgldisplay_cat.debug(false)
413  << " ]\n";
414  }
415 
416  if (nformats > 0) {
417  if (need_pbuffer) {
418  best_quality = 0;
419  }
420 
421  for (int i = 0; i < nformats; i++) {
422  FrameBufferProperties pfprop;
423  if (get_properties_advanced(pfprop, twindow_dc, pformat[i])) {
424  int quality = pfprop.get_quality(properties);
425  if (quality > best_quality) {
426  best_pfnum = pformat[i];
427  best_quality = quality;
428  best_prop = pfprop;
429  }
430  }
431  }
432 
433  if (!properties.get_srgb_color()) {
434  best_prop.set_srgb_color(false);
435  }
436 
437  _pfnum = best_pfnum;
438  _pfnum_supports_pbuffer = need_pbuffer;
439  _pfnum_properties = best_prop;
440 
441  if (wgldisplay_cat.is_debug()) {
442  wgldisplay_cat.debug()
443  << "Selected advanced pixfmt #" << _pfnum << " = "
444  << _pfnum_properties << "\n";
445  }
446  }
447 
448  wglDeleteContext(twindow_ctx);
449  release_twindow();
450 }
451 
452 /**
453  * Resets all internal state as if the gsg were newly created.
454  */
456 reset() {
457  GLGraphicsStateGuardian::reset();
458 
459  _supports_swap_control = has_extension("WGL_EXT_swap_control");
460 
461  if (_supports_swap_control) {
462  _wglSwapIntervalEXT =
463  (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
464  if (_wglSwapIntervalEXT == nullptr) {
465  wgldisplay_cat.error()
466  << "Driver claims to support WGL_EXT_swap_control extension, but does not define all functions.\n";
467  _supports_swap_control = false;
468  }
469  }
470 
471  if (_supports_swap_control) {
472  // Set the video-sync setting up front, if we have the extension that
473  // supports it.
474  _wglSwapIntervalEXT(sync_video ? 1 : 0);
475  }
476 
477  _supports_pbuffer = has_extension("WGL_ARB_pbuffer");
478 
479  if (_supports_pbuffer) {
480  _wglCreatePbufferARB =
481  (PFNWGLCREATEPBUFFERARBPROC)wglGetProcAddress("wglCreatePbufferARB");
482  _wglGetPbufferDCARB =
483  (PFNWGLGETPBUFFERDCARBPROC)wglGetProcAddress("wglGetPbufferDCARB");
484  _wglReleasePbufferDCARB =
485  (PFNWGLRELEASEPBUFFERDCARBPROC)wglGetProcAddress("wglReleasePbufferDCARB");
486  _wglDestroyPbufferARB =
487  (PFNWGLDESTROYPBUFFERARBPROC)wglGetProcAddress("wglDestroyPbufferARB");
488  _wglQueryPbufferARB =
489  (PFNWGLQUERYPBUFFERARBPROC)wglGetProcAddress("wglQueryPbufferARB");
490 
491  if (_wglCreatePbufferARB == nullptr ||
492  _wglGetPbufferDCARB == nullptr ||
493  _wglReleasePbufferDCARB == nullptr ||
494  _wglDestroyPbufferARB == nullptr ||
495  _wglQueryPbufferARB == nullptr) {
496  wgldisplay_cat.error()
497  << "Driver claims to support WGL_ARB_pbuffer extension, but does not define all functions.\n";
498  _supports_pbuffer = false;
499  }
500  }
501 
502  _supports_pixel_format = has_extension("WGL_ARB_pixel_format");
503 
504  if (_supports_pixel_format) {
505  _wglGetPixelFormatAttribivARB =
506  (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribivARB");
507  _wglGetPixelFormatAttribfvARB =
508  (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribfvARB");
509  _wglChoosePixelFormatARB =
510  (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
511 
512  if (_wglGetPixelFormatAttribivARB == nullptr ||
513  _wglGetPixelFormatAttribfvARB == nullptr ||
514  _wglChoosePixelFormatARB == nullptr) {
515  wgldisplay_cat.error()
516  << "Driver claims to support WGL_ARB_pixel_format extension, but does not define all functions.\n";
517  _supports_pixel_format = false;
518  }
519  }
520 
521  _supports_wgl_multisample = has_extension("WGL_ARB_multisample");
522 
523  _supports_wgl_render_texture = has_extension("WGL_ARB_render_texture");
524 
525  if (_supports_wgl_render_texture) {
526  _wglBindTexImageARB =
527  (PFNWGLBINDTEXIMAGEARBPROC)wglGetProcAddress("wglBindTexImageARB");
528  _wglReleaseTexImageARB =
529  (PFNWGLRELEASETEXIMAGEARBPROC)wglGetProcAddress("wglReleaseTexImageARB");
530  _wglSetPbufferAttribARB =
531  (PFNWGLSETPBUFFERATTRIBARBPROC)wglGetProcAddress("wglSetPbufferAttribARB");
532  if (_wglBindTexImageARB == nullptr ||
533  _wglReleaseTexImageARB == nullptr ||
534  _wglSetPbufferAttribARB == nullptr) {
535  wgldisplay_cat.error()
536  << "Driver claims to support WGL_ARB_render_texture, but does not define all functions.\n";
537  _supports_wgl_render_texture = false;
538  }
539  }
540 }
541 
542 /**
543  * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
544  * further extensions strings may be appropriate to that interface, in
545  * addition to the GL extension strings return by glGetString().
546  */
547 void wglGraphicsStateGuardian::
548 get_extra_extensions() {
549  // This is a little bit tricky, since the query function is itself an
550  // extension.
551 
552  // Look for the ARB flavor first, which wants one parameter, the HDC of the
553  // drawing context.
554  PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB =
555  (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
556  if (wglGetExtensionsStringARB != nullptr) {
557  HDC hdc = wglGetCurrentDC();
558  if (hdc != 0) {
559  save_extensions((const char *)wglGetExtensionsStringARB(hdc));
560  return;
561  }
562  }
563 
564  // If that failed, look for the EXT flavor, which wants no parameters.
565  PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT =
566  (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
567  if (wglGetExtensionsStringEXT != nullptr) {
568  save_extensions((const char *)wglGetExtensionsStringEXT());
569  }
570 }
571 
572 /**
573  * Returns the pointer to the GL extension function with the indicated name.
574  * It is the responsibility of the caller to ensure that the required
575  * extension is defined in the OpenGL runtime prior to calling this; it is an
576  * error to call this for a function that is not defined.
577  */
578 void *wglGraphicsStateGuardian::
579 do_get_extension_func(const char *name) {
580  return (void*) wglGetProcAddress(name);
581 }
582 
583 /**
584  * Creates a suitable context for rendering into the given window. This
585  * should only be called from the draw thread.
586  */
587 void wglGraphicsStateGuardian::
588 make_context(HDC hdc) {
589  // We should only call this once for a particular GSG.
590  nassertv(!_made_context);
591 
592  _made_context = true;
593 
594  // Attempt to create a context.
595  wglGraphicsPipe::_current_valid = false;
596 
597  if (_wglCreateContextAttribsARB != nullptr) {
598  // We have a fancier version of wglCreateContext that allows us to specify
599  // what kind of OpenGL context we would like.
600  int attrib_list[32];
601  int n = 0;
602  attrib_list[0] = 0;
603 
604  if (gl_version.get_num_words() > 0) {
605  attrib_list[n++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
606  attrib_list[n++] = gl_version[0];
607  if (gl_version.get_num_words() > 1) {
608  attrib_list[n++] = WGL_CONTEXT_MINOR_VERSION_ARB;
609  attrib_list[n++] = gl_version[1];
610  }
611  }
612  int flags = 0;
613  if (gl_debug) {
614  flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
615  }
616  if (gl_forward_compatible) {
617  flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
618  if (gl_version.get_num_words() == 0 || gl_version[0] < 2) {
619  wgldisplay_cat.error()
620  << "gl-forward-compatible requires gl-version >= 3 0\n";
621  }
622  }
623  if (flags != 0) {
624  attrib_list[n++] = WGL_CONTEXT_FLAGS_ARB;
625  attrib_list[n++] = flags;
626  }
627 #ifndef SUPPORT_FIXED_FUNCTION
628  attrib_list[n++] = WGL_CONTEXT_PROFILE_MASK_ARB;
629  attrib_list[n++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
630 #endif
631  attrib_list[n] = 0;
632 
633  _context = _wglCreateContextAttribsARB(hdc, 0, attrib_list);
634  } else {
635  _context = wglCreateContext(hdc);
636  }
637 
638  if (_context == nullptr) {
639  wgldisplay_cat.error()
640  << "Could not create GL context.\n";
641  _is_valid = false;
642  return;
643  }
644 
645  // Now share texture context with the indicated GSG.
646  if (_share_with != nullptr) {
647  HGLRC share_context = _share_with->get_share_context();
648  if (share_context == nullptr) {
649  // Whoops, the target context hasn't yet made its own context. In that
650  // case, it will share context with us.
651  _share_with->redirect_share_pool(this);
652 
653  } else {
654  if (!wglShareLists(share_context, _context)) {
655  wgldisplay_cat.error()
656  << "Could not share texture contexts between wglGraphicsStateGuardians.\n";
657  // Too bad we couldn't detect this error sooner. Now there's really
658  // no way to tell the application it's hosed.
659  _is_valid = false;
660 
661  } else {
662  _prepared_objects = _share_with->get_prepared_objects();
663  }
664  }
665 
666  _share_with = nullptr;
667  }
668 }
669 
670 /**
671  * Returns a wgl context handle for the purpose of sharing texture context
672  * with this GSG. This will either be the GSG's own context handle, if it
673  * exists yet, or the context handle of some other GSG that this GSG is
674  * planning to share with. If this returns NULL, none of the GSG's in this
675  * share pool have yet created their context.
676  */
677 HGLRC wglGraphicsStateGuardian::
678 get_share_context() const {
679  if (_made_context) {
680  return _context;
681  }
682  if (_share_with != nullptr) {
683  return _share_with->get_share_context();
684  }
685  return nullptr;
686 }
687 
688 /**
689  * Directs the GSG (along with all GSG's it is planning to share a texture
690  * context with) to share texture context with the indicated GSG.
691  *
692  * This assumes that this GSG's context has not yet been created, and neither
693  * have any of the GSG's it is planning to share texture context with; but the
694  * graphics context for the indicated GSG has already been created.
695  */
696 void wglGraphicsStateGuardian::
697 redirect_share_pool(wglGraphicsStateGuardian *share_with) {
698  nassertv(!_made_context);
699  if (_share_with != nullptr) {
700  _share_with->redirect_share_pool(share_with);
701  } else {
702  _share_with = share_with;
703  }
704 }
705 
706 /**
707  * Creates an invisible window to associate with the GL context, even if we
708  * are not going to use it. This is necessary because in the Windows OpenGL
709  * API, we have to create window before we can create a GL context--even
710  * before we can ask about what GL extensions are available!
711  */
712 bool wglGraphicsStateGuardian::
713 make_twindow() {
714  release_twindow();
715 
716  DWORD window_style = 0;
717 
718  register_twindow_class();
719  HINSTANCE hinstance = GetModuleHandle(nullptr);
720  _twindow = CreateWindow(_twindow_class_name, "twindow", window_style,
721  0, 0, 1, 1, nullptr, nullptr, hinstance, 0);
722 
723  if (!_twindow) {
724  wgldisplay_cat.error()
725  << "CreateWindow() failed!" << std::endl;
726  return false;
727  }
728 
729  ShowWindow(_twindow, SW_HIDE);
730 
731  _twindow_dc = GetDC(_twindow);
732 
733  PIXELFORMATDESCRIPTOR pixelformat;
734  if (!SetPixelFormat(_twindow_dc, _pfnum, &pixelformat)) {
735  wgldisplay_cat.error()
736  << "SetPixelFormat(" << _pfnum << ") failed after window create\n";
737  release_twindow();
738  return false;
739  }
740 
741  return true;
742 }
743 
744 /**
745  * Closes and frees the resources associated with the temporary window created
746  * by a previous call to make_twindow().
747  */
748 void wglGraphicsStateGuardian::
749 release_twindow() {
750  if (_twindow_dc) {
751  ReleaseDC(_twindow, _twindow_dc);
752  _twindow_dc = 0;
753  }
754  if (_twindow) {
755  DestroyWindow(_twindow);
756  _twindow = 0;
757  }
758 }
759 
760 /**
761  * Registers a Window class for the twindow created by all wglGraphicsPipes.
762  * This only needs to be done once per session.
763  */
764 void wglGraphicsStateGuardian::
765 register_twindow_class() {
766  if (_twindow_class_registered) {
767  return;
768  }
769 
770  WNDCLASS wc;
771 
772  HINSTANCE instance = GetModuleHandle(nullptr);
773 
774  // Clear before filling in window structure!
775  ZeroMemory(&wc, sizeof(WNDCLASS));
776  wc.style = CS_OWNDC;
777  wc.lpfnWndProc = DefWindowProc;
778  wc.hInstance = instance;
779  wc.lpszClassName = _twindow_class_name;
780 
781  if (!RegisterClass(&wc)) {
782  wgldisplay_cat.error()
783  << "could not register window class!" << std::endl;
784  return;
785  }
786  _twindow_class_registered = true;
787 }
788 
789 #define GAMMA_1 (255.0 * 256.0)
790 
791 static bool _gamma_table_initialized = false;
792 static unsigned short _original_gamma_table [256 * 3];
793 
794 void _create_gamma_table_wgl (PN_stdfloat gamma, unsigned short *original_red_table, unsigned short *original_green_table, unsigned short *original_blue_table, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
795  int i;
796  double gamma_correction;
797 
798  if (gamma <= 0.0) {
799  // avoid divide by zero and negative exponents
800  gamma = 1.0;
801  }
802  gamma_correction = 1.0 / (double) gamma;
803 
804  for (i = 0; i < 256; i++) {
805  double r;
806  double g;
807  double b;
808 
809  if (original_red_table) {
810  r = (double) original_red_table [i] / GAMMA_1;
811  g = (double) original_green_table [i] / GAMMA_1;
812  b = (double) original_blue_table [i] / GAMMA_1;
813  }
814  else {
815  r = ((double) i / 255.0);
816  g = r;
817  b = r;
818  }
819 
820  r = pow (r, gamma_correction);
821  g = pow (g, gamma_correction);
822  b = pow (b, gamma_correction);
823 
824  if (r > 1.00) {
825  r = 1.0;
826  }
827  if (g > 1.00) {
828  g = 1.0;
829  }
830  if (b > 1.00) {
831  b = 1.0;
832  }
833 
834  r = r * GAMMA_1;
835  g = g * GAMMA_1;
836  b = b * GAMMA_1;
837 
838  red_table [i] = r;
839  green_table [i] = g;
840  blue_table [i] = b;
841  }
842 }
843 
844 /**
845  * Static function for getting the original gamma.
846  */
848 get_gamma_table(void) {
849  bool get;
850 
851  get = false;
852  if (_gamma_table_initialized == false) {
853  HDC hdc = GetDC(nullptr);
854 
855  if (hdc) {
856  if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
857  _gamma_table_initialized = true;
858  get = true;
859  }
860 
861  ReleaseDC (nullptr, hdc);
862  }
863  }
864 
865  return get;
866 }
867 
868 /**
869  * Static function for setting gamma which is needed for atexit.
870  */
872 static_set_gamma(bool restore, PN_stdfloat gamma) {
873  bool set;
874  HDC hdc = GetDC(nullptr);
875 
876  set = false;
877  if (hdc) {
878  unsigned short ramp [256 * 3];
879 
880  if (restore && _gamma_table_initialized) {
881  _create_gamma_table_wgl (gamma, &_original_gamma_table [0], &_original_gamma_table [256], &_original_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
882  }
883  else {
884  _create_gamma_table_wgl (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
885  }
886 
887  if (SetDeviceGammaRamp (hdc, ramp)) {
888  set = true;
889 
890  // Register an atexit handler
891  static std::atomic_flag gamma_modified = ATOMIC_FLAG_INIT;
892  if (!gamma_modified.test_and_set()) {
893  atexit(atexit_function);
894  }
895  }
896 
897  ReleaseDC (nullptr, hdc);
898  }
899 
900  return set;
901 }
902 
903 /**
904  * Non static version of setting gamma. Returns true on success.
905  */
907 set_gamma(PN_stdfloat gamma) {
908  bool set;
909 
910  set = static_set_gamma(false, gamma);
911  if (set) {
912  _gamma = gamma;
913  }
914 
915  return set;
916 }
917 
918 /**
919  * Restore original gamma.
920  */
922 restore_gamma() {
923  static_set_gamma(true, 1.0f);
924 }
925 
926 /**
927  * This function is passed to the atexit function.
928  */
930 atexit_function(void) {
931  static_set_gamma(true, 1.0);
932 }
FrameBufferProperties
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
Definition: frameBufferProperties.h:26
FrameBufferProperties::set_rgba_bits
void set_rgba_bits(int r, int g, int b, int a)
Convenience method for setting the red, green, blue and alpha bits in one go.
Definition: frameBufferProperties.I:255
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
wglGraphicsStateGuardian::fail_pfnum
bool fail_pfnum()
This is called by wglGraphicsWindow when it finds it cannot use the pfnum determined by the GSG.
Definition: wglGraphicsStateGuardian.cxx:81
wglGraphicsStateGuardian::get_twindow_dc
HDC get_twindow_dc()
Returns the DC associated with the temporary, invisible window that was created with the gsg to query...
Definition: wglGraphicsStateGuardian.I:75
wglGraphicsStateGuardian::set_gamma
bool set_gamma(PN_stdfloat gamma)
Non static version of setting gamma.
Definition: wglGraphicsStateGuardian.cxx:907
ConfigVariableBase::has_value
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set,...
Definition: configVariableBase.I:145
wglGraphicsStateGuardian::atexit_function
static void atexit_function(void)
This function is passed to the atexit function.
Definition: wglGraphicsStateGuardian.cxx:930
wglGraphicsStateGuardian::choose_pixel_format
void choose_pixel_format(const FrameBufferProperties &properties, bool need_pbuffer)
Selects a pixel format for all the windows and buffers that use this gsg.
Definition: wglGraphicsStateGuardian.cxx:250
FrameBufferProperties::clear
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
Definition: frameBufferProperties.cxx:185
GraphicsEngine
This class is the main interface to controlling the render process.
Definition: graphicsEngine.h:53
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
config_wgldisplay.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
wglGraphicsStateGuardian::restore_gamma
void restore_gamma()
Restore original gamma.
Definition: wglGraphicsStateGuardian.cxx:922
wglGraphicsStateGuardian::get_gamma_table
static bool get_gamma_table(void)
Static function for getting the original gamma.
Definition: wglGraphicsStateGuardian.cxx:848
wglGraphicsBuffer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
wglGraphicsStateGuardian
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
Definition: wglGraphicsStateGuardian.h:28
FrameBufferProperties::get_quality
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.
Definition: frameBufferProperties.cxx:438
wglGraphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
wglGraphicsStateGuardian.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
FrameBufferProperties::set_all_specified
void set_all_specified()
Marks all bits as having been specified.
Definition: frameBufferProperties.cxx:334
wglGraphicsStateGuardian::get_properties_advanced
bool get_properties_advanced(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor, using the WGL extensio...
Definition: wglGraphicsStateGuardian.cxx:153
wglGraphicsStateGuardian::static_set_gamma
static bool static_set_gamma(bool restore, PN_stdfloat gamma)
Static function for setting gamma which is needed for atexit.
Definition: wglGraphicsStateGuardian.cxx:872
wglGraphicsStateGuardian::get_properties
void get_properties(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor.
Definition: wglGraphicsStateGuardian.cxx:97
FrameBufferProperties::set_color_bits
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
Definition: frameBufferProperties.h:124
wglGraphicsStateGuardian::reset
virtual void reset()
Resets all internal state as if the gsg were newly created.
Definition: wglGraphicsStateGuardian.cxx:456