mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-26 22:53:16 -04:00
Merge branch 'release/1.10.x' into incoming
This commit is contained in:
commit
a5610f217e
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@ -347,9 +347,9 @@ jobs:
|
||||
shell: powershell
|
||||
run: |
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win64.zip", "thirdparty-tools.zip")
|
||||
$wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.13/panda3d-1.10.13-tools-win64.zip", "thirdparty-tools.zip")
|
||||
Expand-Archive -Path thirdparty-tools.zip
|
||||
Move-Item -Path thirdparty-tools/panda3d-1.10.11/thirdparty -Destination .
|
||||
Move-Item -Path thirdparty-tools/panda3d-1.10.13/thirdparty -Destination .
|
||||
- name: Get thirdparty packages (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
@ -358,10 +358,39 @@ jobs:
|
||||
mv panda3d-1.10.13/thirdparty thirdparty
|
||||
rmdir panda3d-1.10.13
|
||||
(cd thirdparty/darwin-libs-a && rm -rf rocket)
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: '3.11'
|
||||
- name: Build Python 3.11
|
||||
shell: bash
|
||||
run: |
|
||||
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
|
||||
- name: Test Python 3.11
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install pytest
|
||||
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Build Python 3.10
|
||||
shell: bash
|
||||
run: |
|
||||
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
|
||||
- name: Test Python 3.10
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install pytest
|
||||
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Build Python 3.9
|
||||
shell: bash
|
||||
run: |
|
||||
@ -371,10 +400,11 @@ jobs:
|
||||
run: |
|
||||
python -m pip install pytest
|
||||
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: '3.8'
|
||||
- name: Build Python 3.8
|
||||
shell: bash
|
||||
run: |
|
||||
@ -384,10 +414,11 @@ jobs:
|
||||
run: |
|
||||
python -m pip install pytest
|
||||
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
|
||||
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.7
|
||||
python-version: '3.7'
|
||||
- name: Build Python 3.7
|
||||
shell: bash
|
||||
run: |
|
||||
@ -397,6 +428,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install pytest
|
||||
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
|
||||
|
||||
- name: Make installer
|
||||
run: |
|
||||
python makepanda/makepackage.py --verbose --lzma
|
||||
|
@ -64,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
|
||||
[click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
|
||||
building them from source.
|
||||
|
||||
- https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-win64.zip
|
||||
- https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-win32.zip
|
||||
- https://www.panda3d.org/download/panda3d-1.10.13/panda3d-1.10.13-tools-win64.zip
|
||||
- https://www.panda3d.org/download/panda3d-1.10.13/panda3d-1.10.13-tools-win32.zip
|
||||
|
||||
After acquiring these dependencies, you can build Panda3D from the command
|
||||
prompt using the following command. Change the `--msvc-version` option based
|
||||
|
@ -296,9 +296,10 @@ void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_dis
|
||||
|
||||
// Reset the film size, offset and far-plane
|
||||
Camera* cam = DCAST(Camera, _cam_nodes[i].node());
|
||||
cam->get_lens()->set_film_size(1, 1);
|
||||
cam->get_lens()->set_film_offset(0, 0);
|
||||
cam->get_lens()->set_near_far(1, 100);
|
||||
Lens *lens = cam->get_lens();
|
||||
lens->set_film_size(1, 1);
|
||||
lens->set_film_offset(0, 0);
|
||||
lens->set_near_far(1, 100);
|
||||
|
||||
// Find a good initial position
|
||||
_cam_nodes[i].set_pos(cam_start);
|
||||
@ -320,16 +321,16 @@ void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_dis
|
||||
if (_max_film_sizes[i].get_x() < film_size.get_x()) _max_film_sizes[i].set_x(film_size.get_x());
|
||||
if (_max_film_sizes[i].get_y() < film_size.get_y()) _max_film_sizes[i].set_y(film_size.get_y());
|
||||
|
||||
cam->get_lens()->set_film_size(_max_film_sizes[i] * filmsize_bias);
|
||||
lens->set_film_size(_max_film_sizes[i] * filmsize_bias);
|
||||
} else {
|
||||
// If we don't use a fixed film size, we can just set the film size
|
||||
// on the lens.
|
||||
cam->get_lens()->set_film_size(film_size * filmsize_bias);
|
||||
lens->set_film_size(film_size * filmsize_bias);
|
||||
}
|
||||
|
||||
// Compute new film offset
|
||||
cam->get_lens()->set_film_offset(film_offset);
|
||||
cam->get_lens()->set_near_far(10, best_max_extent.get_z());
|
||||
lens->set_film_offset(film_offset);
|
||||
lens->set_near_far(10, best_max_extent.get_z());
|
||||
_camera_nearfar[i] = LVecBase2(10, best_max_extent.get_z());
|
||||
|
||||
// Compute the camera MVP
|
||||
@ -399,7 +400,8 @@ void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) {
|
||||
}
|
||||
|
||||
// Do the actual PSSM
|
||||
compute_pssm_splits( transform, _pssm_distance / lens->get_far(), light_vector );
|
||||
double far_recip = std::max(1.0 / (double)lens->get_far(), (double)lens_far_limit);
|
||||
compute_pssm_splits( transform, _pssm_distance * far_recip, light_vector );
|
||||
|
||||
_update_collector.stop();
|
||||
}
|
||||
|
@ -513,7 +513,7 @@ main(int argc, char **argv) {
|
||||
for (i = 1; i < argc; ++i) {
|
||||
Filename filename = Filename::from_os_specific(argv[i]);
|
||||
if (!parser.parse_file(filename)) {
|
||||
cerr << "Error parsing file: '" << argv[i] << "'\n";
|
||||
cerr << "interrogate failed to parse file: '" << argv[i] << "'\n";
|
||||
exit(1);
|
||||
}
|
||||
builder.add_source_file(filename.to_os_generic());
|
||||
|
@ -211,6 +211,10 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
|
||||
|
||||
/* Python 3.9 */
|
||||
|
||||
#ifndef PyCFunction_CheckExact
|
||||
# define PyCFunction_CheckExact(op) (Py_TYPE(op) == &PyCFunction_Type)
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX < 0x03090000
|
||||
INLINE PyObject *PyObject_CallNoArgs(PyObject *func) {
|
||||
#if PY_VERSION_HEX >= 0x03080000
|
||||
|
@ -25,6 +25,9 @@ typedef _object PyObject;
|
||||
struct _typeobject;
|
||||
typedef _typeobject PyTypeObject;
|
||||
|
||||
struct _frame;
|
||||
typedef _frame PyFrameObject;
|
||||
|
||||
typedef struct {} PyStringObject;
|
||||
typedef struct {} PyUnicodeObject;
|
||||
|
||||
|
@ -375,6 +375,7 @@ SectionGroup "Python modules" SecGroupPython
|
||||
!insertmacro PyBindingSection 3.9-32 .cp39-win32.pyd
|
||||
!insertmacro PyBindingSection 3.10-32 .cp310-win32.pyd
|
||||
!insertmacro PyBindingSection 3.11-32 .cp311-win32.pyd
|
||||
!insertmacro PyBindingSection 3.12-32 .cp312-win32.pyd
|
||||
!else
|
||||
!insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd
|
||||
@ -383,6 +384,7 @@ SectionGroup "Python modules" SecGroupPython
|
||||
!insertmacro PyBindingSection 3.9 .cp39-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.10 .cp310-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.11 .cp311-win_amd64.pyd
|
||||
!insertmacro PyBindingSection 3.12 .cp312-win_amd64.pyd
|
||||
!endif
|
||||
SectionGroupEnd
|
||||
|
||||
@ -492,6 +494,7 @@ Function .onInit
|
||||
!insertmacro MaybeEnablePyBindingSection 3.9-32
|
||||
!insertmacro MaybeEnablePyBindingSection 3.10-32
|
||||
!insertmacro MaybeEnablePyBindingSection 3.11-32
|
||||
!insertmacro MaybeEnablePyBindingSection 3.12-32
|
||||
${EndIf}
|
||||
!else
|
||||
!insertmacro MaybeEnablePyBindingSection 3.5
|
||||
@ -502,6 +505,7 @@ Function .onInit
|
||||
!insertmacro MaybeEnablePyBindingSection 3.9
|
||||
!insertmacro MaybeEnablePyBindingSection 3.10
|
||||
!insertmacro MaybeEnablePyBindingSection 3.11
|
||||
!insertmacro MaybeEnablePyBindingSection 3.12
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
@ -519,6 +523,10 @@ Function .onInit
|
||||
SectionSetFlags ${SecPyBindings3.11} ${SF_RO}
|
||||
SectionSetInstTypes ${SecPyBindings3.11} 0
|
||||
!endif
|
||||
!ifdef SecPyBindings3.12
|
||||
SectionSetFlags ${SecPyBindings3.12} ${SF_RO}
|
||||
SectionSetInstTypes ${SecPyBindings3.12} 0
|
||||
!endif
|
||||
${EndUnless}
|
||||
FunctionEnd
|
||||
|
||||
@ -831,6 +839,7 @@ Section Uninstall
|
||||
!insertmacro RemovePythonPath 3.9-32
|
||||
!insertmacro RemovePythonPath 3.10-32
|
||||
!insertmacro RemovePythonPath 3.11-32
|
||||
!insertmacro RemovePythonPath 3.12-32
|
||||
!else
|
||||
!insertmacro RemovePythonPath 3.5
|
||||
!insertmacro RemovePythonPath 3.6
|
||||
@ -839,6 +848,7 @@ Section Uninstall
|
||||
!insertmacro RemovePythonPath 3.9
|
||||
!insertmacro RemovePythonPath 3.10
|
||||
!insertmacro RemovePythonPath 3.11
|
||||
!insertmacro RemovePythonPath 3.12
|
||||
!endif
|
||||
|
||||
SetDetailsPrint both
|
||||
@ -908,6 +918,7 @@ SectionEnd
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9-32} $(DESC_SecPyBindings3.9-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.10-32} $(DESC_SecPyBindings3.10-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.11-32} $(DESC_SecPyBindings3.11-32)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12-32} $(DESC_SecPyBindings3.12-32)
|
||||
!else
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6)
|
||||
@ -916,6 +927,7 @@ SectionEnd
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9} $(DESC_SecPyBindings3.9)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.10} $(DESC_SecPyBindings3.10)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.11} $(DESC_SecPyBindings3.11)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12} $(DESC_SecPyBindings3.12)
|
||||
!endif
|
||||
!ifdef INCLUDE_PYVER
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
|
||||
|
@ -3815,6 +3815,7 @@ IGATEFILES=GetDirectoryContents('panda/src/pstatclient', ["*.h", "*_composite*.c
|
||||
IGATEFILES.remove("config_pstats.h")
|
||||
TargetAdd('libp3pstatclient.in', opts=OPTS, input=IGATEFILES)
|
||||
TargetAdd('libp3pstatclient.in', opts=['IMOD:panda3d.core', 'ILIB:libp3pstatclient', 'SRCDIR:panda/src/pstatclient'])
|
||||
PyTargetAdd('p3pstatclient_pStatClient_ext.obj', opts=OPTS, input='pStatClient_ext.cxx')
|
||||
|
||||
#
|
||||
# DIRECTORY: panda/src/gobj/
|
||||
@ -4244,6 +4245,7 @@ PyTargetAdd('core.pyd', input='p3putil_ext_composite.obj')
|
||||
PyTargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj')
|
||||
PyTargetAdd('core.pyd', input='p3event_asyncFuture_ext.obj')
|
||||
PyTargetAdd('core.pyd', input='p3event_pythonTask.obj')
|
||||
PyTargetAdd('core.pyd', input='p3pstatclient_pStatClient_ext.obj')
|
||||
PyTargetAdd('core.pyd', input='p3gobj_ext_composite.obj')
|
||||
PyTargetAdd('core.pyd', input='p3pgraph_ext_composite.obj')
|
||||
PyTargetAdd('core.pyd', input='p3display_ext_composite.obj')
|
||||
|
@ -433,7 +433,7 @@ def SetTarget(target, arch=None):
|
||||
ANDROID_ABI = 'x86_64'
|
||||
ANDROID_TRIPLE = 'x86_64-linux-android'
|
||||
else:
|
||||
exit('Android architecture must be arm, armv7a, arm64, mips, mips64, x86 or x86_64')
|
||||
exit('Android architecture must be arm, armv7a, arm64, mips, mips64, x86 or x86_64, use --arch to specify')
|
||||
|
||||
ANDROID_TRIPLE += str(ANDROID_API)
|
||||
TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-'
|
||||
|
@ -381,8 +381,7 @@ Texture(const string &name) :
|
||||
_reloading = false;
|
||||
|
||||
CDWriter cdata(_cycler, true);
|
||||
do_set_format(cdata, F_rgb);
|
||||
do_set_component_type(cdata, T_unsigned_byte);
|
||||
cdata->inc_properties_modified();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -10782,11 +10781,10 @@ CData() {
|
||||
_y_size = 1;
|
||||
_z_size = 1;
|
||||
_num_views = 1;
|
||||
|
||||
// We will override the format in a moment (in the Texture constructor), but
|
||||
// set it to something else first to avoid the check in do_set_format
|
||||
// depending on an uninitialized value.
|
||||
_format = F_rgba;
|
||||
_num_components = 3;
|
||||
_component_width = 1;
|
||||
_format = F_rgb;
|
||||
_component_type = T_unsigned_byte;
|
||||
|
||||
// Only used for buffer textures.
|
||||
_usage_hint = GeomEnums::UH_unspecified;
|
||||
|
@ -22,6 +22,12 @@ set(P3PSTATCLIENT_SOURCES
|
||||
pStatThread.cxx
|
||||
)
|
||||
|
||||
set(P3PSTATCLIENT_IGATEEXT
|
||||
pStatClient_ext.I
|
||||
pStatClient_ext.cxx
|
||||
pStatClient_ext.h
|
||||
)
|
||||
|
||||
composite_sources(p3pstatclient P3PSTATCLIENT_SOURCES)
|
||||
add_component_library(p3pstatclient SYMBOL BUILDING_PANDA_PSTATCLIENT
|
||||
${P3PSTATCLIENT_HEADERS} ${P3PSTATCLIENT_SOURCES})
|
||||
@ -31,7 +37,7 @@ if(HAVE_NET AND WANT_NATIVE_NET)
|
||||
target_link_libraries(p3pstatclient p3net)
|
||||
endif()
|
||||
|
||||
target_interrogate(p3pstatclient ALL)
|
||||
target_interrogate(p3pstatclient ALL EXTENSIONS ${P3PSTATCLIENT_IGATEEXT})
|
||||
|
||||
if(NOT BUILD_METALIBS)
|
||||
install(TARGETS p3pstatclient
|
||||
|
@ -87,6 +87,16 @@ ConfigVariableBool pstats_thread_profiling
|
||||
PRC_DESC("Set this true to query the system for thread statistics, such as "
|
||||
"the number of context switches and time spent waiting."));
|
||||
|
||||
ConfigVariableBool pstats_python_profiler
|
||||
("pstats-python-profiler", false,
|
||||
PRC_DESC("Set this true to integrate with the Python profiler to show "
|
||||
"detailed information about individual Python functions in "
|
||||
"PStats, similar to the information offered by Python's built-in "
|
||||
"profiler. This can be really useful to find bottlenecks in a "
|
||||
"Python program, but enabling this will slow down the application "
|
||||
"somewhat, and requires a recent version of the PStats server, so "
|
||||
"it is not enabled by default."));
|
||||
|
||||
// The rest are different in that they directly control the server, not the
|
||||
// client.
|
||||
ConfigVariableBool pstats_scroll_mode
|
||||
|
@ -39,6 +39,7 @@ 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_thread_profiling;
|
||||
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_python_profiler;
|
||||
|
||||
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_scroll_mode;
|
||||
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_history;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "patomic.h"
|
||||
#include "numeric_types.h"
|
||||
#include "bitArray.h"
|
||||
#include "extension.h"
|
||||
|
||||
class PStatClientImpl;
|
||||
class PStatCollector;
|
||||
@ -88,8 +89,8 @@ PUBLISHED:
|
||||
MAKE_PROPERTY(current_thread, get_current_thread);
|
||||
MAKE_PROPERTY(real_time, get_real_time);
|
||||
|
||||
INLINE static bool connect(const std::string &hostname = std::string(), int port = -1);
|
||||
INLINE static void disconnect();
|
||||
EXTEND INLINE static bool connect(const std::string &hostname = std::string(), int port = -1);
|
||||
EXTEND INLINE static void disconnect();
|
||||
INLINE static bool is_connected();
|
||||
|
||||
INLINE static void resume_after_pause();
|
||||
@ -101,8 +102,8 @@ PUBLISHED:
|
||||
void client_main_tick();
|
||||
void client_thread_tick();
|
||||
void client_thread_tick(const std::string &sync_name);
|
||||
bool client_connect(std::string hostname, int port);
|
||||
void client_disconnect();
|
||||
EXTEND bool client_connect(std::string hostname, int port);
|
||||
EXTEND void client_disconnect();
|
||||
bool client_is_connected() const;
|
||||
|
||||
void client_resume_after_pause();
|
||||
@ -258,6 +259,8 @@ private:
|
||||
friend class PStatThread;
|
||||
friend class PStatClientImpl;
|
||||
friend class GraphicsStateGuardian;
|
||||
|
||||
friend class Extension<PStatClient>;
|
||||
};
|
||||
|
||||
#include "pStatClient.I"
|
||||
|
@ -566,6 +566,13 @@ send_hello() {
|
||||
message._major_version = get_current_pstat_major_version();
|
||||
message._minor_version = get_current_pstat_minor_version();
|
||||
|
||||
// The Python profiling feature may send nested start/stop pairs, so requires
|
||||
// a server version capable of dealing with this.
|
||||
if (pstats_python_profiler && message._major_version <= 3) {
|
||||
message._major_version = 3;
|
||||
message._minor_version = std::max(message._minor_version, 1);
|
||||
}
|
||||
|
||||
Datagram datagram;
|
||||
message.encode(datagram);
|
||||
_writer.send(datagram, _tcp_connection, true);
|
||||
|
31
panda/src/pstatclient/pStatClient_ext.I
Normal file
31
panda/src/pstatclient/pStatClient_ext.I
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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."
|
||||
*
|
||||
* @file pStatClient_ext.I
|
||||
* @author rdb
|
||||
* @date 2022-11-29
|
||||
*/
|
||||
|
||||
/**
|
||||
* Attempts to establish a connection to the indicated PStatServer. Returns
|
||||
* true if successful, false on failure.
|
||||
*/
|
||||
INLINE bool Extension<PStatClient>::
|
||||
connect(const std::string &hostname, int port) {
|
||||
PStatClient *client = PStatClient::get_global_pstats();
|
||||
return invoke_extension<PStatClient>(client).client_connect(hostname, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection previously established.
|
||||
*/
|
||||
INLINE void Extension<PStatClient>::
|
||||
disconnect() {
|
||||
PStatClient *client = PStatClient::get_global_pstats();
|
||||
invoke_extension<PStatClient>(client).client_disconnect();
|
||||
}
|
336
panda/src/pstatclient/pStatClient_ext.cxx
Normal file
336
panda/src/pstatclient/pStatClient_ext.cxx
Normal file
@ -0,0 +1,336 @@
|
||||
/**
|
||||
* 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."
|
||||
*
|
||||
* @file pStatClient_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2022-11-23
|
||||
*/
|
||||
|
||||
#include "pStatClient_ext.h"
|
||||
|
||||
#if defined(HAVE_PYTHON) && defined(DO_PSTATS)
|
||||
|
||||
#include "pStatCollector.h"
|
||||
#include "config_pstatclient.h"
|
||||
|
||||
#ifndef CPPPARSER
|
||||
#include "frameobject.h"
|
||||
#endif
|
||||
|
||||
static bool _python_profiler_enabled = false;
|
||||
|
||||
// Used to cache stuff onto PyCodeObjects.
|
||||
static Py_ssize_t _extra_index = -1;
|
||||
|
||||
// Stores a mapping between C method definitions and collector indices.
|
||||
static pmap<PyMethodDef *, int> _c_method_collectors;
|
||||
|
||||
// Parent collector for all Python profiling collectors.
|
||||
static PStatCollector code_collector("App:Python");
|
||||
|
||||
/**
|
||||
* Walks up the type hierarchy to find the class where the method originates.
|
||||
*/
|
||||
static bool
|
||||
find_method(PyTypeObject *&cls, PyObject *name, PyCodeObject *code) {
|
||||
PyObject *meth = _PyType_Lookup(cls, name);
|
||||
if (meth == nullptr || !PyFunction_Check(meth) ||
|
||||
PyFunction_GET_CODE(meth) != (PyObject *)code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls->tp_bases != nullptr) {
|
||||
Py_ssize_t size = PyTuple_GET_SIZE(cls->tp_bases);
|
||||
for (Py_ssize_t i = 0; i < size; ++i) {
|
||||
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(cls->tp_bases, i);
|
||||
|
||||
if (find_method(base, name, code)) {
|
||||
cls = base;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find it in any of the bases, it must be defined here.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collector for a Python frame.
|
||||
*/
|
||||
static int
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
__declspec(noinline)
|
||||
#endif
|
||||
make_python_frame_collector(PyFrameObject *frame, PyCodeObject *code) {
|
||||
#if PY_VERSION_HEX >= 0x030B0000 // 3.11
|
||||
// Fetch the module name out of the frame's global scope.
|
||||
PyObject *globals = PyFrame_GetGlobals(frame);
|
||||
PyObject *py_mod_name = PyDict_GetItemString(globals, "__name__");
|
||||
Py_DECREF(globals);
|
||||
|
||||
const char *mod_name = py_mod_name ? PyUnicode_AsUTF8(py_mod_name) : "<unknown>";
|
||||
const char *meth_name = PyUnicode_AsUTF8(code->co_qualname);
|
||||
char buffer[1024];
|
||||
size_t len = snprintf(buffer, sizeof(buffer), "%s:%s", mod_name, meth_name);
|
||||
for (size_t i = 0; i < len - 1; ++i) {
|
||||
if (buffer[i] == '.') {
|
||||
buffer[i] = ':';
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Try to figure out the type name. There's no obvious way to do this.
|
||||
// It's possible that the first argument passed to this function is the
|
||||
// self instance or the current type (for a classmethod), but we have to
|
||||
// double-check that to make sure.
|
||||
PyTypeObject *cls = nullptr;
|
||||
if (code->co_argcount >= 1) {
|
||||
PyFrame_FastToLocals(frame);
|
||||
PyObject *first_arg = PyDict_GetItem(frame->f_locals, PyTuple_GET_ITEM(code->co_varnames, 0));
|
||||
cls = PyType_Check(first_arg) ? (PyTypeObject *)first_arg : Py_TYPE(first_arg);
|
||||
if ((cls->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0) {
|
||||
// Mangling scheme for methods starting (but not ending) with "__"
|
||||
PyObject *meth_name = code->co_name;
|
||||
Py_ssize_t len = PyUnicode_GET_LENGTH(meth_name);
|
||||
if (len >= 2 && PyUnicode_READ_CHAR(meth_name, 0) == '_' && PyUnicode_READ_CHAR(meth_name, 1) == '_' &&
|
||||
(len < 4 || PyUnicode_READ_CHAR(meth_name, len - 1) != '_' || PyUnicode_READ_CHAR(meth_name, len - 2) != '_')) {
|
||||
const char *cls_name = cls->tp_name;
|
||||
while (cls_name[0] == '_') {
|
||||
++cls_name;
|
||||
}
|
||||
meth_name = PyUnicode_FromFormat("_%s%S", cls_name, meth_name);
|
||||
} else {
|
||||
Py_INCREF(meth_name);
|
||||
}
|
||||
if (!find_method(cls, meth_name, code)) {
|
||||
// Not a matching method object, it's something else. Forget it.
|
||||
cls = nullptr;
|
||||
}
|
||||
Py_DECREF(meth_name);
|
||||
} else {
|
||||
cls = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the module name out of the frame's global scope.
|
||||
PyObject *py_mod_name = PyDict_GetItemString(frame->f_globals, "__name__");
|
||||
if (py_mod_name == nullptr && cls != nullptr) {
|
||||
py_mod_name = PyDict_GetItemString(cls->tp_dict, "__module__");
|
||||
}
|
||||
|
||||
const char *mod_name = py_mod_name ? PyUnicode_AsUTF8(py_mod_name) : "<unknown>";
|
||||
char buffer[1024];
|
||||
size_t len = snprintf(buffer, sizeof(buffer), "%s:", mod_name);
|
||||
for (size_t i = 0; i < len - 1; ++i) {
|
||||
if (buffer[i] == '.') {
|
||||
buffer[i] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
const char *meth_name = PyUnicode_AsUTF8(code->co_name);
|
||||
if (cls != nullptr) {
|
||||
len += snprintf(buffer + len, sizeof(buffer) - len, "%s:%s", cls->tp_name, meth_name);
|
||||
} else {
|
||||
len += snprintf(buffer + len, sizeof(buffer) - len, "%s", meth_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add parentheses, unless it's something special like <listcomp>
|
||||
if (len < sizeof(buffer) - 2 && buffer[len - 1] != '>') {
|
||||
buffer[len++] = '(';
|
||||
buffer[len++] = ')';
|
||||
buffer[len] = '\0';
|
||||
}
|
||||
|
||||
PStatCollector collector(code_collector, buffer);
|
||||
intptr_t collector_index = collector.get_index();
|
||||
if (_extra_index != -1) {
|
||||
_PyCode_SetExtra((PyObject *)code, _extra_index, (void *)collector_index);
|
||||
}
|
||||
return collector_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a collector for a C function.
|
||||
*/
|
||||
static int
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
__declspec(noinline)
|
||||
#endif
|
||||
make_c_function_collector(PyCFunctionObject *meth) {
|
||||
char buffer[1024];
|
||||
size_t len;
|
||||
if (meth->m_self != nullptr && !PyModule_Check(meth->m_self)) {
|
||||
PyTypeObject *cls = PyType_Check(meth->m_self) ? (PyTypeObject *)meth->m_self : Py_TYPE(meth->m_self);
|
||||
|
||||
const char *dot = strrchr(cls->tp_name, '.');
|
||||
if (dot != nullptr) {
|
||||
// The module name is included in the type name.
|
||||
snprintf(buffer, sizeof(buffer), "%s:%s()", cls->tp_name, meth->m_ml->ml_name);
|
||||
len = (dot - cls->tp_name) + 1;
|
||||
} else {
|
||||
// If there's no module name, we need to get it from __module__.
|
||||
PyObject *py_mod_name = cls->tp_dict ? PyDict_GetItemString(cls->tp_dict, "__module__") : nullptr;
|
||||
const char *mod_name;
|
||||
if (py_mod_name != nullptr) {
|
||||
mod_name = PyUnicode_AsUTF8(py_mod_name);
|
||||
} else {
|
||||
// Is it a built-in, like int or dict?
|
||||
PyObject *builtins = PyEval_GetBuiltins();
|
||||
if (PyDict_GetItemString(builtins, cls->tp_name) == (PyObject *)cls) {
|
||||
mod_name = "builtins";
|
||||
} else {
|
||||
mod_name = "<unknown>";
|
||||
}
|
||||
}
|
||||
len = snprintf(buffer, sizeof(buffer), "%s:%s:%s()", mod_name, cls->tp_name, meth->m_ml->ml_name) - 2;
|
||||
}
|
||||
}
|
||||
else if (meth->m_self != nullptr) {
|
||||
const char *mod_name = PyModule_GetName(meth->m_self);
|
||||
len = snprintf(buffer, sizeof(buffer), "%s:%s()", mod_name, meth->m_ml->ml_name) - 2;
|
||||
}
|
||||
else {
|
||||
snprintf(buffer, sizeof(buffer), "%s()", meth->m_ml->ml_name);
|
||||
len = 0;
|
||||
}
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (buffer[i] == '.') {
|
||||
buffer[i] = ':';
|
||||
}
|
||||
}
|
||||
PStatCollector collector(code_collector, buffer);
|
||||
int collector_index = collector.get_index();
|
||||
_c_method_collectors[meth->m_ml] = collector.get_index();
|
||||
return collector_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to establish a connection to the indicated PStatServer. Returns
|
||||
* true if successful, false on failure.
|
||||
*/
|
||||
bool Extension<PStatClient>::
|
||||
client_connect(std::string hostname, int port) {
|
||||
extern struct Dtool_PyTypedObject Dtool_PStatThread;
|
||||
|
||||
if (_this->client_connect(std::move(hostname), port)) {
|
||||
// Pass a PStatThread as argument.
|
||||
if (!_python_profiler_enabled && pstats_python_profiler) {
|
||||
PStatThread *thread = new PStatThread(_this->get_current_thread());
|
||||
PyObject *arg = DTool_CreatePyInstance((void *)thread, Dtool_PStatThread, true, false);
|
||||
if (_extra_index == -1) {
|
||||
_extra_index = _PyEval_RequestCodeExtraIndex(nullptr);
|
||||
}
|
||||
PyEval_SetProfile(&trace_callback, arg);
|
||||
_python_profiler_enabled = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (_python_profiler_enabled) {
|
||||
PyEval_SetProfile(nullptr, nullptr);
|
||||
_python_profiler_enabled = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection previously established.
|
||||
*/
|
||||
void Extension<PStatClient>::
|
||||
client_disconnect() {
|
||||
_this->client_disconnect();
|
||||
if (_python_profiler_enabled) {
|
||||
PyEval_SetProfile(nullptr, nullptr);
|
||||
_python_profiler_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback passed to PyEval_SetProfile.
|
||||
*/
|
||||
int Extension<PStatClient>::
|
||||
trace_callback(PyObject *py_thread, PyFrameObject *frame, int what, PyObject *arg) {
|
||||
intptr_t collector_index;
|
||||
|
||||
if (what == PyTrace_CALL || what == PyTrace_RETURN || what == PyTrace_EXCEPTION) {
|
||||
// Normal Python frame entry/exit.
|
||||
#if PY_VERSION_HEX >= 0x030B0000 // 3.11
|
||||
PyCodeObject *code = PyFrame_GetCode(frame);
|
||||
#else
|
||||
PyCodeObject *code = frame->f_code;
|
||||
#endif
|
||||
|
||||
// The index for this collector is cached on the code object.
|
||||
if (_PyCode_GetExtra((PyObject *)code, _extra_index, (void **)&collector_index) != 0 || collector_index == 0) {
|
||||
collector_index = make_python_frame_collector(frame, code);
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x030B0000 // 3.11
|
||||
Py_DECREF(code);
|
||||
#endif
|
||||
} else if (what == PyTrace_C_CALL || what == PyTrace_C_RETURN || what == PyTrace_C_EXCEPTION) {
|
||||
// Call to a C function or method, which has no frame of its own.
|
||||
if (PyCFunction_CheckExact(arg)) {
|
||||
PyCFunctionObject *meth = (PyCFunctionObject *)arg;
|
||||
auto it = _c_method_collectors.find(meth->m_ml);
|
||||
if (it != _c_method_collectors.end()) {
|
||||
collector_index = it->second;
|
||||
} else {
|
||||
collector_index = make_c_function_collector(meth);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collector_index <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
PStatThread &pthread = *(PStatThread *)DtoolInstance_VOID_PTR(py_thread);
|
||||
PStatClient *client = pthread.get_client();
|
||||
if (!client->client_is_connected()) {
|
||||
// Client was disconnected, disable Python profiling.
|
||||
PyEval_SetProfile(nullptr, nullptr);
|
||||
_python_profiler_enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int thread_index = pthread.get_index();
|
||||
|
||||
#ifdef _DEBUG
|
||||
nassertr(collector_index >= 0 && collector_index < client->get_num_collectors(), -1);
|
||||
nassertr(thread_index >= 0 && thread_index < client->get_num_threads(), -1);
|
||||
#endif
|
||||
|
||||
PStatClient::Collector *collector = client->get_collector_ptr(collector_index);
|
||||
PStatClient::InternalThread *thread = client->get_thread_ptr(thread_index);
|
||||
|
||||
if (collector->is_active() && thread->_is_active) {
|
||||
double as_of = client->get_real_time();
|
||||
|
||||
LightMutexHolder holder(thread->_thread_lock);
|
||||
if (thread->_thread_active) {
|
||||
if (what == PyTrace_CALL || what == PyTrace_C_CALL) {
|
||||
thread->_frame_data.add_start(collector_index, as_of);
|
||||
} else {
|
||||
thread->_frame_data.add_stop(collector_index, as_of);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON && DO_PSTATS
|
49
panda/src/pstatclient/pStatClient_ext.h
Normal file
49
panda/src/pstatclient/pStatClient_ext.h
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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."
|
||||
*
|
||||
* @file pStatClient_ext.h
|
||||
* @author rdb
|
||||
* @date 2022-11-23
|
||||
*/
|
||||
|
||||
#ifndef PSTATCLIENT_EXT_H
|
||||
#define PSTATCLIENT_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#if defined(HAVE_PYTHON) && defined(DO_PSTATS)
|
||||
|
||||
#include "extension.h"
|
||||
#include "pStatClient.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
typedef struct _frame PyFrameObject;
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for PStatClient, which are called
|
||||
* instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<PStatClient> : public ExtensionBase<PStatClient> {
|
||||
public:
|
||||
INLINE static bool connect(const std::string &hostname = std::string(), int port = -1);
|
||||
INLINE static void disconnect();
|
||||
|
||||
bool client_connect(std::string hostname, int port);
|
||||
void client_disconnect();
|
||||
|
||||
private:
|
||||
static int trace_callback(PyObject *py_thread, PyFrameObject *frame,
|
||||
int what, PyObject *arg);
|
||||
};
|
||||
|
||||
#include "pStatClient_ext.I"
|
||||
|
||||
#endif // HAVE_PYTHON && DO_PSTATS
|
||||
|
||||
#endif // PSTATCLIENT_EXT_H
|
@ -43,11 +43,9 @@ class Thread;
|
||||
class EXPCL_PANDA_PSTATCLIENT PStatCollector {
|
||||
#ifdef DO_PSTATS
|
||||
|
||||
private:
|
||||
INLINE PStatCollector(PStatClient *client, int index);
|
||||
|
||||
public:
|
||||
PStatCollector() = default;
|
||||
INLINE PStatCollector(PStatClient *client, int index);
|
||||
|
||||
PUBLISHED:
|
||||
INLINE explicit PStatCollector(const std::string &name,
|
||||
|
@ -27,9 +27,10 @@ using std::string;
|
||||
|
||||
static const int current_pstat_major_version = 3;
|
||||
static const int current_pstat_minor_version = 0;
|
||||
// Initialized at 2.0 on 51801, when version numbers were first added.
|
||||
// Incremented to 2.1 on 52101 to add support for TCP frame data. Incremented
|
||||
// to 3.0 on 42805 to bump TCP headers to 32 bits.
|
||||
// Initialized at 2.0 on 5/18/01, when version numbers were first added.
|
||||
// Incremented to 2.1 on 5/21/01 to add support for TCP frame data.
|
||||
// Incremented to 3.0 on 4/28/05 to bump TCP headers to 32 bits.
|
||||
// Incremented to 3.1 on 11/29/22 to support nested start/stop pairs.
|
||||
|
||||
/**
|
||||
* Returns the current major version number of the PStats protocol. This is
|
||||
|
@ -82,3 +82,11 @@ INLINE int PStatThread::
|
||||
get_index() const {
|
||||
return _index;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE PStatClient *PStatThread::
|
||||
get_client() const {
|
||||
return _client;
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ PUBLISHED:
|
||||
MAKE_PROPERTY(thread, get_thread);
|
||||
MAKE_PROPERTY(index, get_index);
|
||||
|
||||
public:
|
||||
PStatClient *get_client() const;
|
||||
|
||||
private:
|
||||
PStatClient *_client;
|
||||
int _index;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "gtkStatsMonitor.h"
|
||||
#include "pStatCollectorDef.h"
|
||||
#include "numeric_types.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
static const int default_strip_chart_width = 400;
|
||||
static const int default_strip_chart_height = 100;
|
||||
@ -119,7 +120,7 @@ new_data(int thread_index, int frame_number) {
|
||||
if (!_pause) {
|
||||
update();
|
||||
|
||||
std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
|
||||
std::string text = get_total_text();
|
||||
if (_net_value_text != text) {
|
||||
_net_value_text = text;
|
||||
gtk_label_set_text(GTK_LABEL(_total_label), _net_value_text.c_str());
|
||||
|
@ -194,7 +194,8 @@ handle_client_control_message(const PStatClientControlMessage &message) {
|
||||
|
||||
if (message._major_version != server_major_version ||
|
||||
(message._major_version == server_major_version &&
|
||||
message._minor_version > server_minor_version)) {
|
||||
message._minor_version > server_minor_version &&
|
||||
(message._major_version != 3 || message._minor_version != 1))) {
|
||||
_monitor->bad_version(message._client_hostname, message._client_progname,
|
||||
message._client_pid,
|
||||
message._major_version, message._minor_version,
|
||||
|
@ -306,6 +306,21 @@ get_title_text() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text suitable for the total label above the graph.
|
||||
*/
|
||||
std::string PStatStripChart::
|
||||
get_total_text() {
|
||||
std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
|
||||
if (get_collector_index() != 0 && !_view.get_show_level()) {
|
||||
const PStatViewLevel *level = _view.get_level(get_collector_index());
|
||||
if (level != nullptr && level->get_count() > 0) {
|
||||
text += " / " + format_string(level->get_count()) + "x";
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse hovers over a label, and should return the text that
|
||||
* should appear on the tooltip.
|
||||
|
@ -70,8 +70,9 @@ public:
|
||||
INLINE int height_to_pixel(double value) const;
|
||||
INLINE double pixel_to_height(int y) const;
|
||||
|
||||
bool is_title_unknown() const;
|
||||
INLINE bool is_title_unknown() const;
|
||||
std::string get_title_text();
|
||||
std::string get_total_text();
|
||||
std::string get_label_tooltip(int collector_index) const;
|
||||
|
||||
virtual void write_datagram(Datagram &dg) const final;
|
||||
|
@ -15,13 +15,9 @@
|
||||
|
||||
#include "pStatFrameData.h"
|
||||
#include "pStatCollectorDef.h"
|
||||
#include "vector_int.h"
|
||||
#include "plist.h"
|
||||
#include "pset.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* This class is used within this module only--in fact, within
|
||||
@ -30,94 +26,89 @@
|
||||
*/
|
||||
class FrameSample {
|
||||
public:
|
||||
typedef plist<FrameSample *> Started;
|
||||
void start(double time, FrameSample *started) {
|
||||
// Keep track of nested start/stop pairs. We only consider the outer one.
|
||||
if (_started++ > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FrameSample() {
|
||||
_touched = false;
|
||||
_is_started = false;
|
||||
_pushed = false;
|
||||
_net_time = 0.0;
|
||||
nassertv(!_pushed);
|
||||
_net_time -= time;
|
||||
push_all(time, started);
|
||||
nassertv(_next == nullptr && _prev == nullptr);
|
||||
_prev = started->_prev;
|
||||
_next = started;
|
||||
_prev->_next = this;
|
||||
started->_prev = this;
|
||||
}
|
||||
void data_point(double time, bool is_start, Started &started) {
|
||||
_touched = true;
|
||||
|
||||
// We only consider events that change the startstop state. With two
|
||||
// consecutive 'start' events, for instance, we ignore the second one.
|
||||
void stop(double time, FrameSample *started) {
|
||||
nassertv(_started > 0);
|
||||
if (--_started > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* *** That's not quite the right thing to do. We should keep track of the
|
||||
* nesting level and bracket things correctly, so that we ignore the second
|
||||
* start and the *first* stop, but respect the outer startstop. For the short
|
||||
* term, this works, because the client is already doing this logic and won't
|
||||
* send us nested startstop pairs, but we'd like to generalize this in the
|
||||
* future so we can deal with these nested pairs properly.
|
||||
*/
|
||||
nassertv(is_start != _is_started);
|
||||
|
||||
_is_started = is_start;
|
||||
nassertv(_next != nullptr && _prev != nullptr);
|
||||
|
||||
if (_pushed) {
|
||||
nassertv(!_is_started);
|
||||
Started::iterator si = find(started.begin(), started.end(), this);
|
||||
nassertv(si != started.end());
|
||||
started.erase(si);
|
||||
|
||||
_prev->_next = _next;
|
||||
_next->_prev = _prev;
|
||||
_next = _prev = nullptr;
|
||||
} else {
|
||||
if (_is_started) {
|
||||
_net_time -= time;
|
||||
push_all(time, started);
|
||||
started.push_back(this);
|
||||
} else {
|
||||
_net_time += time;
|
||||
Started::iterator si = find(started.begin(), started.end(), this);
|
||||
nassertv(si != started.end());
|
||||
started.erase(si);
|
||||
pop_one(time, started);
|
||||
}
|
||||
_net_time += time;
|
||||
_prev->_next = _next;
|
||||
_next->_prev = _prev;
|
||||
_next = _prev = nullptr;
|
||||
pop_one(time, started);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void push(double time) {
|
||||
if (!_pushed) {
|
||||
_pushed = true;
|
||||
if (_is_started) {
|
||||
if (_started > 0) {
|
||||
_net_time += time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pop(double time) {
|
||||
if (_pushed) {
|
||||
_pushed = false;
|
||||
if (_is_started) {
|
||||
if (_started > 0) {
|
||||
_net_time -= time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void push_all(double time, Started &started) {
|
||||
Started::iterator si;
|
||||
for (si = started.begin(); si != started.end(); ++si) {
|
||||
(*si)->push(time);
|
||||
void push_all(double time, FrameSample *started) {
|
||||
for (FrameSample *sample = started->_next;
|
||||
sample != started; sample = sample->_next) {
|
||||
sample->push(time);
|
||||
}
|
||||
}
|
||||
|
||||
void pop_one(double time, Started &started) {
|
||||
Started::reverse_iterator si;
|
||||
for (si = started.rbegin(); si != started.rend(); ++si) {
|
||||
if ((*si)->_pushed) {
|
||||
(*si)->pop(time);
|
||||
void pop_one(double time, FrameSample *started) {
|
||||
for (FrameSample *sample = started->_prev;
|
||||
sample != started; sample = sample->_prev) {
|
||||
if (sample->_pushed) {
|
||||
sample->pop(time);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _touched;
|
||||
bool _is_started;
|
||||
bool _pushed;
|
||||
double _net_time;
|
||||
public:
|
||||
FrameSample *_next = nullptr;
|
||||
FrameSample *_prev = nullptr;
|
||||
double _net_time = 0.0;
|
||||
int _started = 0;
|
||||
int _count = 0;
|
||||
bool _pushed = false;
|
||||
bool _is_new = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -279,19 +270,19 @@ get_level(int collector) {
|
||||
void PStatView::
|
||||
update_time_data(const PStatFrameData &frame_data) {
|
||||
int num_events = frame_data.get_num_events();
|
||||
int num_collectors = _client_data->get_num_collectors();
|
||||
|
||||
typedef pvector<FrameSample> Samples;
|
||||
Samples samples(_client_data->get_num_collectors());
|
||||
typedef std::vector<FrameSample> Samples;
|
||||
Samples samples(num_collectors);
|
||||
|
||||
FrameSample::Started started;
|
||||
// Keep a linked list of started samples.
|
||||
FrameSample started;
|
||||
started._next = &started;
|
||||
started._prev = &started;
|
||||
|
||||
_all_collectors_known = true;
|
||||
|
||||
|
||||
// This tracks the set of samples we actually care about.
|
||||
typedef pset<int> GotSamples;
|
||||
GotSamples got_samples;
|
||||
|
||||
int new_collectors = 0;
|
||||
int i;
|
||||
for (i = 0; i < num_events; i++) {
|
||||
int collector_index = frame_data.get_time_collector(i);
|
||||
@ -301,42 +292,40 @@ update_time_data(const PStatFrameData &frame_data) {
|
||||
_all_collectors_known = false;
|
||||
|
||||
} else {
|
||||
nassertv(collector_index >= 0 && collector_index < (int)samples.size());
|
||||
nassertv(collector_index >= 0 && collector_index < num_collectors);
|
||||
|
||||
if (_client_data->get_child_distance(_constraint, collector_index) >= 0) {
|
||||
// Here's a data point we care about: anything at constraint level or
|
||||
// below.
|
||||
if (is_start == samples[collector_index]._is_started) {
|
||||
if (!is_start) {
|
||||
// A "stop" in the middle of a frame implies a "start" since time
|
||||
// 0 (that is, since the first data point in the frame).
|
||||
samples[collector_index].data_point(frame_data.get_time(0), true, started);
|
||||
samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
|
||||
} else {
|
||||
// An extra "start" for a collector that's already started is an
|
||||
// error.
|
||||
nout << "Unexpected data point for "
|
||||
<< _client_data->get_collector_fullname(collector_index)
|
||||
<< "\n";
|
||||
}
|
||||
if (is_start) {
|
||||
samples[collector_index].start(frame_data.get_time(i), &started);
|
||||
samples[collector_index]._count++;
|
||||
} else {
|
||||
samples[collector_index].data_point(frame_data.get_time(i), is_start, started);
|
||||
got_samples.insert(collector_index);
|
||||
// A "stop" in the middle of a frame implies a "start" since time
|
||||
// 0 (that is, since the first data point in the frame).
|
||||
if (samples[collector_index]._started == 0) {
|
||||
samples[collector_index].start(frame_data.get_time(0), &started);
|
||||
}
|
||||
samples[collector_index].stop(frame_data.get_time(i), &started);
|
||||
}
|
||||
|
||||
if (!samples[collector_index]._is_new) {
|
||||
samples[collector_index]._is_new = true;
|
||||
++new_collectors;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure everything is stopped.
|
||||
|
||||
Samples::iterator si;
|
||||
for (i = 0, si = samples.begin(); si != samples.end(); ++i, ++si) {
|
||||
if ((*si)._is_started) {
|
||||
(*si).data_point(frame_data.get_end(), false, started);
|
||||
if ((*si)._started > 0) {
|
||||
(*si).stop(frame_data.get_end(), &started);
|
||||
}
|
||||
}
|
||||
|
||||
nassertv(started.empty());
|
||||
nassertv(started._next == &started && started._prev == &started);
|
||||
|
||||
bool any_new_levels = false;
|
||||
|
||||
@ -356,11 +345,11 @@ update_time_data(const PStatFrameData &frame_data) {
|
||||
}
|
||||
|
||||
int collector_index = level->_collector;
|
||||
GotSamples::iterator gi;
|
||||
gi = got_samples.find(collector_index);
|
||||
if (gi != got_samples.end()) {
|
||||
if (samples[collector_index]._is_new) {
|
||||
level->_value_alone = samples[collector_index]._net_time;
|
||||
got_samples.erase(gi);
|
||||
level->_count = samples[collector_index]._count;
|
||||
samples[collector_index]._is_new = false;
|
||||
--new_collectors;
|
||||
}
|
||||
|
||||
li = lnext;
|
||||
@ -368,14 +357,15 @@ update_time_data(const PStatFrameData &frame_data) {
|
||||
|
||||
// Finally, any samples left over in the got_samples set are new collectors
|
||||
// that we need to add to the Levels list.
|
||||
if (!got_samples.empty()) {
|
||||
if (new_collectors > 0) {
|
||||
any_new_levels = true;
|
||||
|
||||
GotSamples::const_iterator gi;
|
||||
for (gi = got_samples.begin(); gi != got_samples.end(); ++gi) {
|
||||
int collector_index = (*gi);
|
||||
PStatViewLevel *level = get_level(collector_index);
|
||||
level->_value_alone = samples[*gi]._net_time;
|
||||
for (int collector_index = 0; collector_index < num_collectors; ++collector_index) {
|
||||
if (samples[collector_index]._is_new) {
|
||||
PStatViewLevel *level = get_level(collector_index);
|
||||
level->_value_alone = samples[collector_index]._net_time;
|
||||
level->_count = samples[collector_index]._count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,6 +499,7 @@ bool PStatView::
|
||||
reset_level(PStatViewLevel *level) {
|
||||
bool any_changed = false;
|
||||
level->_value_alone = 0.0;
|
||||
level->_count = 0;
|
||||
|
||||
if (level->_collector == _constraint) {
|
||||
return false;
|
||||
|
@ -27,3 +27,11 @@ INLINE double PStatViewLevel::
|
||||
get_value_alone() const {
|
||||
return _value_alone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of start/stop pairs for this collector.
|
||||
*/
|
||||
INLINE int PStatViewLevel::
|
||||
get_count() const {
|
||||
return _count;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
INLINE int get_collector() const;
|
||||
INLINE double get_value_alone() const;
|
||||
double get_net_value() const;
|
||||
INLINE int get_count() const;
|
||||
|
||||
void sort_children(const PStatClientData *client_data);
|
||||
|
||||
@ -39,6 +40,7 @@ public:
|
||||
|
||||
private:
|
||||
int _collector;
|
||||
int _count = 0;
|
||||
double _value_alone;
|
||||
PStatViewLevel *_parent;
|
||||
|
||||
|
@ -97,7 +97,7 @@ new_data(int thread_index, int frame_number) {
|
||||
if (!_pause) {
|
||||
update();
|
||||
|
||||
std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
|
||||
std::string text = get_total_text();
|
||||
if (_net_value_text != text) {
|
||||
_net_value_text = text;
|
||||
RECT rect;
|
||||
|
Loading…
x
Reference in New Issue
Block a user