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 
52  get_gamma_table();
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 }
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set,...
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
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.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.
void set_all_specified()
Marks all bits as having been specified.
This class is the main interface to controlling the render process.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
HDC get_twindow_dc()
Returns the DC associated with the temporary, invisible window that was created with the gsg to query...
bool set_gamma(PN_stdfloat gamma)
Non static version of setting gamma.
virtual void reset()
Resets all internal state as if the gsg were newly created.
void get_properties(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor.
void choose_pixel_format(const FrameBufferProperties &properties, bool need_pbuffer)
Selects a pixel format for all the windows and buffers that use this gsg.
bool get_properties_advanced(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor, using the WGL extensio...
static bool static_set_gamma(bool restore, PN_stdfloat gamma)
Static function for setting gamma which is needed for atexit.
static bool get_gamma_table(void)
Static function for getting the original gamma.
static void atexit_function(void)
This function is passed to the atexit function.
void restore_gamma()
Restore original gamma.
bool fail_pfnum()
This is called by wglGraphicsWindow when it finds it cannot use the pfnum determined by the GSG.
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.