From db0fd516a0f515ec0fb007f3dda9031b94132b4d Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 24 Sep 2014 23:53:37 +0000 Subject: [PATCH] Add GPU profiling capabilities to PStats using OpenGL timer queries --- panda/src/display/Sources.pp | 2 + panda/src/display/graphicsEngine.cxx | 243 +++++----- panda/src/display/graphicsStateGuardian.I | 45 +- panda/src/display/graphicsStateGuardian.cxx | 251 ++++++++-- panda/src/display/graphicsStateGuardian.h | 46 +- panda/src/display/pStatGPUTimer.I | 61 +++ panda/src/display/pStatGPUTimer.h | 65 +++ panda/src/glstuff/glCgShaderContext_src.cxx | 24 +- panda/src/glstuff/glGraphicsBuffer_src.cxx | 31 +- panda/src/glstuff/glGraphicsBuffer_src.h | 23 +- .../src/glstuff/glGraphicsStateGuardian_src.I | 14 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 455 +++++++++++------- .../src/glstuff/glGraphicsStateGuardian_src.h | 85 ++-- panda/src/glstuff/glLatencyQueryContext_src.I | 14 + .../src/glstuff/glLatencyQueryContext_src.cxx | 54 +++ panda/src/glstuff/glLatencyQueryContext_src.h | 57 +++ panda/src/glstuff/glShaderContext_src.cxx | 47 +- panda/src/glstuff/glTimerQueryContext_src.I | 28 ++ panda/src/glstuff/glTimerQueryContext_src.cxx | 104 ++++ panda/src/glstuff/glTimerQueryContext_src.h | 68 +++ panda/src/glstuff/glmisc_src.cxx | 12 +- panda/src/glstuff/glmisc_src.h | 1 + panda/src/glstuff/glstuff_src.cxx | 2 + panda/src/glstuff/glstuff_src.h | 2 + panda/src/glstuff/panda_glext.h | 6 +- panda/src/gobj/Sources.pp | 3 + panda/src/gobj/config_gobj.cxx | 8 +- panda/src/gobj/p3gobj_composite1.cxx | 3 + panda/src/gobj/p3gobj_composite2.cxx | 4 +- panda/src/gobj/timerQueryContext.I | 26 + panda/src/gobj/timerQueryContext.cxx | 35 ++ panda/src/gobj/timerQueryContext.h | 60 +++ panda/src/gsgbase/graphicsStateGuardianBase.h | 7 +- panda/src/pstatclient/config_pstats.cxx | 11 + panda/src/pstatclient/config_pstats.h | 1 + panda/src/pstatclient/pStatClient.cxx | 109 +++-- panda/src/pstatclient/pStatClient.h | 7 +- panda/src/pstatclient/pStatClientImpl.cxx | 60 ++- panda/src/pstatclient/pStatClientImpl.h | 3 +- panda/src/pstatclient/pStatProperties.cxx | 8 +- panda/src/pstatclient/pStatThread.I | 15 +- panda/src/pstatclient/pStatThread.h | 4 +- panda/src/pstatclient/pStatTimer.h | 4 +- 43 files changed, 1624 insertions(+), 484 deletions(-) create mode 100644 panda/src/display/pStatGPUTimer.I create mode 100644 panda/src/display/pStatGPUTimer.h create mode 100644 panda/src/glstuff/glLatencyQueryContext_src.I create mode 100644 panda/src/glstuff/glLatencyQueryContext_src.cxx create mode 100644 panda/src/glstuff/glLatencyQueryContext_src.h create mode 100644 panda/src/glstuff/glTimerQueryContext_src.I create mode 100644 panda/src/glstuff/glTimerQueryContext_src.cxx create mode 100644 panda/src/glstuff/glTimerQueryContext_src.h create mode 100644 panda/src/gobj/timerQueryContext.I create mode 100644 panda/src/gobj/timerQueryContext.cxx create mode 100644 panda/src/gobj/timerQueryContext.h diff --git a/panda/src/display/Sources.pp b/panda/src/display/Sources.pp index b2f093957a..dfad1153c1 100644 --- a/panda/src/display/Sources.pp +++ b/panda/src/display/Sources.pp @@ -38,6 +38,7 @@ lru.h \ nativeWindowHandle.I nativeWindowHandle.h \ parasiteBuffer.I parasiteBuffer.h \ + pStatGPUTimer.I pStatGPUTimer.h \ windowHandle.I windowHandle.h \ windowProperties.I windowProperties.h \ renderBuffer.h \ @@ -112,6 +113,7 @@ lru.h \ nativeWindowHandle.I nativeWindowHandle.h \ parasiteBuffer.I parasiteBuffer.h \ + pStatGPUTimer.I pStatGPUTimer.h \ windowHandle.I windowHandle.h \ windowProperties.I windowProperties.h \ renderBuffer.h \ diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index ce2641dd5b..e57c559525 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -24,6 +24,7 @@ #include "cullTraverser.h" #include "clockObject.h" #include "pStatTimer.h" +#include "pStatGPUTimer.h" #include "pStatClient.h" #include "pStatCollector.h" #include "mutexHolder.h" @@ -51,7 +52,7 @@ #define WINDOWS_LEAN_AND_MEAN #include #include - #undef WINDOWS_LEAN_AND_MEAN + #undef WINDOWS_LEAN_AND_MEAN #else #include #endif @@ -139,7 +140,7 @@ GraphicsEngine(Pipeline *pipeline) : _windows_sorted = true; _window_sort_index = 0; _needs_open_windows = false; - + set_threading_model(GraphicsThreadingModel(threading_model)); if (!_threading_model.is_default()) { display_cat.info() @@ -187,7 +188,7 @@ set_threading_model(const GraphicsThreadingModel &threading_model) { return; } } - + #ifndef THREADED_PIPELINE if (!threading_model.is_single_threaded()) { display_cat.warning() @@ -237,7 +238,7 @@ get_threading_model() const { // // If a null pointer is supplied for the gsg, then this // routine will create a new gsg. -// +// // This routine is only called from the app thread. //////////////////////////////////////////////////////////////////// @@ -282,7 +283,7 @@ make_output(GraphicsPipe *pipe, // already-initialized gsg. // Simplify the input parameters. - + int x_size = 0, y_size = 0; if (win_prop.has_size()) { x_size = win_prop.get_x_size(); @@ -358,7 +359,7 @@ make_output(GraphicsPipe *pipe, // an unencumbered GSG. return NULL; } - + // Determine if a parasite buffer meets the user's specs. bool can_use_parasite = false; @@ -380,7 +381,7 @@ make_output(GraphicsPipe *pipe, // Even if prefer-parasite-buffer is set, parasites are not preferred // if the host window is too small, or if the host window does not // have the requested properties. - + if ((prefer_parasite_buffer) && (can_use_parasite) && (x_size <= host->get_x_size())&& @@ -407,10 +408,10 @@ make_output(GraphicsPipe *pipe, } // Ask the pipe to create a window. - + for (int retry=0; retry<10; retry++) { bool precertify = false; - PT(GraphicsOutput) window = + PT(GraphicsOutput) window = pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify); if (window != (GraphicsOutput *)NULL) { window->_sort = sort; @@ -454,11 +455,11 @@ make_output(GraphicsPipe *pipe, nassertr(removed, NULL); } } - + // Parasite buffers were not preferred, but the pipe could not // create a window to the user's specs. Try a parasite as a // last hope. - + if (can_use_parasite) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; @@ -612,7 +613,7 @@ remove_all_windows() { // Access: Published // Description: Resets the framebuffer of the current window. This // is currently used by DirectX 8 only. It calls a -// reset_window function on each active window to +// reset_window function on each active window to // release/create old/new framebuffer //////////////////////////////////////////////////////////////////// void GraphicsEngine:: @@ -711,11 +712,11 @@ render_frame() { if (!_windows_sorted) { do_resort_windows(); } - + if (sync_flip && _flip_state != FS_flip) { do_flip_frame(current_thread); } - + // Are any of the windows ready to be deleted? Windows new_windows; new_windows.reserve(_windows.size()); @@ -725,10 +726,10 @@ render_frame() { nassertv(win != NULL); if (win->get_delete_flag()) { do_remove_window(win, current_thread); - + } else { new_windows.push_back(win); - + // Let's calculate each scene's bounding volume here in App, // before we cycle the pipeline. The cull traversal will // calculate it anyway, but if we calculate it in App first @@ -769,11 +770,11 @@ render_frame() { } _loaded_textures.clear(); } - + // Now it's time to do any drawing from the main frame--after all of // the App code has executed, but before we begin the next frame. _app.do_frame(this, current_thread); - + // Grab each thread's mutex again after all windows have flipped, // and wait for the thread to finish. { @@ -782,32 +783,32 @@ render_frame() { for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; thread->_cv_mutex.acquire(); - + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } } } - + #if defined(THREADED_PIPELINE) && defined(DO_PSTATS) _cyclers_pcollector.set_level(_pipeline->get_num_cyclers()); _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers()); - + #ifdef DEBUG_THREADS if (PStatClient::is_connected()) { _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this); _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this); } #endif // DEBUG_THREADS - + #endif // THREADED_PIPELINE && DO_PSTATS - + GeomCacheManager::flush_level(); CullTraverser::flush_level(); RenderState::flush_level(); TransformState::flush_level(); CullableObject::flush_level(); - + // Now cycle the pipeline and officially begin the next frame. #ifdef THREADED_PIPELINE { @@ -815,15 +816,15 @@ render_frame() { _pipeline->cycle(); } #endif // THREADED_PIPELINE - + global_clock->tick(current_thread); if (global_clock->check_errors(current_thread)) { throw_event("clock_error"); } - + #ifdef DO_PSTATS PStatClient::main_tick(); - + // Reset our pcollectors that track data across the frame. CullTraverser::_nodes_pcollector.clear_level(); CullTraverser::_geom_nodes_pcollector.clear_level(); @@ -832,18 +833,18 @@ render_frame() { GeomCacheManager::_geom_cache_record_pcollector.clear_level(); GeomCacheManager::_geom_cache_erase_pcollector.clear_level(); GeomCacheManager::_geom_cache_evict_pcollector.clear_level(); - + GraphicsStateGuardian::init_frame_pstats(); - + _transform_states_pcollector.set_level(TransformState::get_num_states()); _render_states_pcollector.set_level(RenderState::get_num_states()); if (pstats_unused_states) { _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states()); _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states()); } - + _sw_sprites_pcollector.clear_level(); - + _cnode_volume_pcollector.clear_level(); _gnode_volume_pcollector.clear_level(); _geom_volume_pcollector.clear_level(); @@ -868,18 +869,18 @@ render_frame() { _occlusion_passed_pcollector.clear_level(); _occlusion_failed_pcollector.clear_level(); _occlusion_tests_pcollector.clear_level(); - + if (PStatClient::is_connected()) { size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size(); size_t independent = GeomVertexArrayData::get_independent_lru()->get_total_size(); size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size(); size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size(); size_t pending = VertexDataPage::get_pending_lru()->get_total_size(); - + VertexDataSaveFile *save_file = VertexDataPage::get_save_file(); size_t total_disk = save_file->get_total_file_size(); size_t used_disk = save_file->get_used_file_size(); - + _vertex_data_small_pcollector.set_level(small_buf); _vertex_data_independent_pcollector.set_level(independent); _vertex_data_pending_pcollector.set_level(pending); @@ -888,11 +889,11 @@ render_frame() { _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk); _vertex_data_used_disk_pcollector.set_level(used_disk); } - + #endif // DO_PSTATS - + GeomVertexArrayData::lru_epoch(); - + // Now signal all of our threads to begin their next frame. Threads::const_iterator ti; for (ti = _threads.begin(); ti != _threads.end(); ++ti) { @@ -903,24 +904,24 @@ render_frame() { } thread->_cv_mutex.release(); } - + // Some threads may still be drawing, so indicate that we have to // wait for those threads before we can flip. _flip_state = _auto_flip ? FS_flip : FS_draw; } // Now the lock is released. - + if (yield_timeslice) { // Nap for a moment to yield the timeslice, to be polite to other // running applications. PStatTimer timer(_yield_pcollector, current_thread); Thread::force_yield(); - } else if (!Thread::is_true_threads()) { + } else if (!Thread::is_true_threads()) { PStatTimer timer(_yield_pcollector, current_thread); Thread::consider_yield(); } - + // Anything that happens outside of GraphicsEngine::render_frame() // is deemed to be App. _app_pcollector.start(); @@ -960,11 +961,11 @@ open_windows() { for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; thread->_cv_mutex.acquire(); - + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } - + thread->_thread_state = TS_do_windows; thread->_cv_start.notify(); thread->_cv_mutex.release(); @@ -1003,7 +1004,7 @@ 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 +// 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 @@ -1081,7 +1082,7 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; MutexHolder holder2(thread->_cv_mutex); - + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1130,7 +1131,7 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; MutexHolder holder2(thread->_cv_mutex); - + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1148,7 +1149,7 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::get_global_ptr // Access: Published, Static -// Description: +// Description: //////////////////////////////////////////////////////////////////// GraphicsEngine *GraphicsEngine:: get_global_ptr() { @@ -1216,7 +1217,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy()); NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread); - CPT(TransformState) cull_center_transform = + CPT(TransformState) cull_center_transform = scene_setup->get_cull_center().get_transform(scene_parent, current_thread); local_frustum->xform(cull_center_transform->get_mat()); @@ -1337,7 +1338,7 @@ cull_and_draw_together(const GraphicsEngine::Windows &wlist, if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { win->clear(current_thread); - + int num_display_regions = win->get_num_active_display_regions(); for (int i = 0; i < num_display_regions; i++) { DisplayRegion *dr = win->get_active_display_region(i); @@ -1376,7 +1377,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, GraphicsStateGuardian *gsg = win->get_gsg(); nassertv(gsg != (GraphicsStateGuardian *)NULL); - DisplayRegionPipelineReader *dr_reader = + DisplayRegionPipelineReader *dr_reader = new DisplayRegionPipelineReader(dr, current_thread); win->change_scenes(dr_reader); @@ -1409,9 +1410,9 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr, // Issue the cull callback on this DisplayRegion. DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup); cbobj->do_callback(&cbdata); - + // The callback has taken care of the culling. - + } else { // Perform the cull normally. dr->do_cull(&cull_handler, scene_setup, gsg, current_thread); @@ -1455,7 +1456,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { for (int i = 0; i < num_display_regions; ++i) { DisplayRegion *dr = win->get_active_display_region(i); if (dr != (DisplayRegion *)NULL) { - DisplayRegionPipelineReader *dr_reader = + DisplayRegionPipelineReader *dr_reader = new DisplayRegionPipelineReader(dr, current_thread); NodePath camera = dr_reader->get_camera(); AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(camera, (DisplayRegion *)NULL)).first; @@ -1466,7 +1467,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { dr_reader = NULL; (*aci).second = dr; cull_to_bins(win, dr, current_thread); - + } else { // We have already culled a scene using this camera in // this thread, and now we're being asked to cull another @@ -1480,7 +1481,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { setup_scene(win->get_gsg(), dr_reader), current_thread); } - + if (dr_reader != (DisplayRegionPipelineReader *)NULL) { delete dr_reader; } @@ -1538,7 +1539,7 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { PStatTimer timer(_cull_sort_pcollector, current_thread); cull_result->finish_cull(scene_setup, current_thread); } - + // Save the results for next frame. dr->set_cull_result(cull_result, scene_setup, current_thread); } @@ -1559,53 +1560,73 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { size_t wlist_size = wlist.size(); for (size_t wi = 0; wi < wlist_size; ++wi) { GraphicsOutput *win = wlist[wi]; + if (win->is_active()) { - if (win->flip_ready()) { + GraphicsStateGuardian *gsg = win->get_gsg(); + + GraphicsOutput *host = win->get_host(); + if (host->flip_ready()) { { + // We can't use a PStatGPUTimer before begin_frame, so when using GPU + // timing, it is advisable to set auto-flip to #t. PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); - win->begin_flip(); + host->begin_flip(); } { PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); - win->end_flip(); + host->end_flip(); } } - PStatTimer timer(win->get_draw_window_pcollector(), current_thread); if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { - win->clear(current_thread); + // We have to place this collector inside begin_frame, because + // we need a current context for PStatGPUTimer to work. + { + PStatGPUTimer timer(win->get_gsg(), win->get_draw_window_pcollector(), current_thread); + win->clear(current_thread); - if (display_cat.is_spam()) { - display_cat.spam() - << "Drawing window " << win->get_name() << "\n"; - } - int num_display_regions = win->get_num_active_display_regions(); - for (int i = 0; i < num_display_regions; ++i) { - DisplayRegion *dr = win->get_active_display_region(i); - if (dr != (DisplayRegion *)NULL) { - draw_bins(win, dr, current_thread); + if (display_cat.is_spam()) { + display_cat.spam() + << "Drawing window " << win->get_name() << "\n"; + } + int num_display_regions = win->get_num_active_display_regions(); + for (int i = 0; i < num_display_regions; ++i) { + DisplayRegion *dr = win->get_active_display_region(i); + if (dr != (DisplayRegion *)NULL) { + draw_bins(win, dr, current_thread); + } } } win->end_frame(GraphicsOutput::FM_render, current_thread); if (_auto_flip) { +#ifdef DO_PSTATS + // This is a good time to perform a latency query. + if (win->get_gsg()->get_timer_queries_active()) { + win->get_gsg()->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index()); + } +#endif + if (win->flip_ready()) { { + // begin_flip doesn't do anything interesting, let's not waste two timer queries on that. PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); win->begin_flip(); } { - PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); + PStatGPUTimer timer(win->get_gsg(), GraphicsEngine::_flip_end_pcollector, current_thread); win->end_flip(); } } } + } else { if (display_cat.is_spam()) { display_cat.spam() << "Not drawing window " << win->get_name() << "\n"; } } + } else { if (display_cat.is_spam()) { display_cat.spam() @@ -1722,8 +1743,6 @@ ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) } } - - //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::do_sync_frame // Access: Private @@ -1751,7 +1770,6 @@ do_sync_frame(Thread *current_thread) { _flip_state = FS_sync; } - //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::do_ready_flip // Access: Private @@ -1777,8 +1795,6 @@ do_ready_flip(Thread *current_thread) { } _app.do_ready_flip(this,current_thread); _flip_state = FS_sync; - - } //////////////////////////////////////////////////////////////////// @@ -1811,7 +1827,7 @@ do_flip_frame(Thread *current_thread) { } } } - + // Now signal all of our threads to flip the windows. _app.do_flip(this, current_thread); @@ -1895,7 +1911,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { // There must be a singular transform over the scene. if (!_singular_warning_last_frame) { display_cat.warning() - << "Scene " << scene_root << " has net scale (" + << "Scene " << scene_root << " has net scale (" << scene_root.get_scale(NodePath()) << "); cannot render.\n"; _singular_warning_this_frame = true; } @@ -1906,7 +1922,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { // There must be a singular transform over the camera. if (!_singular_warning_last_frame) { display_cat.warning() - << "Camera " << camera << " has net scale (" + << "Camera " << camera << " has net scale (" << camera.get_scale(NodePath()) << "); cannot render.\n"; } _singular_warning_this_frame = true; @@ -1950,11 +1966,12 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) { void GraphicsEngine:: do_draw(CullResult *cull_result, SceneSetup *scene_setup, GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) { - // Statistics - PStatTimer timer(dr->get_draw_region_pcollector(), current_thread); - GraphicsStateGuardian *gsg = win->get_gsg(); CallbackObject *cbobj; + GraphicsStateGuardian *gsg = win->get_gsg(); + + // Statistics + PStatGPUTimer timer(gsg, dr->get_draw_region_pcollector(), current_thread); { DisplayRegionPipelineReader dr_reader(dr, current_thread); @@ -2033,20 +2050,20 @@ do_add_window(GraphicsOutput *window, _windows_sorted = false; _windows.push_back(window); - WindowRenderer *cull = + WindowRenderer *cull = get_window_renderer(threading_model.get_cull_name(), threading_model.get_cull_stage()); - WindowRenderer *draw = + WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name(), threading_model.get_draw_stage()); - + if (threading_model.get_cull_sorting()) { cull->add_window(cull->_cull, window); draw->add_window(draw->_draw, window); } else { cull->add_window(cull->_cdraw, window); } - + // Ask the pipe which thread it prefers to run its windowing // commands in (the "window thread"). This is the thread that // handles the commands to open, resize, etc. the window. X @@ -2056,7 +2073,7 @@ do_add_window(GraphicsOutput *window, // has been bound in a given thread, it cannot subsequently be bound // in any other thread, and we have to bind a context in // open_window()). - + switch (window->get_pipe()->get_preferred_window_thread()) { case GraphicsPipe::PWT_app: _app.add_window(_app._window, window); @@ -2095,8 +2112,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, } auto_adjust_capabilities(gsg); - - WindowRenderer *draw = + + WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name(), threading_model.get_draw_stage()); @@ -2227,7 +2244,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { << "textures_power_2 to 'up' or 'down'.\n"; textures_power_2 = ATS_down; // Not a fix. Just suppresses further error messages. } - + if (textures_auto_power_2 && !Texture::has_textures_power_2()) { if (gsg->get_supports_tex_non_pow2()) { Texture::set_textures_power_2(ATS_none); @@ -2235,13 +2252,13 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { Texture::set_textures_power_2(textures_power_2); } } - - if ((Texture::get_textures_power_2() == ATS_none) && + + if ((Texture::get_textures_power_2() == ATS_none) && (!gsg->get_supports_tex_non_pow2())) { - + // Overaggressive configuration detected - - display_cat.error() + + display_cat.error() << "The 'textures_power_2' configuration is set to 'none', meaning \n" << "that non-power-of-two texture support is required, but the video \n" << "driver I'm trying to use does not support non-power-of-two textures.\n"; @@ -2250,7 +2267,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { display_cat.error() << "The 'none' did not come from the config file. In other words,\n" << "the variable 'textures_power_2' was altered procedurally.\n"; - + if (textures_auto_power_2) { display_cat.error() << "It is possible that it was set by panda's automatic mechanisms,\n" @@ -2263,7 +2280,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { } } } - + if (shader_auto_utilization && (shader_utilization != SUT_none)) { display_cat.error() << "Invalid panda config file: if you set the config-variable\n" @@ -2271,7 +2288,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { << "shader_utilization to 'none'.\n"; shader_utilization = SUT_none; // Not a fix. Just suppresses further error messages. } - + if (shader_auto_utilization && !Shader::have_shader_utilization()) { if (gsg->get_supports_basic_shaders()) { Shader::set_shader_utilization(SUT_basic); @@ -2279,13 +2296,13 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { Shader::set_shader_utilization(SUT_none); } } - - if ((Shader::get_shader_utilization() != SUT_none) && + + if ((Shader::get_shader_utilization() != SUT_none) && (!gsg->get_supports_basic_shaders())) { - + // Overaggressive configuration detected - - display_cat.error() + + display_cat.error() << "The 'shader_utilization' config variable is set, meaning\n" << "that panda may try to generate shaders. However, the video \n" << "driver I'm trying to use does not support shaders.\n"; @@ -2294,7 +2311,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) { display_cat.error() << "The 'shader_utilization' setting did not come from the config\n" << "file. In other words, it was altered procedurally.\n"; - + if (shader_auto_utilization) { display_cat.error() << "It is possible that it was set by panda's automatic mechanisms,\n" @@ -2322,7 +2339,7 @@ terminate_threads(Thread *current_thread) { // We spend almost our entire time in this method just waiting for // threads. Time it appropriately. PStatTimer timer(_wait_pcollector, current_thread); - + // First, wait for all the threads to finish their current frame. // Grabbing the mutex should achieve that. Threads::const_iterator ti; @@ -2330,7 +2347,7 @@ terminate_threads(Thread *current_thread) { RenderThread *thread = (*ti).second; thread->_cv_mutex.acquire(); } - + // Now tell them to close their windows and terminate. for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; @@ -2344,7 +2361,7 @@ terminate_threads(Thread *current_thread) { RenderThread *thread = (*ti).second; thread->join(); } - + _threads.clear(); } @@ -2450,7 +2467,7 @@ get_window_renderer(const string &name, int pipeline_stage) { //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::WindowRenderer::Constructor // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// GraphicsEngine::WindowRenderer:: WindowRenderer(const string &name) : @@ -2588,7 +2605,7 @@ do_frame(GraphicsEngine *engine, Thread *current_thread) { // This one has no outstanding pointers; clean it up. GraphicsPipe *pipe = gsg->get_pipe(); engine->close_gsg(pipe, gsg); - } else { + } else { // This one is ok; preserve it. new_gsgs.insert(gsg); } @@ -2667,12 +2684,12 @@ do_close(GraphicsEngine *engine, Thread *current_thread) { // This one has no outstanding pointers; clean it up. GraphicsPipe *pipe = gsg->get_pipe(); engine->close_gsg(pipe, gsg); - } else { + } else { // This one is ok; preserve it. new_gsgs.insert(gsg); } } - + _gsgs.swap(new_gsgs); } @@ -2728,10 +2745,10 @@ any_done_gsgs() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::RenderThread::Constructor // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// GraphicsEngine::RenderThread:: -RenderThread(const string &name, GraphicsEngine *engine) : +RenderThread(const string &name, GraphicsEngine *engine) : Thread(name, "Main"), WindowRenderer(name), _engine(engine), diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 900e349254..6b6f5fe370 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -1,6 +1,6 @@ // Filename: graphicsStateGuardian.I // Created by: drose (24Sep99) -// Updated by: fperazzi, PandaSE (29Apr10) (added +// Updated by: fperazzi, PandaSE (29Apr10) (added // get_max_2d_texture_array_layers and related) // //////////////////////////////////////////////////////////////////// @@ -334,10 +334,10 @@ get_max_3d_texture_dimension() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_max_2d_texture_array_layers // Access: Published -// Description: Returns the largest possible number of pages, or -1 +// Description: Returns the largest possible number of pages, or -1 // if there is no particular limit. Returns 0 if 2-d // texture arrays not supported. -// +// // The value returned may not be meaningful until after // the graphics context has been fully created (e.g. the // window has been opened). @@ -713,6 +713,45 @@ get_supports_geometry_instancing() const { return _supports_geometry_instancing; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_supports_occlusion_query +// Access: Published +// Description: Returns true if this GSG supports an occlusion query. +// If this is true, then begin_occlusion_query() and +// end_occlusion_query() may be called to bracket a +// sequence of draw_triangles() (or whatever) calls to +// measure pixels that pass the depth test. +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +get_supports_occlusion_query() const { + return _supports_occlusion_query; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_supports_timer_query +// Access: Published +// Description: Returns true if this GSG supports a timer query. +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +get_supports_timer_query() const { + return _supports_timer_query; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_timer_queries_active +// Access: Published +// Description: Returns true if timer queries are currently +// enabled on this GSG. +//////////////////////////////////////////////////////////////////// +bool GraphicsStateGuardian:: +get_timer_queries_active() const { +#ifdef DO_PSTATS + return _timer_queries_active; +#else + return false; +#endif +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_max_color_targets // Access: Published diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 1cbd3f49c6..2a76922425 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -28,6 +28,7 @@ #include "throw_event.h" #include "clockObject.h" #include "pStatTimer.h" +#include "pStatGPUTimer.h" #include "geomTristrips.h" #include "geomTrifans.h" #include "geomLinestrips.h" @@ -55,6 +56,7 @@ #include "colorScaleAttrib.h" #include "clipPlaneAttrib.h" #include "fogAttrib.h" +#include "config_pstats.h" #include #include @@ -87,9 +89,19 @@ PStatCollector GraphicsStateGuardian::_draw_primitive_pcollector("Draw:Primitive PStatCollector GraphicsStateGuardian::_draw_set_state_pcollector("Draw:Set State"); PStatCollector GraphicsStateGuardian::_clear_pcollector("Draw:Clear"); PStatCollector GraphicsStateGuardian::_flush_pcollector("Draw:Flush"); +PStatCollector GraphicsStateGuardian::_compute_dispatch_pcollector("Draw:Compute dispatch"); PStatCollector GraphicsStateGuardian::_wait_occlusion_pcollector("Wait:Occlusion"); +PStatCollector GraphicsStateGuardian::_wait_timer_pcollector("Wait:Timer Queries"); +PStatCollector GraphicsStateGuardian::_timer_queries_pcollector("Timer queries"); +PStatCollector GraphicsStateGuardian::_command_latency_pcollector("Command latency"); +PStatCollector GraphicsStateGuardian::_prepare_pcollector("Draw:Prepare"); +PStatCollector GraphicsStateGuardian::_prepare_texture_pcollector("Draw:Prepare:Texture"); +PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geom"); +PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader"); +PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer"); +PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer"); PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform"); PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test"); @@ -115,7 +127,6 @@ PStatCollector GraphicsStateGuardian::_draw_set_state_stencil_pcollector("Draw:S PStatCollector GraphicsStateGuardian::_draw_set_state_fog_pcollector("Draw:Set State:Fog"); PStatCollector GraphicsStateGuardian::_draw_set_state_scissor_pcollector("Draw:Set State:Scissor"); - PT(TextureStage) GraphicsStateGuardian::_alpha_scale_texture_stage = NULL; TypeHandle GraphicsStateGuardian::_type_handle; @@ -197,6 +208,16 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, _max_vertex_transform_indices = 0; _supports_occlusion_query = false; + _supports_timer_query = false; + +#ifdef DO_PSTATS + _timer_queries_active = false; + _last_query_frame = 0; + _last_num_queried = 0; + //_timer_delta = 0.0; + + _pstats_gpu_thread = -1; +#endif // Initially, we set this to false; a GSG that knows it has this // property should set it to true. @@ -261,7 +282,7 @@ GraphicsStateGuardian:: delete _stencil_render_states; _stencil_render_states = 0; } - + if (_shader_generator) { delete _shader_generator; _shader_generator = 0; @@ -322,7 +343,7 @@ get_supported_geom_rendering() const { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_supports_cg_profile // Access: Published, Virtual -// Description: Returns true if this particular GSG supports the +// Description: Returns true if this particular GSG supports the // specified Cg Shader Profile. //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: @@ -405,7 +426,7 @@ get_prepared_objects() { //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: set_gamma(PN_stdfloat gamma) { - _gamma = gamma; + _gamma = gamma; return false; } @@ -437,7 +458,7 @@ restore_gamma() { // function returns false. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: -traverse_prepared_textures(GraphicsStateGuardian::TextureCallback *func, +traverse_prepared_textures(GraphicsStateGuardian::TextureCallback *func, void *callback_arg) { ReMutexHolder holder(_prepared_objects->_lock); PreparedGraphicsObjects::Textures::const_iterator ti; @@ -702,20 +723,6 @@ void GraphicsStateGuardian:: release_index_buffer(IndexBufferContext *) { } -//////////////////////////////////////////////////////////////////// -// Function: GraphicsStateGuardian::get_supports_occlusion_query -// Access: Public, Virtual -// Description: Returns true if this GSG supports an occlusion query. -// If this is true, then begin_occlusion_query() and -// end_occlusion_query() may be called to bracket a -// sequence of draw_triangles() (or whatever) calls to -// measure pixels that pass the depth test. -//////////////////////////////////////////////////////////////////// -bool GraphicsStateGuardian:: -get_supports_occlusion_query() const { - return _supports_occlusion_query; -} - //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::begin_occlusion_query // Access: Public, Virtual @@ -754,6 +761,17 @@ end_occlusion_query() { return result; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::issue_timer_query +// Access: Public, Virtual +// Description: Adds a timer query to the command stream, associated +// with the given PStats collector index. +//////////////////////////////////////////////////////////////////// +PT(TimerQueryContext) GraphicsStateGuardian:: +issue_timer_query(int pstats_index) { + return NULL; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::dispatch_compute // Access: Public, Virtual @@ -888,7 +906,7 @@ compute_distance_to(const LPoint3 &point) const { // the need for a separate routine to fetch these values. // // The "altered" bits indicate what parts of the -// state_and_transform have changed since the last +// state_and_transform have changed since the last // time this particular ShaderMatSpec was evaluated. // This may allow data to be cached and not reevaluated. // @@ -896,7 +914,7 @@ compute_distance_to(const LPoint3 &point) const { const LMatrix4 *GraphicsStateGuardian:: fetch_specified_value(Shader::ShaderMatSpec &spec, int altered) { LVecBase3 v; - + if (altered & spec._dep[0]) { const LMatrix4 *t = fetch_specified_part(spec._part[0], spec._arg[0], spec._cache[0]); if (t != &spec._cache[0]) { @@ -909,7 +927,7 @@ fetch_specified_value(Shader::ShaderMatSpec &spec, int altered) { spec._cache[1] = *t; } } - + switch(spec._func) { case Shader::SMF_compose: spec._value.multiply(spec._cache[0], spec._cache[1]); @@ -1470,6 +1488,29 @@ begin_frame(Thread *current_thread) { _state_rs = RenderState::make_empty(); _state_mask.clear(); +#ifdef DO_PSTATS + // We have to do this here instead of in GraphicsEngine because + // we need a current context to issue timer queries. + int frame = ClockObject::get_global_clock()->get_frame_count(); + if (_last_query_frame < frame) { + _last_query_frame = frame; + _timer_queries_pcollector.clear_level(); + + // Now is a good time to flush previous frame's queries. We + // may not actually have all of the previous frame's results + // in yet, but that's okay; the GPU data is allowed to lag a + // few frames behind. + flush_timer_queries(); + + if (_timer_queries_active) { + // Issue a stop and start event for collector 0, marking the + // beginning of the new frame. + issue_timer_query(0x8000); + issue_timer_query(0x0000); + } + } +#endif + return !_needs_reset; } @@ -1566,6 +1607,138 @@ end_frame(Thread *current_thread) { _prepared_objects->_graphics_memory_lru.begin_epoch(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::flush_timer_queries +// Access: Public +// Description: Called by the graphics engine on the draw thread +// to check the status of the running timer queries +// and submit their results to the PStats server. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +flush_timer_queries() { +#ifdef DO_PSTATS + // This uses the lower-level PStats interfaces for now because + // of all the unnecessary overhead that would otherwise be incurred + // when adding such a large amount of data at once. + + PStatClient *client = PStatClient::get_global_pstats(); + + if (!client->client_is_connected()) { + _timer_queries_active = false; + return; + } + + if (!_timer_queries_active) { + if (pstats_gpu_timing && _supports_timer_query) { + // Check if timer queries should be enabled. + _timer_queries_active = true; + } else { + return; + } + } + + // Currently, we use one thread per GSG, for convenience. In the + // future, we may want to try and use one thread per graphics card. + if (_pstats_gpu_thread == -1) { + _pstats_gpu_thread = client->make_gpu_thread(get_driver_renderer()).get_index(); + } + PStatThread gpu_thread(client, _pstats_gpu_thread); + + // Get the results of all the timer queries. + int first = 0; + if (!_pending_timer_queries.empty()) { + int count = _pending_timer_queries.size(); + if (count == 0) { + return; + } + + PStatGPUTimer timer(this, _wait_timer_pcollector); + + if (_last_num_queried > 0) { + // We know how many queries were available last frame, and this + // usually stays fairly constant, so use this as a starting point. + int i = min(_last_num_queried, count) - 1; + + if (_pending_timer_queries[i]->is_answer_ready()) { + first = count; + while (i < count) { + if (!_pending_timer_queries[++i]->is_answer_ready()) { + first = i; + break; + } + } + } else { + first = 0; + while (i > 0) { + if (_pending_timer_queries[--i]->is_answer_ready()) { + first = i + 1; + break; + } + } + } + } else { + // We figure out which tasks the GPU has already finished by doing + // a binary search for the first query that does not have an answer + // ready. We know then that everything before that must be ready. + while (count > 0) { + int step = count / 2; + int i = first + step; + if (_pending_timer_queries[i]->is_answer_ready()) { + first += step + 1; + count -= step + 1; + } else { + count = step; + } + } + } + + if (first <= 0) { + return; + } + + _last_num_queried = first; + + int frame_index = ClockObject::get_global_clock()->get_frame_count(); + + for (int i = 0; i < first; ++i) { + CPT(TimerQueryContext) query = _pending_timer_queries[i]; + + double time_data = query->get_timestamp(); // + _timer_delta; + + if (query->_pstats_index == _command_latency_pcollector.get_index()) { + // Special case for the latency pcollector. + PStatCollectorDef *cdef; + cdef = client->get_collector_ptr(query->_pstats_index)->get_def(client, query->_pstats_index); + _pstats_gpu_data.add_level(query->_pstats_index, time_data * cdef->_factor); + + } else if (query->_pstats_index & 0x8000) { + _pstats_gpu_data.add_stop(query->_pstats_index & 0x7fff, time_data); + + } else { + _pstats_gpu_data.add_start(query->_pstats_index & 0x7fff, time_data); + } + + // We found an end-frame marker (a stop event for collector 0). + // This means that the GPU actually caught up with that frame, + // and we can flush the GPU thread's frame data to the pstats server. + if (query->_pstats_index == 0x8000) { + gpu_thread.add_frame(_pstats_gpu_data); + _pstats_gpu_data.clear(); + } + } + } + + if (first > 0) { + // Do this out of the scope of _wait_timer_pcollector. + _pending_timer_queries.erase( + _pending_timer_queries.begin(), + _pending_timer_queries.begin() + first + ); + _timer_queries_pcollector.add_level_now(first); + } +#endif +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::depth_offset_decals // Access: Public, Virtual @@ -1819,7 +1992,7 @@ reset() { delete _stencil_render_states; _stencil_render_states = 0; } - _stencil_render_states = new StencilRenderStates (this); + _stencil_render_states = new StencilRenderStates(this); } //////////////////////////////////////////////////////////////////// @@ -1935,12 +2108,12 @@ do_issue_clip_plane() { enable_clip_planes(true); _clip_planes_enabled = true; } - + enable_clip_plane(num_enabled, true); if (num_enabled == 0) { begin_bind_clip_planes(); } - + bind_clip_plane(plane, num_enabled); num_enabled++; } @@ -2123,7 +2296,7 @@ do_issue_light() { if (num_enabled == 0) { begin_bind_lights(); } - + light_obj->bind(this, light, num_enabled); num_enabled++; } @@ -2278,24 +2451,24 @@ create_gamma_table (PN_stdfloat gamma, unsigned short *red_table, unsigned short // avoid divide by zero and negative exponents gamma = 1.0; } - + for (i = 0; i < 256; i++) { double g; double x; PN_stdfloat gamma_correction; - + x = ((double) i / 255.0); - gamma_correction = 1.0 / gamma; + gamma_correction = 1.0 / gamma; x = pow (x, (double) gamma_correction); if (x > 1.00) { x = 1.0; } - g = x * 65535.0; + g = x * 65535.0; red_table [i] = (int)g; green_table [i] = (int)g; blue_table [i] = (int)g; - } + } } //////////////////////////////////////////////////////////////////// @@ -2638,7 +2811,7 @@ async_reload_texture(TextureContext *tc) { string task_name = string("reload:") + tc->get_texture()->get_name(); PT(AsyncTaskManager) task_mgr = _loader->get_task_manager(); - + // See if we are already loading this task. AsyncTaskCollection orig_tasks = task_mgr->find_tasks(task_name); int num_tasks = orig_tasks.get_num_tasks(); @@ -2655,7 +2828,7 @@ async_reload_texture(TextureContext *tc) { // This texture has not yet been queued to be reloaded. Queue it up // now. - PT(AsyncTask) request = + PT(AsyncTask) request = new TextureReloadRequest(task_name, _prepared_objects, tc->get_texture(), _supports_compressed_texture); @@ -2762,20 +2935,20 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_driver_vendor // Access: Public, Virtual -// Description: Returns the vendor of the video card driver +// Description: Returns the vendor of the video card driver //////////////////////////////////////////////////////////////////// string GraphicsStateGuardian:: get_driver_vendor() { - return string("0"); + return string(); } //////////////////////////////////////////////////////////////////// -// Function: GraphicsStateGuardian::get_driver_vendor +// Function: GraphicsStateGuardian::get_driver_renderer // Access: Public, Virtual // Description: Returns GL_Renderer //////////////////////////////////////////////////////////////////// string GraphicsStateGuardian::get_driver_renderer() { - return string("0"); + return string(); } //////////////////////////////////////////////////////////////////// @@ -2783,12 +2956,12 @@ string GraphicsStateGuardian::get_driver_renderer() { // Access: Public, Virtual // Description: Returns driver version // This has an implementation-defined meaning, and may -// be "0" if the particular graphics implementation +// be "" if the particular graphics implementation // does not provide a way to query this information. //////////////////////////////////////////////////////////////////// string GraphicsStateGuardian:: get_driver_version() { - return string("0"); + return string(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 996d5cfc2e..883a57c0c2 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -44,6 +44,7 @@ #include "bitMask.h" #include "texture.h" #include "occlusionQueryContext.h" +#include "timerQueryContext.h" #include "stencilRenderStates.h" #include "loader.h" #include "shaderAttrib.h" @@ -151,6 +152,10 @@ PUBLISHED: INLINE bool get_supports_two_sided_stencil() const; INLINE bool get_supports_geometry_instancing() const; + INLINE bool get_supports_occlusion_query() const; + INLINE bool get_supports_timer_query() const; + INLINE bool get_timer_queries_active() const; + INLINE int get_max_color_targets() const; INLINE int get_maximum_simultaneous_render_targets() const; @@ -200,7 +205,7 @@ PUBLISHED: virtual int get_driver_version_minor(); virtual int get_driver_shader_version_major(); virtual int get_driver_shader_version_minor(); - + bool set_scene(SceneSetup *scene_setup); virtual SceneSetup *get_scene() const; @@ -222,10 +227,11 @@ public: virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data); virtual void release_index_buffer(IndexBufferContext *ibc); - virtual bool get_supports_occlusion_query() const; virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); + virtual PT(TimerQueryContext) issue_timer_query(int pstats_index); + virtual void dispatch_compute(int size_x, int size_y, int size_z); virtual PT(GeomMunger) get_geom_munger(const RenderState *state, @@ -239,7 +245,7 @@ public: virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const; virtual void clear(DrawableRegion *clearable); - + const LMatrix4 *fetch_specified_value(Shader::ShaderMatSpec &spec, int altered); const LMatrix4 *fetch_specified_part(Shader::ShaderMatInput input, InternalName *name, LMatrix4 &t); const Shader::ShaderPtrData *fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec); @@ -260,6 +266,8 @@ PUBLISHED: public: virtual void end_frame(Thread *current_thread); + void flush_timer_queries(); + void set_current_properties(const FrameBufferProperties *properties); virtual bool depth_offset_decals(); @@ -375,7 +383,7 @@ protected: // This bitmask contains a 1 bit everywhere that _state_rs has a // known value. If a bit is 0, the corresponding state must be // re-sent. - // + // // Derived GSGs should initialize _inv_state_mask in reset() as a mask of // 1's where they don't care, and 0's where they do care, about the state. RenderState::SlotMask _state_mask; @@ -406,7 +414,7 @@ protected: unsigned int _color_write_mask; - CPT(DisplayRegion) _current_display_region; + PT(DisplayRegion) _current_display_region; Lens::StereoChannel _current_stereo_channel; int _current_tex_view_offset; CPT(Lens) _current_lens; @@ -481,6 +489,19 @@ protected: bool _supports_occlusion_query; PT(OcclusionQueryContext) _current_occlusion_query; + bool _supports_timer_query; +#ifdef DO_PSTATS + int _pstats_gpu_thread; + bool _timer_queries_active; + PStatFrameData _pstats_gpu_data; + + int _last_query_frame; + int _last_num_queried; + //double _timer_delta; + typedef pdeque TimerQueryQueue; + TimerQueryQueue _pending_timer_queries; +#endif + bool _copy_texture_inverted; bool _supports_multisample; bool _supports_generate_mipmap; @@ -520,13 +541,13 @@ protected: PN_stdfloat _gamma; Texture::QualityLevel _texture_quality_override; - + ShaderGenerator* _shader_generator; #ifndef NDEBUG PT(Texture) _flash_texture; #endif - + public: // Statistics static PStatCollector _vertex_buffer_switch_pcollector; @@ -558,7 +579,18 @@ public: static PStatCollector _draw_set_state_pcollector; static PStatCollector _clear_pcollector; static PStatCollector _flush_pcollector; + static PStatCollector _compute_dispatch_pcollector; static PStatCollector _wait_occlusion_pcollector; + static PStatCollector _wait_timer_pcollector; + static PStatCollector _timer_queries_pcollector; + static PStatCollector _command_latency_pcollector; + + static PStatCollector _prepare_pcollector; + static PStatCollector _prepare_texture_pcollector; + static PStatCollector _prepare_geom_pcollector; + static PStatCollector _prepare_shader_pcollector; + static PStatCollector _prepare_vertex_buffer_pcollector; + static PStatCollector _prepare_index_buffer_pcollector; // A whole slew of collectors to measure the cost of individual // state changes. These are disabled by default. diff --git a/panda/src/display/pStatGPUTimer.I b/panda/src/display/pStatGPUTimer.I new file mode 100644 index 0000000000..27f4938917 --- /dev/null +++ b/panda/src/display/pStatGPUTimer.I @@ -0,0 +1,61 @@ +// Filename: pStatGPUTimer.I +// Created by: rdb (21Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + + +#ifdef DO_PSTATS + +//////////////////////////////////////////////////////////////////// +// Function: PStatGPUTimer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatGPUTimer:: +PStatGPUTimer(GraphicsStateGuardian *gsg, PStatCollector &collector) : + PStatTimer(collector), + _gsg(gsg) +{ + if (gsg->get_timer_queries_active()) { + gsg->issue_timer_query(collector.get_index()); + //cerr << "issuing " << collector << " active " << collector.is_active() << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatGPUTimer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatGPUTimer:: +PStatGPUTimer(GraphicsStateGuardian *gsg, PStatCollector &collector, Thread *current_thread) : + PStatTimer(collector, current_thread), + _gsg(gsg) +{ + if (gsg->get_timer_queries_active()) { + gsg->issue_timer_query(collector.get_index()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatGPUTimer::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PStatGPUTimer:: +~PStatGPUTimer() { + if (_gsg->get_timer_queries_active()) { + _gsg->issue_timer_query(_collector.get_index() | 0x8000); + } +} + +#endif diff --git a/panda/src/display/pStatGPUTimer.h b/panda/src/display/pStatGPUTimer.h new file mode 100644 index 0000000000..4c67e3ea4c --- /dev/null +++ b/panda/src/display/pStatGPUTimer.h @@ -0,0 +1,65 @@ +// Filename: pStatGPUTimer.h +// Created by: rdb (21Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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 PSTATGPUTIMER_H +#define PSTATGPUTIMER_H + +#include "pandabase.h" +#include "pStatTimer.h" +#include "pStatCollector.h" +#include "config_pstats.h" +#include "timerQueryContext.h" + +class Thread; +class GraphicsStateGuardian; + +//////////////////////////////////////////////////////////////////// +// Class : PStatGPUTimer +// Description : This is a special type of PStatTimer that also +// uses a timer query on the GSG to measure how long +// a task actually takes to execute on the GPU, rather +// than how long it took for the API commands to be +// queued up. +// +// This class may only be used on the draw thread. +// +// At present, it tracks both the CPU time (like a +// regular PStatTimer does) and the GPU time, which +// is recorded using a special PStatThread. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_DISPLAY PStatGPUTimer : public PStatTimer { +public: +#ifdef DO_PSTATS + INLINE PStatGPUTimer(GraphicsStateGuardian *gsg, + PStatCollector &collector); + INLINE PStatGPUTimer(GraphicsStateGuardian *gsg, + PStatCollector &collector, + Thread *current_thread); + INLINE ~PStatGPUTimer(); + + GraphicsStateGuardian *_gsg; + +private: +#else // DO_PSTATS + + INLINE PStatGPUTimer(GraphicsStateGuardian *, PStatCollector &) { } + INLINE PStatGPUTimer(GraphicsStateGuardian *, PStatCollector &, Thread *) { } + INLINE ~PStatGPUTimer() { } + +#endif // DO_PSTATS +}; + +#include "pStatGPUTimer.I" + +#endif diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 50ecab80d0..99a64092a5 100755 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -18,7 +18,7 @@ #include "Cg/cgGL.h" -#include "pStatTimer.h" +#include "pStatGPUTimer.h" TypeHandle CLP(CgShaderContext)::_type_handle; @@ -50,7 +50,7 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte nassertv(s->get_language() == Shader::SL_Cg); - // Ask the shader to compile itself for us and + // Ask the shader to compile itself for us and // to give us the resulting Cg program objects. if (!s->cg_compile_for(_glgsg->_shader_caps, _cg_context, @@ -223,7 +223,7 @@ unbind() { //////////////////////////////////////////////////////////////////// void CLP(CgShaderContext):: issue_parameters(int altered) { - PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector); + //PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector); if (!valid()) { return; @@ -233,9 +233,9 @@ issue_parameters(int altered) { for (int i=0; i<(int)_shader->_ptr_spec.size(); i++) { if (altered & (_shader->_ptr_spec[i]._dep[0] | _shader->_ptr_spec[i]._dep[1])) { const Shader::ShaderPtrSpec& _ptr = _shader->_ptr_spec[i]; - Shader::ShaderPtrData* ptr_data = + Shader::ShaderPtrData* ptr_data = const_cast< Shader::ShaderPtrData*>(_glgsg->fetch_ptr_parameter(_ptr)); - + if (ptr_data == NULL){ //the input is not contained in ShaderPtrData release_resources(); return; @@ -249,14 +249,14 @@ issue_parameters(int altered) { int input_size = _ptr._dim[0] * _ptr._dim[1] * _ptr._dim[2]; // dimension is negative only if the parameter had the (deprecated)k_ prefix. - if ((input_size > ptr_data->_size) && (_ptr._dim[0] > 0)) { - GLCAT.error() << _ptr._id._name << ": incorrect number of elements, expected " + if ((input_size > ptr_data->_size) && (_ptr._dim[0] > 0)) { + GLCAT.error() << _ptr._id._name << ": incorrect number of elements, expected " << input_size <<" got " << ptr_data->_size << "\n"; release_resources(); return; } CGparameter p = _cg_parameter_map[_ptr._id._seqno]; - + switch (ptr_data->_type) { case Shader::SPT_float: switch(_ptr._info._class) { @@ -271,7 +271,7 @@ issue_parameters(int altered) { case Shader::SAC_matrix: cgGLSetMatrixParameterfc(p,(float*)ptr_data->_ptr); continue; case Shader::SAC_array: { switch(_ptr._info._subclass) { - case Shader::SAC_scalar: + case Shader::SAC_scalar: cgGLSetParameterArray1f(p,0,_ptr._dim[0],(float*)ptr_data->_ptr); continue; case Shader::SAC_vector: switch(_ptr._dim[2]) { @@ -298,7 +298,7 @@ issue_parameters(int altered) { case Shader::SAC_matrix: cgGLSetMatrixParameterdc(p,(double*)ptr_data->_ptr); continue; case Shader::SAC_array: { switch(_ptr._info._subclass) { - case Shader::SAC_scalar: + case Shader::SAC_scalar: cgGLSetParameterArray1d(p,0,_ptr._dim[0],(double*)ptr_data->_ptr); continue; case Shader::SAC_vector: switch(_ptr._dim[2]) { @@ -323,8 +323,8 @@ issue_parameters(int altered) { case Shader::SAT_vec4: cgSetParameter4iv(p,(int*)ptr_data->_ptr); continue; } } - default: GLCAT.error() << _ptr._id._name << ":" << "unrecognized parameter type\n"; - release_resources(); + default: GLCAT.error() << _ptr._id._name << ":" << "unrecognized parameter type\n"; + release_resources(); return; } } diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 232d33044a..671003a1f1 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -27,7 +27,10 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe, int flags, GraphicsStateGuardian *gsg, GraphicsOutput *host) : - GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) + GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), + _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"), + _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"), + _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples") { CLP(GraphicsStateGuardian) *glgsg; @@ -175,7 +178,7 @@ begin_frame(FrameMode mode, Thread *current_thread) { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_R(glgsg, _gsg, false); - pvector::iterator it; + TextureContexts::iterator it; for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { CLP(TextureContext) *gtc = *it; @@ -274,6 +277,8 @@ rebuild_bitplanes() { return; } + PStatGPUTimer timer(glgsg, _bind_texture_pcollector); + // Calculate bitplane size. This can be larger than the buffer. if (_creation_flags & GraphicsPipe::BF_size_track_host) { if (_host->get_size() != _size) { @@ -437,6 +442,17 @@ rebuild_bitplanes() { // Bind the FBO if (_fbo[layer] == 0) { glgsg->_glGenFramebuffers(1, &_fbo[layer]); + + if (glgsg->_use_object_labels) { + if (num_fbos > 1) { + GLchar name[128]; + GLsizei len = snprintf(name, 128, "%s[%d]", _name.c_str(), layer); + glgsg->_glObjectLabel(GL_FRAMEBUFFER, _fbo[layer], len, name); + } else { + glgsg->_glObjectLabel(GL_FRAMEBUFFER, _fbo[layer], _name.size(), _name.data()); + } + } + if (_fbo[layer] == 0) { report_my_gl_errors(); return; @@ -1131,6 +1147,8 @@ generate_mipmaps() { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); + //PStatGPUTimer timer(glgsg, _generate_mipmap_pcollector); + pvector::iterator it; for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { CLP(TextureContext) *gtc = *it; @@ -1168,7 +1186,8 @@ end_frame(FrameMode mode, Thread *current_thread) { copy_to_textures(); } - // Unbind the FBO + // Unbind the FBO. TODO: calling bind_fbo is slow, so we should + // probably move this to begin_frame to prevent unnecessary calls. CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); glgsg->bind_fbo(0); @@ -1604,10 +1623,12 @@ check_host_valid() { //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: resolve_multisamples() { + nassertv(_fbo.size() > 0); + CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); - nassertv(_fbo.size() > 0); + PStatGPUTimer timer(glgsg, _resolve_multisample_pcollector); if (gl_enable_memory_barriers) { // Issue memory barriers as necessary to make sure that the @@ -1631,7 +1652,7 @@ resolve_multisamples() { } glgsg->_glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo); glgsg->_glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, _fbo_multisample); - + // If the depth buffer is shared, resolve it only on the last to render FBO. bool do_depth_blit = false; if (_rbm[RTP_depth_stencil] != 0 || _rbm[RTP_depth] != 0) { diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 34d9cfa0a4..653e5dc8b2 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -34,7 +34,7 @@ // * Can render onto a texture without clearing it first. // * Supports multisample antialiased rendering. // -// Some of these deserve a little explanation. +// Some of these deserve a little explanation. // Auxiliary bitplanes are additional bitplanes above // and beyond the normal depth,stencil,color. One can // use them to render out multiple textures in a single @@ -43,14 +43,14 @@ // buffer will be equal to the texture's previous // contents. This alo means you can meaningfully // share a bitplane between two buffers by binding -// the same texture to both buffers. +// the same texture to both buffers. // // If either of the necessary OpenGL extensions is not // available, then the glGraphicsBuffer will not be // available (although it may still be possible to // create a wglGraphicsBuffer or glxGraphicsBuffer). // -// This class now also uses the extensions +// This class now also uses the extensions // EXT_framebuffer_multisample and EXT_framebuffer_blit // to allow for multisample antialiasing these offscreen // render targets. If these extensions are unavailable @@ -87,13 +87,13 @@ public: protected: virtual void close_buffer(); virtual bool open_buffer(); - + void check_host_valid(); - + void report_my_errors(int line, const char *file); private: - + void bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane plane, GLenum attachpoint); void bind_slot_multisample(bool rb_resize, Texture **attach, @@ -127,7 +127,8 @@ private: // List of textures for which we might have to generate mipmaps // after rendering one frame. - pvector _texture_contexts; + typedef pvector TextureContexts; + TextureContexts _texture_contexts; // The cube map face we are currently drawing to or have just // finished drawing to, or -1 if we are not drawing to a cube map. @@ -136,10 +137,14 @@ private: bool _initial_clear; bool _needs_rebuild; UpdateSeq _last_textures_seq; - + CLP(GraphicsBuffer) *_shared_depth_buffer; list _shared_depth_buffer_list; - + + PStatCollector _bind_texture_pcollector; + PStatCollector _generate_mipmap_pcollector; + PStatCollector _resolve_multisample_pcollector; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.I b/panda/src/glstuff/glGraphicsStateGuardian_src.I index 9008a3cd23..f776d69c2e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.I +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.I @@ -27,6 +27,7 @@ INLINE bool CLP(GraphicsStateGuardian):: report_errors(int line, const char *source_file) { #ifndef NDEBUG + PStatTimer timer(_check_error_pcollector); GLenum error_code = glGetError(); if (error_code != GL_NO_ERROR) { int error_count = 0; @@ -46,6 +47,7 @@ INLINE void CLP(GraphicsStateGuardian):: report_my_errors(int line, const char *source_file) { #ifndef NDEBUG if (_check_errors) { + PStatTimer timer(_check_error_pcollector); GLenum error_code = glGetError(); if (error_code != GL_NO_ERROR) { if (!report_errors_loop(line, source_file, error_code, _error_count)) { @@ -69,6 +71,7 @@ report_my_errors(int line, const char *source_file) { //////////////////////////////////////////////////////////////////// INLINE bool CLP(GraphicsStateGuardian):: clear_errors(int line, const char *source_file) { + PStatTimer timer(_check_error_pcollector); GLenum error_code = glGetError(); if (error_code != GL_NO_ERROR) { int error_count = 0; @@ -92,6 +95,7 @@ clear_errors(int line, const char *source_file) { INLINE void CLP(GraphicsStateGuardian):: clear_my_errors(int line, const char *source_file) { if (_check_errors) { + PStatTimer timer(_check_error_pcollector); GLenum error_code = glGetError(); if (error_code != GL_NO_ERROR) { int error_count = 0; @@ -743,7 +747,7 @@ get_clip_plane_id(int index) const { //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_multisample // Access: Public -// Description: Returns if this glGsg supports multisample +// Description: Returns if this glGsg supports multisample // antialiasing for framebuffer objects. //////////////////////////////////////////////////////////////////// INLINE bool CLP(GraphicsStateGuardian):: @@ -754,7 +758,7 @@ get_supports_framebuffer_multisample() { //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_multisample // Access: Public -// Description: Returns if this glGsg supports multisample +// Description: Returns if this glGsg supports multisample // antialiasing for framebuffer objects. //////////////////////////////////////////////////////////////////// INLINE bool CLP(GraphicsStateGuardian):: @@ -766,7 +770,7 @@ get_supports_framebuffer_multisample_coverage_nv() { //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_blit // Access: Public -// Description: Returns if this glGsg supports multisample +// Description: Returns if this glGsg supports multisample // antialiasing for framebuffer objects. //////////////////////////////////////////////////////////////////// INLINE bool CLP(GraphicsStateGuardian):: @@ -778,7 +782,7 @@ get_supports_framebuffer_blit() { //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::UsageTextureKey::Constructor // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE CLP(GraphicsStateGuardian)::UsageTextureKey:: UsageTextureKey(int x_size, int y_size) : @@ -792,7 +796,7 @@ UsageTextureKey(int x_size, int y_size) : //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::UsageTextureKey::operator < // Access: Public -// Description: +// Description: //////////////////////////////////////////////////////////////////// INLINE bool CLP(GraphicsStateGuardian)::UsageTextureKey:: operator < (const CLP(GraphicsStateGuardian)::UsageTextureKey &other) const { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 0050471808..818e03e7bc 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1,6 +1,6 @@ // Filename: glGraphicsStateGuardian_src.cxx // Created by: drose (02Feb99) -// Updated by: fperazzi, PandaSE (05May10) (added +// Updated by: fperazzi, PandaSE (05May10) (added // get_supports_cg_profile) // //////////////////////////////////////////////////////////////////// @@ -77,7 +77,11 @@ PStatCollector CLP(GraphicsStateGuardian)::_load_display_list_pcollector("Draw:T PStatCollector CLP(GraphicsStateGuardian)::_primitive_batches_display_list_pcollector("Primitive batches:Display lists"); PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists"); PStatCollector CLP(GraphicsStateGuardian)::_vertices_immediate_pcollector("Vertices:Immediate mode"); -PStatCollector CLP(GraphicsStateGuardian)::_compute_dispatch_pcollector("Draw:Compute dispatch"); +PStatCollector CLP(GraphicsStateGuardian)::_memory_barrier_pcollector("Draw:Memory barriers"); +PStatCollector CLP(GraphicsStateGuardian)::_vertex_array_update_pcollector("Draw:Update arrays"); +PStatCollector CLP(GraphicsStateGuardian)::_texture_update_pcollector("Draw:Update texture"); +PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO"); +PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors"); #ifdef OPENGLES_2 PT(Shader) CLP(GraphicsStateGuardian)::_default_shader = NULL; @@ -315,7 +319,7 @@ CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) : #ifdef DO_PSTATS if (gl_finish) { GLCAT.warning() - << "The config variable gl-finish is set True. This may have a substantial negative impact your render performance.\n"; + << "The config variable gl-finish is set to true. This may have a substantial negative impact on your render performance.\n"; } #endif // DO_PSTATS } @@ -441,6 +445,7 @@ reset() { // Initialize OpenGL debugging output first, if enabled and supported. _supports_debug = false; + _use_object_labels = false; #ifndef OPENGLES_1 if (gl_debug) { PFNGLDEBUGMESSAGECALLBACKPROC _glDebugMessageCallback; @@ -452,14 +457,19 @@ reset() { get_extension_func("glDebugMessageCallbackKHR"); _glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) get_extension_func("glDebugMessageControlKHR"); + _glObjectLabel = (PFNGLOBJECTLABELPROC) + get_extension_func("glObjectLabelKHR"); #else _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) get_extension_func("glDebugMessageCallback"); _glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) get_extension_func("glDebugMessageControl"); + _glObjectLabel = (PFNGLOBJECTLABELPROC) + get_extension_func("glObjectLabel"); #endif glEnable(GL_DEBUG_OUTPUT); // Not supported in ARB version _supports_debug = true; + _use_object_labels = gl_debug_object_labels; } else if (has_extension("GL_ARB_debug_output")) { _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) @@ -681,7 +691,7 @@ reset() { _supports_depth_stencil = has_extension("GL_EXT_packed_depth_stencil") || has_extension("GL_OES_packed_depth_stencil"); } - + _supports_3d_texture = false; if (is_at_least_gl_version(1, 2)) { @@ -1038,7 +1048,7 @@ reset() { // ATI drivers have never provided correct shadow support. _supports_shadow_filter = false; } - + _supports_texture_combine = has_extension("GL_ARB_texture_env_combine") || is_at_least_gl_version(1, 3) || is_at_least_gles_version(1, 1); _supports_texture_saved_result = @@ -1095,7 +1105,7 @@ reset() { _shader_caps._active_vprofile = (int)CG_PROFILE_ARBVP1; _shader_caps._active_fprofile = (int)CG_PROFILE_ARBFP1; _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN; // No geometry shader if only using basic - } else { + } else { _shader_caps._active_vprofile = (int)cgGLGetLatestProfile(CG_GL_VERTEX); _shader_caps._active_fprofile = (int)cgGLGetLatestProfile(CG_GL_FRAGMENT); _shader_caps._active_gprofile = (int)cgGLGetLatestProfile(CG_GL_GEOMETRY); @@ -1534,6 +1544,23 @@ reset() { } #endif + _supports_timer_query = false; +#if defined(DO_PSTATS) && !defined(OPENGLES) + if (is_at_least_gl_version(3, 3) || has_extension("GL_ARB_timer_query")) { + _supports_timer_query = true; + + _glQueryCounter = (PFNGLQUERYCOUNTERPROC) + get_extension_func("glQueryCounter"); + _glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) + get_extension_func("glGetQueryObjecti64v"); + _glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) + get_extension_func("glGetQueryObjectui64v"); + + _glGetInteger64v = (PFNGLGETINTEGER64VPROC) + get_extension_func("glGetInteger64v"); + } +#endif + #ifdef OPENGLES_2 // In OpenGL ES 2.x, this is supported in the core. _glBlendEquation = glBlendEquation; @@ -2085,7 +2112,7 @@ reset() { CG_PROFILE_UNKNOWN, SM_00, }; - + int index; CG_PROFILE_TO_SHADER_MODEL *cg_profile_to_shader_model; @@ -2103,7 +2130,7 @@ reset() { { GraphicsPipe *pipe; DisplayInformation *display_information; - + pipe = this->get_pipe(); if (pipe) { display_information = pipe->get_display_information (); @@ -2151,17 +2178,17 @@ reset() { //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::finish // Access: Public, Virtual -// Description: Force the graphics card to finish drawing before +// 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 +// 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 +// 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 + // 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 @@ -2183,7 +2210,7 @@ finish() { //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: clear(DrawableRegion *clearable) { - PStatTimer timer(_clear_pcollector); + PStatGPUTimer timer(this, _clear_pcollector); report_my_gl_errors(); if (!clearable->is_any_clear_active()) { @@ -2467,7 +2494,7 @@ calc_projection_mat(const Lens *lens) { // projection matrix. result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f); } - + return TransformState::make_mat(result); } @@ -2556,6 +2583,17 @@ begin_frame(Thread *current_thread) { } #endif // NDEBUG +#ifdef DO_PSTATS + /*if (_supports_timer_query) { + // Measure the difference between the OpenGL clock and the + // PStats clock. + GLint64 time_ns; + _glGetInteger64v(GL_TIMESTAMP, &time_ns); + _timer_delta = time_ns * -0.000000001; + _timer_delta += PStatClient::get_global_pstats()->get_real_time(); + }*/ +#endif + #ifndef OPENGLES_1 if (_current_properties->get_srgb_color()) { glEnable(GL_FRAMEBUFFER_SRGB); @@ -2657,6 +2695,15 @@ end_frame(Thread *current_thread) { } #endif + // Calling glFlush() at the end of the frame is particularly + // necessary if this is a single-buffered visual, so that the frame + // will be finished drawing before we return to the application. + // It's not clear what effect this has on our total frame time. + //if (_force_flush || _current_properties->is_single_buffered()) { + // gl_flush(); + //} + maybe_gl_finish(); + GraphicsStateGuardian::end_frame(current_thread); // Flush any PCollectors specific to this kind of GSG. @@ -2669,7 +2716,7 @@ end_frame(Thread *current_thread) { { LightMutexHolder holder(_lock); if (!_deleted_display_lists.empty()) { - DeletedDisplayLists::iterator ddli; + DeletedNames::iterator ddli; for (ddli = _deleted_display_lists.begin(); ddli != _deleted_display_lists.end(); ++ddli) { @@ -2682,36 +2729,30 @@ end_frame(Thread *current_thread) { _deleted_display_lists.clear(); } - // And deleted occlusion queries, too. - if (!_deleted_queries.empty()) { - DeletedDisplayLists::iterator ddli; - for (ddli = _deleted_queries.begin(); - ddli != _deleted_queries.end(); - ++ddli) { - if (GLCAT.is_debug()) { - GLCAT.debug() - << "releasing query index " << (int)(*ddli) << "\n"; + // And deleted queries, too, unless we're using query timers + // in which case we'll need to reuse lots of them. + if (!_timer_queries_active && !_deleted_queries.empty()) { + if (GLCAT.is_spam()) { + DeletedNames::iterator dqi; + for (dqi = _deleted_queries.begin(); + dqi != _deleted_queries.end(); + ++dqi) { + GLCAT.spam() + << "releasing query index " << (int)(*dqi) << "\n"; } - _glDeleteQueries(1, &(*ddli)); } + _glDeleteQueries(_deleted_queries.size(), &_deleted_queries[0]); _deleted_queries.clear(); } } #endif // OPENGLES - // Calling glFlush() at the end of the frame is particularly - // necessary if this is a single-buffered visual, so that the frame - // will be finished drawing before we return to the application. - // It's not clear what effect this has on our total frame time. - if (_force_flush || _current_properties->is_single_buffered()) { - gl_flush(); - } - maybe_gl_finish(); - #ifndef NDEBUG if (_check_errors || (_supports_debug && gl_debug)) { report_my_gl_errors(); } else { + PStatTimer timer(_check_error_pcollector); + // If _check_errors is false, we still want to check for errors // once during this frame, so that we know if anything went wrong. GLenum error_code = glGetError(); @@ -2970,48 +3011,51 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, #ifdef SUPPORT_IMMEDIATE_MODE _use_sender = !vertex_arrays; #endif - + + { + //PStatGPUTimer timer(this, _vertex_array_update_pcollector); #ifdef OPENGLES_1 - if (!update_standard_vertex_arrays(force)) { - return false; - } -#else - if (_current_shader_context == 0) { - // No shader. - if (_vertex_array_shader_context != 0) { - _vertex_array_shader_context->disable_shader_vertex_arrays(); - } if (!update_standard_vertex_arrays(force)) { return false; } - } else { - // Shader. - if (_vertex_array_shader_context == 0 || _vertex_array_shader_context->uses_standard_vertex_arrays()) { - // Previous shader used standard arrays. - if (_current_shader_context->uses_standard_vertex_arrays()) { - // So does the current, so update them. - if (!update_standard_vertex_arrays(force)) { - return false; - } - } else { - // The current shader does not, so disable them entirely. - disable_standard_vertex_arrays(); +#else + if (_current_shader_context == 0) { + // No shader. + if (_vertex_array_shader_context != 0) { + _vertex_array_shader_context->disable_shader_vertex_arrays(); } - } - if (_current_shader_context->uses_custom_vertex_arrays()) { - // The current shader also uses custom vertex arrays. - if (!_current_shader_context-> - update_shader_vertex_arrays(_vertex_array_shader_context, force)) { + if (!update_standard_vertex_arrays(force)) { return false; } } else { - _vertex_array_shader_context->disable_shader_vertex_arrays(); + // Shader. + if (_vertex_array_shader_context == 0 || _vertex_array_shader_context->uses_standard_vertex_arrays()) { + // Previous shader used standard arrays. + if (_current_shader_context->uses_standard_vertex_arrays()) { + // So does the current, so update them. + if (!update_standard_vertex_arrays(force)) { + return false; + } + } else { + // The current shader does not, so disable them entirely. + disable_standard_vertex_arrays(); + } + } + if (_current_shader_context->uses_custom_vertex_arrays()) { + // The current shader also uses custom vertex arrays. + if (!_current_shader_context-> + update_shader_vertex_arrays(_vertex_array_shader_context, force)) { + return false; + } + } else { + _vertex_array_shader_context->disable_shader_vertex_arrays(); + } } - } - _vertex_array_shader = _current_shader; - _vertex_array_shader_context = _current_shader_context; + _vertex_array_shader = _current_shader; + _vertex_array_shader_context = _current_shader_context; #endif // OPENGLES_1 + } report_my_gl_errors(); return true; @@ -3121,7 +3165,7 @@ update_standard_vertex_arrays(bool force) { Geom::NumericType numeric_type; int start; int stride; - + if (_data_reader->get_normal_info(array_reader, numeric_type, start, stride)) { if (!setup_array_data(client_pointer, array_reader, force)) { @@ -3133,7 +3177,7 @@ update_standard_vertex_arrays(bool force) { } else { glDisableClientState(GL_NORMAL_ARRAY); } - + #ifndef NDEBUG if (_show_texture_usage) { // In show_texture_usage mode, all colors are white, so as not @@ -3157,12 +3201,12 @@ update_standard_vertex_arrays(bool force) { glEnableClientState(GL_COLOR_ARRAY); } else { glDisableClientState(GL_COLOR_ARRAY); - + // Since we don't have per-vertex color, the implicit color is // white. GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f); } - + // Now set up each of the active texture coordinate stages--or at // least those for which we're not generating texture coordinates // automatically. @@ -3175,7 +3219,7 @@ update_standard_vertex_arrays(bool force) { // This stage is not one of the stages that doesn't need // texcoords issued for it. const InternalName *name = stage->get_texcoord_name(); - + if (_data_reader->get_array_info(name, array_reader, num_values, numeric_type, start, stride)) { // The vertex data does have texcoords for this stage. @@ -3185,7 +3229,7 @@ update_standard_vertex_arrays(bool force) { glTexCoordPointer(num_values, get_numeric_type(numeric_type), stride, client_pointer + start); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - + } else { // The vertex data doesn't have texcoords for this stage (even // though they're needed). @@ -3195,10 +3239,10 @@ update_standard_vertex_arrays(bool force) { // No texcoords are needed for this stage. glDisableClientState(GL_TEXTURE_COORD_ARRAY); } - + ++stage_index; } - + // Be sure also to disable any texture stages we had enabled before. while (stage_index < _last_max_stage_index) { _glClientActiveTexture(GL_TEXTURE0 + stage_index); @@ -3305,8 +3349,7 @@ unbind_buffers() { // way." Called only from begin_draw_primitives. //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: -disable_standard_vertex_arrays() -{ +disable_standard_vertex_arrays() { #ifndef OPENGLES_2 #ifdef SUPPORT_IMMEDIATE_MODE if (_use_sender) return; @@ -3343,7 +3386,7 @@ disable_standard_vertex_arrays() //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); #ifndef NDEBUG if (GLCAT.is_spam()) { @@ -3410,7 +3453,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); report_my_gl_errors(); @@ -3537,7 +3580,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); #ifndef NDEBUG if (GLCAT.is_spam()) { GLCAT.spam() << "draw_trifans: " << *(reader->get_object()) << "\n"; @@ -3617,7 +3660,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_patches(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); #ifndef NDEBUG if (GLCAT.is_spam()) { @@ -3693,7 +3736,7 @@ draw_patches(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_lines(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); #ifndef NDEBUG if (GLCAT.is_spam()) { GLCAT.spam() << "draw_lines: " << *(reader->get_object()) << "\n"; @@ -3767,7 +3810,7 @@ draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: draw_points(const GeomPrimitivePipelineReader *reader, bool force) { - //PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _draw_primitive_pcollector, reader->get_current_thread()); #ifndef NDEBUG if (GLCAT.is_spam()) { GLCAT.spam() << "draw_points: " << *(reader->get_object()) << "\n"; @@ -3890,6 +3933,8 @@ issue_memory_barrier(GLbitfield barriers) { return; } + PStatGPUTimer timer(this, _memory_barrier_pcollector); + if (GLCAT.is_debug()) { GLCAT.debug() << "Issuing memory barriers:"; } @@ -3939,6 +3984,8 @@ issue_memory_barrier(GLbitfield barriers) { //////////////////////////////////////////////////////////////////// TextureContext *CLP(GraphicsStateGuardian):: prepare_texture(Texture *tex, int view) { + PStatGPUTimer timer(this, _prepare_texture_pcollector); + report_my_gl_errors(); // Make sure we'll support this texture when it's rendered. Don't // bother to prepare it if we won't. @@ -3950,7 +3997,7 @@ prepare_texture(Texture *tex, int view) { return NULL; } break; - + case Texture::TT_2d_texture_array: if (!_supports_2d_texture_array) { GLCAT.warning() @@ -3997,6 +4044,8 @@ update_texture(TextureContext *tc, bool force) { CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); if (gtc->was_image_modified() || !gtc->_has_storage) { + PStatGPUTimer timer(this, _texture_update_pcollector); + // If the texture image was modified, reload the texture. apply_texture(tc); if (gtc->was_properties_modified()) { @@ -4010,6 +4059,8 @@ update_texture(TextureContext *tc, bool force) { } } else if (gtc->was_properties_modified()) { + PStatGPUTimer timer(this, _texture_update_pcollector); + // If only the properties have been modified, we don't necessarily // need to reload the texture. apply_texture(tc); @@ -4097,6 +4148,7 @@ extract_texture_data(Texture *tex) { //////////////////////////////////////////////////////////////////// GeomContext *CLP(GraphicsStateGuardian):: prepare_geom(Geom *geom) { + PStatGPUTimer timer(this, _prepare_geom_pcollector); return new CLP(GeomContext)(geom); } @@ -4124,6 +4176,8 @@ release_geom(GeomContext *gc) { //////////////////////////////////////////////////////////////////// ShaderContext *CLP(GraphicsStateGuardian):: prepare_shader(Shader *se) { + PStatGPUTimer timer(this, _prepare_shader_pcollector); + #ifndef OPENGLES_1 ShaderContext *result = NULL; @@ -4194,6 +4248,8 @@ record_deleted_display_list(GLuint index) { VertexBufferContext *CLP(GraphicsStateGuardian):: prepare_vertex_buffer(GeomVertexArrayData *data) { if (_supports_buffers) { + PStatGPUTimer timer(this, _prepare_vertex_buffer_pcollector); + CLP(VertexBufferContext) *gvbc = new CLP(VertexBufferContext)(this, _prepared_objects, data); _glGenBuffers(1, &gvbc->_index); @@ -4252,7 +4308,7 @@ apply_vertex_buffer(VertexBufferContext *vbc, return false; } - PStatTimer timer(_load_vertex_buffer_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _load_vertex_buffer_pcollector, reader->get_current_thread()); if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) { _glBufferData(GL_ARRAY_BUFFER, num_bytes, client_pointer, get_usage(reader->get_usage_hint())); @@ -4383,6 +4439,8 @@ setup_array_data(const unsigned char *&client_pointer, IndexBufferContext *CLP(GraphicsStateGuardian):: prepare_index_buffer(GeomPrimitive *data) { if (_supports_buffers) { + PStatGPUTimer timer(this, _prepare_index_buffer_pcollector); + CLP(IndexBufferContext) *gibc = new CLP(IndexBufferContext)(this, _prepared_objects, data); _glGenBuffers(1, &gibc->_index); @@ -4444,7 +4502,7 @@ apply_index_buffer(IndexBufferContext *ibc, return false; } - PStatTimer timer(_load_index_buffer_pcollector, reader->get_current_thread()); + PStatGPUTimer timer(this, _load_index_buffer_pcollector, reader->get_current_thread()); if (gibc->changed_size(reader) || gibc->changed_usage_hint(reader)) { _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes, client_pointer, get_usage(reader->get_usage_hint())); @@ -4594,7 +4652,7 @@ begin_occlusion_query() { _glBeginQuery(GL_SAMPLES_PASSED, query->_index); _current_occlusion_query = query; - + report_my_gl_errors(); #endif // OPENGLES } @@ -4619,7 +4677,7 @@ end_occlusion_query() { PT(OcclusionQueryContext) result = _current_occlusion_query; GLuint index = DCAST(CLP(OcclusionQueryContext), result)->_index; - + if (GLCAT.is_debug()) { GLCAT.debug() << "ending occlusion query index " << (int)index << "\n"; @@ -4635,9 +4693,9 @@ end_occlusion_query() { static ConfigVariableInt limit_occlusion_queries("limit-occlusion-queries", 0); if (limit_occlusion_queries > 0) { if (index > (unsigned int)limit_occlusion_queries) { - PStatTimer timer(_wait_occlusion_pcollector); + PStatGPUTimer timer(this, _wait_occlusion_pcollector); GLuint result; - _glGetQueryObjectuiv(index - (unsigned int)limit_occlusion_queries, + _glGetQueryObjectuiv(index - (unsigned int)limit_occlusion_queries, GL_QUERY_RESULT, &result); } } @@ -4648,6 +4706,54 @@ end_occlusion_query() { #endif // OPENGLES } +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::issue_timer_query +// Access: Public, Virtual +// Description: Adds a timer query to the command stream, associated +// with the given PStats collector index. +//////////////////////////////////////////////////////////////////// +PT(TimerQueryContext) CLP(GraphicsStateGuardian):: +issue_timer_query(int pstats_index) { +#if defined(DO_PSTATS) && !defined(OPENGLES) + nassertr(_supports_timer_query, NULL); + + PT(CLP(TimerQueryContext)) query; + + // Hack + if (pstats_index == _command_latency_pcollector.get_index()) { + query = new CLP(LatencyQueryContext)(this, pstats_index); + } else { + query = new CLP(TimerQueryContext)(this, pstats_index); + } + + if (_deleted_queries.size() >= 1) { + query->_index = _deleted_queries.back(); + _deleted_queries.pop_back(); + } else { + _glGenQueries(1, &query->_index); + + if (GLCAT.is_spam()) { + GLCAT.spam() << "Generating query for " << pstats_index + << ": " << query->_index << "\n"; + } + } + + if (_use_object_labels) { + // Assign a label to it based on the PStatCollector name. + const PStatClient *client = PStatClient::get_global_pstats(); + string name = client->get_collector_fullname(pstats_index & 0x7fff); + _glObjectLabel(GL_QUERY, query->_index, name.size(), name.data()); + } + + // Issue the timestamp query. + _glQueryCounter(query->_index, GL_TIMESTAMP); + + _pending_timer_queries.push_back(DCAST(TimerQueryContext, query)); + + return DCAST(TimerQueryContext, query); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::dispatch_compute // Access: Public, Virtual @@ -4659,7 +4765,7 @@ dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) { #ifndef OPENGLES maybe_gl_finish(); - PStatTimer timer(_compute_dispatch_pcollector); + PStatGPUTimer timer(this, _compute_dispatch_pcollector); nassertv(_supports_compute_shaders); nassertv(_current_shader_context != NULL); _glDispatchCompute(num_groups_x, num_groups_y, num_groups_z); @@ -4697,7 +4803,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, if (gl_color_mask) { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } - + int xo, yo, w, h; dr->get_region_pixels(xo, yo, w, h); tex->set_size_padded(w, h, tex->get_z_size()); @@ -5193,7 +5299,7 @@ do_issue_shader(bool state_has_changed) { context = shader->prepare_now(get_prepared_objects(), this); } #endif - + if (context == 0 || (context->valid() == false)) { if (_current_shader_context != 0) { _current_shader_context->unbind(); @@ -5788,8 +5894,8 @@ void CLP(GraphicsStateGuardian):: bind_light(PointLight *light_obj, const NodePath &light, int light_id) { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_bind_point_pcollector("Draw:Set State:Light:Bind:Point"); - // PStatTimer timer(_draw_set_state_light_bind_point_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_bind_point_pcollector); + GLenum id = get_light_id(light_id); static const LColor black(0.0f, 0.0f, 0.0f, 1.0f); call_glLightfv(id, GL_AMBIENT, black); @@ -5833,7 +5939,7 @@ void CLP(GraphicsStateGuardian):: bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_bind_directional_pcollector("Draw:Set State:Light:Bind:Directional"); - // PStatTimer timer(_draw_set_state_light_bind_directional_pcollector); + // PStatGPUTimer timer(this, _draw_set_state_light_bind_directional_pcollector); pair lookup = _dlights.insert(DirectionalLights::value_type(light, DirectionalLightFrameData())); DirectionalLightFrameData &fdata = (*lookup.first).second; @@ -5885,8 +5991,8 @@ void CLP(GraphicsStateGuardian):: bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_bind_spotlight_pcollector("Draw:Set State:Light:Bind:Spotlight"); - // PStatTimer timer(_draw_set_state_light_bind_spotlight_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_bind_spotlight_pcollector); + Lens *lens = light_obj->get_lens(); nassertv(lens != (Lens *)NULL); @@ -6019,6 +6125,7 @@ gl_flush() const { GLenum CLP(GraphicsStateGuardian):: gl_get_error() const { if (_check_errors) { + PStatTimer timer(_check_error_pcollector); return glGetError(); } else { return GL_NO_ERROR; @@ -6180,12 +6287,12 @@ query_gl_version() { _gl_shadlang_ver_minor = 0; } } - } + } else if (_gl_version_major >= 2) { const char *verstr = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); if ((verstr == NULL) || (sscanf(verstr, "%d.%d", &_gl_shadlang_ver_major, &_gl_shadlang_ver_minor) != 2)) { - GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n"; + GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n"; } } #endif @@ -6454,7 +6561,7 @@ set_draw_buffer(int rbtype) { } } #endif // OPENGLES - + // Also ensure that any global color channels are masked out. if (gl_color_mask) { glColorMask((_color_write_mask & ColorWriteAttrib::C_red) != 0, @@ -6462,7 +6569,7 @@ set_draw_buffer(int rbtype) { (_color_write_mask & ColorWriteAttrib::C_blue) != 0, (_color_write_mask & ColorWriteAttrib::C_alpha) != 0); } - + report_my_gl_errors(); } @@ -7910,8 +8017,8 @@ void CLP(GraphicsStateGuardian):: enable_lighting(bool enable) { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_enable_lighting_pcollector("Draw:Set State:Light:Enable lighting"); - // PStatTimer timer(_draw_set_state_light_enable_lighting_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_enable_lighting_pcollector); + if (enable) { glEnable(GL_LIGHTING); } else { @@ -7932,8 +8039,8 @@ void CLP(GraphicsStateGuardian):: set_ambient_light(const LColor &color) { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_ambient_pcollector("Draw:Set State:Light:Ambient"); - // PStatTimer timer(_draw_set_state_light_ambient_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_ambient_pcollector); + LColor c = color; c.set(c[0] * _light_color_scale[0], c[1] * _light_color_scale[1], @@ -7953,8 +8060,8 @@ set_ambient_light(const LColor &color) { void CLP(GraphicsStateGuardian):: enable_light(int light_id, bool enable) { // static PStatCollector _draw_set_state_light_enable_light_pcollector("Draw:Set State:Light:Enable light"); - // PStatTimer timer(_draw_set_state_light_enable_light_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_enable_light_pcollector); + if (enable) { glEnable(get_light_id(light_id)); } else { @@ -7978,8 +8085,8 @@ void CLP(GraphicsStateGuardian):: begin_bind_lights() { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_begin_bind_pcollector("Draw:Set State:Light:Begin bind"); - // PStatTimer timer(_draw_set_state_light_begin_bind_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_begin_bind_pcollector); + // We need to temporarily load a new matrix so we can define the // light in a known coordinate system. We pick the transform of the // root. (Alternatively, we could leave the current transform where @@ -8009,8 +8116,8 @@ void CLP(GraphicsStateGuardian):: end_bind_lights() { #ifndef OPENGLES_2 // static PStatCollector _draw_set_state_light_end_bind_pcollector("Draw:Set State:Light:End bind"); - // PStatTimer timer(_draw_set_state_light_end_bind_pcollector); - + // PStatGPUTimer timer(this, _draw_set_state_light_end_bind_pcollector); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); #endif @@ -8142,11 +8249,11 @@ set_state_and_transform(const RenderState *target, #endif _state_pcollector.add_level(1); - PStatTimer timer1(_draw_set_state_pcollector); + PStatGPUTimer timer1(this, _draw_set_state_pcollector); if (transform != _internal_transform) { - //PStatTimer timer(_draw_set_state_transform_pcollector); - _state_pcollector.add_level(1); + //PStatGPUTimer timer(this, _draw_set_state_transform_pcollector); + _transform_state_pcollector.add_level(1); _internal_transform = transform; do_issue_transform(); } @@ -8177,9 +8284,9 @@ set_state_and_transform(const RenderState *target, int alpha_test_slot = AlphaTestAttrib::get_class_slot(); if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) || !_state_mask.get_bit(alpha_test_slot) || - (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test) != + (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test) != _state_shader->get_flag(ShaderAttrib::F_subsume_alpha_test))) { - //PStatTimer timer(_draw_set_state_alpha_test_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_alpha_test_pcollector); do_issue_alpha_test(); _state_mask.set_bit(alpha_test_slot); } @@ -8187,7 +8294,7 @@ set_state_and_transform(const RenderState *target, int antialias_slot = AntialiasAttrib::get_class_slot(); if (_target_rs->get_attrib(antialias_slot) != _state_rs->get_attrib(antialias_slot) || !_state_mask.get_bit(antialias_slot)) { - //PStatTimer timer(_draw_set_state_antialias_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_antialias_pcollector); do_issue_antialias(); _state_mask.set_bit(antialias_slot); } @@ -8195,7 +8302,7 @@ set_state_and_transform(const RenderState *target, int clip_plane_slot = ClipPlaneAttrib::get_class_slot(); if (_target_rs->get_attrib(clip_plane_slot) != _state_rs->get_attrib(clip_plane_slot) || !_state_mask.get_bit(clip_plane_slot)) { - //PStatTimer timer(_draw_set_state_clip_plane_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_clip_plane_pcollector); do_issue_clip_plane(); _state_mask.set_bit(clip_plane_slot); } @@ -8206,7 +8313,7 @@ set_state_and_transform(const RenderState *target, _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) || !_state_mask.get_bit(color_slot) || !_state_mask.get_bit(color_scale_slot)) { - //PStatTimer timer(_draw_set_state_color_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_color_pcollector); do_issue_color(); do_issue_color_scale(); _state_mask.set_bit(color_slot); @@ -8222,7 +8329,7 @@ set_state_and_transform(const RenderState *target, int cull_face_slot = CullFaceAttrib::get_class_slot(); if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) || !_state_mask.get_bit(cull_face_slot)) { - //PStatTimer timer(_draw_set_state_cull_face_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_cull_face_pcollector); do_issue_cull_face(); _state_mask.set_bit(cull_face_slot); } @@ -8230,7 +8337,7 @@ set_state_and_transform(const RenderState *target, int depth_offset_slot = DepthOffsetAttrib::get_class_slot(); if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) || !_state_mask.get_bit(depth_offset_slot)) { - //PStatTimer timer(_draw_set_state_depth_offset_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_depth_offset_pcollector); do_issue_depth_offset(); _state_mask.set_bit(depth_offset_slot); } @@ -8238,7 +8345,7 @@ set_state_and_transform(const RenderState *target, int depth_test_slot = DepthTestAttrib::get_class_slot(); if (_target_rs->get_attrib(depth_test_slot) != _state_rs->get_attrib(depth_test_slot) || !_state_mask.get_bit(depth_test_slot)) { - //PStatTimer timer(_draw_set_state_depth_test_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_depth_test_pcollector); do_issue_depth_test(); _state_mask.set_bit(depth_test_slot); } @@ -8246,7 +8353,7 @@ set_state_and_transform(const RenderState *target, int depth_write_slot = DepthWriteAttrib::get_class_slot(); if (_target_rs->get_attrib(depth_write_slot) != _state_rs->get_attrib(depth_write_slot) || !_state_mask.get_bit(depth_write_slot)) { - //PStatTimer timer(_draw_set_state_depth_write_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_depth_write_pcollector); do_issue_depth_write(); _state_mask.set_bit(depth_write_slot); } @@ -8254,7 +8361,7 @@ set_state_and_transform(const RenderState *target, int render_mode_slot = RenderModeAttrib::get_class_slot(); if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) || !_state_mask.get_bit(render_mode_slot)) { - //PStatTimer timer(_draw_set_state_render_mode_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_render_mode_pcollector); do_issue_render_mode(); _state_mask.set_bit(render_mode_slot); } @@ -8262,7 +8369,7 @@ set_state_and_transform(const RenderState *target, int rescale_normal_slot = RescaleNormalAttrib::get_class_slot(); if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) || !_state_mask.get_bit(rescale_normal_slot)) { - //PStatTimer timer(_draw_set_state_rescale_normal_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_rescale_normal_pcollector); do_issue_rescale_normal(); _state_mask.set_bit(rescale_normal_slot); } @@ -8270,7 +8377,7 @@ set_state_and_transform(const RenderState *target, int shade_model_slot = ShadeModelAttrib::get_class_slot(); if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) || !_state_mask.get_bit(shade_model_slot)) { - //PStatTimer timer(_draw_set_state_shade_model_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_shade_model_pcollector); do_issue_shade_model(); _state_mask.set_bit(shade_model_slot); } @@ -8284,9 +8391,9 @@ set_state_and_transform(const RenderState *target, !_state_mask.get_bit(transparency_slot) || !_state_mask.get_bit(color_write_slot) || !_state_mask.get_bit(color_blend_slot) || - (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) != + (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) != _state_shader->get_flag(ShaderAttrib::F_disable_alpha_write))) { - //PStatTimer timer(_draw_set_state_blending_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_blending_pcollector); do_issue_blending(); _state_mask.set_bit(transparency_slot); _state_mask.set_bit(color_write_slot); @@ -8294,7 +8401,7 @@ set_state_and_transform(const RenderState *target, } if (_target_shader != _state_shader) { - //PStatTimer timer(_draw_set_state_shader_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_shader_pcollector); #ifndef OPENGLES_1 do_issue_shader(true); #endif @@ -8310,7 +8417,7 @@ set_state_and_transform(const RenderState *target, int texture_slot = TextureAttrib::get_class_slot(); if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) || !_state_mask.get_bit(texture_slot)) { - //PStatTimer timer(_draw_set_state_texture_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_texture_pcollector); determine_target_texture(); int prev_active = _num_active_texture_stages; do_issue_texture(); @@ -8324,7 +8431,7 @@ set_state_and_transform(const RenderState *target, _state_texture = _target_texture; _state_mask.set_bit(texture_slot); } - + // If one of the previously-loaded TexGen modes modified the texture // matrix, then if either state changed, we have to change both of // them now. @@ -8343,7 +8450,7 @@ set_state_and_transform(const RenderState *target, int tex_matrix_slot = TexMatrixAttrib::get_class_slot(); if (_target_rs->get_attrib(tex_matrix_slot) != _state_rs->get_attrib(tex_matrix_slot) || !_state_mask.get_bit(tex_matrix_slot)) { - //PStatTimer timer(_draw_set_state_tex_matrix_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_tex_matrix_pcollector); do_issue_tex_matrix(); _state_mask.set_bit(tex_matrix_slot); } @@ -8351,7 +8458,7 @@ set_state_and_transform(const RenderState *target, int tex_gen_slot = TexGenAttrib::get_class_slot(); if (_target_tex_gen != _state_tex_gen || !_state_mask.get_bit(tex_gen_slot)) { - //PStatTimer timer(_draw_set_state_tex_gen_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_tex_gen_pcollector); do_issue_tex_gen(); _state_tex_gen = _target_tex_gen; _state_mask.set_bit(tex_gen_slot); @@ -8360,7 +8467,7 @@ set_state_and_transform(const RenderState *target, int material_slot = MaterialAttrib::get_class_slot(); if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) || !_state_mask.get_bit(material_slot)) { - //PStatTimer timer(_draw_set_state_material_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_material_pcollector); do_issue_material(); _state_mask.set_bit(material_slot); #ifndef OPENGLES_1 @@ -8373,7 +8480,7 @@ set_state_and_transform(const RenderState *target, int light_slot = LightAttrib::get_class_slot(); if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) || !_state_mask.get_bit(light_slot)) { - //PStatTimer timer(_draw_set_state_light_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_light_pcollector); do_issue_light(); _state_mask.set_bit(light_slot); } @@ -8381,7 +8488,7 @@ set_state_and_transform(const RenderState *target, int stencil_slot = StencilAttrib::get_class_slot(); if (_target_rs->get_attrib(stencil_slot) != _state_rs->get_attrib(stencil_slot) || !_state_mask.get_bit(stencil_slot)) { - //PStatTimer timer(_draw_set_state_stencil_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_stencil_pcollector); do_issue_stencil(); _state_mask.set_bit(stencil_slot); } @@ -8389,7 +8496,7 @@ set_state_and_transform(const RenderState *target, int fog_slot = FogAttrib::get_class_slot(); if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) || !_state_mask.get_bit(fog_slot)) { - //PStatTimer timer(_draw_set_state_fog_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_fog_pcollector); do_issue_fog(); _state_mask.set_bit(fog_slot); #ifndef OPENGLES_1 @@ -8402,7 +8509,7 @@ set_state_and_transform(const RenderState *target, int scissor_slot = ScissorAttrib::get_class_slot(); if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) || !_state_mask.get_bit(scissor_slot)) { - //PStatTimer timer(_draw_set_state_scissor_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_scissor_pcollector); do_issue_scissor(); _state_mask.set_bit(scissor_slot); } @@ -8547,7 +8654,7 @@ update_standard_texture_bindings() { show_stage_index = i; } } - + if (show_stage_index >= 0) { update_show_usage_texture_bindings(show_stage_index); return; @@ -8594,7 +8701,7 @@ update_standard_texture_bindings() { // Something wrong with this texture; skip it. continue; } - + #ifndef OPENGLES_2 // Then, turn on the current texture mode. GLenum target = get_texture_target(texture->get_texture_type()); @@ -8610,7 +8717,7 @@ update_standard_texture_bindings() { #endif glEnable(target); #endif - + if (!update_texture(tc, false)) { #ifndef OPENGLES_2 glDisable(target); @@ -8618,7 +8725,7 @@ update_standard_texture_bindings() { continue; } apply_texture(tc); - + if (stage->involves_color_scale() && _color_scale_enabled) { LColor color = stage->get_color(); color.set(color[0] * _current_color_scale[0], @@ -8630,7 +8737,7 @@ update_standard_texture_bindings() { } else { call_glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, stage->get_color()); } - + if (stage->get_mode() == TextureStage::M_decal) { if (texture->get_num_components() < 3 && _supports_texture_combine) { // Make a special case for 1- and 2-channel decal textures. @@ -8646,12 +8753,12 @@ update_standard_texture_bindings() { glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); - + } else { // Normal 3- and 4-channel decal textures. glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); } - + } else if (stage->get_mode() == TextureStage::M_combine) { if (!_supports_texture_combine) { GLCAT.warning() @@ -8664,7 +8771,7 @@ update_standard_texture_bindings() { glTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, stage->get_alpha_scale()); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, get_texture_combine_type(stage->get_combine_rgb_mode())); - + switch (stage->get_num_combine_rgb_operands()) { case 3: glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, @@ -8673,7 +8780,7 @@ update_standard_texture_bindings() { glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, get_texture_operand_type(stage->get_combine_rgb_operand2())); // fall through - + case 2: glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, get_texture_src_type(stage->get_combine_rgb_source1(), @@ -8681,7 +8788,7 @@ update_standard_texture_bindings() { glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, get_texture_operand_type(stage->get_combine_rgb_operand1())); // fall through - + case 1: glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, get_texture_src_type(stage->get_combine_rgb_source0(), @@ -8689,13 +8796,13 @@ update_standard_texture_bindings() { glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, get_texture_operand_type(stage->get_combine_rgb_operand0())); // fall through - + default: break; } glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, get_texture_combine_type(stage->get_combine_alpha_mode())); - + switch (stage->get_num_combine_alpha_operands()) { case 3: glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, @@ -8740,7 +8847,7 @@ update_standard_texture_bindings() { last_stage = i; } } - + #ifndef OPENGLES_2 // Disable the texture stages that are no longer used. for (i = num_stages; i < _num_active_texture_stages; i++) { @@ -8759,10 +8866,10 @@ update_standard_texture_bindings() { } } #endif // OPENGLES_2 - + // Save the count of texture stages for next time. _num_active_texture_stages = num_stages; - + report_my_gl_errors(); #endif } @@ -8827,7 +8934,7 @@ update_show_usage_texture_bindings(int show_stage_index) { } } #endif - + // Save the count of texture stages for next time. _num_active_texture_stages = num_stages; @@ -8911,7 +9018,7 @@ upload_usage_texture(int width, int height) { // A simple union to store the colors values bytewise, and get the // answer wordwise, independently of machine byte-ordernig. union { - struct { + struct { unsigned char r, g, b, a; } b; PN_uint32 w; @@ -8927,7 +9034,7 @@ upload_usage_texture(int width, int height) { for (int p = 0; p < num_pixels; ++p) { buffer[p] = store.w; } - + glTexImage2D(GL_TEXTURE_2D, n, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); if (width == 1 && height == 1) { @@ -8968,9 +9075,9 @@ disable_standard_texture_bindings() { glDisable(GL_TEXTURE_CUBE_MAP); } } - + _num_active_texture_stages = 0; - + report_my_gl_errors(); #endif // OPENGLES_2 } @@ -9446,6 +9553,8 @@ apply_texture(TextureContext *tc) { //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: upload_texture(CLP(TextureContext) *gtc, bool force) { + PStatGPUTimer timer(this, _load_texture_pcollector); + Texture *tex = gtc->get_texture(); if (_effective_incomplete_render && !force) { @@ -9557,7 +9666,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force) { // reduce the texture at load time instead; of course, the user // doesn't always know ahead of time what the hardware limits are. - if ((max_dimension_x > 0 && max_dimension_y > 0 && max_dimension_z > 0) && + if ((max_dimension_x > 0 && max_dimension_y > 0 && max_dimension_z > 0) && image_compression == Texture::CM_off) { while (tex->get_expected_mipmap_x_size(mipmap_bias) > max_dimension_x || tex->get_expected_mipmap_y_size(mipmap_bias) > max_dimension_y || @@ -9633,12 +9742,18 @@ upload_texture(CLP(TextureContext) *gtc, bool force) { } if (needs_reload) { + if (_use_object_labels) { + // This seems like a good time to assign a label for the debug messages. + const string &name = tex->get_name(); + _glObjectLabel(GL_TEXTURE, gtc->_index, name.size(), name.data()); + } + + // Figure out whether mipmaps will be generated by the GPU or by + // Panda (or not at all), and how many mipmap levels should be created. gtc->_generate_mipmaps = false; int num_levels = 1; CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias); - // Figure out whether mipmaps will be generated by the GPU or by - // Panda (or not at all), and how many mipmap levels should be created. if (image.is_null()) { if (uses_mipmaps) { if (_supports_generate_mipmap) { @@ -9657,7 +9772,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force) { if (num_levels <= 1) { // No RAM mipmap levels available. Should we generate some? - if (!_supports_generate_mipmap || !driver_generate_mipmaps || + if (!_supports_generate_mipmap || !driver_generate_mipmaps || image_compression != Texture::CM_off) { // Yes, the GL can't or won't generate them, so we need to. // Note that some drivers (nVidia) will *corrupt memory* if @@ -9739,7 +9854,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force) { if (!image.is_null() && uses_mipmaps) { if (tex->get_num_ram_mipmap_images() - mipmap_bias <= 1) { // No RAM mipmap levels available. Should we generate some? - if (!_supports_generate_mipmap || !driver_generate_mipmaps || + if (!_supports_generate_mipmap || !driver_generate_mipmaps || image_compression != Texture::CM_off) { // Yes, the GL can't or won't generate them, so we need to. // Note that some drivers (nVidia) will *corrupt memory* if @@ -9894,7 +10009,6 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, return false; } - PStatTimer timer(_load_texture_pcollector); Texture *tex = gtc->get_texture(); nassertr(tex != (Texture *)NULL, false); @@ -10251,7 +10365,7 @@ bool CLP(GraphicsStateGuardian):: upload_simple_texture(CLP(TextureContext) *gtc) { report_my_gl_errors(); - PStatTimer timer(_load_texture_pcollector); + PStatGPUTimer timer(this, _load_texture_pcollector); Texture *tex = gtc->get_texture(); nassertr(tex != (Texture *)NULL, false); @@ -10664,7 +10778,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { case GL_R32F: type = Texture::T_float; format = Texture::F_r32; - break; + break; #endif #ifndef OPENGLES @@ -11097,7 +11211,7 @@ get_supports_cg_profile(const string &name) const { CGprofile profile = cgGetProfile(name.c_str()); if (profile == CG_PROFILE_UNKNOWN) { - GLCAT.error() << name <<", unknown Cg-profile\n"; + GLCAT.error() << name << ", unknown Cg-profile\n"; return false; } return (cgGLIsProfileSupported(profile) != 0); @@ -11111,6 +11225,12 @@ get_supports_cg_profile(const string &name) const { //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: bind_fbo(GLuint fbo) { + if (_current_fbo == fbo) { + return; + } + + PStatGPUTimer timer(this, _fbo_bind_pcollector); + nassertv(_glBindFramebuffer != 0); #if defined(OPENGLES_2) _glBindFramebuffer(GL_FRAMEBUFFER, fbo); @@ -11119,6 +11239,7 @@ bind_fbo(GLuint fbo) { #else _glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); #endif + _current_fbo = fbo; } @@ -11149,7 +11270,7 @@ static int gl_stencil_operations_array [ ] = { GL_DECR_WRAP, #endif GL_INVERT, - + GL_INCR, GL_DECR, }; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index e7cfa3001c..b47c22ce48 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -1,6 +1,6 @@ // Filename: glGraphicsStateGuardian_src.h // Created by: drose (02Feb99) -// Updated by: fperazzi, PandaSE (05May10) (added +// Updated by: fperazzi, PandaSE (05May10) (added // get_supports_cg_profile) // //////////////////////////////////////////////////////////////////// @@ -37,6 +37,7 @@ #include "pmap.h" #include "geomVertexArrayData.h" #include "lightMutex.h" +#include "pStatGPUTimer.h" class PlaneNode; class Light; @@ -58,6 +59,7 @@ typedef double GLdouble; typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); @@ -65,6 +67,8 @@ typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *params); typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); // There is some trivial disagreement between different gl.h headers about this one, so we use our own typename. @@ -98,32 +102,32 @@ typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); -typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); -typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); -typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); -typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); -typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); -typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); -typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); -typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); -typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); -typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); -typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); -typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); #ifdef OPENGLES_2 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); #else -typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); #endif typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); @@ -189,7 +193,7 @@ typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary); -typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEPROC) (GLuint texture); typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEPROC) (GLuint texture, GLuint sampler); typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTPROC) (GLuint64 handle); @@ -228,7 +232,7 @@ public: virtual int get_driver_version_minor(); virtual int get_driver_shader_version_major(); virtual int get_driver_shader_version_minor(); - + #ifndef OPENGLES_1 static void debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, GLvoid *userParam); #endif @@ -302,13 +306,15 @@ public: virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); + virtual PT(TimerQueryContext) issue_timer_query(int pstats_index); + virtual void dispatch_compute(int size_x, int size_y, int size_z); virtual PT(GeomMunger) make_geom_munger(const RenderState *state, Thread *current_thread); virtual void clear(DrawableRegion *region); - + virtual bool framebuffer_copy_to_texture (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram @@ -332,9 +338,9 @@ public: void draw_immediate_composite_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode); #endif // SUPPORT_IMMEDIATE_MODE - INLINE static bool report_errors(int line, const char *source_file); + INLINE bool report_errors(int line, const char *source_file); INLINE void report_my_errors(int line, const char *source_file); - INLINE static bool clear_errors(int line, const char *source_file); + INLINE bool clear_errors(int line, const char *source_file); INLINE void clear_my_errors(int line, const char *source_file); INLINE const string &get_gl_vendor() const; @@ -475,8 +481,8 @@ protected: bool upload_texture(CLP(TextureContext) *gtc, bool force); bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, bool uses_mipmaps, int mipmap_bias, - GLenum texture_target, GLenum page_target, - GLint internal_format, GLint external_format, + GLenum texture_target, GLenum page_target, + GLint internal_format, GLint external_format, GLenum component_type, bool one_page_only, int z, Texture::CompressionMode image_compression); @@ -590,7 +596,6 @@ protected: public: bool _supports_point_parameters; PFNGLPOINTPARAMETERFVPROC _glPointParameterfv; - bool _supports_point_sprite; bool _supports_vertex_blend; @@ -703,6 +708,12 @@ public: PFNGLGETQUERYIVPROC _glGetQueryiv; PFNGLGETQUERYOBJECTUIVPROC _glGetQueryObjectuiv; + PFNGLQUERYCOUNTERPROC _glQueryCounter; + PFNGLGETQUERYOBJECTI64VPROC _glGetQueryObjecti64v; + PFNGLGETQUERYOBJECTUI64VPROC _glGetQueryObjectui64v; + + PFNGLGETINTEGER64VPROC _glGetInteger64v; + PFNGLACTIVESTENCILFACEEXTPROC _glActiveStencilFaceEXT; #ifndef OPENGLES_1 @@ -778,9 +789,9 @@ public: #endif LightMutex _lock; - typedef pvector DeletedDisplayLists; - DeletedDisplayLists _deleted_display_lists; - DeletedDisplayLists _deleted_queries; + typedef pvector DeletedNames; + DeletedNames _deleted_display_lists; + DeletedNames _deleted_queries; #ifndef OPENGLES // Stores textures for which memory bariers should be issued. @@ -797,6 +808,9 @@ public: bool _force_flush; bool _supports_debug; + bool _use_object_labels; + PFNGLOBJECTLABELPROC _glObjectLabel; + #ifndef NDEBUG bool _show_texture_usage; int _show_texture_usage_max_size; @@ -818,7 +832,11 @@ public: static PStatCollector _primitive_batches_display_list_pcollector; static PStatCollector _vertices_display_list_pcollector; static PStatCollector _vertices_immediate_pcollector; - static PStatCollector _compute_dispatch_pcollector; + static PStatCollector _memory_barrier_pcollector; + static PStatCollector _vertex_array_update_pcollector; + static PStatCollector _texture_update_pcollector; + static PStatCollector _fbo_bind_pcollector; + static PStatCollector _check_error_pcollector; public: virtual TypeHandle get_type() const { @@ -846,6 +864,7 @@ private: friend class CLP(CgShaderContext); friend class CLP(GraphicsBuffer); friend class CLP(OcclusionQueryContext); + friend class CLP(TimerQueryContext); }; #include "glGraphicsStateGuardian_src.I" diff --git a/panda/src/glstuff/glLatencyQueryContext_src.I b/panda/src/glstuff/glLatencyQueryContext_src.I new file mode 100644 index 0000000000..d87b0c6263 --- /dev/null +++ b/panda/src/glstuff/glLatencyQueryContext_src.I @@ -0,0 +1,14 @@ +// Filename: glLatencyQueryContext_src.I +// Created by: rdb (24Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/glstuff/glLatencyQueryContext_src.cxx b/panda/src/glstuff/glLatencyQueryContext_src.cxx new file mode 100644 index 0000000000..b99217f282 --- /dev/null +++ b/panda/src/glstuff/glLatencyQueryContext_src.cxx @@ -0,0 +1,54 @@ +// Filename: glLatencyQueryContext_src.cxx +// Created by: rdb (24Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// 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 OPENGLES // Timer queries not supported by OpenGL ES. + +TypeHandle CLP(LatencyQueryContext)::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CLP(LatencyQueryContext)::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CLP(LatencyQueryContext):: +CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, + int pstats_index) : + CLP(TimerQueryContext)(glgsg, pstats_index), + _timestamp(0) +{ + glgsg->_glGetInteger64v(GL_TIMESTAMP, &_timestamp); +} + +//////////////////////////////////////////////////////////////////// +// Function: LatencyQueryContext::get_timestamp +// Access: Public, Virtual +// Description: Returns the timestamp that is the result of this +// timer query. There's no guarantee about which +// clock this uses, the only guarantee is that +// subtracting a start time from an end time should +// yield a time in seconds. +// If is_answer_ready() did not return true, this +// function may block before it returns. +// +// It is only valid to call this from the draw thread. +//////////////////////////////////////////////////////////////////// +double CLP(LatencyQueryContext):: +get_timestamp() const { + GLint64 time_ns; + _glgsg->_glGetQueryObjecti64v(_index, GL_QUERY_RESULT, &time_ns); + + return (time_ns - _timestamp) * 0.000000001; +} + +#endif // OPENGLES diff --git a/panda/src/glstuff/glLatencyQueryContext_src.h b/panda/src/glstuff/glLatencyQueryContext_src.h new file mode 100644 index 0000000000..e2c5eae4e3 --- /dev/null +++ b/panda/src/glstuff/glLatencyQueryContext_src.h @@ -0,0 +1,57 @@ +// Filename: glLatencyQueryContext_src.h +// Created by: rdb (24Sep14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +class GraphicsStateGuardian; + +#ifndef OPENGLES // Timer queries not supported by OpenGL ES. + +//////////////////////////////////////////////////////////////////// +// Class : GLLatencyQueryContext +// Description : This is a special variant of GLTimerQueryContext +// that measures the command latency, ie. the time +// it takes for the GPU to actually get to the commands +// we are issuing right now. +//////////////////////////////////////////////////////////////////// +class EXPCL_GL CLP(LatencyQueryContext) : public CLP(TimerQueryContext) { +public: + CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index); + + ALLOC_DELETED_CHAIN(CLP(LatencyQueryContext)); + + virtual double get_timestamp() const; + + GLint64 _timestamp; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CLP(TimerQueryContext)::init_type(); + register_type(_type_handle, CLASSPREFIX_QUOTED "LatencyQueryContext", + CLP(TimerQueryContext)::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glLatencyQueryContext_src.I" + +#endif // OPENGLES + diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index f8dadb991f..d13da80863 100755 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -16,7 +16,7 @@ #ifndef OPENGLES_1 -#include "pStatTimer.h" +#include "pStatGPUTimer.h" TypeHandle CLP(ShaderContext)::_type_handle; @@ -35,19 +35,19 @@ TypeHandle CLP(ShaderContext)::_type_handle; // Access: Public // Description: The Panda CG shader syntax defines a useful set of shorthand notations for setting nodepath // properties as shaderinputs. For example, float4 mspos_XXX refers to nodepath XXX's position -// in model space. This function is a rough attempt to reimplement some of the shorthand +// in model space. This function is a rough attempt to reimplement some of the shorthand // notations for GLSL. The code is ~99% composed of excerpts dealing with matrix shaderinputs -// from Shader::compile_parameter. -// -// Given a uniform variable name queried from the compiled shader passed in via arg_id, +// from Shader::compile_parameter. +// +// Given a uniform variable name queried from the compiled shader passed in via arg_id, // 1) parse the name // 2a) if the name refers to a Panda shorthand notation // push the appropriate matrix into shader._mat_spec // returns True // 2b) If the name doesn't refer to a Panda shorthand notation // returns False -// -// The boolean return is used to notify down-river processing whether the shader var/parm was +// +// The boolean return is used to notify down-river processing whether the shader var/parm was // actually picked up and the appropriate ShaderMatSpec pushed onto _mat_spec. //////////////////////////////////////////////////////////////////// bool CLP(ShaderContext):: @@ -700,7 +700,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext } } } - + // Now we've processed the uniforms, we'll process the attribs. _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, ¶m_count); _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, ¶m_maxlength); @@ -836,7 +836,7 @@ release_resources() { } _glsl_shaders.clear(); - + _glgsg->report_my_gl_errors(); } @@ -890,7 +890,7 @@ unbind() { //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: issue_parameters(int altered) { - PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector); + //PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector); if (!valid()) { return; @@ -1374,12 +1374,11 @@ glsl_report_shader_errors(GLuint shader) { _glgsg->_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); if (length > 1) { - info_log = (char *) malloc(length); + info_log = (char *) alloca(length); _glgsg->_glGetShaderInfoLog(shader, length, &num_chars, info_log); if (strcmp(info_log, "Success.\n") != 0 && strcmp(info_log, "No errors.\n") != 0) { GLCAT.error(false) << info_log << "\n"; } - free(info_log); } } @@ -1397,19 +1396,18 @@ glsl_report_program_errors(GLuint program) { _glgsg->_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); if (length > 1) { - info_log = (char *) malloc(length); + info_log = (char *) alloca(length); _glgsg->_glGetProgramInfoLog(program, length, &num_chars, info_log); if (strcmp(info_log, "Success.\n") != 0 && strcmp(info_log, "No errors.\n") != 0) { GLCAT.error(false) << info_log << "\n"; } - free(info_log); } } //////////////////////////////////////////////////////////////////// // Function: Shader::glsl_compile_shader // Access: Private -// Description: +// Description: //////////////////////////////////////////////////////////////////// bool CLP(ShaderContext):: glsl_compile_shader(Shader::ShaderType type) { @@ -1451,16 +1449,21 @@ glsl_compile_shader(Shader::ShaderType type) { return false; } + if (_glgsg->_use_object_labels) { + string name = _shader->get_filename(type); + _glgsg->_glObjectLabel(GL_SHADER, handle, name.size(), name.data()); + } + string text_str = _shader->get_text(type); const char* text = text_str.c_str(); _glgsg->_glShaderSource(handle, 1, &text, NULL); _glgsg->_glCompileShader(handle); GLint status; _glgsg->_glGetShaderiv(handle, GL_COMPILE_STATUS, &status); - + if (status != GL_TRUE) { - GLCAT.error() - << "An error occurred while compiling shader " + GLCAT.error() + << "An error occurred while compiling shader " << _shader->get_filename(type) << "\n"; glsl_report_shader_errors(handle); _glgsg->_glDeleteShader(handle); @@ -1485,6 +1488,12 @@ glsl_compile_and_link() { if (!_glsl_program) { return false; } + + if (_glgsg->_use_object_labels) { + string name = _shader->get_filename(); + _glgsg->_glObjectLabel(GL_PROGRAM, _glsl_program, name.size(), name.data()); + } + bool valid = true; if (!_shader->get_text(Shader::ST_vertex).empty()) { @@ -1507,7 +1516,7 @@ glsl_compile_and_link() { nassertr(_glgsg->_glProgramParameteri != NULL, false); GLint max_vertices; glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &max_vertices); - _glgsg->_glProgramParameteri(_glsl_program, GL_GEOMETRY_VERTICES_OUT_ARB, max_vertices); + _glgsg->_glProgramParameteri(_glsl_program, GL_GEOMETRY_VERTICES_OUT_ARB, max_vertices); } #endif diff --git a/panda/src/glstuff/glTimerQueryContext_src.I b/panda/src/glstuff/glTimerQueryContext_src.I new file mode 100644 index 0000000000..594cf90eea --- /dev/null +++ b/panda/src/glstuff/glTimerQueryContext_src.I @@ -0,0 +1,28 @@ +// Filename: glTimerQueryContext_src.I +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CLP(TimerQueryContext)::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CLP(TimerQueryContext):: +CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg, + int pstats_index) : + TimerQueryContext(pstats_index), + _glgsg(glgsg), + _index(0) +{ +} diff --git a/panda/src/glstuff/glTimerQueryContext_src.cxx b/panda/src/glstuff/glTimerQueryContext_src.cxx new file mode 100644 index 0000000000..59c5f874a8 --- /dev/null +++ b/panda/src/glstuff/glTimerQueryContext_src.cxx @@ -0,0 +1,104 @@ +// Filename: glTimerQueryContext_src.cxx +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "pnotify.h" +#include "dcast.h" +#include "lightMutexHolder.h" +#include "pStatTimer.h" + +#ifndef OPENGLES // Timer queries not supported by OpenGL ES. + +TypeHandle CLP(TimerQueryContext)::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: GLTimerQueryContext::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CLP(TimerQueryContext):: +~CLP(TimerQueryContext)() { + if (_index != 0) { + // Tell the GSG to recycle this index when it gets around to it. + LightMutexHolder holder(_glgsg->_lock); + _glgsg->_deleted_queries.push_back(_index); + _index = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GLTimerQueryContext::is_answer_ready +// Access: Public, Virtual +// Description: Returns true if the query's answer is ready, false +// otherwise. If this returns false, the application +// must continue to poll until it returns true. +// +// It is only valid to call this from the draw thread. +//////////////////////////////////////////////////////////////////// +bool CLP(TimerQueryContext):: +is_answer_ready() const { + GLuint result; + _glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT_AVAILABLE, &result); + + return (result != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: GLTimerQueryContext::waiting_for_answer +// Access: Public, Virtual +// Description: Requests the graphics engine to expedite the pending +// answer--the application is now waiting until the +// answer is ready. +// +// It is only valid to call this from the draw thread. +//////////////////////////////////////////////////////////////////// +void CLP(TimerQueryContext):: +waiting_for_answer() { + PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector); + glFlush(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TimerQueryContext::get_timestamp +// Access: Public, Virtual +// Description: Returns the timestamp that is the result of this +// timer query. There's no guarantee about which +// clock this uses, the only guarantee is that +// subtracting a start time from an end time should +// yield a time in seconds. +// If is_answer_ready() did not return true, this +// function may block before it returns. +// +// It is only valid to call this from the draw thread. +//////////////////////////////////////////////////////////////////// +double CLP(TimerQueryContext):: +get_timestamp() const { + GLuint64 time_ns; + + /*GLuint available; + _glgsg->_glGetQueryObjectuiv(_index[1], GL_QUERY_RESULT_AVAILABLE, &available); + if (available) { + // The answer is ready now. + do_get_timestamps(begin_ns, end_ns); + } else { + // The answer is not ready; this call will block. + PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector); + do_get_timestamps(begin_ns, end_ns); + }*/ + + _glgsg->_glGetQueryObjectui64v(_index, GL_QUERY_RESULT, &time_ns); + + return time_ns * 0.000000001; +} + +#endif // OPENGLES diff --git a/panda/src/glstuff/glTimerQueryContext_src.h b/panda/src/glstuff/glTimerQueryContext_src.h new file mode 100644 index 0000000000..ce4248ffd1 --- /dev/null +++ b/panda/src/glstuff/glTimerQueryContext_src.h @@ -0,0 +1,68 @@ +// Filename: glTimerQueryContext_src.h +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" +#include "timerQueryContext.h" +#include "deletedChain.h" +#include "clockObject.h" + +class GraphicsStateGuardian; + +#ifndef OPENGLES // Timer queries not supported by OpenGL ES. + +//////////////////////////////////////////////////////////////////// +// Class : GLTimerQueryContext +// Description : This class manages a timer query that can be used +// by a PStatGPUTimer to measure the time a task takes +// to execute on the GPU. +// This records the current timestamp; a pair of these +// is usually used to get the elapsed time. +//////////////////////////////////////////////////////////////////// +class EXPCL_GL CLP(TimerQueryContext) : public TimerQueryContext { +public: + INLINE CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg, + int pstats_index); + virtual ~CLP(TimerQueryContext)(); + + ALLOC_DELETED_CHAIN(CLP(TimerQueryContext)); + + virtual bool is_answer_ready() const; + virtual void waiting_for_answer(); + virtual double get_timestamp() const; + + GLuint _index; + CLP(GraphicsStateGuardian) *_glgsg; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TimerQueryContext::init_type(); + register_type(_type_handle, CLASSPREFIX_QUOTED "TimerQueryContext", + TimerQueryContext::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "glTimerQueryContext_src.I" + +#endif // OPENGLES + diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index d01deb3da7..3c1d8a2b86 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -151,6 +151,13 @@ ConfigVariableEnum gl_debug_abort_level "that triggered the error message. " "This feature is not available when NDEBUG has been defined.")); +ConfigVariableBool gl_debug_object_labels + ("gl-debug-object-labels", true, + PRC_DESC("When gl-debug is set to true, this will tell OpenGL the " + "name of textures, shaders, and other objects, so that OpenGL " + "can display those in error messages. There's usually no " + "reason to disable this.")); + ConfigVariableBool gl_debug_buffers ("gl-debug-buffers", false, PRC_DESC("Set this true, in addition to enabling debug notify for " @@ -162,7 +169,8 @@ ConfigVariableBool gl_finish PRC_DESC("Set this true to force a call to glFinish() after every major " "graphics operation. This is likely to slow down rendering " "performance substantially, but it will make PStats graphs " - "more accurately reflect where the graphics bottlenecks are. " + "more accurately reflect where the graphics bottlenecks are, " + "although it is better to use timer queries when available. " "This variable is enabled only if PStats is compiled in.")); ConfigVariableBool gl_force_depth_stencil @@ -252,6 +260,8 @@ void CLP(init_classes)() { #ifndef OPENGLES CLP(OcclusionQueryContext)::init_type(); + CLP(TimerQueryContext)::init_type(); + CLP(LatencyQueryContext)::init_type(); #endif PandaSystem *ps = PandaSystem::get_global_ptr(); diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index 1d7e330f83..c57046ee27 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -58,6 +58,7 @@ extern ConfigVariableEnum gl_min_buffer_usage_hint; extern ConfigVariableBool gl_debug; extern ConfigVariableBool gl_debug_synchronous; extern ConfigVariableEnum gl_debug_abort_level; +extern ConfigVariableBool gl_debug_object_labels; extern ConfigVariableBool gl_debug_buffers; extern ConfigVariableBool gl_finish; extern ConfigVariableBool gl_force_depth_stencil; diff --git a/panda/src/glstuff/glstuff_src.cxx b/panda/src/glstuff/glstuff_src.cxx index 49d54804b8..518162a53a 100644 --- a/panda/src/glstuff/glstuff_src.cxx +++ b/panda/src/glstuff/glstuff_src.cxx @@ -22,6 +22,8 @@ #include "glVertexBufferContext_src.cxx" #include "glIndexBufferContext_src.cxx" #include "glOcclusionQueryContext_src.cxx" +#include "glTimerQueryContext_src.cxx" +#include "glLatencyQueryContext_src.cxx" #include "glGeomContext_src.cxx" #include "glGeomMunger_src.cxx" #include "glShaderContext_src.cxx" diff --git a/panda/src/glstuff/glstuff_src.h b/panda/src/glstuff/glstuff_src.h index 0892a8bba3..cb0bcb4941 100644 --- a/panda/src/glstuff/glstuff_src.h +++ b/panda/src/glstuff/glstuff_src.h @@ -36,6 +36,8 @@ #include "glVertexBufferContext_src.h" #include "glIndexBufferContext_src.h" #include "glOcclusionQueryContext_src.h" +#include "glTimerQueryContext_src.h" +#include "glLatencyQueryContext_src.h" #include "glGeomContext_src.h" #include "glGeomMunger_src.h" #include "glShaderContext_src.h" diff --git a/panda/src/glstuff/panda_glext.h b/panda/src/glstuff/panda_glext.h index 3af15892cd..6af0dacb1e 100644 --- a/panda/src/glstuff/panda_glext.h +++ b/panda/src/glstuff/panda_glext.h @@ -6,7 +6,7 @@ extern "C" { /* ** Copyright (c) 2007-2012 The Khronos Group Inc. -** +** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including @@ -14,10 +14,10 @@ extern "C" { ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: -** +** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. -** +** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp index 547c12cea6..a79670dd6d 100644 --- a/panda/src/gobj/Sources.pp +++ b/panda/src/gobj/Sources.pp @@ -66,6 +66,7 @@ textureReloadRequest.I textureReloadRequest.h \ textureStage.I textureStage.h \ textureStagePool.I textureStagePool.h \ + timerQueryContext.I timerQueryContext.h \ transformBlend.I transformBlend.h \ transformBlendTable.I transformBlendTable.h \ transformTable.I transformTable.h \ @@ -136,6 +137,7 @@ textureReloadRequest.cxx \ textureStage.cxx \ textureStagePool.cxx \ + timerQueryContext.cxx \ transformBlend.cxx \ transformBlendTable.cxx \ transformTable.cxx \ @@ -208,6 +210,7 @@ textureReloadRequest.I textureReloadRequest.h \ textureStage.I textureStage.h \ textureStagePool.I textureStagePool.h \ + timerQueryContext.I timerQueryContext.h \ transformBlend.I transformBlend.h \ transformBlendTable.I transformBlendTable.h \ transformTable.I transformTable.h \ diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 9357816d67..1eea586b13 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -44,6 +44,7 @@ #include "textureReloadRequest.h" #include "textureStage.h" #include "textureContext.h" +#include "timerQueryContext.h" #include "shader.h" #include "shaderContext.h" #include "transformBlend.h" @@ -92,7 +93,7 @@ ConfigVariableInt texture_scale_limit "to both X and Y.")); ConfigVariableList exclude_texture_scale -("exclude-texture-scale", +("exclude-texture-scale", PRC_DESC("This is a list of glob patterns for texture filenames " "(excluding the directory part of the filename, but including " "the extension); for instance, 'digits_*.png'. Any texture " @@ -287,7 +288,7 @@ ConfigVariableInt vertex_column_alignment "this alignment for the vertex animation columns only.")); ConfigVariableBool vertex_animation_align_16 -("vertex-animation-align-16", +("vertex-animation-align-16", #ifdef LINMATH_ALIGN true, #else @@ -343,7 +344,7 @@ ConfigVariableInt simple_image_size ConfigVariableDouble simple_image_threshold ("simple-image-threshold", 0.1, - PRC_DESC("This is a value that indicates how closely a texture's " + PRC_DESC("This is a value that indicates how closely a texture's " "generated simple " "image should approximate the original image. The smaller the " "number, the closer the match; small numbers will result in " @@ -568,6 +569,7 @@ ConfigureFn(config_gobj) { TexturePoolFilter::init_type(); TextureReloadRequest::init_type(); TextureStage::init_type(); + TimerQueryContext::init_type(); TransformBlend::init_type(); TransformBlendTable::init_type(); TransformTable::init_type(); diff --git a/panda/src/gobj/p3gobj_composite1.cxx b/panda/src/gobj/p3gobj_composite1.cxx index c758d30a00..a584185f68 100644 --- a/panda/src/gobj/p3gobj_composite1.cxx +++ b/panda/src/gobj/p3gobj_composite1.cxx @@ -30,3 +30,6 @@ #include "indexBufferContext.cxx" #include "internalName.cxx" #include "lens.cxx" +#include "material.cxx" +#include "materialPool.cxx" +#include "matrixLens.cxx" diff --git a/panda/src/gobj/p3gobj_composite2.cxx b/panda/src/gobj/p3gobj_composite2.cxx index 0238604980..d25852b69c 100644 --- a/panda/src/gobj/p3gobj_composite2.cxx +++ b/panda/src/gobj/p3gobj_composite2.cxx @@ -1,6 +1,3 @@ -#include "material.cxx" -#include "materialPool.cxx" -#include "matrixLens.cxx" #include "occlusionQueryContext.cxx" #include "orthographicLens.cxx" #include "perspectiveLens.cxx" @@ -21,6 +18,7 @@ #include "textureReloadRequest.cxx" #include "textureStage.cxx" #include "textureStagePool.cxx" +#include "timerQueryContext.cxx" #include "transformBlend.cxx" #include "transformBlendTable.cxx" #include "transformTable.cxx" diff --git a/panda/src/gobj/timerQueryContext.I b/panda/src/gobj/timerQueryContext.I new file mode 100644 index 0000000000..be86483329 --- /dev/null +++ b/panda/src/gobj/timerQueryContext.I @@ -0,0 +1,26 @@ +// Filename: occlusionQueryContext.I +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: TimerQueryContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TimerQueryContext:: +TimerQueryContext(int pstats_index) : + _pstats_index(pstats_index), + _frame_index(ClockObject::get_global_clock()->get_frame_count()) +{ +} diff --git a/panda/src/gobj/timerQueryContext.cxx b/panda/src/gobj/timerQueryContext.cxx new file mode 100644 index 0000000000..c19131f5fc --- /dev/null +++ b/panda/src/gobj/timerQueryContext.cxx @@ -0,0 +1,35 @@ +// Filename: timerQueryContext.cxx +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + +#include "timerQueryContext.h" + +TypeHandle TimerQueryContext::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: TimerQueryContext::get_timestamp +// Access: Public, Virtual +// Description: Returns the timestamp that is the result of this +// timer query. There's no guarantee about which +// clock this uses, the only guarantee is that +// subtracting a start time from an end time should +// yield a time in seconds. +// If is_answer_ready() did not return true, this +// function may block before it returns. +// +// It is only valid to call this from the draw thread. +//////////////////////////////////////////////////////////////////// +double TimerQueryContext:: +get_timestamp() const { + return 0.0; +} diff --git a/panda/src/gobj/timerQueryContext.h b/panda/src/gobj/timerQueryContext.h new file mode 100644 index 0000000000..429dc65917 --- /dev/null +++ b/panda/src/gobj/timerQueryContext.h @@ -0,0 +1,60 @@ +// Filename: timerQueryContext.h +// Created by: rdb (22Aug14) +// +//////////////////////////////////////////////////////////////////// +// +// 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 TIMERQUERYCONTEXT_H +#define TIMERQUERYCONTEXT_H + +#include "pandabase.h" +#include "queryContext.h" +#include "clockObject.h" +#include "pStatCollector.h" + +//////////////////////////////////////////////////////////////////// +// Class : TimerQueryContext +// Description : +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_GOBJ TimerQueryContext : public QueryContext { +public: + INLINE TimerQueryContext(int pstats_index); + + ALLOC_DELETED_CHAIN(TimerQueryContext); + + virtual double get_timestamp() const=0; + + int _frame_index; + int _pstats_index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + QueryContext::init_type(); + register_type(_type_handle, "TimerQueryContext", + QueryContext::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class PreparedGraphicsObjects; +}; + +#include "timerQueryContext.I" + +#endif diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 33e3793edd..32022ebe9a 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -32,7 +32,6 @@ class GraphicsOutputBase; class VertexBufferContext; class IndexBufferContext; -class OcclusionQueryContext; class GeomContext; class GeomNode; class Geom; @@ -123,7 +122,6 @@ PUBLISHED: virtual bool get_supports_multisample() const=0; virtual int get_supported_geom_rendering() const=0; - virtual bool get_supports_occlusion_query() const=0; virtual bool get_supports_shadow_filter() const=0; public: @@ -153,16 +151,13 @@ public: virtual ShaderContext *prepare_shader(Shader *shader)=0; virtual void release_shader(ShaderContext *sc)=0; - + virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data)=0; virtual void release_vertex_buffer(VertexBufferContext *vbc)=0; virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0; virtual void release_index_buffer(IndexBufferContext *ibc)=0; - virtual void begin_occlusion_query()=0; - virtual PT(OcclusionQueryContext) end_occlusion_query()=0; - virtual void dispatch_compute(int size_x, int size_y, int size_z)=0; virtual PT(GeomMunger) get_geom_munger(const RenderState *state, diff --git a/panda/src/pstatclient/config_pstats.cxx b/panda/src/pstatclient/config_pstats.cxx index 7a78718c86..a529dba637 100644 --- a/panda/src/pstatclient/config_pstats.cxx +++ b/panda/src/pstatclient/config_pstats.cxx @@ -68,6 +68,17 @@ ConfigVariableDouble pstats_target_frame_rate "This frame rate is marked with a different-colored line; " "otherwise, this setting has no effect.")); +ConfigVariableBool pstats_gpu_timing +("pstats-gpu-timing", false, + PRC_DESC("Set this true to query the graphics library for the actual time " + "that graphics operations take to execute on the video card. " + "Enabling this will harm performance, but this information can " + "be more useful than the regular Draw information in tracking " + "down bottlenecks, because the CPU-based Draw collectors only " + "measure how long it takes for the API call to complete, which " + "is not usually an accurate reflectino of how long the actual " + "operation takes on the video card.")); + // The rest are different in that they directly control the server, // not the client. ConfigVariableBool pstats_scroll_mode diff --git a/panda/src/pstatclient/config_pstats.h b/panda/src/pstatclient/config_pstats.h index 2d818af324..be9b0e1e49 100644 --- a/panda/src/pstatclient/config_pstats.h +++ b/panda/src/pstatclient/config_pstats.h @@ -38,6 +38,7 @@ extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_tcp_ratio; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableString pstats_host; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableInt pstats_port; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_target_frame_rate; +extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_gpu_timing; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_scroll_mode; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_history; diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 6417891a90..fa526afd69 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -152,7 +152,7 @@ get_collector_fullname(int index) const { if (parent_index == 0) { return collector->get_name(); } else { - return get_collector_fullname(parent_index) + ":" + + return get_collector_fullname(parent_index) + ":" + collector->get_name(); } } @@ -180,7 +180,6 @@ get_main_thread() const { return PStatThread((PStatClient *)this, 0); } - //////////////////////////////////////////////////////////////////// // Function: PStatClient::get_current_thread // Access: Published @@ -191,7 +190,7 @@ get_main_thread() const { PStatThread PStatClient:: get_current_thread() const { if (!client_is_connected()) { - // No need to make the relatively expensive call to + // No need to make the relatively expensive call to // Thread::get_current_thread() if we're not even connected. return get_main_thread(); } @@ -222,10 +221,10 @@ main_tick() { _mmap_size_pcollector.set_level(MemoryUsage::get_panda_mmap_size()); - + TypeRegistry *type_reg = TypeRegistry::ptr(); int num_typehandles = type_reg->get_num_typehandles(); - + while ((int)type_handle_cols.size() < num_typehandles) { type_handle_cols.push_back(TypeHandleCollector()); } @@ -261,7 +260,7 @@ main_tick() { case TypeHandle::MC_limit: // Not used. break; - } + } } } size_t min_usage = (single_total_usage + array_total_usage + dc_active_total_usage + dc_inactive_total_usage) / 1024; @@ -288,7 +287,7 @@ main_tick() { case TypeHandle::MC_singleton: category = "Heap:Single"; break; - + case TypeHandle::MC_array: category = "Heap:Array"; break; @@ -315,11 +314,11 @@ main_tick() { case TypeHandle::MC_singleton: single_other_usage -= usage; break; - + case TypeHandle::MC_array: array_other_usage -= usage; break; - + case TypeHandle::MC_deleted_chain_active: dc_active_other_usage -= usage; break; @@ -348,7 +347,7 @@ main_tick() { #endif // DO_MEMORY_USAGE get_global_pstats()->client_main_tick(); -} +} //////////////////////////////////////////////////////////////////// // Function: PStatClient::thread_tick @@ -359,7 +358,7 @@ main_tick() { void PStatClient:: thread_tick(const string &sync_name) { get_global_pstats()->client_thread_tick(sync_name); -} +} //////////////////////////////////////////////////////////////////// // Function: PStatClient::client_main_tick @@ -383,8 +382,8 @@ client_main_tick() { _threads_by_sync_name.find("Main"); if (ni != _threads_by_sync_name.end()) { const vector_int &indices = (*ni).second; - for (vector_int::const_iterator vi = indices.begin(); - vi != indices.end(); + for (vector_int::const_iterator vi = indices.begin(); + vi != indices.end(); ++vi) { _impl->new_frame(*vi); } @@ -407,8 +406,8 @@ client_thread_tick(const string &sync_name) { _threads_by_sync_name.find(sync_name); if (ni != _threads_by_sync_name.end()) { const vector_int &indices = (*ni).second; - for (vector_int::const_iterator vi = indices.begin(); - vi != indices.end(); + for (vector_int::const_iterator vi = indices.begin(); + vi != indices.end(); ++vi) { _impl->new_frame(*vi); } @@ -463,7 +462,7 @@ PStatClient *PStatClient:: get_global_pstats() { if (_global_pstats == (PStatClient *)NULL) { _global_pstats = new PStatClient; - + ClockObject::_start_clock_wait = start_clock_wait; ClockObject::_start_clock_busy_wait = start_clock_busy_wait; ClockObject::_stop_clock_wait = stop_clock_wait; @@ -616,8 +615,8 @@ do_make_thread(Thread *thread) { // We have seen a thread with this name before. Can we re-use any // of them? const vector_int &indices = (*ni).second; - for (vector_int::const_iterator vi = indices.begin(); - vi != indices.end(); + for (vector_int::const_iterator vi = indices.begin(); + vi != indices.end(); ++vi) { int index = (*vi); nassertr(index >= 0 && index < _num_threads, PStatThread()); @@ -637,20 +636,26 @@ do_make_thread(Thread *thread) { int new_index = _num_threads; thread->set_pstats_index(new_index); thread->set_pstats_callback(this); - _threads_by_name[thread->get_name()].push_back(new_index); - _threads_by_sync_name[thread->get_sync_name()].push_back(new_index); - + InternalThread *pthread = new InternalThread(thread); add_thread(pthread); - // We need an additional PerThreadData for this thread in all of the - // collectors. - CollectorPointer *collectors = (CollectorPointer *)_collectors; - for (int ci = 0; ci < _num_collectors; ++ci) { - Collector *collector = collectors[ci]; - collector->_per_thread.push_back(PerThreadData()); - nassertr((int)collector->_per_thread.size() == _num_threads, PStatThread()); - } + return PStatThread(this, new_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::make_gpu_thread +// Access: Private +// Description: Returns a PStatThread representing the GPU. +// This is normally called by the GSG only. +//////////////////////////////////////////////////////////////////// +PStatThread PStatClient:: +make_gpu_thread(const string &name) { + ReMutexHolder holder(_lock); + int new_index = _num_threads; + + InternalThread *pthread = new InternalThread(name, "GPU"); + add_thread(pthread); return PStatThread(this, new_index); } @@ -960,7 +965,7 @@ get_level(int collector_index, int thread_index) const { // Description: This function is added as a hook into ClockObject, so // that we may time the delay for // ClockObject::wait_until(), used for certain special -// clock modes. +// clock modes. // // This callback is a hack around the fact that we can't // let the ClockObject directly create a PStatCollector, @@ -977,7 +982,7 @@ start_clock_wait() { // Description: This function is added as a hook into ClockObject, so // that we may time the delay for // ClockObject::wait_until(), used for certain special -// clock modes. +// clock modes. // // This callback is a hack around the fact that we can't // let the ClockObject directly create a PStatCollector, @@ -995,7 +1000,7 @@ start_clock_busy_wait() { // Description: This function is added as a hook into ClockObject, so // that we may time the delay for // ClockObject::wait_until(), used for certain special -// clock modes. +// clock modes. // // This callback is a hack around the fact that we can't // let the ClockObject directly create a PStatCollector, @@ -1051,6 +1056,9 @@ add_collector(PStatClient::Collector *collector) { //////////////////////////////////////////////////////////////////// void PStatClient:: add_thread(PStatClient::InternalThread *thread) { + _threads_by_name[thread->_name].push_back(_num_threads); + _threads_by_sync_name[thread->_sync_name].push_back(_num_threads); + if (_num_threads >= _threads_size) { // We need to grow the array. We have to be careful here, because // there might be clients accessing the array right now who are @@ -1071,12 +1079,21 @@ add_thread(PStatClient::InternalThread *thread) { // and then no more.) new_threads[_num_threads] = thread; - AtomicAdjust::inc(_num_threads); } else { ThreadPointer *threads = (ThreadPointer *)_threads; threads[_num_threads] = thread; - AtomicAdjust::inc(_num_threads); + } + + AtomicAdjust::inc(_num_threads); + + // We need an additional PerThreadData for this thread in all of the + // collectors. + CollectorPointer *collectors = (CollectorPointer *)_collectors; + for (int ci = 0; ci < _num_collectors; ++ci) { + Collector *collector = collectors[ci]; + collector->_per_thread.push_back(PerThreadData()); + nassertv((int)collector->_per_thread.size() == _num_threads); } } @@ -1148,7 +1165,7 @@ make_def(const PStatClient *client, int this_index) { if (_def == (PStatCollectorDef *)NULL) { _def = new PStatCollectorDef(this_index, _name); if (_parent_index != this_index) { - const PStatCollectorDef *parent_def = + const PStatCollectorDef *parent_def = client->get_collector_def(_parent_index); _def->set_parent(*parent_def); } @@ -1157,9 +1174,9 @@ make_def(const PStatClient *client, int this_index) { } //////////////////////////////////////////////////////////////////// -// Function: PStatClient::Collector::make_def +// Function: PStatClient::InternalThread::Constructor // Access: Private -// Description: Creates the new PStatCollectorDef for this collector. +// Description: //////////////////////////////////////////////////////////////////// PStatClient::InternalThread:: InternalThread(Thread *thread) : @@ -1174,4 +1191,22 @@ InternalThread(Thread *thread) : { } +//////////////////////////////////////////////////////////////////// +// Function: PStatClient::InternalThread::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PStatClient::InternalThread:: +InternalThread(const string &name, const string &sync_name) : + _thread(NULL), + _name(name), + _sync_name(sync_name), + _is_active(false), + _frame_number(0), + _next_packet(0.0), + _thread_active(true), + _thread_lock(string("PStatClient::InternalThread ") + name) +{ +} + #endif // DO_PSTATS diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index 809d3d438c..1b733a543d 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -35,6 +35,7 @@ class PStatCollector; class PStatCollectorDef; class PStatThread; +class GraphicsStateGuardian; //////////////////////////////////////////////////////////////////// // Class : PStatClient @@ -113,6 +114,7 @@ private: PStatThread do_get_current_thread() const; PStatThread make_thread(Thread *thread); PStatThread do_make_thread(Thread *thread); + PStatThread make_gpu_thread(const string &name); bool is_active(int collector_index, int thread_index) const; bool is_started(int collector_index, int thread_index) const; @@ -171,7 +173,7 @@ private: INLINE const string &get_name() const; INLINE bool is_active() const; INLINE PStatCollectorDef *get_def(const PStatClient *client, int this_index) const; - + private: void make_def(const PStatClient *client, int this_index); @@ -201,6 +203,7 @@ private: class InternalThread { public: InternalThread(Thread *thread); + InternalThread(const string &name, const string &sync_name = "Main"); WPT(Thread) _thread; string _name; @@ -248,6 +251,7 @@ private: friend class PStatCollector; friend class PStatThread; friend class PStatClientImpl; + friend class GraphicsStateGuardian; }; #include "pStatClient.I" @@ -272,4 +276,3 @@ PUBLISHED: #endif // DO_PSTATS #endif - diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index 578f214824..2d1feb7853 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -47,7 +47,7 @@ PStatClientImpl(PStatClient *client) : _reader(this, 0), _writer(this, pstats_threaded_write ? 1 : 0) { - _writer.set_max_queue_size(pstats_max_queue_size); + _writer.set_max_queue_size(pstats_max_queue_size); _reader.set_tcp_header_size(4); _writer.set_tcp_header_size(4); _is_connected = false; @@ -201,10 +201,10 @@ new_frame(int thread_index) { // Fill up the level data for all the collectors who have level // data for this pthread. int num_collectors = _client->_num_collectors; - PStatClient::CollectorPointer *collectors = + PStatClient::CollectorPointer *collectors = (PStatClient::CollectorPointer *)_client->_collectors; for (int i = 0; i < num_collectors; i++) { - const PStatClient::PerThreadData &ptd = + const PStatClient::PerThreadData &ptd = collectors[i]->_per_thread[thread_index]; if (ptd._has_level) { pthread->_frame_data.add_level(i, ptd._level); @@ -219,13 +219,55 @@ new_frame(int thread_index) { _client->start(0, thread_index, frame_start); // Also record the time for the PStats operation itself. + int current_thread_index = Thread::get_current_thread()->get_pstats_index(); int pstats_index = PStatClient::_pstats_pcollector.get_index(); - _client->start(pstats_index, thread_index, frame_start); + _client->start(pstats_index, current_thread_index, frame_start); if (frame_number != -1) { transmit_frame_data(thread_index, frame_number, frame_data); } - _client->stop(pstats_index, thread_index, get_real_time()); + _client->stop(pstats_index, current_thread_index, get_real_time()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PStatClientImpl::add_frame +// Access: Public +// Description: Slightly lower-level interface than new_frame that +// takes a set of frame data. +//////////////////////////////////////////////////////////////////// +void PStatClientImpl:: +add_frame(int thread_index, const PStatFrameData &frame_data) { + nassertv(thread_index >= 0 && thread_index < _client->_num_threads); + + PStatClient::InternalThread *pthread = _client->get_thread_ptr(thread_index); + + // If we're the main thread, we should exchange control packets with + // the server. + if (thread_index == 0) { + transmit_control_data(); + } + + // If we've got the UDP port by the time the frame starts, it's + // time to become active and start actually tracking data. + if (_got_udp_port) { + pthread->_is_active = true; + } + + if (!pthread->_is_active) { + return; + } + + int frame_number = pthread->_frame_number++; + + // Also record the time for the PStats operation itself. + int current_thread_index = Thread::get_current_thread()->get_pstats_index(); + int pstats_index = PStatClient::_pstats_pcollector.get_index(); + _client->start(pstats_index, current_thread_index); + + if (frame_number != -1) { + transmit_frame_data(thread_index, frame_number, frame_data); + } + _client->stop(pstats_index, current_thread_index); } //////////////////////////////////////////////////////////////////// @@ -235,7 +277,7 @@ new_frame(int thread_index) { // transmit the latest data to the PStatServer. //////////////////////////////////////////////////////////////////// void PStatClientImpl:: -transmit_frame_data(int thread_index, int frame_number, +transmit_frame_data(int thread_index, int frame_number, const PStatFrameData &frame_data) { nassertv(thread_index >= 0 && thread_index < _client->_num_threads); PStatClient::InternalThread *thread = _client->get_thread_ptr(thread_index); @@ -397,7 +439,7 @@ report_new_collectors() { // single datagram. So we limit ourselves here to sending only // half that many. static const int max_collectors_at_once = 700; - + while (_is_connected && _collectors_reported < _client->_num_collectors) { PStatClientControlMessage message; message._type = PStatClientControlMessage::T_define_collectors; @@ -408,7 +450,7 @@ report_new_collectors() { _collectors_reported++; i++; } - + Datagram datagram; message.encode(datagram); _writer.send(datagram, _tcp_connection, true); @@ -427,7 +469,7 @@ report_new_threads() { PStatClientControlMessage message; message._type = PStatClientControlMessage::T_define_threads; message._first_thread_index = _threads_reported; - PStatClient::ThreadPointer *threads = + PStatClient::ThreadPointer *threads = (PStatClient::ThreadPointer *)_client->_threads; while (_threads_reported < _client->_num_threads) { message._names.push_back(threads[_threads_reported]->_name); diff --git a/panda/src/pstatclient/pStatClientImpl.h b/panda/src/pstatclient/pStatClientImpl.h index 28ed2d7cb0..7f95584e09 100644 --- a/panda/src/pstatclient/pStatClientImpl.h +++ b/panda/src/pstatclient/pStatClientImpl.h @@ -72,6 +72,7 @@ public: INLINE void client_resume_after_pause(); void new_frame(int thread_index); + void add_frame(int thread_index, const PStatFrameData &frame_data); private: void transmit_frame_data(int thread_index, int frame_number, @@ -90,7 +91,7 @@ private: void report_new_threads(); void handle_server_control_message(const PStatServerControlMessage &message); - virtual void connection_reset(const PT(Connection) &connection, + virtual void connection_reset(const PT(Connection) &connection, bool okflag); PStatClient *_client; diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 463a3bd13b..f14047732d 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -152,9 +152,10 @@ static TimeCollectorProperties time_properties[] = { { 1, "Draw:Flush", { 0.9, 0.2, 0.7 } }, { 1, "Draw:Sync", { 0.5, 0.7, 0.7 } }, { 0, "Draw:Transform", { 0.0, 0.5, 0.0 } }, - { 0, "Draw:Primitive", { 0.0, 0.0, 0.5 } }, - { 0, "Draw:Set State", { 0.2, 0.6, 0.8 } }, + { 1, "Draw:Primitive", { 0.0, 0.0, 0.5 } }, + { 1, "Draw:Set State", { 0.2, 0.6, 0.8 } }, { 1, "Draw:Wait occlusion", { 1.0, 0.5, 0.0 } }, + { 1, "Draw:Bind FBO", { 0.0, 0.8, 0.8 } }, { 0, NULL } }; @@ -226,6 +227,7 @@ static LevelCollectorProperties level_properties[] = { { 1, "Dirty PipelineCyclers", { 0.2, 0.2, 0.2 }, "", 5000 }, { 1, "Collision Volumes", { 1.0, 0.8, 0.5 }, "", 500 }, { 1, "Collision Tests", { 0.5, 0.8, 1.0 }, "", 100 }, + { 1, "Command latency", { 0.8, 0.2, 0.0 }, "ms", 10, 1.0 / 1000.0 }, { 0, NULL } }; @@ -339,7 +341,7 @@ initialize_collector_def(const PStatClient *client, PStatCollectorDef *def) { ("pstats-factor-" + config_name, 1.0, "", ConfigVariable::F_dynamic); ConfigVariableDouble pstats_color ("pstats-color-" + config_name, 0.0, "", ConfigVariable::F_dynamic); - + if (pstats_active.has_value()) { def->_is_active = pstats_active; def->_active_explicitly_set = true; diff --git a/panda/src/pstatclient/pStatThread.I b/panda/src/pstatclient/pStatThread.I index 36cd57e539..bf9d673eb9 100644 --- a/panda/src/pstatclient/pStatThread.I +++ b/panda/src/pstatclient/pStatThread.I @@ -26,7 +26,7 @@ PStatThread() { //////////////////////////////////////////////////////////////////// // Function: PStatThread::Constructor -// Access: Private +// Access: Published // Description: Normally, this constructor is called only from // PStatClient. Use one of the constructors below to // create your own Thread. @@ -109,6 +109,19 @@ new_frame() { #endif } +//////////////////////////////////////////////////////////////////// +// Function: PStatThread::add_frame +// Access: Public +// Description: This is a slightly lower-level version of new_frame +// that also specifies the data to send for this frame. +//////////////////////////////////////////////////////////////////// +INLINE void PStatThread:: +add_frame(const PStatFrameData &frame_data) { +#ifdef DO_PSTATS + _client->get_impl()->add_frame(_index, frame_data); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: PStatThread::get_index // Access: Published diff --git a/panda/src/pstatclient/pStatThread.h b/panda/src/pstatclient/pStatThread.h index 8e684a3cd3..df0435585b 100644 --- a/panda/src/pstatclient/pStatThread.h +++ b/panda/src/pstatclient/pStatThread.h @@ -18,6 +18,7 @@ #include "pandabase.h" #include "pStatClient.h" +#include "pStatFrameData.h" class Thread; @@ -30,15 +31,16 @@ class Thread; class EXPCL_PANDA_PSTATCLIENT PStatThread { private: INLINE PStatThread(); - INLINE PStatThread(PStatClient *client, int index); PUBLISHED: + INLINE PStatThread(PStatClient *client, int index); INLINE PStatThread(Thread *thread, PStatClient *client = NULL); INLINE PStatThread(const PStatThread ©); INLINE void operator = (const PStatThread ©); INLINE void new_frame(); + INLINE void add_frame(const PStatFrameData &frame_data); Thread *get_thread() const; INLINE int get_index() const; diff --git a/panda/src/pstatclient/pStatTimer.h b/panda/src/pstatclient/pStatTimer.h index 060fd46f11..640838ecb6 100644 --- a/panda/src/pstatclient/pStatTimer.h +++ b/panda/src/pstatclient/pStatTimer.h @@ -38,12 +38,12 @@ public: INLINE PStatTimer(PStatCollector &collector, Thread *current_thread); INLINE ~PStatTimer(); -private: +protected: PStatCollector &_collector; PStatThread _thread; #else // DO_PSTATS - INLINE PStatTimer(PStatCollector &) { } + INLINE PStatTimer(PStatCollector &) { } INLINE PStatTimer(PStatCollector &, Thread *) { } INLINE ~PStatTimer() { }