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