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

This commit is contained in:
Bei Yang 2010-06-11 16:43:53 +00:00
parent 1edd26540b
commit 626cb5966a
8 changed files with 165 additions and 1 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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();