first pass (broken) of multi-threaded rendering
This commit is contained in:
parent
dc2653365a
commit
a8d12a79b5
|
|
@ -58,12 +58,6 @@ const bool pipe_spec_is_remote = config_display.Defined("pipe-machine")
|
|||
const bool compare_state_by_pointer =
|
||||
config_display.GetBool("compare-state-by-pointer", true);
|
||||
|
||||
// This is normally true to enable the cull traversal to perform
|
||||
// state-sorting and alpha-sorting. Turn this false to disable these
|
||||
// features and likely improve cull performance at the expense of draw
|
||||
// (and at the expense of correct alpha).
|
||||
const bool cull_sorting = config_display.GetBool("cull-sorting", true);
|
||||
|
||||
// This is normally true; set it false to disable view-frustum culling
|
||||
// (primarily useful for debugging).
|
||||
const bool view_frustum_cull = config_display.GetBool("view-frustum-cull", true);
|
||||
|
|
@ -73,6 +67,17 @@ const bool view_frustum_cull = config_display.GetBool("view-frustum-cull", true)
|
|||
// of per-frame overhead to count these things up.
|
||||
const bool pstats_unused_states = config_display.GetBool("pstats-unused-states", false);
|
||||
|
||||
// This is the default threading model to use when the GraphicsEngine
|
||||
// is created, e.g. app-cull-draw or appculldraw or some such. (See
|
||||
// GraphicsEngine::set_threading_model()).
|
||||
|
||||
// Warning! The code that uses this is currently experimental and
|
||||
// incomplete, and will almost certainly crash! Do not set
|
||||
// threading-model to anything other than its default of a
|
||||
// single-threaded model unless you are developing Panda's threading
|
||||
// system!
|
||||
const string threading_model = config_display.GetString("threading-model", "appculldraw");
|
||||
|
||||
|
||||
Config::ConfigTable::Symbol::iterator pipe_modules_begin(void) {
|
||||
return disp->begin();
|
||||
|
|
|
|||
|
|
@ -36,10 +36,11 @@ extern const bool pipe_spec_is_file;
|
|||
extern const bool pipe_spec_is_remote;
|
||||
|
||||
extern const bool compare_state_by_pointer;
|
||||
extern const bool cull_sorting;
|
||||
extern const bool view_frustum_cull;
|
||||
extern const bool pstats_unused_states;
|
||||
|
||||
extern const string threading_model;
|
||||
|
||||
extern Config::ConfigTable::Symbol::iterator pipe_modules_begin(void);
|
||||
extern Config::ConfigTable::Symbol::iterator pipe_modules_end(void);
|
||||
extern Config::ConfigTable::Symbol::iterator gsg_modules_begin(void);
|
||||
|
|
|
|||
|
|
@ -16,3 +16,30 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::get_threading_model
|
||||
// Access: Published
|
||||
// Description: Returns the current threading model in use; see
|
||||
// set_threading_model().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GraphicsEngine::ThreadingModel GraphicsEngine::
|
||||
get_threading_model() const {
|
||||
return _threading_model;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::start_cull
|
||||
// Access: Private
|
||||
// Description: Starts the cull stage for the current frame. This
|
||||
// may or may not execute within a sub-thread, according
|
||||
// to the current threading model.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GraphicsEngine::
|
||||
start_cull() {
|
||||
if (_cull_sorting) {
|
||||
cull_bin_draw();
|
||||
} else {
|
||||
cull_and_draw_together();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#include "clockObject.h"
|
||||
#include "pStatTimer.h"
|
||||
#include "pStatClient.h"
|
||||
#include "mutexHolder.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
#ifndef CPPPARSER
|
||||
PStatCollector GraphicsEngine::_cull_pcollector("Cull");
|
||||
|
|
@ -47,6 +49,32 @@ GraphicsEngine(Pipeline *pipeline) :
|
|||
if (_pipeline == (Pipeline *)NULL) {
|
||||
_pipeline = Pipeline::get_render_pipeline();
|
||||
}
|
||||
|
||||
ThreadingModel tm = string_threading_model(threading_model);
|
||||
if (tm == TM_invalid) {
|
||||
display_cat.warning()
|
||||
<< "Invalid threading model: " << threading_model << "\n";
|
||||
set_threading_model(TM_appculldraw);
|
||||
} else {
|
||||
set_threading_model(tm);
|
||||
}
|
||||
|
||||
if (tm != TM_appculldraw) {
|
||||
display_cat.info()
|
||||
<< "Using threading model " << get_threading_model() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::Destructor
|
||||
// Access: Published
|
||||
// Description: Gracefully cleans up the graphics engine and its
|
||||
// related threads. Does not delete the associated
|
||||
// windows.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GraphicsEngine::
|
||||
~GraphicsEngine() {
|
||||
terminate_threads();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -87,10 +115,21 @@ remove_window(GraphicsWindow *window) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::
|
||||
render_frame() {
|
||||
if (cull_sorting) {
|
||||
cull_bin_draw();
|
||||
if (_cull_thread == (Thread *)NULL) {
|
||||
// Nonthreaded cull: perform cull within the app thread. This
|
||||
// corresponds to either TM_appculldraw, TM_appcull_draw, or
|
||||
// TM_appcdraw.
|
||||
start_cull();
|
||||
|
||||
} else {
|
||||
cull_and_draw_together();
|
||||
// Threaded cull: perform cull within its own thread. Here we
|
||||
// simply signal the thread and then let it go about its business
|
||||
// while we return.
|
||||
MutexHolder holder(_cull_thread->_cv_mutex);
|
||||
if (_cull_thread->_thread_state == TS_wait) {
|
||||
_cull_thread->_thread_state = TS_do_frame;
|
||||
_cull_thread->_cv.signal();
|
||||
}
|
||||
}
|
||||
|
||||
// **** This doesn't belong here; it really belongs in the Pipeline,
|
||||
|
|
@ -107,16 +146,191 @@ render_frame() {
|
|||
// only for special effects, like shaders, that require
|
||||
// a complete offscreen render pass before they can
|
||||
// complete.
|
||||
//
|
||||
// This always executes completely within the calling
|
||||
// thread, regardless of the threading model in use.
|
||||
// Thus, it may be an error to call this from the app
|
||||
// thread if the threading model is not TM_appculldraw;
|
||||
// similarly, it is an error to call it from cull if the
|
||||
// threading model is TM_appcull_draw or
|
||||
// TM_app_cull_draw.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::
|
||||
render_subframe(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
|
||||
if (cull_sorting) {
|
||||
if (_cull_sorting) {
|
||||
cull_bin_draw(gsg, dr);
|
||||
} else {
|
||||
cull_and_draw_together(gsg, dr);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::set_threading_model
|
||||
// Access: Published
|
||||
// Description: Changes the way the app, cull, and draw steps are
|
||||
// divided among threads. The allowable settings are:
|
||||
//
|
||||
// TM_appculldraw: single-threaded. All steps are
|
||||
// performed by the calling thread; render_frame()
|
||||
// does not return until the frame has been fully
|
||||
// rendered.
|
||||
//
|
||||
// TM_appcull_draw: app and cull are performed
|
||||
// together, while draw is performed in a separate
|
||||
// thread. render_frame() will return after cull has
|
||||
// been completed; draw will have started in the
|
||||
// sub-thread.
|
||||
//
|
||||
// TM_app_culldraw: app is separate, while cull and
|
||||
// draw are performed in sequence within a separate
|
||||
// thread. render_frame() will return almost
|
||||
// immediately (after waiting for cull to start in
|
||||
// the sub-thread).
|
||||
//
|
||||
// TM_app_cull_draw: all stages of the pipeline are in
|
||||
// separate threads. render_frame() returns almost
|
||||
// immediately, after waiting for cull to start in
|
||||
// its sub-thread; when cull finishes, it passes the
|
||||
// frame off to a separate thread for draw and then
|
||||
// begins working on culling the next frame.
|
||||
//
|
||||
// TM_appcdraw: single-threaded, like TM_appculldraw,
|
||||
// but cull and draw are performed simultaneously,
|
||||
// rather than sequentially. This means that the
|
||||
// scene will always be rendered in scene graph
|
||||
// order; binning, state-sorting and back-to-front
|
||||
// sorting cannot be achieved.
|
||||
//
|
||||
// TM_app_cdraw: similar to TM_appcdraw, but the
|
||||
// cull&draw step is performed in a separate thread.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::
|
||||
set_threading_model(ThreadingModel threading_model) {
|
||||
bool spawn_cull_thread;
|
||||
bool spawn_draw_thread;
|
||||
bool cull_sorting;
|
||||
|
||||
switch (threading_model) {
|
||||
case TM_appculldraw:
|
||||
spawn_cull_thread = false;
|
||||
spawn_draw_thread = false;
|
||||
cull_sorting = true;
|
||||
break;
|
||||
|
||||
case TM_appcull_draw:
|
||||
spawn_cull_thread = false;
|
||||
spawn_draw_thread = true;
|
||||
cull_sorting = true;
|
||||
break;
|
||||
|
||||
case TM_app_culldraw:
|
||||
spawn_cull_thread = true;
|
||||
spawn_draw_thread = false;
|
||||
cull_sorting = true;
|
||||
break;
|
||||
|
||||
case TM_app_cull_draw:
|
||||
spawn_cull_thread = true;
|
||||
spawn_draw_thread = true;
|
||||
cull_sorting = true;
|
||||
break;
|
||||
|
||||
case TM_appcdraw:
|
||||
spawn_cull_thread = false;
|
||||
spawn_draw_thread = false;
|
||||
cull_sorting = false;
|
||||
break;
|
||||
|
||||
case TM_app_cdraw:
|
||||
spawn_cull_thread = true;
|
||||
spawn_draw_thread = false;
|
||||
cull_sorting = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
display_cat.error()
|
||||
<< "Invalid threading model: " << (int)threading_model << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if ((spawn_cull_thread || spawn_draw_thread) &&
|
||||
!Thread::is_threading_supported()) {
|
||||
display_cat.warning()
|
||||
<< "Threading model " << threading_model
|
||||
<< " requested but threading not supported.\n";
|
||||
threading_model = (cull_sorting) ? TM_appculldraw : TM_appcdraw;
|
||||
spawn_cull_thread = false;
|
||||
spawn_draw_thread = false;
|
||||
}
|
||||
|
||||
if (_threading_model != threading_model || _cull_sorting != cull_sorting) {
|
||||
terminate_threads();
|
||||
|
||||
_threading_model = threading_model;
|
||||
_cull_sorting = cull_sorting;
|
||||
|
||||
if (spawn_cull_thread) {
|
||||
const char *thread_name;
|
||||
if (spawn_draw_thread) {
|
||||
thread_name = "cull";
|
||||
} else if (cull_sorting) {
|
||||
thread_name = "culldraw";
|
||||
} else {
|
||||
thread_name = "cdraw";
|
||||
}
|
||||
_cull_thread = new CullThread(thread_name, this);
|
||||
MutexHolder holder(_cull_thread->_cv_mutex);
|
||||
if (!_cull_thread->start(TP_normal, false, true)) {
|
||||
display_cat.error()
|
||||
<< "Unable to start cull thread.\n";
|
||||
_cull_thread = (CullThread *)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (spawn_draw_thread) {
|
||||
_draw_thread = new DrawThread("draw", this);
|
||||
MutexHolder holder(_draw_thread->_cv_mutex);
|
||||
if (!_draw_thread->start(TP_normal, true, true)) {
|
||||
display_cat.error()
|
||||
<< "Unable to start draw thread.\n";
|
||||
_draw_thread = (DrawThread *)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::string_threading_model
|
||||
// Access: Public, Static
|
||||
// Description: Returns the ThreadingModel value corresponding to the
|
||||
// indicated string, or TM_invalid if the threading
|
||||
// model is invalid.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GraphicsEngine::ThreadingModel GraphicsEngine::
|
||||
string_threading_model(const string &string) {
|
||||
if (cmp_nocase_uh(string, "appculldraw") == 0) {
|
||||
return TM_appculldraw;
|
||||
|
||||
} else if (cmp_nocase_uh(string, "appcull_draw") == 0) {
|
||||
return TM_appcull_draw;
|
||||
|
||||
} else if (cmp_nocase_uh(string, "app_culldraw") == 0) {
|
||||
return TM_app_culldraw;
|
||||
|
||||
} else if (cmp_nocase_uh(string, "app_cull_draw") == 0) {
|
||||
return TM_app_cull_draw;
|
||||
|
||||
} else if (cmp_nocase_uh(string, "appcdraw") == 0) {
|
||||
return TM_appcdraw;
|
||||
|
||||
} else if (cmp_nocase_uh(string, "app_cdraw") == 0) {
|
||||
return TM_app_cdraw;
|
||||
|
||||
} else {
|
||||
return TM_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::cull_and_draw_together
|
||||
// Access: Private
|
||||
|
|
@ -191,6 +405,7 @@ cull_bin_draw() {
|
|||
DisplayRegion *dr = win->get_display_region(i);
|
||||
cull_bin_draw(win->get_gsg(), dr);
|
||||
}
|
||||
|
||||
win->end_frame();
|
||||
}
|
||||
win->process_events();
|
||||
|
|
@ -387,3 +602,124 @@ setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup) {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::terminate_threads
|
||||
// Access: Private
|
||||
// Description: Signals our child threads to terminate and waits for
|
||||
// them to clean up.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::
|
||||
terminate_threads() {
|
||||
if (_cull_thread != (Thread *)NULL) {
|
||||
MutexHolder holder(_cull_thread->_cv_mutex);
|
||||
_cull_thread->_thread_state = TS_terminate;
|
||||
_cull_thread->_cv.signal();
|
||||
}
|
||||
if (_draw_thread != (Thread *)NULL) {
|
||||
MutexHolder holder(_draw_thread->_cv_mutex);
|
||||
_draw_thread->_thread_state = TS_terminate;
|
||||
_draw_thread->_cv.signal();
|
||||
}
|
||||
|
||||
if (_cull_thread != (Thread *)NULL) {
|
||||
_cull_thread->join();
|
||||
_cull_thread = (CullThread *)NULL;
|
||||
}
|
||||
if (_draw_thread != (Thread *)NULL) {
|
||||
_draw_thread->join();
|
||||
_draw_thread = (DrawThread *)NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::CullThread::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GraphicsEngine::CullThread::
|
||||
CullThread(const string &name, GraphicsEngine *engine) :
|
||||
Thread(name),
|
||||
_engine(engine),
|
||||
_cv(_cv_mutex)
|
||||
{
|
||||
_thread_state = TS_wait;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::CullThread::thread_main
|
||||
// Access: Public, Virtual
|
||||
// Description: Runs the cull thread.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::CullThread::
|
||||
thread_main() {
|
||||
MutexHolder holder(_cv_mutex);
|
||||
while (_thread_state != TS_terminate) {
|
||||
_cv.wait();
|
||||
if (_thread_state == TS_do_frame) {
|
||||
_engine->start_cull();
|
||||
_thread_state = TS_wait;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::DrawThread::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GraphicsEngine::DrawThread::
|
||||
DrawThread(const string &name, GraphicsEngine *engine) :
|
||||
Thread(name),
|
||||
_engine(engine),
|
||||
_cv(_cv_mutex)
|
||||
{
|
||||
_thread_state = TS_wait;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsEngine::DrawThread::thread_main
|
||||
// Access: Public, Virtual
|
||||
// Description: Runs the draw thread.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsEngine::DrawThread::
|
||||
thread_main() {
|
||||
MutexHolder holder(_cv_mutex);
|
||||
while (_thread_state != TS_terminate) {
|
||||
_cv.wait();
|
||||
if (_thread_state == TS_do_frame) {
|
||||
// _engine->start_draw();
|
||||
_thread_state = TS_wait;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator << (ostream &out, GraphicsEngine::ThreadingModel threading_model) {
|
||||
switch (threading_model) {
|
||||
case GraphicsEngine::TM_invalid:
|
||||
return out << "invalid";
|
||||
|
||||
case GraphicsEngine::TM_appculldraw:
|
||||
return out << "appculldraw";
|
||||
|
||||
case GraphicsEngine::TM_appcull_draw:
|
||||
return out << "appcull_draw";
|
||||
|
||||
case GraphicsEngine::TM_app_culldraw:
|
||||
return out << "app_culldraw";
|
||||
|
||||
case GraphicsEngine::TM_app_cull_draw:
|
||||
return out << "app_cull_draw";
|
||||
|
||||
case GraphicsEngine::TM_appcdraw:
|
||||
return out << "appcdraw";
|
||||
|
||||
case GraphicsEngine::TM_app_cdraw:
|
||||
return out << "app_cdraw";
|
||||
|
||||
default:
|
||||
return out << "**invalid threading model (" << (int)threading_model
|
||||
<< ")**";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
#include "graphicsWindow.h"
|
||||
#include "sceneSetup.h"
|
||||
#include "pointerTo.h"
|
||||
#include "thread.h"
|
||||
#include "mutex.h"
|
||||
#include "conditionVar.h"
|
||||
#include "pset.h"
|
||||
#include "pStatCollector.h"
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ class DisplayRegion;
|
|||
class EXPCL_PANDA GraphicsEngine : public Namable {
|
||||
PUBLISHED:
|
||||
GraphicsEngine(Pipeline *pipeline = NULL);
|
||||
~GraphicsEngine();
|
||||
|
||||
void add_window(GraphicsWindow *window);
|
||||
bool remove_window(GraphicsWindow *window);
|
||||
|
|
@ -53,7 +57,24 @@ PUBLISHED:
|
|||
void render_frame();
|
||||
void render_subframe(GraphicsStateGuardian *gsg, DisplayRegion *dr);
|
||||
|
||||
enum ThreadingModel {
|
||||
TM_invalid,
|
||||
TM_appculldraw,
|
||||
TM_appcull_draw,
|
||||
TM_app_culldraw,
|
||||
TM_app_cull_draw,
|
||||
TM_appcdraw,
|
||||
TM_app_cdraw,
|
||||
};
|
||||
|
||||
void set_threading_model(ThreadingModel threading_model);
|
||||
INLINE ThreadingModel get_threading_model() const;
|
||||
|
||||
public:
|
||||
static ThreadingModel string_threading_model(const string &string);
|
||||
|
||||
private:
|
||||
INLINE void start_cull();
|
||||
void cull_and_draw_together();
|
||||
void cull_and_draw_together(GraphicsStateGuardian *gsg, DisplayRegion *dr);
|
||||
|
||||
|
|
@ -69,15 +90,56 @@ private:
|
|||
|
||||
bool setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup);
|
||||
|
||||
void terminate_threads();
|
||||
|
||||
enum ThreadState {
|
||||
TS_wait,
|
||||
TS_do_frame,
|
||||
TS_terminate
|
||||
};
|
||||
|
||||
class CullThread : public Thread {
|
||||
public:
|
||||
CullThread(const string &name, GraphicsEngine *engine);
|
||||
virtual void thread_main();
|
||||
|
||||
GraphicsEngine *_engine;
|
||||
Mutex _cv_mutex;
|
||||
ConditionVar _cv;
|
||||
ThreadState _thread_state;
|
||||
};
|
||||
|
||||
class DrawThread : public Thread {
|
||||
public:
|
||||
DrawThread(const string &name, GraphicsEngine *engine);
|
||||
virtual void thread_main();
|
||||
|
||||
GraphicsEngine *_engine;
|
||||
Mutex _cv_mutex;
|
||||
ConditionVar _cv;
|
||||
ThreadState _thread_state;
|
||||
};
|
||||
|
||||
Pipeline *_pipeline;
|
||||
|
||||
typedef pset<PT(GraphicsWindow)> Windows;
|
||||
Windows _windows;
|
||||
|
||||
ThreadingModel _threading_model;
|
||||
bool _cull_sorting;
|
||||
|
||||
PT(CullThread) _cull_thread;
|
||||
PT(DrawThread) _draw_thread;
|
||||
|
||||
static PStatCollector _cull_pcollector;
|
||||
static PStatCollector _draw_pcollector;
|
||||
|
||||
friend class CullThread;
|
||||
};
|
||||
|
||||
ostream &
|
||||
operator << (ostream &out, GraphicsEngine::ThreadingModel threading_model);
|
||||
|
||||
#include "graphicsEngine.I"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue