From 626cb5966a15a164466b29482dafad1492c414f8 Mon Sep 17 00:00:00 2001 From: Bei Yang Date: Fri, 11 Jun 2010 16:43:53 +0000 Subject: [PATCH] Added a windows OpenGL implementation of finish frame and begin finish. This allows for frame flip syncronization across multiple machines. This is a change made by WDI to aid cave rendering --- panda/src/display/graphicsEngine.cxx | 90 +++++++++++++++++++ panda/src/display/graphicsEngine.h | 4 + panda/src/display/graphicsOutput.cxx | 15 ++++ panda/src/display/graphicsOutput.h | 1 + .../glstuff/glGraphicsStateGuardian_src.cxx | 22 +++++ .../src/glstuff/glGraphicsStateGuardian_src.h | 2 +- panda/src/wgldisplay/wglGraphicsWindow.cxx | 31 +++++++ panda/src/wgldisplay/wglGraphicsWindow.h | 1 + 8 files changed, 165 insertions(+), 1 deletion(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 7af3fb5c5b..b1476b084f 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -949,6 +949,31 @@ sync_frame() { } } + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::ready_flip +// Access: Published +// Description: Waits for all the threads that started drawing their +// last frame to finish drawing. Returns when all threads have +// actually finished drawing, as opposed to 'sync_frame' +// we seems to return once all draw calls have been submitted. +// Calling 'flip_frame' after this function should immediately +// cause a buffer flip. This function will only work in +// opengl right now, for all other graphics pipelines it will +// simply return immediately. In opengl it's a bit of a hack: +// it will attempt to read a single pixel from the frame buffer to +// force the graphics card to finish drawing before it returns +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +ready_flip() { + Thread *current_thread = Thread::get_current_thread(); + LightReMutexHolder holder(_lock, current_thread); + + if (_flip_state == FS_draw) { + do_ready_flip(current_thread); + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::flip_frame // Access: Published @@ -1570,6 +1595,27 @@ flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { } } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::ready_flip_windows +// Access: Private +// Description: This is called by the RenderThread object to flip the +// buffers for all of the non-single-buffered windows in +// the given list. This is run in the draw thread. +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { + Windows::const_iterator wi; + for (wi = wlist.begin(); wi != wlist.end(); ++wi) { + GraphicsOutput *win = (*wi); + if (win->flip_ready()) { + PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); + win->ready_flip(); + } + } +} + + + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::do_sync_frame // Access: Private @@ -1597,6 +1643,36 @@ do_sync_frame(Thread *current_thread) { _flip_state = FS_sync; } + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::do_ready_flip +// Access: Private +// Description: Wait until all draw calls have finished drawing and +// the frame is ready to flip +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +do_ready_flip(Thread *current_thread) { + nassertv(_lock.debug_is_locked()); + + // Statistics + PStatTimer timer(_sync_pcollector, current_thread); + + nassertv(_flip_state == FS_draw); + + // Wait for all the threads to finish their current frame. Grabbing + // and releasing the mutex should achieve that. + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + thread->_cv_mutex.acquire(); + thread->_cv_mutex.release(); + } + _app.do_ready_flip(this,current_thread); + _flip_state = FS_sync; + + +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::do_flip_frame // Access: Private @@ -2444,6 +2520,20 @@ do_flip(GraphicsEngine *engine, Thread *current_thread) { engine->flip_windows(_draw, current_thread); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::WindowRenderer::do_ready_flip +// Access: Public +// Description: Prepares windows for flipping by waiting until all draw +// calls are finished +//////////////////////////////////////////////////////////////////// +void GraphicsEngine::WindowRenderer:: +do_ready_flip(GraphicsEngine *engine, Thread *current_thread) { + LightReMutexHolder holder(_wl_lock); + engine->ready_flip_windows(_cdraw, current_thread); + engine->ready_flip_windows(_draw, current_thread); +} + + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::WindowRenderer::do_close // Access: Public diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 386759a24f..dffa94ab67 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -101,6 +101,7 @@ PUBLISHED: BLOCKING void render_frame(); BLOCKING void open_windows(); BLOCKING void sync_frame(); + BLOCKING void ready_flip(); BLOCKING void flip_frame(); bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg); @@ -144,8 +145,10 @@ private: void make_contexts(const Windows &wlist, Thread *current_thread); void process_events(const Windows &wlist, Thread *current_thread); + void ready_flip_windows(const Windows &wlist, Thread *current_thread); void flip_windows(const Windows &wlist, Thread *current_thread); void do_sync_frame(Thread *current_thread); + void do_ready_flip(Thread *current_thread); void do_flip_frame(Thread *current_thread); INLINE void close_gsg(GraphicsPipe *pipe, GraphicsStateGuardian *gsg); @@ -261,6 +264,7 @@ private: void resort_windows(); void do_frame(GraphicsEngine *engine, Thread *current_thread); void do_windows(GraphicsEngine *engine, Thread *current_thread); + void do_ready_flip(GraphicsEngine *engine, Thread *current_thread); void do_flip(GraphicsEngine *engine, Thread *current_thread); void do_release(GraphicsEngine *engine, Thread *current_thread); void do_close(GraphicsEngine *engine, Thread *current_thread); diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 325def9016..5b83e690fc 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -1256,6 +1256,21 @@ void GraphicsOutput:: begin_flip() { } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::ready_flip +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after end_frame() has been called on all windows, to +// initiate the exchange of the front and back buffers. +// +// This should instruct the window to prepare for the +// flip when it is command but not actually flip +// +//////////////////////////////////////////////////////////////////// +void GraphicsOutput:: +ready_flip() { +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::end_flip // Access: Public, Virtual diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index 77fc12147a..b69d584a58 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -212,6 +212,7 @@ public: // These methods will be called within the app (main) thread. virtual void begin_flip(); + virtual void ready_flip(); virtual void end_flip(); // It is an error to call any of the following methods from any diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index cc0f69ce10..d898b6b1ea 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1707,6 +1707,28 @@ reset() { } +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::finish +// Access: Public, Virtual +// Description: Force the graphics card to finish drawing before +// returning. !!!!!HACK WARNING!!!! +// glfinish does not actually wait for the graphics card to finish drawing +// only for draw calls to finish. Thus flip may not happene +// immediately. Instead we read a single pixel from +// the framebuffer. This forces the graphics card to +// finish drawing the frame before returning. +//////////////////////////////////////////////////////////////////// +void CLP(GraphicsStateGuardian):: +finish() { + // Rather than call glfinish which returns immediately if + // draw commands have been submitted, we will read a single pixel + // from the frame. That will force the graphics card to finish + // drawing before it is called + char data[4]; + GLP(ReadPixels)(0,0,1,1,GL_RGBA,GL_UNSIGNED_BYTE,&data); + //GLP(Finish); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::clear // Access: Public diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 14685d4165..cfec4c041e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -276,7 +276,7 @@ public: void bind_fbo(GLuint fbo); virtual bool get_supports_cg_profile(const string &name) const; - + void finish(); protected: void do_issue_transform(); diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index ceeb5d23c2..574f4aabce 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -144,6 +144,37 @@ begin_flip() { } } +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsWindow::ready_flip +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// after end_frame() has been called on all windows, to +// initiate the exchange of the front and back buffers. +// +// This should instruct the window to prepare for the +// flip when command, but will not actually flip +// +// We have the two separate functions, begin_flip() and +// end_flip(), to make it easier to flip all of the +// windows at the same time. +//////////////////////////////////////////////////////////////////// +void wglGraphicsWindow:: +ready_flip() { + if (_hdc) { + // The documentation on SwapBuffers() is not at all clear on + // whether the GL context needs to be current before it can be + // called. Empirically, it appears that it is not necessary in + // many cases, but it definitely is necessary at least in the case + // of Mesa on Windows. + wglGraphicsStateGuardian *wglgsg; + DCAST_INTO_V(wglgsg, _gsg); + HGLRC context = wglgsg->get_context(_hdc); + nassertv(context); + wglGraphicsPipe::wgl_make_current(_hdc, context, &_make_current_pcollector); + wglgsg->finish(); + } +} + //////////////////////////////////////////////////////////////////// // Function: wglGraphicsWindow::close_window // Access: Protected, Virtual diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h index fe3260edea..4726243232 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.h +++ b/panda/src/wgldisplay/wglGraphicsWindow.h @@ -38,6 +38,7 @@ public: virtual void end_frame(FrameMode mode, Thread *current_thread); virtual void begin_flip(); + virtual void ready_flip(); protected: virtual void close_window();