diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 5ff1ae188d..5540f8d485 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -585,7 +585,7 @@ class ShowBase(DirectObject.DirectObject): def openWindow(self, props = None, fbprops = None, pipe = None, gsg = None, type = None, name = None, size = None, aspectRatio = None, makeCamera = True, keepCamera = False, - scene = None, stereo = None, + scene = None, stereo = None, unexposedDraw = None, callbackWindowDict = None, requireWindow = None): """ Creates a window and adds it to the list of windows that are @@ -601,6 +601,9 @@ class ShowBase(DirectObject.DirectObject): If keepCamera is false but makeCamera is true, a new camera is set up to render into the new window. + If unexposedDraw is not None, it specifies the initial value + of GraphicsWindow.setUnexposedDraw(). + If callbackWindowDict is not None, a CallbackGraphicWindow is created instead, which allows the caller to create the actual window with its own OpenGL context, and direct Panda's @@ -618,7 +621,7 @@ class ShowBase(DirectObject.DirectObject): props = props, fbprops = fbprops, pipe = pipe, gsg = gsg, type = type, name = name, size = size, aspectRatio = aspectRatio, makeCamera = makeCamera, keepCamera = keepCamera, - scene = scene, stereo = stereo, + scene = scene, stereo = stereo, unexposedDraw = unexposedDraw, callbackWindowDict = callbackWindowDict) if self.win: @@ -678,7 +681,7 @@ class ShowBase(DirectObject.DirectObject): def _doOpenWindow(self, props = None, fbprops = None, pipe = None, gsg = None, type = None, name = None, size = None, aspectRatio = None, makeCamera = True, keepCamera = False, - scene = None, stereo = None, + scene = None, stereo = None, unexposedDraw = None, callbackWindowDict = None): if pipe == None: pipe = self.pipe @@ -745,6 +748,9 @@ class ShowBase(DirectObject.DirectObject): # Couldn't create a window! return None + if unexposedDraw is not None and hasattr(win, 'setUnexposedDraw'): + win.setUnexposedDraw(unexposedDraw) + if callbackWindowDict: # If we asked for (and received) a CallbackGraphicsWindow, # we now have to assign the callbacks, before we start diff --git a/direct/src/wxwidgets/WxPandaWindow.py b/direct/src/wxwidgets/WxPandaWindow.py index 444f10dc6b..09fcdaa5f1 100644 --- a/direct/src/wxwidgets/WxPandaWindow.py +++ b/direct/src/wxwidgets/WxPandaWindow.py @@ -40,7 +40,8 @@ class EmbeddedPandaWindow(wx.Window): # only happen on 32-bit Windows. wp.setParentWindow(self.GetHandle() & 0xffffffff) - self.win = base.openWindow(props = wp, gsg = gsg, type = 'onscreen') + self.win = base.openWindow(props = wp, gsg = gsg, type = 'onscreen', + unexposedDraw = False) self.Bind(wx.EVT_SIZE, self.onSize) # This doesn't actually do anything, since wx won't call diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 941006311a..de49ef3633 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -336,6 +336,11 @@ ConfigVariableInt parent_window_handle "an HWND on Windows, or the NSWindow pointer or XWindow pointer " "converted to an integer, on OSX and X11.")); +ConfigVariableBool win_unexposed_draw +("win-unexposed-draw", true, + PRC_DESC("Specifies the default setting of GraphicsWindow::set_unexposed_draw(). " + "See that method for more information.")); + ConfigVariableFilename subprocess_window ("subprocess-window", "", PRC_DESC("The filename of a SubprocessWindowBuffer's temporary mmap file, " diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index 48edde4f19..ddaad07b57 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -78,6 +78,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableFilename cursor_filename; extern EXPCL_PANDA_DISPLAY ConfigVariableEnum z_order; extern EXPCL_PANDA_DISPLAY ConfigVariableString window_title; extern EXPCL_PANDA_DISPLAY ConfigVariableInt parent_window_handle; +extern EXPCL_PANDA_DISPLAY ConfigVariableBool win_unexposed_draw; extern EXPCL_PANDA_DISPLAY ConfigVariableFilename subprocess_window; extern EXPCL_PANDA_DISPLAY ConfigVariableString framebuffer_mode; diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I index 62d5969d75..19e52ef7f2 100644 --- a/panda/src/display/graphicsOutput.I +++ b/panda/src/display/graphicsOutput.I @@ -845,8 +845,10 @@ clear_cube_map_selection() { //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::trigger_flip -// Access: Public -// Description: Set the flip_ready flag, only if legal to do so. +// Access: Protected +// Description: To be called at the end of the frame, after the +// window has successfully been drawn and is ready to be +// flipped (if appropriate). //////////////////////////////////////////////////////////////////// INLINE void GraphicsOutput:: trigger_flip() { diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index b70b788cbd..d9d1a588c6 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -92,12 +92,13 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe, _creation_flags = flags; _x_size = _y_size = 0; _has_size = win_prop.has_size(); + _is_nonzero_size = false; if (_has_size) { _x_size = win_prop.get_x_size(); _y_size = win_prop.get_y_size(); + _is_nonzero_size = (_x_size > 0 && _y_size > 0); } _is_valid = false; - _is_nonzero_size = false; _flip_ready = false; _cube_map_index = -1; _cube_map_dr = NULL; diff --git a/panda/src/display/graphicsWindow.I b/panda/src/display/graphicsWindow.I index 3a7bb12b17..39ba7c71b7 100644 --- a/panda/src/display/graphicsWindow.I +++ b/panda/src/display/graphicsWindow.I @@ -40,6 +40,36 @@ is_fullscreen() const { return _properties.get_fullscreen(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::set_unexposed_draw +// Access: Published +// Description: If this flag is false, the window is redrawn only +// after it has received a recent "unexpose" or "draw" +// event from the underlying windowing systme. If this +// flag is true, the window is redrawn every frame +// regardless. Setting this false may prevent the +// window from redrawing unnecessarily when it is +// hidden, and may play nicer with other windows on the +// desktop, but may adversely affect frame rate even +// when the window is fully visible; setting it true will +// ensure that the window contents are always current. +//////////////////////////////////////////////////////////////////// +INLINE void GraphicsWindow:: +set_unexposed_draw(bool unexposed_draw) { + _unexposed_draw = unexposed_draw; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsWindow::get_unexposed_draw +// Access: Published +// Description: See set_unexposed_draw(). +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsWindow:: +get_unexposed_draw() const { + return _unexposed_draw; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsWindow::get_window_handle // Access: Published diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index cf0838b104..36dbfd8f1a 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -62,6 +62,8 @@ GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, request_properties(win_prop); _window_event = "window-event"; + _got_expose_event = false; + _unexposed_draw = win_unexposed_draw; set_pixel_zoom(pixel_zoom); } diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index e43aa9cf01..00ded5833f 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -71,6 +71,9 @@ PUBLISHED: void set_close_request_event(const string &close_request_event); string get_close_request_event() const; + INLINE void set_unexposed_draw(bool unexposed_draw); + INLINE bool get_unexposed_draw() const; + INLINE WindowHandle *get_window_handle() const; // Mouse and keyboard routines @@ -151,6 +154,8 @@ protected: PT(WindowHandle) _window_handle; PT(WindowHandle) _parent_window_handle; + bool _got_expose_event; + private: LightReMutex _properties_lock; // protects _requested_properties, _rejected_properties, and @@ -160,6 +165,7 @@ private: WindowProperties _rejected_properties; string _window_event; string _close_request_event; + bool _unexposed_draw; #ifdef HAVE_PYTHON typedef pset PythonWinProcClasses; diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx index b9c5fefb48..21802d7a88 100755 --- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx @@ -97,6 +97,14 @@ begin_frame(FrameMode mode, Thread *current_thread) { return false; } + if (!get_unexposed_draw() && !_got_expose_event) { + if (wdxdisplay9_cat.is_spam()) { + wdxdisplay9_cat.spam() + << "Not drawing " << this << ": unexposed.\n"; + } + return false; + } + if (_awaiting_restore) { // The fullscreen window was recently restored; we can't continue // until the GSG says we can. @@ -129,7 +137,6 @@ begin_frame(FrameMode mode, Thread *current_thread) { //////////////////////////////////////////////////////////////////// void wdxGraphicsWindow9:: end_frame(FrameMode mode, Thread *current_thread) { - end_frame_spam(mode); nassertv(_gsg != (GraphicsStateGuardian *)NULL); @@ -160,7 +167,7 @@ end_flip() { if (_dxgsg != (DXGraphicsStateGuardian9 *)NULL && is_active()) { _dxgsg->show_frame(); } - GraphicsWindow::end_flip(); + WinGraphicsWindow::end_flip(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/tinydisplay/tinyWinGraphicsWindow.cxx b/panda/src/tinydisplay/tinyWinGraphicsWindow.cxx index ddfaab42a6..b55a234d2e 100755 --- a/panda/src/tinydisplay/tinyWinGraphicsWindow.cxx +++ b/panda/src/tinydisplay/tinyWinGraphicsWindow.cxx @@ -72,6 +72,14 @@ begin_frame(FrameMode mode, Thread *current_thread) { return false; } + if (!get_unexposed_draw() && !_got_expose_event) { + if (tinydisplay_cat.is_spam()) { + tinydisplay_cat.spam() + << "Not drawing " << this << ": unexposed.\n"; + } + return false; + } + TinyGraphicsStateGuardian *tinygsg; DCAST_INTO_R(tinygsg, _gsg, false); diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index 3f6a88fe1c..b264b7e308 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -66,6 +66,19 @@ begin_frame(FrameMode mode, Thread *current_thread) { if (_gsg == (GraphicsStateGuardian *)NULL) { return false; } + + if (!get_unexposed_draw() && !_got_expose_event) { + if (wgldisplay_cat.is_spam()) { + wgldisplay_cat.spam() + << "Not drawing " << this << ": unexposed.\n"; + } + return false; + } + + if (wgldisplay_cat.is_spam()) { + wgldisplay_cat.spam() + << "Drawing " << this << ": exposed.\n"; + } wglGraphicsStateGuardian *wglgsg; DCAST_INTO_R(wglgsg, _gsg, false); @@ -93,7 +106,6 @@ begin_frame(FrameMode mode, Thread *current_thread) { //////////////////////////////////////////////////////////////////// void wglGraphicsWindow:: end_frame(FrameMode mode, Thread *current_thread) { - end_frame_spam(mode); nassertv(_gsg != (GraphicsStateGuardian *)NULL); diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index a07d27cf4d..8fcfea2d8f 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -364,6 +364,30 @@ set_properties_now(WindowProperties &properties) { } } +//////////////////////////////////////////////////////////////////// +// Function: WinGraphicsWindow::trigger_flip +// Access: Protected +// Description: To be called at the end of the frame, after the +// window has successfully been drawn and is ready to be +// flipped (if appropriate). +//////////////////////////////////////////////////////////////////// +void WinGraphicsWindow:: +trigger_flip() { + GraphicsWindow::trigger_flip(); + + if (!get_unexposed_draw()) { + // Now that we've drawn or whatever, invalidate the rectangle so + // we won't redraw again until we get the WM_PAINT message. + + InvalidateRect(_hWnd, NULL, FALSE); + _got_expose_event = false; + + if (windisplay_cat.is_spam()) { + windisplay_cat.spam() + << "InvalidateRect: " << this << "\n"; + } + } +} //////////////////////////////////////////////////////////////////// // Function: WinGraphicsWindow::close_window @@ -1450,7 +1474,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { handle_reshape(); } break; - + case WM_EXITSIZEMOVE: handle_reshape(); break; @@ -1458,6 +1482,19 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case WM_WINDOWPOSCHANGED: adjust_z_order(); break; + + case WM_PAINT: + // In response to WM_PAINT, we check to see if there are any + // update regions at all; if there are, we declare the window + // exposed. This is used to implement !_unexposed_draw. + if (GetUpdateRect(_hWnd, NULL, false)) { + if (windisplay_cat.is_spam()) { + windisplay_cat.spam() + << "Got update regions: " << this << "\n"; + } + _got_expose_event = true; + } + break; case WM_LBUTTONDOWN: if (_lost_keypresses) { diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index 2becff96b4..c36ca847b1 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -88,6 +88,7 @@ public: virtual TouchInfo get_touch_info(int index); protected: + void trigger_flip(); virtual void close_window(); virtual bool open_window(); virtual void fullscreen_minimized(WindowProperties &properties);