Browse Source

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

Bei Yang 15 years ago
parent
commit
626cb5966a

+ 90 - 0
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
 //     Function: GraphicsEngine::flip_frame
 //       Access: Published
 //       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
 //     Function: GraphicsEngine::do_sync_frame
 //       Access: Private
 //       Access: Private
@@ -1597,6 +1643,36 @@ do_sync_frame(Thread *current_thread) {
   _flip_state = FS_sync;
   _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
 //     Function: GraphicsEngine::do_flip_frame
 //       Access: Private
 //       Access: Private
@@ -2444,6 +2520,20 @@ do_flip(GraphicsEngine *engine, Thread *current_thread) {
   engine->flip_windows(_draw, 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
 //     Function: GraphicsEngine::WindowRenderer::do_close
 //       Access: Public
 //       Access: Public

+ 4 - 0
panda/src/display/graphicsEngine.h

@@ -101,6 +101,7 @@ PUBLISHED:
   BLOCKING void render_frame();
   BLOCKING void render_frame();
   BLOCKING void open_windows();
   BLOCKING void open_windows();
   BLOCKING void sync_frame();
   BLOCKING void sync_frame();
+  BLOCKING void ready_flip();  
   BLOCKING void flip_frame();
   BLOCKING void flip_frame();
 
 
   bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
   bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
@@ -144,8 +145,10 @@ private:
   void make_contexts(const Windows &wlist, Thread *current_thread);
   void make_contexts(const Windows &wlist, Thread *current_thread);
 
 
   void process_events(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 flip_windows(const Windows &wlist, Thread *current_thread);
   void do_sync_frame(Thread *current_thread);
   void do_sync_frame(Thread *current_thread);
+  void do_ready_flip(Thread *current_thread);
   void do_flip_frame(Thread *current_thread);
   void do_flip_frame(Thread *current_thread);
   INLINE void close_gsg(GraphicsPipe *pipe, GraphicsStateGuardian *gsg);
   INLINE void close_gsg(GraphicsPipe *pipe, GraphicsStateGuardian *gsg);
 
 
@@ -261,6 +264,7 @@ private:
     void resort_windows();
     void resort_windows();
     void do_frame(GraphicsEngine *engine, Thread *current_thread);
     void do_frame(GraphicsEngine *engine, Thread *current_thread);
     void do_windows(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_flip(GraphicsEngine *engine, Thread *current_thread);
     void do_release(GraphicsEngine *engine, Thread *current_thread);
     void do_release(GraphicsEngine *engine, Thread *current_thread);
     void do_close(GraphicsEngine *engine, Thread *current_thread);
     void do_close(GraphicsEngine *engine, Thread *current_thread);

+ 15 - 0
panda/src/display/graphicsOutput.cxx

@@ -1256,6 +1256,21 @@ void GraphicsOutput::
 begin_flip() {
 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
 //     Function: GraphicsOutput::end_flip
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 1 - 0
panda/src/display/graphicsOutput.h

@@ -212,6 +212,7 @@ public:
 
 
   // These methods will be called within the app (main) thread.
   // These methods will be called within the app (main) thread.
   virtual void begin_flip();
   virtual void begin_flip();
+  virtual void ready_flip();
   virtual void end_flip();
   virtual void end_flip();
 
 
   // It is an error to call any of the following methods from any
   // It is an error to call any of the following methods from any

+ 22 - 0
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
 //     Function: GraphicsStateGuardian::clear
 //       Access: Public
 //       Access: Public

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -276,7 +276,7 @@ public:
 
 
   void bind_fbo(GLuint fbo);
   void bind_fbo(GLuint fbo);
   virtual bool get_supports_cg_profile(const string &name) const;
   virtual bool get_supports_cg_profile(const string &name) const;
-
+  void finish();
 
 
 protected:
 protected:
   void do_issue_transform();
   void do_issue_transform();

+ 31 - 0
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
 //     Function: wglGraphicsWindow::close_window
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/wgldisplay/wglGraphicsWindow.h

@@ -38,6 +38,7 @@ public:
   virtual void end_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
 
 
   virtual void begin_flip();
   virtual void begin_flip();
+  virtual void ready_flip();
 
 
 protected:
 protected:
   virtual void close_window();
   virtual void close_window();