Panda3D
wglGraphicsBuffer.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 wglGraphicsBuffer.cxx
10  * @author drose
11  * @date 2004-02-08
12  */
13 
14 #include "wglGraphicsBuffer.h"
15 #include "wglGraphicsPipe.h"
16 #include "config_wgldisplay.h"
17 #include "glgsg.h"
19 #include "pStatTimer.h"
20 
21 #include <wingdi.h>
22 
23 TypeHandle wglGraphicsBuffer::_type_handle;
24 
25 
26 /**
27  *
28  */
29 wglGraphicsBuffer::
30 wglGraphicsBuffer(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  GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
38 {
39  _pbuffer = (HPBUFFERARB)0;
40  _pbuffer_dc = (HDC)0;
41  release_pbuffer();
42 
43  // Since the pbuffer never gets flipped, we get screenshots from the same
44  // buffer we draw into.
45  _screenshot_buffer_type = _draw_buffer_type;
46 }
47 
48 /**
49  *
50  */
51 wglGraphicsBuffer::
52 ~wglGraphicsBuffer() {
53 }
54 
55 /**
56  * This function will be called within the draw thread before beginning
57  * rendering for a given frame. It should do whatever setup is required, and
58  * return true if the frame should be rendered, or false if it should be
59  * skipped.
60  */
62 begin_frame(FrameMode mode, Thread *current_thread) {
63 
64  begin_frame_spam(mode);
65  if (_gsg == nullptr) {
66  return false;
67  }
68 
70  DCAST_INTO_R(wglgsg, _gsg, false);
71 
72  HGLRC context = wglgsg->get_context(_pbuffer_dc);
73  if (context == 0) {
74  return false;
75  }
76 
77  if (_pbuffer_bound) {
78  if (_fb_properties.is_single_buffered()) {
79  wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
80  } else {
81  wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
82  }
83  }
84 
85  if (!rebuild_bitplanes()) {
86  wglGraphicsPipe::wgl_make_current(0, 0, &_make_current_pcollector);
87  return false;
88  }
89 
90  wglGraphicsPipe::wgl_make_current(_pbuffer_dc, context,
91  &_make_current_pcollector);
92 
93  if (mode == FM_render) {
94  CDLockedReader cdata(_cycler);
95  for (size_t i = 0; i != cdata->_textures.size(); ++i) {
96  const RenderTexture &rt = cdata->_textures[i];
97  RenderTextureMode rtm_mode = rt._rtm_mode;
98  RenderTexturePlane plane = rt._plane;
99  if (rtm_mode == RTM_bind_or_copy && plane != RTP_color) {
100  CDWriter cdataw(_cycler, cdata, false);
101  nassertr(cdata->_textures.size() == cdataw->_textures.size(), false);
102  cdataw->_textures[i]._rtm_mode = RTM_copy_texture;
103  }
104  }
105  clear_cube_map_selection();
106  }
107 
108  _gsg->set_current_properties(&get_fb_properties());
109  return _gsg->begin_frame(current_thread);
110 }
111 
112 /**
113  * This function will be called within the draw thread after rendering is
114  * completed for a given frame. It should do whatever finalization is
115  * required.
116  */
118 end_frame(FrameMode mode, Thread *current_thread) {
119  end_frame_spam(mode);
120  nassertv(_gsg != nullptr);
121 
122  if (mode == FM_render) {
123  copy_to_textures();
124  bind_texture_to_pbuffer();
125  }
126 
127  _gsg->end_frame(current_thread);
128 
129  if (mode == FM_render) {
130  trigger_flip();
131  clear_cube_map_selection();
132  }
133 }
134 
135 /**
136  * Looks for the appropriate texture, and binds that texture to the pbuffer.
137  */
138 void wglGraphicsBuffer::
139 bind_texture_to_pbuffer() {
140  wglGraphicsStateGuardian *wglgsg;
141  DCAST_INTO_V(wglgsg, _gsg);
142 
143  // Find the color texture, if there is one. That one can be bound to the
144  // framebuffer. All others must be marked RTM_copy_to_texture.
145 
146  int tex_index = -1;
147  CDLockedReader cdata(_cycler);
148  for (size_t i = 0; i != cdata->_textures.size(); ++i) {
149  const RenderTexture &rt = cdata->_textures[i];
150  RenderTexturePlane plane = rt._plane;
151  if (plane == RTP_color && rt._rtm_mode == RTM_bind_or_copy) {
152  tex_index = i;
153  break;
154  }
155  }
156 
157  if (tex_index >= 0) {
158  const RenderTexture &rt = cdata->_textures[tex_index];
159  Texture *tex = rt._texture;
160  if ((_pbuffer_bound != 0)&&(_pbuffer_bound != tex)) {
161  _pbuffer_bound->release(wglgsg->get_prepared_objects());
162  _pbuffer_bound = 0;
163  }
165 
166  if (tex->get_match_framebuffer_format()) {
167  if (_fb_properties.get_alpha_bits()) {
168  tex->set_format(Texture::F_rgba);
169  } else {
170  tex->set_format(Texture::F_rgb);
171  }
172  }
173  TextureContext *tc = tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg);
174  nassertv(tc != nullptr);
175  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
176  GLenum target = wglgsg->get_texture_target(tex->get_texture_type());
177  if (target == GL_NONE) {
178  CDWriter cdataw(_cycler, cdata, false);
179  nassertv(cdata->_textures.size() == cdataw->_textures.size());
180  cdataw->_textures[tex_index]._rtm_mode = RTM_copy_texture;
181  return;
182  }
183  GLP(BindTexture)(target, gtc->_index);
184  if (_fb_properties.is_single_buffered()) {
185  wglgsg->_wglBindTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
186  } else {
187  wglgsg->_wglBindTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
188  }
189  _pbuffer_bound = tex;
190  } else {
191  if (_pbuffer_bound != 0) {
192  _pbuffer_bound->release(wglgsg->get_prepared_objects());
193  _pbuffer_bound = 0;
194  }
195  }
196 }
197 
198 /**
199  * Called internally when the window is in render-to-a-texture mode and we are
200  * in the process of rendering the six faces of a cube map. This should do
201  * whatever needs to be done to switch the buffer to the indicated face.
202  */
205  wglGraphicsStateGuardian *wglgsg;
206  DCAST_INTO_V(wglgsg, _gsg);
207 
208  nassertv(wglgsg->_wglSetPbufferAttribARB != nullptr);
209 
210  static const int max_attrib_list = 64;
211  int iattrib_list[max_attrib_list];
212  int ni = 0;
213 
214  iattrib_list[ni++] = WGL_CUBE_MAP_FACE_ARB;
215  iattrib_list[ni++] = WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + page;
216 
217  // Terminate the list.
218  nassertv(ni <= max_attrib_list);
219  iattrib_list[ni] = 0;
220 
221  wglgsg->_wglSetPbufferAttribARB(_pbuffer, iattrib_list);
222 }
223 
224 /**
225  * Do whatever processing is necessary to ensure that the window responds to
226  * user events. Also, honor any requests recently made via
227  * request_properties()
228  *
229  * This function is called only within the window thread.
230  */
234 
235  MSG msg;
236 
237  // Handle all the messages on the queue in a row. Some of these might be
238  // for another window, but they will get dispatched appropriately.
239  while (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
240  process_1_event();
241  }
242 }
243 
244 /**
245  * Returns true if this particular GraphicsOutput can render directly into a
246  * texture, or false if it must always copy-to-texture at the end of each
247  * frame to achieve this effect.
248  */
251  if (_gsg == nullptr) {
252  return false;
253  }
254 
255  wglGraphicsStateGuardian *wglgsg;
256  DCAST_INTO_R(wglgsg, _gsg, false);
257  return wglgsg->get_supports_wgl_render_texture();
258 }
259 
260 /**
261  * Closes the buffer right now. Called from the window thread.
262  */
263 void wglGraphicsBuffer::
264 close_buffer() {
265  if (_gsg != nullptr) {
266  wglGraphicsStateGuardian *wglgsg;
267  DCAST_INTO_V(wglgsg, _gsg);
268 
269  _gsg.clear();
270  }
271 
272  release_pbuffer();
273 
274  _is_valid = false;
275 }
276 
277 /**
278  * Opens the window right now. Called from the window thread. Returns true
279  * if the window is successfully opened, or false if there was a problem.
280  */
281 bool wglGraphicsBuffer::
282 open_buffer() {
283 
284  // pbuffers don't seem to work correctly in double-buffered mode. Besides,
285  // the back buffer is a pointless waste of space. So always use a single-
286  // buffered gsg.
287 
288  _fb_properties.set_back_buffers(0);
289  _draw_buffer_type = RenderBuffer::T_front;
290  _screenshot_buffer_type = RenderBuffer::T_front;
291 
292  // GSG creationinitialization.
293 
294  wglGraphicsStateGuardian *wglgsg;
295  if (_gsg == 0) {
296  // There is no old gsg. Create a new one.
297  wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr);
298  wglgsg->choose_pixel_format(_fb_properties, true);
299  _gsg = wglgsg;
300  } else {
301  // If the old gsg has the wrong pixel format, create a new one that shares
302  // with the old gsg.
303  DCAST_INTO_R(wglgsg, _gsg, false);
304  if ((!wglgsg->get_fb_properties().subsumes(_fb_properties))||
305  (!wglgsg->get_fb_properties().is_single_buffered())||
306  (!wglgsg->pfnum_supports_pbuffer())) {
307  wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, wglgsg);
308  wglgsg->choose_pixel_format(_fb_properties, true);
309  _gsg = wglgsg;
310  }
311  }
312 
313  // Use the temp window to initialize the gsg.
314 
315  HDC twindow_dc = wglgsg->get_twindow_dc();
316  if (twindow_dc == 0) {
317  // If we couldn't make a window, we can't get a GL context.
318  _gsg = nullptr;
319  return false;
320  }
321  HGLRC context = wglgsg->get_context(twindow_dc);
322  if (context == 0) {
323  _gsg = nullptr;
324  return false;
325  }
326  wglGraphicsPipe::wgl_make_current(twindow_dc, context,
327  &_make_current_pcollector);
328  wglgsg->reset_if_new();
329  wglgsg->report_my_gl_errors();
331  (_fb_properties,wglgsg->get_gl_renderer())) {
332  _gsg = nullptr;
333  return false;
334  }
335  _fb_properties = wglgsg->get_fb_properties();
336 
337  // Now that we have fully made a window and used that window to create a
338  // rendering context, we can attempt to create a pbuffer. This might fail
339  // if the pbuffer extensions are not supported.
340 
341  if (!rebuild_bitplanes()) {
342  wglGraphicsPipe::wgl_make_current(0, 0, &_make_current_pcollector);
343  _gsg = nullptr;
344  return false;
345  }
346 
347  _is_valid = true;
348 
349  return true;
350 }
351 
352 /**
353  * Destroys the pbuffer if it has been created. The intent is that this may
354  * allow it to be recreated with different options.
355  */
356 void wglGraphicsBuffer::
357 release_pbuffer() {
358  if (_gsg == 0) {
359  return;
360  }
361 
362  wglGraphicsStateGuardian *wglgsg;
363  DCAST_INTO_V(wglgsg, _gsg);
364 
365  if (_pbuffer_bound != 0) {
366  _pbuffer_bound->release(wglgsg->get_prepared_objects());
367  _pbuffer_bound = 0;
368  }
369  wglGraphicsPipe::wgl_make_current(0, 0, nullptr);
370  if (_pbuffer_dc) {
371  wglgsg->_wglReleasePbufferDCARB(_pbuffer, _pbuffer_dc);
372  }
373  if (_pbuffer) {
374  wglgsg->_wglDestroyPbufferARB(_pbuffer);
375  }
376  _pbuffer = (HPBUFFERARB)0;
377  _pbuffer_dc = (HDC)0;
378  _pbuffer_mipmap = false;
379  _pbuffer_sizex = 0;
380  _pbuffer_sizey = 0;
381  _pbuffer_type = Texture::TT_2d_texture;
382 }
383 
384 /**
385  * Once the GL context has been fully realized, attempts to create an
386  * offscreen pbuffer if the graphics API supports it. Returns true if
387  * successful, false on failure.
388  */
389 bool wglGraphicsBuffer::
390 rebuild_bitplanes() {
391  wglGraphicsStateGuardian *wglgsg;
392  DCAST_INTO_R(wglgsg, _gsg, false);
393 
394  if (!wglgsg->_supports_pbuffer) {
395  wgldisplay_cat.info()
396  << "PBuffers not supported by GL implementation.\n";
397  return false;
398  }
399 
400  // Find the texture to bind to the color buffer.
401  Texture *bindtexture = nullptr;
402  for (int i=0; i<count_textures(); i++) {
403  if ((get_rtm_mode(i) == RTM_bind_or_copy)&&
404  (get_texture(i)->get_format() != Texture::F_depth_stencil)) {
405  bindtexture = get_texture(i);
406  break;
407  }
408  }
409 
410  // If we already have a pbuffer, and if it's lost, then force the rebuild.
411 
412  if (_pbuffer_dc) {
413  int flag = 0;
414  wglgsg->_wglQueryPbufferARB(_pbuffer, WGL_PBUFFER_LOST_ARB, &flag);
415  if (flag != 0) {
416  release_pbuffer();
417  }
418  }
419 
420  // Determine what pbuffer attributes are needed for currently-applicable
421  // textures.
422 
423  if ((_host != 0)&&(_creation_flags & GraphicsPipe::BF_size_track_host)) {
424  if (_host->get_size() != _size) {
425  set_size_and_recalc(_host->get_x_size(),
426  _host->get_y_size());
427  }
428  }
429  int desired_x = get_x_size();
430  int desired_y = get_y_size();
431  if ((bindtexture != 0)&&(Texture::get_textures_power_2() != ATS_none)) {
432  desired_x = Texture::up_to_power_2(desired_x);
433  desired_y = Texture::up_to_power_2(desired_y);
434  }
435  bool desired_mipmap = false;
436  Texture::TextureType desired_type = Texture::TT_2d_texture;
437  if (bindtexture != 0) {
438  desired_mipmap = bindtexture->uses_mipmaps();
439  desired_type = bindtexture->get_texture_type();
440  }
441 
442  if ((_pbuffer != 0)&&
443  (_pbuffer_sizex == desired_x)&&
444  (_pbuffer_sizey == desired_y)&&
445  (_pbuffer_mipmap == desired_mipmap)&&
446  (_pbuffer_type == desired_type)) {
447  // the pbuffer we already have is fine. Do not rebuild.
448  return true;
449  }
450 
451  // Release the old pbuffer, if there was one.
452 
453  release_pbuffer();
454 
455  // Allocate the new pbuffer.
456 
457  int pfnum = wglgsg->get_pfnum();
458 
459  static const int max_attrib_list = 64;
460  int iattrib_list[max_attrib_list];
461  int ni = 0;
462 
463  if (_fb_properties.get_alpha_bits()) {
464  iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
465  iattrib_list[ni++] = WGL_TEXTURE_RGBA_ARB;
466  } else {
467  iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
468  iattrib_list[ni++] = WGL_TEXTURE_RGB_ARB;
469  }
470 
471  if (desired_mipmap) {
472  iattrib_list[ni++] = WGL_MIPMAP_TEXTURE_ARB;
473  iattrib_list[ni++] = 1;
474  }
475 
476  switch (desired_type) {
477  case Texture::TT_cube_map:
478  iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
479  iattrib_list[ni++] = WGL_TEXTURE_CUBE_MAP_ARB;
480  break;
481 
482  case Texture::TT_1d_texture:
483  iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
484  iattrib_list[ni++] = WGL_TEXTURE_1D_ARB;
485  break;
486 
487  default:
488  iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
489  iattrib_list[ni++] = WGL_TEXTURE_2D_ARB;
490  }
491 
492  // Terminate the list.
493  nassertr(ni <= max_attrib_list, false);
494  iattrib_list[ni] = 0;
495 
496  HDC twindow_dc = wglgsg->get_twindow_dc();
497  if (twindow_dc == 0) {
498  return false;
499  }
500 
501  HGLRC context = wglgsg->get_context(twindow_dc);
502  if (context == 0) {
503  return false;
504  }
505  wglGraphicsPipe::wgl_make_current(twindow_dc, context,
506  &_make_current_pcollector);
507 
508  _pbuffer = wglgsg->_wglCreatePbufferARB(twindow_dc, pfnum,
509  desired_x, desired_y, iattrib_list);
510 
511  if (_pbuffer == 0) {
512  wgldisplay_cat.info()
513  << "Attempt to create pbuffer failed.\n";
514  return false;
515  }
516 
517  _pbuffer_dc = wglgsg->_wglGetPbufferDCARB(_pbuffer);
518  _pbuffer_mipmap = desired_mipmap;
519  _pbuffer_type = desired_type;
520  _pbuffer_sizex = desired_x;
521  _pbuffer_sizey = desired_y;
522 
523  return true;
524 }
525 
526 /**
527  * Handles one event from the message queue.
528  */
529 void wglGraphicsBuffer::
530 process_1_event() {
531  MSG msg;
532 
533  if (!GetMessage(&msg, nullptr, 0, 0)) {
534  // WM_QUIT received. We need a cleaner way to deal with this.
535  // DestroyAllWindows(false);
536  exit(msg.wParam); // this will invoke AtExitFn
537  }
538 
539  // Translate virtual key messages
540  TranslateMessage(&msg);
541  // Call window_proc
542  DispatchMessage(&msg);
543 }
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.
HDC get_twindow_dc()
Returns the DC associated with the temporary, invisible window that was created with the gsg to query...
int get_pfnum() const
Returns the pixel format number chosen for windows that use this context.
void set_size_and_recalc(int x, int y)
Changes the x_size and y_size, then recalculates structures that depend on size.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
virtual Texture * get_texture(int i=0) const
Returns the nth texture into which the GraphicsOutput renders.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
int get_y_size() const
Returns the visible height of the window or buffer, if it is known.
get_texture_type
Returns the overall interpretation of the texture.
Definition: texture.h:357
This is a special class object that holds all the information returned by a particular GSG to indicat...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A tiny specialization on GLGraphicsStateGuardian to add some wgl-specific information.
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: texture.cxx:1567
virtual void process_events()
Honor any requests recently made via request_open() or request_close().
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well.
Definition: texture.cxx:1907
static AutoTextureScale get_textures_power_2()
This flag returns ATS_none, ATS_up, or ATS_down and controls the scaling of textures in general.
Definition: texture.I:1863
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition: texture.h:362
A container for the various kinds of properties we might ask to have on a graphics window before we o...
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping,...
Definition: texture.I:1127
bool get_supports_wgl_render_texture() const
Returns true if this particular GSG can render from a wglGraphicsBuffer directly into a texture,...
An offscreen buffer for rendering into.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_format
Changes the format value for the texture components.
Definition: texture.h:362
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.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
int count_textures() const
If the GraphicsOutput is set to render into a texture, returns the number of textures that are being ...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
get_match_framebuffer_format
Returns true if the special flag was set that indicates to the GSG that the Texture's format should b...
Definition: texture.h:581
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).
int get_x_size() const
Returns the visible width of the window or buffer, if it is known.
TextureContext * prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the texture on the particular GSG, if it does not already exist.
Definition: texture.cxx:1956
const FrameBufferProperties & get_fb_properties() const
Returns the properties of the pixel format that was chosen for this gsg.
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1983
A thread; that is, a lightweight process.
Definition: thread.h:46
RenderTextureMode get_rtm_mode(int i=0) const
Returns the RenderTextureMode associated with the nth render-texture.
bool pfnum_supports_pbuffer() const
Returns true if the gsg's pixel format is capable of supporting a pbuffer.
Encapsulates all the communication with a particular instance of a given rendering backend.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void select_target_tex_page(int page)
Called internally when the window is in render-to-a-texture mode and we are in the process of renderi...
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 bool get_supports_render_texture() const
Returns true if this particular GraphicsOutput can render directly into a texture,...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.