// Filename: graphicsEngine.h // Created by: drose (24Feb02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #ifndef GRAPHICSENGINE_H #define GRAPHICSENGINE_H #include "pandabase.h" #include "graphicsWindow.h" #include "graphicsBuffer.h" #include "frameBufferProperties.h" #include "graphicsThreadingModel.h" #include "sceneSetup.h" #include "pointerTo.h" #include "thread.h" #include "pmutex.h" #include "lightReMutex.h" #include "conditionVar.h" #include "pStatCollector.h" #include "pset.h" #include "ordered_vector.h" #include "indirectLess.h" #include "loader.h" #include "referenceCount.h" class Pipeline; class DisplayRegion; class GraphicsPipe; class FrameBufferProperties; class Texture; //////////////////////////////////////////////////////////////////// // Class : GraphicsEngine // Description : This class is the main interface to controlling the // render process. There is typically only one // GraphicsEngine in an application, and it synchronizes // rendering to all all of the active windows; although // it is possible to have multiple GraphicsEngine // objects if multiple synchronicity groups are // required. // // The GraphicsEngine is responsible for managing the // various cull and draw threads. The application // simply calls engine->render_frame() and considers it // done. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_DISPLAY GraphicsEngine : public ReferenceCount { PUBLISHED: GraphicsEngine(Pipeline *pipeline = NULL); ~GraphicsEngine(); void set_threading_model(const GraphicsThreadingModel &threading_model); GraphicsThreadingModel get_threading_model() const; INLINE void set_auto_flip(bool auto_flip); INLINE bool get_auto_flip() const; INLINE void set_portal_cull(bool value); INLINE bool get_portal_cull() const; INLINE void set_default_loader(Loader *loader); INLINE Loader *get_default_loader() const; GraphicsOutput *make_output(GraphicsPipe *pipe, const string &name, int sort, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg = NULL, GraphicsOutput *host = NULL); // Syntactic shorthand versions of make_output INLINE GraphicsOutput *make_buffer(GraphicsOutput *host, const string &name, int sort, int x_size, int y_size); INLINE GraphicsOutput *make_buffer(GraphicsStateGuardian *gsg, const string &name, int sort, int x_size, int y_size); INLINE GraphicsOutput *make_parasite(GraphicsOutput *host, const string &name, int sort, int x_size, int y_size); bool remove_window(GraphicsOutput *window); void remove_all_windows(); void reset_all_windows(bool swapchain); bool is_empty() const; int get_num_windows() const; GraphicsOutput *get_window(int n) const; MAKE_SEQ(get_windows, get_num_windows, get_window); 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); static GraphicsEngine *get_global_ptr(); public: enum ThreadState { TS_wait, TS_do_frame, TS_do_flip, TS_do_release, TS_do_windows, TS_terminate, TS_done }; void texture_uploaded(Texture *tex); public: static void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, GraphicsStateGuardian *gsg, Thread *current_thread); private: typedef ov_set< PT(GraphicsOutput), IndirectLess > Windows; typedef pset< PT(GraphicsStateGuardian) > GSGs; static bool scene_root_func(const PandaNode *node); bool is_scene_root(const PandaNode *node); void set_window_sort(GraphicsOutput *window, int sort); void cull_and_draw_together(const Windows &wlist, Thread *current_thread); void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); void cull_to_bins(const Windows &wlist, Thread *current_thread); void cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); void draw_bins(const Windows &wlist, Thread *current_thread); void draw_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); 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); PT(SceneSetup) setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr); void do_draw(CullResult *cull_result, SceneSetup *scene_setup, GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread); void do_add_window(GraphicsOutput *window, const GraphicsThreadingModel &threading_model); void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, const GraphicsThreadingModel &threading_model); void do_remove_window(GraphicsOutput *window, Thread *current_thread); void do_resort_windows(); void terminate_threads(Thread *current_thread); void auto_adjust_capabilities(GraphicsStateGuardian *gsg); #ifdef DO_PSTATS typedef map CyclerTypeCounters; CyclerTypeCounters _all_cycler_types; CyclerTypeCounters _dirty_cycler_types; static void pstats_count_cycler_type(TypeHandle type, int count, void *data); static void pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data); #endif // DO_PSTATS static const RenderState *get_invert_polygon_state(); // The WindowRenderer class records the stages of the pipeline that // each thread (including the main thread, a.k.a. "app") should // process, and the list of windows for each stage. // There is one WindowRenderer instance for app, and another // instance for each thread (the thread-specific WindowRenderers are // actually instances of RenderThread, below, which inherits from // WindowRenderer). // The idea is that each window is associated with one or more // WindowRenderer objects, according to the threads in which its // rendering tasks (window, cull, and draw) are divided into. // The "window" task is responsible for doing any updates to the // window itself, such as size and placement, and is wholly // responsible for any API calls to the windowing system itself, // unrelated to OpenGL-type calls. This is normally done in app // (the design of X-Windows is such that all X calls must be issued // in the same thread). // The "cull" task is responsible for crawling through the scene // graph and discovering all of the Geoms that are within the // viewing frustum. It assembles all such Geoms, along with their // computed net state and transform, in a linked list of // CullableObjects, which it stores for the "draw" task, next. // The "draw" task is responsible for walking through the list of // CullableObjects recorded by the cull task, and issuing the // appropriate graphics commands to draw them. // There is an additional task, not often used, called "cdraw". // This task, if activated, will crawl through the scene graph and // issue graphics commands immediately, as each Geom is discovered. // It is only rarely used because it cannot perform sorting beyond // basic scene graph order, making it less useful than a separate // cull and draw task. // It is possible for all three of the normal tasks: window, cull, // and draw, to be handled by the same thread. This is the normal, // single-threaded model: all tasks are handled by the app thread. // In this case, the window will be added to _app's _window, _cull, // and _draw lists. // On the other hand, a window's tasks may also be distributed among // as many as three threads. For instance, if the window is listed // on _app's _window list, but on thread A's _cull list, and thread // B's _draw list, then the window task will be handled in the app // thread, while the cull task will be handled by thread A, and the // draw task will be handled (in parallel) by thread B. (In order // for this to work, it will be necessary that thread A and B are // configured to view different stages of the graphics pipeline. // This is a more advanced topic than there is room to discuss in // this comment.) // Manipulation of the various window lists in each WindowRenderer // object is always performed in the app thread. The auxiliary // threads are slaves to the app thread, and they can only perform // one of a handful of specified tasks, none of which includes // adding or removing windows from its lists. The full set of tasks // that a WindowRenderer may perform is enumerated in ThreadState, // above; see RenderThread::thread_main(). // There is a pair of condition variables for each thread, _cv_start // and _cv_done, that is used to synchronize requests made by app to // a particular thread. The usual procedure to request a thread to // perform a particular task is the following: the app thread waits // on the thread's _cv_done variable, stores the value corresponding // to the desired task in the thread's _thread_state value, then // signals the thread's _cv_start variable. The thread, in turn, // will perform its requested task, set its _thread_state to // TS_wait, and signal _cv_done. See examples in the code, // e.g. open_windows(), for more details on this process. // It is of course not necessary to signal any threads in order to // perform tasks listed in the _app WindowRenderer. For this object // only, we simply call the appropriate methods on _app when we want // the tasks to be performed. class WindowRenderer { public: WindowRenderer(const string &name); void add_gsg(GraphicsStateGuardian *gsg); void add_window(Windows &wlist, GraphicsOutput *window); void remove_window(GraphicsOutput *window); 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); void do_pending(GraphicsEngine *engine, Thread *current_thread); bool any_done_gsgs() const; public: Windows _cull; // cull stage Windows _cdraw; // cull-and-draw-together stage Windows _draw; // draw stage Windows _window; // window stage, i.e. process windowing events // These are not kept sorted. Windows _pending_close; // moved from _window, pending close. GSGs _gsgs; // draw stage LightReMutex _wl_lock; }; class RenderThread : public Thread, public WindowRenderer { public: RenderThread(const string &name, GraphicsEngine *engine); virtual void thread_main(); GraphicsEngine *_engine; Mutex _cv_mutex; ConditionVar _cv_start; ConditionVar _cv_done; ThreadState _thread_state; }; WindowRenderer *get_window_renderer(const string &name, int pipeline_stage); Pipeline *_pipeline; Windows _windows; bool _windows_sorted; unsigned int _window_sort_index; bool _needs_open_windows; WindowRenderer _app; typedef pmap Threads; Threads _threads; GraphicsThreadingModel _threading_model; bool _auto_flip; bool _portal_enabled; //toggle to portal culling on/off PT(Loader) _default_loader; enum FlipState { FS_draw, // Still drawing. FS_sync, // All windows are done drawing. FS_flip, // All windows are done drawing and have flipped. }; FlipState _flip_state; bool _singular_warning_last_frame; bool _singular_warning_this_frame; class LoadedTexture { public: PT(Texture) _tex; UpdateSeq _image_modified; }; typedef pvector LoadedTextures; LoadedTextures _loaded_textures; LightReMutex _lock; static PT(GraphicsEngine) _global_ptr; static PStatCollector _wait_pcollector; static PStatCollector _cycle_pcollector; static PStatCollector _app_pcollector; static PStatCollector _render_frame_pcollector; static PStatCollector _do_frame_pcollector; static PStatCollector _yield_pcollector; static PStatCollector _cull_pcollector; static PStatCollector _cull_setup_pcollector; static PStatCollector _cull_sort_pcollector; static PStatCollector _draw_pcollector; static PStatCollector _sync_pcollector; static PStatCollector _flip_pcollector; static PStatCollector _flip_begin_pcollector; static PStatCollector _flip_end_pcollector; static PStatCollector _transform_states_pcollector; static PStatCollector _transform_states_unused_pcollector; static PStatCollector _render_states_pcollector; static PStatCollector _render_states_unused_pcollector; static PStatCollector _cyclers_pcollector; static PStatCollector _dirty_cyclers_pcollector; static PStatCollector _delete_pcollector; static PStatCollector _sw_sprites_pcollector; static PStatCollector _vertex_data_small_pcollector; static PStatCollector _vertex_data_independent_pcollector; static PStatCollector _vertex_data_pending_pcollector; static PStatCollector _vertex_data_resident_pcollector; static PStatCollector _vertex_data_compressed_pcollector; static PStatCollector _vertex_data_used_disk_pcollector; static PStatCollector _vertex_data_unused_disk_pcollector; static PStatCollector _cnode_volume_pcollector; static PStatCollector _gnode_volume_pcollector; static PStatCollector _geom_volume_pcollector; static PStatCollector _node_volume_pcollector; static PStatCollector _volume_pcollector; static PStatCollector _test_pcollector; static PStatCollector _volume_polygon_pcollector; static PStatCollector _test_polygon_pcollector; static PStatCollector _volume_plane_pcollector; static PStatCollector _test_plane_pcollector; static PStatCollector _volume_sphere_pcollector; static PStatCollector _test_sphere_pcollector; static PStatCollector _volume_tube_pcollector; static PStatCollector _test_tube_pcollector; static PStatCollector _volume_inv_sphere_pcollector; static PStatCollector _test_inv_sphere_pcollector; static PStatCollector _volume_geom_pcollector; static PStatCollector _test_geom_pcollector; static PStatCollector _occlusion_untested_pcollector; static PStatCollector _occlusion_passed_pcollector; static PStatCollector _occlusion_failed_pcollector; static PStatCollector _occlusion_tests_pcollector; friend class WindowRenderer; friend class GraphicsOutput; }; #include "graphicsEngine.I" #endif