726 lines
24 KiB
C++
726 lines
24 KiB
C++
// Filename: graphicsEngine.cxx
|
|
// Created by: drose (24Feb02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://www.panda3d.org/license.txt .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d@yahoogroups.com .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "graphicsEngine.h"
|
|
#include "config_display.h"
|
|
#include "pipeline.h"
|
|
#include "drawCullHandler.h"
|
|
#include "binCullHandler.h"
|
|
#include "cullResult.h"
|
|
#include "cullTraverser.h"
|
|
#include "clockObject.h"
|
|
#include "pStatTimer.h"
|
|
#include "pStatClient.h"
|
|
#include "mutexHolder.h"
|
|
#include "string_utils.h"
|
|
|
|
#ifndef CPPPARSER
|
|
PStatCollector GraphicsEngine::_cull_pcollector("Cull");
|
|
PStatCollector GraphicsEngine::_draw_pcollector("Draw");
|
|
#endif // CPPPARSER
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::Constructor
|
|
// Access: Published
|
|
// Description: Creates a new GraphicsEngine object. The Pipeline is
|
|
// normally left to default to NULL, which indicates the
|
|
// global render pipeline, but it may be any Pipeline
|
|
// you choose.
|
|
////////////////////////////////////////////////////////////////////
|
|
GraphicsEngine::
|
|
GraphicsEngine(Pipeline *pipeline) :
|
|
_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();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::add_window
|
|
// Access: Published
|
|
// Description: Adds a new window to the set of windows that will be
|
|
// processed when render_frame() is called. This also
|
|
// increments the reference count to the window.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
add_window(GraphicsWindow *window) {
|
|
_windows.insert(window);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::remove_window
|
|
// Access: Published
|
|
// Description: Removes the indicated window from the set of windows
|
|
// that will be processed when render_frame() is called.
|
|
// This also decrements the reference count to the
|
|
// window, allowing the window to be destructed if there
|
|
// are no other references to it.
|
|
//
|
|
// The return value is true if the window was removed,
|
|
// false if it was not found.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GraphicsEngine::
|
|
remove_window(GraphicsWindow *window) {
|
|
size_t count = _windows.erase(window);
|
|
return (count != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::render_frame
|
|
// Access: Published
|
|
// Description: Renders the next frame in all the registered windows,
|
|
// and flips all of the frame buffers.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
render_frame() {
|
|
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 {
|
|
// 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,
|
|
// but here it is for now.
|
|
ClockObject::get_global_clock()->tick();
|
|
PStatClient::main_tick();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::render_subframe
|
|
// Access: Published
|
|
// Description: Performs a complete cull and draw pass for one
|
|
// particular display region. This is normally useful
|
|
// 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) {
|
|
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
|
|
// Description: An implementation of render_frame() that renders the
|
|
// frame with a DrawCullHandler, to cull and draw all
|
|
// windows in the same pass.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
cull_and_draw_together() {
|
|
Windows::iterator wi;
|
|
for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
|
|
GraphicsWindow *win = (*wi);
|
|
if (win->get_window_active()) {
|
|
win->begin_frame();
|
|
win->clear();
|
|
|
|
int num_display_regions = win->get_num_display_regions();
|
|
for (int i = 0; i < num_display_regions; i++) {
|
|
DisplayRegion *dr = win->get_display_region(i);
|
|
cull_and_draw_together(win->get_gsg(), dr);
|
|
}
|
|
win->end_frame();
|
|
}
|
|
win->process_events();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::cull_and_draw_together
|
|
// Access: Private
|
|
// Description: An implementation of render_frame() that renders the
|
|
// frame with a DrawCullHandler, to cull and draw all
|
|
// windows in the same pass.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
cull_and_draw_together(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
|
|
nassertv(gsg != (GraphicsStateGuardian *)NULL);
|
|
|
|
PT(SceneSetup) scene_setup = setup_scene(dr->get_camera(), gsg);
|
|
if (setup_gsg(gsg, scene_setup)) {
|
|
DisplayRegionStack old_dr = gsg->push_display_region(dr);
|
|
gsg->prepare_display_region();
|
|
if (dr->is_any_clear_active()) {
|
|
gsg->clear(dr);
|
|
}
|
|
|
|
DrawCullHandler cull_handler(gsg);
|
|
do_cull(&cull_handler, scene_setup, gsg);
|
|
|
|
gsg->pop_display_region(old_dr);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::cull_bin_draw
|
|
// Access: Private
|
|
// Description: An implementation of render_frame() that renders the
|
|
// frame with a BinCullHandler, to cull into bins and
|
|
// then draw the bins. This is the normal method.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
cull_bin_draw() {
|
|
Windows::iterator wi;
|
|
for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
|
|
GraphicsWindow *win = (*wi);
|
|
if (win->get_window_active()) {
|
|
win->begin_frame();
|
|
win->clear();
|
|
|
|
int num_display_regions = win->get_num_display_regions();
|
|
for (int i = 0; i < num_display_regions; i++) {
|
|
DisplayRegion *dr = win->get_display_region(i);
|
|
cull_bin_draw(win->get_gsg(), dr);
|
|
}
|
|
|
|
win->end_frame();
|
|
}
|
|
win->process_events();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::cull_bin_draw
|
|
// Access: Private
|
|
// Description: An implementation of render_frame() that renders the
|
|
// frame with a BinCullHandler, to cull into bins and
|
|
// then draw the bins. This is the normal method.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
|
|
nassertv(gsg != (GraphicsStateGuardian *)NULL);
|
|
|
|
PT(CullResult) cull_result = dr->_cull_result;
|
|
if (cull_result == (CullResult *)NULL) {
|
|
cull_result = new CullResult(gsg);
|
|
}
|
|
|
|
PT(SceneSetup) scene_setup = setup_scene(dr->get_camera(), gsg);
|
|
if (scene_setup != (SceneSetup *)NULL) {
|
|
BinCullHandler cull_handler(cull_result);
|
|
do_cull(&cull_handler, scene_setup, gsg);
|
|
|
|
cull_result->finish_cull();
|
|
|
|
// Save the results for next frame.
|
|
dr->_cull_result = cull_result->make_next();
|
|
|
|
// Now draw.
|
|
do_draw(cull_result, scene_setup, gsg, dr);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::setup_scene
|
|
// Access: Private
|
|
// Description: Returns a new SceneSetup object appropriate for
|
|
// rendering the scene from the indicated camera, or
|
|
// NULL if the scene should not be rendered for some
|
|
// reason.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(SceneSetup) GraphicsEngine::
|
|
setup_scene(const NodePath &camera, GraphicsStateGuardian *gsg) {
|
|
if (camera.is_empty()) {
|
|
// No camera, no draw.
|
|
return NULL;
|
|
}
|
|
|
|
Camera *camera_node;
|
|
DCAST_INTO_R(camera_node, camera.node(), NULL);
|
|
|
|
if (!camera_node->is_active()) {
|
|
// Camera inactive, no draw.
|
|
return NULL;
|
|
}
|
|
|
|
Lens *lens = camera_node->get_lens();
|
|
if (lens == (Lens *)NULL) {
|
|
// No lens, no draw.
|
|
return NULL;
|
|
}
|
|
|
|
NodePath scene_root = camera_node->get_scene();
|
|
if (scene_root.is_empty()) {
|
|
// No scene, no draw.
|
|
return NULL;
|
|
}
|
|
|
|
PT(SceneSetup) scene_setup = new SceneSetup;
|
|
|
|
// We will need both the camera transform (the net transform to the
|
|
// camera from the scene) and the world transform (the camera
|
|
// transform inverse, or the net transform to the scene from the
|
|
// camera).
|
|
CPT(TransformState) camera_transform = camera.get_transform(scene_root);
|
|
CPT(TransformState) world_transform = scene_root.get_transform(camera);
|
|
|
|
// The render transform is the same as the world transform, except
|
|
// it is converted into the GSG's internal coordinate system. This
|
|
// is the transform that the GSG will apply to all of its vertices.
|
|
CPT(TransformState) cs_transform = TransformState::make_identity();
|
|
CoordinateSystem external_cs = gsg->get_coordinate_system();
|
|
CoordinateSystem internal_cs = gsg->get_internal_coordinate_system();
|
|
if (internal_cs != CS_default && internal_cs != external_cs) {
|
|
cs_transform =
|
|
TransformState::make_mat(LMatrix4f::convert_mat(external_cs, internal_cs));
|
|
}
|
|
|
|
scene_setup->set_scene_root(scene_root);
|
|
scene_setup->set_camera_path(camera);
|
|
scene_setup->set_camera_node(camera_node);
|
|
scene_setup->set_lens(lens);
|
|
scene_setup->set_camera_transform(camera_transform);
|
|
scene_setup->set_world_transform(world_transform);
|
|
scene_setup->set_cs_transform(cs_transform);
|
|
|
|
return scene_setup;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::do_cull
|
|
// Access: Private
|
|
// Description: Fires off a cull traversal using the indicated camera.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
|
|
GraphicsStateGuardian *gsg) {
|
|
// Statistics
|
|
PStatTimer timer(_cull_pcollector);
|
|
|
|
CullTraverser trav;
|
|
trav.set_cull_handler(cull_handler);
|
|
trav.set_depth_offset_decals(gsg->depth_offset_decals());
|
|
trav.set_scene(scene_setup);
|
|
trav.set_camera_mask(scene_setup->get_camera_node()->get_camera_mask());
|
|
|
|
if (view_frustum_cull) {
|
|
// If we're to be performing view-frustum culling, determine the
|
|
// bounding volume associated with the current viewing frustum.
|
|
|
|
// First, we have to get the current viewing frustum, which comes
|
|
// from the lens.
|
|
PT(BoundingVolume) bv = scene_setup->get_lens()->make_bounds();
|
|
|
|
if (bv != (BoundingVolume *)NULL &&
|
|
bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
|
|
// Transform it into the appropriate coordinate space.
|
|
PT(GeometricBoundingVolume) local_frustum;
|
|
local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
|
|
local_frustum->xform(scene_setup->get_camera_transform()->get_mat());
|
|
|
|
trav.set_view_frustum(local_frustum);
|
|
}
|
|
}
|
|
|
|
trav.traverse(scene_setup->get_scene_root());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::do_draw
|
|
// Access: Private
|
|
// Description: Draws the previously-culled scene.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GraphicsEngine::
|
|
do_draw(CullResult *cull_result, SceneSetup *scene_setup,
|
|
GraphicsStateGuardian *gsg, DisplayRegion *dr) {
|
|
// Statistics
|
|
PStatTimer timer(_draw_pcollector);
|
|
|
|
if (setup_gsg(gsg, scene_setup)) {
|
|
DisplayRegionStack old_dr = gsg->push_display_region(dr);
|
|
gsg->prepare_display_region();
|
|
if (dr->is_any_clear_active()) {
|
|
gsg->clear(dr);
|
|
}
|
|
cull_result->draw();
|
|
gsg->pop_display_region(old_dr);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GraphicsEngine::setup_gsg
|
|
// Access: Private
|
|
// Description: Sets up the GSG to draw the indicated scene. Returns
|
|
// true if the scene (and its lens) is acceptable, false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GraphicsEngine::
|
|
setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup) {
|
|
if (scene_setup == (SceneSetup *)NULL) {
|
|
// No scene, no draw.
|
|
return false;
|
|
}
|
|
|
|
const Lens *lens = scene_setup->get_lens();
|
|
if (lens == (const Lens *)NULL) {
|
|
// No lens, no draw.
|
|
return false;
|
|
}
|
|
|
|
if (!gsg->set_lens(lens)) {
|
|
// The lens is inappropriate somehow.
|
|
display_cat.error()
|
|
<< gsg->get_type() << " cannot render with " << lens->get_type()
|
|
<< "\n";
|
|
return false;
|
|
}
|
|
|
|
gsg->set_scene(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
|
|
<< ")**";
|
|
}
|
|
}
|