Panda3D
wglGraphicsWindow.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 wglGraphicsWindow.cxx
10  * @author drose
11  * @date 2002-12-20
12  */
13 
14 #include "wglGraphicsWindow.h"
15 #include "config_wgldisplay.h"
16 #include "config_windisplay.h"
17 #include "wglGraphicsPipe.h"
18 #include "pStatTimer.h"
19 #include "glgsg.h"
21 
22 #include <wingdi.h>
23 
24 TypeHandle wglGraphicsWindow::_type_handle;
25 
26 /**
27  *
28  */
29 wglGraphicsWindow::
30 wglGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
31  const std::string &name,
32  const FrameBufferProperties &fb_prop,
33  const WindowProperties &win_prop,
34  int flags,
36  GraphicsOutput *host) :
37  WinGraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
38 {
39  _hdc = (HDC)0;
40 }
41 
42 /**
43  *
44  */
45 wglGraphicsWindow::
46 ~wglGraphicsWindow() {
47 }
48 
49 /**
50  * This function will be called within the draw thread before beginning
51  * rendering for a given frame. It should do whatever setup is required, and
52  * return true if the frame should be rendered, or false if it should be
53  * skipped.
54  */
56 begin_frame(FrameMode mode, Thread *current_thread) {
57 
58  begin_frame_spam(mode);
59  if (_gsg == nullptr) {
60  return false;
61  }
62 
63  if (!get_unexposed_draw() && !_got_expose_event) {
64  if (wgldisplay_cat.is_spam()) {
65  wgldisplay_cat.spam()
66  << "Not drawing " << this << ": unexposed.\n";
67  }
68  return false;
69  }
70 
71  if (wgldisplay_cat.is_spam()) {
72  wgldisplay_cat.spam()
73  << "Drawing " << this << ": exposed.\n";
74  }
75 
77  DCAST_INTO_R(wglgsg, _gsg, false);
78 
79  HGLRC context = wglgsg->get_context(_hdc);
80  nassertr(context, false);
81 
82  if (!wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector)) {
83  wgldisplay_cat.error()
84  << "Failed to make WGL context current.\n";
85  return false;
86  }
87  wglgsg->reset_if_new();
88 
89  if (mode == FM_render) {
90  wglgsg->push_group_marker(std::string("wglGraphicsWindow ") + get_name());
91  clear_cube_map_selection();
92  }
93 
94  _gsg->set_current_properties(&get_fb_properties());
95  return _gsg->begin_frame(current_thread);
96 }
97 
98 /**
99  * This function will be called within the draw thread after rendering is
100  * completed for a given frame. It should do whatever finalization is
101  * required.
102  */
104 end_frame(FrameMode mode, Thread *current_thread) {
105  end_frame_spam(mode);
106 
107  nassertv(_gsg != nullptr);
108 
109  if (mode == FM_render) {
110  copy_to_textures();
111  }
112 
113  _gsg->end_frame(current_thread);
114 
115  if (mode == FM_render) {
116  trigger_flip();
117  clear_cube_map_selection();
118 
119  wglGraphicsStateGuardian *wglgsg;
120  DCAST_INTO_V(wglgsg, _gsg);
121  wglgsg->pop_group_marker();
122  }
123 }
124 
125 /**
126  * This function will be called within the draw thread after end_frame() has
127  * been called on all windows, to initiate the exchange of the front and back
128  * buffers.
129  *
130  * This should instruct the window to prepare for the flip at the next video
131  * sync, but it should not wait.
132  *
133  * We have the two separate functions, begin_flip() and end_flip(), to make it
134  * easier to flip all of the windows at the same time.
135  */
138 }
139 
140 /**
141  * This function will be called within the draw thread after end_frame() has
142  * been called on all windows, to initiate the exchange of the front and back
143  * buffers.
144  *
145  * This should instruct the window to prepare for the flip when command, but
146  * will not actually flip
147  *
148  * We have the two separate functions, begin_flip() and end_flip(), to make it
149  * easier to flip all of the windows at the same time.
150  */
153  if (_hdc) {
154  // The documentation on SwapBuffers() is not at all clear on whether the
155  // GL context needs to be current before it can be called. Empirically,
156  // it appears that it is not necessary in many cases, but it definitely is
157  // necessary at least in the case of Mesa on Windows.
158  wglGraphicsStateGuardian *wglgsg;
159  DCAST_INTO_V(wglgsg, _gsg);
160  HGLRC context = wglgsg->get_context(_hdc);
161  nassertv(context);
162  wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
163  wglgsg->finish();
164  }
165 }
166 
167 /**
168  * This function will be called within the draw thread after begin_flip() has
169  * been called on all windows, to finish the exchange of the front and back
170  * buffers.
171  *
172  * This should cause the window to wait for the flip, if necessary.
173  */
176  if (_hdc != nullptr && _flip_ready) {
177  // The documentation on SwapBuffers() is not at all clear on whether the
178  // GL context needs to be current before it can be called. Empirically,
179  // it appears that it is not necessary in many cases, but it definitely is
180  // necessary at least in the case of Mesa on Windows.
181  wglGraphicsStateGuardian *wglgsg;
182  DCAST_INTO_V(wglgsg, _gsg);
183  HGLRC context = wglgsg->get_context(_hdc);
184  nassertv(context);
185  wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
186  SwapBuffers(_hdc);
187  }
189 }
190 
191 /**
192  * Closes the window right now. Called from the window thread.
193  */
194 void wglGraphicsWindow::
195 close_window() {
196  if (_gsg != nullptr) {
197  wglGraphicsPipe::wgl_make_current(_hdc, nullptr, &_make_current_pcollector);
198  _gsg.clear();
199  }
200  ReleaseDC(_hWnd, _hdc);
201  _hdc = (HDC)0;
202  WinGraphicsWindow::close_window();
203 }
204 
205 /**
206  * Opens the window right now. Called from the window thread. Returns true
207  * if the window is successfully opened, or false if there was a problem.
208  */
209 bool wglGraphicsWindow::
210 open_window() {
211  if (!WinGraphicsWindow::open_window()) {
212  return false;
213  }
214 
215  // GSG creationinitialization.
216 
217  wglGraphicsStateGuardian *wglgsg;
218  if (_gsg == 0) {
219  // There is no old gsg. Create a new one.
220  wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr);
221  wglgsg->choose_pixel_format(_fb_properties, false);
222  _gsg = wglgsg;
223  } else {
224  // If the old gsg has the wrong pixel format, create a new one that shares
225  // with the old gsg.
226  DCAST_INTO_R(wglgsg, _gsg, false);
227  if (!wglgsg->get_fb_properties().subsumes(_fb_properties)) {
228  wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, wglgsg);
229  wglgsg->choose_pixel_format(_fb_properties, false);
230  _gsg = wglgsg;
231  }
232  }
233 
234  // Set up the pixel format of the window appropriately for GL.
235 
236  _hdc = GetDC(_hWnd);
237  int pfnum = wglgsg->get_pfnum();
238  PIXELFORMATDESCRIPTOR pixelformat;
239  DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
240  &pixelformat);
241 
242 #ifdef NOTIFY_DEBUG
243  char msg[200];
244  sprintf(msg, "Selected GL PixelFormat is #%d", pfnum);
245  print_pfd(&pixelformat, msg);
246 #endif
247 
248  BOOL set_pfnum = SetPixelFormat(_hdc, pfnum, &pixelformat);
249 
250  if (!set_pfnum) {
251  if (wglgsg->fail_pfnum()) {
252  wgldisplay_cat.error()
253  << "SetPixelFormat(" << pfnum << ") failed; trying "
254  << wglgsg->get_pfnum() << " instead\n";
255 
256  pfnum = wglgsg->get_pfnum();
257  DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
258  &pixelformat);
259 
260 #ifdef NOTIFY_DEBUG
261  sprintf(msg, "Selected GL PixelFormat is #%d", pfnum);
262  print_pfd(&pixelformat, msg);
263 #endif
264 
265  DescribePixelFormat(_hdc, pfnum, sizeof(PIXELFORMATDESCRIPTOR),
266  &pixelformat);
267  set_pfnum = SetPixelFormat(_hdc, pfnum, &pixelformat);
268  }
269  }
270 
271  if (!set_pfnum) {
272  wgldisplay_cat.error()
273  << "SetPixelFormat(" << pfnum << ") failed after window create\n";
274  close_window();
275  return false;
276  }
277 
278 #ifndef NDEBUG
279  if (gl_force_invalid) {
280  wgldisplay_cat.error()
281  << "Artificially failing window.\n";
282  close_window();
283  return false;
284  }
285 #endif // NDEBUG
286 
287  // Initializes _colormap
288  setup_colormap(pixelformat);
289 
290  // Make sure we have a context created.
291  HGLRC context = wglgsg->get_context(_hdc);
292  if (!context) {
293  // The context failed to create for some reason.
294  wgldisplay_cat.error()
295  << "Closing window because no valid context is available.\n";
296  close_window();
297  return false;
298  }
299 
300  // Initialize the gsg.
301  wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector);
302  wglgsg->reset_if_new();
303  wglgsg->report_my_gl_errors();
305  (_fb_properties,wglgsg->get_gl_renderer())) {
306  close_window();
307  return false;
308  }
309  _fb_properties = wglgsg->get_fb_properties();
310 
311  return true;
312 }
313 
314 /**
315  * Sets up a colormap for the window matching the selected pixel format. This
316  * is necessary before creating a GL context.
317  */
318 void wglGraphicsWindow::
319 setup_colormap(const PIXELFORMATDESCRIPTOR &pixelformat) {
320  LOGPALETTE *logical;
321  int n;
322 
323  if (!(pixelformat.dwFlags & PFD_NEED_PALETTE ||
324  pixelformat.iPixelType == PFD_TYPE_COLORINDEX))
325  return;
326 
327  n = 1 << pixelformat.cColorBits;
328 
329  /* allocate a bunch of memory for the logical palette (assume 256
330  colors in a Win32 palette */
331  logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
332  sizeof(PALETTEENTRY) * n);
333  memset(logical, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n);
334 
335  /* set the entries in the logical palette */
336  logical->palVersion = 0x300;
337  logical->palNumEntries = n;
338 
339  /* start with a copy of the current system palette */
340  GetSystemPaletteEntries(_hdc, 0, 256, &logical->palPalEntry[0]);
341 
342  if (pixelformat.iPixelType == PFD_TYPE_RGBA) {
343  int redMask = (1 << pixelformat.cRedBits) - 1;
344  int greenMask = (1 << pixelformat.cGreenBits) - 1;
345  int blueMask = (1 << pixelformat.cBlueBits) - 1;
346  int i;
347 
348  /* fill in an RGBA color palette */
349  for (i = 0; i < n; ++i) {
350  logical->palPalEntry[i].peRed =
351  (((i >> pixelformat.cRedShift) & redMask) * 255) / redMask;
352  logical->palPalEntry[i].peGreen =
353  (((i >> pixelformat.cGreenShift) & greenMask) * 255) / greenMask;
354  logical->palPalEntry[i].peBlue =
355  (((i >> pixelformat.cBlueShift) & blueMask) * 255) / blueMask;
356  logical->palPalEntry[i].peFlags = 0;
357  }
358  }
359 
360  _colormap = CreatePalette(logical);
361  free(logical);
362 
363  SelectPalette(_hdc, _colormap, FALSE);
364  RealizePalette(_hdc);
365 }
366 
367 #ifdef NOTIFY_DEBUG
368 
369 typedef enum {Software, MCD, ICD} OGLDriverType;
370 static const char *OGLDrvStrings[3] = {"Software","MCD","ICD"};
371 
372 /**
373  * Reports information about the selected pixel format descriptor, along with
374  * the indicated message.
375  */
376 void wglGraphicsWindow::
377 print_pfd(PIXELFORMATDESCRIPTOR *pfd, char *msg) {
378  if (!wgldisplay_cat.is_debug()) {
379  return;
380  }
381 
382  OGLDriverType drvtype;
383  if ((pfd->dwFlags & PFD_GENERIC_ACCELERATED) &&
384  (pfd->dwFlags & PFD_GENERIC_FORMAT)) {
385  drvtype=MCD;
386  } else if (!(pfd->dwFlags & PFD_GENERIC_ACCELERATED) && !(pfd->dwFlags & PFD_GENERIC_FORMAT)) {
387  drvtype=ICD;
388  } else {
389  drvtype=Software;
390  }
391 
392 #define PRINT_FLAG(FLG) ((pfd->dwFlags & PFD_##FLG) ? (" PFD_" #FLG "|") : "")
393  wgldisplay_cat.debug()
394  << "================================\n";
395 
396  wgldisplay_cat.debug()
397  << msg << ", " << OGLDrvStrings[drvtype] << " driver\n"
398  << "PFD flags: 0x" << std::hex << pfd->dwFlags << std::dec << " ("
399  << PRINT_FLAG(GENERIC_ACCELERATED)
400  << PRINT_FLAG(GENERIC_FORMAT)
401  << PRINT_FLAG(DOUBLEBUFFER)
402  << PRINT_FLAG(SUPPORT_OPENGL)
403  << PRINT_FLAG(SUPPORT_GDI)
404  << PRINT_FLAG(STEREO)
405  << PRINT_FLAG(DRAW_TO_WINDOW)
406  << PRINT_FLAG(DRAW_TO_BITMAP)
407  << PRINT_FLAG(SWAP_EXCHANGE)
408  << PRINT_FLAG(SWAP_COPY)
409  << PRINT_FLAG(SWAP_LAYER_BUFFERS)
410  << PRINT_FLAG(NEED_PALETTE)
411  << PRINT_FLAG(NEED_SYSTEM_PALETTE)
412  << PRINT_FLAG(SUPPORT_DIRECTDRAW) << ")\n"
413  << "PFD iPixelType: "
414  << ((pfd->iPixelType==PFD_TYPE_RGBA) ? "PFD_TYPE_RGBA":"PFD_TYPE_COLORINDEX")
415  << std::endl
416  << "PFD cColorBits: " << (DWORD)pfd->cColorBits
417  << " R: " << (DWORD)pfd->cRedBits
418  <<" G: " << (DWORD)pfd->cGreenBits
419  <<" B: " << (DWORD)pfd->cBlueBits << std::endl
420  << "PFD cAlphaBits: " << (DWORD)pfd->cAlphaBits
421  << " DepthBits: " << (DWORD)pfd->cDepthBits
422  <<" StencilBits: " << (DWORD)pfd->cStencilBits
423  <<" AccumBits: " << (DWORD)pfd->cAccumBits
424  << std::endl;
425 }
426 #endif
int get_pfnum() const
Returns the pixel format number chosen for windows that use this context.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void end_flip()
This function will be called within the draw thread after begin_flip() has been called on all windows...
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool fail_pfnum()
This is called by wglGraphicsWindow when it finds it cannot use the pfnum determined by the GSG.
virtual void ready_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
An abstract base class for glGraphicsWindow and dxGraphicsWindow (and, in general,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
HGLRC get_context(HDC hdc)
Returns the GL context associated with the GSG.
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
This is a base class for the various different classes that represent the result of a frame of render...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool verify_hardware_software(const FrameBufferProperties &props, const std::string &renderer) const
Validates that the properties represent the desired kind of renderer (hardware or software).
get_unexposed_draw
See set_unexposed_draw().
const FrameBufferProperties & get_fb_properties() const
Returns the properties of the pixel format that was chosen for this gsg.
get_name
Returns the name that was passed to the GraphicsOutput constructor.
A thread; that is, a lightweight process.
Definition: thread.h:46
virtual void begin_flip()
This function will be called within the draw thread after end_frame() has been called on all windows,...
Encapsulates all the communication with a particular instance of a given rendering backend.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool subsumes(const FrameBufferProperties &other) const
Returns true if this set of properties makes strictly greater or equal demands of the framebuffer tha...
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...
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.