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 
20 TypeHandle wglGraphicsStateGuardian::_type_handle;
21 
22 const char * const wglGraphicsStateGuardian::_twindow_class_name = "wglGraphicsStateGuardian";
23 bool wglGraphicsStateGuardian::_twindow_class_registered = false;
24 
25 /**
26  *
27  */
28 wglGraphicsStateGuardian::
29 wglGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
30  wglGraphicsStateGuardian *share_with) :
31  GLGraphicsStateGuardian(engine, pipe),
32  _share_with(share_with)
33 {
34  _made_context = false;
35  _context = (HGLRC)nullptr;
36 
37  _twindow = (HWND)0;
38  _twindow_dc = (HDC)0;
39 
40  _pfnum = -1;
41  _pfnum_supports_pbuffer = false;
42  _pfnum_properties.clear();
43 
44  _supports_pbuffer = false;
45  _supports_pixel_format = false;
46  _supports_wgl_multisample = false;
47  _supports_wgl_render_texture = false;
48 
49  _wglCreateContextAttribsARB = nullptr;
50 
52  atexit(atexit_function);
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  */
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  if (gl_debug) {
613  attrib_list[n++] = WGL_CONTEXT_FLAGS_ARB;
614  attrib_list[n++] = WGL_CONTEXT_DEBUG_BIT_ARB;
615  }
616 #ifndef SUPPORT_FIXED_FUNCTION
617  attrib_list[n++] = WGL_CONTEXT_PROFILE_MASK_ARB;
618  attrib_list[n++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
619 #endif
620  attrib_list[n] = 0;
621 
622  _context = _wglCreateContextAttribsARB(hdc, 0, attrib_list);
623  } else {
624  _context = wglCreateContext(hdc);
625  }
626 
627  if (_context == nullptr) {
628  wgldisplay_cat.error()
629  << "Could not create GL context.\n";
630  _is_valid = false;
631  return;
632  }
633 
634  // Now share texture context with the indicated GSG.
635  if (_share_with != nullptr) {
636  HGLRC share_context = _share_with->get_share_context();
637  if (share_context == nullptr) {
638  // Whoops, the target context hasn't yet made its own context. In that
639  // case, it will share context with us.
640  _share_with->redirect_share_pool(this);
641 
642  } else {
643  if (!wglShareLists(share_context, _context)) {
644  wgldisplay_cat.error()
645  << "Could not share texture contexts between wglGraphicsStateGuardians.\n";
646  // Too bad we couldn't detect this error sooner. Now there's really
647  // no way to tell the application it's hosed.
648  _is_valid = false;
649 
650  } else {
651  _prepared_objects = _share_with->get_prepared_objects();
652  }
653  }
654 
655  _share_with = nullptr;
656  }
657 }
658 
659 /**
660  * Returns a wgl context handle for the purpose of sharing texture context
661  * with this GSG. This will either be the GSG's own context handle, if it
662  * exists yet, or the context handle of some other GSG that this GSG is
663  * planning to share with. If this returns NULL, none of the GSG's in this
664  * share pool have yet created their context.
665  */
666 HGLRC wglGraphicsStateGuardian::
667 get_share_context() const {
668  if (_made_context) {
669  return _context;
670  }
671  if (_share_with != nullptr) {
672  return _share_with->get_share_context();
673  }
674  return nullptr;
675 }
676 
677 /**
678  * Directs the GSG (along with all GSG's it is planning to share a texture
679  * context with) to share texture context with the indicated GSG.
680  *
681  * This assumes that this GSG's context has not yet been created, and neither
682  * have any of the GSG's it is planning to share texture context with; but the
683  * graphics context for the indicated GSG has already been created.
684  */
685 void wglGraphicsStateGuardian::
686 redirect_share_pool(wglGraphicsStateGuardian *share_with) {
687  nassertv(!_made_context);
688  if (_share_with != nullptr) {
689  _share_with->redirect_share_pool(share_with);
690  } else {
691  _share_with = share_with;
692  }
693 }
694 
695 /**
696  * Creates an invisible window to associate with the GL context, even if we
697  * are not going to use it. This is necessary because in the Windows OpenGL
698  * API, we have to create window before we can create a GL context--even
699  * before we can ask about what GL extensions are available!
700  */
701 bool wglGraphicsStateGuardian::
702 make_twindow() {
703  release_twindow();
704 
705  DWORD window_style = 0;
706 
707  register_twindow_class();
708  HINSTANCE hinstance = GetModuleHandle(nullptr);
709  _twindow = CreateWindow(_twindow_class_name, "twindow", window_style,
710  0, 0, 1, 1, nullptr, nullptr, hinstance, 0);
711 
712  if (!_twindow) {
713  wgldisplay_cat.error()
714  << "CreateWindow() failed!" << std::endl;
715  return false;
716  }
717 
718  ShowWindow(_twindow, SW_HIDE);
719 
720  _twindow_dc = GetDC(_twindow);
721 
722  PIXELFORMATDESCRIPTOR pixelformat;
723  if (!SetPixelFormat(_twindow_dc, _pfnum, &pixelformat)) {
724  wgldisplay_cat.error()
725  << "SetPixelFormat(" << _pfnum << ") failed after window create\n";
726  release_twindow();
727  return false;
728  }
729 
730  return true;
731 }
732 
733 /**
734  * Closes and frees the resources associated with the temporary window created
735  * by a previous call to make_twindow().
736  */
737 void wglGraphicsStateGuardian::
738 release_twindow() {
739  if (_twindow_dc) {
740  ReleaseDC(_twindow, _twindow_dc);
741  _twindow_dc = 0;
742  }
743  if (_twindow) {
744  DestroyWindow(_twindow);
745  _twindow = 0;
746  }
747 }
748 
749 /**
750  * Registers a Window class for the twindow created by all wglGraphicsPipes.
751  * This only needs to be done once per session.
752  */
753 void wglGraphicsStateGuardian::
754 register_twindow_class() {
755  if (_twindow_class_registered) {
756  return;
757  }
758 
759  WNDCLASS wc;
760 
761  HINSTANCE instance = GetModuleHandle(nullptr);
762 
763  // Clear before filling in window structure!
764  ZeroMemory(&wc, sizeof(WNDCLASS));
765  wc.style = CS_OWNDC;
766  wc.lpfnWndProc = DefWindowProc;
767  wc.hInstance = instance;
768  wc.lpszClassName = _twindow_class_name;
769 
770  if (!RegisterClass(&wc)) {
771  wgldisplay_cat.error()
772  << "could not register window class!" << std::endl;
773  return;
774  }
775  _twindow_class_registered = true;
776 }
777 
778 #define GAMMA_1 (255.0 * 256.0)
779 
780 static bool _gamma_table_initialized = false;
781 static unsigned short _original_gamma_table [256 * 3];
782 
783 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) {
784  int i;
785  double gamma_correction;
786 
787  if (gamma <= 0.0) {
788  // avoid divide by zero and negative exponents
789  gamma = 1.0;
790  }
791  gamma_correction = 1.0 / (double) gamma;
792 
793  for (i = 0; i < 256; i++) {
794  double r;
795  double g;
796  double b;
797 
798  if (original_red_table) {
799  r = (double) original_red_table [i] / GAMMA_1;
800  g = (double) original_green_table [i] / GAMMA_1;
801  b = (double) original_blue_table [i] / GAMMA_1;
802  }
803  else {
804  r = ((double) i / 255.0);
805  g = r;
806  b = r;
807  }
808 
809  r = pow (r, gamma_correction);
810  g = pow (g, gamma_correction);
811  b = pow (b, gamma_correction);
812 
813  if (r > 1.00) {
814  r = 1.0;
815  }
816  if (g > 1.00) {
817  g = 1.0;
818  }
819  if (b > 1.00) {
820  b = 1.0;
821  }
822 
823  r = r * GAMMA_1;
824  g = g * GAMMA_1;
825  b = b * GAMMA_1;
826 
827  red_table [i] = r;
828  green_table [i] = g;
829  blue_table [i] = b;
830  }
831 }
832 
833 /**
834  * Static function for getting the original gamma.
835  */
838  bool get;
839 
840  get = false;
841  if (_gamma_table_initialized == false) {
842  HDC hdc = GetDC(nullptr);
843 
844  if (hdc) {
845  if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
846  _gamma_table_initialized = true;
847  get = true;
848  }
849 
850  ReleaseDC (nullptr, hdc);
851  }
852  }
853 
854  return get;
855 }
856 
857 /**
858  * Static function for setting gamma which is needed for atexit.
859  */
861 static_set_gamma(bool restore, PN_stdfloat gamma) {
862  bool set;
863  HDC hdc = GetDC(nullptr);
864 
865  set = false;
866  if (hdc) {
867  unsigned short ramp [256 * 3];
868 
869  if (restore && _gamma_table_initialized) {
870  _create_gamma_table_wgl (gamma, &_original_gamma_table [0], &_original_gamma_table [256], &_original_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
871  }
872  else {
873  _create_gamma_table_wgl (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
874  }
875 
876  if (SetDeviceGammaRamp (hdc, ramp)) {
877  set = true;
878  }
879 
880  ReleaseDC (nullptr, hdc);
881  }
882 
883  return set;
884 }
885 
886 /**
887  * Non static version of setting gamma. Returns true on success.
888  */
890 set_gamma(PN_stdfloat gamma) {
891  bool set;
892 
893  set = static_set_gamma(false, gamma);
894  if (set) {
895  _gamma = gamma;
896  }
897 
898  return set;
899 }
900 
901 /**
902  * Restore original gamma.
903  */
906  static_set_gamma(true, 1.0f);
907 }
908 
909 /**
910  * This function is passed to the atexit function.
911  */
914  static_set_gamma(true, 1.0);
915 }
void set_all_specified()
Marks all bits as having been specified.
HDC get_twindow_dc()
Returns the DC associated with the temporary, invisible window that was created with the gsg to query...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
bool set_gamma(PN_stdfloat gamma)
Non static version of setting gamma.
static bool get_gamma_table(void)
Static function for getting the original gamma.
void restore_gamma()
Restore original gamma.
static bool static_set_gamma(bool restore, PN_stdfloat gamma)
Static function for setting gamma which is needed for atexit.
bool get_properties_advanced(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor, using the WGL extensio...
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.
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
virtual void reset()
Resets all internal state as if the gsg were newly created.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void atexit_function(void)
This function is passed to the atexit function.
void choose_pixel_format(const FrameBufferProperties &properties, bool need_pbuffer)
Selects a pixel format for all the windows and buffers that use this gsg.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void get_properties(FrameBufferProperties &properties, HDC hdc, int pfnum)
Gets the FrameBufferProperties to match the indicated pixel format descriptor.
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set,...
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.