Add GPU profiling capabilities to PStats using OpenGL timer queries

This commit is contained in:
rdb 2014-09-24 23:53:37 +00:00
parent 4f3cc072c2
commit db0fd516a0
43 changed files with 1624 additions and 484 deletions

View File

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

View File

@ -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 <WinSock2.h>
#include <wtypes.h>
#undef WINDOWS_LEAN_AND_MEAN
#undef WINDOWS_LEAN_AND_MEAN
#else
#include <sys/time.h>
#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),

View File

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

View File

@ -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 <algorithm>
#include <limits.h>
@ -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();
}
////////////////////////////////////////////////////////////////////

View File

@ -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<PT(TimerQueryContext)> 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.

View File

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

View File

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

View File

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

View File

@ -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<CLP(TextureContext)*>::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<CLP(TextureContext)*>::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) {

View File

@ -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<CLP(TextureContext)*> _texture_contexts;
typedef pvector<CLP(TextureContext)*> 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 <CLP(GraphicsBuffer) *> _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;

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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<GLuint> DeletedDisplayLists;
DeletedDisplayLists _deleted_display_lists;
DeletedDisplayLists _deleted_queries;
typedef pvector<GLuint> 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"

View File

@ -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."
//
////////////////////////////////////////////////////////////////////

View File

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

View File

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

View File

@ -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, &param_count);
_glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &param_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

View File

@ -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)
{
}

View File

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

View File

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

View File

@ -151,6 +151,13 @@ ConfigVariableEnum<NotifySeverity> 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();

View File

@ -58,6 +58,7 @@ extern ConfigVariableEnum<GeomEnums::UsageHint> gl_min_buffer_usage_hint;
extern ConfigVariableBool gl_debug;
extern ConfigVariableBool gl_debug_synchronous;
extern ConfigVariableEnum<NotifySeverity> 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,3 +30,6 @@
#include "indexBufferContext.cxx"
#include "internalName.cxx"
#include "lens.cxx"
#include "material.cxx"
#include "materialPool.cxx"
#include "matrixLens.cxx"

View File

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

View File

@ -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())
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &copy);
INLINE void operator = (const PStatThread &copy);
INLINE void new_frame();
INLINE void add_frame(const PStatFrameData &frame_data);
Thread *get_thread() const;
INLINE int get_index() const;

View File

@ -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() { }