From 38bea01dab8f4dedd5fce9f8b9e82cebbf663189 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 22 Jan 2022 15:45:00 +0100 Subject: [PATCH 001/166] device: Do not enumerate keyboard/mouse devices on macOS by default This causes an annoying "this app would like to receive keystrokes from any application" alert to be shown Enable iokit-scan-mouse-devices or iokit-scan-keyboard-devices to restore the old behavior --- panda/src/device/ioKitInputDeviceManager.cxx | 27 +++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/panda/src/device/ioKitInputDeviceManager.cxx b/panda/src/device/ioKitInputDeviceManager.cxx index 3f1c807469..aa8d67301c 100644 --- a/panda/src/device/ioKitInputDeviceManager.cxx +++ b/panda/src/device/ioKitInputDeviceManager.cxx @@ -16,6 +16,18 @@ #if defined(__APPLE__) && !defined(CPPPARSER) +static ConfigVariableBool iokit_scan_mouse_devices +("iokit-scan-mouse-devices", false, + PRC_DESC("Set this to true to enable capturing raw mouse data via IOKit on " + "macOS. This is disabled by default because newer macOS versions " + "will prompt the user explicitly for permissions when this is on.")); + +static ConfigVariableBool iokit_scan_keyboard_devices +("iokit-scan-keyboard-devices", false, + PRC_DESC("Set this to true to enable capturing raw keyboard data via IOKit on " + "macOS. This is disabled by default because newer macOS versions " + "will prompt the user explicitly for permissions when this is on.")); + /** * Initializes the input device manager by scanning which devices are currently * connected and setting up any platform-dependent structures necessary for @@ -34,15 +46,22 @@ IOKitInputDeviceManager() { int page = kHIDPage_GenericDesktop; int usages[] = {kHIDUsage_GD_GamePad, kHIDUsage_GD_Joystick, - kHIDUsage_GD_Mouse, - kHIDUsage_GD_Keyboard, - kHIDUsage_GD_MultiAxisController, 0}; - int *usage = usages; + kHIDUsage_GD_MultiAxisController, + 0, 0, 0}; + + int num_usages = 3; + if (iokit_scan_mouse_devices) { + usages[num_usages++] = kHIDUsage_GD_Mouse; + } + if (iokit_scan_keyboard_devices) { + usages[num_usages++] = kHIDUsage_GD_Keyboard; + } // This giant mess is necessary to create an array of match dictionaries // that will match the devices we're interested in. CFMutableArrayRef match = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); nassertv(match); + int *usage = usages; while (*usage) { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFNumberRef page_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page); From 692221cacb284b40cf3fe4944089eada69207611 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 22 Jan 2022 15:56:53 +0100 Subject: [PATCH 002/166] cocoadisplay: Invert direction of horizontal scroll Now behaves consistent with other applications (tested with Logitech MX Master 3 for Mac on macOS 10.13 in unnatural scrolling configuration). Set `cocoa-invert-wheel-x true` to revert to old behaviour. --- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 15 ++++++++++----- panda/src/cocoadisplay/config_cocoadisplay.h | 2 ++ panda/src/cocoadisplay/config_cocoadisplay.mm | 5 +++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 87a3c96777..0b973a0317 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -2021,8 +2021,10 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { */ void CocoaGraphicsWindow:: handle_wheel_event(double x, double y) { - cocoadisplay_cat.spam() - << "Wheel delta " << x << ", " << y << "\n"; + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Wheel delta " << x << ", " << y << "\n"; + } if (y > 0.0) { _input->button_down(MouseButton::wheel_up()); @@ -2032,11 +2034,14 @@ handle_wheel_event(double x, double y) { _input->button_up(MouseButton::wheel_down()); } - // TODO: check if this is correct, I don't own a MacBook - if (x > 0.0) { + if (x != 0 && cocoa_invert_wheel_x) { + x = -x; + } + + if (x < 0.0) { _input->button_down(MouseButton::wheel_right()); _input->button_up(MouseButton::wheel_right()); - } else if (x < 0.0) { + } else if (x > 0.0) { _input->button_down(MouseButton::wheel_left()); _input->button_up(MouseButton::wheel_left()); } diff --git a/panda/src/cocoadisplay/config_cocoadisplay.h b/panda/src/cocoadisplay/config_cocoadisplay.h index 43a0e057c3..43051a67c5 100644 --- a/panda/src/cocoadisplay/config_cocoadisplay.h +++ b/panda/src/cocoadisplay/config_cocoadisplay.h @@ -20,6 +20,8 @@ NotifyCategoryDecl(cocoadisplay, EXPCL_PANDA_COCOADISPLAY, EXPTP_PANDA_COCOADISPLAY); +extern ConfigVariableBool cocoa_invert_wheel_x; + extern EXPCL_PANDA_COCOADISPLAY void init_libcocoadisplay(); #endif diff --git a/panda/src/cocoadisplay/config_cocoadisplay.mm b/panda/src/cocoadisplay/config_cocoadisplay.mm index 96662d6c9c..a79144769b 100644 --- a/panda/src/cocoadisplay/config_cocoadisplay.mm +++ b/panda/src/cocoadisplay/config_cocoadisplay.mm @@ -31,6 +31,11 @@ ConfigureFn(config_cocoadisplay) { init_libcocoadisplay(); } +ConfigVariableBool cocoa_invert_wheel_x +("cocoa-invert-wheel-x", false, + PRC_DESC("Set this to true to swap the wheel_left and wheel_right mouse " + "button events, to restore to the pre-1.10.12 behavior.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be From 8034cb5a926cbbbd5a66298f512d4c2e56fbee4a Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:11:29 +0100 Subject: [PATCH 003/166] dtoolbase: Introduce patomic<> as replacement for AtomicAdjust This typedefs to std::atomic<> when building with true threading, and uses a dummy implementation without. This lets us use the full range of atomic operations offered by C++11, including explicit specification of memory fences. Using barriers lets the compiler generate more optimal code since currently we are using the quite strict sequential-consistent memory ordering for all operations. ReferenceCount has been changed to use the correct barriers (I hope). This may especially make a difference on weak ordering systems such as ARM. Over time we should gradually replace the use of AtomicAdjust with the new patomic file. --- dtool/src/dtoolbase/dtoolbase_cc.h | 5 - dtool/src/dtoolbase/mutexSpinlockImpl.h | 2 - dtool/src/dtoolbase/patomic.I | 267 +++++++++++++++++++++++ dtool/src/dtoolbase/patomic.h | 108 +++++++++ dtool/src/dtoolbase/typeHandle.cxx | 30 ++- dtool/src/dtoolbase/typeRegistryNode.cxx | 3 +- dtool/src/dtoolbase/typeRegistryNode.h | 3 +- dtool/src/prc/notify.cxx | 7 +- panda/src/event/asyncFuture.cxx | 1 + panda/src/express/referenceCount.I | 81 ++++--- panda/src/express/referenceCount.cxx | 19 +- panda/src/express/referenceCount.h | 5 +- panda/src/express/weakPointerToBase.I | 7 + panda/src/express/weakReferenceList.I | 6 +- panda/src/express/weakReferenceList.cxx | 4 +- panda/src/express/weakReferenceList.h | 5 +- panda/src/pgui/pgScrollFrame.h | 7 +- panda/src/physics/physicalNode.cxx | 7 +- 18 files changed, 477 insertions(+), 90 deletions(-) create mode 100644 dtool/src/dtoolbase/patomic.I create mode 100644 dtool/src/dtoolbase/patomic.h diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h index de8baeedab..d19db7f5ef 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -49,8 +49,6 @@ // interrogate pass (CPPPARSER isn't defined), this maps to public. #define PUBLISHED __published -#define PHAVE_ATOMIC 1 - typedef int ios_openmode; typedef int ios_fmtflags; typedef int ios_iostate; @@ -112,9 +110,6 @@ typedef std::ios::seekdir ios_seekdir; #define INLINE inline #endif -// Expect that we have access to the header. -#define PHAVE_ATOMIC 1 - // Determine the availability of C++11 features. #if defined(_MSC_VER) && _MSC_VER < 1900 // Visual Studio 2015 #error Microsoft Visual C++ 2015 or later is required to compile Panda3D. diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.h b/dtool/src/dtoolbase/mutexSpinlockImpl.h index cd858f5551..ca84f58ea9 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.h +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.h @@ -19,9 +19,7 @@ #ifdef MUTEX_SPINLOCK -#ifdef PHAVE_ATOMIC #include -#endif /** * Uses a simple user-space spinlock to implement a mutex. It is usually not diff --git a/dtool/src/dtoolbase/patomic.I b/dtool/src/dtoolbase/patomic.I new file mode 100644 index 0000000000..dbb3934da0 --- /dev/null +++ b/dtool/src/dtoolbase/patomic.I @@ -0,0 +1,267 @@ +/** + * 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 patomic.I + * @author rdb + * @date 2022-01-28 + */ + +/** + * Value initializer. + */ +template +constexpr patomic:: +patomic(T desired) noexcept : _value(desired) { +} + +/** + * Returns true if this is a lock free type (which it always is). + */ +template +ALWAYS_INLINE bool patomic:: +is_lock_free() const noexcept { + return true; +} + +/** + * Returns the stored value. + */ +template +ALWAYS_INLINE T patomic:: +load(std::memory_order order) const noexcept { + return _value; +} + +/** + * Returns the stored value. + */ +template +ALWAYS_INLINE patomic:: +operator T() const noexcept { + return _value; +} + +/** + * Changes the stored value. + */ +template +ALWAYS_INLINE void patomic:: +store(T desired, std::memory_order order) noexcept { + _value = desired; +} + +/** + * Changes the stored value. + */ +template +ALWAYS_INLINE T patomic:: +operator=(T desired) noexcept { + _value = desired; +} + +/** + * Changes the stored value, returning the previous value. + */ +template +ALWAYS_INLINE T patomic:: +exchange(T desired, std::memory_order) noexcept { + T current = _value; + _value = desired; + return current; +} + +/** + * Sets the desired value if the current value is as the first argument. + * If it is not, the current value is written to expected. + */ +template +ALWAYS_INLINE bool patomic:: +compare_exchange_weak(T &expected, T desired, + std::memory_order, std::memory_order) noexcept { + T current = _value; + if (_value == expected) { + _value = desired; + return true; + } else { + expected = current; + return false; + } +} + +/** + * Sets the desired value if the current value is as the first argument. + * If it is not, the current value is written to expected. + */ +template +ALWAYS_INLINE bool patomic:: +compare_exchange_strong(T &expected, T desired, + std::memory_order, std::memory_order) noexcept { + T current = _value; + if (_value == expected) { + _value = desired; + return true; + } else { + expected = current; + return false; + } +} + +/** + * Adds to the stored value, returns the old value. + */ +template +ALWAYS_INLINE T patomic:: +fetch_add(T arg, std::memory_order) noexcept { + T old = _value; + _value += arg; + return old; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +fetch_sub(T arg, std::memory_order) noexcept { + T old = _value; + _value -= arg; + return old; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +fetch_and(T arg, std::memory_order) noexcept { + T old = _value; + _value &= arg; + return old; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +fetch_or(T arg, std::memory_order) noexcept { + T old = _value; + _value |= arg; + return old; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +fetch_xor(T arg, std::memory_order) noexcept { + T old = _value; + _value ^= arg; + return old; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator ++(int) noexcept { + return _value++; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator --(int) noexcept { + return _value--; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator ++() noexcept { + return ++_value; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator --() noexcept { + return --_value; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator +=(T arg) noexcept { + return _value += arg; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator -=(T arg) noexcept { + return _value -= arg; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator &=(T arg) noexcept { + return _value &= arg; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator |=(T arg) noexcept { + return _value |= arg; +} + +/** + * + */ +template +ALWAYS_INLINE T patomic:: +operator ^=(T arg) noexcept { + return _value ^= arg; +} + + +/** + * Sets the flag to true and returns the previous value. + */ +ALWAYS_INLINE bool patomic_flag:: +test_and_set(std::memory_order order) noexcept { + bool value = __internal_flag; + __internal_flag = true; + return value; +} + +/** + * Sets the flag to false. + */ +ALWAYS_INLINE void patomic_flag:: +clear(std::memory_order order) noexcept { + __internal_flag = false; +} diff --git a/dtool/src/dtoolbase/patomic.h b/dtool/src/dtoolbase/patomic.h new file mode 100644 index 0000000000..903391cf3e --- /dev/null +++ b/dtool/src/dtoolbase/patomic.h @@ -0,0 +1,108 @@ +/** + * 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 patomic.h + * @author rdb + * @date 2022-01-28 + */ + +#ifndef PATOMIC_H +#define PATOMIC_H + +#include "dtoolbase.h" +#include "selectThreadImpl.h" + +#include + +#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) + +/** + * Dummy implementation of std::atomic that does not do any atomic operations, + * used when compiling without HAVE_THREADS or with SIMPLE_THREADS. + */ +template +struct patomic { + using value_type = T; + + constexpr patomic() noexcept = default; + constexpr patomic(T desired) noexcept; + + ALWAYS_INLINE patomic(const patomic &) = delete; + ALWAYS_INLINE patomic &operator=(const patomic &) = delete; + + static constexpr bool is_always_lock_free = true; + ALWAYS_INLINE bool is_lock_free() const noexcept; + + ALWAYS_INLINE T load(std::memory_order order = std::memory_order_seq_cst) const noexcept; + ALWAYS_INLINE operator T() const noexcept; + + ALWAYS_INLINE void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE T operator=(T desired) noexcept; + + ALWAYS_INLINE T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept; + + ALWAYS_INLINE bool compare_exchange_weak(T &expected, T desired, + std::memory_order success = std::memory_order_seq_cst, + std::memory_order failure = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE bool compare_exchange_strong(T &expected, T desired, + std::memory_order success = std::memory_order_seq_cst, + std::memory_order failure = std::memory_order_seq_cst) noexcept; + + ALWAYS_INLINE T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE T fetch_and(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE T fetch_or(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE T fetch_xor(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept; + + ALWAYS_INLINE T operator ++(int) noexcept; + ALWAYS_INLINE T operator --(int) noexcept; + ALWAYS_INLINE T operator ++() noexcept; + ALWAYS_INLINE T operator --() noexcept; + ALWAYS_INLINE T operator +=(T arg) noexcept; + ALWAYS_INLINE T operator -=(T arg) noexcept; + ALWAYS_INLINE T operator &=(T arg) noexcept; + ALWAYS_INLINE T operator |=(T arg) noexcept; + ALWAYS_INLINE T operator ^=(T arg) noexcept; + +private: + T _value; +}; + +/** + * Dummy implementation of std::atomic_flag that does not do any atomic + * operations. + */ +struct EXPCL_DTOOL_DTOOLBASE patomic_flag { + constexpr patomic_flag() noexcept = default; + + patomic_flag(const patomic_flag &) = delete; + patomic_flag &operator=(const patomic_flag &) = delete; + + ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept; + + bool __internal_flag = false; +}; + +#define patomic_thread_fence(order) (std::atomic_signal_fence((order))) + +#include "patomic.I" + +#else + +// We're using real threading, so use the real implementation. +template +using patomic = std::atomic; + +typedef std::atomic_flag patomic_flag; + +#define patomic_thread_fence(order) (std::atomic_thread_fence((order))) + +#endif + +#endif diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index c72c3f9d99..6b7ee5aaf7 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -13,7 +13,6 @@ #include "typeHandle.h" #include "typeRegistryNode.h" -#include "atomicAdjust.h" /** * Returns the total allocated memory used by objects of this type, for the @@ -29,7 +28,7 @@ get_memory_usage(MemoryClass memory_class) const { } else { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - return (size_t)AtomicAdjust::get(rnode->_memory_usage[memory_class]); + return rnode->_memory_usage[memory_class].load(std::memory_order_relaxed); } #endif // DO_MEMORY_USAGE return 0; @@ -48,10 +47,8 @@ inc_memory_usage(MemoryClass memory_class, size_t size) { if ((*this) != TypeHandle::none()) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - AtomicAdjust::add(rnode->_memory_usage[memory_class], (AtomicAdjust::Integer)size); - // cerr << *this << ".inc(" << memory_class << ", " << size << ") -> " << - // rnode->_memory_usage[memory_class] << "\n"; - if (rnode->_memory_usage[memory_class] < 0) { + size_t prev = rnode->_memory_usage[memory_class].fetch_add(size, std::memory_order_relaxed); + if (prev + size < prev) { std::cerr << "Memory usage overflow for type " << rnode->_name << ".\n"; abort(); } @@ -72,10 +69,8 @@ dec_memory_usage(MemoryClass memory_class, size_t size) { if ((*this) != TypeHandle::none()) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - AtomicAdjust::add(rnode->_memory_usage[memory_class], -(AtomicAdjust::Integer)size); - // cerr << *this << ".dec(" << memory_class << ", " << size << ") -> " << - // rnode->_memory_usage[memory_class] << "\n"; - assert(rnode->_memory_usage[memory_class] >= 0); + size_t prev = rnode->_memory_usage[memory_class].fetch_sub(size, std::memory_order_relaxed); + assert(prev - size <= prev); } #endif // DO_MEMORY_USAGE } @@ -97,8 +92,8 @@ allocate_array(size_t size) { #endif TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)alloc_size); - if (rnode->_memory_usage[MC_array] < 0) { + size_t prev = rnode->_memory_usage[MC_array].fetch_add(alloc_size, std::memory_order_relaxed); + if (prev + size < prev) { std::cerr << "Memory usage overflow for type " << rnode->_name << ".\n"; abort(); } @@ -124,8 +119,11 @@ reallocate_array(void *old_ptr, size_t size) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)new_size - (AtomicAdjust::Integer)old_size); - assert(rnode->_memory_usage[MC_array] >= 0); + if (new_size > old_size) { + rnode->_memory_usage[MC_array].fetch_add(new_size - old_size, std::memory_order_relaxed); + } else { + rnode->_memory_usage[MC_array].fetch_sub(old_size - new_size, std::memory_order_relaxed); + } } #else void *new_ptr = PANDA_REALLOC_ARRAY(old_ptr, size); @@ -146,8 +144,8 @@ deallocate_array(void *ptr) { if ((*this) != TypeHandle::none()) { TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr); assert(rnode != nullptr); - AtomicAdjust::add(rnode->_memory_usage[MC_array], -(AtomicAdjust::Integer)alloc_size); - assert(rnode->_memory_usage[MC_array] >= 0); + size_t prev = rnode->_memory_usage[MC_array].fetch_sub(alloc_size, std::memory_order_relaxed); + assert(prev - alloc_size <= prev); } #endif // DO_MEMORY_USAGE PANDA_FREE_ARRAY(ptr); diff --git a/dtool/src/dtoolbase/typeRegistryNode.cxx b/dtool/src/dtoolbase/typeRegistryNode.cxx index 19b4629236..291a6ec95a 100644 --- a/dtool/src/dtoolbase/typeRegistryNode.cxx +++ b/dtool/src/dtoolbase/typeRegistryNode.cxx @@ -23,10 +23,9 @@ bool TypeRegistryNode::_paranoid_inheritance = false; */ TypeRegistryNode:: TypeRegistryNode(TypeHandle handle, const std::string &name, TypeHandle &ref) : - _handle(handle), _name(name), _ref(ref) + _handle(handle), _name(name), _ref(ref), _memory_usage{} { clear_subtree(); - memset(_memory_usage, 0, sizeof(_memory_usage)); } /** diff --git a/dtool/src/dtoolbase/typeRegistryNode.h b/dtool/src/dtoolbase/typeRegistryNode.h index 7dd7f387cc..6ff2275103 100644 --- a/dtool/src/dtoolbase/typeRegistryNode.h +++ b/dtool/src/dtoolbase/typeRegistryNode.h @@ -18,6 +18,7 @@ #include "typeHandle.h" #include "numeric_types.h" +#include "patomic.h" #include #include @@ -50,7 +51,7 @@ public: Classes _child_classes; PyObject *_python_type = nullptr; - AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit]; + patomic _memory_usage[TypeHandle::MC_limit]; static bool _paranoid_inheritance; diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index 2c1b737ddb..2a82f2926d 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -18,13 +18,10 @@ #include "configVariableBool.h" #include "filename.h" #include "config_prc.h" +#include "patomic.h" #include -#ifdef PHAVE_ATOMIC -#include -#endif - #ifdef BUILD_IPHONE #include #endif @@ -439,7 +436,7 @@ config_initialized() { "The filename to which to write all the output of notify"); // We use this to ensure that only one thread can initialize the output. - static std::atomic_flag initialized = ATOMIC_FLAG_INIT; + static patomic_flag initialized = ATOMIC_FLAG_INIT; std::string value = notify_output.get_value(); if (!value.empty() && !initialized.test_and_set()) { diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx index 696680bb1a..45bcc0e392 100644 --- a/panda/src/event/asyncFuture.cxx +++ b/panda/src/event/asyncFuture.cxx @@ -39,6 +39,7 @@ AsyncFuture:: if (result_ref != nullptr) { _result_ref.cheat() = nullptr; if (!result_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete _result; } _result = nullptr; diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I index f4d71b1117..0344ed3839 100644 --- a/panda/src/express/referenceCount.I +++ b/panda/src/express/referenceCount.I @@ -27,9 +27,9 @@ TypeHandle RefCountObj::_type_handle; * inheritance. */ INLINE ReferenceCount:: -ReferenceCount() { - _weak_list = nullptr; - _ref_count = 0; +ReferenceCount() : + _weak_list(nullptr), + _ref_count(0) { #ifdef DO_MEMORY_USAGE MemoryUsage::record_pointer(this); #endif @@ -44,9 +44,9 @@ ReferenceCount() { * try. */ INLINE ReferenceCount:: -ReferenceCount(const ReferenceCount &) { - _weak_list = nullptr; - _ref_count = 0; +ReferenceCount(const ReferenceCount &) : + _weak_list(nullptr), + _ref_count(0) { #ifdef DO_MEMORY_USAGE MemoryUsage::record_pointer(this); #endif @@ -69,7 +69,7 @@ operator = (const ReferenceCount &) { // to create an automatic (local variable) instance of a class that derives // from ReferenceCount. Or maybe your headers are out of sync, and you need // to make clean in direct or some higher tree. - nassertv(_ref_count != deleted_ref_count); + nassertv(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count); } /** @@ -78,23 +78,29 @@ operator = (const ReferenceCount &) { ReferenceCount:: ~ReferenceCount() { TAU_PROFILE("ReferenceCount::~ReferenceCount()", " ", TAU_USER); + + // We can safely use relaxed ordering for everything in this destructor, + // since (1) we already issued an acquire barrier before invoking delete, + // and (2) we are the only thread accessing this object at this point. + int ref_count = _ref_count.load(std::memory_order_relaxed); + // If this assertion fails, we're trying to delete an object that was just // deleted. Possibly you used a real pointer instead of a PointerTo at some // point, and the object was deleted when the PointerTo went out of scope. // Maybe you tried to create an automatic (local variable) instance of a // class that derives from ReferenceCount. Or maybe your headers are out of // sync, and you need to make clean in direct or some higher tree. - nassertv(_ref_count != deleted_ref_count); + nassertv(ref_count != deleted_ref_count); // If this assertion fails, we're trying to delete a static object that // still has an outstanding reference count. You should make sure that all // references to your static objects are gone by the time the object itself // destructs. - nassertv(_ref_count <= local_ref_count); + nassertv(ref_count <= local_ref_count); // If this assertion fails, the reference counts are all screwed up // altogether. Maybe some errant code stomped all over memory somewhere. - nassertv(_ref_count >= 0); + nassertv(ref_count >= 0); // If this assertion fails, someone tried to delete this object while its // reference count was still positive. Maybe you tried to point a PointerTo @@ -105,19 +111,20 @@ ReferenceCount:: // Another possibility is you inadvertently omitted a copy constructor for a // ReferenceCount object, and then bitwise copied a dynamically allocated // value--reference count and all--onto a locally allocated one. - nassertv(_ref_count == 0 || _ref_count == local_ref_count); + nassertv(ref_count == 0 || ref_count == local_ref_count); // Tell our weak reference holders that we're going away now. - if (_weak_list != nullptr) { + WeakReferenceList *weak_list = _weak_list.load(std::memory_order_relaxed); + if (weak_list != nullptr) { ((WeakReferenceList *)_weak_list)->mark_deleted(); - _weak_list = nullptr; + _weak_list.store(nullptr, std::memory_order_release); } #ifndef NDEBUG // Ok, all clear to delete. Now set the reference count to // deleted_ref_count, so we'll have a better chance of noticing if we happen // to have a stray pointer to it still out there. - _ref_count = deleted_ref_count; + _ref_count.store(deleted_ref_count, std::memory_order_relaxed); #endif #ifdef DO_MEMORY_USAGE @@ -133,7 +140,7 @@ get_ref_count() const { #ifdef _DEBUG test_ref_count_integrity(); #endif - return (int)AtomicAdjust::get(_ref_count); + return _ref_count.load(std::memory_order_acquire); } /** @@ -154,7 +161,7 @@ ref() const { nassertv(test_ref_count_integrity()); #endif - AtomicAdjust::inc(_ref_count); + _ref_count.fetch_add(1, std::memory_order_relaxed); } /** @@ -184,9 +191,9 @@ unref() const { // If this assertion fails, you tried to unref an object with a zero // reference count. Are you using ref() and unref() directly? Are you sure // you can't use PointerTo's? - nassertr(_ref_count > 0, 0); + nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0); #endif - return AtomicAdjust::dec(_ref_count); + return _ref_count.fetch_sub(1, std::memory_order_release) != 1; } /** @@ -227,11 +234,11 @@ test_ref_count_nonzero() const { */ INLINE void ReferenceCount:: local_object() { + int prev_count = _ref_count.exchange(local_ref_count, std::memory_order_relaxed); + // If this assertion fails, you didn't call this immediately after creating // a local object. - nassertv(_ref_count == 0); - - _ref_count = local_ref_count; + nassertv(prev_count == 0); } /** @@ -242,7 +249,7 @@ local_object() { */ INLINE bool ReferenceCount:: has_weak_list() const { - return _weak_list != nullptr; + return _weak_list.load(std::memory_order_relaxed) != nullptr; } /** @@ -255,10 +262,10 @@ has_weak_list() const { */ INLINE WeakReferenceList *ReferenceCount:: get_weak_list() const { - if (AtomicAdjust::get_ptr(_weak_list) == nullptr) { + if (_weak_list.load(std::memory_order_relaxed) == nullptr) { ((ReferenceCount *)this)->create_weak_list(); } - return (WeakReferenceList *)AtomicAdjust::get_ptr(_weak_list); + return _weak_list.load(std::memory_order_consume); } /** @@ -273,7 +280,7 @@ weak_ref() { #ifdef _DEBUG nassertr(test_ref_count_integrity(), nullptr); #else - nassertr(_ref_count != deleted_ref_count, nullptr); + nassertr(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count, nullptr); #endif WeakReferenceList *weak_ref = get_weak_list(); weak_ref->ref(); @@ -290,7 +297,7 @@ weak_unref() { #ifdef _DEBUG nassertv(test_ref_count_integrity()); #endif - WeakReferenceList *weak_list = (WeakReferenceList *)_weak_list; + WeakReferenceList *weak_list = _weak_list.load(std::memory_order_consume); nassertv(weak_list != nullptr); bool nonzero = weak_list->unref(); nassertv(nonzero); @@ -307,13 +314,16 @@ ref_if_nonzero() const { #ifdef _DEBUG test_ref_count_integrity(); #endif - AtomicAdjust::Integer ref_count; + int ref_count = _ref_count.load(std::memory_order_relaxed); do { - ref_count = AtomicAdjust::get(_ref_count); if (ref_count <= 0) { return false; } - } while (ref_count != AtomicAdjust::compare_and_exchange(_ref_count, ref_count, ref_count + 1)); + } + while (!_ref_count.compare_exchange_weak(ref_count, ref_count + 1, + std::memory_order_seq_cst, + std::memory_order_relaxed)); + return true; } @@ -321,15 +331,21 @@ ref_if_nonzero() const { * Atomically decreases the reference count of this object if it is one. * Do not use this. This exists only to implement a special case with the * state cache. - * @return false if the reference count was decremented to zero. + * @return false on success, ie. if the reference count was decremented to 0. */ INLINE bool ReferenceCount:: unref_if_one() const { #ifdef _DEBUG nassertr(test_ref_count_integrity(), 0); - nassertr(_ref_count > 0, 0); + nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0); #endif - return (AtomicAdjust::compare_and_exchange(_ref_count, 1, 0) != 1); + + // Presumably if the ref count becomes 0, someone is about to delete the + // object or something like that, hence the acquire order on success. + int expected = 1; + return !_ref_count.compare_exchange_strong(expected, 0, + std::memory_order_acquire, + std::memory_order_relaxed); } /** @@ -350,6 +366,7 @@ unref_delete(RefCountType *ptr) { if (!ptr->unref()) { // If the reference count has gone to zero, delete the object. + patomic_thread_fence(std::memory_order_acquire); delete ptr; } } diff --git a/panda/src/express/referenceCount.cxx b/panda/src/express/referenceCount.cxx index f446b3be46..0d5f98ea84 100644 --- a/panda/src/express/referenceCount.cxx +++ b/panda/src/express/referenceCount.cxx @@ -23,17 +23,19 @@ TypeHandle ReferenceCount::_type_handle; */ bool ReferenceCount:: do_test_ref_count_integrity() const { + int ref_count = _ref_count.load(std::memory_order_relaxed); + // If this assertion fails, we're trying to delete an object that was just // deleted. Possibly you used a real pointer instead of a PointerTo at some // point, and the object was deleted when the PointerTo went out of scope. // Maybe you tried to create an automatic (local variable) instance of a // class that derives from ReferenceCount. Or maybe your headers are out of // sync, and you need to make clean in direct or some higher tree. - nassertr(_ref_count != deleted_ref_count, false); + nassertr(ref_count != deleted_ref_count, false); // If this assertion fails, the reference counts are all screwed up // altogether. Maybe some errant code stomped all over memory somewhere. - nassertr(_ref_count >= 0, false); + nassertr(ref_count >= 0, false); return true; } @@ -44,7 +46,7 @@ do_test_ref_count_integrity() const { bool ReferenceCount:: do_test_ref_count_nonzero() const { nassertr(do_test_ref_count_integrity(), false); - nassertr(_ref_count > 0, false); + nassertr(_ref_count.load(std::memory_order_relaxed) > 0, false); return true; } @@ -54,11 +56,12 @@ do_test_ref_count_nonzero() const { */ void ReferenceCount:: create_weak_list() { - WeakReferenceList *weak_list = new WeakReferenceList; - void *orig = - AtomicAdjust::compare_and_exchange_ptr(_weak_list, nullptr, weak_list); - if (orig != nullptr) { + WeakReferenceList *new_list = new WeakReferenceList; + WeakReferenceList *old_list = nullptr; + if (!_weak_list.compare_exchange_strong(old_list, new_list, + std::memory_order_release, + std::memory_order_relaxed)) { // Someone else created it first. - delete weak_list; + delete new_list; } } diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h index 09ef7b4501..b57b85d4a3 100644 --- a/panda/src/express/referenceCount.h +++ b/panda/src/express/referenceCount.h @@ -23,6 +23,7 @@ #include "atomicAdjust.h" #include "numeric_types.h" #include "deletedChain.h" +#include "patomic.h" #include @@ -89,8 +90,8 @@ private: deleted_ref_count = -100, }; - mutable AtomicAdjust::Integer _ref_count; - AtomicAdjust::Pointer _weak_list; // WeakReferenceList * + mutable patomic _ref_count; + patomic _weak_list; public: static TypeHandle get_class_type() { diff --git a/panda/src/express/weakPointerToBase.I b/panda/src/express/weakPointerToBase.I index 3b27047da1..11b04dabfe 100644 --- a/panda/src/express/weakPointerToBase.I +++ b/panda/src/express/weakPointerToBase.I @@ -117,6 +117,7 @@ INLINE WeakPointerToBase:: ~WeakPointerToBase() { WeakReferenceList *old_ref = (WeakReferenceList *)_weak_ref; if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -143,6 +144,7 @@ reassign(To *ptr) { // Now remove the old reference. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -183,6 +185,7 @@ reassign(const WeakPointerToBase ©) { // Now remove the old reference. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -205,6 +208,7 @@ reassign(WeakPointerToBase &&from) noexcept { // Now delete the old pointer. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -233,6 +237,7 @@ reassign(const WeakPointerToBase ©) { // Now remove the old reference. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -260,6 +265,7 @@ reassign(WeakPointerToBase &&from) noexcept { // Now delete the old pointer. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } @@ -627,6 +633,7 @@ clear() { // Now remove the old reference. if (old_ref != nullptr && !old_ref->unref()) { + patomic_thread_fence(std::memory_order_acquire); delete old_ref; } } diff --git a/panda/src/express/weakReferenceList.I b/panda/src/express/weakReferenceList.I index eb6e7b9976..c9368cc51b 100644 --- a/panda/src/express/weakReferenceList.I +++ b/panda/src/express/weakReferenceList.I @@ -18,7 +18,7 @@ */ INLINE void WeakReferenceList:: ref() const { - AtomicAdjust::inc(_count); + _count.fetch_add(1, std::memory_order_relaxed); } /** @@ -30,7 +30,7 @@ ref() const { */ INLINE bool WeakReferenceList:: unref() const { - return AtomicAdjust::dec(_count); + return _count.fetch_sub(1, std::memory_order_release) != 1; } /** @@ -41,5 +41,5 @@ unref() const { */ INLINE bool WeakReferenceList:: was_deleted() const { - return AtomicAdjust::get(_count) < _alive_offset; + return _count.load(std::memory_order_relaxed) < _alive_offset; } diff --git a/panda/src/express/weakReferenceList.cxx b/panda/src/express/weakReferenceList.cxx index ac1363c85a..438a10df1c 100644 --- a/panda/src/express/weakReferenceList.cxx +++ b/panda/src/express/weakReferenceList.cxx @@ -27,7 +27,7 @@ WeakReferenceList() : _count(_alive_offset) { */ WeakReferenceList:: ~WeakReferenceList() { - nassertv(_count == 0); + nassertv(_count.load(std::memory_order_relaxed) == 0); } /** @@ -91,7 +91,7 @@ mark_deleted() { // Decrement the special offset added to the weak pointer count to indicate // that it can be deleted when all the weak references have gone. - AtomicAdjust::Integer result = AtomicAdjust::add(_count, -_alive_offset); + int result = _count.fetch_sub(_alive_offset, std::memory_order_relaxed) - _alive_offset; _lock.unlock(); if (result == 0) { // There are no weak references remaining either, so delete this. diff --git a/panda/src/express/weakReferenceList.h b/panda/src/express/weakReferenceList.h index 30ab68edae..69874741c5 100644 --- a/panda/src/express/weakReferenceList.h +++ b/panda/src/express/weakReferenceList.h @@ -17,6 +17,7 @@ #include "pandabase.h" #include "pmap.h" #include "mutexImpl.h" +#include "patomic.h" class WeakPointerCallback; @@ -53,8 +54,8 @@ private: // This has a very large number added to it if the object is still alive. // It could be 1, but having it be a large number makes it easy to check // whether the object has been deleted or not. - static const AtomicAdjust::Integer _alive_offset = (1 << 30); - mutable AtomicAdjust::Integer _count; + static const int _alive_offset = (1 << 30); + mutable patomic _count; friend class ReferenceCount; }; diff --git a/panda/src/pgui/pgScrollFrame.h b/panda/src/pgui/pgScrollFrame.h index bb8b3a3af3..5a7c19386d 100644 --- a/panda/src/pgui/pgScrollFrame.h +++ b/panda/src/pgui/pgScrollFrame.h @@ -19,10 +19,7 @@ #include "pgVirtualFrame.h" #include "pgSliderBarNotify.h" #include "pgSliderBar.h" - -#ifdef PHAVE_ATOMIC -#include -#endif +#include "patomic.h" /** * This is a special kind of frame that pretends to be much larger than it @@ -96,7 +93,7 @@ private: private: bool _needs_remanage; bool _needs_recompute_clip; - std::atomic_flag _canvas_computed; + patomic_flag _canvas_computed; bool _has_virtual_frame; LVecBase4 _virtual_frame; diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx index d6103101e6..dee169bfe5 100644 --- a/panda/src/physics/physicalNode.cxx +++ b/panda/src/physics/physicalNode.cxx @@ -13,13 +13,10 @@ #include "physicalNode.h" #include "physicsManager.h" - -#ifdef PHAVE_ATOMIC -#include -#endif +#include "patomic.h" // static stuff. -static std::atomic_flag warned_copy_physical_node = ATOMIC_FLAG_INIT; +static patomic_flag warned_copy_physical_node = ATOMIC_FLAG_INIT; TypeHandle PhysicalNode::_type_handle; From 36edb22fd1e4392bd34536afd1119db0648f4947 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:23:55 +0100 Subject: [PATCH 004/166] pstats: Include client pid with hello message Useful for uniquely identifying the process in a situation where multiple clients on the same host connect with the same server. New version bump is not necessary, as old servers should just ignore the extra field in the datagram. --- panda/src/pstatclient/pStatClientControlMessage.cxx | 6 ++++++ panda/src/pstatclient/pStatClientControlMessage.h | 1 + panda/src/pstatclient/pStatClientImpl.cxx | 5 +++++ pandatool/src/pstatserver/pStatMonitor.I | 8 ++++++++ pandatool/src/pstatserver/pStatMonitor.cxx | 6 ++++-- pandatool/src/pstatserver/pStatMonitor.h | 6 +++++- pandatool/src/pstatserver/pStatReader.cxx | 4 +++- 7 files changed, 32 insertions(+), 4 deletions(-) diff --git a/panda/src/pstatclient/pStatClientControlMessage.cxx b/panda/src/pstatclient/pStatClientControlMessage.cxx index e7fd765547..5cbac136e4 100644 --- a/panda/src/pstatclient/pStatClientControlMessage.cxx +++ b/panda/src/pstatclient/pStatClientControlMessage.cxx @@ -39,6 +39,7 @@ encode(Datagram &datagram) const { datagram.add_string(_client_progname); datagram.add_uint16(_major_version); datagram.add_uint16(_minor_version); + datagram.add_uint32(_client_pid); break; case T_define_collectors: @@ -86,6 +87,11 @@ decode(const Datagram &datagram, PStatClientVersion *version) { _major_version = source.get_uint16(); _minor_version = source.get_uint16(); } + if (source.get_remaining_size() >= 4) { + _client_pid = source.get_uint32(); + } else { + _client_pid = -1; + } break; case T_define_collectors: diff --git a/panda/src/pstatclient/pStatClientControlMessage.h b/panda/src/pstatclient/pStatClientControlMessage.h index 97e7dede98..32a9f65f70 100644 --- a/panda/src/pstatclient/pStatClientControlMessage.h +++ b/panda/src/pstatclient/pStatClientControlMessage.h @@ -47,6 +47,7 @@ public: // Used for T_hello std::string _client_hostname; std::string _client_progname; + int _client_pid; int _major_version; int _minor_version; diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index f25b863b97..dcf0cdfef2 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -396,6 +396,11 @@ send_hello() { message._type = PStatClientControlMessage::T_hello; message._client_hostname = get_hostname(); message._client_progname = _client_name; +#ifdef _WIN32 + message._client_pid = GetCurrentProcessId(); +#else + message._client_pid = getpid(); +#endif message._major_version = get_current_pstat_major_version(); message._minor_version = get_current_pstat_minor_version(); diff --git a/pandatool/src/pstatserver/pStatMonitor.I b/pandatool/src/pstatserver/pStatMonitor.I index 761b7b9a79..ac9f2e46f9 100644 --- a/pandatool/src/pstatserver/pStatMonitor.I +++ b/pandatool/src/pstatserver/pStatMonitor.I @@ -69,3 +69,11 @@ INLINE std::string PStatMonitor:: get_client_progname() const { return _client_progname; } + +/** + * Returns the process id of the client, or -1 if it is not known. + */ +INLINE int PStatMonitor:: +get_client_pid() const { + return _client_pid; +} diff --git a/pandatool/src/pstatserver/pStatMonitor.cxx b/pandatool/src/pstatserver/pStatMonitor.cxx index 09e22b093a..20444352c1 100644 --- a/pandatool/src/pstatserver/pStatMonitor.cxx +++ b/pandatool/src/pstatserver/pStatMonitor.cxx @@ -38,10 +38,11 @@ PStatMonitor:: * indicates the client's reported hostname and program name. */ void PStatMonitor:: -hello_from(const string &hostname, const string &progname) { +hello_from(const string &hostname, const string &progname, int pid) { _client_known = true; _client_hostname = hostname; _client_progname = progname; + _client_pid = pid; got_hello(); } @@ -52,12 +53,13 @@ hello_from(const string &hostname, const string &progname) { * effect. */ void PStatMonitor:: -bad_version(const string &hostname, const string &progname, +bad_version(const string &hostname, const string &progname, int pid, int client_major, int client_minor, int server_major, int server_minor) { _client_known = true; _client_hostname = hostname; _client_progname = progname; + _client_pid = 0; got_bad_version(client_major, client_minor, server_major, server_minor); } diff --git a/pandatool/src/pstatserver/pStatMonitor.h b/pandatool/src/pstatserver/pStatMonitor.h index f7e92073c4..8c1259c9f0 100644 --- a/pandatool/src/pstatserver/pStatMonitor.h +++ b/pandatool/src/pstatserver/pStatMonitor.h @@ -43,8 +43,10 @@ public: PStatMonitor(PStatServer *server); virtual ~PStatMonitor(); - void hello_from(const std::string &hostname, const std::string &progname); + void hello_from(const std::string &hostname, const std::string &progname, + int pid); void bad_version(const std::string &hostname, const std::string &progname, + int pid, int client_major, int client_minor, int server_major, int server_minor); void set_client_data(PStatClientData *client_data); @@ -63,6 +65,7 @@ public: INLINE bool is_client_known() const; INLINE std::string get_client_hostname() const; INLINE std::string get_client_progname() const; + INLINE int get_client_pid() const; PStatView &get_view(int thread_index); PStatView &get_level_view(int collector_index, int thread_index); @@ -98,6 +101,7 @@ private: bool _client_known; std::string _client_hostname; std::string _client_progname; + int _client_pid; typedef pmap Views; Views _views; diff --git a/pandatool/src/pstatserver/pStatReader.cxx b/pandatool/src/pstatserver/pStatReader.cxx index 02b01f26a2..a2a1b4b7c9 100644 --- a/pandatool/src/pstatserver/pStatReader.cxx +++ b/pandatool/src/pstatserver/pStatReader.cxx @@ -195,11 +195,13 @@ handle_client_control_message(const PStatClientControlMessage &message) { (message._major_version == server_major_version && message._minor_version > server_minor_version)) { _monitor->bad_version(message._client_hostname, message._client_progname, + message._client_pid, message._major_version, message._minor_version, server_major_version, server_minor_version); _monitor->close(); } else { - _monitor->hello_from(message._client_hostname, message._client_progname); + _monitor->hello_from(message._client_hostname, message._client_progname, + message._client_pid); } } break; From c7c1c683da1b05b19b72e459d2711f2a6c12434d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:29:49 +0100 Subject: [PATCH 005/166] pstats: Disable "App:Show code:General" collector for now It is generating negative values, needs further investigation - maybe we need to restructure the whole hierarchy --- panda/src/pstatclient/pStatProperties.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index cce31e693f..7f4aa84d40 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -109,6 +109,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "App:Collisions:Reset", { 0.0, 0.0, 0.5 } }, { 0, "App:Data graph", { 0.5, 0.8, 0.4 } }, { 1, "App:Show code", { 0.8, 0.2, 1.0 } }, + { 0, "App:Show code:General", { 0.4, 0.3, 0.9 } }, { 0, "App:Show code:Nametags", { 0.8, 0.8, 1.0 } }, { 0, "App:Show code:Nametags:2d", { 0.0, 0.0, 0.5 } }, { 0, "App:Show code:Nametags:2d:Contents", { 0.0, 0.5, 0.0 } }, From c66ca2ece13b1d754e9fd87a748c5131732bbc79 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:31:27 +0100 Subject: [PATCH 006/166] pstats: Report PStats overhead more honestly Half the overhead was being missed due to the unfortunate collector placement --- panda/src/pstatclient/pStatClientImpl.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index dcf0cdfef2..02f1764b81 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -158,6 +158,8 @@ client_disconnect() { */ void PStatClientImpl:: new_frame(int thread_index) { + double frame_start = get_real_time(); + nassertv(thread_index >= 0 && thread_index < _client->_num_threads); PStatClient::InternalThread *pthread = _client->get_thread_ptr(thread_index); @@ -178,7 +180,6 @@ new_frame(int thread_index) { return; } - double frame_start = get_real_time(); int frame_number = -1; PStatFrameData frame_data; From f6322d8c93cd402aa479b994b24129b8caefbede Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:32:51 +0100 Subject: [PATCH 007/166] pipeline: De-inline ConditionVarDummyImpl methods This is the only ConditionVar implementation to import thread.h directly --- panda/src/pipeline/conditionVarDummyImpl.I | 16 ---------------- panda/src/pipeline/conditionVarDummyImpl.cxx | 17 +++++++++++++++++ panda/src/pipeline/conditionVarDummyImpl.h | 5 ++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/panda/src/pipeline/conditionVarDummyImpl.I b/panda/src/pipeline/conditionVarDummyImpl.I index ddb356bfe6..2ea7c39588 100644 --- a/panda/src/pipeline/conditionVarDummyImpl.I +++ b/panda/src/pipeline/conditionVarDummyImpl.I @@ -25,22 +25,6 @@ INLINE ConditionVarDummyImpl:: ~ConditionVarDummyImpl() { } -/** - * - */ -INLINE void ConditionVarDummyImpl:: -wait() { - Thread::force_yield(); -} - -/** - * - */ -INLINE void ConditionVarDummyImpl:: -wait(double) { - Thread::force_yield(); -} - /** * */ diff --git a/panda/src/pipeline/conditionVarDummyImpl.cxx b/panda/src/pipeline/conditionVarDummyImpl.cxx index 36dd47e659..815501d96f 100644 --- a/panda/src/pipeline/conditionVarDummyImpl.cxx +++ b/panda/src/pipeline/conditionVarDummyImpl.cxx @@ -13,3 +13,20 @@ #include "selectThreadImpl.h" #include "conditionVarDummyImpl.h" +#include "thread.h" + +/** + * + */ +void ConditionVarDummyImpl:: +wait() { + Thread::force_yield(); +} + +/** + * + */ +void ConditionVarDummyImpl:: +wait(double) { + Thread::force_yield(); +} diff --git a/panda/src/pipeline/conditionVarDummyImpl.h b/panda/src/pipeline/conditionVarDummyImpl.h index 150b77f910..c5e2b942c4 100644 --- a/panda/src/pipeline/conditionVarDummyImpl.h +++ b/panda/src/pipeline/conditionVarDummyImpl.h @@ -16,7 +16,6 @@ #include "pandabase.h" #include "selectThreadImpl.h" -#include "thread.h" #include "pnotify.h" @@ -31,8 +30,8 @@ public: INLINE ConditionVarDummyImpl(MutexDummyImpl &mutex); INLINE ~ConditionVarDummyImpl(); - INLINE void wait(); - INLINE void wait(double timeout); + void wait(); + void wait(double timeout); INLINE void notify(); INLINE void notify_all(); }; From fb7a2d7a13cc0c274dada6c3bb728055c6aa4527 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:36:37 +0100 Subject: [PATCH 008/166] text-stats: Add JSON output mode in chrome://tracing format This allows the whole trace to be captured and then loaded into chrome://tracing or https://ui.perfetto.dev --- pandatool/src/text-stats/textMonitor.cxx | 57 +++++++++++++++++++++--- pandatool/src/text-stats/textMonitor.h | 5 ++- pandatool/src/text-stats/textStats.cxx | 19 +++++++- pandatool/src/text-stats/textStats.h | 1 + 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/pandatool/src/text-stats/textMonitor.cxx b/pandatool/src/text-stats/textMonitor.cxx index 2f4f458387..dd437d6106 100644 --- a/pandatool/src/text-stats/textMonitor.cxx +++ b/pandatool/src/text-stats/textMonitor.cxx @@ -22,9 +22,10 @@ * */ TextMonitor:: -TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data ) : PStatMonitor(server) { - _outStream = outStream; //[PECI] - _show_raw_data = show_raw_data; +TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json) : PStatMonitor(server) { + _outStream = outStream; //[PECI] + _show_raw_data = show_raw_data; + _json = json; } /** @@ -73,6 +74,30 @@ got_bad_version(int client_major, int client_minor, << server_major << "." << server_minor << ".\n"; } +/** + * Called whenever a new Thread definition is received from the client. + * Generally, the client will send all of its threads over shortly after + * connecting, but there's no guarantee that they will all be received before + * the first frames are received. The monitor should be prepared to accept + * new Thread definitions midstream. + */ +void TextMonitor:: +new_thread(int thread_index) { + if (_json) { + const PStatClientData *client_data = get_client_data(); + + int pid = get_client_pid(); + if (pid < 0) { + pid = _dummy_pid; + } + + (*_outStream) + << "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":" << pid + << ",\"tid\":" << thread_index << ",\"args\":{\"name\":\"" + << client_data->get_thread_name(thread_index) << "\"}},\n"; + } +} + /** * Called as each frame's data is made available. There is no gurantee the * frames will arrive in order, or that all of them will arrive at all. The @@ -84,12 +109,29 @@ new_data(int thread_index, int frame_number) { PStatView &view = get_view(thread_index); const PStatThreadData *thread_data = view.get_thread_data(); - if (frame_number == thread_data->get_latest_frame_number()) { - view.set_to_frame(frame_number); + view.set_to_frame(frame_number); - if (view.all_collectors_known()) { - const PStatClientData *client_data = get_client_data(); + if (true) { + const PStatClientData *client_data = get_client_data(); + if (_json) { + int pid = get_client_pid(); + if (pid < 0) { + pid = _dummy_pid; + } + + const PStatFrameData &frame_data = thread_data->get_frame(frame_number); + int num_events = frame_data.get_num_events(); + for (int i = 0; i < num_events; ++i) { + int collector_index = frame_data.get_time_collector(i); + (*_outStream) + << "{\"name\":\"" << client_data->get_collector_fullname(collector_index) + << "\",\"ts\":" << (uint64_t)(frame_data.get_time(i) * 1000000) + << ",\"ph\":\"" << (frame_data.is_start(i) ? 'B' : 'E') << "\"" + << ",\"tid\":" << thread_index << ",\"pid\":" << pid << "},\n"; + } + } + else { (*_outStream) << "\rThread " << client_data->get_thread_name(thread_index) << " frame " << frame_number << ", " @@ -149,6 +191,7 @@ new_data(int thread_index, int frame_number) { void TextMonitor:: lost_connection() { nout << "Lost connection.\n"; + ++_dummy_pid; } /** diff --git a/pandatool/src/text-stats/textMonitor.h b/pandatool/src/text-stats/textMonitor.h index 4c88340e46..13cfa860c5 100644 --- a/pandatool/src/text-stats/textMonitor.h +++ b/pandatool/src/text-stats/textMonitor.h @@ -29,7 +29,7 @@ class TextStats; */ class TextMonitor : public PStatMonitor { public: - TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data); + TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json = false); TextStats *get_server(); virtual std::string get_monitor_name(); @@ -37,6 +37,7 @@ public: virtual void got_hello(); virtual void got_bad_version(int client_major, int client_minor, int server_major, int server_minor); + virtual void new_thread(int thread_index); virtual void new_data(int thread_index, int frame_number); virtual void lost_connection(); virtual bool is_thread_safe(); @@ -47,6 +48,8 @@ public: private: std::ostream *_outStream; //[PECI] bool _show_raw_data; + bool _json; + int _dummy_pid = 0; }; #include "textMonitor.I" diff --git a/pandatool/src/text-stats/textStats.cxx b/pandatool/src/text-stats/textStats.cxx index 9f2929f440..0e677172aa 100644 --- a/pandatool/src/text-stats/textStats.cxx +++ b/pandatool/src/text-stats/textStats.cxx @@ -50,6 +50,11 @@ TextStats() { "time per collector.", &TextStats::dispatch_none, &_show_raw_data, nullptr); + add_option + ("j", "", 0, + "Output data in JSON format.", + &TextStats::dispatch_none, &_json, nullptr); + add_option ("o", "filename", 0, "Filename where to print. If not given then stderr is being used.", @@ -66,7 +71,7 @@ TextStats() { PStatMonitor *TextStats:: make_monitor() { - return new TextMonitor(this, _outFile, _show_raw_data); + return new TextMonitor(this, _outFile, _show_raw_data, _json); } @@ -87,13 +92,23 @@ run() { nout << "Listening for connections.\n"; if (_got_outputFileName) { - _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out); + _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out | std::ios::trunc); } else { _outFile = &(nout); } + if (_json) { + (*_outFile) << "[\n"; + } + main_loop(&user_interrupted); nout << "Exiting.\n"; + + if (_json) { + // Remove the last comma. + _outFile->seekp(-3, std::ios::cur); + (*_outFile) << "\n]\n"; + } } diff --git a/pandatool/src/text-stats/textStats.h b/pandatool/src/text-stats/textStats.h index b6853ecb46..99725f0e30 100644 --- a/pandatool/src/text-stats/textStats.h +++ b/pandatool/src/text-stats/textStats.h @@ -37,6 +37,7 @@ public: private: int _port; bool _show_raw_data; + bool _json = false; // [PECI] bool _got_outputFileName; From 350836cc38de2200b9888730dc9b7c1192aed981 Mon Sep 17 00:00:00 2001 From: Maxwell175 Date: Tue, 1 Feb 2022 01:31:14 -0800 Subject: [PATCH 009/166] PythonUtil: remove Enum class (#1253) --- direct/src/showbase/PythonUtil.py | 108 +----------------------------- 1 file changed, 1 insertion(+), 107 deletions(-) diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 178cfa6bca..b38f0a234d 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -11,7 +11,7 @@ __all__ = [ 'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic', 'findPythonModule', 'mostDerivedLast', 'clampScalar', 'weightedChoice', 'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32', - 'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton', + 'SerialNumGen', 'serialNum', 'uniqueName', 'Singleton', 'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr', 'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString', 'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall', @@ -1223,111 +1223,6 @@ def uniqueName(name): global _serialGen return '%s-%s' % (name, _serialGen.next()) -class EnumIter: - def __init__(self, enum): - self._values = tuple(enum._stringTable.keys()) - self._index = 0 - def __iter__(self): - return self - def __next__(self): - if self._index >= len(self._values): - raise StopIteration - self._index += 1 - return self._values[self._index-1] - -class Enum: - """Pass in list of strings or string of comma-separated strings. - Items are accessible as instance.item, and are assigned unique, - increasing integer values. Pass in integer for 'start' to override - starting value. - - Example: - - >>> colors = Enum('red, green, blue') - >>> colors.red - 0 - >>> colors.green - 1 - >>> colors.blue - 2 - >>> colors.getString(colors.red) - 'red' - """ - - if __debug__: - # chars that cannot appear within an item string. - def _checkValidIdentifier(item): - import string - invalidChars = string.whitespace + string.punctuation - invalidChars = invalidChars.replace('_', '') - invalidFirstChars = invalidChars+string.digits - if item[0] in invalidFirstChars: - raise SyntaxError("Enum '%s' contains invalid first char" % - item) - if not disjoint(item, invalidChars): - for char in item: - if char in invalidChars: - raise SyntaxError( - "Enum\n'%s'\ncontains illegal char '%s'" % - (item, char)) - return 1 - _checkValidIdentifier = staticmethod(_checkValidIdentifier) - - def __init__(self, items, start=0): - if isinstance(items, str): - items = items.split(',') - - self._stringTable = {} - - # make sure we don't overwrite an existing element of the class - assert self._checkExistingMembers(items) - assert uniqueElements(items) - - i = start - for item in items: - # remove leading/trailing whitespace - item = item.strip() - # is there anything left? - if len(item) == 0: - continue - # make sure there are no invalid characters - assert Enum._checkValidIdentifier(item) - self.__dict__[item] = i - self._stringTable[i] = item - i += 1 - - def __iter__(self): - return EnumIter(self) - - def hasString(self, string): - return string in set(self._stringTable.values()) - - def fromString(self, string): - if self.hasString(string): - return self.__dict__[string] - # throw an error - {}[string] - - def getString(self, value): - return self._stringTable[value] - - def __contains__(self, value): - return value in self._stringTable - - def __len__(self): - return len(self._stringTable) - - def copyTo(self, obj): - # copies all members onto obj - for name, value in self._stringTable: - setattr(obj, name, value) - - if __debug__: - def _checkExistingMembers(self, items): - for item in items: - if hasattr(self, item): - return 0 - return 1 ############################################################ # class: Singleton @@ -2635,7 +2530,6 @@ class PriorityCallbacks: builtins.Functor = Functor builtins.Stack = Stack builtins.Queue = Queue -builtins.Enum = Enum builtins.SerialNumGen = SerialNumGen builtins.SerialMaskedGen = SerialMaskedGen builtins.ScratchPad = ScratchPad From 174cb4899074a728260bc36aba04484a24208546 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 11:32:36 +0100 Subject: [PATCH 010/166] gtk-stats: Replace uses of deprecated GTK APIs As of this change, requires GTK 2.24 --- pandatool/src/gtk-stats/gtkStatsGraph.cxx | 17 +- pandatool/src/gtk-stats/gtkStatsLabel.cxx | 17 +- .../src/gtk-stats/gtkStatsLabelStack.cxx | 5 +- pandatool/src/gtk-stats/gtkStatsMonitor.cxx | 220 +++++++++++------- pandatool/src/gtk-stats/gtkStatsMonitor.h | 11 +- pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 13 +- .../src/gtk-stats/gtkStatsStripChart.cxx | 15 +- 7 files changed, 179 insertions(+), 119 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index e56e262d23..7d4b18f1a2 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -45,7 +45,7 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : GtkWidget *parent_window = monitor->get_window(); - GdkDisplay *display = gdk_drawable_get_display(parent_window->window); + GdkDisplay *display = gdk_window_get_display(gtk_widget_get_window(parent_window)); _hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2); _pixmap = nullptr; @@ -323,12 +323,14 @@ gboolean GtkStatsGraph:: handle_motion(GtkWidget *widget, int graph_x, int graph_y) { _potential_drag_mode = consider_drag_start(graph_x, graph_y); + GdkWindow *window = gtk_widget_get_window(_window); + if (_potential_drag_mode == DM_guide_bar || _drag_mode == DM_guide_bar) { - gdk_window_set_cursor(_window->window, _hand_cursor); + gdk_window_set_cursor(window, _hand_cursor); } else { - gdk_window_set_cursor(_window->window, nullptr); + gdk_window_set_cursor(window, nullptr); } return TRUE; @@ -344,7 +346,8 @@ setup_pixmap(int xsize, int ysize) { _pixmap_xsize = std::max(xsize, 0); _pixmap_ysize = std::max(ysize, 0); - _pixmap = gdk_pixmap_new(_graph_window->window, _pixmap_xsize, _pixmap_ysize, -1); + GdkWindow *window = gtk_widget_get_window(_graph_window); + _pixmap = gdk_pixmap_new(window, _pixmap_xsize, _pixmap_ysize, -1); // g_object_ref(_pixmap); Should this be ref_sink? _pixmap_gc = gdk_gc_new(_pixmap); // g_object_ref(_pixmap_gc); Should this be ref_sink? @@ -392,8 +395,10 @@ graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; if (self->_pixmap != nullptr) { - gdk_draw_drawable(self->_graph_window->window, - self->_graph_window->style->fg_gc[0], + GdkWindow *window = gtk_widget_get_window(self->_graph_window); + GtkStyle *style = gtk_widget_get_style(self->_graph_window); + gdk_draw_drawable(window, + style->fg_gc[0], self->_pixmap, 0, 0, 0, 0, self->_pixmap_xsize, self->_pixmap_ysize); } diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index cac7be3859..badd940806 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -162,25 +162,28 @@ gboolean GtkStatsLabel:: expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; - GdkGC *gc = gdk_gc_new(widget->window); + GdkWindow *window = gtk_widget_get_window(widget); + GdkGC *gc = gdk_gc_new(window); gdk_gc_set_rgb_fg_color(gc, &self->_bg_color); - gdk_draw_rectangle(widget->window, gc, TRUE, 0, 0, - widget->allocation.width, widget->allocation.height); + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + gdk_draw_rectangle(window, gc, TRUE, 0, 0, allocation.width, allocation.height); // Center the text within the rectangle. int width, height; pango_layout_get_pixel_size(self->_layout, &width, &height); gdk_gc_set_rgb_fg_color(gc, &self->_fg_color); - gdk_draw_layout(widget->window, gc, - (widget->allocation.width - width) / 2, 0, + gdk_draw_layout(window, gc, + (allocation.width - width) / 2, 0, self->_layout); // Now draw the highlight rectangle, if any. if (self->_highlight || self->_mouse_within) { - gdk_draw_rectangle(widget->window, gc, FALSE, 0, 0, - widget->allocation.width - 1, widget->allocation.height - 1); + gdk_draw_rectangle(window, gc, FALSE, 0, 0, + allocation.width - 1, allocation.height - 1); } g_object_unref(gc); diff --git a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx index a640fb3217..03de5ef7b9 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx @@ -48,9 +48,12 @@ int GtkStatsLabelStack:: get_label_y(int label_index, GtkWidget *target_widget) const { nassertr(label_index >= 0 && label_index < (int)_labels.size(), 0); + GtkAllocation allocation; + gtk_widget_get_allocation(_widget, &allocation); + // Assume all labels have the same height. int height = _labels[0]->get_height(); - int start_y = _widget->allocation.height - height * label_index; + int start_y = allocation.height - height * label_index; int x, y; gtk_widget_translate_coordinates(_widget, target_widget, diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index 4f78d88450..8bf5eef52b 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -22,32 +22,12 @@ #include "pStatCollectorDef.h" #include "indent.h" -typedef void vc(); - -GtkItemFactoryEntry GtkStatsMonitor::menu_entries[] = { - { (gchar *)"/Options", nullptr, nullptr, 0, (gchar *)"" }, - { (gchar *)"/Options/Units", nullptr, nullptr, 0, (gchar *)"" }, - { (gchar *)"/Options/Units/ms", nullptr, (vc *)&handle_menu_command, MI_time_ms, (gchar *)"" }, - { (gchar *)"/Options/Units/Hz", nullptr, (vc *)&handle_menu_command, MI_time_hz, (gchar *)"/Options/Units/ms" }, - { (gchar *)"/Speed", nullptr, nullptr, 0, (gchar *)"" }, - { (gchar *)"/Speed/1", nullptr, (vc *)&handle_menu_command, MI_speed_1, (gchar *)"" }, - { (gchar *)"/Speed/2", nullptr, (vc *)&handle_menu_command, MI_speed_2, (gchar *)"/Speed/1" }, - { (gchar *)"/Speed/3", nullptr, (vc *)&handle_menu_command, MI_speed_3, (gchar *)"/Speed/1" }, - { (gchar *)"/Speed/6", nullptr, (vc *)&handle_menu_command, MI_speed_6, (gchar *)"/Speed/1" }, - { (gchar *)"/Speed/12", nullptr, (vc *)&handle_menu_command, MI_speed_12, (gchar *)"/Speed/1" }, - { (gchar *)"/Speed/sep", nullptr, nullptr, 0, (gchar *)"" }, - { (gchar *)"/Speed/pause", nullptr, (vc *)&handle_menu_command, MI_pause, (gchar *)"" }, -}; - -int GtkStatsMonitor::num_menu_entries = sizeof(menu_entries) / sizeof(GtkItemFactoryEntry); - /** * */ GtkStatsMonitor:: GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) { _window = nullptr; - _item_factory = nullptr; // These will be filled in later when the menu is created. _time_units = 0; @@ -160,8 +140,7 @@ new_collector(int collector_index) { void GtkStatsMonitor:: new_thread(int thread_index) { GtkStatsChartMenu *chart_menu = new GtkStatsChartMenu(this, thread_index); - GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, ""); - chart_menu->add_to_menu_bar(menu_bar, _next_chart_index); + chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index); ++_next_chart_index; _chart_menus.push_back(chart_menu); } @@ -379,40 +358,27 @@ create_window() { gtk_window_set_default_size(GTK_WINDOW(_window), 500, 360); // Set up the menu. - GtkAccelGroup *accel_group = gtk_accel_group_new(); - _item_factory = - gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group); - gtk_item_factory_create_items(_item_factory, num_menu_entries, menu_entries, - this); + GtkAccelGroup *accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(_window), accel_group); - GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, ""); + _menu_bar = gtk_menu_bar_new(); _next_chart_index = 2; + setup_options_menu(); + setup_speed_menu(); setup_frame_rate_label(); - ChartMenus::iterator mi; - for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) { - (*mi)->add_to_menu_bar(menu_bar, _next_chart_index); + for (GtkStatsChartMenu *chart_menu : _chart_menus) { + chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index); ++_next_chart_index; } // Pack the menu into the window. GtkWidget *main_vbox = gtk_vbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(_window), main_vbox); - gtk_box_pack_start(GTK_BOX(main_vbox), menu_bar, FALSE, TRUE, 0); - - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(_item_factory, "/Speed/3")), - TRUE); - set_scroll_speed(3); - - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(_item_factory, "/Options/Units/ms")), - TRUE); - set_time_units(PStatGraph::GBU_ms); + gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0); gtk_widget_show_all(_window); gtk_widget_show(_window); - - set_pause(false); } /** @@ -462,6 +428,126 @@ window_destroy(GtkWidget *widget, gpointer data) { self->close(); } + +/** + * Creates the "Options" pulldown menu. + */ +void GtkStatsMonitor:: +setup_options_menu() { + _options_menu = gtk_menu_new(); + + GtkWidget *item = gtk_menu_item_new_with_label("Options"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _options_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item); + + GtkWidget *units_menu = gtk_menu_new(); + item = gtk_menu_item_new_with_label("Units"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), units_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(_options_menu), item); + + item = gtk_radio_menu_item_new_with_label(nullptr, "ms"); + gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_time_units(PStatGraph::GBU_ms); + }), this); + + item = gtk_radio_menu_item_new_with_label( + gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)), "Hz"); + gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_time_units(PStatGraph::GBU_hz); + }), this); + + set_time_units(PStatGraph::GBU_ms); +} + +/** + * Creates the "Speed" pulldown menu. + */ +void GtkStatsMonitor:: +setup_speed_menu() { + _speed_menu = gtk_menu_new(); + + GtkWidget *item = gtk_menu_item_new_with_label("Speed"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _speed_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item); + + GSList *group = nullptr; + item = gtk_radio_menu_item_new_with_label(group, "1"); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_scroll_speed(1); + } + }), this); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + + item = gtk_radio_menu_item_new_with_label(group, "2"); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_scroll_speed(2); + } + }), this); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + + item = gtk_radio_menu_item_new_with_label(group, "3"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_scroll_speed(3); + } + }), this); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + + item = gtk_radio_menu_item_new_with_label(group, "6"); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_scroll_speed(6); + } + }), this); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + + item = gtk_radio_menu_item_new_with_label(group, "12"); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_scroll_speed(12); + } + }), this); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); + + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + + item = gtk_check_menu_item_new_with_label("pause"); + gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item); + g_signal_connect(G_OBJECT(item), "toggled", + G_CALLBACK(+[](GtkMenuItem *item, gpointer data) { + GtkStatsMonitor *self = (GtkStatsMonitor *)data; + self->set_pause(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); + }), this); + + set_scroll_speed(3); + set_pause(false); +} + /** * Creates the frame rate label on the right end of the menu bar. This is * used as a text label to display the main thread's frame rate to the user, @@ -470,59 +556,13 @@ window_destroy(GtkWidget *widget, gpointer data) { */ void GtkStatsMonitor:: setup_frame_rate_label() { - GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, ""); - _frame_rate_menu_item = gtk_menu_item_new(); _frame_rate_label = gtk_label_new(""); gtk_container_add(GTK_CONTAINER(_frame_rate_menu_item), _frame_rate_label); gtk_widget_show(_frame_rate_menu_item); gtk_widget_show(_frame_rate_label); - gtk_menu_item_right_justify(GTK_MENU_ITEM(_frame_rate_menu_item)); + gtk_menu_item_set_right_justified(GTK_MENU_ITEM(_frame_rate_menu_item), TRUE); - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), _frame_rate_menu_item); -} - -/** - * - */ -void GtkStatsMonitor:: -handle_menu_command(gpointer callback_data, guint menu_id, GtkWidget *widget) { - GtkStatsMonitor *self = (GtkStatsMonitor *)callback_data; - switch (menu_id) { - case MI_none: - break; - - case MI_time_ms: - self->set_time_units(PStatGraph::GBU_ms); - break; - - case MI_time_hz: - self->set_time_units(PStatGraph::GBU_hz); - break; - - case MI_speed_1: - self->set_scroll_speed(1); - break; - - case MI_speed_2: - self->set_scroll_speed(2); - break; - - case MI_speed_3: - self->set_scroll_speed(3); - break; - - case MI_speed_6: - self->set_scroll_speed(6); - break; - - case MI_speed_12: - self->set_scroll_speed(12); - break; - - case MI_pause: - self->set_pause(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))); - break; - } + gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _frame_rate_menu_item); } diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.h b/pandatool/src/gtk-stats/gtkStatsMonitor.h index be1f1c7d42..ba872289a7 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.h +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.h @@ -82,10 +82,10 @@ private: static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data); static void window_destroy(GtkWidget *widget, gpointer data); + void setup_options_menu(); + void setup_speed_menu(); void setup_frame_rate_label(); - static void handle_menu_command(gpointer callback_data, guint menu_id, GtkWidget *widget); - typedef pset Graphs; Graphs _graphs; @@ -96,7 +96,9 @@ private: Menus _menus; GtkWidget *_window; - GtkItemFactory *_item_factory; + GtkWidget *_menu_bar; + GtkWidget *_options_menu; + GtkWidget *_speed_menu; int _next_chart_index; GtkWidget *_frame_rate_menu_item; GtkWidget *_frame_rate_label; @@ -105,9 +107,6 @@ private: double _scroll_speed; bool _pause; - static GtkItemFactoryEntry menu_entries[]; - static int num_menu_entries; - friend class GtkStatsGraph; }; diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 60a3dc7887..2717fa5201 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -204,9 +204,11 @@ idle() { */ void GtkStatsPianoRoll:: additional_graph_window_paint() { + GdkWindow *window = gtk_widget_get_window(_graph_window); + int num_user_guide_bars = get_num_user_guide_bars(); for (int i = 0; i < num_user_guide_bars; i++) { - draw_guide_bar(_graph_window->window, get_user_guide_bar(i)); + draw_guide_bar(window, get_user_guide_bar(i)); } } @@ -424,7 +426,8 @@ draw_guide_labels() { */ void GtkStatsPianoRoll:: draw_guide_label(const PStatGraph::GuideBar &bar) { - GdkGC *gc = gdk_gc_new(_scale_area->window); + GdkWindow *window = gtk_widget_get_window(_scale_area); + GdkGC *gc = gdk_gc_new(window); switch (bar._style) { case GBS_target: @@ -467,9 +470,11 @@ draw_guide_label(const PStatGraph::GuideBar &bar) { x, 0, &x, &junk_y); + GtkAllocation allocation; + gtk_widget_get_allocation(_scale_area, &allocation); + int this_x = x - width / 2; - gdk_draw_layout(_scale_area->window, gc, this_x, - _scale_area->allocation.height - height, layout); + gdk_draw_layout(window, gc, this_x, allocation.height - height, layout); } g_object_unref(layout); diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index 117ef96576..daee7a31e7 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -255,10 +255,11 @@ copy_region(int start_x, int end_x, int dest_x) { _brush_origin += (dest_x - start_x); // SetBrushOrgEx(_bitmap_dc, _brush_origin, 0, NULL); + GdkWindow *window = gtk_widget_get_window(_graph_window); GdkRectangle rect = { dest_x, 0, end_x - start_x, get_ysize() }; - gdk_window_invalidate_rect(_graph_window->window, &rect, FALSE); + gdk_window_invalidate_rect(window, &rect, FALSE); } /** @@ -327,10 +328,11 @@ end_draw(int from_x, int to_x) { draw_guide_bar(_pixmap, from_x, to_x, get_guide_bar(i)); } + GdkWindow *window = gtk_widget_get_window(_graph_window); GdkRectangle rect = { from_x, 0, to_x - from_x + 1, get_ysize() }; - gdk_window_invalidate_rect(_graph_window->window, &rect, FALSE); + gdk_window_invalidate_rect(window, &rect, FALSE); } /** @@ -339,9 +341,11 @@ end_draw(int from_x, int to_x) { */ void GtkStatsStripChart:: additional_graph_window_paint() { + GdkWindow *window = gtk_widget_get_window(_graph_window); + int num_user_guide_bars = get_num_user_guide_bars(); for (int i = 0; i < num_user_guide_bars; i++) { - draw_guide_bar(_graph_window->window, 0, get_xsize(), get_user_guide_bar(i)); + draw_guide_bar(window, 0, get_xsize(), get_user_guide_bar(i)); } } @@ -558,7 +562,8 @@ draw_guide_labels() { */ int GtkStatsStripChart:: draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { - GdkGC *gc = gdk_gc_new(_scale_area->window); + GdkWindow *window = gtk_widget_get_window(_scale_area); + GdkGC *gc = gdk_gc_new(window); switch (bar._style) { case GBS_target: @@ -603,7 +608,7 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { int this_y = y - height / 2; if (last_y < this_y || last_y > this_y + height) { - gdk_draw_layout(_scale_area->window, gc, 0, this_y, layout); + gdk_draw_layout(window, gc, 0, this_y, layout); last_y = this_y; } } From 3a38543f65670b2d754838c5b08a556df1485a01 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 13:35:32 +0100 Subject: [PATCH 011/166] gtk-stats: Fix mouse motion detected outside strip chart graph area --- .../src/gtk-stats/gtkStatsStripChart.cxx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index daee7a31e7..cfa92ef7e5 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -406,20 +406,23 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) { gboolean GtkStatsStripChart:: handle_button_press(GtkWidget *widget, int graph_x, int graph_y, bool double_click) { - if (double_click) { - // Double-clicking on a color bar in the graph is the same as double- - // clicking on the corresponding label. - clicked_label(get_collector_under_pixel(graph_x, graph_y)); - return TRUE; + if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + if (double_click) { + // Double-clicking on a color bar in the graph is the same as double- + // clicking on the corresponding label. + clicked_label(get_collector_under_pixel(graph_x, graph_y)); + return TRUE; + } + + if (_potential_drag_mode == DM_none) { + set_drag_mode(DM_scale); + _drag_scale_start = pixel_to_height(graph_y); + // SetCapture(_graph_window); + return TRUE; + } } - if (_potential_drag_mode == DM_none) { - set_drag_mode(DM_scale); - _drag_scale_start = pixel_to_height(graph_y); - // SetCapture(_graph_window); - return TRUE; - - } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { + if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { set_drag_mode(DM_guide_bar); _drag_start_y = graph_y; // SetCapture(_graph_window); @@ -459,7 +462,8 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { */ gboolean GtkStatsStripChart:: handle_motion(GtkWidget *widget, int graph_x, int graph_y) { - if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { + if (_drag_mode == DM_none && _potential_drag_mode == DM_none && + graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { // When the mouse is over a color bar, highlight it. _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y)); From 4c3bc5a42e5b4bbe50c8ca5624c50ad730357d4d Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 15:36:08 +0100 Subject: [PATCH 012/166] gtk-stats: Use cairo instead of GDK for drawing Drawing via GDK is deprecated and no longer supported in GTK 3 --- pandatool/src/gtk-stats/gtkStatsGraph.cxx | 101 ++++++++---------- pandatool/src/gtk-stats/gtkStatsGraph.h | 29 ++--- pandatool/src/gtk-stats/gtkStatsLabel.cxx | 37 +++---- pandatool/src/gtk-stats/gtkStatsLabel.h | 4 +- pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 48 ++++----- pandatool/src/gtk-stats/gtkStatsPianoRoll.h | 4 +- .../src/gtk-stats/gtkStatsStripChart.cxx | 91 +++++++++------- pandatool/src/gtk-stats/gtkStatsStripChart.h | 7 +- 8 files changed, 154 insertions(+), 167 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index 7d4b18f1a2..88ca89a6fe 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -15,20 +15,14 @@ #include "gtkStatsMonitor.h" #include "gtkStatsLabelStack.h" -const GdkColor GtkStatsGraph::rgb_white = { - 0, 0xffff, 0xffff, 0xffff +const double GtkStatsGraph::rgb_light_gray[3] = { + 0x9a / (double)0xff, 0x9a / (double)0xff, 0x9a / (double)0xff, }; -const GdkColor GtkStatsGraph::rgb_light_gray = { - 0, 0x9a9a, 0x9a9a, 0x9a9a, +const double GtkStatsGraph::rgb_dark_gray[3] = { + 0x33 / (double)0xff, 0x33 / (double)0xff, 0x33 / (double)0xff, }; -const GdkColor GtkStatsGraph::rgb_dark_gray = { - 0, 0x3333, 0x3333, 0x3333, -}; -const GdkColor GtkStatsGraph::rgb_black = { - 0, 0x0000, 0x0000, 0x0000 -}; -const GdkColor GtkStatsGraph::rgb_user_guide_bar = { - 0, 0x8282, 0x9696, 0xffff +const double GtkStatsGraph::rgb_user_guide_bar[3] = { + 0x82 / (double)0xff, 0x96 / (double)0xff, 0xff / (double)0xff, }; /** @@ -48,11 +42,11 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : GdkDisplay *display = gdk_window_get_display(gtk_widget_get_window(parent_window)); _hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2); - _pixmap = nullptr; - _pixmap_gc = nullptr; + _cr_surface = nullptr; + _cr = nullptr; - _pixmap_xsize = 0; - _pixmap_ysize = 0; + _surface_xsize = 0; + _surface_ysize = 0; _window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -128,12 +122,11 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : GtkStatsGraph:: ~GtkStatsGraph() { _monitor = nullptr; - release_pixmap(); + release_surface(); Brushes::iterator bi; for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) { - GdkGC *gc = (*bi).second; - g_object_unref(gc); + cairo_pattern_destroy((*bi).second); } _label_stack.clear_labels(); @@ -236,10 +229,10 @@ close() { } /** - * Returns a GC suitable for drawing in the indicated collector's color. + * Returns a pattern suitable for drawing in the indicated collector's color. */ -GdkGC *GtkStatsGraph:: -get_collector_gc(int collector_index) { +cairo_pattern_t *GtkStatsGraph:: +get_collector_pattern(int collector_index) { Brushes::iterator bi; bi = _brushes.find(collector_index); if (bi != _brushes.end()) { @@ -248,17 +241,10 @@ get_collector_gc(int collector_index) { // Ask the monitor what color this guy should be. LRGBColor rgb = _monitor->get_collector_color(collector_index); + cairo_pattern_t *pattern = cairo_pattern_create_rgb(rgb[0], rgb[1], rgb[2]); - GdkColor c; - c.red = (int)(rgb[0] * 65535.0f); - c.green = (int)(rgb[1] * 65535.0f); - c.blue = (int)(rgb[2] * 65535.0f); - GdkGC *gc = gdk_gc_new(_pixmap); - // g_object_ref(gc); Should this be ref_sink? - gdk_gc_set_rgb_fg_color(gc, &c); - - _brushes[collector_index] = gc; - return gc; + _brushes[collector_index] = pattern; + return pattern; } /** @@ -266,7 +252,7 @@ get_collector_gc(int collector_index) { * class opportunity to do some further painting into the graph window. */ void GtkStatsGraph:: -additional_graph_window_paint() { +additional_graph_window_paint(cairo_t *cr) { } /** @@ -340,31 +326,27 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { * Sets up a backing-store bitmap of the indicated size. */ void GtkStatsGraph:: -setup_pixmap(int xsize, int ysize) { - release_pixmap(); +setup_surface(int xsize, int ysize) { + release_surface(); - _pixmap_xsize = std::max(xsize, 0); - _pixmap_ysize = std::max(ysize, 0); + _surface_xsize = std::max(xsize, 0); + _surface_ysize = std::max(ysize, 0); - GdkWindow *window = gtk_widget_get_window(_graph_window); - _pixmap = gdk_pixmap_new(window, _pixmap_xsize, _pixmap_ysize, -1); - // g_object_ref(_pixmap); Should this be ref_sink? - _pixmap_gc = gdk_gc_new(_pixmap); - // g_object_ref(_pixmap_gc); Should this be ref_sink? + _cr_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, _surface_xsize, _surface_ysize); + _cr = cairo_create(_cr_surface); - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); - gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0, - _pixmap_xsize, _pixmap_ysize); + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_paint(_cr); } /** - * Frees the backing-store bitmap created by setup_pixmap(). + * Frees the backing-store bitmap created by setup_surface(). */ void GtkStatsGraph:: -release_pixmap() { - if (_pixmap != nullptr) { - g_object_unref(_pixmap); - g_object_unref(_pixmap_gc); +release_surface() { + if (_cr_surface != nullptr) { + cairo_surface_destroy(_cr_surface); + cairo_destroy(_cr); } } @@ -394,16 +376,17 @@ gboolean GtkStatsGraph:: graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; - if (self->_pixmap != nullptr) { - GdkWindow *window = gtk_widget_get_window(self->_graph_window); - GtkStyle *style = gtk_widget_get_style(self->_graph_window); - gdk_draw_drawable(window, - style->fg_gc[0], - self->_pixmap, 0, 0, 0, 0, - self->_pixmap_xsize, self->_pixmap_ysize); + GdkWindow *window = gtk_widget_get_window(self->_graph_window); + cairo_t *cr = gdk_cairo_create(window); + + if (self->_cr_surface != nullptr) { + cairo_set_source_surface(cr, self->_cr_surface, 0, 0); + cairo_paint(cr); } - self->additional_graph_window_paint(); + self->additional_graph_window_paint(cr); + + cairo_destroy(cr); return TRUE; } @@ -417,7 +400,7 @@ configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event, GtkStatsGraph *self = (GtkStatsGraph *)data; self->changed_graph_size(event->width, event->height); - self->setup_pixmap(event->width, event->height); + self->setup_surface(event->width, event->height); self->force_redraw(); return TRUE; diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.h b/pandatool/src/gtk-stats/gtkStatsGraph.h index 94efa0cb06..9340f777b5 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.h +++ b/pandatool/src/gtk-stats/gtkStatsGraph.h @@ -19,6 +19,7 @@ #include "pmap.h" #include +#include class GtkStatsMonitor; @@ -55,9 +56,9 @@ public: protected: void close(); - GdkGC *get_collector_gc(int collector_index); + cairo_pattern_t *get_collector_pattern(int collector_index); - virtual void additional_graph_window_paint(); + virtual void additional_graph_window_paint(cairo_t *cr); virtual DragMode consider_drag_start(int graph_x, int graph_y); virtual void set_drag_mode(DragMode drag_mode); @@ -67,8 +68,8 @@ protected: virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); protected: - // Table of GC's for our various collectors. - typedef pmap Brushes; + // Table of patterns for our various collectors. + typedef pmap Brushes; Brushes _brushes; GtkStatsMonitor *_monitor; @@ -83,9 +84,9 @@ protected: GdkCursor *_hand_cursor; - GdkPixmap *_pixmap; - GdkGC *_pixmap_gc; - int _pixmap_xsize, _pixmap_ysize; + cairo_surface_t *_cr_surface; + cairo_t *_cr; + int _surface_xsize, _surface_ysize; /* COLORREF _dark_color; @@ -104,15 +105,15 @@ protected: bool _pause; - static const GdkColor rgb_white; - static const GdkColor rgb_light_gray; - static const GdkColor rgb_dark_gray; - static const GdkColor rgb_black; - static const GdkColor rgb_user_guide_bar; + static const double rgb_white[3]; + static const double rgb_light_gray[3]; + static const double rgb_dark_gray[3]; + static const double rgb_black[3]; + static const double rgb_user_guide_bar[3]; private: - void setup_pixmap(int xsize, int ysize); - void release_pixmap(); + void setup_surface(int xsize, int ysize); + void release_surface(); static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data); diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index badd940806..f9c3234c60 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -15,6 +15,8 @@ #include "gtkStatsMonitor.h" #include "gtkStatsGraph.h" +#include + int GtkStatsLabel::_left_margin = 2; int GtkStatsLabel::_right_margin = 2; int GtkStatsLabel::_top_margin = 2; @@ -57,21 +59,14 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, _layout = gtk_widget_create_pango_layout(_widget, _text.c_str()); // Set the fg and bg colors on the label. - LRGBColor rgb = _monitor->get_collector_color(_collector_index); - _bg_color.red = (int)(rgb[0] * 65535.0f); - _bg_color.green = (int)(rgb[1] * 65535.0f); - _bg_color.blue = (int)(rgb[2] * 65535.0f); + _bg_color = _monitor->get_collector_color(_collector_index); // Should our foreground be black or white? - double bright = - rgb[0] * 0.299 + - rgb[1] * 0.587 + - rgb[2] * 0.114; - + PN_stdfloat bright = _bg_color.dot(LRGBColor(0.299, 0.587, 0.114)); if (bright >= 0.5) { - _fg_color.red = _fg_color.green = _fg_color.blue = 0; + _fg_color = LRGBColor(0); } else { - _fg_color.red = _fg_color.green = _fg_color.blue = 0xffff; + _fg_color = LRGBColor(1); } // What are the extents of the text? This determines the minimum size of @@ -163,30 +158,30 @@ expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; GdkWindow *window = gtk_widget_get_window(widget); - GdkGC *gc = gdk_gc_new(window); - gdk_gc_set_rgb_fg_color(gc, &self->_bg_color); + cairo_t *cr = gdk_cairo_create(window); + cairo_set_source_rgb(cr, self->_bg_color[0], self->_bg_color[1], self->_bg_color[2]); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); - gdk_draw_rectangle(window, gc, TRUE, 0, 0, allocation.width, allocation.height); + cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); + cairo_fill(cr); // Center the text within the rectangle. int width, height; pango_layout_get_pixel_size(self->_layout, &width, &height); - gdk_gc_set_rgb_fg_color(gc, &self->_fg_color); - gdk_draw_layout(window, gc, - (allocation.width - width) / 2, 0, - self->_layout); + cairo_set_source_rgb(cr, self->_fg_color[0], self->_fg_color[1], self->_fg_color[2]); + cairo_move_to(cr, (allocation.width - width) / 2, 0); + pango_cairo_show_layout(cr, self->_layout); // Now draw the highlight rectangle, if any. if (self->_highlight || self->_mouse_within) { - gdk_draw_rectangle(window, gc, FALSE, 0, 0, - allocation.width - 1, allocation.height - 1); + cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); + cairo_stroke(cr); } - g_object_unref(gc); + cairo_destroy(cr); return TRUE; } diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.h b/pandatool/src/gtk-stats/gtkStatsLabel.h index 624e4193f9..e82153c7b6 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.h +++ b/pandatool/src/gtk-stats/gtkStatsLabel.h @@ -61,8 +61,8 @@ private: int _collector_index; std::string _text; GtkWidget *_widget; - GdkColor _fg_color; - GdkColor _bg_color; + LRGBColor _fg_color; + LRGBColor _bg_color; PangoLayout *_layout; /* diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 2717fa5201..022d41bdfd 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -142,9 +142,8 @@ set_horizontal_scale(double time_width) { */ void GtkStatsPianoRoll:: clear_region() { - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); - gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0, - get_xsize(), get_ysize()); + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_paint(_cr); } /** @@ -157,7 +156,7 @@ begin_draw() { // Draw in the guide bars. int num_guide_bars = get_num_guide_bars(); for (int i = 0; i < num_guide_bars; i++) { - draw_guide_bar(_pixmap, get_guide_bar(i)); + draw_guide_bar(_cr, get_guide_bar(i)); } } @@ -171,11 +170,9 @@ draw_bar(int row, int from_x, int to_x) { int height = _label_stack.get_label_height(row); int collector_index = get_label_collector(row); - GdkGC *gc = get_collector_gc(collector_index); - - gdk_draw_rectangle(_pixmap, gc, TRUE, - from_x, y - height + 2, - to_x - from_x, height - 4); + cairo_set_source(_cr, get_collector_pattern(collector_index)); + cairo_rectangle(_cr, from_x, y - height + 2, to_x - from_x, height - 4); + cairo_fill(_cr); } } @@ -203,12 +200,10 @@ idle() { * class opportunity to do some further painting into the graph window. */ void GtkStatsPianoRoll:: -additional_graph_window_paint() { - GdkWindow *window = gtk_widget_get_window(_graph_window); - +additional_graph_window_paint(cairo_t *cr) { int num_user_guide_bars = get_num_user_guide_bars(); for (int i = 0; i < num_user_guide_bars; i++) { - draw_guide_bar(window, get_user_guide_bar(i)); + draw_guide_bar(cr, get_user_guide_bar(i)); } } @@ -382,25 +377,27 @@ update_labels() { * Draws the line for the indicated guide bar on the graph. */ void GtkStatsPianoRoll:: -draw_guide_bar(GdkDrawable *surface, const PStatGraph::GuideBar &bar) { +draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { int x = height_to_pixel(bar._height); if (x > 0 && x < get_xsize() - 1) { // Only draw it if it's not too close to the top. switch (bar._style) { case GBS_target: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_light_gray); + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); break; case GBS_user: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_user_guide_bar); + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; case GBS_normal: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_dark_gray); + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } - gdk_draw_line(surface, _pixmap_gc, x, 0, x, get_ysize()); + cairo_move_to(cr, x, 0); + cairo_line_to(cr, x, get_ysize()); + cairo_stroke(cr); } } @@ -427,19 +424,19 @@ draw_guide_labels() { void GtkStatsPianoRoll:: draw_guide_label(const PStatGraph::GuideBar &bar) { GdkWindow *window = gtk_widget_get_window(_scale_area); - GdkGC *gc = gdk_gc_new(window); + cairo_t *cr = gdk_cairo_create(window); switch (bar._style) { case GBS_target: - gdk_gc_set_rgb_fg_color(gc, &rgb_light_gray); + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); break; case GBS_user: - gdk_gc_set_rgb_fg_color(gc, &rgb_user_guide_bar); + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; case GBS_normal: - gdk_gc_set_rgb_fg_color(gc, &rgb_dark_gray); + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -456,7 +453,7 @@ draw_guide_label(const PStatGraph::GuideBar &bar) { if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); - g_object_unref(gc); + cairo_destroy(cr); return; } } @@ -474,11 +471,12 @@ draw_guide_label(const PStatGraph::GuideBar &bar) { gtk_widget_get_allocation(_scale_area, &allocation); int this_x = x - width / 2; - gdk_draw_layout(window, gc, this_x, allocation.height - height, layout); + cairo_move_to(cr, this_x, allocation.height - height); + pango_cairo_show_layout(cr, layout); } g_object_unref(layout); - g_object_unref(gc); + cairo_destroy(cr); } /** diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h index cf54e75c66..7661b0bd3a 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h @@ -48,7 +48,7 @@ protected: virtual void end_draw(); virtual void idle(); - virtual void additional_graph_window_paint(); + virtual void additional_graph_window_paint(cairo_t *cr); virtual DragMode consider_drag_start(int graph_x, int graph_y); virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, @@ -59,7 +59,7 @@ protected: private: int get_collector_under_pixel(int xpoint, int ypoint); void update_labels(); - void draw_guide_bar(GdkDrawable *surface, const PStatGraph::GuideBar &bar); + void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar); void draw_guide_labels(); void draw_guide_label(const PStatGraph::GuideBar &bar); diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index cfa92ef7e5..06cedf0aed 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -33,8 +33,6 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, default_strip_chart_height), GtkStatsGraph(monitor) { - _brush_origin = 0; - if (show_level) { // If it's a level-type graph, show the appropriate units. if (_unit_name.empty()) { @@ -236,9 +234,8 @@ update_labels() { */ void GtkStatsStripChart:: clear_region() { - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); - gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0, - get_xsize(), get_ysize()); + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_paint(_cr); } /** @@ -247,13 +244,23 @@ clear_region() { */ void GtkStatsStripChart:: copy_region(int start_x, int end_x, int dest_x) { - gdk_draw_drawable(_pixmap, _pixmap_gc, _pixmap, - start_x, 0, dest_x, 0, - end_x - start_x, get_ysize()); + // We are not allowed to copy a surface onto itself, so we have to create a + // temporary surface to copy to. + end_x = std::min(end_x, get_xsize()); + cairo_surface_t *temp_surface = + cairo_image_surface_create(CAIRO_FORMAT_RGB24, end_x - start_x, get_ysize()); + { + cairo_t *temp_cr = cairo_create(temp_surface); + cairo_set_source_surface(temp_cr, _cr_surface, -start_x, 0); + cairo_paint(temp_cr); + cairo_destroy(temp_cr); + } - // Also shift the brush origin over, so we still get proper dithering. - _brush_origin += (dest_x - start_x); - // SetBrushOrgEx(_bitmap_dc, _brush_origin, 0, NULL); + cairo_set_source_surface(_cr, temp_surface, 0, 0); + cairo_rectangle(_cr, dest_x, 0, end_x - start_x, get_ysize()); + cairo_fill(_cr); + + cairo_surface_destroy(temp_surface); GdkWindow *window = gtk_widget_get_window(_graph_window); GdkRectangle rect = { @@ -269,9 +276,9 @@ copy_region(int start_x, int end_x, int dest_x) { void GtkStatsStripChart:: draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) { // Start by clearing the band first. - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); - gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, x, 0, - w + 1, get_ysize()); + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_rectangle(_cr, x, 0, w, get_ysize()); + cairo_fill(_cr); double overall_time = 0.0; int y = get_ysize(); @@ -280,18 +287,20 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) { for (fi = fdata.begin(); fi != fdata.end(); ++fi) { const ColorData &cd = (*fi); overall_time += cd._net_value; - GdkGC *gc = get_collector_gc(cd._collector_index); + cairo_set_source(_cr, get_collector_pattern(cd._collector_index)); if (overall_time > get_vertical_scale()) { // Off the top. Go ahead and clamp it by hand, in case it's so far off // the top we'd overflow the 16-bit pixel value. - gdk_draw_rectangle(_pixmap, gc, TRUE, x, 0, w, y); + cairo_rectangle(_cr, x, 0, w, y); + cairo_fill(_cr); // And we can consider ourselves done now. return; } int top_y = height_to_pixel(overall_time); - gdk_draw_rectangle(_pixmap, gc, TRUE, x, top_y, w, y - top_y); + cairo_rectangle(_cr, x, top_y, w, y - top_y); + cairo_fill(_cr); y = top_y; } } @@ -301,9 +310,8 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) { */ void GtkStatsStripChart:: draw_empty(int x, int w) { - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white); - gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, x, 0, - w + 1, get_ysize()); + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_rectangle(_cr, x, 0, w, get_ysize()); } /** @@ -311,8 +319,10 @@ draw_empty(int x, int w) { */ void GtkStatsStripChart:: draw_cursor(int x) { - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_black); - gdk_draw_line(_pixmap, _pixmap_gc, x, 0, x, get_ysize()); + cairo_set_source_rgb(_cr, 0.0, 0.0, 0.0); + cairo_move_to(_cr, x, 0); + cairo_line_to(_cr, x, get_ysize()); + cairo_stroke(_cr); } /** @@ -325,12 +335,12 @@ end_draw(int from_x, int to_x) { // Draw in the guide bars. int num_guide_bars = get_num_guide_bars(); for (int i = 0; i < num_guide_bars; i++) { - draw_guide_bar(_pixmap, from_x, to_x, get_guide_bar(i)); + draw_guide_bar(_cr, from_x, to_x, get_guide_bar(i)); } GdkWindow *window = gtk_widget_get_window(_graph_window); GdkRectangle rect = { - from_x, 0, to_x - from_x + 1, get_ysize() + from_x, 0, to_x - from_x, get_ysize() }; gdk_window_invalidate_rect(window, &rect, FALSE); } @@ -340,12 +350,10 @@ end_draw(int from_x, int to_x) { * class opportunity to do some further painting into the graph window. */ void GtkStatsStripChart:: -additional_graph_window_paint() { - GdkWindow *window = gtk_widget_get_window(_graph_window); - +additional_graph_window_paint(cairo_t *cr) { int num_user_guide_bars = get_num_user_guide_bars(); for (int i = 0; i < num_user_guide_bars; i++) { - draw_guide_bar(window, 0, get_xsize(), get_user_guide_bar(i)); + draw_guide_bar(cr, 0, get_xsize(), get_user_guide_bar(i)); } } @@ -512,7 +520,7 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { * Draws the line for the indicated guide bar on the graph. */ void GtkStatsStripChart:: -draw_guide_bar(GdkDrawable *surface, int from_x, int to_x, +draw_guide_bar(cairo_t *cr, int from_x, int to_x, const PStatGraph::GuideBar &bar) { int y = height_to_pixel(bar._height); @@ -520,18 +528,20 @@ draw_guide_bar(GdkDrawable *surface, int from_x, int to_x, // Only draw it if it's not too close to the top. switch (bar._style) { case GBS_target: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_light_gray); + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); break; case GBS_user: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_user_guide_bar); + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; case GBS_normal: - gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_dark_gray); + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } - gdk_draw_line(surface, _pixmap_gc, from_x, y, to_x, y); + cairo_move_to(cr, from_x, y); + cairo_line_to(cr, to_x, y); + cairo_stroke(cr); } } @@ -567,19 +577,19 @@ draw_guide_labels() { int GtkStatsStripChart:: draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { GdkWindow *window = gtk_widget_get_window(_scale_area); - GdkGC *gc = gdk_gc_new(window); + cairo_t *cr = gdk_cairo_create(window); switch (bar._style) { case GBS_target: - gdk_gc_set_rgb_fg_color(gc, &rgb_light_gray); + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); break; case GBS_user: - gdk_gc_set_rgb_fg_color(gc, &rgb_user_guide_bar); + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; case GBS_normal: - gdk_gc_set_rgb_fg_color(gc, &rgb_dark_gray); + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -596,7 +606,7 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); - g_object_unref(gc); + cairo_destroy(cr); return last_y; } } @@ -612,13 +622,14 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { int this_y = y - height / 2; if (last_y < this_y || last_y > this_y + height) { - gdk_draw_layout(window, gc, 0, this_y, layout); + cairo_move_to(cr, 0, this_y); + pango_cairo_show_layout(cr, layout); last_y = this_y; } } g_object_unref(layout); - g_object_unref(gc); + cairo_destroy(cr); return last_y; } diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h index 484d669c9f..359f681f54 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.h +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h @@ -54,7 +54,7 @@ protected: virtual void draw_cursor(int x); virtual void end_draw(int from_x, int to_x); - virtual void additional_graph_window_paint(); + virtual void additional_graph_window_paint(cairo_t *cr); virtual DragMode consider_drag_start(int graph_x, int graph_y); virtual void set_drag_mode(DragMode drag_mode); @@ -64,8 +64,8 @@ protected: virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); private: - void draw_guide_bar(GdkDrawable *surface, int from_x, int to_x, - const PStatGraph::GuideBar &bar); + void draw_guide_bar(cairo_t *cr, int from_x, int to_x, + const PStatGraph::GuideBar &bar); void draw_guide_labels(); int draw_guide_label(const PStatGraph::GuideBar &bar, int last_y); @@ -74,7 +74,6 @@ private: GdkEventExpose *event, gpointer data); private: - int _brush_origin; std::string _net_value_text; GtkWidget *_top_hbox; From 87f5aea80e1e083de896ffa333b3578f5ade3de5 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 17:17:56 +0100 Subject: [PATCH 013/166] gtk-stats: Update to GTK version 3, since version 2 is EOL --- doc/INSTALL | 2 +- dtool/Package.cmake | 15 +++++------ makepanda/makepanda.py | 15 ++++------- pandatool/src/gtk-stats/CMakeLists.txt | 4 +-- pandatool/src/gtk-stats/gtkStats.cxx | 2 +- pandatool/src/gtk-stats/gtkStatsChartMenu.cxx | 8 +++--- pandatool/src/gtk-stats/gtkStatsGraph.cxx | 17 +++++-------- pandatool/src/gtk-stats/gtkStatsGraph.h | 6 ++--- pandatool/src/gtk-stats/gtkStatsLabel.cxx | 11 +++----- pandatool/src/gtk-stats/gtkStatsLabel.h | 5 ++-- .../src/gtk-stats/gtkStatsLabelStack.cxx | 2 +- pandatool/src/gtk-stats/gtkStatsMonitor.cxx | 3 +-- pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 21 ++++++---------- pandatool/src/gtk-stats/gtkStatsPianoRoll.h | 7 +++--- .../src/gtk-stats/gtkStatsStripChart.cxx | 25 ++++++++----------- pandatool/src/gtk-stats/gtkStatsStripChart.h | 7 +++--- 16 files changed, 62 insertions(+), 88 deletions(-) diff --git a/doc/INSTALL b/doc/INSTALL index 2deff94b14..6deaa5394a 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -186,7 +186,7 @@ it will show you the available command-line options: --use-opencv --no-opencv (enable/disable use of OPENCV) --use-directcam --no-directcam (enable/disable use of DIRECTCAM) --use-vision --no-vision (enable/disable use of VISION) - --use-gtk2 --no-gtk2 (enable/disable use of GTK2) + --use-gtk3 --no-gtk3 (enable/disable use of GTK3) --use-npapi --no-npapi (enable/disable use of NPAPI) --use-mfc --no-mfc (enable/disable use of MFC) --use-wx --no-wx (enable/disable use of WX) diff --git a/dtool/Package.cmake b/dtool/Package.cmake index 4c41cfb7d2..293b1d7090 100644 --- a/dtool/Package.cmake +++ b/dtool/Package.cmake @@ -556,16 +556,17 @@ package_option(HarfBuzz package_status(HarfBuzz "HarfBuzz") -# GTK2 +# GTK3 -set(Freetype_FIND_QUIETLY TRUE) # Fix for builtin FindGTK2 -set(GTK2_GTK_FIND_QUIETLY TRUE) # Fix for builtin FindGTK2 -find_package(GTK2 QUIET COMPONENTS gtk) +if(NOT WIN32) + find_package(GTK3 QUIET) +endif() -package_option(GTK2) - -package_status(GTK2 "gtk+-2") +package_option(GTK3 + "This is necessary to build the PStats performance analysis tool on platforms + other than Windows.") +package_status(GTK3 "gtk+-3") # # ------------ Physics engines ------------ diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index d58482d80c..c70b6302f0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -93,7 +93,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "VRPN", "OPENSSL", # Transport "FFTW", # Algorithm helpers "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality - "GTK2", # GTK2 is used for PStats on Unix + "GTK3", # GTK3 is used for PStats on Unix "MFC", "WX", "FLTK", # Used for web plug-in only "COCOA", # macOS toolkits "X11", # Unix platform support @@ -960,7 +960,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("OPENSSL", "openssl", ("ssl", "crypto"), ("openssl/ssl.h", "openssl/crypto.h")) SmartPkgEnable("ZLIB", "zlib", ("z"), "zlib.h") - SmartPkgEnable("GTK2", "gtk+-2.0") + SmartPkgEnable("GTK3", "gtk+-3.0") if not PkgSkip("OPENSSL") and GetTarget() != "darwin": LibName("OPENSSL", "-Wl,--exclude-libs,libssl.a") @@ -978,11 +978,6 @@ if (COMPILER=="GCC"): if GetHost() != "darwin": # Workaround for an issue where pkg-config does not include this path if GetTargetArch() in ("x86_64", "amd64"): - if (os.path.isdir("/usr/lib64/glib-2.0/include")): - IncDirectory("GTK2", "/usr/lib64/glib-2.0/include") - if (os.path.isdir("/usr/lib64/gtk-2.0/include")): - IncDirectory("GTK2", "/usr/lib64/gtk-2.0/include") - if not PkgSkip("X11"): if (os.path.isdir("/usr/X11R6/lib64")): LibDirectory("ALWAYS", "/usr/X11R6/lib64") @@ -5841,19 +5836,19 @@ if not PkgSkip("PANDATOOL"): # DIRECTORY: pandatool/src/gtk-stats/ # -if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK2")): +if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK3")): if GetTarget() == 'windows': OPTS=['DIR:pandatool/src/win-stats'] TargetAdd('pstats_composite1.obj', opts=OPTS, input='winstats_composite1.cxx') else: - OPTS=['DIR:pandatool/src/gtk-stats', 'GTK2'] + OPTS=['DIR:pandatool/src/gtk-stats', 'GTK3'] TargetAdd('pstats_composite1.obj', opts=OPTS, input='gtkstats_composite1.cxx') TargetAdd('pstats.exe', input='pstats_composite1.obj') TargetAdd('pstats.exe', input='libp3pstatserver.lib') TargetAdd('pstats.exe', input='libp3progbase.lib') TargetAdd('pstats.exe', input='libp3pandatoolbase.lib') TargetAdd('pstats.exe', input=COMMON_PANDA_LIBS) - TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK2']) + TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK3']) # # DIRECTORY: pandatool/src/xfileprogs/ diff --git a/pandatool/src/gtk-stats/CMakeLists.txt b/pandatool/src/gtk-stats/CMakeLists.txt index b78ebf2e80..9c0c8c0944 100644 --- a/pandatool/src/gtk-stats/CMakeLists.txt +++ b/pandatool/src/gtk-stats/CMakeLists.txt @@ -1,4 +1,4 @@ -if(NOT HAVE_GTK2 OR NOT HAVE_NET) +if(NOT HAVE_GTK3 OR NOT HAVE_NET) return() endif() @@ -28,7 +28,7 @@ set(GTKSTATS_SOURCES composite_sources(gtk-stats GTKSTATS_SOURCES) add_executable(gtk-stats ${GTKSTATS_HEADERS} ${GTKSTATS_SOURCES}) -target_link_libraries(gtk-stats p3progbase p3pstatserver PKG::GTK2) +target_link_libraries(gtk-stats p3progbase p3pstatserver PKG::GTK3) # This program is NOT actually called gtk-stats. It's pstats-gtk on Win32 and # pstats everywhere else (as the Win32 GUI is not built). diff --git a/pandatool/src/gtk-stats/gtkStats.cxx b/pandatool/src/gtk-stats/gtkStats.cxx index e6889ff23b..959b83a6a4 100644 --- a/pandatool/src/gtk-stats/gtkStats.cxx +++ b/pandatool/src/gtk-stats/gtkStats.cxx @@ -44,7 +44,7 @@ timer(gpointer data) { // are getting starved and falling behind, so that the user still gets a // chance to see *something* happen onscreen, even if it's just // increasingly old data. - gdk_window_process_all_updates(); + //gdk_window_process_all_updates(); } return TRUE; diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx index c7f8b3f4be..6a67588b1e 100644 --- a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx +++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx @@ -104,9 +104,9 @@ do_update() { // We put a separator between the above frame collector and the first // level collector. if (needs_separator) { - GtkWidget *sep = gtk_separator_menu_item_new(); - gtk_widget_show(sep); - gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep); + GtkWidget *sep = gtk_separator_menu_item_new(); + gtk_widget_show(sep); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep); needs_separator = false; } @@ -138,7 +138,7 @@ do_update() { */ void GtkStatsChartMenu:: add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level, - bool show_level) { + bool show_level) { int collector = view_level->get_collector(); const PStatClientData *client_data = _monitor->get_client_data(); diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index 88ca89a6fe..0fb46af19d 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -73,8 +73,8 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : gtk_widget_add_events(_graph_window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK); - g_signal_connect(G_OBJECT(_graph_window), "expose_event", - G_CALLBACK(graph_expose_callback), this); + g_signal_connect(G_OBJECT(_graph_window), "draw", + G_CALLBACK(graph_draw_callback), this); g_signal_connect(G_OBJECT(_graph_window), "configure_event", G_CALLBACK(configure_graph_callback), this); g_signal_connect(G_OBJECT(_graph_window), "button_press_event", @@ -91,18 +91,18 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : // A VBox to hold the graph's frame, and any numbers (scale legend? total?) // above it. - _graph_vbox = gtk_vbox_new(FALSE, 0); + _graph_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_end(GTK_BOX(_graph_vbox), graph_frame, TRUE, TRUE, 0); // An HBox to hold the graph's frame, and the scale legend to the right of // it. - _graph_hbox = gtk_hbox_new(FALSE, 0); + _graph_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox, TRUE, TRUE, 0); // An HPaned to hold the label stack and the graph hbox. - _hpaned = gtk_hpaned_new(); + _hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); gtk_container_add(GTK_CONTAINER(_window), _hpaned); gtk_container_set_border_width(GTK_CONTAINER(_window), 8); @@ -373,12 +373,9 @@ window_destroy(GtkWidget *widget, gpointer data) { * Fills in the graph window. */ gboolean GtkStatsGraph:: -graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { +graph_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; - GdkWindow *window = gtk_widget_get_window(self->_graph_window); - cairo_t *cr = gdk_cairo_create(window); - if (self->_cr_surface != nullptr) { cairo_set_source_surface(cr, self->_cr_surface, 0, 0); cairo_paint(cr); @@ -386,8 +383,6 @@ graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { self->additional_graph_window_paint(cr); - cairo_destroy(cr); - return TRUE; } diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.h b/pandatool/src/gtk-stats/gtkStatsGraph.h index 9340f777b5..ddf1f11543 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.h +++ b/pandatool/src/gtk-stats/gtkStatsGraph.h @@ -118,10 +118,10 @@ private: static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data); static void window_destroy(GtkWidget *widget, gpointer data); - static gboolean graph_expose_callback(GtkWidget *widget, - GdkEventExpose *event, gpointer data); + static gboolean graph_draw_callback(GtkWidget *widget, + cairo_t *cr, gpointer data); static gboolean configure_graph_callback(GtkWidget *widget, - GdkEventConfigure *event, gpointer data); + GdkEventConfigure *event, gpointer data); protected: static gboolean button_press_event_callback(GtkWidget *widget, diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index f9c3234c60..09c5f1a39e 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -15,8 +15,6 @@ #include "gtkStatsMonitor.h" #include "gtkStatsGraph.h" -#include - int GtkStatsLabel::_left_margin = 2; int GtkStatsLabel::_right_margin = 2; int GtkStatsLabel::_top_margin = 2; @@ -44,8 +42,8 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, gtk_widget_add_events(_widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK); - g_signal_connect(G_OBJECT(_widget), "expose_event", - G_CALLBACK(expose_event_callback), this); + g_signal_connect(G_OBJECT(_widget), "draw", + G_CALLBACK(draw_callback), this); g_signal_connect(G_OBJECT(_widget), "enter_notify_event", G_CALLBACK(enter_notify_event_callback), this); g_signal_connect(G_OBJECT(_widget), "leave_notify_event", @@ -154,11 +152,9 @@ set_mouse_within(bool mouse_within) { * Draws the background color of the label. */ gboolean GtkStatsLabel:: -expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { +draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; - GdkWindow *window = gtk_widget_get_window(widget); - cairo_t *cr = gdk_cairo_create(window); cairo_set_source_rgb(cr, self->_bg_color[0], self->_bg_color[1], self->_bg_color[2]); GtkAllocation allocation; @@ -181,7 +177,6 @@ expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { cairo_stroke(cr); } - cairo_destroy(cr); return TRUE; } diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.h b/pandatool/src/gtk-stats/gtkStatsLabel.h index e82153c7b6..53737661d8 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.h +++ b/pandatool/src/gtk-stats/gtkStatsLabel.h @@ -17,6 +17,7 @@ #include "pandatoolbase.h" #include +#include class GtkStatsMonitor; class GtkStatsGraph; @@ -43,8 +44,8 @@ public: private: void set_mouse_within(bool mouse_within); - static gboolean expose_event_callback(GtkWidget *widget, - GdkEventExpose *event, gpointer data); + static gboolean draw_callback(GtkWidget *widget, + cairo_t *cr, gpointer data); static gboolean enter_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event, gpointer data); diff --git a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx index 03de5ef7b9..079190019c 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx @@ -20,7 +20,7 @@ */ GtkStatsLabelStack:: GtkStatsLabelStack() { - _widget = gtk_vbox_new(FALSE, 0); + _widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); _highlight_label = -1; } diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index 8bf5eef52b..b312d5ccc3 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -160,7 +160,6 @@ new_data(int thread_index, int frame_number) { } } - /** * Called whenever the connection to the client has been lost. This is a * permanent state change. The monitor should update its display to represent @@ -373,7 +372,7 @@ create_window() { } // Pack the menu into the window. - GtkWidget *main_vbox = gtk_vbox_new(FALSE, 1); + GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); gtk_container_add(GTK_CONTAINER(_window), main_vbox); gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0); diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 022d41bdfd..74e4de6318 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -35,8 +35,8 @@ GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) : // Add a DrawingArea widget on top of the graph, to display all of the scale // units. _scale_area = gtk_drawing_area_new(); - g_signal_connect(G_OBJECT(_scale_area), "expose_event", - G_CALLBACK(expose_event_callback), this); + g_signal_connect(G_OBJECT(_scale_area), "draw", + G_CALLBACK(draw_callback), this); gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0); gtk_widget_set_size_request(_scale_area, 0, 20); @@ -405,16 +405,16 @@ draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { * This is called during the servicing of expose_event. */ void GtkStatsPianoRoll:: -draw_guide_labels() { +draw_guide_labels(cairo_t *cr) { int i; int num_guide_bars = get_num_guide_bars(); for (i = 0; i < num_guide_bars; i++) { - draw_guide_label(get_guide_bar(i)); + draw_guide_label(cr, get_guide_bar(i)); } int num_user_guide_bars = get_num_user_guide_bars(); for (i = 0; i < num_user_guide_bars; i++) { - draw_guide_label(get_user_guide_bar(i)); + draw_guide_label(cr, get_user_guide_bar(i)); } } @@ -422,10 +422,7 @@ draw_guide_labels() { * Draws the text for the indicated guide bar label at the top of the graph. */ void GtkStatsPianoRoll:: -draw_guide_label(const PStatGraph::GuideBar &bar) { - GdkWindow *window = gtk_widget_get_window(_scale_area); - cairo_t *cr = gdk_cairo_create(window); - +draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { switch (bar._style) { case GBS_target: cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); @@ -453,7 +450,6 @@ draw_guide_label(const PStatGraph::GuideBar &bar) { if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); - cairo_destroy(cr); return; } } @@ -476,16 +472,15 @@ draw_guide_label(const PStatGraph::GuideBar &bar) { } g_object_unref(layout); - cairo_destroy(cr); } /** * Draws in the scale labels. */ gboolean GtkStatsPianoRoll:: -expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { +draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsPianoRoll *self = (GtkStatsPianoRoll *)data; - self->draw_guide_labels(); + self->draw_guide_labels(cr); return TRUE; } diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h index 7661b0bd3a..97ad5ad597 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h @@ -60,11 +60,10 @@ private: int get_collector_under_pixel(int xpoint, int ypoint); void update_labels(); void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar); - void draw_guide_labels(); - void draw_guide_label(const PStatGraph::GuideBar &bar); + void draw_guide_labels(cairo_t *cr); + void draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar); - static gboolean expose_event_callback(GtkWidget *widget, - GdkEventExpose *event, gpointer data); + static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data); }; #endif diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index 06cedf0aed..f66218b3c8 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -47,7 +47,7 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, } // Put some stuff on top of the graph. - _top_hbox = gtk_hbox_new(FALSE, 0); + _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox, FALSE, FALSE, 0); @@ -64,8 +64,8 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, // Add a DrawingArea widget to the right of the graph, to display all of the // scale units. _scale_area = gtk_drawing_area_new(); - g_signal_connect(G_OBJECT(_scale_area), "expose_event", - G_CALLBACK(expose_event_callback), this); + g_signal_connect(G_OBJECT(_scale_area), "draw", + G_CALLBACK(draw_callback), this); gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area, FALSE, FALSE, 0); gtk_widget_set_size_request(_scale_area, 40, 0); @@ -549,23 +549,23 @@ draw_guide_bar(cairo_t *cr, int from_x, int to_x, * This is called during the servicing of expose_event. */ void GtkStatsStripChart:: -draw_guide_labels() { +draw_guide_labels(cairo_t *cr) { // Draw in the labels for the guide bars. int last_y = -100; int i; int num_guide_bars = get_num_guide_bars(); for (i = 0; i < num_guide_bars; i++) { - last_y = draw_guide_label(get_guide_bar(i), last_y); + last_y = draw_guide_label(cr, get_guide_bar(i), last_y); } GuideBar top_value = make_guide_bar(get_vertical_scale()); - draw_guide_label(top_value, last_y); + draw_guide_label(cr, top_value, last_y); last_y = -100; int num_user_guide_bars = get_num_user_guide_bars(); for (i = 0; i < num_user_guide_bars; i++) { - last_y = draw_guide_label(get_user_guide_bar(i), last_y); + last_y = draw_guide_label(cr, get_user_guide_bar(i), last_y); } } @@ -575,10 +575,7 @@ draw_guide_labels() { * value is given. Returns the top pixel value of the new label. */ int GtkStatsStripChart:: -draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { - GdkWindow *window = gtk_widget_get_window(_scale_area); - cairo_t *cr = gdk_cairo_create(window); - +draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y) { switch (bar._style) { case GBS_target: cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); @@ -606,7 +603,6 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); - cairo_destroy(cr); return last_y; } } @@ -629,7 +625,6 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) { } g_object_unref(layout); - cairo_destroy(cr); return last_y; } @@ -648,9 +643,9 @@ toggled_callback(GtkToggleButton *button, gpointer data) { * Draws in the scale labels. */ gboolean GtkStatsStripChart:: -expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) { +draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsStripChart *self = (GtkStatsStripChart *)data; - self->draw_guide_labels(); + self->draw_guide_labels(cr); return TRUE; } diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h index 359f681f54..466b6a7753 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.h +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h @@ -66,12 +66,11 @@ protected: private: void draw_guide_bar(cairo_t *cr, int from_x, int to_x, const PStatGraph::GuideBar &bar); - void draw_guide_labels(); - int draw_guide_label(const PStatGraph::GuideBar &bar, int last_y); + void draw_guide_labels(cairo_t *cr); + int draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y); static void toggled_callback(GtkToggleButton *button, gpointer data); - static gboolean expose_event_callback(GtkWidget *widget, - GdkEventExpose *event, gpointer data); + static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data); private: std::string _net_value_text; From f0b81d5bf0787f8420d8d604db177aa12c1e1dd4 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 17:25:43 +0100 Subject: [PATCH 014/166] gtk-stats: Fix top row in piano roll chart being cut off with high DPI --- pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 74e4de6318..720e5aa6ea 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -39,8 +39,15 @@ GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) : G_CALLBACK(draw_callback), this); gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0); - gtk_widget_set_size_request(_scale_area, 0, 20); + // It should be large enough to display the labels. + { + PangoLayout *layout = gtk_widget_create_pango_layout(_window, "0123456789 ms"); + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + gtk_widget_set_size_request(_scale_area, 0, height + 1); + g_object_unref(layout); + } gtk_widget_set_size_request(_graph_window, default_piano_roll_width, default_piano_roll_height); From 7da70cf9399e2703ac9cacbb6977edeb173de159 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Feb 2022 19:17:02 +0100 Subject: [PATCH 015/166] pstatserver: Major improvements to PStats server UI, including: * New "Flame Graph" chart for seeing all collectors in a frame, much easier to read than piano roll * Update controls, fonts, background color to more modern visual style on Windows * Proper support for high DPI monitors (with correct scaling) * Add tooltips for collector labels showing full name and averaged value * Colors of collectors are now converted to sRGB transfer encoding * Major performance improvement to piano roll view on Windows * Movering mouse over labels now highlights the corresponding area in chart * Label hover effect changed to darkening effect instead of border * Reimplement graph as static common control on Windows * Check boxes are now clickable by their label on Windows * Graph windows have minimum sizes on Windows --- makepanda/makepanda.py | 2 +- pandatool/src/gtk-stats/CMakeLists.txt | 2 + pandatool/src/gtk-stats/gtkStatsChartMenu.cxx | 38 +- .../src/gtk-stats/gtkStatsFlameGraph.cxx | 551 +++++++++++++++ pandatool/src/gtk-stats/gtkStatsFlameGraph.h | 81 +++ pandatool/src/gtk-stats/gtkStatsGraph.cxx | 83 ++- pandatool/src/gtk-stats/gtkStatsGraph.h | 24 +- pandatool/src/gtk-stats/gtkStatsLabel.cxx | 121 +++- pandatool/src/gtk-stats/gtkStatsLabel.h | 23 +- .../src/gtk-stats/gtkStatsLabelStack.cxx | 4 +- pandatool/src/gtk-stats/gtkStatsMonitor.cxx | 14 + pandatool/src/gtk-stats/gtkStatsMonitor.h | 1 + pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 29 +- pandatool/src/gtk-stats/gtkStatsPianoRoll.h | 3 +- .../src/gtk-stats/gtkStatsStripChart.cxx | 50 +- pandatool/src/gtk-stats/gtkStatsStripChart.h | 3 +- .../src/gtk-stats/gtkstats_composite1.cxx | 1 + pandatool/src/pstatserver/CMakeLists.txt | 5 +- .../pstatserver/p3pstatserver_composite1.cxx | 1 + pandatool/src/pstatserver/pStatFlameGraph.I | 89 +++ pandatool/src/pstatserver/pStatFlameGraph.cxx | 297 ++++++++ pandatool/src/pstatserver/pStatFlameGraph.h | 107 +++ pandatool/src/pstatserver/pStatStripChart.I | 9 + pandatool/src/pstatserver/pStatStripChart.cxx | 69 +- pandatool/src/pstatserver/pStatStripChart.h | 4 +- pandatool/src/win-stats/CMakeLists.txt | 6 +- pandatool/src/win-stats/winStats.cxx | 14 + pandatool/src/win-stats/winStatsChartMenu.cxx | 29 +- .../src/win-stats/winStatsFlameGraph.cxx | 639 ++++++++++++++++++ pandatool/src/win-stats/winStatsFlameGraph.h | 80 +++ pandatool/src/win-stats/winStatsGraph.cxx | 191 +++--- pandatool/src/win-stats/winStatsGraph.h | 22 +- pandatool/src/win-stats/winStatsLabel.I | 68 ++ pandatool/src/win-stats/winStatsLabel.cxx | 198 +++--- pandatool/src/win-stats/winStatsLabel.h | 28 +- .../src/win-stats/winStatsLabelStack.cxx | 87 ++- pandatool/src/win-stats/winStatsLabelStack.h | 4 + pandatool/src/win-stats/winStatsMonitor.cxx | 59 +- pandatool/src/win-stats/winStatsMonitor.h | 7 + pandatool/src/win-stats/winStatsPianoRoll.cxx | 77 ++- pandatool/src/win-stats/winStatsPianoRoll.h | 4 +- .../src/win-stats/winStatsStripChart.cxx | 89 +-- pandatool/src/win-stats/winStatsStripChart.h | 4 +- .../src/win-stats/winstats_composite1.cxx | 1 + 44 files changed, 2806 insertions(+), 412 deletions(-) create mode 100644 pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx create mode 100644 pandatool/src/gtk-stats/gtkStatsFlameGraph.h create mode 100644 pandatool/src/pstatserver/pStatFlameGraph.I create mode 100644 pandatool/src/pstatserver/pStatFlameGraph.cxx create mode 100644 pandatool/src/pstatserver/pStatFlameGraph.h create mode 100644 pandatool/src/win-stats/winStatsFlameGraph.cxx create mode 100644 pandatool/src/win-stats/winStatsFlameGraph.h create mode 100644 pandatool/src/win-stats/winStatsLabel.I diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index c70b6302f0..6c22c458f9 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -5848,7 +5848,7 @@ if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK3") TargetAdd('pstats.exe', input='libp3progbase.lib') TargetAdd('pstats.exe', input='libp3pandatoolbase.lib') TargetAdd('pstats.exe', input=COMMON_PANDA_LIBS) - TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK3']) + TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINCOMCTL', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK3']) # # DIRECTORY: pandatool/src/xfileprogs/ diff --git a/pandatool/src/gtk-stats/CMakeLists.txt b/pandatool/src/gtk-stats/CMakeLists.txt index 9c0c8c0944..f44270de6a 100644 --- a/pandatool/src/gtk-stats/CMakeLists.txt +++ b/pandatool/src/gtk-stats/CMakeLists.txt @@ -4,6 +4,7 @@ endif() set(GTKSTATS_HEADERS gtkStatsChartMenu.h + gtkStatsFlameGraph.h gtkStatsGraph.h gtkStatsLabel.h gtkStatsLabelStack.h @@ -17,6 +18,7 @@ set(GTKSTATS_HEADERS set(GTKSTATS_SOURCES gtkStats.cxx gtkStatsChartMenu.cxx + gtkStatsFlameGraph.cxx gtkStatsGraph.cxx gtkStatsLabel.cxx gtkStatsLabelStack.cxx diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx index 6a67588b1e..3c23398962 100644 --- a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx +++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx @@ -116,20 +116,34 @@ do_update() { } } - // Also a menu item for a piano roll (following a separator). + // Also menu items for flame graph and piano roll (following a separator). GtkWidget *sep = gtk_separator_menu_item_new(); gtk_widget_show(sep); gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep); - GtkStatsMonitor::MenuDef smd(_thread_index, -1, false); - const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); + { + GtkStatsMonitor::MenuDef smd(_thread_index, -2, false); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); - GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll"); - gtk_widget_show(menu_item); - gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); + GtkWidget *menu_item = gtk_menu_item_new_with_label("Flame Graph"); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); - g_signal_connect_swapped(G_OBJECT(menu_item), "activate", - G_CALLBACK(handle_menu), (void *)(const void *)menu_def); + g_signal_connect_swapped(G_OBJECT(menu_item), "activate", + G_CALLBACK(handle_menu), (void *)(const void *)menu_def); + } + + { + GtkStatsMonitor::MenuDef smd(_thread_index, -1, false); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll"); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); + + g_signal_connect_swapped(G_OBJECT(menu_item), "activate", + G_CALLBACK(handle_menu), (void *)(const void *)menu_def); + } } /** @@ -189,9 +203,13 @@ handle_menu(gpointer data) { return; } - if (menu_def->_collector_index < 0) { + if (menu_def->_collector_index == -2) { + monitor->open_flame_graph(menu_def->_thread_index); + } + else if (menu_def->_collector_index < 0) { monitor->open_piano_roll(menu_def->_thread_index); - } else { + } + else { monitor->open_strip_chart(menu_def->_thread_index, menu_def->_collector_index, menu_def->_show_level); diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx new file mode 100644 index 0000000000..bb52f1374c --- /dev/null +++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx @@ -0,0 +1,551 @@ +/** + * 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 gtkStatsFlameGraph.cxx + * @author rdb + * @date 2022-02-02 + */ + +#include "gtkStatsFlameGraph.h" +#include "gtkStatsLabel.h" +#include "gtkStatsMonitor.h" +#include "pStatCollectorDef.h" + +static const int default_flame_graph_width = 800; +static const int default_flame_graph_height = 150; + +/** + * + */ +GtkStatsFlameGraph:: +GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, + int collector_index) : + PStatFlameGraph(monitor, monitor->get_view(thread_index), + thread_index, collector_index, + default_flame_graph_width, + default_flame_graph_height), + GtkStatsGraph(monitor) +{ + // Let's show the units on the guide bar labels. There's room. + set_guide_bar_units(get_guide_bar_units() | GBU_show_units); + + // Put some stuff on top of the graph. + _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox, + FALSE, FALSE, 0); + + _average_check_box = gtk_check_button_new_with_label("Average"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_average_check_box), TRUE); + g_signal_connect(G_OBJECT(_average_check_box), "toggled", + G_CALLBACK(toggled_callback), this); + + // Add a DrawingArea widget on top of the graph, to display all of the scale + // units. + _scale_area = gtk_drawing_area_new(); + g_signal_connect(G_OBJECT(_scale_area), "draw", G_CALLBACK(draw_callback), this); + + _total_label = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(_top_hbox), _average_check_box, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0); + + gtk_widget_set_size_request(_graph_window, default_flame_graph_width, + default_flame_graph_height); + + // Add a fixed container to the overlay to allow arbitrary positioning + // of labels therein. + _fixed = gtk_fixed_new(); + gtk_overlay_add_overlay(GTK_OVERLAY(_graph_overlay), _fixed); + + gtk_widget_show_all(_window); + gtk_widget_show(_window); + + // Allow the window to be resized as small as the user likes. We have to do + // this after the window has been shown; otherwise, it will affect the + // window's initial size. + gtk_widget_set_size_request(_window, 0, 0); + + clear_region(); +} + +/** + * + */ +GtkStatsFlameGraph:: +~GtkStatsFlameGraph() { +} + +/** + * Called whenever a new Collector definition is received from the client. + */ +void GtkStatsFlameGraph:: +new_collector(int collector_index) { + GtkStatsGraph::new_collector(collector_index); +} + +/** + * Called as each frame's data is made available. There is no guarantee the + * frames will arrive in order, or that all of them will arrive at all. The + * monitor should be prepared to accept frames received out-of-order or + * missing. + */ +void GtkStatsFlameGraph:: +new_data(int thread_index, int frame_number) { + if (is_title_unknown()) { + std::string window_title = get_title_text(); + if (!is_title_unknown()) { + gtk_window_set_title(GTK_WINDOW(_window), window_title.c_str()); + } + } + + if (!_pause) { + update(); + + std::string text = format_number(get_horizontal_scale(), get_guide_bar_units(), get_guide_bar_unit_name()); + if (_net_value_text != text) { + _net_value_text = text; + gtk_label_set_text(GTK_LABEL(_total_label), _net_value_text.c_str()); + } + } +} + +/** + * Called when it is necessary to redraw the entire graph. + */ +void GtkStatsFlameGraph:: +force_redraw() { + PStatFlameGraph::force_redraw(); +} + +/** + * Called when the user has resized the window, forcing a resize of the graph. + */ +void GtkStatsFlameGraph:: +changed_graph_size(int graph_xsize, int graph_ysize) { + PStatFlameGraph::changed_size(graph_xsize, graph_ysize); +} + +/** + * Called when the user selects a new time units from the monitor pulldown + * menu, this should adjust the units for the graph to the indicated mask if + * it is a time-based graph. + */ +void GtkStatsFlameGraph:: +set_time_units(int unit_mask) { + int old_unit_mask = get_guide_bar_units(); + if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) { + unit_mask = unit_mask & (GBU_hz | GBU_ms); + unit_mask |= (old_unit_mask & GBU_show_units); + set_guide_bar_units(unit_mask); + + gtk_widget_queue_draw(_scale_area); + } +} + +/** + * Called when the user single-clicks on a label. + */ +void GtkStatsFlameGraph:: +on_click_label(int collector_index) { + int prev_collector_index = get_collector_index(); + if (collector_index == prev_collector_index && collector_index != 0) { + // Clicking on the top label means to go up to the parent level. + const PStatClientData *client_data = + GtkStatsGraph::_monitor->get_client_data(); + if (client_data->has_collector(collector_index)) { + const PStatCollectorDef &def = + client_data->get_collector_def(collector_index); + collector_index = def._parent_index; + set_collector_index(collector_index); + } + } + else { + // Clicking on any other label means to focus on that. + set_collector_index(collector_index); + } + + // Change the root collector to show the full name. + if (prev_collector_index != collector_index) { + auto it = _labels.find(prev_collector_index); + if (it != _labels.end()) { + it->second->update_text(false); + } + it = _labels.find(collector_index); + if (it != _labels.end()) { + it->second->update_text(true); + } + } +} + +/** + * Called when the user hovers the mouse over a label. + */ +void GtkStatsFlameGraph:: +on_enter_label(int collector_index) { + if (collector_index != _highlighted_index) { + _highlighted_index = collector_index; + } +} + +/** + * Called when the user's mouse cursor leaves a label. + */ +void GtkStatsFlameGraph:: +on_leave_label(int collector_index) { + if (collector_index == _highlighted_index && collector_index != -1) { + _highlighted_index = -1; + } +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsFlameGraph:: +get_label_tooltip(int collector_index) const { + return PStatFlameGraph::get_label_tooltip(collector_index); +} + +/** + * Repositions the labels. + */ +void GtkStatsFlameGraph:: +update_labels() { + PStatFlameGraph::update_labels(); +} + +/** + * Repositions a label. If width is 0, the label should be deleted. + */ +void GtkStatsFlameGraph:: +update_label(int collector_index, int row, int x, int width) { + GtkStatsLabel *label; + + auto it = _labels.find(collector_index); + if (it != _labels.end()) { + label = it->second; + if (width == 0) { + gtk_container_remove(GTK_CONTAINER(_fixed), label->get_widget()); + delete label; + _labels.erase(it); + return; + } + gtk_fixed_move(GTK_FIXED(_fixed), label->get_widget(), x, _ysize - (row + 1) * label->get_height()); + } + else { + if (width == 0) { + return; + } + label = new GtkStatsLabel(GtkStatsGraph::_monitor, this, _thread_index, collector_index, false, false); + _labels[collector_index] = label; + gtk_fixed_put(GTK_FIXED(_fixed), label->get_widget(), x, _ysize - (row + 1) * label->get_height()); + } + + gtk_widget_set_size_request(label->get_widget(), std::min(width, _xsize), label->get_height()); +} + +/** + * Calls update_guide_bars with parameters suitable to this kind of graph. + */ +void GtkStatsFlameGraph:: +normal_guide_bars() { + // We want vaguely 100 pixels between guide bars. + double res = gdk_screen_get_resolution(gdk_screen_get_default()); + int num_bars = (int)(get_xsize() / (100.0 * (res > 0 ? res / 96.0 : 1.0))); + + _guide_bars.clear(); + + double dist = get_horizontal_scale() / num_bars; + + for (int i = 1; i < num_bars; ++i) { + _guide_bars.push_back(make_guide_bar(i * dist)); + } + + _guide_bars_changed = true; +} + +/** + * Erases the chart area. + */ +void GtkStatsFlameGraph:: +clear_region() { + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_paint(_cr); +} + +/** + * Erases the chart area in preparation for drawing a bunch of bars. + */ +void GtkStatsFlameGraph:: +begin_draw() { + clear_region(); + + // Draw in the guide bars. + int num_guide_bars = get_num_guide_bars(); + for (int i = 0; i < num_guide_bars; i++) { + draw_guide_bar(_cr, get_guide_bar(i)); + } +} + +/** + * Called after all the bars have been drawn, this triggers a refresh event to + * draw it to the window. + */ +void GtkStatsFlameGraph:: +end_draw() { + gtk_widget_queue_draw(_graph_window); +} + +/** + * Called at the end of the draw cycle. + */ +void GtkStatsFlameGraph:: +idle() { +} + +/** + * This is called during the servicing of the draw event; it gives a derived + * class opportunity to do some further painting into the graph window. + */ +void GtkStatsFlameGraph:: +additional_graph_window_paint(cairo_t *cr) { + int num_user_guide_bars = get_num_user_guide_bars(); + for (int i = 0; i < num_user_guide_bars; i++) { + draw_guide_bar(cr, get_user_guide_bar(i)); + } +} + +/** + * Based on the mouse position within the window's client area, look for + * draggable things the mouse might be hovering over and return the + * apprioprate DragMode enum or DM_none if nothing is indicated. + */ +GtkStatsGraph::DragMode GtkStatsFlameGraph:: +consider_drag_start(int graph_x, int graph_y) { + if (graph_y >= 0 && graph_y < get_ysize()) { + if (graph_x >= 0 && graph_x < get_xsize()) { + // See if the mouse is over a user-defined guide bar. + int x = graph_x; + double from_height = pixel_to_height(x - 2); + double to_height = pixel_to_height(x + 2); + _drag_guide_bar = find_user_guide_bar(from_height, to_height); + if (_drag_guide_bar >= 0) { + return DM_guide_bar; + } + + } else { + // The mouse is left or right of the graph; maybe create a new guide + // bar. + return DM_new_guide_bar; + } + } + + return DM_none; +} + +/** + * Called when the mouse button is depressed within the graph window. + */ +gboolean GtkStatsFlameGraph:: +handle_button_press(GtkWidget *widget, int graph_x, int graph_y, + bool double_click) { + if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + if (double_click) { + // Clicking on whitespace in the graph goes to the parent. + on_click_label(get_collector_index()); + return TRUE; + } + } + + if (_potential_drag_mode == DM_none) { + set_drag_mode(DM_scale); + _drag_scale_start = pixel_to_height(graph_x); + // SetCapture(_graph_window); + return TRUE; + + } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { + set_drag_mode(DM_guide_bar); + _drag_start_x = graph_x; + // SetCapture(_graph_window); + return TRUE; + } + + return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y, + double_click); +} + +/** + * Called when the mouse button is released within the graph window. + */ +gboolean GtkStatsFlameGraph:: +handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { + if (_drag_mode == DM_scale) { + set_drag_mode(DM_none); + // ReleaseCapture(); + return handle_motion(widget, graph_x, graph_y); + + } else if (_drag_mode == DM_guide_bar) { + if (graph_x < 0 || graph_x >= get_xsize()) { + remove_user_guide_bar(_drag_guide_bar); + } else { + move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x)); + } + set_drag_mode(DM_none); + // ReleaseCapture(); + return handle_motion(widget, graph_x, graph_y); + } + + return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y); +} + +/** + * Called when the mouse is moved within the graph window. + */ +gboolean GtkStatsFlameGraph:: +handle_motion(GtkWidget *widget, int graph_x, int graph_y) { + if (_drag_mode == DM_new_guide_bar) { + // We haven't created the new guide bar yet; we won't until the mouse + // comes within the graph's region. + if (graph_x >= 0 && graph_x < get_xsize()) { + set_drag_mode(DM_guide_bar); + _drag_guide_bar = add_user_guide_bar(pixel_to_height(graph_x)); + return TRUE; + } + } + else if (_drag_mode == DM_guide_bar) { + move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x)); + return TRUE; + } + + return GtkStatsGraph::handle_motion(widget, graph_x, graph_y); +} + +/** + * Draws the line for the indicated guide bar on the graph. + */ +void GtkStatsFlameGraph:: +draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { + int x = height_to_pixel(bar._height); + + if (x > 0 && x < get_xsize() - 1) { + // Only draw it if it's not too close to the top. + switch (bar._style) { + case GBS_target: + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); + break; + + case GBS_user: + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); + break; + + case GBS_normal: + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); + break; + } + cairo_move_to(cr, x, 0); + cairo_line_to(cr, x, get_ysize()); + cairo_stroke(cr); + } +} + +/** + * This is called during the servicing of the draw event. + */ +void GtkStatsFlameGraph:: +draw_guide_labels(cairo_t *cr) { + int i; + int num_guide_bars = get_num_guide_bars(); + for (i = 0; i < num_guide_bars; i++) { + draw_guide_label(cr, get_guide_bar(i)); + } + + int num_user_guide_bars = get_num_user_guide_bars(); + for (i = 0; i < num_user_guide_bars; i++) { + draw_guide_label(cr, get_user_guide_bar(i)); + } +} + +/** + * Draws the text for the indicated guide bar label at the top of the graph. + */ +void GtkStatsFlameGraph:: +draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { + switch (bar._style) { + case GBS_target: + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); + break; + + case GBS_user: + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); + break; + + case GBS_normal: + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); + break; + } + + int x = height_to_pixel(bar._height); + const std::string &label = bar._label; + + PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str()); + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + + if (bar._style != GBS_user) { + double from_height = pixel_to_height(x - width); + double to_height = pixel_to_height(x + width); + if (find_user_guide_bar(from_height, to_height) >= 0) { + // Omit the label: there's a user-defined guide bar in the same space. + g_object_unref(layout); + return; + } + } + + if (x >= 0 && x < get_xsize()) { + // Now convert our x to a coordinate within our drawing area. + int junk_y; + + // The x coordinate comes from the graph_window. + gtk_widget_translate_coordinates(_graph_window, _scale_area, + x, 0, + &x, &junk_y); + + GtkAllocation allocation; + gtk_widget_get_allocation(_scale_area, &allocation); + + int this_x = x - width / 2; + if (this_x >= 0 && this_x + width < allocation.width) { + cairo_move_to(cr, this_x, allocation.height - height); + pango_cairo_show_layout(cr, layout); + } + } + + g_object_unref(layout); +} + +/** + * Called when the average check box is toggled. + */ +void GtkStatsFlameGraph:: +toggled_callback(GtkToggleButton *button, gpointer data) { + GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data; + + bool active = gtk_toggle_button_get_active(button); + self->set_average_mode(active); +} + +/** + * Draws in the scale labels. + */ +gboolean GtkStatsFlameGraph:: +draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { + GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data; + self->draw_guide_labels(cr); + + return TRUE; +} diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.h b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h new file mode 100644 index 0000000000..f6e57c3bce --- /dev/null +++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h @@ -0,0 +1,81 @@ +/** + * 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 gtkStatsFlameGraph.h + * @author rdb + * @date 2022-02-02 + */ + +#ifndef GTKSTATSFLAMEGRAPH_H +#define GTKSTATSFLAMEGRAPH_H + +#include "pandatoolbase.h" + +#include "gtkStatsGraph.h" +#include "pStatFlameGraph.h" + +class GtkStatsLabel; + +/** + * A window that draws a flame chart, which shows the collectors explicitly + * stopping and starting, one frame at a time. + */ +class GtkStatsFlameGraph : public PStatFlameGraph, public GtkStatsGraph { +public: + GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, + int collector_index=0); + virtual ~GtkStatsFlameGraph(); + + virtual void new_collector(int collector_index); + virtual void new_data(int thread_index, int frame_number); + virtual void force_redraw(); + virtual void changed_graph_size(int graph_xsize, int graph_ysize); + + virtual void set_time_units(int unit_mask); + virtual void on_click_label(int collector_index); + virtual void on_enter_label(int collector_index); + virtual void on_leave_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; + +protected: + virtual void update_labels(); + virtual void update_label(int collector_index, int row, int x, int width); + virtual void normal_guide_bars(); + + void clear_region(); + virtual void begin_draw(); + virtual void end_draw(); + virtual void idle(); + + virtual void additional_graph_window_paint(cairo_t *cr); + virtual DragMode consider_drag_start(int graph_x, int graph_y); + + virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, + bool double_click); + virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y); + virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); + +private: + void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar); + void draw_guide_labels(cairo_t *cr); + void draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar); + + static void toggled_callback(GtkToggleButton *button, gpointer data); + static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data); + +private: + std::string _net_value_text; + pmap _labels; + + GtkWidget *_top_hbox; + GtkWidget *_average_check_box; + GtkWidget *_total_label; + GtkWidget *_fixed; +}; + +#endif diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index 0fb46af19d..4a34ca75fe 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -14,6 +14,7 @@ #include "gtkStatsGraph.h" #include "gtkStatsMonitor.h" #include "gtkStatsLabelStack.h" +#include "convert_srgb.h" const double GtkStatsGraph::rgb_light_gray[3] = { 0x9a / (double)0xff, 0x9a / (double)0xff, 0x9a / (double)0xff, @@ -84,15 +85,20 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : g_signal_connect(G_OBJECT(_graph_window), "motion_notify_event", G_CALLBACK(motion_notify_event_callback), this); + // An overlay inside the frame, for charts that want to display widgets on + // top of the graph. + _graph_overlay = gtk_overlay_new(); + gtk_container_add(GTK_CONTAINER(_graph_overlay), _graph_window); + // A Frame to hold the graph. - GtkWidget *graph_frame = gtk_frame_new(nullptr); - gtk_frame_set_shadow_type(GTK_FRAME(graph_frame), GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(graph_frame), _graph_window); + _graph_frame = gtk_frame_new(nullptr); + gtk_frame_set_shadow_type(GTK_FRAME(_graph_frame), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(_graph_frame), _graph_overlay); // A VBox to hold the graph's frame, and any numbers (scale legend? total?) // above it. _graph_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_end(GTK_BOX(_graph_vbox), graph_frame, + gtk_box_pack_end(GTK_BOX(_graph_vbox), _graph_frame, TRUE, TRUE, 0); // An HBox to hold the graph's frame, and the scale legend to the right of @@ -103,10 +109,11 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : // An HPaned to hold the label stack and the graph hbox. _hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + gtk_paned_set_wide_handle(GTK_PANED(_hpaned), TRUE); gtk_container_add(GTK_CONTAINER(_window), _hpaned); gtk_container_set_border_width(GTK_CONTAINER(_window), 8); - gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), TRUE, TRUE); + gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), FALSE, FALSE); gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE); _drag_mode = DM_none; @@ -124,10 +131,11 @@ GtkStatsGraph:: _monitor = nullptr; release_surface(); - Brushes::iterator bi; - for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) { - cairo_pattern_destroy((*bi).second); + for (auto &item : _brushes) { + cairo_pattern_destroy(item.second.first); + cairo_pattern_destroy(item.second.second); } + _brushes.clear(); _label_stack.clear_labels(); @@ -152,13 +160,6 @@ void GtkStatsGraph:: new_data(int thread_index, int frame_number) { } -/** - * Called when it is necessary to redraw the entire graph. - */ -void GtkStatsGraph:: -force_redraw() { -} - /** * Called when the user has resized the window, forcing a resize of the graph. */ @@ -207,7 +208,38 @@ user_guide_bars_changed() { * Called when the user single-clicks on a label. */ void GtkStatsGraph:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { +} + +/** + * Called when the user hovers the mouse over a label. + */ +void GtkStatsGraph:: +on_enter_label(int collector_index) { + if (collector_index != _highlighted_index) { + _highlighted_index = collector_index; + force_redraw(); + } +} + +/** + * Called when the user's mouse cursor leaves a label. + */ +void GtkStatsGraph:: +on_leave_label(int collector_index) { + if (collector_index == _highlighted_index && collector_index != -1) { + _highlighted_index = -1; + force_redraw(); + } +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsGraph:: +get_label_tooltip(int collector_index) const { + return std::string(); } /** @@ -232,23 +264,30 @@ close() { * Returns a pattern suitable for drawing in the indicated collector's color. */ cairo_pattern_t *GtkStatsGraph:: -get_collector_pattern(int collector_index) { +get_collector_pattern(int collector_index, bool highlight) { Brushes::iterator bi; bi = _brushes.find(collector_index); if (bi != _brushes.end()) { - return (*bi).second; + return highlight ? (*bi).second.second : (*bi).second.first; } // Ask the monitor what color this guy should be. LRGBColor rgb = _monitor->get_collector_color(collector_index); - cairo_pattern_t *pattern = cairo_pattern_create_rgb(rgb[0], rgb[1], rgb[2]); + cairo_pattern_t *pattern = cairo_pattern_create_rgb( + encode_sRGB_float(rgb[0]), + encode_sRGB_float(rgb[1]), + encode_sRGB_float(rgb[2])); + cairo_pattern_t *hpattern = cairo_pattern_create_rgb( + encode_sRGB_float(rgb[0] * 0.75f), + encode_sRGB_float(rgb[1] * 0.75f), + encode_sRGB_float(rgb[2] * 0.75f)); - _brushes[collector_index] = pattern; - return pattern; + _brushes[collector_index] = std::make_pair(pattern, hpattern); + return highlight ? hpattern : pattern; } /** - * This is called during the servicing of expose_event; it gives a derived + * This is called during the servicing of the draw event; it gives a derived * class opportunity to do some further painting into the graph window. */ void GtkStatsGraph:: diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.h b/pandatool/src/gtk-stats/gtkStatsGraph.h index ddf1f11543..3a3f123faf 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.h +++ b/pandatool/src/gtk-stats/gtkStatsGraph.h @@ -44,7 +44,7 @@ public: virtual void new_collector(int collector_index); virtual void new_data(int thread_index, int frame_number); - virtual void force_redraw(); + virtual void force_redraw()=0; virtual void changed_graph_size(int graph_xsize, int graph_ysize); virtual void set_time_units(int unit_mask); @@ -52,11 +52,14 @@ public: void set_pause(bool pause); void user_guide_bars_changed(); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); + virtual void on_enter_label(int collector_index); + virtual void on_leave_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; protected: void close(); - cairo_pattern_t *get_collector_pattern(int collector_index); + cairo_pattern_t *get_collector_pattern(int collector_index, bool highlight = false); virtual void additional_graph_window_paint(cairo_t *cr); virtual DragMode consider_drag_start(int graph_x, int graph_y); @@ -69,12 +72,14 @@ protected: protected: // Table of patterns for our various collectors. - typedef pmap Brushes; + typedef pmap > Brushes; Brushes _brushes; GtkStatsMonitor *_monitor; GtkWidget *_parent_window; GtkWidget *_window; + GtkWidget *_graph_frame; + GtkWidget *_graph_overlay; GtkWidget *_graph_window; GtkWidget *_graph_hbox; GtkWidget *_graph_vbox; @@ -88,21 +93,14 @@ protected: cairo_t *_cr; int _surface_xsize, _surface_ysize; - /* - COLORREF _dark_color; - COLORREF _light_color; - COLORREF _user_guide_bar_color; - HPEN _dark_pen; - HPEN _light_pen; - HPEN _user_guide_bar_pen; - */ - DragMode _drag_mode; DragMode _potential_drag_mode; int _drag_start_x, _drag_start_y; double _drag_scale_start; int _drag_guide_bar; + int _highlighted_index = -1; + bool _pause; static const double rgb_white[3]; diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index 09c5f1a39e..4eb766246e 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -14,6 +14,7 @@ #include "gtkStatsLabel.h" #include "gtkStatsMonitor.h" #include "gtkStatsGraph.h" +#include "convert_srgb.h" int GtkStatsLabel::_left_margin = 2; int GtkStatsLabel::_right_margin = 2; @@ -25,19 +26,14 @@ int GtkStatsLabel::_bottom_margin = 2; */ GtkStatsLabel:: GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, - int thread_index, int collector_index, bool use_fullname) : + int thread_index, int collector_index, bool use_fullname, + bool align_right) : _monitor(monitor), _graph(graph), _thread_index(thread_index), - _collector_index(collector_index) + _collector_index(collector_index), + _align_right(align_right) { - _widget = nullptr; - if (use_fullname) { - _text = _monitor->get_client_data()->get_collector_fullname(_collector_index); - } else { - _text = _monitor->get_client_data()->get_collector_name(_collector_index); - } - _widget = gtk_drawing_area_new(); gtk_widget_add_events(_widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | @@ -50,32 +46,41 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, G_CALLBACK(leave_notify_event_callback), this); g_signal_connect(G_OBJECT(_widget), "button_press_event", G_CALLBACK(button_press_event_callback), this); + g_signal_connect(G_OBJECT(_widget), "query-tooltip", + G_CALLBACK(query_tooltip_callback), this); + gtk_widget_set_has_tooltip(_widget, TRUE); gtk_widget_show(_widget); - // Make up a PangoLayout to represent the text. - _layout = gtk_widget_create_pango_layout(_widget, _text.c_str()); - // Set the fg and bg colors on the label. - _bg_color = _monitor->get_collector_color(_collector_index); + LRGBColor rgb = _monitor->get_collector_color(_collector_index); + _bg_color = LRGBColor( + encode_sRGB_float(rgb[0]), + encode_sRGB_float(rgb[1]), + encode_sRGB_float(rgb[2])); + + _highlight_bg_color = LRGBColor( + encode_sRGB_float(rgb[0] * 0.75f), + encode_sRGB_float(rgb[1] * 0.75f), + encode_sRGB_float(rgb[2] * 0.75f)); // Should our foreground be black or white? - PN_stdfloat bright = _bg_color.dot(LRGBColor(0.299, 0.587, 0.114)); + PN_stdfloat bright = _bg_color.dot(LRGBColor(0.2126, 0.7152, 0.0722)); if (bright >= 0.5) { _fg_color = LRGBColor(0); } else { _fg_color = LRGBColor(1); } - - // What are the extents of the text? This determines the minimum size of - // our widget. - int width, height; - pango_layout_get_pixel_size(_layout, &width, &height); - gtk_widget_set_size_request(_widget, width + 8, height); + if (bright >= 0.5 * 0.75) { + _highlight_fg_color = LRGBColor(0); + } else { + _highlight_fg_color = LRGBColor(1); + } _highlight = false; _mouse_within = false; - _height = height; + + update_text(use_fullname); } /** @@ -83,7 +88,10 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, */ GtkStatsLabel:: ~GtkStatsLabel() { - // DeleteObject(_bg_brush); + if (_layout) { + g_object_unref(_layout); + _layout = nullptr; + } } /** @@ -137,6 +145,33 @@ get_highlight() const { return _highlight; } +/** + * Set to true if the full name of the collector should be shown. + */ +void GtkStatsLabel:: +update_text(bool use_fullname) { + const PStatClientData *client_data = _monitor->get_client_data(); + if (use_fullname) { + _text = client_data->get_collector_fullname(_collector_index); + } else { + _text = client_data->get_collector_name(_collector_index); + } + + // Make up a PangoLayout to represent the text. + if (_layout) { + g_object_unref(_layout); + } + _layout = gtk_widget_create_pango_layout(_widget, _text.c_str()); + + // What are the extents of the text? This determines the minimum size of + // our widget. + int width, height; + pango_layout_get_pixel_size(_layout, &width, &height); + gtk_widget_set_size_request(_widget, width + 8, height); + _ideal_width = width; + _height = height; +} + /** * Used internally to indicate whether the mouse is within the label's widget. */ @@ -155,7 +190,16 @@ gboolean GtkStatsLabel:: draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; - cairo_set_source_rgb(cr, self->_bg_color[0], self->_bg_color[1], self->_bg_color[2]); + LRGBColor bg, fg; + if (self->_highlight || self->_mouse_within) { + bg = self->_highlight_bg_color; + fg = self->_highlight_fg_color; + } else { + bg = self->_bg_color; + fg = self->_fg_color; + + } + cairo_set_source_rgb(cr, bg[0], bg[1], bg[2]); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); @@ -163,19 +207,16 @@ draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); cairo_fill(cr); - // Center the text within the rectangle. int width, height; pango_layout_get_pixel_size(self->_layout, &width, &height); - cairo_set_source_rgb(cr, self->_fg_color[0], self->_fg_color[1], self->_fg_color[2]); - cairo_move_to(cr, (allocation.width - width) / 2, 0); - pango_cairo_show_layout(cr, self->_layout); - - // Now draw the highlight rectangle, if any. - if (self->_highlight || self->_mouse_within) { - cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); - cairo_stroke(cr); + cairo_set_source_rgb(cr, fg[0], fg[1], fg[2]); + if (self->_align_right) { + cairo_move_to(cr, allocation.width - width, 0); + } else { + cairo_move_to(cr, 0, 0); } + pango_cairo_show_layout(cr, self->_layout); return TRUE; } @@ -211,7 +252,21 @@ button_press_event_callback(GtkWidget *widget, GdkEventButton *event, GtkStatsLabel *self = (GtkStatsLabel *)data; bool double_click = (event->type == GDK_2BUTTON_PRESS); if (double_click) { - self->_graph->clicked_label(self->_collector_index); + self->_graph->on_click_label(self->_collector_index); } return TRUE; } + +/** + * Called when a tooltip should be displayed. + */ +gboolean GtkStatsLabel:: +query_tooltip_callback(GtkWidget *widget, gint x, gint y, + gboolean keyboard_tip, GtkTooltip *tooltip, + gpointer data) { + GtkStatsLabel *self = (GtkStatsLabel *)data; + + std::string text = self->_graph->get_label_tooltip(self->_collector_index); + gtk_tooltip_set_text(tooltip, text.c_str()); + return !text.empty(); +} diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.h b/pandatool/src/gtk-stats/gtkStatsLabel.h index 53737661d8..185b29a605 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.h +++ b/pandatool/src/gtk-stats/gtkStatsLabel.h @@ -30,7 +30,8 @@ class GtkStatsGraph; class GtkStatsLabel { public: GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, - int thread_index, int collector_index, bool use_fullname); + int thread_index, int collector_index, bool use_fullname, + bool align_right = true); ~GtkStatsLabel(); GtkWidget *get_widget() const; @@ -42,6 +43,8 @@ public: void set_highlight(bool highlight); bool get_highlight() const; + void update_text(bool use_fullname); + private: void set_mouse_within(bool mouse_within); static gboolean draw_callback(GtkWidget *widget, @@ -55,6 +58,9 @@ private: static gboolean button_press_event_callback(GtkWidget *widget, GdkEventButton *event, gpointer data); + static gboolean query_tooltip_callback(GtkWidget *widget, gint x, gint y, + gboolean keyboard_tip, GtkTooltip *tooltip, + gpointer data); GtkStatsMonitor *_monitor; GtkStatsGraph *_graph; @@ -63,19 +69,16 @@ private: std::string _text; GtkWidget *_widget; LRGBColor _fg_color; + LRGBColor _highlight_fg_color; LRGBColor _bg_color; - PangoLayout *_layout; - - /* - COLORREF _bg_color; - COLORREF _fg_color; - HBRUSH _bg_brush; - HBRUSH _highlight_brush; - */ + LRGBColor _highlight_bg_color; + PangoLayout *_layout = nullptr; + int _height; + int _ideal_width; bool _highlight; bool _mouse_within; - int _height; + bool _align_right; static int _left_margin, _right_margin; static int _top_margin, _bottom_margin; diff --git a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx index 079190019c..5a08b2fe38 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx @@ -84,9 +84,7 @@ get_label_collector_index(int label_index) const { */ void GtkStatsLabelStack:: clear_labels(bool delete_widgets) { - Labels::iterator li; - for (li = _labels.begin(); li != _labels.end(); ++li) { - GtkStatsLabel *label = (*li); + for (GtkStatsLabel *label : _labels) { if (delete_widgets) { gtk_container_remove(GTK_CONTAINER(_widget), label->get_widget()); } diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index b312d5ccc3..c215397339 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -17,6 +17,7 @@ #include "gtkStatsStripChart.h" #include "gtkStatsChartMenu.h" #include "gtkStatsPianoRoll.h" +#include "gtkStatsFlameGraph.h" #include "gtkStatsMenuId.h" #include "pStatGraph.h" #include "pStatCollectorDef.h" @@ -251,6 +252,19 @@ open_piano_roll(int thread_index) { graph->set_pause(_pause); } +/** + * Opens a new flame graph showing the indicated data. + */ +void GtkStatsMonitor:: +open_flame_graph(int thread_index) { + GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index); + add_graph(graph); + + graph->set_time_units(_time_units); + graph->set_scroll_speed(_scroll_speed); + graph->set_pause(_pause); +} + /** * Adds a new MenuDef to the monitor, or returns an existing one if there is * already one just like it. diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.h b/pandatool/src/gtk-stats/gtkStatsMonitor.h index ba872289a7..a79a6ad0ed 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.h +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.h @@ -66,6 +66,7 @@ public: GtkWidget *get_window() const; void open_strip_chart(int thread_index, int collector_index, bool show_level); void open_piano_roll(int thread_index); + void open_flame_graph(int thread_index); const MenuDef *add_menu(const MenuDef &menu_def); diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 720e5aa6ea..3f0bc320fa 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -16,7 +16,7 @@ #include "numeric_types.h" #include "gtkStatsLabelStack.h" -static const int default_piano_roll_width = 400; +static const int default_piano_roll_width = 600; static const int default_piano_roll_height = 200; /** @@ -126,7 +126,7 @@ set_time_units(int unit_mask) { * Called when the user single-clicks on a label. */ void GtkStatsPianoRoll:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { if (collector_index >= 0) { GtkStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false); } @@ -167,6 +167,18 @@ begin_draw() { } } +/** + * Should be overridden by the user class. This hook will be called before + * drawing any one row of bars. These bars correspond to the collector whose + * index is get_row_collector(row), and in the color get_row_color(row). + */ +void GtkStatsPianoRoll:: +begin_row(int row) { + int collector_index = get_label_collector(row); + cairo_set_source(_cr, get_collector_pattern(collector_index, + _highlighted_index == collector_index)); +} + /** * Draws a single bar on the chart. */ @@ -176,8 +188,6 @@ draw_bar(int row, int from_x, int to_x) { int y = _label_stack.get_label_y(row, _graph_window); int height = _label_stack.get_label_height(row); - int collector_index = get_label_collector(row); - cairo_set_source(_cr, get_collector_pattern(collector_index)); cairo_rectangle(_cr, from_x, y - height + 2, to_x - from_x, height - 4); cairo_fill(_cr); } @@ -203,7 +213,7 @@ idle() { } /** - * This is called during the servicing of expose_event; it gives a derived + * This is called during the servicing of the draw event; it gives a derived * class opportunity to do some further painting into the graph window. */ void GtkStatsPianoRoll:: @@ -251,7 +261,7 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, if (double_click) { // Double-clicking on a color bar in the graph is the same as double- // clicking on the corresponding label. - clicked_label(get_collector_under_pixel(graph_x, graph_y)); + on_click_label(get_collector_under_pixel(graph_x, graph_y)); return TRUE; } @@ -303,7 +313,9 @@ gboolean GtkStatsPianoRoll:: handle_motion(GtkWidget *widget, int graph_x, int graph_y) { if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { // When the mouse is over a color bar, highlight it. - _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y)); + int collector_index = get_collector_under_pixel(graph_x, graph_y); + _label_stack.highlight_label(collector_index); + on_enter_label(collector_index); /* // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph @@ -320,6 +332,7 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { } else { // If the mouse is in some drag mode, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); } if (_drag_mode == DM_scale) { @@ -409,7 +422,7 @@ draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { } /** - * This is called during the servicing of expose_event. + * This is called during the servicing of the draw event. */ void GtkStatsPianoRoll:: draw_guide_labels(cairo_t *cr) { diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h index 97ad5ad597..a7cfd6afe8 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h @@ -38,12 +38,13 @@ public: virtual void changed_graph_size(int graph_xsize, int graph_ysize); virtual void set_time_units(int unit_mask); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); void set_horizontal_scale(double time_width); protected: void clear_region(); virtual void begin_draw(); + virtual void begin_row(int row); virtual void draw_bar(int row, int from_x, int to_x); virtual void end_draw(); virtual void idle(); diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index f66218b3c8..eb39d875a9 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -68,8 +68,15 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, G_CALLBACK(draw_callback), this); gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area, FALSE, FALSE, 0); - gtk_widget_set_size_request(_scale_area, 40, 0); + // Make it wide enough to display a typical label. + { + PangoLayout *layout = gtk_widget_create_pango_layout(_window, "99 ms"); + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + gtk_widget_set_size_request(_scale_area, width, 0); + g_object_unref(layout); + } gtk_widget_set_size_request(_graph_window, default_strip_chart_width, default_strip_chart_height); @@ -175,7 +182,7 @@ set_scroll_speed(double scroll_speed) { * Called when the user single-clicks on a label. */ void GtkStatsStripChart:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { if (collector_index < 0) { // Clicking on whitespace in the graph is the same as clicking on the top // label. @@ -202,6 +209,15 @@ clicked_label(int collector_index) { } } +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsStripChart:: +get_label_tooltip(int collector_index) const { + return PStatStripChart::get_label_tooltip(collector_index); +} + /** * Changes the value the height of the vertical axis represents. This may * force a redraw. @@ -287,7 +303,8 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) { for (fi = fdata.begin(); fi != fdata.end(); ++fi) { const ColorData &cd = (*fi); overall_time += cd._net_value; - cairo_set_source(_cr, get_collector_pattern(cd._collector_index)); + cairo_set_source(_cr, get_collector_pattern(cd._collector_index, + _highlighted_index == cd._collector_index)); if (overall_time > get_vertical_scale()) { // Off the top. Go ahead and clamp it by hand, in case it's so far off @@ -346,7 +363,7 @@ end_draw(int from_x, int to_x) { } /** - * This is called during the servicing of expose_event; it gives a derived + * This is called during the servicing of the draw event; it gives a derived * class opportunity to do some further painting into the graph window. */ void GtkStatsStripChart:: @@ -418,7 +435,7 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, if (double_click) { // Double-clicking on a color bar in the graph is the same as double- // clicking on the corresponding label. - clicked_label(get_collector_under_pixel(graph_x, graph_y)); + on_click_label(get_collector_under_pixel(graph_x, graph_y)); return TRUE; } @@ -473,23 +490,14 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { if (_drag_mode == DM_none && _potential_drag_mode == DM_none && graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { // When the mouse is over a color bar, highlight it. - _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y)); - - /* - // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph - // window. - TRACKMOUSEEVENT tme = { - sizeof(TRACKMOUSEEVENT), - TME_LEAVE, - _graph_window, - 0 - }; - TrackMouseEvent(&tme); - */ - - } else { + int collector_index = get_collector_under_pixel(graph_x, graph_y); + _label_stack.highlight_label(collector_index); + on_enter_label(collector_index); + } + else { // If the mouse is in some drag mode, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); } if (_drag_mode == DM_scale) { @@ -546,7 +554,7 @@ draw_guide_bar(cairo_t *cr, int from_x, int to_x, } /** - * This is called during the servicing of expose_event. + * This is called during the servicing of the draw event. */ void GtkStatsStripChart:: draw_guide_labels(cairo_t *cr) { diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h index 466b6a7753..b1fc44b88e 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.h +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h @@ -40,7 +40,8 @@ public: virtual void set_time_units(int unit_mask); virtual void set_scroll_speed(double scroll_speed); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; void set_vertical_scale(double value_height); protected: diff --git a/pandatool/src/gtk-stats/gtkstats_composite1.cxx b/pandatool/src/gtk-stats/gtkstats_composite1.cxx index 61e1fa7ab7..fefd3eeff4 100644 --- a/pandatool/src/gtk-stats/gtkstats_composite1.cxx +++ b/pandatool/src/gtk-stats/gtkstats_composite1.cxx @@ -1,5 +1,6 @@ #include "gtkStats.cxx" #include "gtkStatsChartMenu.cxx" +#include "gtkStatsFlameGraph.cxx" #include "gtkStatsGraph.cxx" #include "gtkStatsLabel.cxx" #include "gtkStatsLabelStack.cxx" diff --git a/pandatool/src/pstatserver/CMakeLists.txt b/pandatool/src/pstatserver/CMakeLists.txt index 359f529b18..8017b7888e 100644 --- a/pandatool/src/pstatserver/CMakeLists.txt +++ b/pandatool/src/pstatserver/CMakeLists.txt @@ -4,6 +4,7 @@ endif() set(P3PSTATSERVER_HEADERS pStatClientData.h + pStatFlameGraph.h pStatFlameGraph.I pStatGraph.h pStatGraph.I pStatListener.h pStatMonitor.h pStatMonitor.I @@ -17,7 +18,9 @@ set(P3PSTATSERVER_HEADERS ) set(P3PSTATSERVER_SOURCES - pStatClientData.cxx pStatGraph.cxx + pStatClientData.cxx + pStatFlameGraph.cxx + pStatGraph.cxx pStatListener.cxx pStatMonitor.cxx pStatPianoRoll.cxx pStatReader.cxx pStatServer.cxx diff --git a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx index aa310d92fc..079c6e6d22 100644 --- a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx +++ b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx @@ -1,4 +1,5 @@ #include "pStatClientData.cxx" +#include "pStatFlameGraph.cxx" #include "pStatGraph.cxx" #include "pStatListener.cxx" #include "pStatMonitor.cxx" diff --git a/pandatool/src/pstatserver/pStatFlameGraph.I b/pandatool/src/pstatserver/pStatFlameGraph.I new file mode 100644 index 0000000000..af7921ae4b --- /dev/null +++ b/pandatool/src/pstatserver/pStatFlameGraph.I @@ -0,0 +1,89 @@ +/** + * 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 pStatFlameGraph.I + * @author rdb + * @date 2022-01-28 + */ + +/** + * Returns the View this chart represents. + */ +INLINE PStatView &PStatFlameGraph:: +get_view() const { + return _view; +} + +/** + * Returns the particular collector whose data this strip chart reflects. + */ +INLINE int PStatFlameGraph:: +get_collector_index() const { + return _collector_index; +} + +/** + * Returns the amount of total time the width of the horizontal axis + * represents. + */ +INLINE double PStatFlameGraph:: +get_horizontal_scale() const { + return _time_width; +} + +/** + * Changes the average_mode flag. When true, the strip chart will average out + * the color values over pstats_average_time seconds, which hides spikes and + * makes the overall trends easier to read. When false, the strip chart shows + * the actual data as it is happening. + */ +INLINE void PStatFlameGraph:: +set_average_mode(bool average_mode) { + if (_average_mode != average_mode) { + _average_mode = average_mode; + force_redraw(); + } +} + +/** + * Returns the current state of the average_mode flag. When true, the strip + * chart will average out the color values over pstats_average_time seconds, + * which hides spikes and makes the overall trends easier to read. When + * false, the strip chart shows the actual data as it is happening. + */ +INLINE bool PStatFlameGraph:: +get_average_mode() const { + return _average_mode; +} + +/** + * Converts a value (i.e. a "height" in the strip chart) to a horizontal + * pixel offset. + */ +INLINE int PStatFlameGraph:: +height_to_pixel(double value) const { + return (int)((double)_xsize * value / _time_width); +} + +/** + * Converts a horizontal pixel offset to a value (a "height" in the strip + * chart). + */ +INLINE double PStatFlameGraph:: +pixel_to_height(int x) const { + return _time_width * (double)x / (double)_xsize; +} + +/** + * Returns true if get_title_text() has never yet returned an answer, false if + * it has. + */ +INLINE bool PStatFlameGraph:: +is_title_unknown() const { + return _title_unknown; +} diff --git a/pandatool/src/pstatserver/pStatFlameGraph.cxx b/pandatool/src/pstatserver/pStatFlameGraph.cxx new file mode 100644 index 0000000000..16643345d7 --- /dev/null +++ b/pandatool/src/pstatserver/pStatFlameGraph.cxx @@ -0,0 +1,297 @@ +/** + * 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 pStatFlameGraph.cxx + * @author rdb + * @date 2022-01-28 + */ + +#include "pStatFlameGraph.h" + +#include "pStatFrameData.h" +#include "pStatCollectorDef.h" +#include "string_utils.h" +#include "config_pstatclient.h" + +#include +#include + +/** + * + */ +PStatFlameGraph:: +PStatFlameGraph(PStatMonitor *monitor, PStatView &view, + int thread_index, int collector_index, int xsize, int ysize) : + PStatGraph(monitor, xsize, ysize), + _thread_index(thread_index), + _view(view), + _collector_index(collector_index) +{ + _average_mode = true; + _average_cursor = 0; + + _time_width = 1.0 / pstats_target_frame_rate; + _current_frame = -1; + + _title_unknown = true; + + _guide_bar_units = GBU_ms | GBU_hz | GBU_show_units; + normal_guide_bars(); +} + +/** + * + */ +PStatFlameGraph:: +~PStatFlameGraph() { +} + +/** + * Updates the chart with the latest data. + */ +void PStatFlameGraph:: +update() { + const PStatClientData *client_data = _monitor->get_client_data(); + + // Don't bother to update the thread data until we know at least something + // about the collectors and threads. + if (client_data->get_num_collectors() != 0 && + client_data->get_num_threads() != 0) { + const PStatThreadData *thread_data = + client_data->get_thread_data(_thread_index); + if (!thread_data->is_empty()) { + int frame_number = thread_data->get_latest_frame_number(); + if (frame_number != _current_frame) { + _current_frame = frame_number; + + update_data(); + force_redraw(); + update_labels(); + } + } + } + + idle(); +} + +/** + * Changes the collector represented by this flame graph. This may force a + * redraw. + */ +void PStatFlameGraph:: +set_collector_index(int collector_index) { + if (_collector_index != collector_index) { + _collector_index = collector_index; + _title_unknown = true; + update_data(); + force_redraw(); + update_labels(); + } +} + +/** + * Returns the text suitable for the title label on the top line. + */ +std::string PStatFlameGraph:: +get_title_text() { + std::string text; + + _title_unknown = false; + + const PStatClientData *client_data = _monitor->get_client_data(); + if (client_data->has_collector(_collector_index)) { + text = client_data->get_collector_fullname(_collector_index); + text += " flame graph"; + } else { + _title_unknown = true; + } + + if (_thread_index != 0) { + if (client_data->has_thread(_thread_index)) { + text += " (" + client_data->get_thread_name(_thread_index) + " thread)"; + } else { + _title_unknown = true; + } + } + + return text; +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string PStatFlameGraph:: +get_label_tooltip(int collector_index) const { + const PStatClientData *client_data = _monitor->get_client_data(); + if (!client_data->has_collector(collector_index)) { + return std::string(); + } + + std::ostringstream text; + text << client_data->get_collector_fullname(collector_index); + + Data::const_iterator it = _data.find(collector_index); + if (it != _data.end()) { + const CollectorData &cd = it->second; + text << " (" << format_number(cd._net_value, get_guide_bar_units(), get_guide_bar_unit_name()) << ")"; + } + + return text.str(); +} + +/** + * + */ +void PStatFlameGraph:: +update_data() { + // First clear the net values, so we'll know which labels should be deleted. + for (auto it = _data.begin(); it != _data.end(); ++it) { + it->second._net_value = 0; + } + + _view.set_to_frame(_current_frame); + + const PStatViewLevel *level = _view.get_level(_collector_index); + double offset = 0; + update_data(level, 0, offset); + + _time_width = (offset != 0) ? offset : 1.0 / pstats_target_frame_rate; + normal_guide_bars(); + + // Cycle through the ring buffers. + _average_cursor = (_average_cursor + 1) % _num_average_frames; +} + +/** + * Recursive helper for get_frame_data. + */ +void PStatFlameGraph:: +update_data(const PStatViewLevel *level, int depth, double &offset) { + double net_value = level->get_net_value(); + + Data::iterator it; + bool inserted; + std::tie(it, inserted) = _data.insert(std::make_pair(level->get_collector(), CollectorData())); + CollectorData &cd = it->second; + cd._offset = offset; + cd._depth = depth; + + if (inserted || !_average_mode) { + // Initialize the values array. + for (double &v : cd._values) { + v = net_value; + } + cd._net_value = net_value; + } else { + cd._values[_average_cursor] = net_value; + + // Calculate the average. + cd._net_value = 0; + for (double value : cd._values) { + cd._net_value += value; + } + cd._net_value /= _num_average_frames; + } + + if (cd._net_value != 0.0) { + cd._net_value = std::max(cd._net_value, 0.0); + + double child_offset = offset; + offset += cd._net_value; + + int num_children = level->get_num_children(); + for (int i = 0; i < num_children; i++) { + const PStatViewLevel *child = level->get_child(i); + update_data(child, depth + 1, child_offset); + } + } +} + +/** + * To be called by the user class when the widget size has changed. This + * updates the chart's internal data and causes it to issue redraw commands to + * reflect the new size. + */ +void PStatFlameGraph:: +changed_size(int xsize, int ysize) { + if (xsize != _xsize || ysize != _ysize) { + _xsize = xsize; + _ysize = ysize; + + normal_guide_bars(); + force_redraw(); + update_labels(); + } +} + +/** + * To be called by the user class when the whole thing needs to be redrawn for + * some reason. + */ +void PStatFlameGraph:: +force_redraw() { + begin_draw(); + end_draw(); +} + +/** + * Resets the list of labels. + */ +void PStatFlameGraph:: +update_labels() { + for (auto it = _data.begin(); it != _data.end(); ++it) { + int collector_index = it->first; + const CollectorData &cd = it->second; + + update_label(collector_index, cd._depth, height_to_pixel(cd._offset), height_to_pixel(cd._net_value)); + } +} + +/** + * Calls update_guide_bars with parameters suitable to this kind of graph. + */ +void PStatFlameGraph:: +normal_guide_bars() { + // We want vaguely 100 pixels between guide bars. + int num_bars = get_xsize() / 100; + + _guide_bars.clear(); + + double dist = _time_width / num_bars; + + for (int i = 1; i < num_bars; ++i) { + _guide_bars.push_back(make_guide_bar(i * dist)); + } + + _guide_bars_changed = true; +} + +/** + * Should be overridden by the user class. This hook will be called before + * drawing any bars in the chart. + */ +void PStatFlameGraph:: +begin_draw() { +} + +/** + * Should be overridden by the user class. This hook will be called after + * drawing a series of color bars in the chart. + */ +void PStatFlameGraph:: +end_draw() { +} + +/** + * Should be overridden by the user class to perform any other updates might + * be necessary after the bars have been redrawn. + */ +void PStatFlameGraph:: +idle() { +} diff --git a/pandatool/src/pstatserver/pStatFlameGraph.h b/pandatool/src/pstatserver/pStatFlameGraph.h new file mode 100644 index 0000000000..148e288af9 --- /dev/null +++ b/pandatool/src/pstatserver/pStatFlameGraph.h @@ -0,0 +1,107 @@ +/** + * 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 pStatFlameGraph.h + * @author rdb + * @date 2022-01-28 + */ + +#ifndef PSTATFLAMEGRAPH_H +#define PSTATFLAMEGRAPH_H + +#include "pandatoolbase.h" + +#include "pStatGraph.h" +#include "pStatMonitor.h" +#include "pStatClientData.h" + +#include "pmap.h" +#include "pdeque.h" + +class PStatFrameData; + +/** + * This is an abstract class that presents the interface for drawing a flame + * chart: it shows the time spent in each of a number of collectors + * as a horizontal bar of color, with time as the horizontal axis. + * + * This class just manages all the flame chart logic; the actual nuts and bolts + * of drawing pixels is left to a user-derived class. + */ +class PStatFlameGraph : public PStatGraph { +public: + PStatFlameGraph(PStatMonitor *monitor, PStatView &view, + int thread_index, int collector_index, + int xsize, int ysize); + virtual ~PStatFlameGraph(); + + void update(); + + INLINE PStatView &get_view() const; + INLINE int get_collector_index() const; + void set_collector_index(int collector_index); + + INLINE double get_horizontal_scale() const; + + INLINE void set_average_mode(bool average_mode); + INLINE bool get_average_mode() const; + + INLINE int height_to_pixel(double value) const; + INLINE double pixel_to_height(int y) const; + + INLINE bool is_title_unknown() const; + std::string get_title_text(); + std::string get_label_tooltip(int collector_index) const; + +protected: + static const size_t _num_average_frames = 200; + + struct CollectorData { + double _offset; + double _net_value; + int _depth; + // This is updated like a ring buffer, initialized with all the same value + // at first, then always at _average_cursor. + double _values[_num_average_frames]; + }; + typedef pmap Data; + + void update_data(); + void update_data(const PStatViewLevel *level, int depth, double &offset); + void changed_size(int xsize, int ysize); + void force_redraw(); + virtual void update_labels(); + virtual void update_label(int collector_index, int row, int x, int width)=0; + virtual void normal_guide_bars(); + + virtual void begin_draw(); + virtual void end_draw(); + virtual void idle(); + +private: + void compute_page(const PStatFrameData &frame_data); + +protected: + int _thread_index; + +private: + PStatView &_view; + int _collector_index; + bool _average_mode; + size_t _average_cursor; + + Data _data; + + double _time_width; + int _current_frame; + bool _title_unknown; +}; + +#include "pStatFlameGraph.I" + +#endif diff --git a/pandatool/src/pstatserver/pStatStripChart.I b/pandatool/src/pstatserver/pStatStripChart.I index 791e722009..35d4c86265 100644 --- a/pandatool/src/pstatserver/pStatStripChart.I +++ b/pandatool/src/pstatserver/pStatStripChart.I @@ -156,6 +156,15 @@ pixel_to_height(int x) const { return _value_height * (double)(get_ysize() - x) / (double)get_ysize(); } +/** + * Returns true if get_title_text() has never yet returned an answer, false if + * it has. + */ +INLINE bool PStatStripChart:: +is_title_unknown() const { + return _title_unknown; +} + /** * Returns true if the indicated collector appears anywhere on the chart at * the current time, false otherwise. diff --git a/pandatool/src/pstatserver/pStatStripChart.cxx b/pandatool/src/pstatserver/pStatStripChart.cxx index c9ffcb3109..09dadc9545 100644 --- a/pandatool/src/pstatserver/pStatStripChart.cxx +++ b/pandatool/src/pstatserver/pStatStripChart.cxx @@ -284,12 +284,71 @@ get_title_text() { } /** - * Returns true if get_title_text() has never yet returned an answer, false if - * it has. + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. */ -bool PStatStripChart:: -is_title_unknown() const { - return _title_unknown; +std::string PStatStripChart:: +get_label_tooltip(int collector_index) const { + const PStatClientData *client_data = _monitor->get_client_data(); + if (!client_data->has_collector(collector_index)) { + return std::string(); + } + + std::ostringstream text; + text << client_data->get_collector_fullname(collector_index); + + double value; + if (collector_index == _collector_index) { + value = get_average_net_value(); + } + else { + const PStatThreadData *thread_data = _view.get_thread_data(); + int now_i, then_i; + if (!thread_data->get_elapsed_frames(then_i, now_i)) { + return text.str(); + } + double now = _time_width + _start_time; + double then = now - pstats_average_time; + + double net_value = 0.0f; + double net_time = 0.0f; + + // We start with just the portion of frame then_i that actually does fall + // within our "then to now" window (usually some portion of it will). + const PStatFrameData &frame_data = thread_data->get_frame(then_i); + if (frame_data.get_end() > then) { + double this_time = (frame_data.get_end() - then); + _view.set_to_frame(frame_data); + + const PStatViewLevel *level = _view.get_level(collector_index); + if (level != nullptr) { + net_value += level->get_net_value() * this_time; + net_time += this_time; + } + } + // Then we get all of each of the remaining frames. + for (int frame_number = then_i + 1; + frame_number <= now_i; + frame_number++) { + const PStatFrameData &frame_data = thread_data->get_frame(frame_number); + double this_time = frame_data.get_net_time(); + _view.set_to_frame(frame_data); + + const PStatViewLevel *level = _view.get_level(collector_index); + if (level != nullptr) { + net_value += level->get_net_value() * this_time; + net_time += this_time; + } + } + + if (net_time == 0) { + return text.str(); + } + value = net_value / net_time; + } + + text << " (" << format_number(value, get_guide_bar_units(), get_guide_bar_unit_name()) << ")"; + return text.str(); } /** diff --git a/pandatool/src/pstatserver/pStatStripChart.h b/pandatool/src/pstatserver/pStatStripChart.h index 5cba0164e2..f98af23ac4 100644 --- a/pandatool/src/pstatserver/pStatStripChart.h +++ b/pandatool/src/pstatserver/pStatStripChart.h @@ -68,8 +68,9 @@ public: INLINE int height_to_pixel(double value) const; INLINE double pixel_to_height(int y) const; - std::string get_title_text(); bool is_title_unknown() const; + std::string get_title_text(); + std::string get_label_tooltip(int collector_index) const; protected: class ColorData { @@ -89,6 +90,7 @@ protected: void compute_average_pixel_data(PStatStripChart::FrameData &result, int &then_i, int &now_i, double now); double get_net_value(int frame_number) const; + double get_net_value(int frame_number, int collector_index) const; double get_average_net_value() const; void changed_size(int xsize, int ysize); diff --git a/pandatool/src/win-stats/CMakeLists.txt b/pandatool/src/win-stats/CMakeLists.txt index 0cd7768c3a..4c10e4f560 100644 --- a/pandatool/src/win-stats/CMakeLists.txt +++ b/pandatool/src/win-stats/CMakeLists.txt @@ -4,9 +4,10 @@ endif() set(WINSTATS_HEADERS winStatsChartMenu.h + winStatsFlameGraph.h winStatsGraph.h winStats.h - winStatsLabel.h + winStatsLabel.h winStatsLabel.I winStatsLabelStack.h winStatsMenuId.h winStatsMonitor.h winStatsMonitor.I @@ -18,6 +19,7 @@ set(WINSTATS_HEADERS set(WINSTATS_SOURCES winStatsChartMenu.cxx winStats.cxx + winStatsFlameGraph.cxx winStatsGraph.cxx winStatsLabel.cxx winStatsLabelStack.cxx @@ -29,7 +31,7 @@ set(WINSTATS_SOURCES composite_sources(win-stats WINSTATS_SOURCES) add_executable(win-stats ${WINSTATS_HEADERS} ${WINSTATS_SOURCES}) -target_link_libraries(win-stats p3progbase p3pstatserver) +target_link_libraries(win-stats p3progbase p3pstatserver comctl32.lib) # This program is NOT actually called win-stats. It's just pstats.exe set_target_properties(win-stats PROPERTIES OUTPUT_NAME "pstats") diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx index e40830e189..5dabb7b71a 100644 --- a/pandatool/src/win-stats/winStats.cxx +++ b/pandatool/src/win-stats/winStats.cxx @@ -20,6 +20,11 @@ #define WIN32_LEAN_AND_MEAN 1 #endif #include +#include +#include + +// Enable common controls version 6, necessary for modern visual styles +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") static const char *toplevel_class_name = "pstats"; static WinStatsServer *server = nullptr; @@ -82,6 +87,15 @@ create_toplevel_window(HINSTANCE application) { } int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { + // Initialize commctl32.dll. + INITCOMMONCONTROLSEX icc; + icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES; + icc.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitCommonControlsEx(&icc); + + // Signal DPI awareness. + SetProcessDPIAware(); + HINSTANCE application = GetModuleHandle(nullptr); HWND toplevel_window = create_toplevel_window(application); diff --git a/pandatool/src/win-stats/winStatsChartMenu.cxx b/pandatool/src/win-stats/winStatsChartMenu.cxx index c042acf8ba..026e082489 100644 --- a/pandatool/src/win-stats/winStatsChartMenu.cxx +++ b/pandatool/src/win-stats/winStatsChartMenu.cxx @@ -125,19 +125,32 @@ do_update() { } } - // Also a menu item for a piano roll (following a separator). + // Also menu items for flame graph and piano roll (following a separator). mii.fMask = MIIM_FTYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); - WinStatsMonitor::MenuDef menu_def(_thread_index, -1, false); - int menu_id = _monitor->get_menu_id(menu_def); + { + WinStatsMonitor::MenuDef menu_def(_thread_index, -2, false); + int menu_id = _monitor->get_menu_id(menu_def); - mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; - mii.fType = MFT_STRING; - mii.wID = menu_id; - mii.dwTypeData = "Piano Roll"; - InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = "Flame Graph"; + InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + } + + { + WinStatsMonitor::MenuDef menu_def(_thread_index, -1, false); + int menu_id = _monitor->get_menu_id(menu_def); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = "Piano Roll"; + InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + } } /** diff --git a/pandatool/src/win-stats/winStatsFlameGraph.cxx b/pandatool/src/win-stats/winStatsFlameGraph.cxx new file mode 100644 index 0000000000..90ad7cd280 --- /dev/null +++ b/pandatool/src/win-stats/winStatsFlameGraph.cxx @@ -0,0 +1,639 @@ +/** + * 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 winStatsFlameGraph.cxx + * @author rdb + * @date 2022-01-28 + */ + +#include "winStatsFlameGraph.h" +#include "winStatsLabel.h" +#include "winStatsMonitor.h" +#include "pStatCollectorDef.h" + +#include + +static const int default_flame_graph_width = 800; +static const int default_flame_graph_height = 150; + +bool WinStatsFlameGraph::_window_class_registered = false; +const char * const WinStatsFlameGraph::_window_class_name = "flame"; + +/** + * + */ +WinStatsFlameGraph:: +WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index, + int collector_index) : + PStatFlameGraph(monitor, monitor->get_view(thread_index), + thread_index, collector_index, + monitor->get_pixel_scale() * default_flame_graph_width / 4, + monitor->get_pixel_scale() * default_flame_graph_height / 4), + WinStatsGraph(monitor) +{ + _left_margin = _pixel_scale * 2; + _right_margin = _pixel_scale * 2; + _top_margin = _pixel_scale * 6; + _bottom_margin = _pixel_scale * 2; + + // Let's show the units on the guide bar labels. There's room. + set_guide_bar_units(get_guide_bar_units() | GBU_show_units); + + _average_check_box = 0; + + create_window(); + clear_region(); +} + +/** + * + */ +WinStatsFlameGraph:: +~WinStatsFlameGraph() { +} + +/** + * Called as each frame's data is made available. There is no guarantee the + * frames will arrive in order, or that all of them will arrive at all. The + * monitor should be prepared to accept frames received out-of-order or + * missing. + */ +void WinStatsFlameGraph:: +new_data(int thread_index, int frame_number) { + if (is_title_unknown()) { + std::string window_title = get_title_text(); + if (!is_title_unknown()) { + SetWindowText(_window, window_title.c_str()); + } + } + + if (!_pause) { + update(); + + std::string text = format_number(get_horizontal_scale(), get_guide_bar_units(), get_guide_bar_unit_name()); + if (_net_value_text != text) { + _net_value_text = text; + RECT rect; + GetClientRect(_window, &rect); + rect.bottom = _top_margin; + InvalidateRect(_window, &rect, TRUE); + } + } +} + +/** + * Called when it is necessary to redraw the entire graph. + */ +void WinStatsFlameGraph:: +force_redraw() { + PStatFlameGraph::force_redraw(); +} + +/** + * Called when the user has resized the window, forcing a resize of the graph. + */ +void WinStatsFlameGraph:: +changed_graph_size(int graph_xsize, int graph_ysize) { + PStatFlameGraph::changed_size(graph_xsize, graph_ysize); +} + +/** + * Called when the user selects a new time units from the monitor pulldown + * menu, this should adjust the units for the graph to the indicated mask if + * it is a time-based graph. + */ +void WinStatsFlameGraph:: +set_time_units(int unit_mask) { + int old_unit_mask = get_guide_bar_units(); + if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) { + unit_mask = unit_mask & (GBU_hz | GBU_ms); + unit_mask |= (old_unit_mask & GBU_show_units); + set_guide_bar_units(unit_mask); + + RECT rect; + GetClientRect(_window, &rect); + rect.left = _right_margin; + InvalidateRect(_window, &rect, TRUE); + } +} + +/** + * Called when the user single-clicks on a label. + */ +void WinStatsFlameGraph:: +on_click_label(int collector_index) { + int prev_collector_index = get_collector_index(); + if (collector_index == prev_collector_index && collector_index != 0) { + // Clicking on the top label means to go up to the parent level. + const PStatClientData *client_data = + WinStatsGraph::_monitor->get_client_data(); + if (client_data->has_collector(collector_index)) { + const PStatCollectorDef &def = + client_data->get_collector_def(collector_index); + collector_index = def._parent_index; + set_collector_index(collector_index); + } + } + else { + // Clicking on any other label means to focus on that. + set_collector_index(collector_index); + } + + // Change the root collector to show the full name. + if (prev_collector_index != collector_index) { + auto it = _labels.find(prev_collector_index); + if (it != _labels.end()) { + it->second->update_text(false); + } + it = _labels.find(collector_index); + if (it != _labels.end()) { + it->second->update_text(true); + } + } +} + +/** + * Called when the user hovers the mouse over a label. + */ +void WinStatsFlameGraph:: +on_enter_label(int collector_index) { + if (collector_index != _highlighted_index) { + _highlighted_index = collector_index; + } +} + +/** + * Called when the user's mouse cursor leaves a label. + */ +void WinStatsFlameGraph:: +on_leave_label(int collector_index) { + if (collector_index == _highlighted_index && collector_index != -1) { + _highlighted_index = -1; + } +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsFlameGraph:: +get_label_tooltip(int collector_index) const { + return PStatFlameGraph::get_label_tooltip(collector_index); +} + +/** + * Repositions the labels. + */ +void WinStatsFlameGraph:: +update_labels() { + if (_graph_window) { + PStatFlameGraph::update_labels(); + } +} + +/** + * Repositions a label. If width is 0, the label should be deleted. + */ +void WinStatsFlameGraph:: +update_label(int collector_index, int row, int x, int width) { + WinStatsLabel *label; + + auto it = _labels.find(collector_index); + if (it != _labels.end()) { + if (width == 0) { + delete it->second; + _labels.erase(it); + return; + } + label = it->second; + } else { + if (width == 0) { + return; + } + label = new WinStatsLabel(WinStatsGraph::_monitor, this, _thread_index, collector_index, false, false); + _labels[collector_index] = label; + label->setup(_graph_window); + } + + label->set_pos(x, _ysize - 2 - row * label->get_height(), std::min(width, _xsize - 2)); +} + +/** + * Calls update_guide_bars with parameters suitable to this kind of graph. + */ +void WinStatsFlameGraph:: +normal_guide_bars() { + // We want vaguely 100 pixels between guide bars. + int num_bars = get_xsize() / (_pixel_scale * 25); + + _guide_bars.clear(); + + double dist = get_horizontal_scale() / num_bars; + + for (int i = 1; i < num_bars; ++i) { + _guide_bars.push_back(make_guide_bar(i * dist)); + } + + _guide_bars_changed = true; +} + +/** + * Erases the chart area. + */ +void WinStatsFlameGraph:: +clear_region() { + RECT rect = { 0, 0, get_xsize(), get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); +} + +/** + * Erases the chart area in preparation for drawing a bunch of bars. + */ +void WinStatsFlameGraph:: +begin_draw() { + clear_region(); + + // Draw in the guide bars. + int num_guide_bars = get_num_guide_bars(); + for (int i = 0; i < num_guide_bars; i++) { + draw_guide_bar(_bitmap_dc, get_guide_bar(i)); + } +} + +/** + * Called after all the bars have been drawn, this triggers a refresh event to + * draw it to the window. + */ +void WinStatsFlameGraph:: +end_draw() { + InvalidateRect(_graph_window, nullptr, FALSE); +} + +/** + * Called at the end of the draw cycle. + */ +void WinStatsFlameGraph:: +idle() { +} + +/** + * + */ +LONG WinStatsFlameGraph:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_LBUTTONDOWN: + if (_potential_drag_mode == DM_new_guide_bar) { + set_drag_mode(DM_new_guide_bar); + SetCapture(_graph_window); + return 0; + } + break; + + case WM_COMMAND: + switch (LOWORD(wparam)) { + case BN_CLICKED: + if ((HWND)lparam == _average_check_box) { + int result = SendMessage(_average_check_box, BM_GETCHECK, 0, 0); + set_average_mode(result == BST_CHECKED); + return 0; + } + break; + } + break; + + default: + break; + } + + return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam); +} + +/** + * + */ +LONG WinStatsFlameGraph:: +graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_LBUTTONDOWN: + if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { + set_drag_mode(DM_guide_bar); + int16_t x = LOWORD(lparam); + _drag_start_x = x; + SetCapture(_graph_window); + return 0; + } + break; + + case WM_MOUSEMOVE: + if (_drag_mode == DM_new_guide_bar) { + // We haven't created the new guide bar yet; we won't until the mouse + // comes within the graph's region. + int16_t x = LOWORD(lparam); + if (x >= 0 && x < get_xsize()) { + set_drag_mode(DM_guide_bar); + _drag_guide_bar = add_user_guide_bar(pixel_to_height(x)); + return 0; + } + } + else if (_drag_mode == DM_guide_bar) { + int16_t x = LOWORD(lparam); + move_user_guide_bar(_drag_guide_bar, pixel_to_height(x)); + return 0; + } + break; + + case WM_LBUTTONUP: + if (_drag_mode == DM_guide_bar) { + int16_t x = LOWORD(lparam); + if (x < 0 || x >= get_xsize()) { + remove_user_guide_bar(_drag_guide_bar); + } else { + move_user_guide_bar(_drag_guide_bar, pixel_to_height(x)); + } + set_drag_mode(DM_none); + ReleaseCapture(); + return 0; + } + break; + + case WM_LBUTTONDBLCLK: + { + // Clicking on whitespace in the graph goes to the parent. + on_click_label(get_collector_index()); + return 0; + } + break; + + default: + break; + } + + return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam); +} + +/** + * This is called during the servicing of WM_PAINT; it gives a derived class + * opportunity to do some further painting into the window (the outer window, + * not the graph window). + */ +void WinStatsFlameGraph:: +additional_window_paint(HDC hdc) { + // Draw in the labels for the guide bars. + SelectObject(hdc, WinStatsGraph::_monitor->get_font()); + SetTextAlign(hdc, TA_LEFT | TA_BOTTOM); + SetBkMode(hdc, TRANSPARENT); + + int y = _top_margin - _pixel_scale / 2; + + int i; + int num_guide_bars = get_num_guide_bars(); + for (i = 0; i < num_guide_bars; i++) { + draw_guide_label(hdc, y, get_guide_bar(i)); + } + + int num_user_guide_bars = get_num_user_guide_bars(); + for (i = 0; i < num_user_guide_bars; i++) { + draw_guide_label(hdc, y, get_user_guide_bar(i)); + } + + RECT rect; + GetClientRect(_window, &rect); + + // Now draw the "net value" label at the top. + SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM); + SetTextColor(hdc, RGB(0, 0, 0)); + TextOut(hdc, rect.right - _right_margin, y, + _net_value_text.data(), _net_value_text.length()); +} + +/** + * This is called during the servicing of WM_PAINT; it gives a derived class + * opportunity to do some further painting into the window (the outer window, + * not the graph window). + */ +void WinStatsFlameGraph:: +additional_graph_window_paint(HDC hdc) { + int num_user_guide_bars = get_num_user_guide_bars(); + for (int i = 0; i < num_user_guide_bars; i++) { + draw_guide_bar(hdc, get_user_guide_bar(i)); + } +} + +/** + * Based on the mouse position within the window's client area, look for + * draggable things the mouse might be hovering over and return the + * apprioprate DragMode enum or DM_none if nothing is indicated. + */ +WinStatsGraph::DragMode WinStatsFlameGraph:: +consider_drag_start(int mouse_x, int mouse_y, int width, int height) { + if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) { + if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) { + // See if the mouse is over a user-defined guide bar. + int x = mouse_x - _graph_left; + double from_height = pixel_to_height(x - 2); + double to_height = pixel_to_height(x + 2); + _drag_guide_bar = find_user_guide_bar(from_height, to_height); + if (_drag_guide_bar >= 0) { + return DM_guide_bar; + } + + } else if (mouse_x < _left_margin - 2 || + mouse_x > width - _right_margin + 2) { + // The mouse is left or right of the graph; maybe create a new guide + // bar. + return DM_new_guide_bar; + } + } + + // Don't upcall; there's no point resizing the margins. + return DM_none; +} + +/** + * Repositions the graph child window within the parent window according to + * the _margin variables. + */ +void WinStatsFlameGraph:: +move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) { + WinStatsGraph::move_graph_window(graph_left, graph_top, graph_xsize, graph_ysize); + if (_average_check_box != 0) { + SIZE size; + SendMessage(_average_check_box, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + + SetWindowPos(_average_check_box, 0, + _left_margin, _top_margin - size.cy - _pixel_scale / 2, + size.cx, size.cy, + SWP_NOZORDER | SWP_SHOWWINDOW); + InvalidateRect(_average_check_box, nullptr, TRUE); + } +} + +/** + * Draws the line for the indicated guide bar on the graph. + */ +void WinStatsFlameGraph:: +draw_guide_bar(HDC hdc, const PStatGraph::GuideBar &bar) { + int x = height_to_pixel(bar._height); + + if (x > 0 && x < get_xsize() - 1) { + // Only draw it if it's not too close to either edge. + switch (bar._style) { + case GBS_target: + SelectObject(hdc, _light_pen); + break; + + case GBS_user: + SelectObject(hdc, _user_guide_bar_pen); + break; + + case GBS_normal: + SelectObject(hdc, _dark_pen); + break; + } + MoveToEx(hdc, x, 0, nullptr); + LineTo(hdc, x, get_ysize()); + } +} + +/** + * Draws the text for the indicated guide bar label at the top of the graph. + */ +void WinStatsFlameGraph:: +draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) { + switch (bar._style) { + case GBS_target: + SetTextColor(hdc, _light_color); + break; + + case GBS_user: + SetTextColor(hdc, _user_guide_bar_color); + break; + + case GBS_normal: + SetTextColor(hdc, _dark_color); + break; + } + + int x = height_to_pixel(bar._height); + const std::string &label = bar._label; + SIZE size; + GetTextExtentPoint32(hdc, label.data(), label.length(), &size); + + if (bar._style != GBS_user) { + double from_height = pixel_to_height(x - size.cx); + double to_height = pixel_to_height(x + size.cx); + if (find_user_guide_bar(from_height, to_height) >= 0) { + // Omit the label: there's a user-defined guide bar in the same space. + return; + } + } + + int this_x = _graph_left + x - size.cx / 2; + if (x >= 0 && x < get_xsize()) { + TextOut(hdc, this_x, y, + label.data(), label.length()); + } +} + +/** + * Creates the window for this strip chart. + */ +void WinStatsFlameGraph:: +create_window() { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(nullptr); + register_window_class(application); + + std::string window_title = get_title_text(); + + RECT win_rect = { + 0, 0, + _left_margin + get_xsize() + _right_margin, + _top_margin + get_ysize() + _bottom_margin + }; + + // compute window size based on desired client area size + AdjustWindowRect(&win_rect, graph_window_style, FALSE); + + _window = + CreateWindow(_window_class_name, window_title.c_str(), graph_window_style, + CW_USEDEFAULT, CW_USEDEFAULT, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), nullptr, application, 0); + if (!_window) { + nout << "Could not create FlameGraph window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); + + _average_check_box = + CreateWindow(WC_BUTTON, "Average", WS_CHILD | BS_AUTOCHECKBOX, + 0, 0, 0, 0, + _window, nullptr, application, 0); + SendMessage(_average_check_box, WM_SETFONT, + (WPARAM)WinStatsGraph::_monitor->get_font(), TRUE); + + if (get_average_mode()) { + SendMessage(_average_check_box, BM_SETCHECK, BST_CHECKED, 0); + } + + // Ensure that the window is on top of the stack. + SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +} + +/** + * Registers the window class for the FlameGraph window, if it has not already + * been registered. + */ +void WinStatsFlameGraph:: +register_window_class(HINSTANCE application) { + if (_window_class_registered) { + return; + } + + WNDCLASS wc; + + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)static_window_proc; + wc.hInstance = application; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.lpszMenuName = nullptr; + wc.lpszClassName = _window_class_name; + + // Reserve space to associate the this pointer with the window. + wc.cbWndExtra = sizeof(WinStatsFlameGraph *); + + if (!RegisterClass(&wc)) { + nout << "Could not register FlameGraph window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +/** + * + */ +LONG WINAPI WinStatsFlameGraph:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsFlameGraph *self = (WinStatsFlameGraph *)GetWindowLongPtr(hwnd, 0); + if (self != nullptr && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} diff --git a/pandatool/src/win-stats/winStatsFlameGraph.h b/pandatool/src/win-stats/winStatsFlameGraph.h new file mode 100644 index 0000000000..262f05979e --- /dev/null +++ b/pandatool/src/win-stats/winStatsFlameGraph.h @@ -0,0 +1,80 @@ +/** + * 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 winStatsFlameGraph.h + * @author rdb + * @date 2022-01-28 + */ + +#ifndef WINSTATSFLAMEGRAPH_H +#define WINSTATSFLAMEGRAPH_H + +#include "pandatoolbase.h" + +#include "winStatsGraph.h" +#include "pStatFlameGraph.h" + +class WinStatsLabel; + +/** + * A window that draws a flame chart, which shows the collectors explicitly + * stopping and starting, one frame at a time. + */ +class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph { +public: + WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index, + int collector_index=0); + virtual ~WinStatsFlameGraph(); + + virtual void new_data(int thread_index, int frame_number); + virtual void force_redraw(); + virtual void changed_graph_size(int graph_xsize, int graph_ysize); + + virtual void set_time_units(int unit_mask); + virtual void on_click_label(int collector_index); + virtual void on_enter_label(int collector_index); + virtual void on_leave_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; + +protected: + virtual void update_labels(); + virtual void update_label(int collector_index, int row, int x, int width); + virtual void normal_guide_bars(); + + void clear_region(); + virtual void begin_draw(); + virtual void end_draw(); + virtual void idle(); + + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + virtual void additional_window_paint(HDC hdc); + virtual void additional_graph_window_paint(HDC hdc); + virtual DragMode consider_drag_start(int mouse_x, int mouse_y, + int width, int height); + virtual void move_graph_window(int graph_left, int graph_top, + int graph_xsize, int graph_ysize); + +private: + void draw_guide_bar(HDC hdc, const GuideBar &bar); + void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar); + void create_window(); + static void register_window_class(HINSTANCE application); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + std::string _net_value_text; + pmap _labels; + + HWND _average_check_box; + + static bool _window_class_registered; + static const char * const _window_class_name; +}; + +#endif diff --git a/pandatool/src/win-stats/winStatsGraph.cxx b/pandatool/src/win-stats/winStatsGraph.cxx index 4946bbc671..7fd3b251fa 100644 --- a/pandatool/src/win-stats/winStatsGraph.cxx +++ b/pandatool/src/win-stats/winStatsGraph.cxx @@ -14,9 +14,11 @@ #include "winStatsGraph.h" #include "winStatsMonitor.h" #include "winStatsLabelStack.h" +#include "convert_srgb.h" -bool WinStatsGraph::_graph_window_class_registered = false; -const char * const WinStatsGraph::_graph_window_class_name = "graph"; +#include + +#define IDC_GRAPH 100 DWORD WinStatsGraph::graph_window_style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE; @@ -40,6 +42,8 @@ WinStatsGraph(WinStatsMonitor *monitor) : _bitmap_xsize = 0; _bitmap_ysize = 0; + _pixel_scale = monitor->get_pixel_scale(); + _dark_color = RGB(51, 51, 51); _light_color = RGB(154, 154, 154); _user_guide_bar_color = RGB(130, 150, 255); @@ -66,11 +70,11 @@ WinStatsGraph:: DeleteObject(_light_pen); DeleteObject(_user_guide_bar_pen); - Brushes::iterator bi; - for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) { - HBRUSH brush = (*bi).second; - DeleteObject(brush); + for (auto &item : _brushes) { + DeleteObject(item.second.first); + DeleteObject(item.second.second); } + _brushes.clear(); if (_graph_window) { DestroyWindow(_graph_window); @@ -97,13 +101,6 @@ void WinStatsGraph:: new_data(int thread_index, int frame_number) { } -/** - * Called when it is necessary to redraw the entire graph. - */ -void WinStatsGraph:: -force_redraw() { -} - /** * Called when the user has resized the window, forcing a resize of the graph. */ @@ -150,7 +147,46 @@ user_guide_bars_changed() { * Called when the user single-clicks on a label. */ void WinStatsGraph:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { +} + +/** + * Called when the user hovers the mouse over a label. + */ +void WinStatsGraph:: +on_enter_label(int collector_index) { + if (collector_index != _highlighted_index) { + _highlighted_index = collector_index; + force_redraw(); + } +} + +/** + * Called when the user's mouse cursor leaves a label. + */ +void WinStatsGraph:: +on_leave_label(int collector_index) { + if (collector_index == _highlighted_index && collector_index != -1) { + _highlighted_index = -1; + force_redraw(); + } +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsGraph:: +get_label_tooltip(int collector_index) const { + return std::string(); +} + +/** + * Returns the window handle of the surrounding window. + */ +HWND WinStatsGraph:: +get_window() { + return _window; } /** @@ -184,8 +220,8 @@ move_label_stack() { RECT rect; GetClientRect(_window, &rect); - rect.left += 8; - rect.right = _left_margin - 8; + rect.left += _pixel_scale * 2; + rect.right = _left_margin - _pixel_scale * 2; rect.bottom -= _bottom_margin; _label_stack.set_pos(rect.left, rect.top, @@ -197,22 +233,27 @@ move_label_stack() { * Returns a brush suitable for drawing in the indicated collector's color. */ HBRUSH WinStatsGraph:: -get_collector_brush(int collector_index) { +get_collector_brush(int collector_index, bool highlight) { Brushes::iterator bi; bi = _brushes.find(collector_index); if (bi != _brushes.end()) { - return (*bi).second; + return highlight ? (*bi).second.second : (*bi).second.first; } // Ask the monitor what color this guy should be. LRGBColor rgb = _monitor->get_collector_color(collector_index); - int r = (int)(rgb[0] * 255.0f); - int g = (int)(rgb[1] * 255.0f); - int b = (int)(rgb[2] * 255.0f); + int r = (int)encode_sRGB_uchar(rgb[0]); + int g = (int)encode_sRGB_uchar(rgb[1]); + int b = (int)encode_sRGB_uchar(rgb[2]); HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); - _brushes[collector_index] = brush; - return brush; + int hr = (int)encode_sRGB_uchar(rgb[0] * 0.75f); + int hg = (int)encode_sRGB_uchar(rgb[1] * 0.75f); + int hb = (int)encode_sRGB_uchar(rgb[2] * 0.75f); + HBRUSH hbrush = CreateSolidBrush(RGB(hr, hg, hb)); + + _brushes[collector_index] = std::make_pair(brush, hbrush); + return highlight ? hbrush : brush; } /** @@ -226,6 +267,20 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { close(); break; + case WM_GETMINMAXINFO: + { + WINDOWINFO winfo; + GetWindowInfo(hwnd, &winfo); + MINMAXINFO &minmax = *(MINMAXINFO *)lparam; + minmax.ptMinTrackSize.x = (winfo.rcClient.left - winfo.rcWindow.left) + + (winfo.rcWindow.right - winfo.rcClient.right) + + (_right_margin + _left_margin); + minmax.ptMinTrackSize.y = (winfo.rcClient.top - winfo.rcWindow.top) + + (winfo.rcWindow.bottom - winfo.rcClient.bottom) + + (_bottom_margin + _top_margin); + return 0; + } + case WM_SIZE: move_label_stack(); InvalidateRect(hwnd, nullptr, TRUE); @@ -319,8 +374,6 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { rect.bottom -= _bottom_margin; if (rect.right > rect.left && rect.bottom > rect.top) { - DrawEdge(hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); - int graph_xsize = rect.right - rect.left; int graph_ysize = rect.bottom - rect.top; if (_bitmap_dc == 0 || @@ -339,6 +392,21 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { return 0; } + case WM_DRAWITEM: + if (wparam == IDC_GRAPH) { + const DRAWITEMSTRUCT &dis = *(DRAWITEMSTRUCT *)lparam; + + // Repaint the graph by copying the backing pixmap in. + BitBlt(dis.hDC, 0, 0, + _bitmap_xsize, _bitmap_ysize, + _bitmap_dc, 0, 0, + SRCCOPY); + + additional_graph_window_paint(dis.hDC); + return 0; + } + break; + default: break; } @@ -357,6 +425,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { force_redraw(); break; + case WM_NCHITTEST: + // Necessary for mouse events to work; default returns HTTRANSPARENT + return HTCLIENT; + case WM_LBUTTONDOWN: // Vector any uncaught WM_LBUTTONDOWN into the main window, so we can drag // margins, etc. @@ -372,28 +444,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ReleaseCapture(); break; - case WM_PAINT: - { - // Repaint the graph by copying the backing pixmap in. - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - - BitBlt(hdc, 0, 0, - _bitmap_xsize, _bitmap_ysize, - _bitmap_dc, 0, 0, - SRCCOPY); - - additional_graph_window_paint(hdc); - - EndPaint(hwnd, &ps); - return 0; - } - default: break; } - return DefWindowProc(hwnd, msg, wparam, lparam); + return DefSubclassProc(hwnd, msg, wparam, lparam); } /** @@ -506,61 +561,29 @@ create_graph_window() { } HINSTANCE application = GetModuleHandle(nullptr); - register_graph_window_class(application); - std::string window_title = "graph"; - DWORD window_style = WS_CHILD | WS_CLIPSIBLINGS; + DWORD window_style = WS_CHILD | WS_CLIPSIBLINGS | + SS_SUNKEN | SS_OWNERDRAW; _graph_window = - CreateWindow(_graph_window_class_name, window_title.c_str(), window_style, - 0, 0, 0, 0, - _window, nullptr, application, 0); + CreateWindow(WC_STATIC, "", window_style, 0, 0, 0, 0, + _window, (HMENU)IDC_GRAPH, application, 0); if (!_graph_window) { nout << "Could not create graph window!\n"; exit(1); } - SetWindowLongPtr(_graph_window, 0, (LONG_PTR)this); -} + EnableWindow(_graph_window, TRUE); -/** - * Registers the window class for the stripChart window, if it has not already - * been registered. - */ -void WinStatsGraph:: -register_graph_window_class(HINSTANCE application) { - if (_graph_window_class_registered) { - return; - } - - WNDCLASS wc; - - ZeroMemory(&wc, sizeof(WNDCLASS)); - wc.style = CS_DBLCLKS; - wc.lpfnWndProc = (WNDPROC)static_graph_window_proc; - wc.hInstance = application; - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = nullptr; - wc.lpszMenuName = nullptr; - wc.lpszClassName = _graph_window_class_name; - - // Reserve space to associate the this pointer with the window. - wc.cbWndExtra = sizeof(WinStatsGraph *); - - if (!RegisterClass(&wc)) { - nout << "Could not register graph window class!\n"; - exit(1); - } - - _graph_window_class_registered = true; + SetWindowSubclass(_graph_window, &static_graph_subclass_proc, 1234, (DWORD_PTR)this); } /** * */ -LONG WINAPI WinStatsGraph:: -static_graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - WinStatsGraph *self = (WinStatsGraph *)GetWindowLongPtr(hwnd, 0); +LRESULT WINAPI WinStatsGraph:: +static_graph_subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR subclass, DWORD_PTR ref_data) { + WinStatsGraph *self = (WinStatsGraph *)ref_data; if (self != nullptr && self->_graph_window == hwnd) { return self->graph_window_proc(hwnd, msg, wparam, lparam); } else { diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h index 82fb04f71d..388a78323f 100644 --- a/pandatool/src/win-stats/winStatsGraph.h +++ b/pandatool/src/win-stats/winStatsGraph.h @@ -48,7 +48,7 @@ public: virtual void new_collector(int collector_index); virtual void new_data(int thread_index, int frame_number); - virtual void force_redraw(); + virtual void force_redraw()=0; virtual void changed_graph_size(int graph_xsize, int graph_ysize); virtual void set_time_units(int unit_mask); @@ -56,7 +56,12 @@ public: void set_pause(bool pause); void user_guide_bars_changed(); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); + virtual void on_enter_label(int collector_index); + virtual void on_leave_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; + + HWND get_window(); protected: void close(); @@ -64,7 +69,7 @@ protected: void setup_label_stack(); void move_label_stack(); - HBRUSH get_collector_brush(int collector_index); + HBRUSH get_collector_brush(int collector_index, bool highlight = false); LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); @@ -80,7 +85,7 @@ protected: protected: // Table of brushes for our various collectors. - typedef pmap Brushes; + typedef pmap > Brushes; Brushes _brushes; WinStatsMonitor *_monitor; @@ -98,6 +103,7 @@ protected: int _bitmap_xsize, _bitmap_ysize; int _left_margin, _right_margin; int _top_margin, _bottom_margin; + int _pixel_scale; COLORREF _dark_color; COLORREF _light_color; @@ -112,18 +118,16 @@ protected: double _drag_scale_start; int _drag_guide_bar; + int _highlighted_index = -1; + bool _pause; private: void setup_bitmap(int xsize, int ysize); void release_bitmap(); void create_graph_window(); - static void register_graph_window_class(HINSTANCE application); - static LONG WINAPI static_graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - - static bool _graph_window_class_registered; - static const char * const _graph_window_class_name; + static LRESULT WINAPI static_graph_subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR subclass, DWORD_PTR ref_data); protected: static DWORD graph_window_style; diff --git a/pandatool/src/win-stats/winStatsLabel.I b/pandatool/src/win-stats/winStatsLabel.I new file mode 100644 index 0000000000..a7cabe90b9 --- /dev/null +++ b/pandatool/src/win-stats/winStatsLabel.I @@ -0,0 +1,68 @@ +/** + * 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 winStatsLabel.I + * @author rdb + * @date 2022-01-29 + */ + +/** + * Returns the x position of the label on its parent. + */ +INLINE int WinStatsLabel:: +get_x() const { + return _x; +} + +/** + * Returns the y position of the label on its parent. + */ +INLINE int WinStatsLabel:: +get_y() const { + return _y; +} + +/** + * Returns the width of the label as we requested it. + */ +INLINE int WinStatsLabel:: +get_width() const { + return _width; +} + +/** + * Returns the height of the label as we requested it. + */ +INLINE int WinStatsLabel:: +get_height() const { + return _height; +} + +/** + * Returns the width the label would really prefer to be. + */ +INLINE int WinStatsLabel:: +get_ideal_width() const { + return _ideal_width; +} + +/** + * Returns the collector this label represents. + */ +INLINE int WinStatsLabel:: +get_collector_index() const { + return _collector_index; +} + +/** + * Returns true if the visual highlight for this label is enabled. + */ +INLINE bool WinStatsLabel:: +get_highlight() const { + return _highlight; +} diff --git a/pandatool/src/win-stats/winStatsLabel.cxx b/pandatool/src/win-stats/winStatsLabel.cxx index 5cd744c501..00021e7376 100644 --- a/pandatool/src/win-stats/winStatsLabel.cxx +++ b/pandatool/src/win-stats/winStatsLabel.cxx @@ -14,6 +14,9 @@ #include "winStatsLabel.h" #include "winStatsMonitor.h" #include "winStatsGraph.h" +#include "convert_srgb.h" + +#include int WinStatsLabel::_left_margin = 2; int WinStatsLabel::_right_margin = 2; @@ -28,38 +31,45 @@ const char * const WinStatsLabel::_window_class_name = "label"; */ WinStatsLabel:: WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph, - int thread_index, int collector_index, bool use_fullname) : + int thread_index, int collector_index, bool use_fullname, + bool align_right) : _monitor(monitor), _graph(graph), _thread_index(thread_index), - _collector_index(collector_index) + _collector_index(collector_index), + _align_right(align_right), + _window(0), + _tooltip_window(0) { - _window = 0; - if (use_fullname) { - _text = _monitor->get_client_data()->get_collector_fullname(_collector_index); - } else { - _text = _monitor->get_client_data()->get_collector_name(_collector_index); - } + update_text(use_fullname); LRGBColor rgb = _monitor->get_collector_color(_collector_index); - int r = (int)(rgb[0] * 255.0f); - int g = (int)(rgb[1] * 255.0f); - int b = (int)(rgb[2] * 255.0f); - _bg_color = RGB(r, g, b); + int r = (int)encode_sRGB_uchar(rgb[0]); + int g = (int)encode_sRGB_uchar(rgb[1]); + int b = (int)encode_sRGB_uchar(rgb[2]); _bg_brush = CreateSolidBrush(RGB(r, g, b)); + // Calculate the color when it is highlighted. + int hr = (int)encode_sRGB_uchar(rgb[0] * 0.75f); + int hg = (int)encode_sRGB_uchar(rgb[1] * 0.75f); + int hb = (int)encode_sRGB_uchar(rgb[2] * 0.75f); + _highlight_bg_brush = CreateSolidBrush(RGB(hr, hg, hb)); + // Should our foreground be black or white? double bright = - rgb[0] * 0.299 + - rgb[1] * 0.587 + - rgb[2] * 0.114; + rgb[0] * 0.2126 + + rgb[1] * 0.7152 + + rgb[2] * 0.0722; if (bright >= 0.5) { _fg_color = RGB(0, 0, 0); - _highlight_brush = (HBRUSH)GetStockObject(BLACK_BRUSH); } else { _fg_color = RGB(255, 255, 255); - _highlight_brush = (HBRUSH)GetStockObject(WHITE_BRUSH); + } + if (bright >= 0.5 * 0.75) { + _highlight_fg_color = RGB(0, 0, 0); + } else { + _highlight_fg_color = RGB(255, 255, 255); } _x = 0; @@ -77,6 +87,10 @@ WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph, WinStatsLabel:: ~WinStatsLabel() { if (_window) { + if (_tooltip_window) { + DestroyWindow(_tooltip_window); + _tooltip_window = 0; + } DestroyWindow(_window); _window = 0; } @@ -96,7 +110,7 @@ setup(HWND parent_window) { create_window(parent_window); HDC hdc = GetDC(_window); - HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); + HFONT hfnt = _monitor->get_font(); SelectObject(hdc, hfnt); SIZE size; @@ -113,59 +127,13 @@ setup(HWND parent_window) { */ void WinStatsLabel:: set_pos(int x, int y, int width) { - _x = x; - _y = y; - _width = width; - SetWindowPos(_window, 0, x, y - _height, _width, _height, - SWP_NOZORDER | SWP_SHOWWINDOW); -} - -/** - * Returns the x position of the label on its parent. - */ -int WinStatsLabel:: -get_x() const { - return _x; -} - -/** - * Returns the y position of the label on its parent. - */ -int WinStatsLabel:: -get_y() const { - return _y; -} - -/** - * Returns the width of the label as we requested it. - */ -int WinStatsLabel:: -get_width() const { - return _width; -} - -/** - * Returns the height of the label as we requested it. - */ -int WinStatsLabel:: -get_height() const { - return _height; -} - -/** - * Returns the width the label would really prefer to be. - */ -int WinStatsLabel:: -get_ideal_width() const { - return _ideal_width; -} - -/** - * Returns the collector this label represents. - */ -int WinStatsLabel:: -get_collector_index() const { - return _collector_index; + if (x != _x || y != _y || width != _width) { + _x = x; + _y = y; + _width = width; + SetWindowPos(_window, 0, x, y - _height, _width, _height, + SWP_NOZORDER | SWP_SHOWWINDOW); + } } /** @@ -180,11 +148,29 @@ set_highlight(bool highlight) { } /** - * Returns true if the visual highlight for this label is enabled. + * Set to true if the full name of the collector should be shown. */ -bool WinStatsLabel:: -get_highlight() const { - return _highlight; +void WinStatsLabel:: +update_text(bool use_fullname) { + const PStatClientData *client_data = _monitor->get_client_data(); + _tooltip_text = client_data->get_collector_fullname(_collector_index); + if (use_fullname) { + _text = _tooltip_text; + } else { + _text = client_data->get_collector_name(_collector_index); + } + + // Recalculate the dimensions. + if (_window) { + HDC hdc = GetDC(_window); + HFONT hfnt = _monitor->get_font(); + SelectObject(hdc, hfnt); + + SIZE size; + GetTextExtentPoint32(hdc, _text.data(), _text.length(), &size); + _height = size.cy + _top_margin + _bottom_margin; + _ideal_width = size.cx + _left_margin + _right_margin; + } } /** @@ -220,6 +206,25 @@ create_window(HWND parent_window) { } SetWindowLongPtr(_window, 0, (LONG_PTR)this); + + // Create the tooltip window. This will cause a TTN_GETDISPINFO message to + // be sent to the window to acquire the tooltip text. + _tooltip_window = CreateWindow(TOOLTIPS_CLASS, nullptr, + WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + _window, nullptr, + application, nullptr); + + if (_tooltip_window != 0) { + TOOLINFO info = { 0 }; + info.cbSize = sizeof(info); + info.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + info.hwnd = _window; + info.uId = (UINT_PTR)_window; + info.lpszText = LPSTR_TEXTCALLBACK; + SendMessage(_tooltip_window, TTM_ADDTOOL, 0, (LPARAM)&info); + } } /** @@ -274,13 +279,16 @@ LONG WinStatsLabel:: window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_LBUTTONDBLCLK: - _graph->clicked_label(_collector_index); + _graph->on_click_label(_collector_index); return 0; case WM_MOUSEMOVE: { // When the mouse enters the label area, highlight the label. - set_mouse_within(true); + if (!_mouse_within) { + set_mouse_within(true); + _graph->on_enter_label(_collector_index); + } // Now we want to get a WM_MOUSELEAVE when the mouse leaves the label. TRACKMOUSEEVENT tme = { @@ -294,7 +302,10 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_MOUSELEAVE: - set_mouse_within(false); + if (_mouse_within) { + set_mouse_within(false); + _graph->on_leave_label(_collector_index); + } break; case WM_PAINT: @@ -303,26 +314,33 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { HDC hdc = BeginPaint(hwnd, &ps); RECT rect = { 0, 0, _width, _height }; - FillRect(hdc, &rect, _bg_brush); + FillRect(hdc, &rect, (_highlight || _mouse_within) ? _highlight_bg_brush : _bg_brush); - if (_highlight || _mouse_within) { - FrameRect(hdc, &rect, _highlight_brush); - } - - HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); + HFONT hfnt = _monitor->get_font(); SelectObject(hdc, hfnt); - SetTextAlign(hdc, TA_RIGHT | TA_TOP); + SetTextAlign(hdc, (_align_right ? TA_RIGHT : TA_LEFT) | TA_TOP); - SetBkColor(hdc, _bg_color); - SetBkMode(hdc, OPAQUE); - SetTextColor(hdc, _fg_color); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, (_highlight || _mouse_within) ? _highlight_fg_color : _fg_color); - TextOut(hdc, _width - _right_margin, _top_margin, - _text.data(), _text.length()); + TextOut(hdc, _align_right ? (_width - _right_margin) : _left_margin, + _top_margin, _text.data(), _text.length()); EndPaint(hwnd, &ps); return 0; } + case WM_NOTIFY: + switch (((LPNMHDR)lparam)->code) { + case TTN_GETDISPINFO: + { + NMTTDISPINFO &info = *(NMTTDISPINFO *)lparam; + _tooltip_text = _graph->get_label_tooltip(_collector_index); + info.lpszText = (char *)_tooltip_text.c_str(); + } + return 0; + } + break; + default: break; } diff --git a/pandatool/src/win-stats/winStatsLabel.h b/pandatool/src/win-stats/winStatsLabel.h index 5642cdb8d0..a970d3e13e 100644 --- a/pandatool/src/win-stats/winStatsLabel.h +++ b/pandatool/src/win-stats/winStatsLabel.h @@ -32,22 +32,25 @@ class WinStatsGraph; class WinStatsLabel { public: WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph, - int thread_index, int collector_index, bool use_fullname); + int thread_index, int collector_index, bool use_fullname, + bool align_right = true); ~WinStatsLabel(); void setup(HWND parent_window); void set_pos(int x, int y, int width); - int get_x() const; - int get_y() const; - int get_width() const; - int get_height() const; - int get_ideal_width() const; + INLINE int get_x() const; + INLINE int get_y() const; + INLINE int get_width() const; + INLINE int get_height() const; + INLINE int get_ideal_width() const; - int get_collector_index() const; + INLINE int get_collector_index() const; void set_highlight(bool highlight); - bool get_highlight() const; + INLINE bool get_highlight() const; + + void update_text(bool use_fullname); private: void set_mouse_within(bool mouse_within); @@ -63,11 +66,13 @@ private: int _thread_index; int _collector_index; std::string _text; + std::string _tooltip_text; HWND _window; - COLORREF _bg_color; + HWND _tooltip_window; COLORREF _fg_color; + COLORREF _highlight_fg_color; HBRUSH _bg_brush; - HBRUSH _highlight_brush; + HBRUSH _highlight_bg_brush; int _x; int _y; @@ -76,6 +81,7 @@ private: int _ideal_width; bool _highlight; bool _mouse_within; + bool _align_right; static int _left_margin, _right_margin; static int _top_margin, _bottom_margin; @@ -84,4 +90,6 @@ private: static const char * const _window_class_name; }; +#include "winStatsLabel.I" + #endif diff --git a/pandatool/src/win-stats/winStatsLabelStack.cxx b/pandatool/src/win-stats/winStatsLabelStack.cxx index af3bcccd00..e6633f025b 100644 --- a/pandatool/src/win-stats/winStatsLabelStack.cxx +++ b/pandatool/src/win-stats/winStatsLabelStack.cxx @@ -200,6 +200,90 @@ add_label(WinStatsMonitor *monitor, WinStatsGraph *graph, return label_index; } +/** + * Replaces the labels with the given collector indices. + */ +void WinStatsLabelStack:: +replace_labels(WinStatsMonitor *monitor, WinStatsGraph *graph, + int thread_index, const vector_int &collector_indices, + bool use_fullname) { + + _ideal_width = 0; + + // First skip the part of the stack that hasn't changed. + size_t li = 0; + size_t ci = 0; + while (ci < collector_indices.size() && li < _labels.size()) { + WinStatsLabel *label = _labels[li]; + if (collector_indices[ci] != label->get_collector_index()) { + // Mismatch. + break; + } + _ideal_width = std::max(_ideal_width, label->get_ideal_width()); + ++ci; + ++li; + } + + if (ci == collector_indices.size()) { + if (ci == _labels.size()) { + // Perfect, nothing changed. + return; + } + + // Simple case, just delete the rest. + while (li < _labels.size()) { + delete _labels[li++]; + } + _labels.resize(ci); + return; + } + + int yp = _height; + if (li > 0) { + WinStatsLabel *label = _labels[li - 1]; + yp = label->get_y() - label->get_height(); + } + + // Make a map of remaining labels. + std::map label_map; + for (size_t li2 = li; li2 < _labels.size(); ++li2) { + WinStatsLabel *label = _labels[li2]; + label_map[label->get_collector_index()] = label; + } + + _labels.resize(collector_indices.size()); + + while (ci < collector_indices.size()) { + int collector_index = collector_indices[ci++]; + + WinStatsLabel *label; + auto it = label_map.find(collector_index); + if (it == label_map.end()) { + // It's not in the map. Create a new label. + label = new WinStatsLabel(monitor, graph, thread_index, collector_index, use_fullname); + if (_window) { + label->setup(_window); + } + } else { + // Erase it from the map, so that it's not deleted. + label = it->second; + label_map.erase(it); + } + if (_window) { + label->set_pos(0, yp, _width); + } + _ideal_width = std::max(_ideal_width, label->get_ideal_width()); + yp -= label->get_height(); + + _labels[li++] = label; + } + + // Anything that's remaining in the label map should be deleted. + for (auto it = label_map.begin(); it != label_map.end(); ++it) { + delete it->second; + } +} + /** * Returns the number of labels in the stack. */ @@ -225,7 +309,6 @@ highlight_label(int collector_index) { } } - /** * Creates the window for this stack. */ @@ -306,7 +389,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { HDC hdc = BeginPaint(hwnd, &ps); RECT rect = { 0, 0, _width, _height }; - FillRect(hdc, &rect, (HBRUSH)COLOR_BACKGROUND); + FillRect(hdc, &rect, (HBRUSH)COLOR_WINDOW); EndPaint(hwnd, &ps); return 0; } diff --git a/pandatool/src/win-stats/winStatsLabelStack.h b/pandatool/src/win-stats/winStatsLabelStack.h index e7fd1da41f..48b9424754 100644 --- a/pandatool/src/win-stats/winStatsLabelStack.h +++ b/pandatool/src/win-stats/winStatsLabelStack.h @@ -16,6 +16,7 @@ #include "pandatoolbase.h" #include "pvector.h" +#include "vector_int.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 @@ -51,6 +52,9 @@ public: void clear_labels(); int add_label(WinStatsMonitor *monitor, WinStatsGraph *graph, int thread_index, int collector_index, bool use_fullname); + void replace_labels(WinStatsMonitor *monitor, WinStatsGraph *graph, + int thread_index, const vector_int &collector_indices, + bool use_fullname); int get_num_labels() const; void highlight_label(int collector_index); diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx index 50c66362d1..7c877c28fd 100644 --- a/pandatool/src/win-stats/winStatsMonitor.cxx +++ b/pandatool/src/win-stats/winStatsMonitor.cxx @@ -15,6 +15,7 @@ #include "winStatsServer.h" #include "winStatsStripChart.h" #include "winStatsPianoRoll.h" +#include "winStatsFlameGraph.h" #include "winStatsChartMenu.h" #include "winStatsMenuId.h" #include "pStatGraph.h" @@ -37,6 +38,25 @@ WinStatsMonitor(WinStatsServer *server) : PStatMonitor(server) { _time_units = 0; _scroll_speed = 0.0; _pause = false; + + // Create the fonts used for rendering the UI. + NONCLIENTMETRICS metrics = {0}; + metrics.cbSize = sizeof(NONCLIENTMETRICS); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) { + _font = CreateFontIndirect(&metrics.lfMenuFont); + } else { + _font = (HFONT)GetStockObject(ANSI_VAR_FONT); + } + + HDC dc = GetDC(nullptr); + _pixel_scale = 0; + if (dc) { + _pixel_scale = GetDeviceCaps(dc, LOGPIXELSX) / (96 / 4); + } + if (_pixel_scale <= 0) { + _pixel_scale = 4; + } + ReleaseDC(nullptr, dc); } /** @@ -252,6 +272,22 @@ get_window() const { return _window; } +/** + * Returns the font that should be used for rendering text. + */ +HFONT WinStatsMonitor:: +get_font() const { + return _font; +} + +/** + * Returns the system DPI scaling as a fraction where 4 = no scaling. + */ +int WinStatsMonitor:: +get_pixel_scale() const { + return _pixel_scale; +} + /** * Opens a new strip chart showing the indicated data. */ @@ -279,6 +315,19 @@ open_piano_roll(int thread_index) { graph->set_pause(_pause); } +/** + * Opens a new flame graph showing the indicated data. + */ +void WinStatsMonitor:: +open_flame_graph(int thread_index) { + WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index); + add_graph(graph); + + graph->set_time_units(_time_units); + graph->set_scroll_speed(_scroll_speed); + graph->set_pause(_pause); +} + /** * Returns the MenuDef properties associated with the indicated menu ID. This * specifies what we expect to do when the given menu has been selected. @@ -604,7 +653,7 @@ register_window_class(HINSTANCE application) { wc.lpfnWndProc = (WNDPROC)static_window_proc; wc.hInstance = application; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = nullptr; wc.lpszClassName = _window_class_name; @@ -701,9 +750,13 @@ handle_menu_command(int menu_id) { default: if (menu_id >= MI_new_chart) { const MenuDef &menu_def = lookup_menu(menu_id); - if (menu_def._collector_index < 0) { + if (menu_def._collector_index == -2) { + open_flame_graph(menu_def._thread_index); + } + else if (menu_def._collector_index < 0) { open_piano_roll(menu_def._thread_index); - } else { + } + else { open_strip_chart(menu_def._thread_index, menu_def._collector_index, menu_def._show_level); } diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h index c70a71507e..9da5034a71 100644 --- a/pandatool/src/win-stats/winStatsMonitor.h +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -66,8 +66,12 @@ public: virtual void user_guide_bars_changed(); HWND get_window() const; + HFONT get_font() const; + int get_pixel_scale() const; + void open_strip_chart(int thread_index, int collector_index, bool show_level); void open_piano_roll(int thread_index); + void open_flame_graph(int thread_index); const MenuDef &lookup_menu(int menu_id) const; int get_menu_id(const MenuDef &menu_def); @@ -109,6 +113,9 @@ private: int _time_units; double _scroll_speed; bool _pause; + int _pixel_scale; + + HFONT _font; static bool _window_class_registered; static const char * const _window_class_name; diff --git a/pandatool/src/win-stats/winStatsPianoRoll.cxx b/pandatool/src/win-stats/winStatsPianoRoll.cxx index 859de11b68..0b22831184 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.cxx +++ b/pandatool/src/win-stats/winStatsPianoRoll.cxx @@ -15,7 +15,7 @@ #include "winStatsMonitor.h" #include "numeric_types.h" -static const int default_piano_roll_width = 400; +static const int default_piano_roll_width = 600; static const int default_piano_roll_height = 200; bool WinStatsPianoRoll::_window_class_registered = false; @@ -27,14 +27,14 @@ const char * const WinStatsPianoRoll::_window_class_name = "piano"; WinStatsPianoRoll:: WinStatsPianoRoll(WinStatsMonitor *monitor, int thread_index) : PStatPianoRoll(monitor, thread_index, - default_piano_roll_width, - default_piano_roll_height), + monitor->get_pixel_scale() * default_piano_roll_width / 4, + monitor->get_pixel_scale() * default_piano_roll_height / 4), WinStatsGraph(monitor) { - _left_margin = 128; - _right_margin = 8; - _top_margin = 16; - _bottom_margin = 8; + _left_margin = _pixel_scale * 32; + _right_margin = _pixel_scale * 2; + _top_margin = _pixel_scale * 5; + _bottom_margin = _pixel_scale * 2; // Let's show the units on the guide bar labels. There's room. set_guide_bar_units(get_guide_bar_units() | GBU_show_units); @@ -103,7 +103,7 @@ set_time_units(int unit_mask) { * Called when the user single-clicks on a label. */ void WinStatsPianoRoll:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { if (collector_index >= 0) { WinStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false); } @@ -123,6 +123,15 @@ set_horizontal_scale(double time_width) { InvalidateRect(_window, &rect, TRUE); } +/** + * Calls update_guide_bars with parameters suitable to this kind of graph. + */ +void WinStatsPianoRoll:: +normal_guide_bars() { + // We want vaguely 100 pixels between guide bars. + update_guide_bars(get_xsize() / (_pixel_scale * 25), get_horizontal_scale()); +} + /** * Erases the chart area. */ @@ -144,6 +153,21 @@ begin_draw() { for (int i = 0; i < num_guide_bars; i++) { draw_guide_bar(_bitmap_dc, get_guide_bar(i)); } + + SelectObject(_bitmap_dc, GetStockObject(NULL_PEN)); +} + +/** + * Should be overridden by the user class. This hook will be called before + * drawing any one row of bars. These bars correspond to the collector whose + * index is get_row_collector(row), and in the color get_row_color(row). + */ +void WinStatsPianoRoll:: +begin_row(int row) { + int collector_index = get_label_collector(row); + HBRUSH brush = get_collector_brush(collector_index, _highlighted_index == collector_index); + SelectObject(_bitmap_dc, brush); + SelectObject(_bitmap_dc, GetStockObject(NULL_PEN)); } /** @@ -155,13 +179,7 @@ draw_bar(int row, int from_x, int to_x) { int y = _label_stack.get_label_y(row) - _graph_top; int height = _label_stack.get_label_height(row); - RECT rect = { - from_x, y - height + 2, - to_x, y - 2, - }; - int collector_index = get_label_collector(row); - HBRUSH brush = get_collector_brush(collector_index); - FillRect(_bitmap_dc, &rect, brush); + RoundRect(_bitmap_dc, from_x, y - height + 2, to_x, y - 2, _pixel_scale, _pixel_scale); } } @@ -233,7 +251,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // When the mouse is over a color bar, highlight it. int16_t x = LOWORD(lparam); int16_t y = HIWORD(lparam); - _label_stack.highlight_label(get_collector_under_pixel(x, y)); + + int collector_index = get_collector_under_pixel(x, y); + _label_stack.highlight_label(collector_index); + on_enter_label(collector_index); // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph // window. @@ -244,10 +265,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 0 }; TrackMouseEvent(&tme); - - } else { + } + else { // If the mouse is in some drag mode, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); } if (_drag_mode == DM_scale) { @@ -278,6 +300,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case WM_MOUSELEAVE: // When the mouse leaves the graph, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); break; case WM_LBUTTONUP: @@ -305,7 +328,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // clicking on the corresponding label. int16_t x = LOWORD(lparam); int16_t y = HIWORD(lparam); - clicked_label(get_collector_under_pixel(x, y)); + on_click_label(get_collector_under_pixel(x, y)); return 0; } break; @@ -325,12 +348,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { void WinStatsPianoRoll:: additional_window_paint(HDC hdc) { // Draw in the labels for the guide bars. - HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); - SelectObject(hdc, hfnt); + SelectObject(hdc, WinStatsGraph::_monitor->get_font()); SetTextAlign(hdc, TA_LEFT | TA_BOTTOM); SetBkMode(hdc, TRANSPARENT); - int y = _top_margin; + int y = _top_margin - 2; int i; int num_guide_bars = get_num_guide_bars(); @@ -411,13 +433,8 @@ get_collector_under_pixel(int xpoint, int ypoint) { */ void WinStatsPianoRoll:: update_labels() { - _label_stack.clear_labels(); - for (int i = 0; i < get_num_labels(); i++) { - int label_index = - _label_stack.add_label(WinStatsGraph::_monitor, this, - _thread_index, - get_label_collector(i), true); - } + _label_stack.replace_labels(WinStatsGraph::_monitor, this, + _thread_index, _labels, true); _labels_changed = false; } @@ -551,7 +568,7 @@ register_window_class(HINSTANCE application) { wc.lpfnWndProc = (WNDPROC)static_window_proc; wc.hInstance = application; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = nullptr; wc.lpszClassName = _window_class_name; diff --git a/pandatool/src/win-stats/winStatsPianoRoll.h b/pandatool/src/win-stats/winStatsPianoRoll.h index b130c99b18..5a5a6f1f45 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.h +++ b/pandatool/src/win-stats/winStatsPianoRoll.h @@ -41,12 +41,14 @@ public: virtual void changed_graph_size(int graph_xsize, int graph_ysize); virtual void set_time_units(int unit_mask); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); void set_horizontal_scale(double time_width); protected: + virtual void normal_guide_bars(); void clear_region(); virtual void begin_draw(); + virtual void begin_row(int row); virtual void draw_bar(int row, int from_x, int to_x); virtual void end_draw(); virtual void idle(); diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx index 879b920673..a7cf195a63 100644 --- a/pandatool/src/win-stats/winStatsStripChart.cxx +++ b/pandatool/src/win-stats/winStatsStripChart.cxx @@ -16,17 +16,13 @@ #include "pStatCollectorDef.h" #include "numeric_types.h" +#include + using std::string; static const int default_strip_chart_width = 400; static const int default_strip_chart_height = 100; -// Surely we aren't expected to hardcode the size of a normal checkbox. But -// Windows seems to require this data to be passed to CreateWindow(), so what -// else can I do? -size_t WinStatsStripChart::_check_box_height = 13; -size_t WinStatsStripChart::_check_box_width = 13; - bool WinStatsStripChart::_window_class_registered = false; const char * const WinStatsStripChart::_window_class_name = "strip"; @@ -40,16 +36,16 @@ WinStatsStripChart(WinStatsMonitor *monitor, int thread_index, show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index), thread_index, collector_index, - default_strip_chart_width, - default_strip_chart_height), + monitor->get_pixel_scale() * default_strip_chart_width / 4, + monitor->get_pixel_scale() * default_strip_chart_height / 4), WinStatsGraph(monitor) { _brush_origin = 0; - _left_margin = 96; - _right_margin = 32; - _top_margin = 16; - _bottom_margin = 8; + _left_margin = _pixel_scale * 24; + _right_margin = _pixel_scale * 12; + _top_margin = _pixel_scale * 6; + _bottom_margin = _pixel_scale * 2; if (show_level) { // If it's a level-type graph, show the appropriate units. @@ -170,7 +166,7 @@ set_scroll_speed(double scroll_speed) { * Called when the user single-clicks on a label. */ void WinStatsStripChart:: -clicked_label(int collector_index) { +on_click_label(int collector_index) { if (collector_index < 0) { // Clicking on whitespace in the graph is the same as clicking on the top // label. @@ -197,6 +193,15 @@ clicked_label(int collector_index) { } } +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsStripChart:: +get_label_tooltip(int collector_index) const { + return PStatStripChart::get_label_tooltip(collector_index); +} + /** * Changes the value the height of the vertical axis represents. This may * force a redraw. @@ -218,11 +223,8 @@ void WinStatsStripChart:: update_labels() { PStatStripChart::update_labels(); - _label_stack.clear_labels(); - for (int i = 0; i < get_num_labels(); i++) { - _label_stack.add_label(WinStatsGraph::_monitor, this, _thread_index, - get_label_collector(i), false); - } + _label_stack.replace_labels(WinStatsGraph::_monitor, this, + _thread_index, _labels, false); _labels_changed = false; } @@ -273,7 +275,7 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) { for (fi = fdata.begin(); fi != fdata.end(); ++fi) { const ColorData &cd = (*fi); overall_time += cd._net_value; - HBRUSH brush = get_collector_brush(cd._collector_index); + HBRUSH brush = get_collector_brush(cd._collector_index, cd._collector_index == _highlighted_index); if (overall_time > get_vertical_scale()) { // Off the top. Go ahead and clamp it by hand, in case it's so far off @@ -391,7 +393,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // When the mouse is over a color bar, highlight it. int16_t x = LOWORD(lparam); int16_t y = HIWORD(lparam); - _label_stack.highlight_label(get_collector_under_pixel(x, y)); + + int collector_index = get_collector_under_pixel(x, y); + _label_stack.highlight_label(collector_index); + on_enter_label(collector_index); // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph // window. @@ -402,10 +407,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 0 }; TrackMouseEvent(&tme); - - } else { + } + else { // If the mouse is in some drag mode, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); } if (_drag_mode == DM_scale) { @@ -436,6 +442,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case WM_MOUSELEAVE: // When the mouse leaves the graph, stop highlighting. _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); break; case WM_LBUTTONUP: @@ -463,7 +470,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // clicking on the corresponding label. int16_t x = LOWORD(lparam); int16_t y = HIWORD(lparam); - clicked_label(get_collector_under_pixel(x, y)); + on_click_label(get_collector_under_pixel(x, y)); return 0; } break; @@ -483,14 +490,13 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { void WinStatsStripChart:: additional_window_paint(HDC hdc) { // Draw in the labels for the guide bars. - HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT); - SelectObject(hdc, hfnt); + SelectObject(hdc, WinStatsGraph::_monitor->get_font()); SetTextAlign(hdc, TA_LEFT | TA_TOP); SetBkMode(hdc, TRANSPARENT); RECT rect; GetClientRect(_window, &rect); - int x = rect.right - _right_margin + 2; + int x = rect.right - _right_margin + _pixel_scale; int last_y = -100; int i; @@ -511,15 +517,8 @@ additional_window_paint(HDC hdc) { // Now draw the "net value" label at the top. SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM); SetTextColor(hdc, RGB(0, 0, 0)); - TextOut(hdc, rect.right - _right_margin, _top_margin, + TextOut(hdc, rect.right - _right_margin - _pixel_scale, _top_margin - _pixel_scale / 2, _net_value_text.data(), _net_value_text.length()); - - // Also draw the "Smooth" label on the check box. This isn't part of the - // check box itself, because doing that doesn't use the right font! Surely - // this isn't the correct Windows(tm) way to do this sort of thing, but I - // don't know any better for now. - SetTextAlign(hdc, TA_LEFT | TA_BOTTOM); - TextOut(hdc, _left_margin + _check_box_width + 2, _top_margin, "Smooth", 6); } /** @@ -594,10 +593,13 @@ void WinStatsStripChart:: move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) { WinStatsGraph::move_graph_window(graph_left, graph_top, graph_xsize, graph_ysize); if (_smooth_check_box != 0) { + SIZE size; + SendMessage(_smooth_check_box, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + SetWindowPos(_smooth_check_box, 0, - _left_margin, _top_margin - _check_box_height - 1, - 0, 0, - SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); + _left_margin, _top_margin - size.cy - _pixel_scale / 2, + size.cx, size.cy, + SWP_NOZORDER | SWP_SHOWWINDOW); InvalidateRect(_smooth_check_box, nullptr, TRUE); } } @@ -715,10 +717,15 @@ create_window() { setup_label_stack(); _smooth_check_box = - CreateWindow("BUTTON", "", - WS_CHILD | BS_AUTOCHECKBOX, - 0, 0, _check_box_width, _check_box_height, + CreateWindow(WC_BUTTON, "Smooth", WS_CHILD | BS_AUTOCHECKBOX, + 0, 0, 0, 0, _window, nullptr, application, 0); + SendMessage(_smooth_check_box, WM_SETFONT, + (WPARAM)WinStatsGraph::_monitor->get_font(), TRUE); + + if (get_average_mode()) { + SendMessage(_smooth_check_box, BM_SETCHECK, BST_CHECKED, 0); + } // Ensure that the window is on top of the stack. SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0, @@ -742,7 +749,7 @@ register_window_class(HINSTANCE application) { wc.lpfnWndProc = (WNDPROC)static_window_proc; wc.hInstance = application; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = nullptr; wc.lpszClassName = _window_class_name; diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h index 9e6f91667f..dcff22a76e 100644 --- a/pandatool/src/win-stats/winStatsStripChart.h +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -43,7 +43,8 @@ public: virtual void set_time_units(int unit_mask); virtual void set_scroll_speed(double scroll_speed); - virtual void clicked_label(int collector_index); + virtual void on_click_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; void set_vertical_scale(double value_height); protected: @@ -79,7 +80,6 @@ private: std::string _net_value_text; HWND _smooth_check_box; - static size_t _check_box_height, _check_box_width; static bool _window_class_registered; static const char * const _window_class_name; diff --git a/pandatool/src/win-stats/winstats_composite1.cxx b/pandatool/src/win-stats/winstats_composite1.cxx index 3eac02e787..cc90367573 100644 --- a/pandatool/src/win-stats/winstats_composite1.cxx +++ b/pandatool/src/win-stats/winstats_composite1.cxx @@ -1,5 +1,6 @@ #include "winStats.cxx" #include "winStatsChartMenu.cxx" +#include "winStatsFlameGraph.cxx" #include "winStatsGraph.cxx" #include "winStatsLabel.cxx" #include "winStatsLabelStack.cxx" From 39d69f13de340ebe493c391e0662f4b2ae274b18 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Feb 2022 20:51:53 +0100 Subject: [PATCH 016/166] dtoolbase: Change DeletedBufferChain to use new C++11-style atomics --- dtool/src/dtoolbase/deletedBufferChain.I | 2 +- dtool/src/dtoolbase/deletedBufferChain.cxx | 22 ++++++++++++---------- dtool/src/dtoolbase/deletedBufferChain.h | 5 +++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/dtool/src/dtoolbase/deletedBufferChain.I b/dtool/src/dtoolbase/deletedBufferChain.I index 9eb17bd4fb..ac5243de76 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.I +++ b/dtool/src/dtoolbase/deletedBufferChain.I @@ -27,7 +27,7 @@ validate(void *ptr) { #if defined(USE_DELETEDCHAINFLAG) && defined(USE_DELETED_CHAIN) const ObjectNode *obj = buffer_to_node(ptr); - return AtomicAdjust::get(obj->_flag) == DCF_alive; + return obj->_flag.load(std::memory_order_relaxed) == DCF_alive; #else return true; #endif // USE_DELETEDCHAINFLAG diff --git a/dtool/src/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx index d009510ad3..d38b833ea1 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.cxx +++ b/dtool/src/dtoolbase/deletedBufferChain.cxx @@ -50,8 +50,8 @@ allocate(size_t size, TypeHandle type_handle) { _lock.unlock(); #ifdef USE_DELETEDCHAINFLAG - assert(obj->_flag == (AtomicAdjust::Integer)DCF_deleted); - obj->_flag = DCF_alive; + DeletedChainFlag orig_flag = obj->_flag.exchange(DCF_alive, std::memory_order_relaxed); + assert(orig_flag == DCF_deleted); #endif // USE_DELETEDCHAINFLAG void *ptr = node_to_buffer(obj); @@ -75,7 +75,7 @@ allocate(size_t size, TypeHandle type_handle) { obj = (ObjectNode *)(aligned - flag_reserved_bytes); #ifdef USE_DELETEDCHAINFLAG - obj->_flag = DCF_alive; + obj->_flag.store(DCF_alive, std::memory_order_relaxed); #endif // USE_DELETEDCHAINFLAG void *ptr = node_to_buffer(obj); @@ -116,14 +116,16 @@ deallocate(void *ptr, TypeHandle type_handle) { ObjectNode *obj = buffer_to_node(ptr); #ifdef USE_DELETEDCHAINFLAG - AtomicAdjust::Integer orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_alive, DCF_deleted); + DeletedChainFlag orig_flag = DCF_alive; + if (UNLIKELY(!obj->_flag.compare_exchange_strong(orig_flag, DCF_deleted, + std::memory_order_relaxed))) { + // If this assertion is triggered, you double-deleted an object. + assert(orig_flag != DCF_deleted); - // If this assertion is triggered, you double-deleted an object. - assert(orig_flag != (AtomicAdjust::Integer)DCF_deleted); - - // If this assertion is triggered, you tried to delete an object that was - // never allocated, or you have heap corruption. - assert(orig_flag == (AtomicAdjust::Integer)DCF_alive); + // If this assertion is triggered, you tried to delete an object that was + // never allocated, or you have heap corruption. + assert(orig_flag == DCF_alive); + } #endif // USE_DELETEDCHAINFLAG _lock.lock(); diff --git a/dtool/src/dtoolbase/deletedBufferChain.h b/dtool/src/dtoolbase/deletedBufferChain.h index 10ac5847fd..3bfd8cc071 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.h +++ b/dtool/src/dtoolbase/deletedBufferChain.h @@ -20,6 +20,7 @@ #include "atomicAdjust.h" #include "numeric_types.h" #include "typeHandle.h" +#include "patomic.h" #include // Though it's tempting, it doesn't seem to be possible to implement @@ -37,7 +38,7 @@ #endif // NDEBUG #ifdef USE_DELETEDCHAINFLAG -enum DeletedChainFlag { +enum DeletedChainFlag : unsigned int { DCF_deleted = 0xfeedba0f, DCF_alive = 0x12487654, }; @@ -73,7 +74,7 @@ private: // In development mode, we piggyback this extra data. This is maintained // out-of-band from the actual pointer returned, so we can safely use this // flag to indicate the difference between allocated and freed pointers. - TVOLATILE AtomicAdjust::Integer _flag; + patomic _flag; #endif // This pointer sits within the buffer, in the same space referenced by From 46a1ad3544ff750ed8f8d8acb8410b1d95b13046 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Feb 2022 22:50:59 +0100 Subject: [PATCH 017/166] pipeline: Improve performance of Thread::get_current_thread() substantially Speedup is realised by using thread-local variables. Note that on Windows we can't inline get_current_thread, but it's still faster this way than calling TlsGetValue. In theory the cache line alignment should help avoid false sharing but I have not profiled that extensively. --- panda/src/pipeline/thread.h | 3 +- panda/src/pipeline/threadPosixImpl.I | 22 +------- panda/src/pipeline/threadPosixImpl.cxx | 55 ++++++++++--------- panda/src/pipeline/threadPosixImpl.h | 7 +-- panda/src/pipeline/threadWin32Impl.I | 24 -------- panda/src/pipeline/threadWin32Impl.cxx | 76 +++++++++++++++----------- panda/src/pipeline/threadWin32Impl.h | 8 +-- 7 files changed, 81 insertions(+), 114 deletions(-) diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 159b93c9df..64f7622d99 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -42,7 +42,8 @@ class AsyncTask; * object will automatically be destructed if no other pointers are * referencing it. */ -class EXPCL_PANDA_PIPELINE Thread : public TypedReferenceCount, public Namable { +// Due to a GCC bug, we can't use alignas() together with an attribute. +class ALIGN_64BYTE EXPCL_PANDA_PIPELINE Thread : public TypedReferenceCount, public Namable { protected: Thread(const std::string &name, const std::string &sync_name); Thread(const Thread ©) = delete; diff --git a/panda/src/pipeline/threadPosixImpl.I b/panda/src/pipeline/threadPosixImpl.I index 22a8c90118..308dc3ede0 100644 --- a/panda/src/pipeline/threadPosixImpl.I +++ b/panda/src/pipeline/threadPosixImpl.I @@ -46,26 +46,8 @@ prepare_for_exit() { INLINE Thread *ThreadPosixImpl:: get_current_thread() { TAU_PROFILE("Thread *ThreadPosixImpl::get_current_thread()", " ", TAU_USER); - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - return (Thread *)pthread_getspecific(_pt_ptr_index); -} - -/** - * Associates the indicated Thread object with the currently-executing thread. - * You should not call this directly; use Thread::bind_thread() instead. - */ -INLINE void ThreadPosixImpl:: -bind_thread(Thread *thread) { - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - int result = pthread_setspecific(_pt_ptr_index, thread); - nassertv(result == 0); -#ifdef ANDROID - bind_java_thread(); -#endif + Thread *thread = _current_thread; + return (thread != nullptr) ? thread : init_current_thread(); } /** diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index f791d2be43..d46660194f 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -28,8 +28,8 @@ static JavaVM *java_vm = nullptr; #endif -pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0; -bool ThreadPosixImpl::_got_pt_ptr_index = false; +__thread Thread *ThreadPosixImpl::_current_thread = nullptr; +static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT; /** * @@ -80,10 +80,6 @@ start(ThreadPriority priority, bool joinable) { _status = S_start_called; _detached = false; - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - pthread_attr_t attr; pthread_attr_init(&attr); @@ -186,6 +182,21 @@ get_unique_id() const { return strm.str(); } +/** + * Associates the indicated Thread object with the currently-executing thread. + * You should not call this directly; use Thread::bind_thread() instead. + */ +void ThreadPosixImpl:: +bind_thread(Thread *thread) { + if (_current_thread == nullptr && thread == Thread::get_main_thread()) { + _main_thread_known.test_and_set(std::memory_order_relaxed); + } + _current_thread = thread; +#ifdef ANDROID + bind_java_thread(); +#endif +} + #ifdef ANDROID /** * Attaches the thread to the Java virtual machine. If this returns true, a @@ -247,8 +258,7 @@ root_func(void *data) { // TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER); ThreadPosixImpl *self = (ThreadPosixImpl *)data; - int result = pthread_setspecific(_pt_ptr_index, self->_parent_obj); - nassertr(result == 0, nullptr); + _current_thread = self->_parent_obj; { self->_mutex.lock(); @@ -302,27 +312,18 @@ root_func(void *data) { } /** - * Allocate a new index to store the Thread parent pointer as a piece of per- - * thread private data. + * Called by get_current_thread() if the current therad pointer is null; checks + * whether it might be the main thread. */ -void ThreadPosixImpl:: -init_pt_ptr_index() { - nassertv(!_got_pt_ptr_index); - - int result = pthread_key_create(&_pt_ptr_index, nullptr); - if (result != 0) { - thread_cat->error() - << "Unable to associate Thread pointers with threads.\n"; - return; +Thread *ThreadPosixImpl:: +init_current_thread() { + Thread *thread = _current_thread; + if (!_main_thread_known.test_and_set(std::memory_order_relaxed)) { + thread = Thread::get_main_thread(); + _current_thread = thread; } - - _got_pt_ptr_index = true; - - // Assume that we must be in the main thread, since this method must be - // called before the first thread is spawned. - Thread *main_thread_obj = Thread::get_main_thread(); - result = pthread_setspecific(_pt_ptr_index, main_thread_obj); - nassertv(result == 0); + nassertr(thread != nullptr, nullptr); + return thread; } #ifdef ANDROID diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index cdfd5eab8a..aa9a4e02ad 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -49,7 +49,7 @@ public: INLINE static void prepare_for_exit(); INLINE static Thread *get_current_thread(); - INLINE static void bind_thread(Thread *thread); + static void bind_thread(Thread *thread); INLINE static bool is_threading_supported(); INLINE static bool is_true_threads(); INLINE static bool is_simple_threads(); @@ -65,7 +65,7 @@ public: private: static void *root_func(void *data); - static void init_pt_ptr_index(); + static Thread *init_current_thread(); // There appears to be a name collision with the word "Status". enum PStatus { @@ -86,8 +86,7 @@ private: JNIEnv *_jni_env; #endif - static pthread_key_t _pt_ptr_index; - static bool _got_pt_ptr_index; + static __thread Thread *_current_thread; }; #include "threadPosixImpl.I" diff --git a/panda/src/pipeline/threadWin32Impl.I b/panda/src/pipeline/threadWin32Impl.I index a4c2b84a21..1ed6ed50ae 100644 --- a/panda/src/pipeline/threadWin32Impl.I +++ b/panda/src/pipeline/threadWin32Impl.I @@ -38,30 +38,6 @@ INLINE void ThreadWin32Impl:: prepare_for_exit() { } -/** - * - */ -INLINE Thread *ThreadWin32Impl:: -get_current_thread() { - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - return (Thread *)TlsGetValue(_pt_ptr_index); -} - -/** - * Associates the indicated Thread object with the currently-executing thread. - * You should not call this directly; use Thread::bind_thread() instead. - */ -INLINE void ThreadWin32Impl:: -bind_thread(Thread *thread) { - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - BOOL result = TlsSetValue(_pt_ptr_index, thread); - nassertv(result); -} - /** * */ diff --git a/panda/src/pipeline/threadWin32Impl.cxx b/panda/src/pipeline/threadWin32Impl.cxx index 4671787918..c151589a47 100644 --- a/panda/src/pipeline/threadWin32Impl.cxx +++ b/panda/src/pipeline/threadWin32Impl.cxx @@ -20,8 +20,28 @@ #include "pointerTo.h" #include "config_pipeline.h" -DWORD ThreadWin32Impl::_pt_ptr_index = 0; -bool ThreadWin32Impl::_got_pt_ptr_index = false; +static thread_local Thread *_current_thread = nullptr; +static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT; + +/** + * Called by get_current_thread() if the current thread pointer is null; checks + * whether it might be the main thread. + * Note that adding noinline speeds up this call *significantly*, don't remove! + */ +static __declspec(noinline) Thread * +init_current_thread() { + Thread *thread = _current_thread; + if (!_main_thread_known.test_and_set(std::memory_order_relaxed)) { + // Assume that we must be in the main thread, since this method must be + // called before the first thread is spawned. + thread = Thread::get_main_thread(); + _current_thread = thread; + } + // If this assertion triggers, you are making Panda calls from a thread + // that has not first been registered using Thread::bind_thread(). + nassertr(thread != nullptr, nullptr); + return thread; +} /** * @@ -62,10 +82,6 @@ start(ThreadPriority priority, bool joinable) { _joinable = joinable; _status = S_start_called; - if (!_got_pt_ptr_index) { - init_pt_ptr_index(); - } - // Increment the parent object's reference count first. The thread will // eventually decrement it when it terminates. _parent_obj->ref(); @@ -133,6 +149,27 @@ get_unique_id() const { return strm.str(); } +/** + * + */ +Thread *ThreadWin32Impl:: +get_current_thread() { + Thread *thread = _current_thread; + return (thread != nullptr) ? thread : init_current_thread(); +} + +/** + * Associates the indicated Thread object with the currently-executing thread. + * You should not call this directly; use Thread::bind_thread() instead. + */ +void ThreadWin32Impl:: +bind_thread(Thread *thread) { + if (_current_thread == nullptr && thread == Thread::get_main_thread()) { + _main_thread_known.test_and_set(std::memory_order_relaxed); + } + _current_thread = thread; +} + /** * The entry point of each thread. */ @@ -143,8 +180,7 @@ root_func(LPVOID data) { // TAU_PROFILE("void ThreadWin32Impl::root_func()", " ", TAU_USER); ThreadWin32Impl *self = (ThreadWin32Impl *)data; - BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj); - nassertr(result, 1); + _current_thread = self->_parent_obj; { self->_mutex.lock(); @@ -185,28 +221,4 @@ root_func(LPVOID data) { return 0; } -/** - * Allocate a new index to store the Thread parent pointer as a piece of per- - * thread private data. - */ -void ThreadWin32Impl:: -init_pt_ptr_index() { - nassertv(!_got_pt_ptr_index); - - _pt_ptr_index = TlsAlloc(); - if (_pt_ptr_index == TLS_OUT_OF_INDEXES) { - thread_cat->error() - << "Unable to associate Thread pointers with threads.\n"; - return; - } - - _got_pt_ptr_index = true; - - // Assume that we must be in the main thread, since this method must be - // called before the first thread is spawned. - Thread *main_thread_obj = Thread::get_main_thread(); - BOOL result = TlsSetValue(_pt_ptr_index, main_thread_obj); - nassertv(result); -} - #endif // THREAD_WIN32_IMPL diff --git a/panda/src/pipeline/threadWin32Impl.h b/panda/src/pipeline/threadWin32Impl.h index 69163230c9..64f64f31b6 100644 --- a/panda/src/pipeline/threadWin32Impl.h +++ b/panda/src/pipeline/threadWin32Impl.h @@ -43,8 +43,8 @@ public: INLINE static void prepare_for_exit(); - INLINE static Thread *get_current_thread(); - INLINE static void bind_thread(Thread *thread); + static Thread *get_current_thread(); + static void bind_thread(Thread *thread); INLINE static bool is_threading_supported(); INLINE static bool is_true_threads(); INLINE static bool is_simple_threads(); @@ -54,7 +54,6 @@ public: private: static DWORD WINAPI root_func(LPVOID data); - static void init_pt_ptr_index(); enum Status { S_new, @@ -70,9 +69,6 @@ private: DWORD _thread_id; bool _joinable; Status _status; - - static DWORD _pt_ptr_index; - static bool _got_pt_ptr_index; }; #include "threadWin32Impl.I" From 07545bc9e318d1799ceabe8838d04d7ad9297a45 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Feb 2022 22:58:13 +0100 Subject: [PATCH 018/166] dtoolbase: Use mimalloc on Windows, disable USE_DELETED_CHAIN Windows' malloc has awful performance. mimalloc is orders of magnitude faster, even faster than DeletedBufferChain. Therefore, only enable USE_DELETED_CHAIN on Windows when building without mimalloc. On Linux, mimalloc doesn't appear to be measurably faster than glibc's own allocator. Both are marginally than DeletedBufferChain, though, and substantially faster in the multi-threaded case, so USE_DELETED_CHAIN is disabled there in all cases. --- dtool/Config.cmake | 47 ++++++++++++++++++++++++++---- dtool/dtool_config.h.in | 6 +++- dtool/src/dtoolbase/CMakeLists.txt | 2 +- dtool/src/dtoolbase/dtoolbase.h | 12 +++++++- dtool/src/dtoolbase/memoryHook.cxx | 12 ++++++++ makepanda/makepanda.py | 23 +++++++++++++-- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/dtool/Config.cmake b/dtool/Config.cmake index 3a19fb90ad..3d8ca9272f 100644 --- a/dtool/Config.cmake +++ b/dtool/Config.cmake @@ -288,6 +288,26 @@ mark_as_advanced(SIMULATE_NETWORK_DELAY DO_MEMORY_USAGE DO_DCAST) # The following options have to do with the memory allocation system. # +find_package(MIMALLOC 1.0 QUIET) + +package_option(MIMALLOC + "The mimalloc allocator. See also USE_MEMORY_MIMALLOC, which +you will need to use to activate it by default. If you do not set +USE_MEMORY_MIMALLOC, Panda will decide whether to use it." + IMPORTED_AS mimalloc-static) + +if (WIN32 AND HAVE_MIMALLOC) + set(_prefer_mimalloc ON) +else() + set(_prefer_mimalloc OFF) +endif() + +option(USE_MEMORY_MIMALLOC + "This is an optional memory allocator with good multi-threading +support. It is recommended on Windows, where it gives much better +performance than the built-in malloc. However, it does not appear +to be significantly faster on glibc-based systems." ${_prefer_mimalloc}) + option(USE_MEMORY_DLMALLOC "This is an optional alternative memory-allocation scheme available within Panda. You can experiment with it to see @@ -307,16 +327,33 @@ if 16-byte alignment must be performed on top of it, wasting up to is required and not provided by the system malloc library, then an alternative malloc system (above) will be used instead." OFF) -option(USE_DELETED_CHAIN - "Define this true to use the DELETED_CHAIN macros, which support +if (WIN32 AND NOT HAVE_MIMALLOC) + option(USE_DELETED_CHAIN + "Define this true to use the DELETED_CHAIN macros, which support fast re-use of existing allocated blocks, minimizing the low-level calls to malloc() and free() for frequently-created and -deleted -objects. There's usually no reason to set this false, unless you -suspect a bug in Panda's memory management code." ON) +objects. This is significantly better than built-in malloc on Windows +but suffers with multiple threads, where mimalloc performs better, so +it is preferred to get mimalloc instead and turn this OFF." ON) +else() + option(USE_DELETED_CHAIN + "Define this true to use the DELETED_CHAIN macros, which support +fast re-use of existing allocated blocks, minimizing the low-level +calls to malloc() and free() for frequently-created and -deleted +objects. However, modern memory allocators generally perform as good, +especially with threading, so best leave this OFF." OFF) +endif() mark_as_advanced(USE_MEMORY_DLMALLOC USE_MEMORY_PTMALLOC2 - MEMORY_HOOK_DO_ALIGN USE_DELETED_CHAIN) + USE_MEMORY_MIMALLOC MEMORY_HOOK_DO_ALIGN USE_DELETED_CHAIN) +if(USE_MEMORY_MIMALLOC) + package_status(MIMALLOC "mimalloc memory allocator") +else() + package_status(MIMALLOC "mimalloc memory allocator (not used)") +endif() + +unset(_prefer_mimalloc) # # This section relates to mobile-device/phone support and options diff --git a/dtool/dtool_config.h.in b/dtool/dtool_config.h.in index 2a0a91946c..f115e022c3 100644 --- a/dtool/dtool_config.h.in +++ b/dtool/dtool_config.h.in @@ -130,8 +130,12 @@ /* Define if we want to support fixed-function OpenGL rendering. */ #cmakedefine SUPPORT_FIXED_FUNCTION -/* Define for either of the alternative malloc schemes. */ +/* Define if we have mimalloc available. */ +#cmakedefine HAVE_MIMALLOC + +/* Define for one of the alternative malloc schemes. */ #cmakedefine USE_MEMORY_DLMALLOC +#cmakedefine USE_MEMORY_MIMALLOC #cmakedefine USE_MEMORY_PTMALLOC2 /* Define if we want to compile in support for pipelining. */ diff --git a/dtool/src/dtoolbase/CMakeLists.txt b/dtool/src/dtoolbase/CMakeLists.txt index aea0a1196f..ee00d3b050 100644 --- a/dtool/src/dtoolbase/CMakeLists.txt +++ b/dtool/src/dtoolbase/CMakeLists.txt @@ -92,7 +92,7 @@ add_component_library(p3dtoolbase NOINIT SYMBOL BUILDING_DTOOL_DTOOLBASE target_include_directories(p3dtoolbase PUBLIC $ $) -target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS) +target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS PKG::MIMALLOC) target_interrogate(p3dtoolbase ${P3DTOOLBASE_SOURCES} EXTENSIONS ${P3DTOOLBASE_IGATEEXT}) if(NOT BUILD_METALIBS) diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index 67dee423da..738bc55482 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -375,6 +375,10 @@ typedef struct _object PyObject; // This specialized malloc implementation can perform the required alignment. #undef MEMORY_HOOK_DO_ALIGN +#elif defined(USE_MEMORY_MIMALLOC) +// This one does, too. +#undef MEMORY_HOOK_DO_ALIGN + #elif defined(USE_MEMORY_PTMALLOC2) // But not this one. For some reason it crashes when we try to build it with // alignment 16. So if we're using ptmalloc2, we need to enforce alignment @@ -385,6 +389,12 @@ typedef struct _object PyObject; // The OS-provided malloc implementation will do the required alignment. #undef MEMORY_HOOK_DO_ALIGN +#elif defined(HAVE_MIMALLOC) && defined(_WIN32) +// Prefer mimalloc on Windows, if we have it. It is significantly faster than +// standard malloc, supports multi-threading well and does the alignment too. +#undef MEMORY_HOOK_DO_ALIGN +#define USE_MEMORY_MIMALLOC 1 + #elif defined(MEMORY_HOOK_DO_ALIGN) // We need memory alignment, and we're willing to provide it ourselves. @@ -426,7 +436,7 @@ typedef struct _object PyObject; #endif /* Determine our memory-allocation requirements. */ -#if defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN) +#if defined(USE_MEMORY_MIMALLOC) || defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN) /* In this case we have some custom memory management requirements. */ #else /* Otherwise, if we have no custom memory management needs at all, we diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index 4abc0661d8..8712099ab7 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -51,6 +51,18 @@ static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0, #if defined(CPPPARSER) +#elif defined(USE_MEMORY_MIMALLOC) + +// mimalloc is a modern memory manager by Microsoft that is very fast as well +// as thread-safe. + +#include "mimalloc.h" + +#define call_malloc mi_malloc +#define call_realloc mi_realloc +#define call_free mi_free +#undef MEMORY_HOOK_MALLOC_LOCK + #elif defined(USE_MEMORY_DLMALLOC) // Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 6c22c458f9..5ef2b8a039 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -103,6 +103,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "PANDAPARTICLESYSTEM", # Built in particle system "CONTRIB", # Experimental "SSE2", "NEON", # Compiler features + "MIMALLOC", # Memory allocators ]) CheckPandaSourceTree() @@ -633,6 +634,7 @@ if (COMPILER == "MSVC"): if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "quartz.lib") if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib") if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.lib") + if (PkgSkip("MIMALLOC")==0): LibName("MIMALLOC", GetThirdpartyDir() + "mimalloc/lib/mimalloc-static.lib") if (PkgSkip("OPENSSL")==0): if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"): LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib") @@ -778,6 +780,8 @@ if (COMPILER == "MSVC"): LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletSoftBody" + suffix) if (COMPILER=="GCC"): + PkgDisable("MIMALLOC") # no discernable benefit over glibc + if GetTarget() != "darwin": PkgDisable("COCOA") @@ -2288,6 +2292,7 @@ DTOOL_CONFIG=[ ("REPORT_OPENSSL_ERRORS", '1', '1'), ("USE_PANDAFILESTREAM", '1', '1'), ("USE_DELETED_CHAIN", '1', '1'), + ("HAVE_MIMALLOC", 'UNDEF', 'UNDEF'), ("HAVE_WGL", '1', 'UNDEF'), ("HAVE_DX9", 'UNDEF', 'UNDEF'), ("HAVE_THREADS", '1', '1'), @@ -2433,6 +2438,20 @@ def WriteConfigSettings(): dtool_config["HAVE_NET"] = '1' + if GetTarget() == 'windows': + if not PkgSkip("MIMALLOC"): + # This is faster than both DeletedBufferChain and malloc, + # especially in the multi-threaded case. + dtool_config["USE_MEMORY_MIMALLOC"] = '1' + dtool_config["USE_DELETED_CHAIN"] = 'UNDEF' + else: + # If we don't have mimalloc, use DeletedBufferChain as fallback, + # which is still more efficient than malloc. + dtool_config["USE_DELETED_CHAIN"] = '1' + else: + # On other systems, the default malloc seems to be fine. + dtool_config["USE_DELETED_CHAIN"] = 'UNDEF' + if (PkgSkip("NVIDIACG")==0): dtool_config["HAVE_CG"] = '1' dtool_config["HAVE_CGGL"] = '1' @@ -3340,7 +3359,7 @@ if GetTarget() == 'windows': # DIRECTORY: dtool/src/dtoolbase/ # -OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL'] +OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL', 'MIMALLOC'] TargetAdd('p3dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx') TargetAdd('p3dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx') TargetAdd('p3dtoolbase_lookup3.obj', opts=OPTS, input='lookup3.c') @@ -3371,7 +3390,7 @@ TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite1.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite2.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_indent.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_lookup3.obj') -TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL']) +TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL','MIMALLOC']) # # DIRECTORY: dtool/src/cppparser/ From f30e87e7d1f48a5f46fefad5b8fba57296363ea6 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Feb 2022 23:51:07 +0100 Subject: [PATCH 019/166] CMake: Add FindGTK3.cmake file --- cmake/modules/FindGTK3.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cmake/modules/FindGTK3.cmake diff --git a/cmake/modules/FindGTK3.cmake b/cmake/modules/FindGTK3.cmake new file mode 100644 index 0000000000..213d9c5745 --- /dev/null +++ b/cmake/modules/FindGTK3.cmake @@ -0,0 +1,12 @@ +find_package(PkgConfig QUIET) + +set(__gtk3_required_version "${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION}") +if(__gtk3_required_version) + set(__gtk3_required_version " >= ${__gtk3_required_version}") +endif() +pkg_check_modules(GTK3 QUIET "gtk+-3.0${__gtk3_required_version}" IMPORTED_TARGET) + +if (NOT TARGET PkgConfig::GTK3) + set(GTK3_FOUND 0) +endif() +unset(__gtk3_required_version) From 2a904f398592ce7effedc4f12720be0cef9b6cc9 Mon Sep 17 00:00:00 2001 From: Disyer Date: Sat, 5 Feb 2022 23:16:59 +0200 Subject: [PATCH 020/166] makepanda: Record cache timestamps as integers rather than floats We don't need the extra precision, in fact it is detrimental to restoring build caches in a cross-platform way. This commit will invalidate all current build caches. --- makepanda/makepandacore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index f436ac27dd..ab961f9a5c 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -717,7 +717,7 @@ def GetTimestamp(path): if path in TIMESTAMPCACHE: return TIMESTAMPCACHE[path] try: - date = os.path.getmtime(path) + date = int(os.path.getmtime(path)) except: date = 0 TIMESTAMPCACHE[path] = date @@ -871,7 +871,7 @@ def JavaGetImports(path): ## ######################################################################## -DCACHE_VERSION = 2 +DCACHE_VERSION = 3 DCACHE_BACKED_UP = False def SaveDependencyCache(): From 5bb616dca793461b294e645536500a0809e41b9d Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 5 Feb 2022 22:13:06 +0100 Subject: [PATCH 021/166] pstatserver: Fix compilation error with STDFLOAT_DOUBLE=1 Regression in 7da70cf9399e2703ac9cacbb6977edeb173de159 Fixes #1259 --- pandatool/src/gtk-stats/gtkStatsGraph.cxx | 12 ++++++------ pandatool/src/gtk-stats/gtkStatsLabel.cxx | 12 ++++++------ pandatool/src/win-stats/winStatsGraph.cxx | 12 ++++++------ pandatool/src/win-stats/winStatsLabel.cxx | 12 ++++++------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index 4a34ca75fe..2734498b12 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -274,13 +274,13 @@ get_collector_pattern(int collector_index, bool highlight) { // Ask the monitor what color this guy should be. LRGBColor rgb = _monitor->get_collector_color(collector_index); cairo_pattern_t *pattern = cairo_pattern_create_rgb( - encode_sRGB_float(rgb[0]), - encode_sRGB_float(rgb[1]), - encode_sRGB_float(rgb[2])); + encode_sRGB_float((float)rgb[0]), + encode_sRGB_float((float)rgb[1]), + encode_sRGB_float((float)rgb[2])); cairo_pattern_t *hpattern = cairo_pattern_create_rgb( - encode_sRGB_float(rgb[0] * 0.75f), - encode_sRGB_float(rgb[1] * 0.75f), - encode_sRGB_float(rgb[2] * 0.75f)); + encode_sRGB_float((float)rgb[0] * 0.75f), + encode_sRGB_float((float)rgb[1] * 0.75f), + encode_sRGB_float((float)rgb[2] * 0.75f)); _brushes[collector_index] = std::make_pair(pattern, hpattern); return highlight ? hpattern : pattern; diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index 4eb766246e..9db4c8cd9c 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -55,14 +55,14 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, // Set the fg and bg colors on the label. LRGBColor rgb = _monitor->get_collector_color(_collector_index); _bg_color = LRGBColor( - encode_sRGB_float(rgb[0]), - encode_sRGB_float(rgb[1]), - encode_sRGB_float(rgb[2])); + encode_sRGB_float((float)rgb[0]), + encode_sRGB_float((float)rgb[1]), + encode_sRGB_float((float)rgb[2])); _highlight_bg_color = LRGBColor( - encode_sRGB_float(rgb[0] * 0.75f), - encode_sRGB_float(rgb[1] * 0.75f), - encode_sRGB_float(rgb[2] * 0.75f)); + encode_sRGB_float((float)rgb[0] * 0.75f), + encode_sRGB_float((float)rgb[1] * 0.75f), + encode_sRGB_float((float)rgb[2] * 0.75f)); // Should our foreground be black or white? PN_stdfloat bright = _bg_color.dot(LRGBColor(0.2126, 0.7152, 0.0722)); diff --git a/pandatool/src/win-stats/winStatsGraph.cxx b/pandatool/src/win-stats/winStatsGraph.cxx index 7fd3b251fa..a95c95f46d 100644 --- a/pandatool/src/win-stats/winStatsGraph.cxx +++ b/pandatool/src/win-stats/winStatsGraph.cxx @@ -242,14 +242,14 @@ get_collector_brush(int collector_index, bool highlight) { // Ask the monitor what color this guy should be. LRGBColor rgb = _monitor->get_collector_color(collector_index); - int r = (int)encode_sRGB_uchar(rgb[0]); - int g = (int)encode_sRGB_uchar(rgb[1]); - int b = (int)encode_sRGB_uchar(rgb[2]); + int r = (int)encode_sRGB_uchar((float)rgb[0]); + int g = (int)encode_sRGB_uchar((float)rgb[1]); + int b = (int)encode_sRGB_uchar((float)rgb[2]); HBRUSH brush = CreateSolidBrush(RGB(r, g, b)); - int hr = (int)encode_sRGB_uchar(rgb[0] * 0.75f); - int hg = (int)encode_sRGB_uchar(rgb[1] * 0.75f); - int hb = (int)encode_sRGB_uchar(rgb[2] * 0.75f); + int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f); + int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f); + int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f); HBRUSH hbrush = CreateSolidBrush(RGB(hr, hg, hb)); _brushes[collector_index] = std::make_pair(brush, hbrush); diff --git a/pandatool/src/win-stats/winStatsLabel.cxx b/pandatool/src/win-stats/winStatsLabel.cxx index 00021e7376..51b9cb9208 100644 --- a/pandatool/src/win-stats/winStatsLabel.cxx +++ b/pandatool/src/win-stats/winStatsLabel.cxx @@ -44,15 +44,15 @@ WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph, update_text(use_fullname); LRGBColor rgb = _monitor->get_collector_color(_collector_index); - int r = (int)encode_sRGB_uchar(rgb[0]); - int g = (int)encode_sRGB_uchar(rgb[1]); - int b = (int)encode_sRGB_uchar(rgb[2]); + int r = (int)encode_sRGB_uchar((float)rgb[0]); + int g = (int)encode_sRGB_uchar((float)rgb[1]); + int b = (int)encode_sRGB_uchar((float)rgb[2]); _bg_brush = CreateSolidBrush(RGB(r, g, b)); // Calculate the color when it is highlighted. - int hr = (int)encode_sRGB_uchar(rgb[0] * 0.75f); - int hg = (int)encode_sRGB_uchar(rgb[1] * 0.75f); - int hb = (int)encode_sRGB_uchar(rgb[2] * 0.75f); + int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f); + int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f); + int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f); _highlight_bg_brush = CreateSolidBrush(RGB(hr, hg, hb)); // Should our foreground be black or white? From 4e925a839a87602dc5a0c9a74218a0674408f530 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 5 Feb 2022 22:19:52 +0100 Subject: [PATCH 022/166] makepanda: Support building with mimalloc on non-Windows For experimentation only - it's disabled by default unless you also specify --override USE_MEMORY_MIMALLOC=1 (I did not see a discernable benefit over glibc, but more experimentation is warranted, especially with older glibc versions) --- makepanda/makepanda.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5ef2b8a039..624ae7a339 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -780,8 +780,6 @@ if (COMPILER == "MSVC"): LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletSoftBody" + suffix) if (COMPILER=="GCC"): - PkgDisable("MIMALLOC") # no discernable benefit over glibc - if GetTarget() != "darwin": PkgDisable("COCOA") @@ -855,6 +853,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus")) SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") + SmartPkgEnable("MIMALLOC", "", ("mimalloc"), "mimalloc.h") # Copy freetype libraries to be specified after harfbuzz libraries as well, # because there's a circular dependency between the two libraries. @@ -924,6 +923,9 @@ if (COMPILER=="GCC"): LibName("ARTOOLKIT", "-Wl,--exclude-libs,libAR.a") LibName("ARTOOLKIT", "-Wl,--exclude-libs,libARMulti.a") + if not PkgSkip("MIMALLOC"): + LibName("MIMALLOC", "-Wl,--exclude-libs,libmimalloc.a") + if PkgSkip("FFMPEG") or GetTarget() == "darwin": cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") if cv_lib == "opencv_core": From 94570f20aada7cd4f0121390a9397e6cd28d2fbe Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 5 Feb 2022 22:07:58 +0100 Subject: [PATCH 023/166] pgraph: Remove need for grabbing lock in RenderState destructor --- panda/src/pgraph/cacheStats.I | 2 +- panda/src/pgraph/cacheStats.cxx | 5 +++-- panda/src/pgraph/cacheStats.h | 3 ++- panda/src/pgraph/renderState.cxx | 2 -- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/panda/src/pgraph/cacheStats.I b/panda/src/pgraph/cacheStats.I index e84ff83878..9cd08c4b3f 100644 --- a/panda/src/pgraph/cacheStats.I +++ b/panda/src/pgraph/cacheStats.I @@ -90,6 +90,6 @@ add_total_size(int count) { INLINE void CacheStats:: add_num_states(int count) { #ifndef NDEBUG - _num_states += count; + _num_states.fetch_add(count, std::memory_order_relaxed); #endif // NDEBUG } diff --git a/panda/src/pgraph/cacheStats.cxx b/panda/src/pgraph/cacheStats.cxx index 4721171e86..118e469c9d 100644 --- a/panda/src/pgraph/cacheStats.cxx +++ b/panda/src/pgraph/cacheStats.cxx @@ -51,12 +51,13 @@ reset(double now) { void CacheStats:: write(std::ostream &out, const char *name) const { #ifndef NDEBUG + int num_states = _num_states.load(std::memory_order_relaxed); out << name << " cache: " << _cache_hits << " hits, " << _cache_misses << " misses\n" << _cache_adds + _cache_new_adds << "(" << _cache_new_adds << ") adds(new), " << _cache_dels << " dels, " - << _total_cache_size << " / " << _num_states << " = " - << (double)_total_cache_size / (double)_num_states + << _total_cache_size << " / " << num_states << " = " + << (double)_total_cache_size / (double)num_states << " average cache size\n"; #endif // NDEBUG } diff --git a/panda/src/pgraph/cacheStats.h b/panda/src/pgraph/cacheStats.h index daeeb5340d..2812bbed4e 100644 --- a/panda/src/pgraph/cacheStats.h +++ b/panda/src/pgraph/cacheStats.h @@ -16,6 +16,7 @@ #include "pandabase.h" #include "clockObject.h" +#include "patomic.h" #include "pnotify.h" /** @@ -45,7 +46,7 @@ private: int _cache_new_adds = 0; int _cache_dels = 0; int _total_cache_size = 0; - int _num_states = 0; + patomic _num_states {0}; double _last_reset = 0.0; bool _cache_report = false; diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 805892b947..76884ea0bb 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -117,8 +117,6 @@ RenderState:: nassertv(!is_destructing()); set_destructing(); - LightReMutexHolder holder(*_states_lock); - // unref() should have cleared these. nassertv(_saved_entry == -1); nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty()); From a12359275f5bfcfa9be561674c4c4f58a6eb8052 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 6 Feb 2022 14:45:56 +0100 Subject: [PATCH 024/166] makepanda: Support building with OpenSSL 1.1.1 on Windows --- makepanda/makepanda.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 71aa749a23..42c35436f8 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -771,9 +771,14 @@ if (COMPILER == "MSVC"): if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"): LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib") LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib") - else: + elif os.path.isfile(GetThirdpartyDir() + "openssl/lib/ssleay32.lib"): LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libeay32.lib") LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/ssleay32.lib") + else: + LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libssl.lib") + LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libcrypto.lib") + LibName("OPENSSL", "crypt32.lib") + LibName("OPENSSL", "ws2_32.lib") if (PkgSkip("PNG")==0): if os.path.isfile(GetThirdpartyDir() + "png/lib/libpng16_static.lib"): LibName("PNG", GetThirdpartyDir() + "png/lib/libpng16_static.lib") From bc6502a8fee424a41ef6c8199c602445729fea35 Mon Sep 17 00:00:00 2001 From: Disyer Date: Sat, 5 Feb 2022 23:16:59 +0200 Subject: [PATCH 025/166] makepanda: Record cache timestamps as integers rather than floats We don't need the extra precision, in fact it is detrimental to restoring build caches in a cross-platform way. This commit will invalidate all current build caches. Cherry-picked from 2a904f398592ce7effedc4f12720be0cef9b6cc9 (see #1260) --- makepanda/makepandacore.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 1ecf349fb1..e650ef019f 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -714,8 +714,10 @@ TIMESTAMPCACHE = {} def GetTimestamp(path): if path in TIMESTAMPCACHE: return TIMESTAMPCACHE[path] - try: date = os.path.getmtime(path) - except: date = 0 + try: + date = int(os.path.getmtime(path)) + except: + date = 0 TIMESTAMPCACHE[path] = date return date @@ -866,7 +868,7 @@ def JavaGetImports(path): ## ######################################################################## -DCACHE_VERSION = 2 +DCACHE_VERSION = 3 DCACHE_BACKED_UP = False def SaveDependencyCache(): From be2e07637f0f31a3957bdc215e4ad23eb6de79a5 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Feb 2022 13:35:32 +0100 Subject: [PATCH 026/166] gtk-stats: Fix mouse motion detected outside strip chart graph area Cherry-picked from 3a38543f65670b2d754838c5b08a556df1485a01 --- .../src/gtk-stats/gtkStatsStripChart.cxx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index 117ef96576..1015c7989a 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -402,20 +402,23 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) { gboolean GtkStatsStripChart:: handle_button_press(GtkWidget *widget, int graph_x, int graph_y, bool double_click) { - if (double_click) { - // Double-clicking on a color bar in the graph is the same as double- - // clicking on the corresponding label. - clicked_label(get_collector_under_pixel(graph_x, graph_y)); - return TRUE; + if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + if (double_click) { + // Double-clicking on a color bar in the graph is the same as double- + // clicking on the corresponding label. + clicked_label(get_collector_under_pixel(graph_x, graph_y)); + return TRUE; + } + + if (_potential_drag_mode == DM_none) { + set_drag_mode(DM_scale); + _drag_scale_start = pixel_to_height(graph_y); + // SetCapture(_graph_window); + return TRUE; + } } - if (_potential_drag_mode == DM_none) { - set_drag_mode(DM_scale); - _drag_scale_start = pixel_to_height(graph_y); - // SetCapture(_graph_window); - return TRUE; - - } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { + if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) { set_drag_mode(DM_guide_bar); _drag_start_y = graph_y; // SetCapture(_graph_window); @@ -455,7 +458,8 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { */ gboolean GtkStatsStripChart:: handle_motion(GtkWidget *widget, int graph_x, int graph_y) { - if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { + if (_drag_mode == DM_none && _potential_drag_mode == DM_none && + graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { // When the mouse is over a color bar, highlight it. _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y)); From a37dfa727e25b81b5f0de962677b2c1ee600f16b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 6 Feb 2022 15:24:33 +0100 Subject: [PATCH 027/166] makepanda: Support building with mimalloc on Windows, experimentally Partial backport of 07545bc9e318d1799ceabe8838d04d7ad9297a45 for Windows, requires building with `--override USE_MEMORY_MIMALLOC=1 --override USE_DELETED_CHAIN=UNDEF` for optimum effect --- dtool/src/dtoolbase/dtoolbase.h | 6 +++++- dtool/src/dtoolbase/memoryHook.cxx | 12 ++++++++++++ makepanda/makepanda.py | 8 ++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index 66142427cc..d89caf2a92 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -387,6 +387,10 @@ typedef struct _object PyObject; // This specialized malloc implementation can perform the required alignment. #undef MEMORY_HOOK_DO_ALIGN +#elif defined(USE_MEMORY_MIMALLOC) +// This one does, too. +#undef MEMORY_HOOK_DO_ALIGN + #elif defined(USE_MEMORY_PTMALLOC2) // But not this one. For some reason it crashes when we try to build it with // alignment 16. So if we're using ptmalloc2, we need to enforce alignment @@ -438,7 +442,7 @@ typedef struct _object PyObject; #endif /* Determine our memory-allocation requirements. */ -#if defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN) +#if defined(USE_MEMORY_MIMALLOC) || defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN) /* In this case we have some custom memory management requirements. */ #else /* Otherwise, if we have no custom memory management needs at all, we diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index 24d5f11a11..7f5872d273 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -51,6 +51,18 @@ static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0, #if defined(CPPPARSER) +#elif defined(USE_MEMORY_MIMALLOC) + +// mimalloc is a modern memory manager by Microsoft that is very fast as well +// as thread-safe. + +#include "mimalloc.h" + +#define call_malloc mi_malloc +#define call_realloc mi_realloc +#define call_free mi_free +#undef MEMORY_HOOK_MALLOC_LOCK + #elif defined(USE_MEMORY_DLMALLOC) // Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 42c35436f8..5d22f69026 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -103,6 +103,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "PANDAPARTICLESYSTEM", # Built in particle system "CONTRIB", # Experimental "SSE2", "NEON", # Compiler features + "MIMALLOC", # Memory allocators ]) CheckPandaSourceTree() @@ -767,6 +768,7 @@ if (COMPILER == "MSVC"): if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "quartz.lib") if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib") if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.lib") + if (PkgSkip("MIMALLOC")==0): LibName("MIMALLOC", GetThirdpartyDir() + "mimalloc/lib/mimalloc-static.lib") if (PkgSkip("OPENSSL")==0): if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"): LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib") @@ -949,6 +951,8 @@ if (COMPILER == "MSVC"): LibName("BULLET", GetThirdpartyDir() + "bullet/lib/BulletSoftBody" + suffix) if (COMPILER=="GCC"): + PkgDisable("MIMALLOC") # no discernable benefit over glibc + if GetTarget() != "darwin": PkgDisable("CARBON") PkgDisable("COCOA") @@ -3766,7 +3770,7 @@ if GetTarget() == 'windows': # DIRECTORY: dtool/src/dtoolbase/ # -OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL'] +OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL', 'MIMALLOC'] TargetAdd('p3dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx') TargetAdd('p3dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx') TargetAdd('p3dtoolbase_lookup3.obj', opts=OPTS, input='lookup3.c') @@ -3797,7 +3801,7 @@ TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite1.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite2.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_indent.obj') TargetAdd('libp3dtool.dll', input='p3dtoolbase_lookup3.obj') -TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL']) +TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL','MIMALLOC']) # # DIRECTORY: dtool/src/cppparser/ From 287b0d5a74530762262e78f070a5d34b6c42c216 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 6 Feb 2022 15:26:41 +0100 Subject: [PATCH 028/166] mathutil: Add proper `__repr__` for LPlane class Fixes #1248 --- panda/src/mathutil/plane_src.cxx | 14 ++++++++++++++ panda/src/mathutil/plane_src.h | 1 + 2 files changed, 15 insertions(+) diff --git a/panda/src/mathutil/plane_src.cxx b/panda/src/mathutil/plane_src.cxx index 7ba18701c6..4463d57756 100644 --- a/panda/src/mathutil/plane_src.cxx +++ b/panda/src/mathutil/plane_src.cxx @@ -158,3 +158,17 @@ void FLOATNAME(LPlane):: write(std::ostream &out, int indent_level) const { indent(out, indent_level) << *this << "\n"; } + +/** + * Returns a string representation of this LPlane. + */ +std::string FLOATNAME(LPlane):: +__repr__() const { + std::ostringstream out; + out << "LPlane" << FLOATTOKEN << "(" + << MAYBE_ZERO(_v(0)) << ", " + << MAYBE_ZERO(_v(1)) << ", " + << MAYBE_ZERO(_v(2)) << ", " + << MAYBE_ZERO(_v(3)) << ")"; + return out.str(); +} diff --git a/panda/src/mathutil/plane_src.h b/panda/src/mathutil/plane_src.h index c1dffc4d5d..c1d7ef0252 100644 --- a/panda/src/mathutil/plane_src.h +++ b/panda/src/mathutil/plane_src.h @@ -61,6 +61,7 @@ PUBLISHED: void output(std::ostream &out) const; void write(std::ostream &out, int indent_level = 0) const; + std::string __repr__() const; }; INLINE_MATHUTIL std::ostream & From 3c142a61ab9b9eec20bdd44b062d421cf271168f Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 7 Feb 2022 11:10:32 +0100 Subject: [PATCH 029/166] makepanda: Properly detect keyboard interrupts on Windows --- makepanda/makepandacore.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index e650ef019f..0b7225432e 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -635,6 +635,9 @@ def oscmd(cmd, ignoreError = False, cwd=None): res = os.spawnl(os.P_WAIT, exe_path, cmd) + if res == -1073741510: # 0xc000013a + exit("keyboard interrupt") + if cwd is not None: os.chdir(pwd) else: From b401884f1c1b25dde079139666f03a01427e8d05 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 7 Feb 2022 11:11:40 +0100 Subject: [PATCH 030/166] makepanda: Support building with OpenEXR 3.0 or 3.1 on Windows --- makepanda/makepanda.py | 48 ++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5d22f69026..e66768aa5b 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -792,23 +792,39 @@ if (COMPILER == "MSVC"): else: LibName("TIFF", GetThirdpartyDir() + "tiff/lib/tiff.lib") if (PkgSkip("OPENEXR")==0): - suffix = "" - if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"): - suffix = "-2_2" - elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"): - suffix = "-2_3" - elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"): - suffix = "-2_4" - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib") - if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"): - suffix += "_s" # _s suffix observed for OpenEXR 2.3 only so far - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib") - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib") - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib") - if suffix == "-2_2": - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib") + if os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXRCore-3_1.lib"): + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_1.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_1.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_1.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_1.lib") + elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib"): + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_0.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_0.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_0.lib") + elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR.lib"): + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath.lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex.lib") else: - LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib") + suffix = "" + if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"): + suffix = "-2_2" + elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"): + suffix = "-2_3" + elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"): + suffix = "-2_4" + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib") + if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"): + suffix += "_s" # _s suffix observed for OpenEXR 2.3 only so far + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib") + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib") + if suffix == "-2_2": + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib") + else: + LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib") IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/OpenEXR") IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/Imath") if (PkgSkip("JPEG")==0): LibName("JPEG", GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib") From 77b0d2d6a7e6853ab0046ce924160b0f6dfd9b27 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 7 Feb 2022 17:02:30 +0100 Subject: [PATCH 031/166] pstats: Switch from AtomicAdjust to C++11-style atomics --- panda/src/pstatclient/pStatClient.I | 18 ++- panda/src/pstatclient/pStatClient.cxx | 160 +++++++++++++------------- panda/src/pstatclient/pStatClient.h | 14 +-- 3 files changed, 92 insertions(+), 100 deletions(-) diff --git a/panda/src/pstatclient/pStatClient.I b/panda/src/pstatclient/pStatClient.I index 452c165901..e7daf8fa0e 100644 --- a/panda/src/pstatclient/pStatClient.I +++ b/panda/src/pstatclient/pStatClient.I @@ -16,8 +16,7 @@ */ INLINE int PStatClient:: get_num_collectors() const { - ReMutexHolder holder(_lock); - return (int)_num_collectors; + return _num_collectors.load(std::memory_order_relaxed); } /** @@ -25,7 +24,7 @@ get_num_collectors() const { */ INLINE PStatCollectorDef *PStatClient:: get_collector_def(int index) const { - nassertr(index >= 0 && index < _num_collectors, nullptr); + nassertr(index >= 0 && index < get_num_collectors(), nullptr); return get_collector_ptr(index)->get_def(this, index); } @@ -35,8 +34,7 @@ get_collector_def(int index) const { */ INLINE int PStatClient:: get_num_threads() const { - ReMutexHolder holder(_lock); - return (int)_num_threads; + return _num_threads.load(std::memory_order_relaxed); } /** @@ -44,7 +42,7 @@ get_num_threads() const { */ INLINE std::string PStatClient:: get_thread_name(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), std::string()); + nassertr(index >= 0 && index < get_num_threads(), std::string()); return get_thread_ptr(index)->_name; } @@ -53,7 +51,7 @@ get_thread_name(int index) const { */ INLINE std::string PStatClient:: get_thread_sync_name(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), std::string()); + nassertr(index >= 0 && index < get_num_threads(), std::string()); return get_thread_ptr(index)->_sync_name; } @@ -62,7 +60,7 @@ get_thread_sync_name(int index) const { */ INLINE PT(Thread) PStatClient:: get_thread_object(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), nullptr); + nassertr(index >= 0 && index < get_num_threads(), nullptr); InternalThread *thread = get_thread_ptr(index); return thread->_thread.lock(); } @@ -144,7 +142,7 @@ get_impl() const { */ INLINE PStatClient::Collector *PStatClient:: get_collector_ptr(int collector_index) const { - CollectorPointer *collectors = (CollectorPointer *)AtomicAdjust::get_ptr(_collectors); + CollectorPointer *collectors = _collectors.load(std::memory_order_consume); return collectors[collector_index]; } @@ -153,7 +151,7 @@ get_collector_ptr(int collector_index) const { */ INLINE PStatClient::InternalThread *PStatClient:: get_thread_ptr(int thread_index) const { - ThreadPointer *threads = (ThreadPointer *)AtomicAdjust::get_ptr(_threads); + ThreadPointer *threads = _threads.load(std::memory_order_consume); return threads[thread_index]; } diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index dd57612107..2bec9b2c43 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -77,14 +77,6 @@ PStatClient() : _lock("PStatClient::_lock"), _impl(nullptr) { - _collectors = nullptr; - _collectors_size = 0; - _num_collectors = 0; - - _threads = nullptr; - _threads_size = 0; - _num_threads = 0; - // We always have a collector at index 0 named "Frame". This tracks the // total frame time and is the root of all other collectors. We have to // make this one by hand since it's the root. @@ -152,7 +144,7 @@ get_max_rate() const { */ PStatCollector PStatClient:: get_collector(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), PStatCollector()); + nassertr(index >= 0 && index < get_num_collectors(), PStatCollector()); return PStatCollector((PStatClient *)this, index); } @@ -161,7 +153,7 @@ get_collector(int index) const { */ string PStatClient:: get_collector_name(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string()); + nassertr(index >= 0 && index < get_num_collectors(), string()); return get_collector_ptr(index)->get_name(); } @@ -173,7 +165,7 @@ get_collector_name(int index) const { */ string PStatClient:: get_collector_fullname(int index) const { - nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string()); + nassertr(index >= 0 && index < get_num_collectors(), string()); Collector *collector = get_collector_ptr(index); int parent_index = collector->get_parent_index(); @@ -191,7 +183,7 @@ get_collector_fullname(int index) const { PStatThread PStatClient:: get_thread(int index) const { ReMutexHolder holder(_lock); - nassertr(index >= 0 && index < _num_threads, PStatThread()); + nassertr(index >= 0 && index < get_num_threads(), PStatThread()); return PStatThread((PStatClient *)this, index); } @@ -462,8 +454,9 @@ client_disconnect() { _impl = nullptr; } - ThreadPointer *threads = (ThreadPointer *)_threads; - for (int ti = 0; ti < _num_threads; ++ti) { + // These can be relaxed loads because we hold the lock. + ThreadPointer *threads = _threads.load(std::memory_order_relaxed); + for (int ti = 0; ti < get_num_threads(); ++ti) { InternalThread *thread = threads[ti]; thread->_frame_number = 0; thread->_is_active = false; @@ -471,14 +464,11 @@ client_disconnect() { thread->_frame_data.clear(); } - CollectorPointer *collectors = (CollectorPointer *)_collectors; - for (int ci = 0; ci < _num_collectors; ++ci) { + CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed); + for (int ci = 0; ci < get_num_collectors(); ++ci) { Collector *collector = collectors[ci]; - PerThread::iterator ii; - for (ii = collector->_per_thread.begin(); - ii != collector->_per_thread.end(); - ++ii) { - (*ii)._nested_count = 0; + for (PerThreadData &per_thread : collector->_per_thread) { + per_thread._nested_count = 0; } } } @@ -576,7 +566,8 @@ PStatCollector PStatClient:: make_collector_with_name(int parent_index, const string &name) { ReMutexHolder holder(_lock); - nassertr(parent_index >= 0 && parent_index < _num_collectors, + int num_collectors = get_num_collectors(); + nassertr(parent_index >= 0 && parent_index < num_collectors, PStatCollector()); Collector *parent = get_collector_ptr(parent_index); @@ -593,26 +584,25 @@ make_collector_with_name(int parent_index, const string &name) { if (ni != parent->_children.end()) { // We already had a collector by this name; return it. int index = (*ni).second; - nassertr(index >= 0 && index < _num_collectors, PStatCollector()); + nassertr(index >= 0 && index < num_collectors, PStatCollector()); return PStatCollector(this, (*ni).second); } // Create a new collector for this name. - int new_index = _num_collectors; - parent->_children.insert(ThingsByName::value_type(name, new_index)); + parent->_children.insert(ThingsByName::value_type(name, num_collectors)); Collector *collector = new Collector(parent_index, name); - // collector->_def = new PStatCollectorDef(new_index, name); + // collector->_def = new PStatCollectorDef(num_collectors, name); // collector->_def->set_parent(*_collectors[parent_index]._def); // initialize_collector_def(this, collector->_def); // We need one PerThreadData for each thread. - while ((int)collector->_per_thread.size() < _num_threads) { + while ((int)collector->_per_thread.size() < get_num_threads()) { collector->_per_thread.push_back(PerThreadData()); } add_collector(collector); - return PStatCollector(this, new_index); + return PStatCollector(this, num_collectors); } /** @@ -662,8 +652,8 @@ do_make_thread(Thread *thread) { vi != indices.end(); ++vi) { int index = (*vi); - nassertr(index >= 0 && index < _num_threads, PStatThread()); - ThreadPointer *threads = (ThreadPointer *)_threads; + nassertr(index >= 0 && index < get_num_threads(), PStatThread()); + ThreadPointer *threads = _threads.load(std::memory_order_relaxed); if (threads[index]->_thread.was_deleted() && threads[index]->_sync_name == thread->get_sync_name()) { // Yes, re-use this one. @@ -676,7 +666,7 @@ do_make_thread(Thread *thread) { } // Create a new PStatsThread for this thread pointer. - int new_index = _num_threads; + int new_index = get_num_threads(); thread->set_pstats_index(new_index); thread->set_pstats_callback(this); @@ -693,7 +683,7 @@ do_make_thread(Thread *thread) { PStatThread PStatClient:: make_gpu_thread(const string &name) { ReMutexHolder holder(_lock); - int new_index = _num_threads; + int new_index = get_num_threads(); InternalThread *pthread = new InternalThread(name, "GPU"); add_thread(pthread); @@ -710,8 +700,8 @@ make_gpu_thread(const string &name) { */ bool PStatClient:: is_active(int collector_index, int thread_index) const { - nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false); - nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false); + nassertr(collector_index >= 0 && collector_index < get_num_collectors(), false); + nassertr(thread_index >= 0 && thread_index < get_num_threads(), false); return (client_is_connected() && get_collector_ptr(collector_index)->is_active() && @@ -727,8 +717,8 @@ is_active(int collector_index, int thread_index) const { */ bool PStatClient:: is_started(int collector_index, int thread_index) const { - nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false); - nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false); + nassertr(collector_index >= 0 && collector_index < get_num_collectors(), false); + nassertr(thread_index >= 0 && thread_index < get_num_threads(), false); Collector *collector = get_collector_ptr(collector_index); InternalThread *thread = get_thread_ptr(thread_index); @@ -758,8 +748,8 @@ start(int collector_index, int thread_index) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -789,8 +779,8 @@ start(int collector_index, int thread_index, double as_of) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -820,8 +810,8 @@ stop(int collector_index, int thread_index) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -862,8 +852,8 @@ stop(int collector_index, int thread_index, double as_of) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -905,8 +895,8 @@ clear_level(int collector_index, int thread_index) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -930,8 +920,8 @@ set_level(int collector_index, int thread_index, double level) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -963,8 +953,8 @@ add_level(int collector_index, int thread_index, double increment) { } #ifdef _DEBUG - nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors)); - nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads)); + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); #endif Collector *collector = get_collector_ptr(collector_index); @@ -991,8 +981,8 @@ get_level(int collector_index, int thread_index) const { } #ifdef _DEBUG - nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), 0.0f); - nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), 0.0f); + nassertr(collector_index >= 0 && collector_index < get_num_collectors(), 0.0f); + nassertr(thread_index >= 0 && thread_index < get_num_threads(), 0.0f); #endif Collector *collector = get_collector_ptr(collector_index); @@ -1050,17 +1040,19 @@ stop_clock_wait() { */ void PStatClient:: add_collector(PStatClient::Collector *collector) { - if (_num_collectors >= _collectors_size) { + int num_collectors = get_num_collectors(); + if (num_collectors >= _collectors_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 not protected by // the lock. - int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2; + size_t new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2; + CollectorPointer *old_collectors = _collectors.load(std::memory_order_relaxed); CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size]; - if (_collectors != nullptr) { - memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer)); + if (old_collectors != nullptr) { + memcpy(new_collectors, old_collectors, num_collectors * sizeof(CollectorPointer)); } - AtomicAdjust::set_ptr(_collectors, new_collectors); - AtomicAdjust::set(_collectors_size, new_collectors_size); + _collectors_size = new_collectors_size; + _collectors.store(new_collectors, std::memory_order_release); // Now, we still have the old array, which we allow to leak. We should // delete it, but there might be a thread out there that's still trying to @@ -1068,14 +1060,14 @@ add_collector(PStatClient::Collector *collector) { // much, since it's not a big leak. (We will only reallocate the array so // many times in an application, and then no more.) - new_collectors[_num_collectors] = collector; - AtomicAdjust::inc(_num_collectors); - - } else { - CollectorPointer *collectors = (CollectorPointer *)_collectors; - collectors[_num_collectors] = collector; - AtomicAdjust::inc(_num_collectors); + new_collectors[num_collectors] = collector; } + else { + CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed); + collectors[num_collectors] = collector; + } + + _num_collectors.fetch_add(1, std::memory_order_release); } /** @@ -1084,21 +1076,22 @@ 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); + int num_threads = get_num_threads(); + _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) { + 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 not protected by // the lock. - int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2; + size_t new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2; + ThreadPointer *old_threads = _threads.load(std::memory_order_relaxed); ThreadPointer *new_threads = new ThreadPointer[new_threads_size]; - if (_threads != nullptr) { - memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer)); + if (old_threads != nullptr) { + memcpy(new_threads, old_threads, num_threads * sizeof(ThreadPointer)); } - // We assume that assignment to a pointer and to an int are each atomic. - AtomicAdjust::set_ptr(_threads, new_threads); - AtomicAdjust::set(_threads_size, new_threads_size); + _threads_size = new_threads_size; + _threads.store(new_threads, std::memory_order_release); // Now, we still have the old array, which we allow to leak. We should // delete it, but there might be a thread out there that's still trying to @@ -1106,22 +1099,23 @@ add_thread(PStatClient::InternalThread *thread) { // much, since it's not a big leak. (We will only reallocate the array so // many times in an application, and then no more.) - new_threads[_num_threads] = thread; - - } else { - ThreadPointer *threads = (ThreadPointer *)_threads; - threads[_num_threads] = thread; + new_threads[num_threads] = thread; + } + else { + ThreadPointer *threads = _threads.load(std::memory_order_relaxed); + threads[num_threads] = thread; } - AtomicAdjust::inc(_num_threads); + _num_threads.fetch_add(1, std::memory_order_release); + ++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) { + CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed); + for (int ci = 0; ci < get_num_collectors(); ++ci) { Collector *collector = collectors[ci]; collector->_per_thread.push_back(PerThreadData()); - nassertv((int)collector->_per_thread.size() == _num_threads); + nassertv((int)collector->_per_thread.size() == num_threads); } } diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index 1e6fe34480..aabc0fb8f6 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -26,7 +26,7 @@ #include "thread.h" #include "weakPointerTo.h" #include "vector_int.h" -#include "atomicAdjust.h" +#include "patomic.h" #include "numeric_types.h" #include "bitArray.h" @@ -195,9 +195,9 @@ private: PerThread _per_thread; }; typedef Collector *CollectorPointer; - AtomicAdjust::Pointer _collectors; // CollectorPointer *_collectors; - AtomicAdjust::Integer _collectors_size; // size of the allocated array - AtomicAdjust::Integer _num_collectors; // number of in-use elements within the array + patomic _collectors {nullptr}; + size_t _collectors_size {0}; // size of the allocated array + patomic _num_collectors {0}; // number of in-use elements within the array // This defines a single thread, i.e. a separate chain of execution, // independent of all other threads. Timing and level data are maintained @@ -224,9 +224,9 @@ private: LightMutex _thread_lock; }; typedef InternalThread *ThreadPointer; - AtomicAdjust::Pointer _threads; // ThreadPointer *_threads; - AtomicAdjust::Integer _threads_size; // size of the allocated array - AtomicAdjust::Integer _num_threads; // number of in-use elements within the array + patomic _threads {nullptr}; + size_t _threads_size {0}; // size of the allocated array + patomic _num_threads {0}; // number of in-use elements within the array mutable PStatClientImpl *_impl; From 355cd5b4cdbba1b506c4675ddc7c9b0e033c800d Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 7 Feb 2022 17:03:58 +0100 Subject: [PATCH 032/166] pstats: Remove unused field from PStatClient::InternalThread --- panda/src/pstatclient/pStatClient.h | 1 - 1 file changed, 1 deletion(-) diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index aabc0fb8f6..56bd0d2a1b 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -216,7 +216,6 @@ private: double _next_packet; bool _thread_active; - BitArray _active_collectors; // no longer used. // This mutex is used to protect writes to _frame_data for this particular // thread, as well as writes to the _per_thread data for this particular From 833ad89ebad58395d0af0b7ec08538e5e4308265 Mon Sep 17 00:00:00 2001 From: "Paul m. p. P" Date: Mon, 7 Feb 2022 19:33:19 +0100 Subject: [PATCH 033/166] py_panda: Fix compilation issue with Python 3.11 --- dtool/src/interrogatedb/py_panda.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 35465a1090..ae6483f5b1 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -744,7 +744,7 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) { if (callable == nullptr) { return nullptr; } - PyObject *result = _PyObject_CallNoArg(callable); + PyObject *result = PyObject_CallNoArgs(callable); Py_DECREF(callable); return result; } @@ -768,7 +768,7 @@ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) { if (callable == nullptr) { return nullptr; } - PyObject *result = _PyObject_CallNoArg(callable); + PyObject *result = PyObject_CallNoArgs(callable); Py_DECREF(callable); return result; } From 07586c82e6d7e137a02223336ef8f128e922905c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:31:50 +0100 Subject: [PATCH 034/166] workflow: Update GitHub CI builder to Windows 2019/2022 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 546a463136..3862904d48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -333,7 +333,7 @@ jobs: if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" strategy: matrix: - os: [ubuntu-18.04, windows-2016, macOS-11] + os: [ubuntu-18.04, windows-2019, macOS-11] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 @@ -365,7 +365,7 @@ jobs: - name: Build Python 3.9 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 + 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.9 shell: bash run: | @@ -378,7 +378,7 @@ jobs: - name: Build Python 3.8 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 + 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.8 shell: bash run: | @@ -391,7 +391,7 @@ jobs: - name: Build Python 3.7 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 + 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.7 shell: bash run: | From aea2d6ef4535e96ec15a593cd5fe817fb75db072 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:28:36 +0100 Subject: [PATCH 035/166] display: Release lock before notifying render thread in GraphicsEngine Otherwise the render thread will wake up only to be blocked by the mutex right away. --- panda/src/display/graphicsEngine.cxx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index b15474fc3f..4951022af7 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -925,10 +925,14 @@ render_frame() { for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; if (thread->_thread_state == TS_wait) { + // Release before notifying, otherwise the other thread will wake up + // and get blocked on the mutex straight away. thread->_thread_state = TS_do_frame; + thread->_cv_mutex.release(); thread->_cv_start.notify(); + } else { + thread->_cv_mutex.release(); } - thread->_cv_mutex.release(); } // Some threads may still be drawing, so indicate that we have to wait for @@ -1036,8 +1040,8 @@ open_windows() { } thread->_thread_state = TS_do_windows; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); } } @@ -1154,11 +1158,11 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { thread->_gsg = gsg; thread->_texture = tex; thread->_thread_state = TS_do_extract; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); thread->_cv_mutex.acquire(); - //XXX is this necessary, or is acquiring the mutex enough? + // Wait for it to finish the extraction. while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1222,11 +1226,11 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph thread->_state = state.p(); thread->_work_groups = work_groups; thread->_thread_state = TS_do_compute; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); thread->_cv_mutex.acquire(); - //XXX is this necessary, or is acquiring the mutex enough? + // Wait for it to finish the compute task. while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1292,11 +1296,11 @@ do_get_screenshot(DisplayRegion *region, GraphicsStateGuardian *gsg) { // Now that the draw thread is idle, signal it to do the extraction task. thread->_region = region; thread->_thread_state = TS_do_screenshot; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); thread->_cv_mutex.acquire(); - //XXX is this necessary, or is acquiring the mutex enough? + // Wait for it to finish the extraction. while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1937,8 +1941,8 @@ do_flip_frame(Thread *current_thread) { RenderThread *thread = (*ti).second; nassertv(thread->_thread_state == TS_wait); thread->_thread_state = TS_do_flip; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); } } @@ -2365,8 +2369,8 @@ terminate_threads(Thread *current_thread) { for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; thread->_thread_state = TS_terminate; - thread->_cv_start.notify(); thread->_cv_mutex.release(); + thread->_cv_start.notify(); } // Finally, wait for them all to finish cleaning up. From cf9574b4120dd9c888ff43845f5ebc465aaf8afd Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:31:18 +0100 Subject: [PATCH 036/166] pstats: Add convenience method for ticking current thread only --- panda/src/pstatclient/pStatClient.cxx | 29 +++++++++++++++++++++++++++ panda/src/pstatclient/pStatClient.h | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 2bec9b2c43..657badb048 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -373,6 +373,14 @@ main_tick() { get_global_pstats()->client_main_tick(); } +/** + * A convenience function to call new_frame() for the current thread. + */ +void PStatClient:: +thread_tick() { + get_global_pstats()->client_thread_tick(); +} + /** * A convenience function to call new_frame() on any threads with the * indicated sync_name @@ -410,6 +418,19 @@ client_main_tick() { } } +/** + * A convenience function to call new_frame() on the current thread. + */ +void PStatClient:: +client_thread_tick() { + ReMutexHolder holder(_lock); + + if (has_impl()) { + PStatThread thread = do_get_current_thread(); + _impl->new_frame(thread.get_index()); + } +} + /** * A convenience function to call new_frame() on all of the threads with the * indicated sync name. @@ -1285,6 +1306,10 @@ void PStatClient:: main_tick() { } +void PStatClient:: +thread_tick() { +} + void PStatClient:: thread_tick(const std::string &) { } @@ -1293,6 +1318,10 @@ void PStatClient:: client_main_tick() { } +void PStatClient:: +client_thread_tick() { +} + void PStatClient:: client_thread_tick(const std::string &sync_name) { } diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index 56bd0d2a1b..9b17f208f3 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -95,9 +95,11 @@ PUBLISHED: INLINE static void resume_after_pause(); static void main_tick(); + static void thread_tick(); static void thread_tick(const std::string &sync_name); 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(); @@ -291,10 +293,12 @@ PUBLISHED: INLINE static void resume_after_pause() { } static void main_tick(); + static void thread_tick(); static void thread_tick(const std::string &); public: 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(); From 93b7ebffaa653ec3684743f8c8bd01f667c9cdae Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:31:34 +0100 Subject: [PATCH 037/166] pstats: PStatClient.connect() should wait for UDP connection to be established This makes the behavior of PStats more predictable, reducing missed frames at the beginning --- panda/src/pstatclient/pStatClientImpl.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index 02f1764b81..85d7e45f27 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -124,6 +124,11 @@ client_connect(std::string hostname, int port) { MutexDebug::increment_pstats(); #endif // DEBUG_THREADS + // Wait for the server hello. + while (!_got_udp_port) { + transmit_control_data(); + } + return _is_connected; } From d7bbcfb0b781a6e217b2cbcdc117cf742acca961 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:19:40 +0100 Subject: [PATCH 038/166] pstats: Some collector reorganisation: - "App:Show code:General" is gone, it was causing too much trouble - Replace odd "Client::GuiObjects" with "Nodes:GUI" - Regroup "Dirty PipelineCyclers" underneath "PipelineCyclers" --- direct/src/gui/DirectGuiBase.py | 6 ---- panda/src/display/graphicsEngine.cxx | 32 ++++++++++++-------- panda/src/display/graphicsEngine.h | 2 +- panda/src/display/graphicsStateGuardian.cxx | 33 +++++++++++---------- panda/src/display/graphicsStateGuardian.h | 6 ++-- panda/src/pgraph/cullTraverser.I | 1 + panda/src/pgraph/cullTraverser.cxx | 1 + panda/src/pgraph/cullTraverser.h | 1 + panda/src/pgui/pgItem.cxx | 2 ++ panda/src/pstatclient/pStatProperties.cxx | 4 +-- 10 files changed, 48 insertions(+), 40 deletions(-) diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 51f08e19e9..159c2f2063 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -100,8 +100,6 @@ from direct.showbase import DirectObject from direct.task import Task from direct.task.TaskManagerGlobal import taskMgr -guiObjectCollector = PStatCollector("Client::GuiObjects") - _track_gui_items = ConfigVariableBool('track-gui-items', False) @@ -732,8 +730,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath): self.guiId = self.guiItem.getId() if ShowBaseGlobal.__dev__: - guiObjectCollector.addLevel(1) - guiObjectCollector.flushLevel() # track gui items by guiId for tracking down leaks if _track_gui_items: if not hasattr(ShowBase, 'guiItems'): @@ -1033,8 +1029,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath): def destroy(self): if hasattr(self, "frameStyle"): if ShowBaseGlobal.__dev__: - guiObjectCollector.subLevel(1) - guiObjectCollector.flushLevel() if hasattr(ShowBase, 'guiItems'): ShowBase.guiItems.pop(self.guiId, None) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 4951022af7..bc98317a01 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -69,7 +69,7 @@ PT(GraphicsEngine) GraphicsEngine::_global_ptr; PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync"); PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle"); -PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General"); +//PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General"); PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame"); PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame"); PStatCollector GraphicsEngine::_yield_pcollector("App:Yield"); @@ -86,7 +86,7 @@ PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformSta PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates"); PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused"); PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers"); -PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers"); +PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("PipelineCyclers:Dirty"); PStatCollector GraphicsEngine::_delete_pcollector("App:Delete"); @@ -182,9 +182,9 @@ GraphicsEngine(Pipeline *pipeline) : GraphicsEngine:: ~GraphicsEngine() { #ifdef DO_PSTATS - if (_app_pcollector.is_started()) { - _app_pcollector.stop(); - } + //if (_app_pcollector.is_started()) { + // _app_pcollector.stop(); + //} #endif remove_all_windows(); @@ -714,9 +714,9 @@ render_frame() { // to be App. #ifdef DO_PSTATS _render_frame_pcollector.start(); - if (_app_pcollector.is_started()) { - _app_pcollector.stop(); - } + //if (_app_pcollector.is_started()) { + // _app_pcollector.stop(); + //} #endif // Make sure our buffers and windows are fully realized before we render a @@ -799,7 +799,10 @@ render_frame() { // 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); + { + PStatTimer timer(_do_frame_pcollector, current_thread); + _app.do_frame(this, current_thread); + } // Grab each thread's mutex again after all windows have flipped, and wait // for the thread to finish. @@ -854,6 +857,7 @@ render_frame() { // Reset our pcollectors that track data across the frame. CullTraverser::_nodes_pcollector.clear_level(); CullTraverser::_geom_nodes_pcollector.clear_level(); + CullTraverser::_pgui_nodes_pcollector.clear_level(); CullTraverser::_geoms_pcollector.clear_level(); GeomCacheManager::_geom_cache_active_pcollector.clear_level(); GeomCacheManager::_geom_cache_record_pcollector.clear_level(); @@ -954,7 +958,7 @@ render_frame() { // Anything that happens outside of GraphicsEngine::render_frame() is deemed // to be App. - _app_pcollector.start(); + //_app_pcollector.start(); _render_frame_pcollector.stop(); } @@ -2573,7 +2577,6 @@ resort_windows() { */ void GraphicsEngine::WindowRenderer:: do_frame(GraphicsEngine *engine, Thread *current_thread) { - PStatTimer timer(engine->_do_frame_pcollector, current_thread); LightReMutexHolder holder(_wl_lock); engine->cull_to_bins(_cull, current_thread); @@ -2745,8 +2748,11 @@ thread_main() { break; case TS_do_frame: - do_pending(_engine, current_thread); - do_frame(_engine, current_thread); + { + PStatTimer timer(_engine->_do_frame_pcollector, current_thread); + do_pending(_engine, current_thread); + do_frame(_engine, current_thread); + } break; case TS_do_flip: diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index e3e7df66c8..0138eb5cf0 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -358,7 +358,7 @@ private: static PStatCollector _wait_pcollector; static PStatCollector _cycle_pcollector; - static PStatCollector _app_pcollector; + //static PStatCollector _app_pcollector; static PStatCollector _render_frame_pcollector; static PStatCollector _do_frame_pcollector; static PStatCollector _yield_pcollector; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 797dba0fcd..322d77d1cd 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -63,9 +63,9 @@ using std::string; -PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex"); -PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index"); -PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader"); +//PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex"); +//PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index"); +//PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader"); PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer"); PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer"); PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer"); @@ -2376,18 +2376,20 @@ begin_frame(Thread *current_thread) { int frame = ClockObject::get_global_clock()->get_frame_count(); if (_last_query_frame < frame) { _last_query_frame = frame; - _timer_queries_pcollector.clear_level(); + if (pstats_gpu_timing && _supports_timer_query) { + _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(); + // 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); + 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 @@ -3223,8 +3225,9 @@ void GraphicsStateGuardian:: init_frame_pstats() { if (PStatClient::is_connected()) { _data_transferred_pcollector.clear_level(); - _vertex_buffer_switch_pcollector.clear_level(); - _index_buffer_switch_pcollector.clear_level(); + //_vertex_buffer_switch_pcollector.clear_level(); + //_index_buffer_switch_pcollector.clear_level(); + //_shader_buffer_switch_pcollector.clear_level(); _primitive_batches_pcollector.clear_level(); _primitive_batches_tristrip_pcollector.clear_level(); diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index b0f9a323dc..42702d1ed3 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -664,9 +664,9 @@ protected: public: // Statistics - static PStatCollector _vertex_buffer_switch_pcollector; - static PStatCollector _index_buffer_switch_pcollector; - static PStatCollector _shader_buffer_switch_pcollector; + //static PStatCollector _vertex_buffer_switch_pcollector; + //static PStatCollector _index_buffer_switch_pcollector; + //static PStatCollector _shader_buffer_switch_pcollector; static PStatCollector _load_vertex_buffer_pcollector; static PStatCollector _load_index_buffer_pcollector; static PStatCollector _load_shader_buffer_pcollector; diff --git a/panda/src/pgraph/cullTraverser.I b/panda/src/pgraph/cullTraverser.I index 7b243bc7c7..8af8c11c9e 100644 --- a/panda/src/pgraph/cullTraverser.I +++ b/panda/src/pgraph/cullTraverser.I @@ -198,6 +198,7 @@ INLINE void CullTraverser:: flush_level() { _nodes_pcollector.flush_level(); _geom_nodes_pcollector.flush_level(); + _pgui_nodes_pcollector.flush_level(); _geoms_pcollector.flush_level(); _geoms_occluded_pcollector.flush_level(); } diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 1183707240..aa1ea8eaa2 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -38,6 +38,7 @@ PStatCollector CullTraverser::_nodes_pcollector("Nodes"); PStatCollector CullTraverser::_geom_nodes_pcollector("Nodes:GeomNodes"); +PStatCollector CullTraverser::_pgui_nodes_pcollector("Nodes:GUI"); PStatCollector CullTraverser::_geoms_pcollector("Geoms"); PStatCollector CullTraverser::_geoms_occluded_pcollector("Geoms:Occluded"); diff --git a/panda/src/pgraph/cullTraverser.h b/panda/src/pgraph/cullTraverser.h index a243a165cc..77856351b8 100644 --- a/panda/src/pgraph/cullTraverser.h +++ b/panda/src/pgraph/cullTraverser.h @@ -108,6 +108,7 @@ public: // Statistics static PStatCollector _nodes_pcollector; static PStatCollector _geom_nodes_pcollector; + static PStatCollector _pgui_nodes_pcollector; static PStatCollector _geoms_pcollector; static PStatCollector _geoms_occluded_pcollector; diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index 8229f434dc..8c410814ed 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -195,6 +195,8 @@ draw_mask_changed() { */ bool PGItem:: cull_callback(CullTraverser *trav, CullTraverserData &data) { + CullTraverser::_pgui_nodes_pcollector.add_level(1); + // We try not to hold the lock for longer than necessary. PT(PandaNode) state_def_root; bool has_frame; diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 7f4aa84d40..30110fec69 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -109,7 +109,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "App:Collisions:Reset", { 0.0, 0.0, 0.5 } }, { 0, "App:Data graph", { 0.5, 0.8, 0.4 } }, { 1, "App:Show code", { 0.8, 0.2, 1.0 } }, - { 0, "App:Show code:General", { 0.4, 0.3, 0.9 } }, + //{ 0, "App:Show code:General", { 0.4, 0.3, 0.9 } }, { 0, "App:Show code:Nametags", { 0.8, 0.8, 1.0 } }, { 0, "App:Show code:Nametags:2d", { 0.0, 0.0, 0.5 } }, { 0, "App:Show code:Nametags:2d:Contents", { 0.0, 0.5, 0.0 } }, @@ -217,7 +217,7 @@ static LevelCollectorProperties level_properties[] = { { 1, "RenderStates:Cached", { 1.0, 0.0, 0.2 } }, { 1, "RenderStates:Unused", { 0.2, 0.2, 0.2 } }, { 1, "PipelineCyclers", { 0.5, 0.5, 1.0 }, "", 50000 }, - { 1, "Dirty PipelineCyclers", { 0.2, 0.2, 0.2 }, "", 5000 }, + { 1, "PipelineCyclers:Dirty", { 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 }, From c0c5eeb27e447df7eee9a98277d88c5cca252180 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 18 Feb 2022 17:23:30 +0100 Subject: [PATCH 039/166] display: Don't start/stop collectors for empty window list --- panda/src/display/graphicsEngine.cxx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index bc98317a01..ec047a8ac0 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -2579,10 +2579,18 @@ void GraphicsEngine::WindowRenderer:: do_frame(GraphicsEngine *engine, Thread *current_thread) { LightReMutexHolder holder(_wl_lock); - engine->cull_to_bins(_cull, current_thread); - engine->cull_and_draw_together(_cdraw, current_thread); - engine->draw_bins(_draw, current_thread); - engine->process_events(_window, current_thread); + if (!_cull.empty()) { + engine->cull_to_bins(_cull, current_thread); + } + if (!_cdraw.empty()) { + engine->cull_and_draw_together(_cdraw, current_thread); + } + if (!_draw.empty()) { + engine->draw_bins(_draw, current_thread); + } + if (!_window.empty()) { + engine->process_events(_window, current_thread); + } // If any GSG's on the list have no more outstanding pointers, clean them // up. (We are in the draw thread for all of these GSG's.) From 161ac4c2f78723590b2c4fc75d176cbee91399c3 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Feb 2022 17:49:46 +0100 Subject: [PATCH 040/166] pstats: Another major update for PStats server UI, including: - New powerful scrolling Timeline view for seeing all time events across all threads - Redo flame graph to use stack-based nesting rather than the standard collector nesting - Rewrite flame graph drawing to not use labels - Status bar appears in main window showing top-level level collectors; double-clicking them brings up their chart and right-clicking them shows their children - Context menus are added when right-clicking labels and charts - Tooltips now appear when mouse hovers over collector area in a chart - Strip chart windows now automatically determine the appropriate scale better - Graph menus redone to allow opening flame chart anywhere as well as strip chart - Instead of just ms everywhere, also use s / us / ns where appropriate - Don't disable smoothing right away on mouse down on strip chart, only after dragging - Windows: The MDI child windows are quite ugly and overlap with the status bar, so instead they are now top-level windows, but some code is added to make them spawn inside and move with the parent window, and minimize to its corner. I can back this out if people prefer the old behavior despite the ugly decoration - Windows: Label text shows ellipsis when cut off - Windows: Graph windows no longer have icons - Windows: Graph windows no longer spawn perfectly on top of each other, rather cascading - GTK: Render at high resolution when GDK_SCALE is not 1 - GTK: Graph windows are forced to be floating in tiling WMs - GTK: Flame chart window no longer has useless dividing bar - GTK: Use more efficient cairo surface types --- pandatool/src/gtk-stats/CMakeLists.txt | 1 + pandatool/src/gtk-stats/gtkStatsChartMenu.cxx | 148 ++-- pandatool/src/gtk-stats/gtkStatsChartMenu.h | 1 - .../src/gtk-stats/gtkStatsFlameGraph.cxx | 321 ++++--- pandatool/src/gtk-stats/gtkStatsFlameGraph.h | 23 +- pandatool/src/gtk-stats/gtkStatsGraph.cxx | 288 +++++-- pandatool/src/gtk-stats/gtkStatsGraph.h | 59 +- pandatool/src/gtk-stats/gtkStatsLabel.cxx | 18 +- pandatool/src/gtk-stats/gtkStatsMonitor.I | 10 +- pandatool/src/gtk-stats/gtkStatsMonitor.cxx | 234 ++++- pandatool/src/gtk-stats/gtkStatsMonitor.h | 29 +- pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx | 173 +++- pandatool/src/gtk-stats/gtkStatsPianoRoll.h | 14 +- .../src/gtk-stats/gtkStatsStripChart.cxx | 210 +++-- pandatool/src/gtk-stats/gtkStatsStripChart.h | 13 +- pandatool/src/gtk-stats/gtkStatsTimeline.cxx | 812 ++++++++++++++++++ pandatool/src/gtk-stats/gtkStatsTimeline.h | 91 ++ .../src/gtk-stats/gtkstats_composite1.cxx | 1 + pandatool/src/pstatserver/CMakeLists.txt | 14 +- .../pstatserver/p3pstatserver_composite1.cxx | 1 + pandatool/src/pstatserver/pStatFlameGraph.I | 33 +- pandatool/src/pstatserver/pStatFlameGraph.cxx | 432 +++++++--- pandatool/src/pstatserver/pStatFlameGraph.h | 76 +- pandatool/src/pstatserver/pStatGraph.cxx | 38 +- pandatool/src/pstatserver/pStatGraph.h | 1 + pandatool/src/pstatserver/pStatPianoRoll.I | 8 + pandatool/src/pstatserver/pStatPianoRoll.cxx | 14 + pandatool/src/pstatserver/pStatPianoRoll.h | 4 + pandatool/src/pstatserver/pStatStripChart.I | 8 + pandatool/src/pstatserver/pStatStripChart.cxx | 49 +- pandatool/src/pstatserver/pStatStripChart.h | 1 + pandatool/src/pstatserver/pStatThreadData.cxx | 8 + pandatool/src/pstatserver/pStatTimeline.I | 139 +++ pandatool/src/pstatserver/pStatTimeline.cxx | 664 ++++++++++++++ pandatool/src/pstatserver/pStatTimeline.h | 130 +++ pandatool/src/pstatserver/pStatView.cxx | 19 +- pandatool/src/win-stats/CMakeLists.txt | 2 + pandatool/src/win-stats/winStatsChartMenu.cxx | 115 ++- .../src/win-stats/winStatsFlameGraph.cxx | 260 ++++-- pandatool/src/win-stats/winStatsFlameGraph.h | 13 +- pandatool/src/win-stats/winStatsGraph.cxx | 137 ++- pandatool/src/win-stats/winStatsGraph.h | 19 + pandatool/src/win-stats/winStatsLabel.cxx | 22 +- pandatool/src/win-stats/winStatsMonitor.I | 10 +- pandatool/src/win-stats/winStatsMonitor.cxx | 308 ++++++- pandatool/src/win-stats/winStatsMonitor.h | 21 +- pandatool/src/win-stats/winStatsPianoRoll.cxx | 90 +- pandatool/src/win-stats/winStatsPianoRoll.h | 7 +- .../src/win-stats/winStatsStripChart.cxx | 117 ++- pandatool/src/win-stats/winStatsStripChart.h | 3 + pandatool/src/win-stats/winStatsTimeline.cxx | 752 ++++++++++++++++ pandatool/src/win-stats/winStatsTimeline.h | 89 ++ .../src/win-stats/winstats_composite1.cxx | 1 + 53 files changed, 5310 insertions(+), 741 deletions(-) create mode 100644 pandatool/src/gtk-stats/gtkStatsTimeline.cxx create mode 100644 pandatool/src/gtk-stats/gtkStatsTimeline.h create mode 100644 pandatool/src/pstatserver/pStatTimeline.I create mode 100644 pandatool/src/pstatserver/pStatTimeline.cxx create mode 100644 pandatool/src/pstatserver/pStatTimeline.h create mode 100644 pandatool/src/win-stats/winStatsTimeline.cxx create mode 100644 pandatool/src/win-stats/winStatsTimeline.h diff --git a/pandatool/src/gtk-stats/CMakeLists.txt b/pandatool/src/gtk-stats/CMakeLists.txt index f44270de6a..68a10d392f 100644 --- a/pandatool/src/gtk-stats/CMakeLists.txt +++ b/pandatool/src/gtk-stats/CMakeLists.txt @@ -26,6 +26,7 @@ set(GTKSTATS_SOURCES gtkStatsPianoRoll.cxx gtkStatsServer.cxx gtkStatsStripChart.cxx + gtkStatsTimeline.cxx ) composite_sources(gtk-stats GTKSTATS_SOURCES) diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx index 3c23398962..e610eb710f 100644 --- a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx +++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx @@ -88,7 +88,25 @@ do_update() { // Now rebuild the menu with the new set of entries. - // The menu item(s) for the thread's frame time goes first. + if (_thread_index == 0) { + // Timeline goes first. + GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_timeline, false); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Timeline"); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); + + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + + GtkWidget *sep = gtk_separator_menu_item_new(); + gtk_widget_show(sep); + gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep); + } + + // The menu item(s) for the thread's frame time goes second. add_view(_menu, view.get_top_level(), false); bool needs_separator = true; @@ -116,33 +134,22 @@ do_update() { } } - // Also menu items for flame graph and piano roll (following a separator). + // Also menu item for piano roll (following a separator). GtkWidget *sep = gtk_separator_menu_item_new(); gtk_widget_show(sep); gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep); { - GtkStatsMonitor::MenuDef smd(_thread_index, -2, false); - const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); - - GtkWidget *menu_item = gtk_menu_item_new_with_label("Flame Graph"); - gtk_widget_show(menu_item); - gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); - - g_signal_connect_swapped(G_OBJECT(menu_item), "activate", - G_CALLBACK(handle_menu), (void *)(const void *)menu_def); - } - - { - GtkStatsMonitor::MenuDef smd(_thread_index, -1, false); + GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_piano_roll, false); const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll"); gtk_widget_show(menu_item); gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item); - g_signal_connect_swapped(G_OBJECT(menu_item), "activate", - G_CALLBACK(handle_menu), (void *)(const void *)menu_def); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); } } @@ -158,64 +165,83 @@ add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level, const PStatClientData *client_data = _monitor->get_client_data(); std::string collector_name = client_data->get_collector_name(collector); - GtkStatsMonitor::MenuDef smd(_thread_index, collector, show_level); - const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); - - GtkWidget *menu_item = gtk_menu_item_new_with_label(collector_name.c_str()); - gtk_widget_show(menu_item); - gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), menu_item); - - g_signal_connect_swapped(G_OBJECT(menu_item), "activate", - G_CALLBACK(handle_menu), (void *)(const void *)menu_def); - int num_children = view_level->get_num_children(); - if (num_children > 1) { - // If the collector has more than one child, add a menu entry to go - // directly to each of its children. - std::string submenu_name = collector_name + " components"; + if (show_level && num_children == 0) { + // For a level collector without children, no point in making a submenu. + GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); - GtkWidget *submenu_item = gtk_menu_item_new_with_label(submenu_name.c_str()); + GtkWidget *menu_item = gtk_menu_item_new_with_label(collector_name.c_str()); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), menu_item); + + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + return; + } + + GtkWidget *menu; + if (!show_level && collector == 0 && num_children == 0) { + // Root collector without children, just add the options directly to the + // parent menu. + menu = parent_menu; + } + else { + // Create a submenu. + GtkWidget *submenu_item = gtk_menu_item_new_with_label(collector_name.c_str()); gtk_widget_show(submenu_item); gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), submenu_item); - GtkWidget *submenu = gtk_menu_new(); - gtk_widget_show(submenu); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_item), submenu); + menu = gtk_menu_new(); + gtk_widget_show(menu); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_item), menu); + } + + { + GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart"); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + if (!show_level) { + if (collector == 0 && num_children == 0) { + collector = -1; + } + + GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_flame_graph, show_level); + const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph"); + gtk_widget_show(menu_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + if (num_children > 0) { + GtkWidget *sep = gtk_separator_menu_item_new(); + gtk_widget_show(sep); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); // Reverse the order since the menus are listed from the top down; we want // to be visually consistent with the graphs, which list these labels from // the bottom up. for (int c = num_children - 1; c >= 0; c--) { - add_view(submenu, view_level->get_child(c), show_level); + add_view(menu, view_level->get_child(c), show_level); } } } -/** - * Callback when a menu item is selected. - */ -void GtkStatsChartMenu:: -handle_menu(gpointer data) { - const GtkStatsMonitor::MenuDef *menu_def = (GtkStatsMonitor::MenuDef *)data; - GtkStatsMonitor *monitor = menu_def->_monitor; - - if (monitor == nullptr) { - return; - } - - if (menu_def->_collector_index == -2) { - monitor->open_flame_graph(menu_def->_thread_index); - } - else if (menu_def->_collector_index < 0) { - monitor->open_piano_roll(menu_def->_thread_index); - } - else { - monitor->open_strip_chart(menu_def->_thread_index, - menu_def->_collector_index, - menu_def->_show_level); - } -} - /** * Removes a previous menu child from the menu. */ diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.h b/pandatool/src/gtk-stats/gtkStatsChartMenu.h index 6206d951ec..41e21aa3ee 100644 --- a/pandatool/src/gtk-stats/gtkStatsChartMenu.h +++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.h @@ -40,7 +40,6 @@ private: void add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level, bool show_level); - static void handle_menu(gpointer data); static void remove_menu_child(GtkWidget *widget, gpointer data); GtkStatsMonitor *_monitor; diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx index bb52f1374c..d5ecdb0d29 100644 --- a/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx @@ -25,11 +25,8 @@ static const int default_flame_graph_height = 150; GtkStatsFlameGraph:: GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, int collector_index) : - PStatFlameGraph(monitor, monitor->get_view(thread_index), - thread_index, collector_index, - default_flame_graph_width, - default_flame_graph_height), - GtkStatsGraph(monitor) + PStatFlameGraph(monitor, thread_index, collector_index, 0, 0), + GtkStatsGraph(monitor, false) { // Let's show the units on the guide bar labels. There's room. set_guide_bar_units(get_guide_bar_units() | GBU_show_units); @@ -54,13 +51,9 @@ GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0); gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0); - gtk_widget_set_size_request(_graph_window, default_flame_graph_width, - default_flame_graph_height); - - // Add a fixed container to the overlay to allow arbitrary positioning - // of labels therein. - _fixed = gtk_fixed_new(); - gtk_overlay_add_overlay(GTK_OVERLAY(_graph_overlay), _fixed); + gtk_widget_set_size_request(_graph_window, + default_flame_graph_width * monitor->get_resolution() / 96, + default_flame_graph_height * monitor->get_resolution() / 96); gtk_widget_show_all(_window); gtk_widget_show(_window); @@ -71,6 +64,10 @@ GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, gtk_widget_set_size_request(_window, 0, 0); clear_region(); + + if (get_average_mode()) { + start_animation(); + } } /** @@ -119,7 +116,9 @@ new_data(int thread_index, int frame_number) { */ void GtkStatsFlameGraph:: force_redraw() { - PStatFlameGraph::force_redraw(); + if (_cr) { + PStatFlameGraph::force_redraw(); + } } /** @@ -152,34 +151,7 @@ set_time_units(int unit_mask) { */ void GtkStatsFlameGraph:: on_click_label(int collector_index) { - int prev_collector_index = get_collector_index(); - if (collector_index == prev_collector_index && collector_index != 0) { - // Clicking on the top label means to go up to the parent level. - const PStatClientData *client_data = - GtkStatsGraph::_monitor->get_client_data(); - if (client_data->has_collector(collector_index)) { - const PStatCollectorDef &def = - client_data->get_collector_def(collector_index); - collector_index = def._parent_index; - set_collector_index(collector_index); - } - } - else { - // Clicking on any other label means to focus on that. - set_collector_index(collector_index); - } - - // Change the root collector to show the full name. - if (prev_collector_index != collector_index) { - auto it = _labels.find(prev_collector_index); - if (it != _labels.end()) { - it->second->update_text(false); - } - it = _labels.find(collector_index); - if (it != _labels.end()) { - it->second->update_text(true); - } - } + set_collector_index(collector_index); } /** @@ -189,6 +161,10 @@ void GtkStatsFlameGraph:: on_enter_label(int collector_index) { if (collector_index != _highlighted_index) { _highlighted_index = collector_index; + + if (!get_average_mode()) { + PStatFlameGraph::force_redraw(); + } } } @@ -199,54 +175,11 @@ void GtkStatsFlameGraph:: on_leave_label(int collector_index) { if (collector_index == _highlighted_index && collector_index != -1) { _highlighted_index = -1; - } -} -/** - * Called when the mouse hovers over a label, and should return the text that - * should appear on the tooltip. - */ -std::string GtkStatsFlameGraph:: -get_label_tooltip(int collector_index) const { - return PStatFlameGraph::get_label_tooltip(collector_index); -} - -/** - * Repositions the labels. - */ -void GtkStatsFlameGraph:: -update_labels() { - PStatFlameGraph::update_labels(); -} - -/** - * Repositions a label. If width is 0, the label should be deleted. - */ -void GtkStatsFlameGraph:: -update_label(int collector_index, int row, int x, int width) { - GtkStatsLabel *label; - - auto it = _labels.find(collector_index); - if (it != _labels.end()) { - label = it->second; - if (width == 0) { - gtk_container_remove(GTK_CONTAINER(_fixed), label->get_widget()); - delete label; - _labels.erase(it); - return; + if (!get_average_mode()) { + PStatFlameGraph::force_redraw(); } - gtk_fixed_move(GTK_FIXED(_fixed), label->get_widget(), x, _ysize - (row + 1) * label->get_height()); } - else { - if (width == 0) { - return; - } - label = new GtkStatsLabel(GtkStatsGraph::_monitor, this, _thread_index, collector_index, false, false); - _labels[collector_index] = label; - gtk_fixed_put(GTK_FIXED(_fixed), label->get_widget(), x, _ysize - (row + 1) * label->get_height()); - } - - gtk_widget_set_size_request(label->get_widget(), std::min(width, _xsize), label->get_height()); } /** @@ -255,8 +188,7 @@ update_label(int collector_index, int row, int x, int width) { void GtkStatsFlameGraph:: normal_guide_bars() { // We want vaguely 100 pixels between guide bars. - double res = gdk_screen_get_resolution(gdk_screen_get_default()); - int num_bars = (int)(get_xsize() / (100.0 * (res > 0 ? res / 96.0 : 1.0))); + int num_bars = get_xsize() / (_pixel_scale * 25); _guide_bars.clear(); @@ -292,6 +224,63 @@ begin_draw() { } } +/** + * Should be overridden by the user class. Should draw a single bar at the + * indicated location. + */ +void GtkStatsFlameGraph:: +draw_bar(int depth, int from_x, int to_x, int collector_index) { + double bottom = get_ysize() - depth * _pixel_scale * 5; + double top = bottom - _pixel_scale * 5; + + bool is_highlighted = collector_index == _highlighted_index; + cairo_set_source(_cr, get_collector_pattern(collector_index, is_highlighted)); + + if (to_x < from_x + 3) { + // It's just a tiny sliver. This is a more reliable way to draw it. + cairo_rectangle(_cr, from_x, top, to_x - from_x, bottom - top); + cairo_fill(_cr); + } + else { + double radius = std::min((double)_pixel_scale, (to_x - from_x) / 2.0); + cairo_new_sub_path(_cr); + cairo_arc(_cr, to_x - radius, top + radius, radius, -0.5 * M_PI, 0.0); + cairo_arc(_cr, to_x - radius, bottom - radius, radius, 0.0, 0.5 * M_PI); + cairo_arc(_cr, from_x + radius, bottom - radius, radius, 0.5 * M_PI, M_PI); + cairo_arc(_cr, from_x + radius, top + radius, radius, M_PI, 1.5 * M_PI); + cairo_close_path(_cr); + cairo_fill(_cr); + + if ((to_x - from_x) >= _pixel_scale * 4) { + // Only bother drawing the text if we've got some space to draw on. + int left = std::max(from_x, 0) + _pixel_scale / 2; + int right = std::min(to_x, get_xsize()) - _pixel_scale / 2; + + const PStatClientData *client_data = GtkStatsGraph::_monitor->get_client_data(); + const std::string &name = client_data->get_collector_name(collector_index); + + // Choose a suitable foreground color. + LRGBColor fg = get_collector_text_color(collector_index, is_highlighted); + cairo_set_source_rgb(_cr, fg[0], fg[1], fg[2]); + + PangoLayout *layout = gtk_widget_create_pango_layout(_graph_window, name.c_str()); + pango_layout_set_attributes(layout, _pango_attrs); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_width(layout, (right - left) * PANGO_SCALE); + pango_layout_set_height(layout, -1); + + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + + // Center the text vertically in the bar. + cairo_move_to(_cr, left, top + (bottom - top - height) / 2); + pango_cairo_show_layout(_cr, layout); + + g_object_unref(layout); + } + } +} + /** * Called after all the bars have been drawn, this triggers a refresh event to * draw it to the window. @@ -308,6 +297,15 @@ void GtkStatsFlameGraph:: idle() { } +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool GtkStatsFlameGraph:: +animate(double time, double dt) { + return PStatFlameGraph::animate(time, dt); +} + /** * This is called during the servicing of the draw event; it gives a derived * class opportunity to do some further painting into the graph window. @@ -320,6 +318,15 @@ additional_graph_window_paint(cairo_t *cr) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsFlameGraph:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return get_bar_tooltip(pixel_to_depth(mouse_y), mouse_x); +} + /** * Based on the mouse position within the window's client area, look for * draggable things the mouse might be hovering over and return the @@ -352,12 +359,74 @@ consider_drag_start(int graph_x, int graph_y) { * Called when the mouse button is depressed within the graph window. */ gboolean GtkStatsFlameGraph:: -handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click) { +handle_button_press(int graph_x, int graph_y, bool double_click, int button) { if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { - if (double_click) { - // Clicking on whitespace in the graph goes to the parent. - on_click_label(get_collector_index()); + int depth = pixel_to_depth(graph_y); + int collector_index = get_bar_collector(depth, graph_x); + if (button == 3) { + if (collector_index >= 0) { + GtkWidget *menu = gtk_menu_new(); + _popup_index = collector_index; + + std::string label = get_bar_tooltip(depth, graph_x); + if (!label.empty()) { + GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + gtk_widget_set_sensitive(menu_item, FALSE); + } + + { + GtkWidget *menu_item = gtk_menu_item_new_with_label("Set as Focus"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + + if (collector_index == 0 && get_collector_index() == 0) { + gtk_widget_set_sensitive(menu_item, FALSE); + } else { + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(+[] (GtkWidget *widget, gpointer data) { + GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data; + self->set_collector_index(self->_popup_index); + }), + this); + } + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + get_thread_index(), collector_index, + GtkStatsMonitor::CT_strip_chart, false, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + get_thread_index(), collector_index, + GtkStatsMonitor::CT_flame_graph, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + gtk_widget_show_all(menu); + gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr); + return TRUE; + } + return FALSE; + } + else if (double_click && button == 1) { + // Double-clicking on a color bar in the graph will zoom the graph into + // that collector. + set_collector_index(collector_index); return TRUE; } } @@ -375,21 +444,21 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, return TRUE; } - return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y, - double_click); + return GtkStatsGraph::handle_button_press(graph_x, graph_y, + double_click, button); } /** * Called when the mouse button is released within the graph window. */ gboolean GtkStatsFlameGraph:: -handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { +handle_button_release(int graph_x, int graph_y) { if (_drag_mode == DM_scale) { set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); - - } else if (_drag_mode == DM_guide_bar) { + return handle_motion(graph_x, graph_y); + } + else if (_drag_mode == DM_guide_bar) { if (graph_x < 0 || graph_x >= get_xsize()) { remove_user_guide_bar(_drag_guide_bar); } else { @@ -397,17 +466,30 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { } set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); + return handle_motion(graph_x, graph_y); } - return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y); + return GtkStatsGraph::handle_button_release(graph_x, graph_y); } /** * Called when the mouse is moved within the graph window. */ gboolean GtkStatsFlameGraph:: -handle_motion(GtkWidget *widget, int graph_x, int graph_y) { +handle_motion(int graph_x, int graph_y) { + if (_drag_mode == DM_none && _potential_drag_mode == DM_none && + graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + // When the mouse is over a color bar, highlight it. + int depth = pixel_to_depth(graph_y); + int collector_index = get_bar_collector(depth, graph_x); + on_enter_label(collector_index); + } + else { + // If the mouse is in some drag mode, stop highlighting. + _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); + } + if (_drag_mode == DM_new_guide_bar) { // We haven't created the new guide bar yet; we won't until the mouse // comes within the graph's region. @@ -422,7 +504,25 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { return TRUE; } - return GtkStatsGraph::handle_motion(widget, graph_x, graph_y); + return GtkStatsGraph::handle_motion(graph_x, graph_y); +} + +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsFlameGraph:: +handle_leave() { + _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); + return TRUE; +} + +/** + * Converts a pixel to a depth index. + */ +int GtkStatsFlameGraph:: +pixel_to_depth(int y) const { + return (get_ysize() - 1 - y) / (_pixel_scale * 5); } /** @@ -443,7 +543,7 @@ draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -484,7 +584,7 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -492,13 +592,13 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { int x = height_to_pixel(bar._height); const std::string &label = bar._label; - PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str()); + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str()); int width, height; pango_layout_get_pixel_size(layout, &width, &height); if (bar._style != GBS_user) { - double from_height = pixel_to_height(x - width); - double to_height = pixel_to_height(x + width); + double from_height = pixel_to_height(x - width * _cr_scale); + double to_height = pixel_to_height(x + width * _cr_scale); if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); @@ -510,6 +610,8 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { // Now convert our x to a coordinate within our drawing area. int junk_y; + x /= _cr_scale; + // The x coordinate comes from the graph_window. gtk_widget_translate_coordinates(_graph_window, _scale_area, x, 0, @@ -537,6 +639,9 @@ toggled_callback(GtkToggleButton *button, gpointer data) { bool active = gtk_toggle_button_get_active(button); self->set_average_mode(active); + if (active) { + self->start_animation(); + } } /** diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.h b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h index f6e57c3bce..ad550d16ac 100644 --- a/pandatool/src/gtk-stats/gtkStatsFlameGraph.h +++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h @@ -28,7 +28,7 @@ class GtkStatsLabel; class GtkStatsFlameGraph : public PStatFlameGraph, public GtkStatsGraph { public: GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index, - int collector_index=0); + int collector_index=-1); virtual ~GtkStatsFlameGraph(); virtual void new_collector(int collector_index); @@ -40,27 +40,30 @@ public: virtual void on_click_label(int collector_index); virtual void on_enter_label(int collector_index); virtual void on_leave_label(int collector_index); - virtual std::string get_label_tooltip(int collector_index) const; protected: - virtual void update_labels(); - virtual void update_label(int collector_index, int row, int x, int width); virtual void normal_guide_bars(); void clear_region(); virtual void begin_draw(); + virtual void draw_bar(int depth, int from_x, int to_x, int collector_index); virtual void end_draw(); virtual void idle(); + virtual bool animate(double time, double dt); + virtual void additional_graph_window_paint(cairo_t *cr); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int graph_x, int graph_y); - virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click); - virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y); - virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); + virtual gboolean handle_button_press(int graph_x, int graph_y, + bool double_click, int button); + virtual gboolean handle_button_release(int graph_x, int graph_y); + virtual gboolean handle_motion(int graph_x, int graph_y); + virtual gboolean handle_leave(); private: + int pixel_to_depth(int y) const; void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar); void draw_guide_labels(cairo_t *cr); void draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar); @@ -70,12 +73,12 @@ private: private: std::string _net_value_text; - pmap _labels; GtkWidget *_top_hbox; GtkWidget *_average_check_box; GtkWidget *_total_label; - GtkWidget *_fixed; + + int _popup_index = -1; }; #endif diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx index 2734498b12..269b8ec47f 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx +++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx @@ -30,7 +30,7 @@ const double GtkStatsGraph::rgb_user_guide_bar[3] = { * */ GtkStatsGraph:: -GtkStatsGraph(GtkStatsMonitor *monitor) : +GtkStatsGraph(GtkStatsMonitor *monitor, bool has_label_stack) : _monitor(monitor) { _parent_window = nullptr; @@ -40,21 +40,25 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : GtkWidget *parent_window = monitor->get_window(); - GdkDisplay *display = gdk_window_get_display(gtk_widget_get_window(parent_window)); + GdkWindow *window = gtk_widget_get_window(parent_window); + GdkDisplay *display = gdk_window_get_display(window); _hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2); + int scale = gdk_window_get_scale_factor(window); + _pixel_scale = scale * monitor->get_resolution() * 4 / 96; + _cr_surface = nullptr; _cr = nullptr; + _cr_scale = scale; + _pango_attrs = nullptr; _surface_xsize = 0; _surface_ysize = 0; _window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - // These calls were intended to kind of emulate the Windows MDI behavior, - // but it's just weird. gtk_window_set_transient_for(GTK_WINDOW(_window), - // GTK_WINDOW(parent_window)); - // gtk_window_set_destroy_with_parent(GTK_WINDOW(_window), TRUE); + gtk_window_set_type_hint(GTK_WINDOW(_window), GDK_WINDOW_TYPE_HINT_UTILITY); + //gtk_window_set_transient_for(GTK_WINDOW(_window), GTK_WINDOW(parent_window)); + //gtk_window_set_position(GTK_WINDOW(_window), GTK_WIN_POS_CENTER_ON_PARENT); gtk_widget_add_events(_window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | @@ -63,17 +67,17 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : G_CALLBACK(window_delete_event), this); g_signal_connect(G_OBJECT(_window), "destroy", G_CALLBACK(window_destroy), this); - g_signal_connect(G_OBJECT(_window), "button_press_event", - G_CALLBACK(button_press_event_callback), this); - g_signal_connect(G_OBJECT(_window), "button_release_event", - G_CALLBACK(button_release_event_callback), this); - g_signal_connect(G_OBJECT(_window), "motion_notify_event", - G_CALLBACK(motion_notify_event_callback), this); + //g_signal_connect(G_OBJECT(_window), "button_press_event", + // G_CALLBACK(button_press_event_callback), this); + //g_signal_connect(G_OBJECT(_window), "button_release_event", + // G_CALLBACK(button_release_event_callback), this); + //g_signal_connect(G_OBJECT(_window), "motion_notify_event", + // G_CALLBACK(motion_notify_event_callback), this); _graph_window = gtk_drawing_area_new(); gtk_widget_add_events(_graph_window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK); + GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); g_signal_connect(G_OBJECT(_graph_window), "draw", G_CALLBACK(graph_draw_callback), this); g_signal_connect(G_OBJECT(_graph_window), "configure_event", @@ -84,37 +88,41 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : G_CALLBACK(button_release_event_callback), this); g_signal_connect(G_OBJECT(_graph_window), "motion_notify_event", G_CALLBACK(motion_notify_event_callback), this); + g_signal_connect(G_OBJECT(_graph_window), "leave_notify_event", + G_CALLBACK(leave_notify_event_callback), this); + g_signal_connect(G_OBJECT(_graph_window), "query-tooltip", + G_CALLBACK(query_tooltip_callback), this); - // An overlay inside the frame, for charts that want to display widgets on - // top of the graph. - _graph_overlay = gtk_overlay_new(); - gtk_container_add(GTK_CONTAINER(_graph_overlay), _graph_window); + gtk_widget_set_has_tooltip(_graph_window, TRUE); // A Frame to hold the graph. _graph_frame = gtk_frame_new(nullptr); gtk_frame_set_shadow_type(GTK_FRAME(_graph_frame), GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(_graph_frame), _graph_overlay); + gtk_container_add(GTK_CONTAINER(_graph_frame), _graph_window); // A VBox to hold the graph's frame, and any numbers (scale legend? total?) // above it. _graph_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_end(GTK_BOX(_graph_vbox), _graph_frame, - TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(_graph_vbox), _graph_frame, TRUE, TRUE, 0); // An HBox to hold the graph's frame, and the scale legend to the right of // it. _graph_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox, - TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox, TRUE, TRUE, 0); // An HPaned to hold the label stack and the graph hbox. - _hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); - gtk_paned_set_wide_handle(GTK_PANED(_hpaned), TRUE); - gtk_container_add(GTK_CONTAINER(_window), _hpaned); - gtk_container_set_border_width(GTK_CONTAINER(_window), 8); + if (has_label_stack) { + _hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + gtk_paned_set_wide_handle(GTK_PANED(_hpaned), TRUE); + gtk_container_add(GTK_CONTAINER(_window), _hpaned); + gtk_container_set_border_width(GTK_CONTAINER(_window), 8); - gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), FALSE, FALSE); - gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE); + gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), FALSE, FALSE); + gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE); + } + else { + gtk_container_add(GTK_CONTAINER(_window), _graph_hbox); + } _drag_mode = DM_none; _potential_drag_mode = DM_none; @@ -128,6 +136,11 @@ GtkStatsGraph(GtkStatsMonitor *monitor) : */ GtkStatsGraph:: ~GtkStatsGraph() { + if (_timer_id != 0) { + gtk_widget_remove_tick_callback(_graph_window, _timer_id); + _timer_id = 0; + } + _monitor = nullptr; release_surface(); @@ -136,9 +149,15 @@ GtkStatsGraph:: cairo_pattern_destroy(item.second.second); } _brushes.clear(); + _text_colors.clear(); _label_stack.clear_labels(); + if (_pango_attrs != nullptr) { + pango_attr_list_unref(_pango_attrs); + _pango_attrs = nullptr; + } + if (_window != nullptr) { GtkWidget *window = _window; _window = nullptr; @@ -211,6 +230,13 @@ void GtkStatsGraph:: on_click_label(int collector_index) { } +/** + * Called when a pop-up menu should be shown for the label. + */ +void GtkStatsGraph:: +on_popup_label(int collector_index) { +} + /** * Called when the user hovers the mouse over a label. */ @@ -260,6 +286,29 @@ close() { } } +/** + * Turns on the animation timer, if it hasn't already been turned on. + */ +void GtkStatsGraph:: +start_animation() { + if (_timer_id != 0) { + return; + } + + _time = 0; + _timer_id = gtk_widget_add_tick_callback(_graph_window, tick_callback, + this, nullptr); +} + +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool GtkStatsGraph:: +animate(double time, double dt) { + return false; +} + /** * Returns a pattern suitable for drawing in the indicated collector's color. */ @@ -286,6 +335,29 @@ get_collector_pattern(int collector_index, bool highlight) { return highlight ? hpattern : pattern; } +/** + * Returns a text color suitable for the given collector. + */ +LRGBColor GtkStatsGraph:: +get_collector_text_color(int collector_index, bool highlight) { + TextColors::iterator tci; + tci = _text_colors.find(collector_index); + if (tci != _text_colors.end()) { + return highlight ? (*tci).second.second : (*tci).second.first; + } + + LRGBColor rgb = _monitor->get_collector_color(collector_index); + double bright = + rgb[0] * 0.2126 + + rgb[1] * 0.7152 + + rgb[2] * 0.0722; + LRGBColor color = bright >= 0.5 ? LRGBColor(0) : LRGBColor(1); + LRGBColor hcolor = bright * 0.75 >= 0.5 ? LRGBColor(0) : LRGBColor(1); + + _text_colors[collector_index] = std::make_pair(color, hcolor); + return highlight ? hcolor : color; +} + /** * This is called during the servicing of the draw event; it gives a derived * class opportunity to do some further painting into the graph window. @@ -294,6 +366,15 @@ void GtkStatsGraph:: additional_graph_window_paint(cairo_t *cr) { } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsGraph:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return std::string(); +} + /** * Based on the mouse position within the graph window, look for draggable * things the mouse might be hovering over and return the appropriate DragMode @@ -318,9 +399,8 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) { * window. */ gboolean GtkStatsGraph:: -handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click) { - if (_potential_drag_mode != DM_none) { +handle_button_press(int graph_x, int graph_y, bool double_click, int button) { + if (_potential_drag_mode != DM_none && button == 1) { set_drag_mode(_potential_drag_mode); _drag_start_x = graph_x; _drag_start_y = graph_y; @@ -334,18 +414,18 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, * window. */ gboolean GtkStatsGraph:: -handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { +handle_button_release(int graph_x, int graph_y) { set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); + return handle_motion(graph_x, graph_y); } /** * Called when the mouse is moved within the window, or any nested window. */ gboolean GtkStatsGraph:: -handle_motion(GtkWidget *widget, int graph_x, int graph_y) { +handle_motion(int graph_x, int graph_y) { _potential_drag_mode = consider_drag_start(graph_x, graph_y); GdkWindow *window = gtk_widget_get_window(_window); @@ -353,29 +433,47 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { if (_potential_drag_mode == DM_guide_bar || _drag_mode == DM_guide_bar) { gdk_window_set_cursor(window, _hand_cursor); - - } else { + } + else { gdk_window_set_cursor(window, nullptr); } return TRUE; } +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsGraph:: +handle_leave() { + return FALSE; +} + /** * Sets up a backing-store bitmap of the indicated size. */ void GtkStatsGraph:: -setup_surface(int xsize, int ysize) { +setup_surface(int xsize, int ysize, int scale) { release_surface(); - _surface_xsize = std::max(xsize, 0); - _surface_ysize = std::max(ysize, 0); + _surface_xsize = xsize; + _surface_ysize = ysize; + _pixel_scale = scale * _monitor->get_resolution() * 4 / 96; - _cr_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, _surface_xsize, _surface_ysize); + GdkWindow *window = gtk_widget_get_window(_graph_window); + _cr_surface = gdk_window_create_similar_image_surface(window, CAIRO_FORMAT_RGB24, _surface_xsize, _surface_ysize, 1); _cr = cairo_create(_cr_surface); + _cr_scale = scale; cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); cairo_paint(_cr); + + // Cache the font scale attribute. + _pango_attrs = pango_attr_list_new(); + PangoAttribute *attr = pango_attr_scale_new(scale * 0.9); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert(_pango_attrs, attr); } /** @@ -386,6 +484,13 @@ release_surface() { if (_cr_surface != nullptr) { cairo_surface_destroy(_cr_surface); cairo_destroy(_cr); + _cr_surface = nullptr; + _cr = nullptr; + } + + if (_pango_attrs != nullptr) { + pango_attr_list_unref(_pango_attrs); + _pango_attrs = nullptr; } } @@ -416,6 +521,8 @@ graph_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; if (self->_cr_surface != nullptr) { + double scale = 1.0 / self->_cr_scale; + cairo_scale(cr, scale, scale); cairo_set_source_surface(cr, self->_cr_surface, 0, 0); cairo_paint(cr); } @@ -430,12 +537,21 @@ graph_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { */ gboolean GtkStatsGraph:: configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event, - gpointer data) { + gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; - self->changed_graph_size(event->width, event->height); - self->setup_surface(event->width, event->height); - self->force_redraw(); + GdkWindow *window = gtk_widget_get_window(widget); + int scale = gdk_window_get_scale_factor(window); + int scaled_xsize = std::max(event->width * scale, 0); + int scaled_ysize = std::max(event->height * scale, 0); + + if (self->_cr == nullptr || + self->_cr_scale != scale || + self->_surface_xsize != scaled_xsize || + self->_surface_ysize != scaled_ysize) { + self->setup_surface(scaled_xsize, scaled_ysize, scale); + self->changed_graph_size(scaled_xsize, scaled_ysize); + } return TRUE; } @@ -446,16 +562,19 @@ configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event, */ gboolean GtkStatsGraph:: button_press_event_callback(GtkWidget *widget, GdkEventButton *event, - gpointer data) { + gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; int graph_x, graph_y; gtk_widget_translate_coordinates(widget, self->_graph_window, - (int)event->x, (int)event->y, - &graph_x, &graph_y); + (int)event->x, (int)event->y, + &graph_x, &graph_y); + graph_x *= self->_cr_scale; + graph_y *= self->_cr_scale; bool double_click = (event->type == GDK_2BUTTON_PRESS); - return self->handle_button_press(widget, graph_x, graph_y, double_click); + return self->handle_button_press(graph_x, graph_y, + double_click, event->button); } /** @@ -464,14 +583,16 @@ button_press_event_callback(GtkWidget *widget, GdkEventButton *event, */ gboolean GtkStatsGraph:: button_release_event_callback(GtkWidget *widget, GdkEventButton *event, - gpointer data) { + gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; int graph_x, graph_y; gtk_widget_translate_coordinates(widget, self->_graph_window, - (int)event->x, (int)event->y, - &graph_x, &graph_y); + (int)event->x, (int)event->y, + &graph_x, &graph_y); + graph_x *= self->_cr_scale; + graph_y *= self->_cr_scale; - return self->handle_button_release(widget, graph_x, graph_y); + return self->handle_button_release(graph_x, graph_y); } /** @@ -479,12 +600,65 @@ button_release_event_callback(GtkWidget *widget, GdkEventButton *event, */ gboolean GtkStatsGraph:: motion_notify_event_callback(GtkWidget *widget, GdkEventMotion *event, - gpointer data) { + gpointer data) { GtkStatsGraph *self = (GtkStatsGraph *)data; int graph_x, graph_y; gtk_widget_translate_coordinates(widget, self->_graph_window, - (int)event->x, (int)event->y, - &graph_x, &graph_y); + (int)event->x, (int)event->y, + &graph_x, &graph_y); + graph_x *= self->_cr_scale; + graph_y *= self->_cr_scale; - return self->handle_motion(widget, graph_x, graph_y); + return self->handle_motion(graph_x, graph_y); +} + +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsGraph:: +leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event, + gpointer data) { + GtkStatsGraph *self = (GtkStatsGraph *)data; + return self->handle_leave(); +} + +/** + * Called when a tooltip should be displayed. + */ +gboolean GtkStatsGraph:: +query_tooltip_callback(GtkWidget *widget, gint x, gint y, + gboolean keyboard_tip, GtkTooltip *tooltip, + gpointer data) { + GtkStatsGraph *self = (GtkStatsGraph *)data; + x *= self->_cr_scale; + y *= self->_cr_scale; + + std::string text = self->get_graph_tooltip(x, y); + gtk_tooltip_set_text(tooltip, text.c_str()); + return !text.empty(); +} + +/** + * Called to update the animations. + */ +gboolean GtkStatsGraph:: +tick_callback(GtkWidget *widget, GdkFrameClock *clock, gpointer data) { + GtkStatsGraph *graph = (GtkStatsGraph *)data; + gint64 new_time = gdk_frame_clock_get_frame_time(clock); + if (graph->_time == 0) { + // First frame, so we don't have a dt yet. + graph->_time = new_time; + return TRUE; + } + gint64 delta = new_time - graph->_time; + if (delta == 0) { + return TRUE; + } + if (graph->animate(new_time / 1000000.0, delta / 1000000.0)) { + graph->_time = new_time; + return TRUE; + } else { + graph->_timer_id = 0; + return FALSE; + } } diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.h b/pandatool/src/gtk-stats/gtkStatsGraph.h index 3a3f123faf..2ff48cecca 100644 --- a/pandatool/src/gtk-stats/gtkStatsGraph.h +++ b/pandatool/src/gtk-stats/gtkStatsGraph.h @@ -17,6 +17,7 @@ #include "pandatoolbase.h" #include "gtkStatsLabelStack.h" #include "pmap.h" +#include "luse.h" #include #include @@ -36,10 +37,11 @@ public: DM_guide_bar, DM_new_guide_bar, DM_sizing, + DM_pan, }; public: - GtkStatsGraph(GtkStatsMonitor *monitor); + GtkStatsGraph(GtkStatsMonitor *monitor, bool has_label_stack); virtual ~GtkStatsGraph(); virtual void new_collector(int collector_index); @@ -53,33 +55,43 @@ public: void user_guide_bars_changed(); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); virtual void on_enter_label(int collector_index); virtual void on_leave_label(int collector_index); virtual std::string get_label_tooltip(int collector_index) const; protected: void close(); + + void start_animation(); + virtual bool animate(double time, double dt); + cairo_pattern_t *get_collector_pattern(int collector_index, bool highlight = false); + LRGBColor get_collector_text_color(int collector_index, bool highlight = false); virtual void additional_graph_window_paint(cairo_t *cr); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int graph_x, int graph_y); virtual void set_drag_mode(DragMode drag_mode); - virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click); - virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y); - virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); + virtual gboolean handle_button_press(int graph_x, int graph_y, + bool double_click, int button); + virtual gboolean handle_button_release(int graph_x, int graph_y); + virtual gboolean handle_motion(int graph_x, int graph_y); + virtual gboolean handle_leave(); protected: // Table of patterns for our various collectors. typedef pmap > Brushes; Brushes _brushes; + typedef pmap > TextColors; + TextColors _text_colors; + GtkStatsMonitor *_monitor; GtkWidget *_parent_window; GtkWidget *_window; GtkWidget *_graph_frame; - GtkWidget *_graph_overlay; GtkWidget *_graph_window; GtkWidget *_graph_hbox; GtkWidget *_graph_vbox; @@ -92,6 +104,9 @@ protected: cairo_surface_t *_cr_surface; cairo_t *_cr; int _surface_xsize, _surface_ysize; + PangoAttrList *_pango_attrs; + int _cr_scale; + int _pixel_scale; DragMode _drag_mode; DragMode _potential_drag_mode; @@ -103,6 +118,9 @@ protected: bool _pause; + guint _timer_id = 0; + gint64 _time = 0; + static const double rgb_white[3]; static const double rgb_light_gray[3]; static const double rgb_dark_gray[3]; @@ -110,27 +128,36 @@ protected: static const double rgb_user_guide_bar[3]; private: - void setup_surface(int xsize, int ysize); + void setup_surface(int xsize, int ysize, int scale); void release_surface(); static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event, - gpointer data); + gpointer data); static void window_destroy(GtkWidget *widget, gpointer data); static gboolean graph_draw_callback(GtkWidget *widget, - cairo_t *cr, gpointer data); + cairo_t *cr, gpointer data); static gboolean configure_graph_callback(GtkWidget *widget, - GdkEventConfigure *event, gpointer data); + GdkEventConfigure *event, + gpointer data); protected: static gboolean button_press_event_callback(GtkWidget *widget, - GdkEventButton *event, - gpointer data); + GdkEventButton *event, + gpointer data); static gboolean button_release_event_callback(GtkWidget *widget, - GdkEventButton *event, - gpointer data); + GdkEventButton *event, + gpointer data); static gboolean motion_notify_event_callback(GtkWidget *widget, - GdkEventMotion *event, - gpointer data); + GdkEventMotion *event, + gpointer data); + static gboolean leave_notify_event_callback(GtkWidget *widget, + GdkEventCrossing *event, + gpointer data); + static gboolean query_tooltip_callback(GtkWidget *widget, gint x, gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip, gpointer data); + static gboolean tick_callback(GtkWidget *widget, GdkFrameClock *clock, + gpointer data); }; #endif diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx index 9db4c8cd9c..7dae836b99 100644 --- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx +++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx @@ -50,7 +50,6 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, G_CALLBACK(query_tooltip_callback), this); gtk_widget_set_has_tooltip(_widget, TRUE); - gtk_widget_show(_widget); // Set the fg and bg colors on the label. LRGBColor rgb = _monitor->get_collector_color(_collector_index); @@ -71,7 +70,7 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, } else { _fg_color = LRGBColor(1); } - if (bright >= 0.5 * 0.75) { + if (bright * 0.75 >= 0.5) { _highlight_fg_color = LRGBColor(0); } else { _highlight_fg_color = LRGBColor(1); @@ -81,6 +80,7 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph, _mouse_within = false; update_text(use_fullname); + gtk_widget_show_all(_widget); } /** @@ -237,7 +237,7 @@ enter_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event, */ gboolean GtkStatsLabel:: leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event, - gpointer data) { + gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; self->set_mouse_within(false); return TRUE; @@ -248,12 +248,14 @@ leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event, */ gboolean GtkStatsLabel:: button_press_event_callback(GtkWidget *widget, GdkEventButton *event, - gpointer data) { + gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; - bool double_click = (event->type == GDK_2BUTTON_PRESS); - if (double_click) { + if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { self->_graph->on_click_label(self->_collector_index); } + else if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + self->_graph->on_popup_label(self->_collector_index); + } return TRUE; } @@ -262,8 +264,8 @@ button_press_event_callback(GtkWidget *widget, GdkEventButton *event, */ gboolean GtkStatsLabel:: query_tooltip_callback(GtkWidget *widget, gint x, gint y, - gboolean keyboard_tip, GtkTooltip *tooltip, - gpointer data) { + gboolean keyboard_tip, GtkTooltip *tooltip, + gpointer data) { GtkStatsLabel *self = (GtkStatsLabel *)data; std::string text = self->_graph->get_label_tooltip(self->_collector_index); diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.I b/pandatool/src/gtk-stats/gtkStatsMonitor.I index 65a5bfbf64..b3e8e55b13 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.I +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.I @@ -14,10 +14,11 @@ /** * */ -GtkStatsMonitor::MenuDef:: -MenuDef(int thread_index, int collector_index, bool show_level) : +INLINE GtkStatsMonitor::MenuDef:: +MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) : _thread_index(thread_index), _collector_index(collector_index), + _chart_type(chart_type), _show_level(show_level), _monitor(nullptr) { @@ -26,7 +27,7 @@ MenuDef(int thread_index, int collector_index, bool show_level) : /** * */ -bool GtkStatsMonitor::MenuDef:: +INLINE bool GtkStatsMonitor::MenuDef:: operator < (const MenuDef &other) const { if (_thread_index != other._thread_index) { return _thread_index < other._thread_index; @@ -34,5 +35,8 @@ operator < (const MenuDef &other) const { if (_collector_index != other._collector_index) { return _collector_index < other._collector_index; } + if (_chart_type != other._chart_type) { + return _chart_type < other._chart_type; + } return (int)_show_level < (int)other._show_level; } diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index c215397339..ff41ed3a3f 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -18,10 +18,10 @@ #include "gtkStatsChartMenu.h" #include "gtkStatsPianoRoll.h" #include "gtkStatsFlameGraph.h" +#include "gtkStatsTimeline.h" #include "gtkStatsMenuId.h" #include "pStatGraph.h" #include "pStatCollectorDef.h" -#include "indent.h" /** * @@ -34,6 +34,8 @@ GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) { _time_units = 0; _scroll_speed = 0.0; _pause = false; + + _resolution = gdk_screen_get_resolution(gdk_screen_get_default()); } /** @@ -154,11 +156,13 @@ new_thread(int thread_index) { */ void GtkStatsMonitor:: new_data(int thread_index, int frame_number) { - Graphs::iterator gi; - for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) { - GtkStatsGraph *graph = (*gi); + for (GtkStatsGraph *graph : _graphs) { graph->new_data(thread_index, frame_number); } + + if (thread_index == 0) { + update_status_bar(); + } } /** @@ -193,6 +197,10 @@ idle() { sprintf(buffer, "%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate); gtk_label_set_text(GTK_LABEL(_frame_rate_label), buffer); + + if (!_status_bar_labels.empty()) { + gtk_label_set_text(GTK_LABEL(_status_bar_labels[0]), buffer); + } } } @@ -225,6 +233,14 @@ get_window() const { return _window; } +/** + * Returns the screen DPI. + */ +double GtkStatsMonitor:: +get_resolution() const { + return _resolution; +} + /** * Opens a new strip chart showing the indicated data. */ @@ -256,8 +272,21 @@ open_piano_roll(int thread_index) { * Opens a new flame graph showing the indicated data. */ void GtkStatsMonitor:: -open_flame_graph(int thread_index) { - GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index); +open_flame_graph(int thread_index, int collector_index) { + GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index); + add_graph(graph); + + graph->set_time_units(_time_units); + graph->set_scroll_speed(_scroll_speed); + graph->set_pause(_pause); +} + +/** + * Opens a new timeline. + */ +void GtkStatsMonitor:: +open_timeline() { + GtkStatsTimeline *graph = new GtkStatsTimeline(this); add_graph(graph); graph->set_time_units(_time_units); @@ -371,7 +400,7 @@ create_window() { gtk_window_set_default_size(GTK_WINDOW(_window), 500, 360); // Set up the menu. - GtkAccelGroup *accel_group = gtk_accel_group_new(); + GtkAccelGroup *accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(_window), accel_group); _menu_bar = gtk_menu_bar_new(); _next_chart_index = 2; @@ -390,8 +419,21 @@ create_window() { gtk_container_add(GTK_CONTAINER(_window), main_vbox); gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0); + // Create the status bar. + _status_bar = gtk_flow_box_new(); + gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(_status_bar), FALSE); + gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(_status_bar), GTK_SELECTION_NONE); + g_signal_connect(G_OBJECT(_status_bar), "button_press_event", + G_CALLBACK(status_bar_button_event), this); + gtk_box_pack_end(GTK_BOX(main_vbox), _status_bar, FALSE, FALSE, 0); + update_status_bar(); + + GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_end(GTK_BOX(main_vbox), sep, FALSE, FALSE, 0); + gtk_widget_show_all(_window); gtk_widget_show(_window); + gtk_widget_realize(_window); } /** @@ -572,6 +614,7 @@ setup_frame_rate_label() { _frame_rate_menu_item = gtk_menu_item_new(); _frame_rate_label = gtk_label_new(""); gtk_container_add(GTK_CONTAINER(_frame_rate_menu_item), _frame_rate_label); + gtk_widget_set_sensitive(_frame_rate_menu_item, FALSE); gtk_widget_show(_frame_rate_menu_item); gtk_widget_show(_frame_rate_label); @@ -579,3 +622,180 @@ setup_frame_rate_label() { gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _frame_rate_menu_item); } + +/** + * Updates the status bar. + */ +void GtkStatsMonitor:: +update_status_bar() { + const PStatClientData *client_data = get_client_data(); + if (client_data == nullptr) { + return; + } + + const PStatThreadData *thread_data = get_client_data()->get_thread_data(0); + if (thread_data == nullptr || thread_data->is_empty()) { + return; + } + const PStatFrameData &frame_data = thread_data->get_latest_frame(); + + pvector collectors; + + // The first label displays the frame rate. + size_t li = 1; + collectors.push_back(0); + if (_status_bar_labels.empty()) { + GtkWidget *label = gtk_label_new(""); + gtk_container_add(GTK_CONTAINER(_status_bar), label); + _status_bar_labels.push_back(label); + } + + // Gather the top-level collector list. + int num_toplevel_collectors = client_data->get_num_toplevel_collectors(); + for (int tc = 0; tc < num_toplevel_collectors; tc++) { + int collector = client_data->get_toplevel_collector(tc); + if (client_data->has_collector(collector) && + client_data->get_collector_has_level(collector, 0)) { + PStatView &view = get_level_view(collector, 0); + view.set_to_frame(frame_data); + double value = view.get_net_value(); + if (value == 0.0) { + // Don't include it unless we've included it before. + if (std::find(_status_bar_collectors.begin(), _status_bar_collectors.end(), collector) == _status_bar_collectors.end()) { + continue; + } + } + + const PStatCollectorDef &def = client_data->get_collector_def(collector); + std::string text = def._name; + text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); + + GtkWidget *label; + if (li < _status_bar_labels.size()) { + label = _status_bar_labels[li++]; + gtk_label_set_text(GTK_LABEL(label), text.c_str()); + } + else { + label = gtk_label_new(text.c_str()); + gtk_container_add(GTK_CONTAINER(_status_bar), label); + _status_bar_labels.push_back(label); + } + + collectors.push_back(collector); + } + } + + _status_bar_collectors = std::move(collectors); + + gtk_widget_show_all(_status_bar); +} + +/** + * Handles clicks on a partion of the status bar. + */ +gboolean GtkStatsMonitor:: +status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { + GtkStatsMonitor *monitor = (GtkStatsMonitor *)data; + + GtkFlowBoxChild *child = gtk_flow_box_get_child_at_pos( + GTK_FLOW_BOX(monitor->_status_bar), event->x, event->y); + if (child == nullptr) { + return FALSE; + } + + // Which child is this? + GList *children = gtk_container_get_children(GTK_CONTAINER(monitor->_status_bar)); + int index = g_list_index(children, child); + g_list_free(children); + if (index < 0 || index >= monitor->_status_bar_labels.size()) { + return FALSE; + } + + const PStatClientData *client_data = monitor->get_client_data(); + if (client_data == nullptr) { + return FALSE; + } + + int collector = monitor->_status_bar_collectors[index]; + + if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { + monitor->open_strip_chart(0, collector, collector != 0); + return TRUE; + } + else if (event->type == GDK_BUTTON_PRESS && event->button == 3 && index > 0) { + PStatView &level_view = monitor->get_level_view(collector, 0); + const PStatViewLevel *view_level = level_view.get_top_level(); + int num_children = view_level->get_num_children(); + if (num_children == 0) { + return FALSE; + } + + GtkWidget *menu = gtk_menu_new(); + + // Reverse the order since the menus are listed from the top down; we want + // to be visually consistent with the graphs, which list these labels from + // the bottom up. + for (int c = num_children - 1; c >= 0; c--) { + const PStatViewLevel *child_level = view_level->get_child(c); + + int child_collector = child_level->get_collector(); + const MenuDef *menu_def = monitor->add_menu({0, child_collector, CT_strip_chart, true}); + + double value = child_level->get_net_value(); + + const PStatCollectorDef &def = client_data->get_collector_def(child_collector); + std::string text = def._name; + text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); + + GtkWidget *menu_item = gtk_menu_item_new_with_label(text.c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(menu_activate), + (void *)menu_def); + } + + gtk_widget_show_all(menu); + + GtkWidget *label = monitor->_status_bar_labels[index]; + gtk_menu_popup_at_widget(GTK_MENU(menu), label, + GDK_GRAVITY_NORTH_WEST, + GDK_GRAVITY_SOUTH_WEST, nullptr); + return TRUE; + } + return FALSE; +} + +/** + * Callback when a menu item is selected. + */ +void GtkStatsMonitor:: +menu_activate(GtkWidget *widget, gpointer data) { + const MenuDef *menu_def = (const MenuDef *)data; + GtkStatsMonitor *monitor = menu_def->_monitor; + + if (monitor == nullptr) { + return; + } + + switch (menu_def->_chart_type) { + case CT_timeline: + monitor->open_timeline(); + break; + + case CT_strip_chart: + monitor->open_strip_chart(menu_def->_thread_index, + menu_def->_collector_index, + menu_def->_show_level); + break; + + case CT_flame_graph: + monitor->open_flame_graph(menu_def->_thread_index, + menu_def->_collector_index); + break; + + case CT_piano_roll: + monitor->open_piano_roll(menu_def->_thread_index); + break; + } +} diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.h b/pandatool/src/gtk-stats/gtkStatsMonitor.h index a79a6ad0ed..99f1baecee 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.h +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.h @@ -34,13 +34,22 @@ class GtkStatsChartMenu; */ class GtkStatsMonitor : public PStatMonitor { public: + enum ChartType { + CT_timeline, + CT_strip_chart, + CT_flame_graph, + CT_piano_roll, + }; + class MenuDef { public: - INLINE MenuDef(int thread_index, int collector_index, bool show_level); + INLINE MenuDef(int thread_index, int collector_index, + ChartType chart_type, bool show_level = false); INLINE bool operator < (const MenuDef &other) const; int _thread_index; int _collector_index; + ChartType _chart_type; bool _show_level; GtkStatsMonitor *_monitor; }; @@ -64,9 +73,12 @@ public: virtual void user_guide_bars_changed(); GtkWidget *get_window() const; + double get_resolution() const; + void open_strip_chart(int thread_index, int collector_index, bool show_level); void open_piano_roll(int thread_index); - void open_flame_graph(int thread_index); + void open_flame_graph(int thread_index, int collector_index = -1); + void open_timeline(); const MenuDef *add_menu(const MenuDef &menu_def); @@ -86,7 +98,16 @@ private: void setup_options_menu(); void setup_speed_menu(); void setup_frame_rate_label(); + void update_status_bar(); + bool show_popup_menu(int collector); + static gboolean status_bar_button_event(GtkWidget *widget, + GdkEventButton *event, + gpointer data); +public: + static void menu_activate(GtkWidget *widget, gpointer data); + +private: typedef pset Graphs; Graphs _graphs; @@ -103,10 +124,14 @@ private: int _next_chart_index; GtkWidget *_frame_rate_menu_item; GtkWidget *_frame_rate_label; + GtkWidget *_status_bar; + pvector _status_bar_collectors; + pvector _status_bar_labels; std::string _window_title; int _time_units; double _scroll_speed; bool _pause; + double _resolution; friend class GtkStatsGraph; }; diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx index 3f0bc320fa..4c86269f02 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx @@ -16,18 +16,16 @@ #include "numeric_types.h" #include "gtkStatsLabelStack.h" -static const int default_piano_roll_width = 600; -static const int default_piano_roll_height = 200; +static const int default_piano_roll_width = 800; +static const int default_piano_roll_height = 400; /** * */ GtkStatsPianoRoll:: GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) : - PStatPianoRoll(monitor, thread_index, - default_piano_roll_width, - default_piano_roll_height), - GtkStatsGraph(monitor) + PStatPianoRoll(monitor, thread_index, 0, 0), + GtkStatsGraph(monitor, true) { // Let's show the units on the guide bar labels. There's room. set_guide_bar_units(get_guide_bar_units() | GBU_show_units); @@ -36,21 +34,21 @@ GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) : // units. _scale_area = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(_scale_area), "draw", - G_CALLBACK(draw_callback), this); - gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, - FALSE, FALSE, 0); + G_CALLBACK(draw_callback), this); + gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0); // It should be large enough to display the labels. { - PangoLayout *layout = gtk_widget_create_pango_layout(_window, "0123456789 ms"); + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "0123456789 ms"); int width, height; pango_layout_get_pixel_size(layout, &width, &height); gtk_widget_set_size_request(_scale_area, 0, height + 1); g_object_unref(layout); } - gtk_widget_set_size_request(_graph_window, default_piano_roll_width, - default_piano_roll_height); + gtk_widget_set_size_request(_graph_window, + default_piano_roll_width * monitor->get_resolution() / 96, + default_piano_roll_height * monitor->get_resolution() / 96); const PStatClientData *client_data = GtkStatsGraph::_monitor->get_client_data(); @@ -77,7 +75,7 @@ GtkStatsPianoRoll:: } /** - * Called as each frame's data is made available. There is no gurantee the + * Called as each frame's data is made available. There is no guarantee the * frames will arrive in order, or that all of them will arrive at all. The * monitor should be prepared to accept frames received out-of-order or * missing. @@ -94,7 +92,9 @@ new_data(int thread_index, int frame_number) { */ void GtkStatsPianoRoll:: force_redraw() { - PStatPianoRoll::force_redraw(); + if (_cr) { + PStatPianoRoll::force_redraw(); + } } /** @@ -132,6 +132,57 @@ on_click_label(int collector_index) { } } +/** + * Called when the user right-clicks on a label. + */ +void GtkStatsPianoRoll:: +on_popup_label(int collector_index) { + GtkWidget *menu = gtk_menu_new(); + + std::string label = get_label_tooltip(collector_index); + if (!label.empty()) { + GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + gtk_widget_set_sensitive(menu_item, FALSE); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + _thread_index, collector_index, GtkStatsMonitor::CT_strip_chart, false, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + gtk_widget_show_all(menu); + gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr); +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsPianoRoll:: +get_label_tooltip(int collector_index) const { + return PStatPianoRoll::get_label_tooltip(collector_index); +} + /** * Changes the amount of time the width of the horizontal axis represents. * This may force a redraw. @@ -188,7 +239,7 @@ draw_bar(int row, int from_x, int to_x) { int y = _label_stack.get_label_y(row, _graph_window); int height = _label_stack.get_label_height(row); - cairo_rectangle(_cr, from_x, y - height + 2, to_x - from_x, height - 4); + cairo_rectangle(_cr, from_x, (y - height + 2) * _cr_scale, to_x - from_x, (height - 4) * _cr_scale); cairo_fill(_cr); } } @@ -224,6 +275,19 @@ additional_graph_window_paint(cairo_t *cr) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsPianoRoll:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + int collector_index = get_collector_under_pixel(mouse_x, mouse_y); + if (collector_index >= 0) { + return get_label_tooltip(collector_index); + } + return std::string(); +} + /** * Based on the mouse position within the graph window, look for draggable * things the mouse might be hovering over and return the appropriate DragMode @@ -256,13 +320,24 @@ consider_drag_start(int graph_x, int graph_y) { * Called when the mouse button is depressed within the graph window. */ gboolean GtkStatsPianoRoll:: -handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click) { - if (double_click) { - // Double-clicking on a color bar in the graph is the same as double- - // clicking on the corresponding label. - on_click_label(get_collector_under_pixel(graph_x, graph_y)); - return TRUE; +handle_button_press(int graph_x, int graph_y, bool double_click, int button) { + if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + int collector_index = get_collector_under_pixel(graph_x, graph_y); + if (button == 3) { + // Right-clicking on a color bar in the graph is the same as right- + // clicking on the corresponding label. + if (collector_index >= 0) { + on_popup_label(collector_index); + return TRUE; + } + return FALSE; + } + else if (double_click && button == 1) { + // Double-clicking on a color bar in the graph is the same as double- + // clicking on the corresponding label. + on_click_label(get_collector_under_pixel(graph_x, graph_y)); + return TRUE; + } } if (_potential_drag_mode == DM_none) { @@ -278,21 +353,21 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, return TRUE; } - return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y, - double_click); + return GtkStatsGraph::handle_button_press(graph_x, graph_y, + double_click, button); } /** * Called when the mouse button is released within the graph window. */ gboolean GtkStatsPianoRoll:: -handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { +handle_button_release(int graph_x, int graph_y) { if (_drag_mode == DM_scale) { set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); - - } else if (_drag_mode == DM_guide_bar) { + return handle_motion(graph_x, graph_y); + } + else if (_drag_mode == DM_guide_bar) { if (graph_x < 0 || graph_x >= get_xsize()) { remove_user_guide_bar(_drag_guide_bar); } else { @@ -300,17 +375,17 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { } set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); + return handle_motion(graph_x, graph_y); } - return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y); + return GtkStatsGraph::handle_button_release(graph_x, graph_y); } /** * Called when the mouse is moved within the graph window. */ gboolean GtkStatsPianoRoll:: -handle_motion(GtkWidget *widget, int graph_x, int graph_y) { +handle_motion(int graph_x, int graph_y) { if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { // When the mouse is over a color bar, highlight it. int collector_index = get_collector_under_pixel(graph_x, graph_y); @@ -328,8 +403,8 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { }; TrackMouseEvent(&tme); */ - - } else { + } + else { // If the mouse is in some drag mode, stop highlighting. _label_stack.highlight_label(-1); on_leave_label(_highlighted_index); @@ -341,8 +416,8 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { set_horizontal_scale(_drag_scale_start / ratio); } return TRUE; - - } else if (_drag_mode == DM_new_guide_bar) { + } + else if (_drag_mode == DM_new_guide_bar) { // We haven't created the new guide bar yet; we won't until the mouse // comes within the graph's region. if (graph_x >= 0 && graph_x < get_xsize()) { @@ -356,7 +431,17 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { return TRUE; } - return GtkStatsGraph::handle_motion(widget, graph_x, graph_y); + return GtkStatsGraph::handle_motion(graph_x, graph_y); +} + +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsPianoRoll:: +handle_leave() { + _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); + return TRUE; } /** @@ -364,14 +449,14 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { * -1. */ int GtkStatsPianoRoll:: -get_collector_under_pixel(int xpoint, int ypoint) { +get_collector_under_pixel(int xpoint, int ypoint) const { if (_label_stack.get_num_labels() == 0) { return -1; } // Assume all of the labels are the same height. int height = _label_stack.get_label_height(0); - int row = (get_ysize() - ypoint) / height; + int row = (get_ysize() - ypoint) / (height * _cr_scale); if (row >= 0 && row < _label_stack.get_num_labels()) { return _label_stack.get_label_collector_index(row); } else { @@ -411,7 +496,7 @@ draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) { cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -452,7 +537,7 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -460,13 +545,13 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { int x = height_to_pixel(bar._height); const std::string &label = bar._label; - PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str()); + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str()); int width, height; pango_layout_get_pixel_size(layout, &width, &height); if (bar._style != GBS_user) { - double from_height = pixel_to_height(x - width); - double to_height = pixel_to_height(x + width); + double from_height = pixel_to_height(x - width * _cr_scale); + double to_height = pixel_to_height(x + width * _cr_scale); if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); @@ -478,6 +563,8 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { // Now convert our x to a coordinate within our drawing area. int junk_y; + x /= _cr_scale; + // The x coordinate comes from the graph_window. gtk_widget_translate_coordinates(_graph_window, _scale_area, x, 0, diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h index a7cfd6afe8..18a30ee633 100644 --- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h +++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h @@ -39,6 +39,8 @@ public: virtual void set_time_units(int unit_mask); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; void set_horizontal_scale(double time_width); protected: @@ -50,15 +52,17 @@ protected: virtual void idle(); virtual void additional_graph_window_paint(cairo_t *cr); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int graph_x, int graph_y); - virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click); - virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y); - virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); + virtual gboolean handle_button_press(int graph_x, int graph_y, + bool double_click, int button); + virtual gboolean handle_button_release(int graph_x, int graph_y); + virtual gboolean handle_motion(int graph_x, int graph_y); + virtual gboolean handle_leave(); private: - int get_collector_under_pixel(int xpoint, int ypoint); + int get_collector_under_pixel(int xpoint, int ypoint) const; void update_labels(); void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar); void draw_guide_labels(cairo_t *cr); diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx index eb39d875a9..78c31d6e45 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx @@ -26,12 +26,9 @@ GtkStatsStripChart:: GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, int collector_index, bool show_level) : PStatStripChart(monitor, - show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index), - thread_index, - collector_index, - default_strip_chart_width, - default_strip_chart_height), - GtkStatsGraph(monitor) + show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index), + thread_index, collector_index, 0, 0), + GtkStatsGraph(monitor, true) { if (show_level) { // If it's a level-type graph, show the appropriate units. @@ -48,38 +45,35 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, // Put some stuff on top of the graph. _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox, - FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox, FALSE, FALSE, 0); _smooth_check_box = gtk_check_button_new_with_label("Smooth"); g_signal_connect(G_OBJECT(_smooth_check_box), "toggled", - G_CALLBACK(toggled_callback), this); + G_CALLBACK(toggled_callback), this); _total_label = gtk_label_new(""); - gtk_box_pack_start(GTK_BOX(_top_hbox), _smooth_check_box, - FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, - FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(_top_hbox), _smooth_check_box, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0); // Add a DrawingArea widget to the right of the graph, to display all of the // scale units. _scale_area = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(_scale_area), "draw", - G_CALLBACK(draw_callback), this); - gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area, - FALSE, FALSE, 0); + G_CALLBACK(draw_callback), this); + gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area, FALSE, FALSE, 0); // Make it wide enough to display a typical label. { - PangoLayout *layout = gtk_widget_create_pango_layout(_window, "99 ms"); + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "99 ms"); int width, height; pango_layout_get_pixel_size(layout, &width, &height); gtk_widget_set_size_request(_scale_area, width, 0); g_object_unref(layout); } - gtk_widget_set_size_request(_graph_window, default_strip_chart_width, - default_strip_chart_height); + gtk_widget_set_size_request(_graph_window, + default_strip_chart_width * monitor->get_resolution() / 96, + default_strip_chart_height * monitor->get_resolution() / 96); gtk_widget_show_all(_window); gtk_widget_show(_window); @@ -87,7 +81,7 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index, // Allow the window to be resized as small as the user likes. We have to do // this after the window has been shown; otherwise, it will affect the // window's initial size. - gtk_widget_set_size_request(_window, 0, 0); + gtk_widget_set_size_request(_graph_window, 0, 0); clear_region(); } @@ -108,7 +102,7 @@ new_collector(int collector_index) { } /** - * Called as each frame's data is made available. There is no gurantee the + * Called as each frame's data is made available. There is no guarantee the * frames will arrive in order, or that all of them will arrive at all. The * monitor should be prepared to accept frames received out-of-order or * missing. @@ -138,7 +132,9 @@ new_data(int thread_index, int frame_number) { */ void GtkStatsStripChart:: force_redraw() { - PStatStripChart::force_redraw(); + if (_cr) { + PStatStripChart::force_redraw(); + } } /** @@ -209,6 +205,66 @@ on_click_label(int collector_index) { } } +/** + * Called when the user right-clicks on a label. + */ +void GtkStatsStripChart:: +on_popup_label(int collector_index) { + GtkWidget *menu = gtk_menu_new(); + _popup_index = collector_index; + + std::string label = get_label_tooltip(collector_index); + if (!label.empty()) { + GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + gtk_widget_set_sensitive(menu_item, FALSE); + } + + { + GtkWidget *menu_item = gtk_menu_item_new_with_label("Set as Focus"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + + if (collector_index == 0 && get_collector_index() == 0) { + gtk_widget_set_sensitive(menu_item, FALSE); + } else { + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(+[] (GtkWidget *widget, gpointer data) { + GtkStatsStripChart *self = (GtkStatsStripChart *)data; + self->set_collector_index(self->_popup_index); + }), + this); + } + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + _thread_index, collector_index, + GtkStatsMonitor::CT_strip_chart, get_view().get_show_level(), + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + if (!get_view().get_show_level()) { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + gtk_widget_show_all(menu); + gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr); +} + /** * Called when the mouse hovers over a label, and should return the text that * should appear on the tooltip. @@ -263,8 +319,9 @@ copy_region(int start_x, int end_x, int dest_x) { // We are not allowed to copy a surface onto itself, so we have to create a // temporary surface to copy to. end_x = std::min(end_x, get_xsize()); + GdkWindow *window = gtk_widget_get_window(_graph_window); cairo_surface_t *temp_surface = - cairo_image_surface_create(CAIRO_FORMAT_RGB24, end_x - start_x, get_ysize()); + gdk_window_create_similar_image_surface(window, CAIRO_FORMAT_RGB24, end_x - start_x, get_ysize(), 1); { cairo_t *temp_cr = cairo_create(temp_surface); cairo_set_source_surface(temp_cr, _cr_surface, -start_x, 0); @@ -278,11 +335,7 @@ copy_region(int start_x, int end_x, int dest_x) { cairo_surface_destroy(temp_surface); - GdkWindow *window = gtk_widget_get_window(_graph_window); - GdkRectangle rect = { - dest_x, 0, end_x - start_x, get_ysize() - }; - gdk_window_invalidate_rect(window, &rect, FALSE); + gdk_window_invalidate_rect(window, nullptr, FALSE); } /** @@ -356,8 +409,9 @@ end_draw(int from_x, int to_x) { } GdkWindow *window = gtk_widget_get_window(_graph_window); + int scale = gdk_window_get_scale_factor(window); GdkRectangle rect = { - from_x, 0, to_x - from_x, get_ysize() + (from_x * scale) / scale, 0, (to_x - from_x) / scale, get_ysize() / scale }; gdk_window_invalidate_rect(window, &rect, FALSE); } @@ -374,6 +428,18 @@ additional_graph_window_paint(cairo_t *cr) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsStripChart:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + if (_highlighted_index != -1) { + return get_label_tooltip(_highlighted_index); + } + return std::string(); +} + /** * Based on the mouse position within the graph window, look for draggable * things the mouse might be hovering over and return the appropriate DragMode @@ -409,19 +475,11 @@ void GtkStatsStripChart:: set_drag_mode(GtkStatsGraph::DragMode drag_mode) { GtkStatsGraph::set_drag_mode(drag_mode); - switch (_drag_mode) { - case DM_scale: - case DM_sizing: - // Disable smoothing for these expensive operations. - set_average_mode(false); - break; - - default: + if (_drag_mode == DM_none) { // Restore smoothing according to the current setting of the check box. bool active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(_smooth_check_box)); set_average_mode(active); - break; } } @@ -429,10 +487,19 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) { * Called when the mouse button is depressed within the graph window. */ gboolean GtkStatsStripChart:: -handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click) { +handle_button_press(int graph_x, int graph_y, bool double_click, int button) { if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { - if (double_click) { + int collector_index = get_collector_under_pixel(graph_x, graph_y); + if (button == 3) { + // Right-clicking on a color bar in the graph is the same as right- + // clicking on the corresponding label. + if (collector_index >= 0) { + on_popup_label(collector_index); + return TRUE; + } + return FALSE; + } + else if (double_click && button == 1) { // Double-clicking on a color bar in the graph is the same as double- // clicking on the corresponding label. on_click_label(get_collector_under_pixel(graph_x, graph_y)); @@ -454,21 +521,21 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y, return TRUE; } - return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y, - double_click); + return GtkStatsGraph::handle_button_press(graph_x, graph_y, + double_click, button); } /** * Called when the mouse button is released within the graph window. */ gboolean GtkStatsStripChart:: -handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { +handle_button_release(int graph_x, int graph_y) { if (_drag_mode == DM_scale) { set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); - - } else if (_drag_mode == DM_guide_bar) { + return handle_motion(graph_x, graph_y); + } + else if (_drag_mode == DM_guide_bar) { if (graph_y < 0 || graph_y >= get_ysize()) { remove_user_guide_bar(_drag_guide_bar); } else { @@ -476,17 +543,17 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) { } set_drag_mode(DM_none); // ReleaseCapture(); - return handle_motion(widget, graph_x, graph_y); + return handle_motion(graph_x, graph_y); } - return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y); + return GtkStatsGraph::handle_button_release(graph_x, graph_y); } /** * Called when the mouse is moved within the graph window. */ gboolean GtkStatsStripChart:: -handle_motion(GtkWidget *widget, int graph_x, int graph_y) { +handle_motion(int graph_x, int graph_y) { if (_drag_mode == DM_none && _potential_drag_mode == DM_none && graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { // When the mouse is over a color bar, highlight it. @@ -501,13 +568,18 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { } if (_drag_mode == DM_scale) { - double ratio = 1.0f - ((double)graph_y / (double)get_ysize()); - if (ratio > 0.0f) { - set_vertical_scale(_drag_scale_start / ratio); + double ratio = 1.0 - ((double)graph_y / (double)get_ysize()); + if (ratio > 0.0) { + double new_scale = _drag_scale_start / ratio; + if (!IS_NEARLY_EQUAL(get_vertical_scale(), new_scale)) { + // Disable smoothing while we do this expensive operation. + set_average_mode(false); + set_vertical_scale(_drag_scale_start / ratio); + } } return TRUE; - - } else if (_drag_mode == DM_new_guide_bar) { + } + else if (_drag_mode == DM_new_guide_bar) { // We haven't created the new guide bar yet; we won't until the mouse // comes within the graph's region. if (graph_y >= 0 && graph_y < get_ysize()) { @@ -515,13 +587,23 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) { _drag_guide_bar = add_user_guide_bar(pixel_to_height(graph_y)); return TRUE; } - - } else if (_drag_mode == DM_guide_bar) { + } + else if (_drag_mode == DM_guide_bar) { move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_y)); return TRUE; } - return GtkStatsGraph::handle_motion(widget, graph_x, graph_y); + return GtkStatsGraph::handle_motion(graph_x, graph_y); +} + +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsStripChart:: +handle_leave() { + _label_stack.highlight_label(-1); + on_leave_label(_highlighted_index); + return TRUE; } /** @@ -543,7 +625,7 @@ draw_guide_bar(cairo_t *cr, int from_x, int to_x, cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -593,7 +675,7 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y) { cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); break; - case GBS_normal: + default: cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); break; } @@ -601,13 +683,13 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y) { int y = height_to_pixel(bar._height); const std::string &label = bar._label; - PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str()); + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str()); int width, height; pango_layout_get_pixel_size(layout, &width, &height); if (bar._style != GBS_user) { - double from_height = pixel_to_height(y + height); - double to_height = pixel_to_height(y - height); + double from_height = pixel_to_height(y + height * _cr_scale); + double to_height = pixel_to_height(y - height * _cr_scale); if (find_user_guide_bar(from_height, to_height) >= 0) { // Omit the label: there's a user-defined guide bar in the same space. g_object_unref(layout); @@ -619,6 +701,8 @@ draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y) { // Now convert our y to a coordinate within our drawing area. int junk_x; + y /= _cr_scale; + // The y coordinate comes from the graph_window. gtk_widget_translate_coordinates(_graph_window, _scale_area, 0, y, diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h index b1fc44b88e..01284e9719 100644 --- a/pandatool/src/gtk-stats/gtkStatsStripChart.h +++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h @@ -41,6 +41,7 @@ public: virtual void set_time_units(int unit_mask); virtual void set_scroll_speed(double scroll_speed); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); virtual std::string get_label_tooltip(int collector_index) const; void set_vertical_scale(double value_height); @@ -56,13 +57,15 @@ protected: virtual void end_draw(int from_x, int to_x); virtual void additional_graph_window_paint(cairo_t *cr); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int graph_x, int graph_y); virtual void set_drag_mode(DragMode drag_mode); - virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y, - bool double_click); - virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y); - virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y); + virtual gboolean handle_button_press(int graph_x, int graph_y, + bool double_click, int button); + virtual gboolean handle_button_release(int graph_x, int graph_y); + virtual gboolean handle_motion(int graph_x, int graph_y); + virtual gboolean handle_leave(); private: void draw_guide_bar(cairo_t *cr, int from_x, int to_x, @@ -79,6 +82,8 @@ private: GtkWidget *_top_hbox; GtkWidget *_smooth_check_box; GtkWidget *_total_label; + + int _popup_index = -1; }; #endif diff --git a/pandatool/src/gtk-stats/gtkStatsTimeline.cxx b/pandatool/src/gtk-stats/gtkStatsTimeline.cxx new file mode 100644 index 0000000000..fbc45ccc1b --- /dev/null +++ b/pandatool/src/gtk-stats/gtkStatsTimeline.cxx @@ -0,0 +1,812 @@ +/** + * 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 gtkStatsTimeline.cxx + * @author rdb + * @date 2022-02-17 + */ + +#include "gtkStatsTimeline.h" +#include "gtkStatsMonitor.h" +#include "numeric_types.h" +#include "gtkStatsLabelStack.h" + +static const int default_timeline_width = 1000; +static const int default_timeline_height = 300; + +/** + * + */ +GtkStatsTimeline:: +GtkStatsTimeline(GtkStatsMonitor *monitor) : + PStatTimeline(monitor, 0, 0), + GtkStatsGraph(monitor, false) +{ + // Let's show the units on the guide bar labels. There's room. + set_guide_bar_units(get_guide_bar_units() | GBU_show_units); + + // Add a DrawingArea widget on top of the graph, to display all of the scale + // units. + _scale_area = gtk_drawing_area_new(); + g_signal_connect(G_OBJECT(_scale_area), "draw", + G_CALLBACK(scale_area_draw_callback), this); + gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0); + + // It should be large enough to display the labels. + { + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "0123456789 ms"); + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + gtk_widget_set_size_request(_scale_area, 0, height + _pixel_scale / 2); + g_object_unref(layout); + } + + // Add a drawing area to the left of the graph to show the thread labels. + _thread_area = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(_graph_hbox), _thread_area, FALSE, FALSE, 0); + gtk_box_reorder_child(GTK_BOX(_graph_hbox), _thread_area, 0); + g_signal_connect(G_OBJECT(_thread_area), "draw", + G_CALLBACK(thread_area_draw_callback), this); + + // Listen for mouse wheel and keyboard events. + gtk_widget_add_events(_graph_window, GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + gtk_widget_set_can_focus(_graph_window, TRUE); + g_signal_connect(G_OBJECT(_graph_window), "scroll_event", + G_CALLBACK(scroll_callback), this); + g_signal_connect(G_OBJECT(_graph_window), "key_press_event", + G_CALLBACK(key_press_callback), this); + g_signal_connect(G_OBJECT(_graph_window), "key_release_event", + G_CALLBACK(key_release_callback), this); + + int min_height = 0; + if (!_threads.empty()) { + int num_rows = _threads.back()._row_offset + _threads.back()._rows.size(); + double height = row_to_pixel(num_rows) + _pixel_scale * 2.5; + min_height = height / _cr_scale; + } + + gtk_widget_set_size_request(_graph_window, + default_timeline_width * monitor->get_resolution() / 96, + std::max(min_height, (int)(default_timeline_height * monitor->get_resolution() / 96))); + + gtk_window_set_title(GTK_WINDOW(_window), "Timeline"); + + _grid_pattern = cairo_pattern_create_rgb(0xdd / 255.0, 0xdd / 255.0, 0xdd / 255.0); + + gtk_widget_show_all(_window); + gtk_widget_show(_window); + + // Allow the window to be resized as small as the user likes. We have to do + // this after the window has been shown; otherwise, it will affect the + // window's initial size. + gtk_widget_set_size_request(_graph_window, 0, min_height); + + clear_region(); +} + +/** + * + */ +GtkStatsTimeline:: +~GtkStatsTimeline() { + cairo_pattern_destroy(_grid_pattern); +} + +/** + * Called as each frame's data is made available. There is no guarantee the + * frames will arrive in order, or that all of them will arrive at all. The + * monitor should be prepared to accept frames received out-of-order or + * missing. + */ +void GtkStatsTimeline:: +new_data(int thread_index, int frame_number) { + PStatTimeline::new_data(thread_index, frame_number); +} + +/** + * Called when it is necessary to redraw the entire graph. + */ +void GtkStatsTimeline:: +force_redraw() { + assert(_cr); + if (_cr) { + PStatTimeline::force_redraw(); + } +} + +/** + * Called when the user has resized the window, forcing a resize of the graph. + */ +void GtkStatsTimeline:: +changed_graph_size(int graph_xsize, int graph_ysize) { + PStatTimeline::changed_size(graph_xsize, graph_ysize); +} + +/** + * Erases the chart area. + */ +void GtkStatsTimeline:: +clear_region() { + cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); + cairo_paint(_cr); +} + +/** + * Erases the chart area in preparation for drawing a bunch of bars. + */ +void GtkStatsTimeline:: +begin_draw() { +} + +/** + * Draws a horizontal separator. + */ +void GtkStatsTimeline:: +draw_separator(int row) { + cairo_set_source(_cr, _grid_pattern); + cairo_rectangle(_cr, 0, (row_to_pixel(row) + row_to_pixel(row + 1)) / 2.0, + get_xsize(), _pixel_scale * 1 / 3); + cairo_fill(_cr); +} + +/** + * Draws a vertical guide bar. If the row is -1, draws it in all rows. + */ +void GtkStatsTimeline:: +draw_guide_bar(int x, GuideBarStyle style) { + double width = _pixel_scale / 3.0; + if (style == GBS_frame) { + width *= 2; + } + + cairo_set_source(_cr, _grid_pattern); + cairo_rectangle(_cr, x - width / 2.0, 0, width, get_ysize()); + cairo_fill(_cr); +} + +/** + * Draws a single bar in the chart for the indicated row, in the color for the + * given collector, for the indicated horizontal pixel range. + */ +void GtkStatsTimeline:: +draw_bar(int row, int from_x, int to_x, int collector_index, + const std::string &collector_name) { + int top = row_to_pixel(row); + int bottom = row_to_pixel(row + 1); + int scale = _pixel_scale; + + bool is_highlighted = row == _highlighted_row && _highlighted_x >= from_x && _highlighted_x < to_x; + cairo_set_source(_cr, get_collector_pattern(collector_index, is_highlighted)); + + if (to_x < from_x + 1) { + // Too tiny to draw. + } + else if (to_x < from_x + scale) { + // It's just a tiny sliver. This is a more reliable way to draw it. + cairo_rectangle(_cr, from_x, top, to_x - from_x, bottom - top); + cairo_fill(_cr); + } + else { + int left = std::max(from_x, -scale - 1); + int right = std::min(std::max(to_x, from_x + 1), get_xsize() + scale); + + double radius = std::min((double)scale, (right - left) / 2.0); + cairo_new_sub_path(_cr); + cairo_arc(_cr, right - radius, top + radius, radius, -0.5 * M_PI, 0.0); + cairo_arc(_cr, right - radius, bottom - radius, radius, 0.0, 0.5 * M_PI); + cairo_arc(_cr, left + radius, bottom - radius, radius, 0.5 * M_PI, M_PI); + cairo_arc(_cr, left + radius, top + radius, radius, M_PI, 1.5 * M_PI); + cairo_close_path(_cr); + cairo_fill(_cr); + + if ((to_x - from_x) >= scale * 4) { + // Only bother drawing the text if we've got some space to draw on. + // Choose a suitable foreground color. + LRGBColor fg = get_collector_text_color(collector_index, is_highlighted); + cairo_set_source_rgb(_cr, fg[0], fg[1], fg[2]); + + // Make sure that the text doesn't run off the chart. + int text_width, text_height; + PangoLayout *layout = gtk_widget_create_pango_layout(_graph_window, collector_name.c_str()); + pango_layout_set_attributes(layout, _pango_attrs); + pango_layout_set_height(layout, -1); + pango_layout_get_pixel_size(layout, &text_width, &text_height); + + double center = (from_x + to_x) / 2.0; + double text_left = std::max(from_x, 0) + scale / 2.0; + double text_right = std::min(to_x, get_xsize()) - scale / 2.0; + double text_top = top + (bottom - top - text_height) / 2.0; + + if (text_width >= text_right - text_left) { + if (text_right - text_left < scale * 6) { + // It's a really tiny space. Draw a single letter. + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + pango_layout_set_text(layout, collector_name.c_str(), 1); + } else { + // It's going to be tricky to fit it, let pango figure it out. + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + } + pango_layout_set_width(layout, (text_right - text_left) * PANGO_SCALE); + cairo_move_to(_cr, text_left, text_top); + } + else if (center - text_width / 2.0 < 0.0) { + // Put it against the left-most edge. + cairo_move_to(_cr, scale, text_top); + } + else if (center + text_width / 2.0 >= get_xsize()) { + // Put it against the right-most edge. + cairo_move_to(_cr, get_xsize() - scale - text_width, text_top); + } + else { + // It fits just fine, center it. + cairo_move_to(_cr, center - text_width / 2.0, text_top); + } + + pango_cairo_show_layout(_cr, layout); + g_object_unref(layout); + } + } +} + +/** + * Called after all the bars have been drawn, this triggers a refresh event to + * draw it to the window. + */ +void GtkStatsTimeline:: +end_draw() { + gtk_widget_queue_draw(_graph_window); + + if (_threads_changed) { + // Make sure the window is large enough to fit all of the threads. + int num_rows = _threads.back()._row_offset + _threads.back()._rows.size(); + double height = row_to_pixel(num_rows) + _pixel_scale * 2.5; + gtk_widget_set_size_request(_graph_window, 0, height / _cr_scale); + + // Calculate the size of the thread area. + PangoLayout *layout = gtk_widget_create_pango_layout(_thread_area, ""); + + int max_width = 0; + for (const ThreadRow &thread_row : _threads) { + pango_layout_set_text(layout, thread_row._label.c_str(), thread_row._label.size()); + + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + + if (width > max_width) { + max_width = width; + } + } + + gtk_widget_set_size_request(_thread_area, max_width + _pixel_scale * 2, 0); + g_object_unref(layout); + gtk_widget_queue_draw(_thread_area); + _threads_changed = false; + } + + if (_guide_bars_changed) { + gtk_widget_queue_draw(_scale_area); + _guide_bars_changed = false; + } +} + +/** + * Called at the end of the draw cycle. + */ +void GtkStatsTimeline:: +idle() { +} + +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool GtkStatsTimeline:: +animate(double time, double dt) { + return PStatTimeline::animate(time, dt); +} + +/** + * This is called during the servicing of the draw event; it gives a derived + * class opportunity to do some further painting into the graph window. + */ +void GtkStatsTimeline:: +additional_graph_window_paint(cairo_t *cr) { +} + +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string GtkStatsTimeline:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return PStatTimeline::get_bar_tooltip(pixel_to_row(mouse_y), mouse_x); +} + +/** + * Based on the mouse position within the graph window, look for draggable + * things the mouse might be hovering over and return the appropriate DragMode + * enum or DM_none if nothing is indicated. + */ +GtkStatsGraph::DragMode GtkStatsTimeline:: +consider_drag_start(int graph_x, int graph_y) { + return GtkStatsGraph::consider_drag_start(graph_x, graph_y); +} + +/** + * Called when the mouse button is depressed within the graph window. + */ +gboolean GtkStatsTimeline:: +handle_button_press(int graph_x, int graph_y, bool double_click, int button) { + if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + if (button == 3) { + // Right-clicking a color bar brings up a context menu. + int row = pixel_to_row(graph_y); + + ColorBar bar; + if (find_bar(row, graph_x, bar)) { + GtkWidget *menu = gtk_menu_new(); + _popup_bar = bar; + + std::string label = get_bar_tooltip(row, graph_x); + if (!label.empty()) { + GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str()); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + gtk_widget_set_sensitive(menu_item, FALSE); + } + + { + GtkWidget *menu_item = gtk_menu_item_new_with_label("Zoom To"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(+[] (GtkWidget *widget, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + const ColorBar &bar = self->_popup_bar; + double width = bar._end - bar._start; + self->zoom_to(width * 1.5, (bar._end + bar._start) / 2.0); + self->scroll_to(bar._start - width / 4.0); + self->start_animation(); + }), + this); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + bar._thread_index, bar._collector_index, + GtkStatsMonitor::CT_strip_chart, false, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + bar._thread_index, bar._collector_index, + GtkStatsMonitor::CT_flame_graph, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + { + const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({ + bar._thread_index, -1, GtkStatsMonitor::CT_piano_roll, + }); + + GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Piano Roll"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + g_signal_connect(G_OBJECT(menu_item), "activate", + G_CALLBACK(GtkStatsMonitor::menu_activate), + (void *)menu_def); + } + + gtk_widget_show_all(menu); + gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr); + return TRUE; + } + return FALSE; + } + else if (double_click && button == 1) { + // Double-clicking on a color bar in the graph will zoom the graph into + // that collector. + int row = pixel_to_row(graph_y); + ColorBar bar; + if (find_bar(row, graph_x, bar)) { + double width = bar._end - bar._start; + zoom_to(width * 1.5, pixel_to_timestamp(graph_x)); + scroll_to(bar._start - width / 4.0); + } else { + // Double-clicking the white area zooms out. + _zoom_speed -= 100.0; + } + start_animation(); + } + + if (_potential_drag_mode == DM_none) { + set_drag_mode(DM_pan); + _drag_start_x = graph_x; + _scroll_speed = 0.0; + _zoom_center = pixel_to_timestamp(graph_x); + return TRUE; + } + } + + return GtkStatsGraph::handle_button_press(graph_x, graph_y, + double_click, button); +} + +/** + * Called when the mouse button is released within the graph window. + */ +gboolean GtkStatsTimeline:: +handle_button_release(int graph_x, int graph_y) { + if (_drag_mode == DM_scale) { + set_drag_mode(DM_none); + // ReleaseCapture(); + return handle_motion(graph_x, graph_y); + } + else if (_drag_mode == DM_guide_bar) { + if (graph_x < 0 || graph_x >= get_xsize()) { + remove_user_guide_bar(_drag_guide_bar); + } else { + move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x)); + } + set_drag_mode(DM_none); + // ReleaseCapture(); + return handle_motion(graph_x, graph_y); + } + + return GtkStatsGraph::handle_button_release(graph_x, graph_y); +} + +/** + * Called when the mouse is moved within the graph window. + */ +gboolean GtkStatsTimeline:: +handle_motion(int graph_x, int graph_y) { + if (_drag_mode == DM_none && _potential_drag_mode == DM_none && + graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) { + // When the mouse is over a color bar, highlight it. + int row = pixel_to_row(graph_y); + std::swap(_highlighted_x, graph_x); + std::swap(_highlighted_row, row); + + if (row >= 0) { + PStatTimeline::force_redraw(row, graph_x, graph_x); + } + PStatTimeline::force_redraw(_highlighted_row, _highlighted_x, _highlighted_x); + + if ((_keys_held & (F_w | F_s)) != 0) { + // Update the zoom center if we move the mouse while zooming with the + // keyboard. + _zoom_center = pixel_to_timestamp(graph_x); + } + } + else { + // If the mouse is in some drag mode, stop highlighting. + if (_highlighted_row != -1) { + int row = _highlighted_row; + _highlighted_row = -1; + PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x); + } + } + + if (_drag_mode == DM_pan) { + int delta = _drag_start_x - graph_x; + _drag_start_x = graph_x; + set_horizontal_scroll(get_horizontal_scroll() + pixel_to_height(delta)); + return 0; + } + + return GtkStatsGraph::handle_motion(graph_x, graph_y); +} + +/** + * Called when the mouse has left the graph window. + */ +gboolean GtkStatsTimeline:: +handle_leave() { + if (_highlighted_row != -1) { + int row = _highlighted_row; + _highlighted_row = -1; + PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x); + } + return TRUE; +} + +/** + * + */ +gboolean GtkStatsTimeline:: +handle_scroll(int graph_x, int graph_y, double dx, double dy, bool ctrl_held) { + gboolean handled = FALSE; + + if (ctrl_held && dy != 0.0) { + handled = TRUE; + zoom_by(dy, pixel_to_timestamp(graph_x)); + start_animation(); + } + + if (dx != 0.0) { + _scroll_speed += dx * 10.0; + handled = TRUE; + start_animation(); + } + + return handled; +} + +/** + * + */ +gboolean GtkStatsTimeline:: +handle_key(bool pressed, guint val, guint16 hw_code) { + // Accept WASD based on their position rather than their mapping + int flag = 0; + switch (hw_code) { + case 25: + flag = F_w; + break; + case 38: + flag = F_a; + break; + case 39: + flag = F_s; + break; + case 40: + flag = F_d; + break; + } + if (flag == 0) { + switch (val) { + case GDK_KEY_Left: + flag = F_left; + break; + case GDK_KEY_Right: + flag = F_right; + break; + case GDK_KEY_w: + flag = F_w; + break; + case GDK_KEY_a: + flag = F_a; + break; + case GDK_KEY_s: + flag = F_s; + break; + case GDK_KEY_d: + flag = F_d; + break; + } + } + if (flag != 0) { + if (pressed) { + if (flag & (F_w | F_s)) { + // Pfoo, GTK sure does make it hard to just get the cursor position. + GdkWindow *window = gtk_widget_get_window(_graph_window); + GdkDisplay *display = gdk_window_get_display(window); + GdkDeviceManager *device_manager = gdk_display_get_device_manager(display); + GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager); + gint x, y; + gdk_window_get_device_position(window, device, &x, &y, nullptr); + _zoom_center = pixel_to_timestamp(x * _cr_scale); + } + if (_keys_held == 0) { + start_animation(); + } + _keys_held |= flag; + } + else if (_keys_held != 0) { + _keys_held &= ~flag; + } + return TRUE; + } + return FALSE; +} + +/** + * This is called during the servicing of the draw event. + */ +void GtkStatsTimeline:: +draw_guide_labels(cairo_t *cr) { + int num_guide_bars = get_num_guide_bars(); + for (int i = 0; i < num_guide_bars; i++) { + draw_guide_label(cr, get_guide_bar(i)); + } +} + +/** + * Draws the text for the indicated guide bar label at the top of the graph. + */ +void GtkStatsTimeline:: +draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) { + const std::string &label = bar._label; + if (label.empty()) { + return; + } + + switch (bar._style) { + case GBS_target: + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); + break; + + case GBS_user: + cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]); + break; + + case GBS_normal: + cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]); + break; + + case GBS_frame: + cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]); + break; + } + + PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str()); + + if (bar._style != GBS_frame) { + // Make the offsets slightly smaller. + PangoAttrList *attrs = pango_attr_list_new(); + PangoAttribute *attr = pango_attr_scale_new(0.9); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert(attrs, attr); + pango_layout_set_attributes(layout, attrs); + pango_attr_list_unref(attrs); + } + + int width, height; + pango_layout_get_pixel_size(layout, &width, &height); + + int x = timestamp_to_pixel(bar._height); + if (x >= 0 && x + width * _cr_scale < get_xsize()) { + // Now convert our x to a coordinate within our drawing area. + int junk_y; + + x /= _cr_scale; + + // The x coordinate comes from the graph_window. + gtk_widget_translate_coordinates(_graph_window, _scale_area, + x, 0, &x, &junk_y); + + GtkAllocation allocation; + gtk_widget_get_allocation(_scale_area, &allocation); + + int this_x = x - width / 2; + if (this_x >= 0 && this_x + width < allocation.width) { + cairo_move_to(cr, this_x, allocation.height - height); + pango_cairo_show_layout(cr, layout); + } + } + + g_object_unref(layout); +} + +/** + * This is called during the servicing of the draw event. + */ +void GtkStatsTimeline:: +draw_thread_labels(cairo_t *cr) { + for (const ThreadRow &thread_row : _threads) { + draw_thread_label(cr, thread_row); + } +} + +/** + * Draws the text for the indicated thread on the side of the graph. + */ +void GtkStatsTimeline:: +draw_thread_label(cairo_t *cr, const ThreadRow &thread_row) { + int top = row_to_pixel(thread_row._row_offset); + if (top <= get_ysize()) { + // Now convert our y to a coordinate within our drawing area. + top /= _cr_scale; + + // The y coordinate comes from the graph_window. + int junk_x; + gtk_widget_translate_coordinates(_graph_window, _thread_area, + 0, top, &junk_x, &top); + + GtkAllocation allocation; + gtk_widget_get_allocation(_thread_area, &allocation); + + PangoLayout *layout = gtk_widget_create_pango_layout(_thread_area, thread_row._label.c_str()); + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_width(layout, (allocation.width - _pixel_scale * 2) * PANGO_SCALE); + + cairo_move_to(cr, _pixel_scale, top); + pango_cairo_show_layout(cr, layout); + g_object_unref(layout); + } +} + +/** + * Draws in the scale labels. + */ +gboolean GtkStatsTimeline:: +scale_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + self->draw_guide_labels(cr); + + return TRUE; +} + +/** + * Draws in the thread labels. + */ +gboolean GtkStatsTimeline:: +thread_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + self->draw_thread_labels(cr); + + return TRUE; +} + +/** + * + */ +gboolean GtkStatsTimeline:: +scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + + bool ctrl_held = (event->state & GDK_CONTROL_MASK) != 0; + + double dx, dy; + if (event->direction == GDK_SCROLL_UP) { + dx = 0; + dy = 1; + } + else if (event->direction == GDK_SCROLL_DOWN) { + dx = 0; + dy = -1; + } + else if (event->direction == GDK_SCROLL_LEFT) { + dx = 1; + dy = 0; + } + else if (event->direction == GDK_SCROLL_RIGHT) { + dx = -1; + dy = 0; + } + else if (!gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy)) { + return FALSE; + } + + int graph_x = (int)(event->x * self->_cr_scale); + int graph_y = (int)(event->y * self->_cr_scale); + return self->handle_scroll(graph_x, graph_y, dx, dy, ctrl_held); +} + +/** + * + */ +gboolean GtkStatsTimeline:: +key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + return self->handle_key(true, event->keyval, event->hardware_keycode); +} + +/** + * + */ +gboolean GtkStatsTimeline:: +key_release_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) { + GtkStatsTimeline *self = (GtkStatsTimeline *)data; + return self->handle_key(false, event->keyval, event->hardware_keycode); +} diff --git a/pandatool/src/gtk-stats/gtkStatsTimeline.h b/pandatool/src/gtk-stats/gtkStatsTimeline.h new file mode 100644 index 0000000000..4ab8d46505 --- /dev/null +++ b/pandatool/src/gtk-stats/gtkStatsTimeline.h @@ -0,0 +1,91 @@ +/** + * 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 gtkStatsTimeline.h + * @author rdb + * @date 2022-02-17 + */ + +#ifndef GTKSTATSTIMELINE_H +#define GTKSTATSTIMELINE_H + +#include "pandatoolbase.h" + +#include "gtkStatsGraph.h" +#include "pStatTimeline.h" + +class GtkStatsMonitor; + +/** + * A window that draws all of the start/stop event pairs on each thread on a + * horizontal scrolling timeline, with concurrent start/stop pairs stacked + * underneath each other. + */ +class GtkStatsTimeline : public PStatTimeline, public GtkStatsGraph { +public: + GtkStatsTimeline(GtkStatsMonitor *monitor); + virtual ~GtkStatsTimeline(); + + virtual void new_data(int thread_index, int frame_number); + virtual void force_redraw(); + virtual void changed_graph_size(int graph_xsize, int graph_ysize); + +protected: + virtual void clear_region(); + virtual void begin_draw(); + virtual void draw_separator(int row); + virtual void draw_guide_bar(int x, GuideBarStyle style); + virtual void draw_bar(int row, int from_x, int to_x, int collector_index, + const std::string &collector_name); + virtual void end_draw(); + virtual void idle(); + + virtual bool animate(double time, double dt); + + virtual void additional_graph_window_paint(cairo_t *cr); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; + virtual DragMode consider_drag_start(int graph_x, int graph_y); + + virtual gboolean handle_button_press(int graph_x, int graph_y, + bool double_click, int button); + virtual gboolean handle_button_release(int graph_x, int graph_y); + virtual gboolean handle_motion(int graph_x, int graph_y); + virtual gboolean handle_leave(); + gboolean handle_scroll(int graph_x, int graph_y, + double dx, double dy, bool ctrl_held); + gboolean handle_key(bool pressed, guint val, guint16 hw_code); + +private: + void draw_guide_labels(cairo_t *cr); + void draw_guide_label(cairo_t *cr, const GuideBar &bar); + void draw_thread_labels(cairo_t *cr); + void draw_thread_label(cairo_t *cr, const ThreadRow &thread_row); + + static gboolean scale_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data); + static gboolean thread_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data); + static gboolean scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data); + static gboolean key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data); + static gboolean key_release_callback(GtkWidget *widget, GdkEventKey *event, gpointer data); + + int row_to_pixel(int y) const { + return y * _pixel_scale * 5 + _pixel_scale; + } + int pixel_to_row(int y) const { + return (y - _pixel_scale) / (_pixel_scale * 5); + } + + GtkWidget *_thread_area; + + cairo_pattern_t *_grid_pattern; + + int _highlighted_row = -1; + int _highlighted_x = 0; + ColorBar _popup_bar; +}; + +#endif diff --git a/pandatool/src/gtk-stats/gtkstats_composite1.cxx b/pandatool/src/gtk-stats/gtkstats_composite1.cxx index fefd3eeff4..3ab44d393e 100644 --- a/pandatool/src/gtk-stats/gtkstats_composite1.cxx +++ b/pandatool/src/gtk-stats/gtkstats_composite1.cxx @@ -8,3 +8,4 @@ #include "gtkStatsPianoRoll.cxx" #include "gtkStatsServer.cxx" #include "gtkStatsStripChart.cxx" +#include "gtkStatsTimeline.cxx" diff --git a/pandatool/src/pstatserver/CMakeLists.txt b/pandatool/src/pstatserver/CMakeLists.txt index 8017b7888e..020b57785a 100644 --- a/pandatool/src/pstatserver/CMakeLists.txt +++ b/pandatool/src/pstatserver/CMakeLists.txt @@ -13,6 +13,7 @@ set(P3PSTATSERVER_HEADERS pStatServer.h pStatStripChart.h pStatStripChart.I pStatThreadData.h pStatThreadData.I + pStatTimeline.h pStatTimeline.I pStatView.h pStatView.I pStatViewLevel.h pStatViewLevel.I ) @@ -22,10 +23,15 @@ set(P3PSTATSERVER_SOURCES pStatFlameGraph.cxx pStatGraph.cxx pStatListener.cxx - pStatMonitor.cxx pStatPianoRoll.cxx - pStatReader.cxx pStatServer.cxx - pStatStripChart.cxx pStatThreadData.cxx - pStatView.cxx pStatViewLevel.cxx + pStatMonitor.cxx + pStatPianoRoll.cxx + pStatReader.cxx + pStatServer.cxx + pStatStripChart.cxx + pStatThreadData.cxx + pStatTimeline.cxx + pStatView.cxx + pStatViewLevel.cxx ) composite_sources(p3pstatserver P3PSTATSERVER_SOURCES) diff --git a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx index 079c6e6d22..a3f7c09d34 100644 --- a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx +++ b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx @@ -8,5 +8,6 @@ #include "pStatServer.cxx" #include "pStatStripChart.cxx" #include "pStatThreadData.cxx" +#include "pStatTimeline.cxx" #include "pStatView.cxx" #include "pStatViewLevel.cxx" diff --git a/pandatool/src/pstatserver/pStatFlameGraph.I b/pandatool/src/pstatserver/pStatFlameGraph.I index af7921ae4b..2ab06a159c 100644 --- a/pandatool/src/pstatserver/pStatFlameGraph.I +++ b/pandatool/src/pstatserver/pStatFlameGraph.I @@ -12,15 +12,15 @@ */ /** - * Returns the View this chart represents. + * Returns the particular thread whose data this flame graph reflects. */ -INLINE PStatView &PStatFlameGraph:: -get_view() const { - return _view; +INLINE int PStatFlameGraph:: +get_thread_index() const { + return _thread_index; } /** - * Returns the particular collector whose data this strip chart reflects. + * Returns the particular collector whose data this flame graph reflects. */ INLINE int PStatFlameGraph:: get_collector_index() const { @@ -41,11 +41,19 @@ get_horizontal_scale() const { * the color values over pstats_average_time seconds, which hides spikes and * makes the overall trends easier to read. When false, the strip chart shows * the actual data as it is happening. + * + * If you set this to true, you need to call animate() periodically so that the + * averages are smoothly updated over time. */ INLINE void PStatFlameGraph:: set_average_mode(bool average_mode) { if (_average_mode != average_mode) { _average_mode = average_mode; + _stack.reset_averages(); + if (!average_mode) { + _time_width = _stack.get_net_value(false); + normal_guide_bars(); + } force_redraw(); } } @@ -87,3 +95,18 @@ INLINE bool PStatFlameGraph:: is_title_unknown() const { return _title_unknown; } +/** + * Returns the net value of this stack level. + */ +INLINE double PStatFlameGraph::StackLevel:: +get_net_value(bool average) const { + if (_collector_index >= 0) { + return average ? _avg_net_value : _net_value; + } else { + double sum = 0.0; + for (auto &item : _children) { + sum += item.second.get_net_value(average); + } + return sum; + } +} diff --git a/pandatool/src/pstatserver/pStatFlameGraph.cxx b/pandatool/src/pstatserver/pStatFlameGraph.cxx index 16643345d7..c146203e63 100644 --- a/pandatool/src/pstatserver/pStatFlameGraph.cxx +++ b/pandatool/src/pstatserver/pStatFlameGraph.cxx @@ -25,12 +25,12 @@ * */ PStatFlameGraph:: -PStatFlameGraph(PStatMonitor *monitor, PStatView &view, +PStatFlameGraph(PStatMonitor *monitor, int thread_index, int collector_index, int xsize, int ysize) : PStatGraph(monitor, xsize, ysize), _thread_index(thread_index), - _view(view), - _collector_index(collector_index) + _collector_index(collector_index), + _orig_collector_index(collector_index) { _average_mode = true; _average_cursor = 0; @@ -70,8 +70,6 @@ update() { _current_frame = frame_number; update_data(); - force_redraw(); - update_labels(); } } } @@ -85,12 +83,20 @@ update() { */ void PStatFlameGraph:: set_collector_index(int collector_index) { + if (collector_index == -1) { + // First go back to the collector where we originally opened this graph, + // and only then go back to the root. + collector_index = _orig_collector_index; + if (_collector_index == _orig_collector_index) { + collector_index = -1; + _orig_collector_index = -1; + } + } if (_collector_index != collector_index) { _collector_index = collector_index; _title_unknown = true; + _stack.clear(); update_data(); - force_redraw(); - update_labels(); } } @@ -104,45 +110,61 @@ get_title_text() { _title_unknown = false; const PStatClientData *client_data = _monitor->get_client_data(); - if (client_data->has_collector(_collector_index)) { - text = client_data->get_collector_fullname(_collector_index); - text += " flame graph"; - } else { - _title_unknown = true; - } - - if (_thread_index != 0) { - if (client_data->has_thread(_thread_index)) { - text += " (" + client_data->get_thread_name(_thread_index) + " thread)"; + if (_collector_index >= 0) { + if (client_data->has_collector(_collector_index)) { + text = client_data->get_collector_fullname(_collector_index); + text += " flame graph"; } else { _title_unknown = true; } + + if (_thread_index != 0) { + if (client_data->has_thread(_thread_index)) { + text += " (" + client_data->get_thread_name(_thread_index) + " thread)"; + } else { + _title_unknown = true; + } + } + } + else if (client_data->has_thread(_thread_index)) { + text += client_data->get_thread_name(_thread_index) + " thread flame graph"; + } + else { + _title_unknown = true; } return text; } /** - * Called when the mouse hovers over a label, and should return the text that + * Called when the mouse hovers over the graph, and should return the text that * should appear on the tooltip. */ std::string PStatFlameGraph:: -get_label_tooltip(int collector_index) const { - const PStatClientData *client_data = _monitor->get_client_data(); - if (!client_data->has_collector(collector_index)) { - return std::string(); +get_bar_tooltip(int depth, int x) const { + const StackLevel *level = _stack.locate(depth, pixel_to_height(x), _average_mode); + if (level != nullptr) { + const PStatClientData *client_data = _monitor->get_client_data(); + if (client_data != nullptr && client_data->has_collector(level->_collector_index)) { + std::ostringstream text; + text << client_data->get_collector_fullname(level->_collector_index); + text << " (" << format_number(level->get_net_value(_average_mode), GBU_show_units | GBU_ms) << ")"; + return text.str(); + } } + return std::string(); +} - std::ostringstream text; - text << client_data->get_collector_fullname(collector_index); - - Data::const_iterator it = _data.find(collector_index); - if (it != _data.end()) { - const CollectorData &cd = it->second; - text << " (" << format_number(cd._net_value, get_guide_bar_units(), get_guide_bar_unit_name()) << ")"; +/** + * Returns the collector index corresponding to the bar at the given location. + */ +int PStatFlameGraph:: +get_bar_collector(int depth, int x) const { + const StackLevel *level = _stack.locate(depth, pixel_to_height(x), _average_mode); + if (level != nullptr) { + return level->_collector_index; } - - return text.str(); + return -1; } /** @@ -150,66 +172,73 @@ get_label_tooltip(int collector_index) const { */ void PStatFlameGraph:: update_data() { - // First clear the net values, so we'll know which labels should be deleted. - for (auto it = _data.begin(); it != _data.end(); ++it) { - it->second._net_value = 0; + const PStatClientData *client_data = _monitor->get_client_data(); + if (client_data == nullptr) { + return; } - _view.set_to_frame(_current_frame); - - const PStatViewLevel *level = _view.get_level(_collector_index); - double offset = 0; - update_data(level, 0, offset); - - _time_width = (offset != 0) ? offset : 1.0 / pstats_target_frame_rate; - normal_guide_bars(); - - // Cycle through the ring buffers. - _average_cursor = (_average_cursor + 1) % _num_average_frames; -} - -/** - * Recursive helper for get_frame_data. - */ -void PStatFlameGraph:: -update_data(const PStatViewLevel *level, int depth, double &offset) { - double net_value = level->get_net_value(); - - Data::iterator it; - bool inserted; - std::tie(it, inserted) = _data.insert(std::make_pair(level->get_collector(), CollectorData())); - CollectorData &cd = it->second; - cd._offset = offset; - cd._depth = depth; - - if (inserted || !_average_mode) { - // Initialize the values array. - for (double &v : cd._values) { - v = net_value; - } - cd._net_value = net_value; - } else { - cd._values[_average_cursor] = net_value; - - // Calculate the average. - cd._net_value = 0; - for (double value : cd._values) { - cd._net_value += value; - } - cd._net_value /= _num_average_frames; + const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index); + if (thread_data == nullptr || thread_data->is_empty()) { + return; } - if (cd._net_value != 0.0) { - cd._net_value = std::max(cd._net_value, 0.0); + const PStatFrameData &frame_data = thread_data->get_frame(_current_frame); - double child_offset = offset; - offset += cd._net_value; + bool first_time = _stack._children.empty(); - int num_children = level->get_num_children(); - for (int i = 0; i < num_children; i++) { - const PStatViewLevel *child = level->get_child(i); - update_data(child, depth + 1, child_offset); + StackLevel *top = &_stack; + top->reset(); + + size_t num_events = frame_data.get_num_events(); + for (size_t ei = 0; ei < num_events; ++ei) { + int collector_index = frame_data.get_time_collector(ei); + double time = frame_data.get_time(ei); + + if (frame_data.is_start(ei)) { + // If we have a collector index, use it to determine which bottom-level + // stack frames we are interested in. + if (_collector_index < 0 || + _collector_index == collector_index || + top != &_stack) { + top = top->start(collector_index, time); + } + else { + // Check whether one of the parents matches, perhaps. + int parent_index = collector_index; + do { + const PStatCollectorDef &def = client_data->get_collector_def(parent_index); + if (parent_index == def._parent_index) { + break; + } + parent_index = def._parent_index; + if (parent_index == _collector_index) { + // Yes, let it through. + top = top->start(collector_index, time); + break; + } + } + while (parent_index >= 0 && client_data->has_collector(parent_index)); + } } + else { + top = top->stop(collector_index, time); + } + } + top = top->stop_all(frame_data.get_end()); + nassertv(top == &_stack); + + if (first_time) { + _stack.reset_averages(); + } + + if (!_average_mode) { + // Redraw right away, except in average mode, where it's done in animate(). + _time_width = _stack.get_net_value(false); + if (_time_width == 0.0) { + _time_width = 1.0 / pstats_target_frame_rate; + } + normal_guide_bars(); + force_redraw(); } } @@ -226,7 +255,6 @@ changed_size(int xsize, int ysize) { normal_guide_bars(); force_redraw(); - update_labels(); } } @@ -237,22 +265,10 @@ changed_size(int xsize, int ysize) { void PStatFlameGraph:: force_redraw() { begin_draw(); + r_draw_level(_stack); end_draw(); } -/** - * Resets the list of labels. - */ -void PStatFlameGraph:: -update_labels() { - for (auto it = _data.begin(); it != _data.end(); ++it) { - int collector_index = it->first; - const CollectorData &cd = it->second; - - update_label(collector_index, cd._depth, height_to_pixel(cd._offset), height_to_pixel(cd._net_value)); - } -} - /** * Calls update_guide_bars with parameters suitable to this kind of graph. */ @@ -280,6 +296,14 @@ void PStatFlameGraph:: begin_draw() { } +/** + * Should be overridden by the user class. Should draw a single bar at the + * indicated location. + */ +void PStatFlameGraph:: +draw_bar(int depth, int from_x, int to_x, int collector_index) { +} + /** * Should be overridden by the user class. This hook will be called after * drawing a series of color bars in the chart. @@ -295,3 +319,215 @@ end_draw() { void PStatFlameGraph:: idle() { } + +/** + * Should be called periodically to update any animated values. Returns false + * to indicate that the animation is done and no longer needs to be called. + */ +bool PStatFlameGraph:: +animate(double time, double dt) { + if (!_average_mode) { + return false; + } + + if (_stack.update_averages(_average_cursor)) { + _time_width = _stack.get_net_value(true); + if (_time_width == 0.0) { + _time_width = 1.0 / pstats_target_frame_rate; + } + normal_guide_bars(); + force_redraw(); + } + + // Cycle through the ring buffers. + _average_cursor = (_average_cursor + 1) % _num_average_frames; + return true; +} + +/** + * Resets all the nodes by setting their _net_value to 0.0. + */ +void PStatFlameGraph::StackLevel:: +reset() { + _start_time = 0.0; + _net_value = 0.0; + _started = false; + + for (auto &item : _children) { + item.second.reset(); + } +} + +/** + * Starts the given collector, which starts a new stack frame as a child of + * the current one. Returns the new child, which is the new stack top. + */ +PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel:: +start(int collector_index, double time) { + StackLevel &child = _children[collector_index]; + child._parent = this; + child._collector_index = collector_index; + child._start_time = std::max(_start_time, time); + child._started = true; + return &child; +} + +/** + * Stops the given collector, which is assumed to be somewhere up the + * hierarchy. Should only be called on the top of the stack, usually. + * Returns the new top of the stack. + */ +PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel:: +stop(int collector_index, double time) { + StackLevel *new_top = r_stop(collector_index, time); + if (new_top != nullptr) { + return new_top; + } + // We have a stop event without a preceding start event. Measure the + // the time from the start of the current stack frame to the stop time. + // Actually, don't do this, because it means that a child may end up with + // more time than the parent. Need a better solution for this - or not? + //start(collector_index, _start_time)->stop(collector_index, time); + return this; +} + +/** + * Stops all still started collectors. Returns the bottom of the stack, + * which is also the new top of the stack. + */ +PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel:: +stop_all(double time) { + if (_parent != nullptr) { + nassertr(_started, this); + _net_value += time - _start_time; + return _parent->stop_all(time); + } else { + return this; + } +} + +/** + * Resets the average calculator, used when first enabling average mode. + */ +void PStatFlameGraph::StackLevel:: +reset_averages() { + double net_value = get_net_value(false); + + for (double &value : _values) { + value = net_value; + } + _avg_net_value = net_value; + + for (auto &item : _children) { + item.second.reset_averages(); + } +} + +/** + * Recursively calculates the averages. Returns true if any value was changed. + */ +bool PStatFlameGraph::StackLevel:: +update_averages(size_t cursor) { + _values[cursor] = get_net_value(false); + + bool changed = false; + + double sum = 0; + for (double value : _values) { + sum += value; + } + double avg = sum / _num_average_frames; + if (avg != _avg_net_value) { + _avg_net_value = avg; + changed = true; + } + + for (auto &item : _children) { + if (item.second.update_averages(cursor)) { + changed = true; + } + } + + return changed; +} + +/** + * Locates a stack level at the given depth and the given time offset. + */ +const PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel:: +locate(int depth, double time, bool average) const { + if (time < 0.0) { + return nullptr; + } + for (const auto &item : _children) { + double value = item.second.get_net_value(average); + if (time < value) { + if (depth == 0) { + // This is it. + return &item.second; + } else { + // Recurse. + return item.second.locate(depth - 1, time, average); + } + } + time -= value; + } + return nullptr; +} + +/** + * Clears everything. + */ +void PStatFlameGraph::StackLevel:: +clear() { + _children.clear(); + _net_value = 0.0; +} + +/** + * Recursive helper used by stop(). + */ +PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel:: +r_stop(int collector_index, double time) { + if (_collector_index == collector_index) { + // Found it. + nassertr(_started, nullptr); + _net_value += time - _start_time; + _started = false; + nassertr(_parent != nullptr, nullptr); + return _parent; + } + else if (_parent != nullptr) { + StackLevel *level = _parent->r_stop(collector_index, time); + if (level != nullptr) { + nassertr(_started, nullptr); + _net_value += time - _start_time; + _started = false; + return level; + } + } + return nullptr; +} + +/** + * Recursively draws a level. + */ +void PStatFlameGraph:: +r_draw_level(const StackLevel &level, int depth, double offset) { + for (const auto &item : level._children) { + const StackLevel &child = item.second; + + double value = child.get_net_value(_average_mode); + + int from_x = height_to_pixel(offset); + int to_x = height_to_pixel(offset + value); + + // No need to recurse if the bars have become smaller than a pixel. + if (to_x > from_x) { + draw_bar(depth, from_x, to_x, child._collector_index); + r_draw_level(child, depth + 1, offset); + } + + offset += value; + } +} diff --git a/pandatool/src/pstatserver/pStatFlameGraph.h b/pandatool/src/pstatserver/pStatFlameGraph.h index 148e288af9..61a61447e2 100644 --- a/pandatool/src/pstatserver/pStatFlameGraph.h +++ b/pandatool/src/pstatserver/pStatFlameGraph.h @@ -35,14 +35,14 @@ class PStatFrameData; */ class PStatFlameGraph : public PStatGraph { public: - PStatFlameGraph(PStatMonitor *monitor, PStatView &view, + PStatFlameGraph(PStatMonitor *monitor, int thread_index, int collector_index, int xsize, int ysize); virtual ~PStatFlameGraph(); void update(); - INLINE PStatView &get_view() const; + INLINE int get_thread_index() const; INLINE int get_collector_index() const; void set_collector_index(int collector_index); @@ -56,47 +56,71 @@ public: INLINE bool is_title_unknown() const; std::string get_title_text(); - std::string get_label_tooltip(int collector_index) const; + + std::string get_bar_tooltip(int depth, int x) const; + int get_bar_collector(int depth, int x) const; protected: - static const size_t _num_average_frames = 200; - - struct CollectorData { - double _offset; - double _net_value; - int _depth; - // This is updated like a ring buffer, initialized with all the same value - // at first, then always at _average_cursor. - double _values[_num_average_frames]; - }; - typedef pmap Data; - void update_data(); - void update_data(const PStatViewLevel *level, int depth, double &offset); void changed_size(int xsize, int ysize); void force_redraw(); - virtual void update_labels(); - virtual void update_label(int collector_index, int row, int x, int width)=0; virtual void normal_guide_bars(); virtual void begin_draw(); + virtual void draw_bar(int depth, int from_x, int to_x, int collector_index); virtual void end_draw(); virtual void idle(); -private: - void compute_page(const PStatFrameData &frame_data); + bool animate(double time, double dt); -protected: +private: + static const size_t _num_average_frames = 150; + + class StackLevel { + public: + void reset(); + + StackLevel *start(int collector_index, double time); + StackLevel *stop(int collector_index, double time); + StackLevel *stop_all(double time); + + INLINE double get_net_value(bool average) const; + void reset_averages(); + bool update_averages(size_t cursor); + + const StackLevel *locate(int depth, double time, bool average) const; + + void clear(); + + private: + StackLevel *r_stop(int collector_index, double time); + + double _net_value = 0.0; + double _avg_net_value = 0.0; + + // This is updated like a ring buffer, initialized with all the same value + // at first, then always at _average_cursor. + double _values[_num_average_frames] = {0.0}; + + double _start_time = 0.0; + bool _started = false; + + int _collector_index = -1; + StackLevel *_parent = nullptr; + pmap _children; + + friend class PStatFlameGraph; + }; + + void r_draw_level(const StackLevel &level, int depth = 0, double offset = 0.0); + + StackLevel _stack; int _thread_index; - -private: - PStatView &_view; int _collector_index; + int _orig_collector_index; bool _average_mode; size_t _average_cursor; - Data _data; - double _time_width; int _current_frame; bool _title_unknown; diff --git a/pandatool/src/pstatserver/pStatGraph.cxx b/pandatool/src/pstatserver/pStatGraph.cxx index 88130ff7ab..38cb21a5c8 100644 --- a/pandatool/src/pstatserver/pStatGraph.cxx +++ b/pandatool/src/pstatserver/pStatGraph.cxx @@ -178,7 +178,13 @@ format_number(double value, int guide_bar_units, const string &unit_name) { if ((guide_bar_units & GBU_named) != 0) { // Units are whatever is specified by unit_name, not a time unit at all. - label = format_number(value); + int int_value = (int)value; + if ((double)int_value == value) { + // Probably a counter or something, don't display .0 suffix. + label = format_string(int_value); + } else { + label = format_number(value); + } if ((guide_bar_units & GBU_show_units) != 0 && !unit_name.empty()) { label += " "; label += unit_name; @@ -187,10 +193,32 @@ format_number(double value, int guide_bar_units, const string &unit_name) { } else { // Units are either milliseconds or hz, or both. if ((guide_bar_units & GBU_ms) != 0) { - double ms = value * 1000.0; - label += format_number(ms); - if ((guide_bar_units & GBU_show_units) != 0) { - label += " ms"; + if ((guide_bar_units & GBU_show_units) != 0 && + value > 0 && value < 0.000001) { + double ns = value * 1000000000.0; + label += format_number(ns); + label += " ns"; + } + else if ((guide_bar_units & GBU_show_units) != 0 && + value > 0 && value < 0.001) { + double us = value * 1000000.0; + label += format_number(us); +#ifdef _WIN32 + label += " \xb5s"; +#else + label += " us"; +#endif + } + else if ((guide_bar_units & GBU_show_units) == 0 || value < 1.0) { + double ms = value * 1000.0; + label += format_number(ms); + if ((guide_bar_units & GBU_show_units) != 0) { + label += " ms"; + } + } + else { + label += format_number(value); + label += " s"; } } diff --git a/pandatool/src/pstatserver/pStatGraph.h b/pandatool/src/pstatserver/pStatGraph.h index 5b8a2d664e..69541a86ec 100644 --- a/pandatool/src/pstatserver/pStatGraph.h +++ b/pandatool/src/pstatserver/pStatGraph.h @@ -52,6 +52,7 @@ public: GBS_normal, GBS_target, GBS_user, + GBS_frame, }; class GuideBar { diff --git a/pandatool/src/pstatserver/pStatPianoRoll.I b/pandatool/src/pstatserver/pStatPianoRoll.I index f0d3fe15cd..8f5bfcc886 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.I +++ b/pandatool/src/pstatserver/pStatPianoRoll.I @@ -11,6 +11,14 @@ * @date 2000-07-18 */ +/** + * Returns the particular thread whose data this piano roll reflects. + */ +INLINE int PStatPianoRoll:: +get_thread_index() const { + return _thread_index; +} + /** * Changes the amount of time the width of the horizontal axis represents. * This may force a redraw. diff --git a/pandatool/src/pstatserver/pStatPianoRoll.cxx b/pandatool/src/pstatserver/pStatPianoRoll.cxx index 923c3991e7..25c2b416cb 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.cxx +++ b/pandatool/src/pstatserver/pStatPianoRoll.cxx @@ -128,6 +128,20 @@ update() { idle(); } +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string PStatPianoRoll:: +get_label_tooltip(int collector_index) const { + const PStatClientData *client_data = _monitor->get_client_data(); + if (!client_data->has_collector(collector_index)) { + return std::string(); + } + + return client_data->get_collector_fullname(collector_index); +} + /** * To be called by the user class when the widget size has changed. This * updates the chart's internal data and causes it to issue redraw commands to diff --git a/pandatool/src/pstatserver/pStatPianoRoll.h b/pandatool/src/pstatserver/pStatPianoRoll.h index e1c597dccf..6c909dc36e 100644 --- a/pandatool/src/pstatserver/pStatPianoRoll.h +++ b/pandatool/src/pstatserver/pStatPianoRoll.h @@ -43,6 +43,8 @@ public: void update(); + INLINE int get_thread_index() const; + INLINE void set_horizontal_scale(double time_width); INLINE double get_horizontal_scale() const; @@ -51,6 +53,8 @@ public: INLINE int height_to_pixel(double value) const; INLINE double pixel_to_height(int y) const; + std::string get_label_tooltip(int collector_index) const; + protected: void changed_size(int xsize, int ysize); void force_redraw(); diff --git a/pandatool/src/pstatserver/pStatStripChart.I b/pandatool/src/pstatserver/pStatStripChart.I index 35d4c86265..3e924a1bc8 100644 --- a/pandatool/src/pstatserver/pStatStripChart.I +++ b/pandatool/src/pstatserver/pStatStripChart.I @@ -19,6 +19,14 @@ get_view() const { return _view; } +/** + * Returns the particular thread whose data this strip chart reflects. + */ +INLINE int PStatStripChart:: +get_thread_index() const { + return _thread_index; +} + /** * Returns the particular collector whose data this strip chart reflects. */ diff --git a/pandatool/src/pstatserver/pStatStripChart.cxx b/pandatool/src/pstatserver/pStatStripChart.cxx index 09dadc9545..2b454edcc5 100644 --- a/pandatool/src/pstatserver/pStatStripChart.cxx +++ b/pandatool/src/pstatserver/pStatStripChart.cxx @@ -56,7 +56,7 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view, _unit_name = def._level_units; } - set_default_vertical_scale(); + set_auto_vertical_scale(); } /** @@ -139,6 +139,7 @@ set_collector_index(int collector_index) { _title_unknown = true; _data.clear(); clear_label_usage(); + set_auto_vertical_scale(); force_redraw(); update_labels(); } @@ -170,26 +171,44 @@ void PStatStripChart:: set_auto_vertical_scale() { const PStatThreadData *thread_data = _view.get_thread_data(); - double max_value = 0.0; + // Calculate the median value. + std::vector values; - int frame_number = -1; - for (int x = 0; x <= _xsize; x++) { - double time = pixel_to_timestamp(x); - frame_number = - thread_data->get_frame_number_at_time(time, frame_number); + if (thread_data != nullptr && !thread_data->is_empty()) { + // Find the oldest visible frame. + double start_time = pixel_to_timestamp(0); + int oldest_frame = std::max( + thread_data->get_frame_number_at_time(start_time), + thread_data->get_oldest_frame_number()); + int latest_frame = thread_data->get_latest_frame_number(); - if (thread_data->has_frame(frame_number)) { - double net_value = get_net_value(frame_number); - max_value = max(max_value, net_value); + for (int frame_number = oldest_frame; frame_number <= latest_frame; ++frame_number) { + if (thread_data->has_frame(frame_number)) { + values.push_back(get_net_value(frame_number)); + } } } - // Ok, now we know what the max value visible in the chart is. Choose a - // scale that will show all of this sensibly. - if (max_value == 0.0) { - set_vertical_scale(1.0); + if (values.empty()) { + set_default_vertical_scale(); + return; + } + + double median; + size_t half = values.size() / 2; + if (values.size() % 2 == 0) { + std::sort(values.begin(), values.end()); + median = (values[half] + values[half + 1]) / 2.0; } else { - set_vertical_scale(max_value * 1.1); + std::nth_element(values.begin(), values.begin() + half, values.end()); + median = values[half]; + } + + if (median > 0.0) { + // Take 1.5 times the median value as the vertical scale. + set_vertical_scale(median * 1.5); + } else { + set_default_vertical_scale(); } } diff --git a/pandatool/src/pstatserver/pStatStripChart.h b/pandatool/src/pstatserver/pStatStripChart.h index f98af23ac4..5802d0baa2 100644 --- a/pandatool/src/pstatserver/pStatStripChart.h +++ b/pandatool/src/pstatserver/pStatStripChart.h @@ -46,6 +46,7 @@ public: bool first_data() const; INLINE PStatView &get_view() const; + INLINE int get_thread_index() const; INLINE int get_collector_index() const; void set_collector_index(int collector_index); diff --git a/pandatool/src/pstatserver/pStatThreadData.cxx b/pandatool/src/pstatserver/pStatThreadData.cxx index 141f927e8f..61cc92e88e 100644 --- a/pandatool/src/pstatserver/pStatThreadData.cxx +++ b/pandatool/src/pstatserver/pStatThreadData.cxx @@ -288,6 +288,14 @@ record_new_frame(int frame_number, PStatFrameData *frame_data) { } int index = frame_number - _first_frame_number; + + // It's possible to receive frames out of order. + while (index < 0) { + _frames.push_front(nullptr); + ++index; + --_first_frame_number; + } + nassertv(index >= 0 && index < (int)_frames.size()); if (_frames[index] != nullptr) { diff --git a/pandatool/src/pstatserver/pStatTimeline.I b/pandatool/src/pstatserver/pStatTimeline.I new file mode 100644 index 0000000000..00dc859f78 --- /dev/null +++ b/pandatool/src/pstatserver/pStatTimeline.I @@ -0,0 +1,139 @@ +/** + * 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 pStatTimeline.I + * @author rdb + * @date 2022-02-11 + */ + +/** + * Changes the amount of time the width of the horizontal axis represents. + * This may force a redraw. + */ +INLINE void PStatTimeline:: +set_horizontal_scale(double time_width) { + double max_time_width = (_highest_end_time - _lowest_start_time) * 2.0; + time_width = std::min(time_width, max_time_width); + + double scale = time_width / get_xsize(); + if (_time_scale != scale) { + _time_scale = scale; + _target_time_scale = scale; + _zoom_speed = 0.0; + normal_guide_bars(); + force_redraw(); + } +} + +/** + * Returns the amount of total time the width of the horizontal axis + * represents. + */ +INLINE double PStatTimeline:: +get_horizontal_scale() const { + return _time_scale * get_xsize(); +} + +/** + * This may force a redraw. + */ +INLINE void PStatTimeline:: +set_horizontal_scroll(double start_time) { + start_time = std::max(std::min(start_time, _highest_end_time), _lowest_start_time); + if (_start_time != start_time) { + _start_time = start_time; + _target_start_time = start_time; + _scroll_speed = 0.0; + normal_guide_bars(); + force_redraw(); + } +} + +/** + * Returns the amount of total time the width of the horizontal axis + * represents. + */ +INLINE double PStatTimeline:: +get_horizontal_scroll() const { + return _start_time; +} + +/** + * Smoothly zooms to the given time width, around the given focal point. + */ +INLINE void PStatTimeline:: +zoom_to(double time_width, double center) { + // Don't allow zooming out to beyond 2x the size of the entire timeline. + // There's a limit of zooming beyond 1 ns per bar, there's just no point... + double max_time_width = (_highest_end_time - _lowest_start_time) * 2.0; + time_width = std::min(std::max(1e-7, time_width), max_time_width); + _target_time_scale = time_width / get_xsize(); + _zoom_center = center; + + double pivot_x = (_zoom_center - _start_time) / _time_scale; + scroll_to(_zoom_center - pivot_x * _target_time_scale); +} + +/** + * Smoothly zooms by the given amount, where 1.0 is a single "tick" of zooming + * in and -1.0 is a single "tick" of zooming out. + */ +INLINE void PStatTimeline:: +zoom_by(double amount, double center) { + zoom_to(_target_time_scale * pow(0.8, amount) * get_xsize(), center); +} + +/** + * Smoothly scrolls to the given time point. + */ +INLINE void PStatTimeline:: +scroll_to(double start_time) { + _target_start_time = std::max(std::min(start_time, _highest_end_time), _lowest_start_time); +} + +/** + * Smoothly scrolls by the given amount. + */ +INLINE void PStatTimeline:: +scroll_by(double delta) { + scroll_to(_target_start_time + delta); +} + +/** + * Converts a timestamp to a horizontal pixel offset. + */ +INLINE int PStatTimeline:: +timestamp_to_pixel(double time) const { + return (int)((double)_xsize * (time - _start_time) / get_horizontal_scale()); +} + +/** + * Converts a horizontal pixel offset to a timestamp. + */ +INLINE double PStatTimeline:: +pixel_to_timestamp(int x) const { + return _time_scale * (double)x + _start_time; +} + +/** + * Converts a value (i.e. a "height" in the strip chart) to a horizontal + * pixel offset. + */ +INLINE int PStatTimeline:: +height_to_pixel(double value) const { + return (int)((double)_xsize * value / get_horizontal_scale()); +} + +/** + * Converts a horizontal pixel offset to a value (a "height" in the strip + * chart). + */ +INLINE double PStatTimeline:: +pixel_to_height(int x) const { + return _time_scale * (double)x; +} diff --git a/pandatool/src/pstatserver/pStatTimeline.cxx b/pandatool/src/pstatserver/pStatTimeline.cxx new file mode 100644 index 0000000000..8c806447c7 --- /dev/null +++ b/pandatool/src/pstatserver/pStatTimeline.cxx @@ -0,0 +1,664 @@ +/** + * 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 pStatTimeline.cxx + * @author rdb + * @date 2022-02-11 + */ + +#include "pStatTimeline.h" + +#include "pStatFrameData.h" +#include "pStatCollectorDef.h" +#include "string_utils.h" +#include "config_pstatclient.h" + +#include + +/** + * + */ +PStatTimeline:: +PStatTimeline(PStatMonitor *monitor, int xsize, int ysize) : + PStatGraph(monitor, xsize, ysize) +{ + // Default to 1 millisecond per 10 pixels. + _time_scale = 1 / 10000.0; + _target_time_scale = _time_scale; + + _guide_bar_units = GBU_ms | GBU_show_units; + + // Load in the initial data, so that the user can see everything back to the + // beginning (or as far as pstats-history goes back to). + const PStatClientData *client_data = monitor->get_client_data(); + if (client_data != nullptr) { + size_t row_offset = 0; + + for (int thread_index = 0; thread_index < client_data->get_num_threads(); ++thread_index) { + _threads.emplace_back(); + ThreadRow &thread_row = _threads.back(); + thread_row._row_offset = row_offset; + + const PStatThreadData *thread_data = client_data->get_thread_data(thread_index); + if (thread_data != nullptr) { + _threads_changed = true; + + if (!thread_data->is_empty()) { + int oldest_frame = thread_data->get_oldest_frame_number(); + int latest_frame = thread_data->get_latest_frame_number(); + + double oldest_start_time = thread_data->get_frame(oldest_frame).get_start(); + double latest_end_time = thread_data->get_frame(latest_frame).get_end(); + + if (!_have_start_time) { + _have_start_time = true; + _lowest_start_time = oldest_start_time; + } + else { + _lowest_start_time = std::min(_lowest_start_time, oldest_start_time); + } + _highest_end_time = std::max(_highest_end_time, latest_end_time); + + for (int frame = oldest_frame; frame <= latest_frame; ++frame) { + update_bars(thread_index, frame); + } + } + } + + row_offset += thread_row._rows.size() + 1; + } + } + + _start_time = _lowest_start_time; + _target_start_time = _start_time; +} + +/** + * + */ +PStatTimeline:: +~PStatTimeline() { +} + +/** + * Called as each frame's data is made available. There is no guarantee the + * frames will arrive in order, or that all of them will arrive at all. The + * monitor should be prepared to accept frames received out-of-order or + * missing. + */ +void PStatTimeline:: +new_data(int thread_index, int frame_number) { + const PStatClientData *client_data = _monitor->get_client_data(); + + if (client_data != nullptr) { + const PStatThreadData *thread_data = + client_data->get_thread_data(thread_index); + + if (thread_data != nullptr && !thread_data->is_empty()) { + const PStatFrameData &frame_data = thread_data->get_frame(frame_number); + double frame_start = frame_data.get_start(); + double frame_end = frame_data.get_end(); + + if (!_have_start_time) { + _start_time = frame_start; + _have_start_time = true; + _lowest_start_time = _start_time; + } + else if (_start_time < _lowest_start_time) { + _lowest_start_time = _start_time; + } + if (frame_end > _highest_end_time) { + _highest_end_time = frame_end; + } + + while (thread_index >= _threads.size()) { + _threads_changed = true; + if (_threads.size() == 0) { + _threads.resize(1); + } else { + _threads.resize(_threads.size() + 1); + _threads[_threads.size() - 1]._row_offset = + _threads[_threads.size() - 2]._row_offset + + _threads[_threads.size() - 2]._rows.size() + 1; + } + } + + if (update_bars(thread_index, frame_number)) { + // The number of rows was changed. + // Change the offset of all subsequent ThreadRows. + ThreadRow &thread_row = _threads[thread_index]; + size_t offset = thread_row._row_offset + thread_row._rows.size() + 1; + for (size_t ti = (size_t)(thread_index + 1); ti < _threads.size(); ++ti) { + _threads[ti]._row_offset = offset; + offset += _threads[ti]._rows.size() + 1; + } + _threads_changed = true; + normal_guide_bars(); + force_redraw(); + } + else if (frame_end >= _start_time || frame_start <= _start_time + get_horizontal_scale()) { + normal_guide_bars(); + begin_draw(); + draw_thread(thread_index, frame_start, frame_end); + end_draw(); + } + } + } + + idle(); +} + +/** + * Called by new_data(). Updates the bars without doing any drawing. Returns + * true if the number of rows was changed (forcing a full redraw), false if + * only new bars were added on the right side. + */ +bool PStatTimeline:: +update_bars(int thread_index, int frame_number) { + const PStatClientData *client_data = _monitor->get_client_data(); + const PStatThreadData *thread_data = client_data->get_thread_data(thread_index); + const PStatFrameData &frame_data = thread_data->get_frame(frame_number); + ThreadRow &thread_row = _threads[thread_index]; + thread_row._label = client_data->get_thread_name(thread_index); + bool changed_num_rows = false; + + // pair + pvector > stack; + + size_t num_events = frame_data.get_num_events(); + for (size_t i = 0; i < num_events; ++i) { + int collector_index = frame_data.get_time_collector(i); + double time = frame_data.get_time(i); + + if (frame_data.is_start(i)) { + stack.push_back(std::make_pair(collector_index, std::max(time, _start_time))); + if (stack.size() > thread_row._rows.size()) { + thread_row._rows.resize(stack.size()); + changed_num_rows = true; + } + } + else if (!stack.empty()) { + if (stack.back().first == collector_index) { + // Most likely case, ending the most recent collector that is still + // open. + double start_time = stack.back().second; + stack.pop_back(); + thread_row._rows[stack.size()].push_back({ + start_time, time, collector_index, thread_index, frame_number}); + + while (!stack.empty() && stack.back().first < 0) { + stack.pop_back(); + } + } + else { + // Unlikely case: ending a collector before a "child" has ended. + // Go back and clear the row where this collector started. + // Don't decrement the row index. + for (size_t i = 0; i < stack.size(); ++i) { + auto &item = stack[stack.size() - 1 - i]; + + if (item.first == collector_index) { + thread_row._rows[stack.size() - 1 - i].push_back({ + item.second, time, collector_index, thread_index, frame_number}); + item.first = -1; + break; + } + } + } + } + else { + // Somehow, we got an end event for a collector we didn't start. + // This shouldn't really happen, so we just ignore it. + } + } + + // Add all unclosed bars. + while (!stack.empty()) { + int collector_index = stack.back().first; + if (collector_index >= 0) { + double start_time = stack.back().second; + thread_row._rows[stack.size() - 1].push_back({ + start_time, frame_data.get_end(), + collector_index, thread_index, frame_number, + }); + } + stack.pop_back(); + } + + if (thread_row._last_frame >= 0 && frame_number < thread_row._last_frame) { + // Added a frame out of order. Resort the rows. + for (Row &row : thread_row._rows) { + std::sort(row.begin(), row.end()); + } + } else { + thread_row._last_frame = frame_number; + } + + return changed_num_rows; +} + +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string PStatTimeline:: +get_bar_tooltip(int row, int x) const { + ColorBar bar; + if (find_bar(row, x, bar)) { + const PStatClientData *client_data = _monitor->get_client_data(); + if (client_data != nullptr && client_data->has_collector(bar._collector_index)) { + std::ostringstream text; + text << client_data->get_collector_fullname(bar._collector_index); + text << " (" << format_number(bar._end - bar._start, GBU_show_units | GBU_ms) << ")"; + return text.str(); + } + } + return std::string(); +} + +/** + * To be called by the user class when the widget size has changed. This + * updates the chart's internal data and causes it to issue redraw commands to + * reflect the new size. + */ +void PStatTimeline:: +changed_size(int xsize, int ysize) { + if (xsize != _xsize || ysize != _ysize) { + _xsize = xsize; + _ysize = ysize; + + normal_guide_bars(); + force_redraw(); + } +} + +/** + * To be called by the user class when the whole thing needs to be redrawn for + * some reason. + */ +void PStatTimeline:: +force_redraw() { + clear_region(); + + begin_draw(); + + for (const GuideBar &bar : _guide_bars) { + int x = timestamp_to_pixel(bar._height); + if (x > 0 && x < get_xsize() - 1) { + draw_guide_bar(x, bar._style); + } + } + + double start_time = _start_time; + double end_time = start_time + get_horizontal_scale(); + + int num_rows = 0; + + for (size_t ti = 0; ti < _threads.size(); ++ti) { + ThreadRow &thread_row = _threads[ti]; + for (size_t ri = 0; ri < thread_row._rows.size(); ++ri) { + draw_row((int)ti, (int)ri, start_time, end_time); + ++num_rows; + } + draw_separator(num_rows++); + } + + end_draw(); +} + +/** + * To be called by the user class when the whole thing needs to be redrawn for + * some reason. + */ +void PStatTimeline:: +force_redraw(int row, int from_x, int to_x) { + double start_time = std::max(_start_time, pixel_to_timestamp(from_x)); + double end_time = std::min(_start_time + get_horizontal_scale(), pixel_to_timestamp(to_x)); + + begin_draw(); + + for (size_t ti = 0; ti < _threads.size(); ++ti) { + ThreadRow &thread_row = _threads[ti]; + if (thread_row._row_offset > row) { + break; + } + + int row_index = row - (int)thread_row._row_offset; + if (row_index < thread_row._rows.size()) { + draw_row((int)ti, row_index, start_time, end_time); + } + } + + end_draw(); +} + +/** + * Calls update_guide_bars with parameters suitable to this kind of graph. + */ +void PStatTimeline:: +normal_guide_bars() { + double start_time = get_horizontal_scroll(); + double time_width = get_horizontal_scale(); + double end_time = start_time + time_width; + + // We want vaguely 150 pixels between guide bars. + int max_frames = get_xsize() / 100; + int l = (int)std::floor(3.0 * log10(pixel_to_height(150)) + 0.5); + double interval = pow(10.0, std::ceil(l / 3.0)); + if ((l + 3000) % 3 == 1) { + interval /= 5; + } + else if ((l + 3000) % 3 == 2) { + interval /= 2; + } + + _guide_bars.clear(); + + // Rather than getting the client data, we look in the color bar data for + // the first row, because the client data gets wiped after a while. + if (!_threads.empty() && !_threads[0]._rows.empty()) { + const Row &row = _threads[0]._rows[0]; + + // Look for the last Frame bar with end time lower than our start time. + Row::const_iterator it = std::lower_bound(row.begin(), row.end(), ColorBar {0.0, start_time}); + while (it != row.end() && it->_collector_index != 0) { + ++it; + } + + int num_frames = 0; + + while (it != row.end() && it->_start <= end_time) { + double frame_start = it->_start; + + if (frame_start > start_time) { + if (!_guide_bars.empty() && height_to_pixel(frame_start - _guide_bars.back()._height) < 30) { + // Get rid of last label, it is in the way. + _guide_bars.back()._label.clear(); + } + std::string label = "#"; + label += format_string(it->_frame_number); + _guide_bars.push_back(GuideBar(frame_start, label, GBS_frame)); + + if (++num_frames > max_frames) { + // Forget it, this is becoming too many lines. + _guide_bars.clear(); + break; + } + } + + do { + ++it; + } + while (it != row.end() && it->_collector_index != 0); + + double frame_width; + if (it != row.end()) { + // Only go up to the start of the next frame, limiting to however much + // fits in the graph. + frame_width = std::min(it->_start - frame_start, end_time - frame_start); + } else { + // Reached the end; just continue to the end of the graph. + frame_width = end_time - frame_start; + } + + if (interval > 0.0) { + int first_bar = std::max((int)((start_time - frame_start) / interval), 1); + int num_bars = (int)std::round(frame_width / interval); + + for (int i = first_bar; i < num_bars; ++i) { + double offset = i * interval; + std::string label = "+"; + label += format_number(offset, GBU_show_units | GBU_ms); + _guide_bars.push_back(GuideBar(frame_start + offset, label, GBS_normal)); + } + } + } + } + + if (_guide_bars.empty() && interval > 0.0) { + int first_bar = std::max((int)(start_time / interval), 1); + int num_bars = (int)std::round(end_time / interval); + + for (int i = first_bar; i < num_bars; ++i) { + double time = i * interval; + std::string label = format_number(time, GBU_show_units | GBU_ms); + _guide_bars.push_back(GuideBar(time, label, GBS_frame)); + } + } + + _guide_bars_changed = true; +} + +/** + * Should be overridden by the user class to wipe out the entire strip chart + * region. + */ +void PStatTimeline:: +clear_region() { +} + +/** + * Should be overridden by the user class. This hook will be called before + * drawing any bars in the chart. + */ +void PStatTimeline:: +begin_draw() { +} + +/** + * + */ +void PStatTimeline:: +draw_thread(int thread_index, double start_time, double end_time) { + if (thread_index < 0 || (size_t)thread_index > _threads.size()) { + return; + } + + ThreadRow &thread_row = _threads[(size_t)thread_index]; + for (size_t ri = 0; ri < thread_row._rows.size(); ++ri) { + draw_row(thread_index, (int)ri, start_time, end_time); + } +} + +/** + * + */ +void PStatTimeline:: +draw_row(int thread_index, int row_index, double start_time, double end_time) { + ThreadRow &thread_row = _threads[thread_index]; + Row &row = thread_row._rows[row_index]; + + const PStatClientData *client_data = _monitor->get_client_data(); + + // Find the first element whose end time is larger than our start time. + // Then iterate until at least the end of the frame. + Row::iterator it = std::lower_bound(row.begin(), row.end(), ColorBar {0.0, start_time}); + if (it == row.end()) { + return; + } + + int frame_number = it->_frame_number; + do { + ColorBar &bar = *it; + + int from_x = timestamp_to_pixel(bar._start); + int to_x = timestamp_to_pixel(bar._end); + + if (to_x >= 0 && to_x > from_x && from_x < get_xsize()) { + if (bar._collector_index != 0) { + draw_bar(thread_row._row_offset + row_index, from_x, to_x, + bar._collector_index, + client_data->get_collector_name(bar._collector_index)); + } else { + draw_bar(thread_row._row_offset + row_index, from_x, to_x, + bar._collector_index, + std::string("Frame ") + format_string(bar._frame_number)); + } + } + + ++it; + } + while (it != row.end() && (it->_start <= end_time || it->_frame_number == frame_number)); +} + +/** + * Draws a horizontal separator. + */ +void PStatTimeline:: +draw_separator(int) { +} + +/** + * Draws a vertical guide bar. If the row is -1, draws it in all rows. + */ +void PStatTimeline:: +draw_guide_bar(int x, GuideBarStyle style) { +} + +/** + * Draws a single bar in the chart for the indicated row, in the color for the + * given collector, for the indicated horizontal pixel range. + */ +void PStatTimeline:: +draw_bar(int, int, int, int, const std::string &) { +} + +/** + * Should be overridden by the user class. This hook will be called after + * drawing a series of color bars in the chart. + */ +void PStatTimeline:: +end_draw() { +} + +/** + * Should be overridden by the user class to perform any other updates might + * be necessary after the bars have been redrawn. + */ +void PStatTimeline:: +idle() { +} + +/** + * Should be called periodically to update any animated values. Returns false + * to indicate that the animation is done and no longer needs to be called. + */ +bool PStatTimeline:: +animate(double time, double dt) { + int hmove = ((_keys_held & (F_right | F_d)) != 0) + - ((_keys_held & (F_left | F_a)) != 0); + int vmove = ((_keys_held & F_w) != 0) + - ((_keys_held & F_s) != 0); + + if (hmove > 0) { + if (_scroll_speed < 0) { + _scroll_speed = 1.0; + } + _scroll_speed += 1.0; + } + else if (hmove < 0) { + if (_scroll_speed > 0) { + _scroll_speed = -1.0; + } + _scroll_speed -= 1.0; + } + else if (_scroll_speed != 0.0) { + _scroll_speed *= std::exp(-12.0 * dt); + if (std::abs(_scroll_speed) < 0.2) { + _scroll_speed = 0.0; + } + } + + if (vmove > 0) { + if (_zoom_speed < 0) { + _zoom_speed = 1.0; + } + _zoom_speed += 1.0; + } + else if (vmove < 0) { + if (_zoom_speed > 0) { + _zoom_speed = -1.0; + } + _zoom_speed -= 1.0; + } + else if (_zoom_speed != 0.0) { + _zoom_speed *= std::exp(-12.0 * dt); + if (std::abs(_zoom_speed) < 0.2) { + _zoom_speed = 0.0; + } + } + + if (_zoom_speed != 0.0) { + zoom_to(get_horizontal_scale() * pow(0.5, _zoom_speed * dt), _zoom_center); + } + + if (_scroll_speed != 0.0) { + scroll_by(_scroll_speed * 300 * _time_scale * dt); + } + + if (_target_start_time != _start_time) { + double dist = _target_start_time - _start_time; + // When the difference is less than 2 pixels, snap to target position. + if (std::abs(dist) < _time_scale * 2) { + _start_time = _target_start_time; + } else { + dist *= 1.0 - std::exp(-12.0 * dt); + _start_time += dist; + } + } + + if (_target_time_scale != _time_scale) { + //double dist = std::log(_target_time_scale) - std::log(_time_scale); + double dist = _target_time_scale - _time_scale; + if (_target_start_time == _start_time && std::abs(dist) < 0.01) { + _time_scale = _target_time_scale; + } else { + dist *= 1.0 - std::exp(-12.0 * dt); + //_time_scale *= std::exp(dist); + _time_scale += dist; + } + } + + normal_guide_bars(); + force_redraw(); + + // Stop the animation when the speed is 0 and no key is still held. + return _keys_held != 0 + || _scroll_speed != 0 + || _zoom_speed != 0 + || _target_start_time != _start_time + || _target_time_scale != _time_scale; +} + +/** + * Return the ColorBar at the indicated position. + */ +bool PStatTimeline:: +find_bar(int row, int x, ColorBar &bar) const { + double time = pixel_to_timestamp(x); + + for (size_t ti = 0; ti < _threads.size(); ++ti) { + const ThreadRow &thread_row = _threads[ti]; + if (thread_row._row_offset > row) { + break; + } + + int row_index = row - (int)thread_row._row_offset; + if (row_index < thread_row._rows.size()) { + // Find the first element whose end time is larger than the given time. + const Row &bars = thread_row._rows[row_index]; + Row::const_iterator it = std::lower_bound(bars.begin(), bars.end(), ColorBar {time, time}); + if (it != bars.end() && it->_start <= time) { + bar = *it; + return true; + } + } + } + + return false; +} diff --git a/pandatool/src/pstatserver/pStatTimeline.h b/pandatool/src/pstatserver/pStatTimeline.h new file mode 100644 index 0000000000..0a9d0f779f --- /dev/null +++ b/pandatool/src/pstatserver/pStatTimeline.h @@ -0,0 +1,130 @@ +/** + * 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 pStatTimeline.h + * @author rdb + * @date 2022-02-11 + */ + +#ifndef PSTATTIMELINE_H +#define PSTATTIMELINE_H + +#include "pandatoolbase.h" + +#include "pStatGraph.h" +#include "pStatMonitor.h" +#include "pStatClientData.h" +#include "pdeque.h" + +class PStatFrameData; + +/** + * This is an abstract class that presents the interface for drawing a piano- + * roll type chart: it shows the time spent in each of a number of collectors + * as a horizontal bar of color, with time as the horizontal axis. + * + * This class just pnages all the piano-roll logic; the actual nuts and bolts + * of drawing pixels is left to a user-derived class. + */ +class PStatTimeline : public PStatGraph { +public: + PStatTimeline(PStatMonitor *monitor, int xsize, int ysize); + virtual ~PStatTimeline(); + + void new_data(int thread_index, int frame_number); + bool update_bars(int thread_index, int frame_number); + + INLINE void set_horizontal_scale(double time_width); + INLINE double get_horizontal_scale() const; + INLINE void set_horizontal_scroll(double time_start); + INLINE double get_horizontal_scroll() const; + + INLINE void zoom_to(double time_width, double pivot); + INLINE void zoom_by(double amount, double center); + INLINE void scroll_to(double time_start); + INLINE void scroll_by(double time_start); + + INLINE int timestamp_to_pixel(double time) const; + INLINE double pixel_to_timestamp(int x) const; + INLINE int height_to_pixel(double value) const; + INLINE double pixel_to_height(int y) const; + + std::string get_bar_tooltip(int row, int x) const; + +protected: + void changed_size(int xsize, int ysize); + void force_redraw(); + void force_redraw(int row, int from_x, int to_x); + void normal_guide_bars(); + + virtual void clear_region(); + virtual void begin_draw(); + void draw_thread(int thread_index, double start_time, double end_time); + void draw_row(int thread_index, int row_index, double start_time, double end_time); + virtual void draw_separator(int row); + virtual void draw_guide_bar(int x, GuideBarStyle style); + virtual void draw_bar(int row, int from_x, int to_x, int collector_index, + const std::string &collector_name); + virtual void end_draw(); + virtual void idle(); + + bool animate(double time, double dt); + + class ColorBar { + public: + double _start, _end; + int _collector_index; + int _thread_index; + int _frame_number; + + bool operator < (const ColorBar &other) const { + return _end < other._end; + } + }; + typedef pvector Row; + typedef pvector Rows; + + bool find_bar(int row, int x, ColorBar &bar) const; + + class ThreadRow { + public: + std::string _label; + Rows _rows; + size_t _row_offset = 0; + int _last_frame = -1; + }; + typedef pvector ThreadRows; + ThreadRows _threads; + bool _threads_changed = true; + + enum KeyFlag { + F_left = 1, + F_right = 2, + F_w = 4, + F_a = 8, + F_s = 16, + F_d = 32, + }; + int _keys_held = 0; + double _scroll_speed = 0.0; + double _zoom_speed = 0.0; + double _zoom_center = 0.0; + +private: + double _time_scale; + double _start_time = 0.0; + double _lowest_start_time = 0.0; + double _highest_end_time = 0.0; + bool _have_start_time = false; + double _target_start_time = 0.0; + double _target_time_scale; +}; + +#include "pStatTimeline.I" + +#endif diff --git a/pandatool/src/pstatserver/pStatView.cxx b/pandatool/src/pstatserver/pStatView.cxx index e69959a731..7d26455e7c 100644 --- a/pandatool/src/pstatserver/pStatView.cxx +++ b/pandatool/src/pstatserver/pStatView.cxx @@ -520,15 +520,16 @@ reset_level(PStatViewLevel *level) { if (level->_parent == nullptr) { // This level didn't know its parent before, but now it does. - PStatViewLevel *parent_level = get_level(parent_index); - nassertr(parent_level != level, true); - - level->_parent = parent_level; - parent_level->_children.push_back(level); - parent_level->sort_children(_client_data); - any_changed = true; - - } else if (level->_parent->_collector != parent_index) { + if (level->_collector != 0 || parent_index != 0) { + PStatViewLevel *parent_level = get_level(parent_index); + nassertr(parent_level != level, true); + level->_parent = parent_level; + parent_level->_children.push_back(level); + parent_level->sort_children(_client_data); + any_changed = true; + } + } + else if (level->_parent->_collector != parent_index) { // This level knew about its parent, but now it's something different. PStatViewLevel *old_parent_level = level->_parent; nassertr(old_parent_level != level, true); diff --git a/pandatool/src/win-stats/CMakeLists.txt b/pandatool/src/win-stats/CMakeLists.txt index 4c10e4f560..99166f6ec9 100644 --- a/pandatool/src/win-stats/CMakeLists.txt +++ b/pandatool/src/win-stats/CMakeLists.txt @@ -14,6 +14,7 @@ set(WINSTATS_HEADERS winStatsPianoRoll.h winStatsServer.h winStatsStripChart.h + winStatsTimeline.h ) set(WINSTATS_SOURCES @@ -27,6 +28,7 @@ set(WINSTATS_SOURCES winStatsPianoRoll.cxx winStatsServer.cxx winStatsStripChart.cxx + winStatsTimeline.cxx ) composite_sources(win-stats WINSTATS_SOURCES) diff --git a/pandatool/src/win-stats/winStatsChartMenu.cxx b/pandatool/src/win-stats/winStatsChartMenu.cxx index 026e082489..71d30282f5 100644 --- a/pandatool/src/win-stats/winStatsChartMenu.cxx +++ b/pandatool/src/win-stats/winStatsChartMenu.cxx @@ -93,15 +93,31 @@ do_update() { } // Now rebuild the menu with the new set of entries. - - // The menu item(s) for the thread's frame time goes first. - add_view(_menu, view.get_top_level(), false); - - bool needs_separator = true; MENUITEMINFO mii; memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); + if (_thread_index == 0) { + // Timeline goes first. + WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_timeline, false); + int menu_id = _monitor->get_menu_id(menu_def); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = "Timeline"; + InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); + } + + // The menu item(s) for the thread's frame time goes second. + add_view(_menu, view.get_top_level(), false); + + bool needs_separator = true; + // And then the menu item(s) for each of the level values. const PStatClientData *client_data = _monitor->get_client_data(); int num_toplevel_collectors = client_data->get_num_toplevel_collectors(); @@ -125,24 +141,13 @@ do_update() { } } - // Also menu items for flame graph and piano roll (following a separator). + // Also menu item for piano roll (following a separator). mii.fMask = MIIM_FTYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); { - WinStatsMonitor::MenuDef menu_def(_thread_index, -2, false); - int menu_id = _monitor->get_menu_id(menu_def); - - mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; - mii.fType = MFT_STRING; - mii.wID = menu_id; - mii.dwTypeData = "Flame Graph"; - InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii); - } - - { - WinStatsMonitor::MenuDef menu_def(_thread_index, -1, false); + WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_piano_roll, false); int menu_id = _monitor->get_menu_id(menu_def); mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; @@ -164,37 +169,77 @@ add_view(HMENU parent_menu, const PStatViewLevel *view_level, bool show_level) { const PStatClientData *client_data = _monitor->get_client_data(); std::string collector_name = client_data->get_collector_name(collector); - WinStatsMonitor::MenuDef menu_def(_thread_index, collector, show_level); - int menu_id = _monitor->get_menu_id(menu_def); - MENUITEMINFO mii; memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; - mii.fType = MFT_STRING; - mii.wID = menu_id; - mii.dwTypeData = (char *)collector_name.c_str(); - InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii); - int num_children = view_level->get_num_children(); - if (num_children > 1) { - // If the collector has more than one child, add a menu entry to go - // directly to each of its children. - HMENU submenu = CreatePopupMenu(); - std::string submenu_name = collector_name + " components"; + if (show_level && num_children == 0) { + // For a level collector without children, no point in making a submenu. + WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_strip_chart, show_level); + int menu_id = _monitor->get_menu_id(menu_def); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = (char *)collector_name.c_str(); + InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii); + return; + } + + HMENU menu; + if (!show_level && collector == 0 && num_children == 0) { + // Root collector without children, just add the options directly to the + // parent menu. + menu = parent_menu; + } + else { + // Create a submenu. + menu = CreatePopupMenu(); mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU; mii.fType = MFT_STRING; - mii.hSubMenu = submenu; - mii.dwTypeData = (char *)submenu_name.c_str(); + mii.hSubMenu = menu; + mii.dwTypeData = (char *)collector_name.c_str(); InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii); + } + + { + WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_strip_chart, show_level); + int menu_id = _monitor->get_menu_id(menu_def); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = "Open Strip Chart"; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii); + } + + if (!show_level) { + if (collector == 0 && num_children == 0) { + collector = -1; + } + + WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_flame_graph); + int menu_id = _monitor->get_menu_id(menu_def); + + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + mii.fType = MFT_STRING; + mii.wID = menu_id; + mii.dwTypeData = "Open Flame Graph"; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii); + } + + if (num_children > 0) { + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii); // Reverse the order since the menus are listed from the top down; we want // to be visually consistent with the graphs, which list these labels from // the bottom up. for (int c = num_children - 1; c >= 0; c--) { - add_view(submenu, view_level->get_child(c), show_level); + add_view(menu, view_level->get_child(c), show_level); } } } diff --git a/pandatool/src/win-stats/winStatsFlameGraph.cxx b/pandatool/src/win-stats/winStatsFlameGraph.cxx index 90ad7cd280..80c3e7a2b8 100644 --- a/pandatool/src/win-stats/winStatsFlameGraph.cxx +++ b/pandatool/src/win-stats/winStatsFlameGraph.cxx @@ -18,8 +18,8 @@ #include -static const int default_flame_graph_width = 800; -static const int default_flame_graph_height = 150; +static const int default_flame_graph_width = 1085; +static const int default_flame_graph_height = 210; bool WinStatsFlameGraph::_window_class_registered = false; const char * const WinStatsFlameGraph::_window_class_name = "flame"; @@ -30,7 +30,7 @@ const char * const WinStatsFlameGraph::_window_class_name = "flame"; WinStatsFlameGraph:: WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index, int collector_index) : - PStatFlameGraph(monitor, monitor->get_view(thread_index), + PStatFlameGraph(monitor, thread_index, collector_index, monitor->get_pixel_scale() * default_flame_graph_width / 4, monitor->get_pixel_scale() * default_flame_graph_height / 4), @@ -127,34 +127,7 @@ set_time_units(int unit_mask) { */ void WinStatsFlameGraph:: on_click_label(int collector_index) { - int prev_collector_index = get_collector_index(); - if (collector_index == prev_collector_index && collector_index != 0) { - // Clicking on the top label means to go up to the parent level. - const PStatClientData *client_data = - WinStatsGraph::_monitor->get_client_data(); - if (client_data->has_collector(collector_index)) { - const PStatCollectorDef &def = - client_data->get_collector_def(collector_index); - collector_index = def._parent_index; - set_collector_index(collector_index); - } - } - else { - // Clicking on any other label means to focus on that. - set_collector_index(collector_index); - } - - // Change the root collector to show the full name. - if (prev_collector_index != collector_index) { - auto it = _labels.find(prev_collector_index); - if (it != _labels.end()) { - it->second->update_text(false); - } - it = _labels.find(collector_index); - if (it != _labels.end()) { - it->second->update_text(true); - } - } + set_collector_index(collector_index); } /** @@ -164,6 +137,11 @@ void WinStatsFlameGraph:: on_enter_label(int collector_index) { if (collector_index != _highlighted_index) { _highlighted_index = collector_index; + clear_graph_tooltip(); + + if (!get_average_mode()) { + PStatFlameGraph::force_redraw(); + } } } @@ -174,53 +152,11 @@ void WinStatsFlameGraph:: on_leave_label(int collector_index) { if (collector_index == _highlighted_index && collector_index != -1) { _highlighted_index = -1; - } -} -/** - * Called when the mouse hovers over a label, and should return the text that - * should appear on the tooltip. - */ -std::string WinStatsFlameGraph:: -get_label_tooltip(int collector_index) const { - return PStatFlameGraph::get_label_tooltip(collector_index); -} - -/** - * Repositions the labels. - */ -void WinStatsFlameGraph:: -update_labels() { - if (_graph_window) { - PStatFlameGraph::update_labels(); - } -} - -/** - * Repositions a label. If width is 0, the label should be deleted. - */ -void WinStatsFlameGraph:: -update_label(int collector_index, int row, int x, int width) { - WinStatsLabel *label; - - auto it = _labels.find(collector_index); - if (it != _labels.end()) { - if (width == 0) { - delete it->second; - _labels.erase(it); - return; + if (!get_average_mode()) { + PStatFlameGraph::force_redraw(); } - label = it->second; - } else { - if (width == 0) { - return; - } - label = new WinStatsLabel(WinStatsGraph::_monitor, this, _thread_index, collector_index, false, false); - _labels[collector_index] = label; - label->setup(_graph_window); } - - label->set_pos(x, _ysize - 2 - row * label->get_height(), std::min(width, _xsize - 2)); } /** @@ -263,6 +199,56 @@ begin_draw() { for (int i = 0; i < num_guide_bars; i++) { draw_guide_bar(_bitmap_dc, get_guide_bar(i)); } + + SelectObject(_bitmap_dc, WinStatsGraph::_monitor->get_font()); + SelectObject(_bitmap_dc, GetStockObject(NULL_PEN)); + SetBkMode(_bitmap_dc, TRANSPARENT); + SetTextAlign(_bitmap_dc, TA_LEFT | TA_TOP | TA_NOUPDATECP); +} + +/** + * Should be overridden by the user class. Should draw a single bar at the + * indicated location. + */ +void WinStatsFlameGraph:: +draw_bar(int depth, int from_x, int to_x, int collector_index) { + int bottom = get_ysize() - 1 - depth * _pixel_scale * 5; + int top = bottom - _pixel_scale * 5; + + bool is_highlighted = collector_index == _highlighted_index; + HBRUSH brush = get_collector_brush(collector_index, is_highlighted); + + if (to_x < from_x + 2) { + // It's just a tiny sliver. This is a more reliable way to draw it. + RECT rect = {from_x, top + 1, from_x + 1, bottom - 1}; + FillRect(_bitmap_dc, &rect, brush); + } + else { + SelectObject(_bitmap_dc, brush); + RoundRect(_bitmap_dc, + std::max(from_x, -_pixel_scale - 1), + top, + std::min(std::max(to_x, from_x + 1), get_xsize() + _pixel_scale), + bottom, + _pixel_scale, + _pixel_scale); + + int left = std::max(from_x, 0) + _pixel_scale / 2; + int right = std::min(to_x, get_xsize()) - _pixel_scale / 2; + + if ((to_x - from_x) >= _pixel_scale * 4) { + // Only bother drawing the text if we've got some space to draw on. + // Choose a suitable foreground color. + SetTextColor(_bitmap_dc, get_collector_text_color(collector_index, is_highlighted)); + + const PStatClientData *client_data = WinStatsGraph::_monitor->get_client_data(); + const std::string &name = client_data->get_collector_name(collector_index); + + RECT rect = {left, top, right, bottom}; + DrawText(_bitmap_dc, name.data(), name.size(), + &rect, DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER); + } + } } /** @@ -281,6 +267,15 @@ void WinStatsFlameGraph:: idle() { } +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool WinStatsFlameGraph:: +animate(double time, double dt) { + return PStatFlameGraph::animate(time, dt); +} + /** * */ @@ -300,10 +295,27 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case BN_CLICKED: if ((HWND)lparam == _average_check_box) { int result = SendMessage(_average_check_box, BM_GETCHECK, 0, 0); - set_average_mode(result == BST_CHECKED); + if (result == BST_CHECKED) { + set_average_mode(true); + start_animation(); + } else { + set_average_mode(false); + } return 0; } break; + + case 101: + set_collector_index(_popup_index); + return 0; + + case 102: + WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index, false); + return 0; + + case 103: + WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index); + return 0; } break; @@ -331,7 +343,25 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { break; case WM_MOUSEMOVE: - if (_drag_mode == DM_new_guide_bar) { + if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { + // When the mouse is over a color bar, highlight it. + int x = LOWORD(lparam); + int y = HIWORD(lparam); + + int collector_index = get_bar_collector(pixel_to_depth(y), x); + on_enter_label(collector_index); + + // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph + // window. + TRACKMOUSEEVENT tme = { + sizeof(TRACKMOUSEEVENT), + TME_LEAVE, + _graph_window, + 0 + }; + TrackMouseEvent(&tme); + } + else if (_drag_mode == DM_new_guide_bar) { // We haven't created the new guide bar yet; we won't until the mouse // comes within the graph's region. int16_t x = LOWORD(lparam); @@ -348,6 +378,13 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; + case WM_MOUSELEAVE: + // When the mouse leaves the graph, stop highlighting. + if (_highlighted_index != -1) { + on_leave_label(_highlighted_index); + } + break; + case WM_LBUTTONUP: if (_drag_mode == DM_guide_bar) { int16_t x = LOWORD(lparam); @@ -364,8 +401,42 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case WM_LBUTTONDBLCLK: { - // Clicking on whitespace in the graph goes to the parent. - on_click_label(get_collector_index()); + // Double-clicking on a color bar in the graph will zoom the graph into + // that collector. + int16_t x = LOWORD(lparam); + int16_t y = HIWORD(lparam); + set_collector_index(get_bar_collector(pixel_to_depth(y), x)); + return 0; + } + break; + + case WM_CONTEXTMENU: + { + POINT point; + if (GetCursorPos(&point)) { + POINT graph_point = point; + if (ScreenToClient(_graph_window, &graph_point)) { + int depth = pixel_to_depth(graph_point.y); + int collector_index = get_bar_collector(depth, graph_point.x); + if (collector_index >= 0) { + _popup_index = collector_index; + HMENU popup = CreatePopupMenu(); + + std::string label = get_bar_tooltip(depth, graph_point.x); + if (!label.empty()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str()); + } + if (collector_index == get_collector_index()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 101, "Set as Focus"); + } else { + AppendMenu(popup, MF_STRING, 101, "Set as Focus"); + } + AppendMenu(popup, MF_STRING, 102, "Open Strip Chart"); + AppendMenu(popup, MF_STRING, 103, "Open Flame Graph"); + TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr); + } + } + } return 0; } break; @@ -425,6 +496,15 @@ additional_graph_window_paint(HDC hdc) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsFlameGraph:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return get_bar_tooltip(pixel_to_depth(mouse_y), mouse_x); +} + /** * Based on the mouse position within the window's client area, look for * draggable things the mouse might be hovering over and return the @@ -474,6 +554,14 @@ move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysiz } } +/** + * Converts a pixel to a depth index. + */ +int WinStatsFlameGraph:: +pixel_to_depth(int y) const { + return (get_ysize() - 1 - y) / (_pixel_scale * 5); +} + /** * Draws the line for the indicated guide bar on the graph. */ @@ -554,6 +642,7 @@ create_window() { register_window_class(application); std::string window_title = get_title_text(); + POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos(); RECT win_rect = { 0, 0, @@ -565,11 +654,13 @@ create_window() { AdjustWindowRect(&win_rect, graph_window_style, FALSE); _window = - CreateWindow(_window_class_name, window_title.c_str(), graph_window_style, - CW_USEDEFAULT, CW_USEDEFAULT, - win_rect.right - win_rect.left, - win_rect.bottom - win_rect.top, - WinStatsGraph::_monitor->get_window(), nullptr, application, 0); + CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name, + window_title.c_str(), graph_window_style, + window_pos.x, window_pos.y, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), + nullptr, application, 0); if (!_window) { nout << "Could not create FlameGraph window!\n"; exit(1); @@ -586,6 +677,7 @@ create_window() { if (get_average_mode()) { SendMessage(_average_check_box, BM_SETCHECK, BST_CHECKED, 0); + start_animation(); } // Ensure that the window is on top of the stack. diff --git a/pandatool/src/win-stats/winStatsFlameGraph.h b/pandatool/src/win-stats/winStatsFlameGraph.h index 262f05979e..92dd48be36 100644 --- a/pandatool/src/win-stats/winStatsFlameGraph.h +++ b/pandatool/src/win-stats/winStatsFlameGraph.h @@ -28,7 +28,7 @@ class WinStatsLabel; class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph { public: WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index, - int collector_index=0); + int collector_index=-1); virtual ~WinStatsFlameGraph(); virtual void new_data(int thread_index, int frame_number); @@ -39,28 +39,30 @@ public: virtual void on_click_label(int collector_index); virtual void on_enter_label(int collector_index); virtual void on_leave_label(int collector_index); - virtual std::string get_label_tooltip(int collector_index) const; protected: - virtual void update_labels(); - virtual void update_label(int collector_index, int row, int x, int width); virtual void normal_guide_bars(); void clear_region(); virtual void begin_draw(); + virtual void draw_bar(int depth, int from_x, int to_x, int collector_index); virtual void end_draw(); virtual void idle(); + virtual bool animate(double time, double dt); + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual void additional_window_paint(HDC hdc); virtual void additional_graph_window_paint(HDC hdc); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int mouse_x, int mouse_y, int width, int height); virtual void move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize); private: + int pixel_to_depth(int y) const; void draw_guide_bar(HDC hdc, const GuideBar &bar); void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar); void create_window(); @@ -69,9 +71,8 @@ private: static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); std::string _net_value_text; - pmap _labels; - HWND _average_check_box; + int _popup_index = -1; static bool _window_class_registered; static const char * const _window_class_name; diff --git a/pandatool/src/win-stats/winStatsGraph.cxx b/pandatool/src/win-stats/winStatsGraph.cxx index a95c95f46d..f57d7c1959 100644 --- a/pandatool/src/win-stats/winStatsGraph.cxx +++ b/pandatool/src/win-stats/winStatsGraph.cxx @@ -14,6 +14,7 @@ #include "winStatsGraph.h" #include "winStatsMonitor.h" #include "winStatsLabelStack.h" +#include "trueClock.h" #include "convert_srgb.h" #include @@ -21,7 +22,7 @@ #define IDC_GRAPH 100 DWORD WinStatsGraph::graph_window_style = -WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE; + WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE; /** * @@ -32,6 +33,7 @@ WinStatsGraph(WinStatsMonitor *monitor) : { _window = 0; _graph_window = 0; + _tooltip_window = 0; _sizewe_cursor = LoadCursor(nullptr, IDC_SIZEWE); _hand_cursor = LoadCursor(nullptr, IDC_HAND); _bitmap = 0; @@ -47,9 +49,11 @@ WinStatsGraph(WinStatsMonitor *monitor) : _dark_color = RGB(51, 51, 51); _light_color = RGB(154, 154, 154); _user_guide_bar_color = RGB(130, 150, 255); + _frame_guide_bar_color = RGB(255, 10, 10); _dark_pen = CreatePen(PS_SOLID, 1, _dark_color); _light_pen = CreatePen(PS_SOLID, 1, _light_color); _user_guide_bar_pen = CreatePen(PS_DASH, 1, _user_guide_bar_color); + _frame_guide_bar_pen = CreatePen(PS_DASH, 1, _frame_guide_bar_color); _drag_mode = DM_none; _potential_drag_mode = DM_none; @@ -69,12 +73,14 @@ WinStatsGraph:: DeleteObject(_dark_pen); DeleteObject(_light_pen); DeleteObject(_user_guide_bar_pen); + DeleteObject(_frame_guide_bar_pen); for (auto &item : _brushes) { DeleteObject(item.second.first); DeleteObject(item.second.second); } _brushes.clear(); + _text_colors.clear(); if (_graph_window) { DestroyWindow(_graph_window); @@ -85,6 +91,11 @@ WinStatsGraph:: DestroyWindow(_window); _window = 0; } + + if (_tooltip_window) { + DestroyWindow(_tooltip_window); + _tooltip_window = 0; + } } /** @@ -150,6 +161,13 @@ void WinStatsGraph:: on_click_label(int collector_index) { } +/** + * Called when a pop-up menu should be shown for the label. + */ +void WinStatsGraph:: +on_popup_label(int collector_index) { +} + /** * Called when the user hovers the mouse over a label. */ @@ -157,6 +175,7 @@ void WinStatsGraph:: on_enter_label(int collector_index) { if (collector_index != _highlighted_index) { _highlighted_index = collector_index; + clear_graph_tooltip(); force_redraw(); } } @@ -168,6 +187,7 @@ void WinStatsGraph:: on_leave_label(int collector_index) { if (collector_index == _highlighted_index && collector_index != -1) { _highlighted_index = -1; + clear_graph_tooltip(); force_redraw(); } } @@ -181,6 +201,16 @@ get_label_tooltip(int collector_index) const { return std::string(); } +/** + * Hides the graph tooltip. + */ +void WinStatsGraph:: +clear_graph_tooltip() { + if (_tooltip_window != 0) { + SendMessage(_tooltip_window, TTM_POP, 0, 0); + } +} + /** * Returns the window handle of the surrounding window. */ @@ -229,6 +259,28 @@ move_label_stack() { } } +/** + * Turns on the animation timer, if it hasn't already been turned on. + */ +void WinStatsGraph:: +start_animation() { + if (!_timer_running) { + TrueClock *clock = TrueClock::get_global_ptr(); + _time = clock->get_short_time(); + SetTimer(_window, 0x100, 16, nullptr); + _timer_running = true; + } +} + +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool WinStatsGraph:: +animate(double time, double dt) { + return false; +} + /** * Returns a brush suitable for drawing in the indicated collector's color. */ @@ -256,6 +308,29 @@ get_collector_brush(int collector_index, bool highlight) { return highlight ? hbrush : brush; } +/** + * Returns a text color suitable for the given collector. + */ +COLORREF WinStatsGraph:: +get_collector_text_color(int collector_index, bool highlight) { + TextColors::iterator tci; + tci = _text_colors.find(collector_index); + if (tci != _text_colors.end()) { + return highlight ? (*tci).second.second : (*tci).second.first; + } + + LRGBColor rgb = _monitor->get_collector_color(collector_index); + double bright = + rgb[0] * 0.2126 + + rgb[1] * 0.7152 + + rgb[2] * 0.0722; + COLORREF color = bright >= 0.5 ? RGB(0, 0, 0) : RGB(255, 255, 255); + COLORREF hcolor = bright * 0.75 >= 0.5 ? RGB(0, 0, 0) : RGB(255, 255, 255); + + _text_colors[collector_index] = std::make_pair(color, hcolor); + return highlight ? hcolor : color; +} + /** * This window_proc should be called up to by the derived classes for any * messages that are not specifically handled by the derived class. @@ -339,8 +414,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { case WM_MOUSEMOVE: if (_drag_mode == DM_left_margin) { int16_t x = LOWORD(lparam); - _left_margin += (x - _drag_start_x); - _drag_start_x = x; + int new_left_margin = _left_margin + (x - _drag_start_x); + _left_margin = std::max(new_left_margin, _pixel_scale * 2); + _drag_start_x = x - (new_left_margin - _left_margin); InvalidateRect(hwnd, nullptr, TRUE); move_label_stack(); return 0; @@ -407,6 +483,33 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; + case WM_TIMER: + { + TrueClock *clock = TrueClock::get_global_ptr(); + double new_time = clock->get_short_time(); + if (!animate(new_time, new_time - _time)) { + KillTimer(hwnd, 0x100); + _timer_running = false; + } + _time = new_time; + } + return 0; + + case WM_NOTIFY: + switch (((LPNMHDR)lparam)->code) { + case TTN_GETDISPINFO: + { + NMTTDISPINFO &info = *(NMTTDISPINFO *)lparam; + POINT point; + if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) { + _tooltip_text = get_graph_tooltip(point.x, point.y); + info.lpszText = (char *)_tooltip_text.c_str(); + } + } + return 0; + } + break; + default: break; } @@ -468,6 +571,15 @@ void WinStatsGraph:: additional_graph_window_paint(HDC hdc) { } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsGraph:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return std::string(); +} + /** * Based on the mouse position within the window's client area, look for * draggable things the mouse might be hovering over and return the @@ -576,6 +688,25 @@ create_graph_window() { EnableWindow(_graph_window, TRUE); SetWindowSubclass(_graph_window, &static_graph_subclass_proc, 1234, (DWORD_PTR)this); + + // Create the tooltip window. This will cause a TTN_GETDISPINFO message to + // be sent to the window to acquire the tooltip text. + _tooltip_window = CreateWindow(TOOLTIPS_CLASS, nullptr, + WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + _window, nullptr, + application, nullptr); + + if (_tooltip_window != 0) { + TOOLINFO info = { 0 }; + info.cbSize = sizeof(info); + info.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + info.hwnd = _window; + info.uId = (UINT_PTR)_graph_window; + info.lpszText = LPSTR_TEXTCALLBACK; + SendMessage(_tooltip_window, TTM_ADDTOOL, 0, (LPARAM)&info); + } } /** diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h index 388a78323f..1cfc1bcee0 100644 --- a/pandatool/src/win-stats/winStatsGraph.h +++ b/pandatool/src/win-stats/winStatsGraph.h @@ -40,6 +40,7 @@ public: DM_guide_bar, DM_new_guide_bar, DM_sizing, + DM_pan, }; public: @@ -57,10 +58,13 @@ public: void user_guide_bars_changed(); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); virtual void on_enter_label(int collector_index); virtual void on_leave_label(int collector_index); virtual std::string get_label_tooltip(int collector_index) const; + void clear_graph_tooltip(); + HWND get_window(); protected: @@ -69,13 +73,18 @@ protected: void setup_label_stack(); void move_label_stack(); + void start_animation(); + virtual bool animate(double time, double dt); + HBRUSH get_collector_brush(int collector_index, bool highlight = false); + COLORREF get_collector_text_color(int collector_index, bool highlight = false); LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual void additional_window_paint(HDC hdc); virtual void additional_graph_window_paint(HDC hdc); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int mouse_x, int mouse_y, int width, int height); virtual void set_drag_mode(DragMode drag_mode); @@ -88,10 +97,15 @@ protected: typedef pmap > Brushes; Brushes _brushes; + typedef pmap > TextColors; + TextColors _text_colors; + WinStatsMonitor *_monitor; HWND _window; HWND _graph_window; + HWND _tooltip_window; WinStatsLabelStack _label_stack; + std::string _tooltip_text; HCURSOR _sizewe_cursor; HCURSOR _hand_cursor; @@ -108,9 +122,11 @@ protected: COLORREF _dark_color; COLORREF _light_color; COLORREF _user_guide_bar_color; + COLORREF _frame_guide_bar_color; HPEN _dark_pen; HPEN _light_pen; HPEN _user_guide_bar_pen; + HPEN _frame_guide_bar_pen; DragMode _drag_mode; DragMode _potential_drag_mode; @@ -122,6 +138,9 @@ protected: bool _pause; + bool _timer_running = false; + double _time; + private: void setup_bitmap(int xsize, int ysize); void release_bitmap(); diff --git a/pandatool/src/win-stats/winStatsLabel.cxx b/pandatool/src/win-stats/winStatsLabel.cxx index 51b9cb9208..595d581a80 100644 --- a/pandatool/src/win-stats/winStatsLabel.cxx +++ b/pandatool/src/win-stats/winStatsLabel.cxx @@ -66,7 +66,7 @@ WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph, } else { _fg_color = RGB(255, 255, 255); } - if (bright >= 0.5 * 0.75) { + if (bright * 0.75 >= 0.5) { _highlight_fg_color = RGB(0, 0, 0); } else { _highlight_fg_color = RGB(255, 255, 255); @@ -282,6 +282,10 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { _graph->on_click_label(_collector_index); return 0; + case WM_CONTEXTMENU: + _graph->on_popup_label(_collector_index); + return 0; + case WM_MOUSEMOVE: { // When the mouse enters the label area, highlight the label. @@ -318,13 +322,23 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { HFONT hfnt = _monitor->get_font(); SelectObject(hdc, hfnt); - SetTextAlign(hdc, (_align_right ? TA_RIGHT : TA_LEFT) | TA_TOP); + SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP); SetBkMode(hdc, TRANSPARENT); SetTextColor(hdc, (_highlight || _mouse_within) ? _highlight_fg_color : _fg_color); - TextOut(hdc, _align_right ? (_width - _right_margin) : _left_margin, - _top_margin, _text.data(), _text.length()); + if (_width > 8) { + UINT format = DT_END_ELLIPSIS | DT_SINGLELINE; + if (_align_right) { + format |= DT_RIGHT; + } else { + format |= DT_LEFT; + } + + RECT margins = { _left_margin, _top_margin, _width - _right_margin, _height - _bottom_margin }; + DrawText(hdc, _text.data(), _text.length(), &margins, format); + } + EndPaint(hwnd, &ps); return 0; } diff --git a/pandatool/src/win-stats/winStatsMonitor.I b/pandatool/src/win-stats/winStatsMonitor.I index 405f0a39f5..6658a5ab70 100644 --- a/pandatool/src/win-stats/winStatsMonitor.I +++ b/pandatool/src/win-stats/winStatsMonitor.I @@ -14,10 +14,11 @@ /** * */ -WinStatsMonitor::MenuDef:: -MenuDef(int thread_index, int collector_index, bool show_level) : +INLINE WinStatsMonitor::MenuDef:: +MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) : _thread_index(thread_index), _collector_index(collector_index), + _chart_type(chart_type), _show_level(show_level) { } @@ -25,7 +26,7 @@ MenuDef(int thread_index, int collector_index, bool show_level) : /** * */ -bool WinStatsMonitor::MenuDef:: +INLINE bool WinStatsMonitor::MenuDef:: operator < (const MenuDef &other) const { if (_thread_index != other._thread_index) { return _thread_index < other._thread_index; @@ -33,5 +34,8 @@ operator < (const MenuDef &other) const { if (_collector_index != other._collector_index) { return _collector_index < other._collector_index; } + if (_chart_type != other._chart_type) { + return _chart_type < other._chart_type; + } return (int)_show_level < (int)other._show_level; } diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx index 7c877c28fd..b6a80c7151 100644 --- a/pandatool/src/win-stats/winStatsMonitor.cxx +++ b/pandatool/src/win-stats/winStatsMonitor.cxx @@ -16,11 +16,16 @@ #include "winStatsStripChart.h" #include "winStatsPianoRoll.h" #include "winStatsFlameGraph.h" +#include "winStatsTimeline.h" #include "winStatsChartMenu.h" #include "winStatsMenuId.h" +#include "pStatFrameData.h" #include "pStatGraph.h" #include "pStatCollectorDef.h" -#include "indent.h" + +#include + +#include bool WinStatsMonitor::_window_class_registered = false; const char * const WinStatsMonitor::_window_class_name = "monitor"; @@ -191,13 +196,14 @@ new_thread(int thread_index) { */ void WinStatsMonitor:: new_data(int thread_index, int frame_number) { - Graphs::iterator gi; - for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) { - WinStatsGraph *graph = (*gi); + for (WinStatsGraph *graph : _graphs) { graph->new_data(thread_index, frame_number); } -} + if (thread_index == 0) { + update_status_bar(); + } +} /** * Called whenever the connection to the client has been lost. This is a @@ -230,16 +236,21 @@ idle() { const PStatThreadData *thread_data = get_client_data()->get_thread_data(0); double frame_rate = thread_data->get_frame_rate(); if (frame_rate != 0.0f) { + // The leading tab centers the text in the status bar. char buffer[128]; - sprintf(buffer, "%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate); + sprintf(buffer, "\t%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate); MENUITEMINFO mii; memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_STRING; - mii.dwTypeData = buffer; + mii.dwTypeData = buffer + 1; // chop off leading tab SetMenuItemInfo(_menu_bar, MI_frame_rate_label, FALSE, &mii); DrawMenuBar(_window); + + if (_status_bar) { + SendMessage(_status_bar, WM_SETTEXT, 0, (LPARAM)buffer); + } } } @@ -288,6 +299,18 @@ get_pixel_scale() const { return _pixel_scale; } +/** + * Returns an amount by which to offset the next window position. + */ +POINT WinStatsMonitor:: +get_new_window_pos() { + int offset = _graphs.size() * 10 * _pixel_scale; + POINT pt; + pt.x = offset + _client_origin.x; + pt.y = offset + _client_origin.y; + return pt; +} + /** * Opens a new strip chart showing the indicated data. */ @@ -319,8 +342,21 @@ open_piano_roll(int thread_index) { * Opens a new flame graph showing the indicated data. */ void WinStatsMonitor:: -open_flame_graph(int thread_index) { - WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index); +open_flame_graph(int thread_index, int collector_index) { + WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index); + add_graph(graph); + + graph->set_time_units(_time_units); + graph->set_scroll_speed(_scroll_speed); + graph->set_pause(_pause); +} + +/** + * Opens a new timeline. + */ +void WinStatsMonitor:: +open_timeline() { + WinStatsTimeline *graph = new WinStatsTimeline(this); add_graph(graph); graph->set_time_units(_time_units); @@ -334,7 +370,7 @@ open_flame_graph(int thread_index) { */ const WinStatsMonitor::MenuDef &WinStatsMonitor:: lookup_menu(int menu_id) const { - static MenuDef invalid(0, 0, false); + static MenuDef invalid(0, 0, CT_strip_chart, false); int menu_index = menu_id - MI_new_chart; nassertr(menu_index >= 0 && menu_index < (int)_menu_by_id.size(), invalid); return _menu_by_id[menu_index]; @@ -516,10 +552,16 @@ create_window() { SetWindowLongPtr(_window, 0, (LONG_PTR)this); + create_status_bar(application); + // For some reason, SW_SHOWNORMAL doesn't always work, but SW_RESTORE seems // to. ShowWindow(_window, SW_RESTORE); SetForegroundWindow(_window); + + _client_origin.x = 0; + _client_origin.y = 0; + ClientToScreen(_window, &_client_origin); } /** @@ -636,6 +678,165 @@ setup_frame_rate_label() { InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii); } +/** + * Sets up a status bar at the bottom of the screen showing assorted level + * values. + */ +void WinStatsMonitor:: +create_status_bar(HINSTANCE application) { + _status_bar = CreateWindow(STATUSCLASSNAME, nullptr, + SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE, + 0, 0, 0, 0, + _window, (HMENU)0, application, nullptr); + + update_status_bar(); + + + ShowWindow(_status_bar, SW_SHOW); + UpdateWindow(_status_bar); + + InvalidateRect(_status_bar, NULL, TRUE); +} + +/** + * Updates the status bar. + */ +void WinStatsMonitor:: +update_status_bar() { + const PStatClientData *client_data = get_client_data(); + if (client_data == nullptr) { + return; + } + + const PStatThreadData *thread_data = get_client_data()->get_thread_data(0); + if (thread_data == nullptr || thread_data->is_empty()) { + return; + } + const PStatFrameData &frame_data = thread_data->get_latest_frame(); + + // Gather the top-level collector list. + pvector parts; + pvector collectors; + size_t total_chars = 0; + + int num_toplevel_collectors = client_data->get_num_toplevel_collectors(); + for (int tc = 0; tc < num_toplevel_collectors; tc++) { + int collector = client_data->get_toplevel_collector(tc); + if (client_data->has_collector(collector) && + client_data->get_collector_has_level(collector, 0)) { + PStatView &view = get_level_view(collector, 0); + view.set_to_frame(frame_data); + double value = view.get_net_value(); + if (value == 0.0) { + // Don't include it unless we've included it before. + if (std::find(_status_bar_collectors.begin(), _status_bar_collectors.end(), collector) == _status_bar_collectors.end()) { + continue; + } + } + + const PStatCollectorDef &def = client_data->get_collector_def(collector); + std::string text = "\t" + def._name; + text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); + total_chars += text.size(); + parts.push_back(text); + collectors.push_back(collector); + } + } + + size_t cur_size = _status_bar_collectors.size(); + _status_bar_collectors = std::move(collectors); + + // Allocate an array for holding the right edge coordinates. + HLOCAL hloc = LocalAlloc(LHND, sizeof(int) * (parts.size() + 1)); + PINT sizes = (PINT)LocalLock(hloc); + + // Allocate the left-most slot for the framerate indicator. + double offset = 28.0 * _pixel_scale; + sizes[0] = (int)(offset + 0.5); + + if (!parts.empty()) { + // Distribute the sizes roughly based on the number of characters. It's not + // as good as measuring the text, but it's good enough. + RECT rect; + double width_per_char = 0; + GetClientRect(_status_bar, &rect); + // Leave room for the grip. + rect.right -= _pixel_scale * 4; + width_per_char = (rect.right - rect.left - offset) / (double)total_chars; + + // If we get below a minimum width, start chopping parts off. + while (!parts.empty() && width_per_char < _pixel_scale * 1.5) { + total_chars -= parts.back().size(); + parts.pop_back(); + width_per_char = (rect.right - rect.left - offset) / (double)total_chars; + } + + if (!parts.empty()) { + for (size_t i = 0; i < parts.size(); ++i) { + offset += (parts[i].size()) * width_per_char; + sizes[i + 1] = (int)(offset + 0.5); + } + } else { + // No room for any collectors; the framerate can take up the whole width. + sizes[0] = rect.right - rect.left; + } + } + + SendMessage(_status_bar, SB_SETPARTS, (WPARAM)(parts.size() + 1), (LPARAM)sizes); + + LocalUnlock(hloc); + LocalFree(hloc); + + for (size_t i = 0; i < parts.size(); ++i) { + SendMessage(_status_bar, SB_SETTEXT, i + 1, (LPARAM)parts[i].c_str()); + } +} + +/** + * Called when someone right-clicks on a part of the status bar. + */ +void WinStatsMonitor:: +show_popup_menu(int collector) { + POINT point; + if (!GetCursorPos(&point)) { + return; + } + + const PStatClientData *client_data = get_client_data(); + if (client_data == nullptr) { + return; + } + + PStatView &level_view = get_level_view(collector, 0); + const PStatViewLevel *view_level = level_view.get_top_level(); + int num_children = view_level->get_num_children(); + if (num_children == 0) { + return; + } + + HMENU popup = CreatePopupMenu(); + + // Reverse the order since the menus are listed from the top down; we want + // to be visually consistent with the graphs, which list these labels from + // the bottom up. + for (int c = num_children - 1; c >= 0; c--) { + const PStatViewLevel *child_level = view_level->get_child(c); + + int child_collector = child_level->get_collector(); + MenuDef menu_def(0, child_collector, CT_strip_chart, true); + int menu_id = get_menu_id(menu_def); + + double value = child_level->get_net_value(); + + const PStatCollectorDef &def = client_data->get_collector_def(child_collector); + std::string text = def._name; + text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); + AppendMenu(popup, MF_STRING, menu_id, text.c_str()); + } + + TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr); +} + /** * Registers the window class for the monitor window, if it has not already * been registered. @@ -691,6 +892,71 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { close(); break; + case WM_WINDOWPOSCHANGED: + if (!_graphs.empty()) { + RECT status_bar_rect; + GetWindowRect(_status_bar, &status_bar_rect); + + RECT client_rect; + GetClientRect(_window, &client_rect); + MapWindowPoints(_window, nullptr, (POINT *)&client_rect, 2); + + int delta_x = client_rect.left - _client_origin.x; + int delta_y = client_rect.top - _client_origin.y; + _client_origin.x = client_rect.left; + _client_origin.y = client_rect.top; + + int iconic_offset = 0; + + for (WinStatsGraph *graph : _graphs) { + RECT child_rect; + HWND window = graph->get_window(); + if (GetWindowRect(window, &child_rect)) { + if (IsIconic(window)) { + // Keep it glued to the bottom-left corner of the parent window. + child_rect.left = client_rect.left + iconic_offset; + child_rect.top = client_rect.bottom - (child_rect.bottom - child_rect.top) - (status_bar_rect.bottom - status_bar_rect.top); + iconic_offset += (child_rect.right - child_rect.left); + } else { + child_rect.left += delta_x; + child_rect.top += delta_y; + } + SetWindowPos(window, 0, child_rect.left, child_rect.top, 0, 0, + SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOACTIVATE); + } + } + } + break; + + case WM_SIZE: + if (_status_bar) { + SendMessage(_status_bar, WM_SIZE, 0, 0); + update_status_bar(); + } + break; + + case WM_NOTIFY: + if (((LPNMHDR)lparam)->code == NM_DBLCLK) { + NMMOUSE &mouse = *(NMMOUSE *)lparam; + if (mouse.dwItemSpec == 0) { + open_strip_chart(0, 0, false); + } + else if (mouse.dwItemSpec >= 1 && mouse.dwItemSpec <= _status_bar_collectors.size()) { + int collector = _status_bar_collectors[mouse.dwItemSpec - 1]; + open_strip_chart(0, collector, true); + } + return TRUE; + } + else if (((LPNMHDR)lparam)->code == NM_RCLICK) { + NMMOUSE &mouse = *(NMMOUSE *)lparam; + if (mouse.dwItemSpec >= 1 && + mouse.dwItemSpec <= _status_bar_collectors.size()) { + int collector = _status_bar_collectors[mouse.dwItemSpec - 1]; + show_popup_menu(collector); + } + } + break; + case WM_COMMAND: if (HIWORD(wparam) <= 1) { int menu_id = LOWORD(wparam); @@ -750,15 +1016,23 @@ handle_menu_command(int menu_id) { default: if (menu_id >= MI_new_chart) { const MenuDef &menu_def = lookup_menu(menu_id); - if (menu_def._collector_index == -2) { - open_flame_graph(menu_def._thread_index); - } - else if (menu_def._collector_index < 0) { - open_piano_roll(menu_def._thread_index); - } - else { + switch (menu_def._chart_type) { + case CT_timeline: + open_timeline(); + break; + + case CT_strip_chart: open_strip_chart(menu_def._thread_index, menu_def._collector_index, menu_def._show_level); + break; + + case CT_flame_graph: + open_flame_graph(menu_def._thread_index, menu_def._collector_index); + break; + + case CT_piano_roll: + open_piano_roll(menu_def._thread_index); + break; } } } diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h index 9da5034a71..9fa4ccf57f 100644 --- a/pandatool/src/win-stats/winStatsMonitor.h +++ b/pandatool/src/win-stats/winStatsMonitor.h @@ -37,13 +37,22 @@ class WinStatsChartMenu; */ class WinStatsMonitor : public PStatMonitor { public: + enum ChartType { + CT_timeline, + CT_strip_chart, + CT_flame_graph, + CT_piano_roll, + }; + class MenuDef { public: - INLINE MenuDef(int thread_index, int collector_index, bool show_level); + INLINE MenuDef(int thread_index, int collector_index, + ChartType chart_type, bool show_level = false); INLINE bool operator < (const MenuDef &other) const; int _thread_index; int _collector_index; + ChartType _chart_type; bool _show_level; }; @@ -68,10 +77,12 @@ public: HWND get_window() const; HFONT get_font() const; int get_pixel_scale() const; + POINT get_new_window_pos(); void open_strip_chart(int thread_index, int collector_index, bool show_level); void open_piano_roll(int thread_index); - void open_flame_graph(int thread_index); + void open_flame_graph(int thread_index, int collector_index = -1); + void open_timeline(); const MenuDef &lookup_menu(int menu_id) const; int get_menu_id(const MenuDef &menu_def); @@ -88,6 +99,9 @@ private: void setup_options_menu(); void setup_speed_menu(); void setup_frame_rate_label(); + void create_status_bar(HINSTANCE application); + void update_status_bar(); + void show_popup_menu(int collector); static void register_window_class(HINSTANCE application); static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); @@ -109,6 +123,9 @@ private: HMENU _menu_bar; HMENU _options_menu; HMENU _speed_menu; + HWND _status_bar; + POINT _client_origin; + pvector _status_bar_collectors; std::string _window_title; int _time_units; double _scroll_speed; diff --git a/pandatool/src/win-stats/winStatsPianoRoll.cxx b/pandatool/src/win-stats/winStatsPianoRoll.cxx index 0b22831184..fc70f6f203 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.cxx +++ b/pandatool/src/win-stats/winStatsPianoRoll.cxx @@ -15,8 +15,8 @@ #include "winStatsMonitor.h" #include "numeric_types.h" -static const int default_piano_roll_width = 600; -static const int default_piano_roll_height = 200; +static const int default_piano_roll_width = 800; +static const int default_piano_roll_height = 400; bool WinStatsPianoRoll::_window_class_registered = false; const char * const WinStatsPianoRoll::_window_class_name = "piano"; @@ -51,7 +51,7 @@ WinStatsPianoRoll:: } /** - * Called as each frame's data is made available. There is no gurantee the + * Called as each frame's data is made available. There is no guarantee the * frames will arrive in order, or that all of them will arrive at all. The * monitor should be prepared to accept frames received out-of-order or * missing. @@ -109,6 +109,36 @@ on_click_label(int collector_index) { } } +/** + * Called when the user right-clicks on a label. + */ +void WinStatsPianoRoll:: +on_popup_label(int collector_index) { + POINT point; + if (collector_index >= 0 && GetCursorPos(&point)) { + _popup_index = collector_index; + + HMENU popup = CreatePopupMenu(); + + std::string label = get_label_tooltip(collector_index); + if (!label.empty()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str()); + } + AppendMenu(popup, MF_STRING, 102, "Open Strip Chart"); + AppendMenu(popup, MF_STRING, 103, "Open Flame Graph"); + TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr); + } +} + +/** + * Called when the mouse hovers over a label, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsPianoRoll:: +get_label_tooltip(int collector_index) const { + return PStatPianoRoll::get_label_tooltip(collector_index); +} + /** * Changes the amount of time the width of the horizontal axis represents. * This may force a redraw. @@ -216,6 +246,18 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; + case WM_COMMAND: + switch (LOWORD(wparam)) { + case 102: + WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index, false); + return 0; + + case 103: + WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index); + return 0; + } + break; + default: break; } @@ -333,6 +375,19 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; + case WM_CONTEXTMENU: + { + POINT point; + if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) { + int collector_index = get_collector_under_pixel(point.x, point.y); + if (collector_index >= 0) { + on_popup_label(collector_index); + } + } + return 0; + } + break; + default: break; } @@ -379,6 +434,19 @@ additional_graph_window_paint(HDC hdc) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsPianoRoll:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + int collector_index = get_collector_under_pixel(mouse_x, mouse_y); + if (collector_index >= 0) { + return get_label_tooltip(collector_index); + } + return std::string(); +} + /** * Based on the mouse position within the window's client area, look for * draggable things the mouse might be hovering over and return the @@ -413,7 +481,7 @@ consider_drag_start(int mouse_x, int mouse_y, int width, int height) { * -1. */ int WinStatsPianoRoll:: -get_collector_under_pixel(int xpoint, int ypoint) { +get_collector_under_pixel(int xpoint, int ypoint) const { if (_label_stack.get_num_labels() == 0) { return -1; } @@ -521,7 +589,7 @@ create_window() { WinStatsGraph::_monitor->get_client_data(); std::string thread_name = client_data->get_thread_name(_thread_index); std::string window_title = thread_name + " thread piano roll"; - + POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos(); RECT win_rect = { 0, 0, @@ -533,11 +601,13 @@ create_window() { AdjustWindowRect(&win_rect, graph_window_style, FALSE); _window = - CreateWindow(_window_class_name, window_title.c_str(), graph_window_style, - CW_USEDEFAULT, CW_USEDEFAULT, - win_rect.right - win_rect.left, - win_rect.bottom - win_rect.top, - WinStatsGraph::_monitor->get_window(), nullptr, application, 0); + CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name, + window_title.c_str(), graph_window_style, + window_pos.x, window_pos.y, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), + nullptr, application, 0); if (!_window) { nout << "Could not create PianoRoll window!\n"; exit(1); diff --git a/pandatool/src/win-stats/winStatsPianoRoll.h b/pandatool/src/win-stats/winStatsPianoRoll.h index 5a5a6f1f45..1fc3ac2443 100644 --- a/pandatool/src/win-stats/winStatsPianoRoll.h +++ b/pandatool/src/win-stats/winStatsPianoRoll.h @@ -42,6 +42,8 @@ public: virtual void set_time_units(int unit_mask); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); + virtual std::string get_label_tooltip(int collector_index) const; void set_horizontal_scale(double time_width); protected: @@ -57,11 +59,12 @@ protected: virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual void additional_window_paint(HDC hdc); virtual void additional_graph_window_paint(HDC hdc); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int mouse_x, int mouse_y, int width, int height); private: - int get_collector_under_pixel(int xpoint, int ypoint); + int get_collector_under_pixel(int xpoint, int ypoint) const; void update_labels(); void draw_guide_bar(HDC hdc, const GuideBar &bar); void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar); @@ -71,6 +74,8 @@ private: static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + int _popup_index = -1; + static bool _window_class_registered; static const char * const _window_class_name; }; diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx index a7cf195a63..b20b2d23aa 100644 --- a/pandatool/src/win-stats/winStatsStripChart.cxx +++ b/pandatool/src/win-stats/winStatsStripChart.cxx @@ -18,8 +18,6 @@ #include -using std::string; - static const int default_strip_chart_width = 400; static const int default_strip_chart_height = 100; @@ -33,7 +31,7 @@ WinStatsStripChart:: WinStatsStripChart(WinStatsMonitor *monitor, int thread_index, int collector_index, bool show_level) : PStatStripChart(monitor, - show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index), + show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index), thread_index, collector_index, monitor->get_pixel_scale() * default_strip_chart_width / 4, @@ -82,7 +80,7 @@ new_collector(int collector_index) { } /** - * Called as each frame's data is made available. There is no gurantee the + * Called as each frame's data is made available. There is no guarantee the * frames will arrive in order, or that all of them will arrive at all. The * monitor should be prepared to accept frames received out-of-order or * missing. @@ -90,7 +88,7 @@ new_collector(int collector_index) { void WinStatsStripChart:: new_data(int thread_index, int frame_number) { if (is_title_unknown()) { - string window_title = get_title_text(); + std::string window_title = get_title_text(); if (!is_title_unknown()) { SetWindowText(_window, window_title.c_str()); } @@ -99,7 +97,7 @@ new_data(int thread_index, int frame_number) { if (!_pause) { update(); - string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name()); + std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name()); if (_net_value_text != text) { _net_value_text = text; RECT rect; @@ -193,6 +191,36 @@ on_click_label(int collector_index) { } } +/** + * Called when the user right-clicks on a label. + */ +void WinStatsStripChart:: +on_popup_label(int collector_index) { + POINT point; + if (collector_index >= 0 && GetCursorPos(&point)) { + _popup_index = collector_index; + + HMENU popup = CreatePopupMenu(); + + std::string label = get_label_tooltip(collector_index); + if (!label.empty()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str()); + } + if (collector_index == 0 && get_collector_index() == 0) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 101, "Set as Focus"); + } else { + AppendMenu(popup, MF_STRING, 101, "Set as Focus"); + } + AppendMenu(popup, MF_STRING, 102, "Open Strip Chart"); + if (get_view().get_show_level()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 103, "Open Flame Graph"); + } else { + AppendMenu(popup, MF_STRING, 103, "Open Flame Graph"); + } + TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr); + } +} + /** * Called when the mouse hovers over a label, and should return the text that * should appear on the tooltip. @@ -355,6 +383,19 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { return 0; } break; + + case 101: + set_collector_index(_popup_index); + break; + + case 102: + WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index, + get_view().get_show_level()); + return 0; + + case 103: + WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index); + return 0; } break; @@ -416,9 +457,14 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (_drag_mode == DM_scale) { int16_t y = HIWORD(lparam); - double ratio = 1.0f - ((double)y / (double)get_ysize()); - if (ratio > 0.0f) { - set_vertical_scale(_drag_scale_start / ratio); + double ratio = 1.0 - ((double)y / (double)get_ysize()); + if (ratio > 0.0) { + double new_scale = _drag_scale_start / ratio; + if (!IS_NEARLY_EQUAL(get_vertical_scale(), new_scale)) { + // Disable smoothing while we do this expensive operation. + set_average_mode(false); + set_vertical_scale(_drag_scale_start / ratio); + } } return 0; @@ -475,6 +521,19 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; + case WM_CONTEXTMENU: + { + POINT point; + if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) { + int collector_index = get_collector_under_pixel(point.x, point.y); + if (collector_index >= 0) { + on_popup_label(collector_index); + } + } + return 0; + } + break; + default: break; } @@ -534,6 +593,18 @@ additional_graph_window_paint(HDC hdc) { } } +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsStripChart:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + if (_highlighted_index != -1) { + return get_label_tooltip(_highlighted_index); + } + return std::string(); +} + /** * Based on the mouse position within the window's client area, look for * draggable things the mouse might be hovering over and return the @@ -569,16 +640,7 @@ void WinStatsStripChart:: set_drag_mode(WinStatsGraph::DragMode drag_mode) { WinStatsGraph::set_drag_mode(drag_mode); - switch (_drag_mode) { - case DM_scale: - case DM_left_margin: - case DM_right_margin: - case DM_sizing: - // Disable smoothing for these expensive operations. - set_average_mode(false); - break; - - default: + if (_drag_mode == DM_none) { // Restore smoothing according to the current setting of the check box. int result = SendMessage(_smooth_check_box, BM_GETCHECK, 0, 0); set_average_mode(result == BST_CHECKED); @@ -654,7 +716,7 @@ draw_guide_label(HDC hdc, int x, const PStatGraph::GuideBar &bar, int last_y) { } int y = height_to_pixel(bar._height); - const string &label = bar._label; + const std::string &label = bar._label; SIZE size; GetTextExtentPoint32(hdc, label.data(), label.length(), &size); @@ -691,7 +753,8 @@ create_window() { HINSTANCE application = GetModuleHandle(nullptr); register_window_class(application); - string window_title = get_title_text(); + std::string window_title = get_title_text(); + POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos(); RECT win_rect = { 0, 0, @@ -703,11 +766,13 @@ create_window() { AdjustWindowRect(&win_rect, graph_window_style, FALSE); _window = - CreateWindow(_window_class_name, window_title.c_str(), graph_window_style, - CW_USEDEFAULT, CW_USEDEFAULT, - win_rect.right - win_rect.left, - win_rect.bottom - win_rect.top, - WinStatsGraph::_monitor->get_window(), nullptr, application, 0); + CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name, + window_title.c_str(), graph_window_style, + window_pos.x, window_pos.y, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), + nullptr, application, 0); if (!_window) { nout << "Could not create StripChart window!\n"; exit(1); diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h index dcff22a76e..850faa704c 100644 --- a/pandatool/src/win-stats/winStatsStripChart.h +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -44,6 +44,7 @@ public: virtual void set_time_units(int unit_mask); virtual void set_scroll_speed(double scroll_speed); virtual void on_click_label(int collector_index); + virtual void on_popup_label(int collector_index); virtual std::string get_label_tooltip(int collector_index) const; void set_vertical_scale(double value_height); @@ -62,6 +63,7 @@ protected: virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); virtual void additional_window_paint(HDC hdc); virtual void additional_graph_window_paint(HDC hdc); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; virtual DragMode consider_drag_start(int mouse_x, int mouse_y, int width, int height); virtual void set_drag_mode(DragMode drag_mode); @@ -80,6 +82,7 @@ private: std::string _net_value_text; HWND _smooth_check_box; + int _popup_index = -1; static bool _window_class_registered; static const char * const _window_class_name; diff --git a/pandatool/src/win-stats/winStatsTimeline.cxx b/pandatool/src/win-stats/winStatsTimeline.cxx new file mode 100644 index 0000000000..225def4f89 --- /dev/null +++ b/pandatool/src/win-stats/winStatsTimeline.cxx @@ -0,0 +1,752 @@ +/** + * 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 winStatsTimeline.cxx + * @author rdb + * @date 2022-02-11 + */ + +#include "winStatsTimeline.h" +#include "winStatsMonitor.h" +#include "numeric_types.h" + +static const int default_timeline_width = 1000; +static const int default_timeline_height = 500; + +bool WinStatsTimeline::_window_class_registered = false; +const char * const WinStatsTimeline::_window_class_name = "timeline"; + +/** + * + */ +WinStatsTimeline:: +WinStatsTimeline(WinStatsMonitor *monitor) : + PStatTimeline(monitor, + monitor->get_pixel_scale() * default_timeline_width / 4, + monitor->get_pixel_scale() * default_timeline_height / 4), + WinStatsGraph(monitor) +{ + _left_margin = _pixel_scale * 24; + _right_margin = _pixel_scale * 2; + _top_margin = _pixel_scale * 5; + _bottom_margin = _pixel_scale * 2; + + normal_guide_bars(); + + create_window(); + clear_region(); + + _grid_brush = CreateSolidBrush(RGB(0xdd, 0xdd, 0xdd)); +} + +/** + * + */ +WinStatsTimeline:: +~WinStatsTimeline() { +} + +/** + * Called as each frame's data is made available. There is no guarantee the + * frames will arrive in order, or that all of them will arrive at all. The + * monitor should be prepared to accept frames received out-of-order or + * missing. + */ +void WinStatsTimeline:: +new_data(int thread_index, int frame_number) { + PStatTimeline::new_data(thread_index, frame_number); +} + +/** + * Called when it is necessary to redraw the entire graph. + */ +void WinStatsTimeline:: +force_redraw() { + PStatTimeline::force_redraw(); +} + +/** + * Called when the user has resized the window, forcing a resize of the graph. + */ +void WinStatsTimeline:: +changed_graph_size(int graph_xsize, int graph_ysize) { + PStatTimeline::changed_size(graph_xsize, graph_ysize); +} + +/** + * Erases the chart area. + */ +void WinStatsTimeline:: +clear_region() { + RECT rect = { 0, 0, get_xsize(), get_ysize() }; + FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); +} + +/** + * Erases the chart area in preparation for drawing a bunch of bars. + */ +void WinStatsTimeline:: +begin_draw() { + SelectObject(_bitmap_dc, WinStatsGraph::_monitor->get_font()); + SelectObject(_bitmap_dc, GetStockObject(NULL_PEN)); + SetBkMode(_bitmap_dc, TRANSPARENT); + SetTextAlign(_bitmap_dc, TA_LEFT | TA_TOP | TA_NOUPDATECP); +} + +/** + * Draws a horizontal separator. + */ +void WinStatsTimeline:: +draw_separator(int row) { + int y = (row_to_pixel(row) + row_to_pixel(row + 1)) / 2; + RECT rect = {0, y, get_xsize(), y + _pixel_scale / 3}; + FillRect(_bitmap_dc, &rect, _grid_brush); +} + +/** + * Draws a vertical guide bar. If the row is -1, draws it in all rows. + */ +void WinStatsTimeline:: +draw_guide_bar(int x, GuideBarStyle style) { + int x1 = x - _pixel_scale / 6; + int x2 = x1 + _pixel_scale / 3; + if (style == GBS_frame) { + ++x2; + } + RECT rect = {x1, 0, x2, get_ysize()}; + FillRect(_bitmap_dc, &rect, _grid_brush); +} + +/** + * Draws a single bar in the chart for the indicated row, in the color for the + * given collector, for the indicated horizontal pixel range. + */ +void WinStatsTimeline:: +draw_bar(int row, int from_x, int to_x, int collector_index, + const std::string &collector_name) { + + int top = row_to_pixel(row); + int bottom = row_to_pixel(row + 1); + + bool is_highlighted = row == _highlighted_row && _highlighted_x >= from_x && _highlighted_x < to_x; + HBRUSH brush = get_collector_brush(collector_index, is_highlighted); + + if (to_x < from_x + 2) { + // It's just a tiny sliver. This is a more reliable way to draw it. + RECT rect = {from_x, top + 1, from_x + 1, bottom - 1}; + FillRect(_bitmap_dc, &rect, brush); + + //if (to_x <= from_x + 2) { + // // Draw an arrow pointing to it, if it's so small. + // POINT vertices[] = {{to_x, bottom}, {to_x - _pixel_scale, bottom + _pixel_scale * 2}, {to_x + _pixel_scale, bottom + _pixel_scale * 2}}; + // Polygon(_bitmap_dc, vertices, 3); + //} + } + else { + SelectObject(_bitmap_dc, brush); + RoundRect(_bitmap_dc, + std::max(from_x, -_pixel_scale - 1), + top, + std::min(std::max(to_x, from_x + 1), get_xsize() + _pixel_scale), + bottom, + _pixel_scale, + _pixel_scale); + + if ((to_x - from_x) >= _pixel_scale * 4) { + // Only bother drawing the text if we've got some space to draw on. + // Choose a suitable foreground color. + SetTextColor(_bitmap_dc, get_collector_text_color(collector_index, is_highlighted)); + + // Make sure that the text doesn't run off the chart. + SIZE size; + GetTextExtentPoint32(_bitmap_dc, collector_name.data(), collector_name.size(), &size); + int center = (from_x + to_x) / 2; + int left = std::max(from_x, 0) + _pixel_scale / 2; + int right = std::min(to_x, get_xsize()) - _pixel_scale / 2; + + if (size.cx >= right - left) { + if (right - left < _pixel_scale * 6) { + // It's a really tiny space. Draw a single letter. + RECT rect = {left, top, right, bottom}; + DrawText(_bitmap_dc, collector_name.data(), 1, + &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); + } else { + // It's going to be tricky to fit it, let Windows figure it out via + // the more expensive DrawText call. + RECT rect = {left, top, right, bottom}; + DrawText(_bitmap_dc, collector_name.data(), collector_name.size(), + &rect, DT_CENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER); + } + } + else { + int text_top = top + (bottom - top - size.cy) / 2; + if (center - size.cx / 2 < 0) { + // Put it against the left-most edge. + TextOut(_bitmap_dc, _pixel_scale, text_top, + collector_name.data(), collector_name.length()); + } + else if (center + size.cx / 2 >= get_xsize()) { + // Put it against the right-most edge. + TextOut(_bitmap_dc, get_xsize() - _pixel_scale - size.cx, text_top, + collector_name.data(), collector_name.length()); + } + else { + // It fits just fine, center it. + TextOut(_bitmap_dc, center - size.cx / 2, text_top, + collector_name.data(), collector_name.length()); + } + } + } + } +} + +/** + * Called after all the bars have been drawn, this triggers a refresh event to + * draw it to the window. + */ +void WinStatsTimeline:: +end_draw() { + InvalidateRect(_graph_window, nullptr, FALSE); + + if (_threads_changed) { + RECT rect; + GetClientRect(_window, &rect); + rect.top = _top_margin; + rect.right = _left_margin; + InvalidateRect(_window, &rect, TRUE); + _threads_changed = false; + } + + if (_guide_bars_changed) { + RECT rect; + GetClientRect(_window, &rect); + rect.bottom = _top_margin; + InvalidateRect(_window, &rect, TRUE); + _guide_bars_changed = false; + } +} + +/** + * Called at the end of the draw cycle. + */ +void WinStatsTimeline:: +idle() { +} + +/** + * Overridden by a derived class to implement an animation. If it returns + * false, the animation timer is stopped. + */ +bool WinStatsTimeline:: +animate(double time, double dt) { + return PStatTimeline::animate(time, dt); +} + +/** + * + */ +LONG WinStatsTimeline:: +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_MOUSELEAVE: + SetFocus(nullptr); + break; + + default: + break; + } + + return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam); +} + +/** + * + */ +LONG WinStatsTimeline:: +graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_LBUTTONDOWN: + if (_potential_drag_mode == DM_none) { + set_drag_mode(DM_pan); + int16_t x = LOWORD(lparam); + _drag_start_x = x; + _scroll_speed = 0.0; + _zoom_center = pixel_to_timestamp(x); + SetCapture(_graph_window); + return 0; + } + break; + + case WM_MOUSEMOVE: + // Make sure we can accept keyboard events, except if we're inactive. + if (GetActiveWindow() == _window) { + SetFocus(hwnd); + } + + if (_drag_mode == DM_none && _potential_drag_mode == DM_none) { + // When the mouse is over a color bar, highlight it. + int x = LOWORD(lparam); + int y = HIWORD(lparam); + double time = pixel_to_timestamp(x); + + int row = pixel_to_row(y); + + if (row != _highlighted_row) { + clear_graph_tooltip(); + } + else if (_highlighted_row >= 0) { + // Is the mouse on the same bar? If not, clear the tooltip. + ColorBar bar; + if (find_bar(row, x, bar)) { + double prev_time = pixel_to_timestamp(_highlighted_x); + if (prev_time < bar._start || prev_time > bar._end) { + clear_graph_tooltip(); + } + } else { + clear_graph_tooltip(); + } + } + + std::swap(_highlighted_x, x); + std::swap(_highlighted_row, row); + + if (row >= 0) { + PStatTimeline::force_redraw(row, x, x); + } + PStatTimeline::force_redraw(_highlighted_row, _highlighted_x, _highlighted_x); + + if ((_keys_held & (F_w | F_s)) != 0) { + // Update the zoom center if we move the mouse while zooming with the + // keyboard. + _zoom_center = time; + } + + // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph + // window. + TRACKMOUSEEVENT tme = { + sizeof(TRACKMOUSEEVENT), + TME_LEAVE, + _graph_window, + 0 + }; + TrackMouseEvent(&tme); + } + else { + // If the mouse is in some drag mode, stop highlighting. + if (_highlighted_row != -1) { + int row = _highlighted_row; + _highlighted_row = -1; + PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x); + clear_graph_tooltip(); + } + } + + if (_drag_mode == DM_pan) { + int16_t x = LOWORD(lparam); + int delta = _drag_start_x - x; + set_horizontal_scroll(get_horizontal_scroll() + pixel_to_height(delta)); + _drag_start_x = x; + return 0; + } + break; + + case WM_MOUSELEAVE: + // When the mouse leaves the graph, stop highlighting. + if (_highlighted_row != -1) { + int row = _highlighted_row; + _highlighted_row = -1; + PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x); + clear_graph_tooltip(); + } + SetFocus(nullptr); + break; + + case WM_LBUTTONUP: + if (_drag_mode == DM_pan) { + set_drag_mode(DM_none); + ReleaseCapture(); + return 0; + } + break; + + case WM_LBUTTONDBLCLK: + { + // Double-clicking on a color bar in the graph will zoom the graph into + // that collector. + int16_t x = LOWORD(lparam); + int16_t y = HIWORD(lparam); + int row = pixel_to_row(y); + ColorBar bar; + if (find_bar(row, x, bar)) { + double width = bar._end - bar._start; + zoom_to(width * 1.5, pixel_to_timestamp(x)); + scroll_to(bar._start - width / 4.0); + } else { + // Double-clicking the white area zooms out. + _zoom_speed -= 100.0; + } + start_animation(); + return 0; + } + break; + + case WM_CONTEXTMENU: + { + // Right-clicking a color bar brings up a context menu. + POINT point; + if (GetCursorPos(&point)) { + POINT graph_point = point; + if (ScreenToClient(_graph_window, &graph_point)) { + int row = pixel_to_row(graph_point.y); + ColorBar bar; + if (find_bar(row, graph_point.x, bar)) { + _popup_bar = bar; + + HMENU popup = CreatePopupMenu(); + + std::string label = get_bar_tooltip(row, graph_point.x); + if (!label.empty()) { + AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str()); + } + AppendMenu(popup, MF_STRING, 101, "Zoom To"); + AppendMenu(popup, MF_STRING, 102, "Open Strip Chart"); + AppendMenu(popup, MF_STRING, 103, "Open Flame Graph"); + AppendMenu(popup, MF_STRING, 104, "Open Piano Roll"); + TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _graph_window, nullptr); + } + } + } + return 0; + } + break; + + case WM_COMMAND: + switch (LOWORD(wparam)) { + case 101: + { + double width = _popup_bar._end - _popup_bar._start; + zoom_to(width * 1.5, (_popup_bar._end + _popup_bar._start) / 2.0); + scroll_to(_popup_bar._start - width / 4.0); + start_animation(); + } + return 0; + + case 102: + WinStatsGraph::_monitor->open_strip_chart(_popup_bar._thread_index, _popup_bar._collector_index, false); + return 0; + + case 103: + WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index); + return 0; + + case 104: + WinStatsGraph::_monitor->open_piano_roll(_popup_bar._thread_index); + return 0; + } + break; + + case WM_MOUSEWHEEL: + { + if (GET_KEYSTATE_WPARAM(wparam) & MK_CONTROL) { + // Zoom in/out around the cursor position. + POINT point; + if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) { + int delta = GET_WHEEL_DELTA_WPARAM(wparam); + zoom_by(delta / 120.0, pixel_to_timestamp(point.x)); + start_animation(); + } + } + return 0; + } + break; + + case WM_MOUSEHWHEEL: + { + int delta = GET_WHEEL_DELTA_WPARAM(wparam); + _scroll_speed += delta / 12.0; + start_animation(); + return 0; + } + break; + + case WM_KEYDOWN: + case WM_KEYUP: + { + int flag = 0; + int vsc = (lparam & 0xff0000) >> 16; + if ((lparam & 0x1000000) == 0) { + // Accept WASD based on their position rather than their mapping + switch (vsc) { + case 17: + flag = F_w; + break; + case 30: + flag = F_a; + break; + case 31: + flag = F_s; + break; + case 32: + flag = F_d; + break; + } + } + if (flag == 0) { + switch (wparam) { + case VK_LEFT: + flag = F_left; + break; + case VK_RIGHT: + flag = F_right; + break; + case 'W': + flag = F_w; + break; + case 'A': + flag = F_a; + break; + case 'S': + flag = F_s; + break; + case 'D': + flag = F_d; + break; + } + } + if (flag != 0) { + if (msg == WM_KEYDOWN) { + if (flag & (F_w | F_s)) { + POINT point; + if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) { + _zoom_center = pixel_to_timestamp(point.x); + } else { + _zoom_center = get_horizontal_scroll() + get_horizontal_scale() / 2.0; + } + } + if (_keys_held == 0) { + start_animation(); + } + _keys_held |= flag; + } + else if (_keys_held != 0) { + _keys_held &= ~flag; + } + } + } + break; + + case WM_KILLFOCUS: + _keys_held = 0; + break; + + default: + break; + } + + return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam); +} + +/** + * This is called during the servicing of WM_PAINT; it gives a derived class + * opportunity to do some further painting into the window (the outer window, + * not the graph window). + */ +void WinStatsTimeline:: +additional_window_paint(HDC hdc) { + // Draw in the labels for the guide bars. + SelectObject(hdc, WinStatsGraph::_monitor->get_font()); + SetTextAlign(hdc, TA_LEFT | TA_BOTTOM); + SetBkMode(hdc, TRANSPARENT); + + int y = _top_margin - 2; + + int num_guide_bars = get_num_guide_bars(); + for (int i = 0; i < num_guide_bars; ++i) { + draw_guide_label(hdc, y, get_guide_bar(i)); + } + + SetTextColor(hdc, _dark_color); + SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP); + + for (const ThreadRow &thread_row : _threads) { + draw_thread_label(hdc, thread_row); + } +} + +/** + * This is called during the servicing of WM_PAINT; it gives a derived class + * opportunity to do some further painting into the window (the outer window, + * not the graph window). + */ +void WinStatsTimeline:: +additional_graph_window_paint(HDC hdc) { +} + +/** + * Called when the mouse hovers over the graph, and should return the text that + * should appear on the tooltip. + */ +std::string WinStatsTimeline:: +get_graph_tooltip(int mouse_x, int mouse_y) const { + return PStatTimeline::get_bar_tooltip(pixel_to_row(mouse_y), mouse_x); +} + +/** + * Based on the mouse position within the window's client area, look for + * draggable things the mouse might be hovering over and return the + * apprioprate DragMode enum or DM_none if nothing is indicated. + */ +WinStatsGraph::DragMode WinStatsTimeline:: +consider_drag_start(int mouse_x, int mouse_y, int width, int height) { + DragMode mode = WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height); + if (mode == DM_right_margin) { + mode = DM_none; + } + return mode; +} + +/** + * Draws the text for the indicated guide bar label at the top of the graph. + */ +void WinStatsTimeline:: +draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) { + const std::string &label = bar._label; + if (label.empty()) { + return; + } + + switch (bar._style) { + case GBS_target: + SetTextColor(hdc, _light_color); + break; + + case GBS_user: + SetTextColor(hdc, _user_guide_bar_color); + break; + + case GBS_normal: + SetTextColor(hdc, _light_color); + break; + + case GBS_frame: + SetTextColor(hdc, _dark_color); + break; + } + + int x = timestamp_to_pixel(bar._height); + SIZE size; + GetTextExtentPoint32(hdc, label.data(), label.length(), &size); + + int this_x = _graph_left + x - size.cx / 2; + if (x >= 0 && x < get_xsize()) { + TextOut(hdc, this_x, y, + label.data(), label.length()); + } +} + +/** + * Draws the text for the indicated thread on the side of the graph. + */ +void WinStatsTimeline:: +draw_thread_label(HDC hdc, const ThreadRow &thread_row) { + int top = row_to_pixel(thread_row._row_offset + 1); + int bottom = row_to_pixel(thread_row._row_offset + 2); + + RECT rect = {_pixel_scale * 2, top, _left_margin - _pixel_scale * 2, bottom}; + DrawText(hdc, thread_row._label.data(), thread_row._label.size(), + &rect, DT_RIGHT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER); +} + +/** + * Creates the window for this strip chart. + */ +void WinStatsTimeline:: +create_window() { + if (_window) { + return; + } + + HINSTANCE application = GetModuleHandle(nullptr); + register_window_class(application); + + POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos(); + + RECT win_rect = { + 0, 0, + _left_margin + get_xsize() + _right_margin, + _top_margin + get_ysize() + _bottom_margin + }; + + // compute window size based on desired client area size + AdjustWindowRect(&win_rect, graph_window_style, FALSE); + + _window = + CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name, + "Timeline", graph_window_style, + window_pos.x, window_pos.y, + win_rect.right - win_rect.left, + win_rect.bottom - win_rect.top, + WinStatsGraph::_monitor->get_window(), nullptr, application, 0); + if (!_window) { + nout << "Could not create timeline window!\n"; + exit(1); + } + + SetWindowLongPtr(_window, 0, (LONG_PTR)this); + + // Ensure that the window is on top of the stack. + SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + + SetFocus(_window); +} + +/** + * Registers the window class for the Timeline window, if it has not already + * been registered. + */ +void WinStatsTimeline:: +register_window_class(HINSTANCE application) { + if (_window_class_registered) { + return; + } + + WNDCLASS wc; + + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)static_window_proc; + wc.hInstance = application; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.lpszMenuName = nullptr; + wc.lpszClassName = _window_class_name; + + // Reserve space to associate the this pointer with the window. + wc.cbWndExtra = sizeof(WinStatsTimeline *); + + if (!RegisterClass(&wc)) { + nout << "Could not register Timeline window class!\n"; + exit(1); + } + + _window_class_registered = true; +} + +/** + * + */ +LONG WINAPI WinStatsTimeline:: +static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + WinStatsTimeline *self = (WinStatsTimeline *)GetWindowLongPtr(hwnd, 0); + if (self != nullptr && self->_window == hwnd) { + return self->window_proc(hwnd, msg, wparam, lparam); + } else { + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} diff --git a/pandatool/src/win-stats/winStatsTimeline.h b/pandatool/src/win-stats/winStatsTimeline.h new file mode 100644 index 0000000000..61dc07b943 --- /dev/null +++ b/pandatool/src/win-stats/winStatsTimeline.h @@ -0,0 +1,89 @@ +/** + * 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 winStatsTimeline.h + * @author rdb + * @date 2022-02-11 + */ + +#ifndef WINSTATSTIMELINE_H +#define WINSTATSTIMELINE_H + +#include "pandatoolbase.h" + +#include "winStatsGraph.h" +#include "pStatTimeline.h" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +class WinStatsMonitor; + +/** + * A window that draws all of the start/stop event pairs on each thread on a + * horizontal scrolling timeline, with concurrent start/stop pairs stacked + * underneath each other. + */ +class WinStatsTimeline : public PStatTimeline, public WinStatsGraph { +public: + WinStatsTimeline(WinStatsMonitor *monitor); + virtual ~WinStatsTimeline(); + + virtual void new_data(int thread_index, int frame_number); + virtual void force_redraw(); + virtual void changed_graph_size(int graph_xsize, int graph_ysize); + +protected: + virtual void clear_region(); + virtual void begin_draw(); + virtual void draw_separator(int row); + virtual void draw_guide_bar(int x, GuideBarStyle style); + virtual void draw_bar(int row, int from_x, int to_x, int collector_index, + const std::string &collector_name); + virtual void end_draw(); + virtual void idle(); + + virtual bool animate(double time, double dt); + + LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + virtual void additional_window_paint(HDC hdc); + virtual void additional_graph_window_paint(HDC hdc); + virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const; + virtual DragMode consider_drag_start(int mouse_x, int mouse_y, + int width, int height); + +private: + void draw_guide_label(HDC hdc, int y, const GuideBar &bar); + void draw_thread_label(HDC hdc, const ThreadRow &thread_row); + + void create_window(); + static void register_window_class(HINSTANCE application); + + static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + int row_to_pixel(int y) const { + return y * _pixel_scale * 5 + _pixel_scale; + } + int pixel_to_row(int y) const { + return (y - _pixel_scale) / (_pixel_scale * 5); + } + + static bool _window_class_registered; + static const char * const _window_class_name; + + HBRUSH _grid_brush; + + int _highlighted_row = -1; + int _highlighted_x = 0; + ColorBar _popup_bar; +}; + +#endif diff --git a/pandatool/src/win-stats/winstats_composite1.cxx b/pandatool/src/win-stats/winstats_composite1.cxx index cc90367573..ef63280222 100644 --- a/pandatool/src/win-stats/winstats_composite1.cxx +++ b/pandatool/src/win-stats/winstats_composite1.cxx @@ -6,5 +6,6 @@ #include "winStatsLabelStack.cxx" #include "winStatsMonitor.cxx" #include "winStatsPianoRoll.cxx" +#include "winStatsTimeline.cxx" #include "winStatsServer.cxx" #include "winStatsStripChart.cxx" From 65cd882cb29edc23cdefcfdf85933ec108ac3947 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 20 Feb 2022 16:23:14 +0100 Subject: [PATCH 041/166] display: PStats collector reorganisation Remove *:do_frame (which adds another stack frame with very little value), remove unused App:Delete collector, merge Flip Begin/End collectors --- panda/src/display/graphicsEngine.cxx | 72 ++++++++-------------------- panda/src/display/graphicsEngine.h | 4 -- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index ec047a8ac0..85f70cb5d6 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -71,7 +71,6 @@ PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync"); PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle"); //PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General"); PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame"); -PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame"); PStatCollector GraphicsEngine::_yield_pcollector("App:Yield"); PStatCollector GraphicsEngine::_cull_pcollector("Cull"); PStatCollector GraphicsEngine::_cull_setup_pcollector("Cull:Setup"); @@ -79,15 +78,12 @@ PStatCollector GraphicsEngine::_cull_sort_pcollector("Cull:Sort"); PStatCollector GraphicsEngine::_draw_pcollector("Draw"); PStatCollector GraphicsEngine::_sync_pcollector("Draw:Sync"); PStatCollector GraphicsEngine::_flip_pcollector("Wait:Flip"); -PStatCollector GraphicsEngine::_flip_begin_pcollector("Wait:Flip:Begin"); -PStatCollector GraphicsEngine::_flip_end_pcollector("Wait:Flip:End"); PStatCollector GraphicsEngine::_transform_states_pcollector("TransformStates"); PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformStates:Unused"); PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates"); PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused"); PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers"); PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("PipelineCyclers:Dirty"); -PStatCollector GraphicsEngine::_delete_pcollector("App:Delete"); PStatCollector GraphicsEngine::_sw_sprites_pcollector("SW Sprites"); @@ -799,10 +795,7 @@ render_frame() { // 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. - { - PStatTimer timer(_do_frame_pcollector, current_thread); - _app.do_frame(this, current_thread); - } + _app.do_frame(this, current_thread); // Grab each thread's mutex again after all windows have flipped, and wait // for the thread to finish. @@ -1416,14 +1409,9 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, GraphicsOutput *win = wlist[wi]; if (win->is_active() && win->get_gsg()->is_active()) { if (win->flip_ready()) { - { - PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); - win->begin_flip(); - } - { - PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); - win->end_flip(); - } + PStatTimer timer(_flip_pcollector, current_thread); + win->begin_flip(); + win->end_flip(); } if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { @@ -1446,14 +1434,9 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, if (_auto_flip) { if (win->flip_ready()) { - { - PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); - win->begin_flip(); - } - { - PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); - win->end_flip(); - } + PStatTimer timer(_flip_pcollector, current_thread); + win->begin_flip(); + win->end_flip(); } } } @@ -1691,6 +1674,8 @@ cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg, */ void GraphicsEngine:: draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { + PStatTimer timer(_draw_pcollector, current_thread); + nassertv(wlist.verify_list()); size_t wlist_size = wlist.size(); @@ -1702,16 +1687,9 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { 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); - host->begin_flip(); - } - { - PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); - host->end_flip(); - } + PStatTimer timer(_flip_pcollector, current_thread); + host->begin_flip(); + host->end_flip(); } if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { @@ -1749,16 +1727,9 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { #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(); - } - { - PStatGPUTimer timer(gsg, GraphicsEngine::_flip_end_pcollector, current_thread); - win->end_flip(); - } + PStatGPUTimer timer(gsg, _flip_pcollector, current_thread); + win->begin_flip(); + win->end_flip(); } } @@ -1820,6 +1791,8 @@ flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { size_t warray_count = 0; GraphicsOutput **warray = (GraphicsOutput **)alloca(warray_size); + PStatTimer timer(_flip_pcollector, current_thread); + size_t i; for (i = 0; i < num_windows; ++i) { GraphicsOutput *win = wlist[i]; @@ -1828,14 +1801,12 @@ flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { warray[warray_count] = win; ++warray_count; - PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); win->begin_flip(); } } for (i = 0; i < warray_count; ++i) { GraphicsOutput *win = warray[i]; - PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread); win->end_flip(); } } @@ -1851,7 +1822,7 @@ ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) for (wi = wlist.begin(); wi != wlist.end(); ++wi) { GraphicsOutput *win = (*wi); if (win->flip_ready()) { - PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); + PStatTimer timer(_flip_pcollector, current_thread); win->ready_flip(); } } @@ -2756,11 +2727,8 @@ thread_main() { break; case TS_do_frame: - { - PStatTimer timer(_engine->_do_frame_pcollector, current_thread); - do_pending(_engine, current_thread); - do_frame(_engine, current_thread); - } + do_pending(_engine, current_thread); + do_frame(_engine, current_thread); break; case TS_do_flip: diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 0138eb5cf0..aef9676b27 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -360,7 +360,6 @@ private: static PStatCollector _cycle_pcollector; //static PStatCollector _app_pcollector; static PStatCollector _render_frame_pcollector; - static PStatCollector _do_frame_pcollector; static PStatCollector _yield_pcollector; static PStatCollector _cull_pcollector; static PStatCollector _cull_setup_pcollector; @@ -368,15 +367,12 @@ private: static PStatCollector _draw_pcollector; static PStatCollector _sync_pcollector; static PStatCollector _flip_pcollector; - static PStatCollector _flip_begin_pcollector; - static PStatCollector _flip_end_pcollector; static PStatCollector _transform_states_pcollector; static PStatCollector _transform_states_unused_pcollector; static PStatCollector _render_states_pcollector; static PStatCollector _render_states_unused_pcollector; static PStatCollector _cyclers_pcollector; static PStatCollector _dirty_cyclers_pcollector; - static PStatCollector _delete_pcollector; static PStatCollector _sw_sprites_pcollector; static PStatCollector _vertex_data_small_pcollector; From 739ad1ebd6e5955efa2c0d2f0ad9c9dda6d236d9 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 20 Feb 2022 16:50:54 +0100 Subject: [PATCH 042/166] pstats: Fix strip chart scale glitches on Windows when switching collector --- pandatool/src/win-stats/winStatsStripChart.cxx | 17 +++++++++++++++++ pandatool/src/win-stats/winStatsStripChart.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx index b20b2d23aa..ccf0707ff4 100644 --- a/pandatool/src/win-stats/winStatsStripChart.cxx +++ b/pandatool/src/win-stats/winStatsStripChart.cxx @@ -230,6 +230,23 @@ get_label_tooltip(int collector_index) const { return PStatStripChart::get_label_tooltip(collector_index); } +/** + * Changes the collector represented by this strip chart. This may force a + * redraw. + */ +void WinStatsStripChart:: +set_collector_index(int collector_index) { + if (get_collector_index() != collector_index) { + PStatStripChart::set_collector_index(collector_index); + + // Redraw the scale labels. + RECT rect; + GetClientRect(_window, &rect); + rect.left = _right_margin; + InvalidateRect(_window, &rect, TRUE); + } +} + /** * Changes the value the height of the vertical axis represents. This may * force a redraw. diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h index 850faa704c..3874bfe41f 100644 --- a/pandatool/src/win-stats/winStatsStripChart.h +++ b/pandatool/src/win-stats/winStatsStripChart.h @@ -46,6 +46,8 @@ public: virtual void on_click_label(int collector_index); virtual void on_popup_label(int collector_index); virtual std::string get_label_tooltip(int collector_index) const; + + void set_collector_index(int collector_index); void set_vertical_scale(double value_height); protected: From 65ee79158f3c75b2ddfe504f9b426a8d617b3b1c Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 20 Feb 2022 16:54:15 +0100 Subject: [PATCH 043/166] showbase: Start recording right away when opening PStats connection Don't wait until the next frame - makes it harder to diagnose long load times in the new Timeline view --- direct/src/showbase/ShowBase.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 5002ddd824..eae8383387 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -1930,7 +1930,11 @@ class ShowBase(DirectObject.DirectObject): if port is None: port = -1 PStatClient.connect(hostname, port) - return PStatClient.isConnected() + if PStatClient.isConnected(): + PStatClient.mainTick() + return True + else: + return False def addSfxManager(self, extraSfxManager): """ From 0a3733ccb9fe15a9c00838a78b062958674ed3a3 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 20 Feb 2022 17:05:44 +0100 Subject: [PATCH 044/166] pstats: GPU timing improvements; use same frame numbering everywhere Timer queries are significantly more efficient, are synchronized to CPU time, and the synchronized frame numbering makes it possible to correlate stuff in the Timeline view --- panda/src/display/graphicsEngine.cxx | 7 - panda/src/display/graphicsStateGuardian.cxx | 184 ++------------- panda/src/display/graphicsStateGuardian.h | 15 +- panda/src/display/graphicsWindow.cxx | 23 +- panda/src/display/graphicsWindow.h | 4 + panda/src/display/pStatGPUTimer.h | 1 - panda/src/ffmpeg/ffmpegVideoCursor.cxx | 2 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 215 ++++++++++++++---- .../src/glstuff/glGraphicsStateGuardian_src.h | 22 +- panda/src/glstuff/glLatencyQueryContext_src.I | 12 - .../src/glstuff/glLatencyQueryContext_src.cxx | 47 ---- panda/src/glstuff/glLatencyQueryContext_src.h | 53 ----- panda/src/glstuff/glShaderContext_src.cxx | 2 +- panda/src/glstuff/glTimerQueryContext_src.I | 24 -- panda/src/glstuff/glTimerQueryContext_src.cxx | 96 -------- panda/src/glstuff/glTimerQueryContext_src.h | 63 ----- panda/src/glstuff/glmisc_src.cxx | 2 - panda/src/glstuff/glstuff_src.cxx | 2 - panda/src/glstuff/glstuff_src.h | 2 - panda/src/gobj/CMakeLists.txt | 2 - panda/src/gobj/config_gobj.cxx | 2 - panda/src/gobj/p3gobj_composite2.cxx | 1 - panda/src/gobj/timerQueryContext.I | 22 -- panda/src/gobj/timerQueryContext.cxx | 30 --- panda/src/gobj/timerQueryContext.h | 58 ----- panda/src/gobj/vertexDataPage.cxx | 2 +- panda/src/pstatclient/pStatClient.cxx | 5 +- panda/src/pstatclient/pStatClientImpl.cxx | 19 +- panda/src/pstatclient/pStatClientImpl.h | 4 +- panda/src/pstatclient/pStatProperties.cxx | 2 +- panda/src/pstatclient/pStatThread.cxx | 8 +- panda/src/pstatclient/pStatThread.h | 4 +- panda/src/wgldisplay/wglGraphicsWindow.cxx | 15 -- panda/src/wgldisplay/wglGraphicsWindow.h | 1 - panda/src/windisplay/winGraphicsWindow.cxx | 15 -- panda/src/windisplay/winGraphicsWindow.h | 2 - 36 files changed, 267 insertions(+), 701 deletions(-) delete mode 100644 panda/src/glstuff/glLatencyQueryContext_src.I delete mode 100644 panda/src/glstuff/glLatencyQueryContext_src.cxx delete mode 100644 panda/src/glstuff/glLatencyQueryContext_src.h delete mode 100644 panda/src/glstuff/glTimerQueryContext_src.I delete mode 100644 panda/src/glstuff/glTimerQueryContext_src.cxx delete mode 100644 panda/src/glstuff/glTimerQueryContext_src.h delete mode 100644 panda/src/gobj/timerQueryContext.I delete mode 100644 panda/src/gobj/timerQueryContext.cxx delete mode 100644 panda/src/gobj/timerQueryContext.h diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 85f70cb5d6..d6dc3c678d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1719,13 +1719,6 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *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 (gsg->get_timer_queries_active()) { - gsg->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index()); - } -#endif - if (win->flip_ready()) { PStatGPUTimer timer(gsg, _flip_pcollector, current_thread); win->begin_flip(); diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 322d77d1cd..503fb841b3 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -98,7 +98,6 @@ PStatCollector GraphicsStateGuardian::_compute_dispatch_pcollector("Draw:Compute 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"); @@ -222,10 +221,6 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, #ifdef DO_PSTATS _timer_queries_active = false; - _last_query_frame = 0; - _last_num_queried = 0; - // _timer_delta = 0.0; - _pstats_gpu_thread = -1; #endif @@ -778,9 +773,17 @@ end_occlusion_query() { * Adds a timer query to the command stream, associated with the given PStats * collector index. */ -PT(TimerQueryContext) GraphicsStateGuardian:: +void GraphicsStateGuardian:: issue_timer_query(int pstats_index) { - return nullptr; +} + +/** + * A latency query is a special type of timer query that measures the + * difference between CPU time and GPU time, ie. how far the GPU is behind in + * processing the commands being generated by the CPU right now. + */ +void GraphicsStateGuardian:: +issue_latency_query(int pstats_index) { } /** @@ -2370,30 +2373,6 @@ 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; - if (pstats_gpu_timing && _supports_timer_query) { - _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; } @@ -2479,133 +2458,6 @@ end_frame(Thread *current_thread) { _prepared_objects->_graphics_memory_lru.begin_epoch(); } -/** - * 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 = std::min(_last_num_queried, count) - 1; - - if (_pending_timer_queries[i]->is_answer_ready()) { - first = count; - while (i < count - 1) { - 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; - - 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 -} - /** * Returns true if this GSG can implement decals using a DepthOffsetAttrib, or * false if that is unreliable and the three-step rendering process should be @@ -3246,8 +3098,19 @@ init_frame_pstats() { _texture_state_pcollector.clear_level(); } } -#endif // DO_PSTATS +/** + * Returns a PStatThread used to represent this GL context. + */ +PStatThread GraphicsStateGuardian:: +get_pstats_thread() { + PStatClient *client = PStatClient::get_global_pstats(); + if (_pstats_gpu_thread == -1) { + _pstats_gpu_thread = client->make_gpu_thread("GPU").get_index(); + } + return PStatThread(client, _pstats_gpu_thread); +} +#endif // DO_PSTATS /** * Create a gamma table. @@ -3467,9 +3330,6 @@ close_gsg() { // Make sure that all the contexts belonging to the GSG are deleted. _prepared_objects.clear(); -#ifdef DO_PSTATS - _pending_timer_queries.clear(); -#endif free_pointers(); } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 42702d1ed3..6d0b26b6e1 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -44,7 +44,6 @@ #include "bitMask.h" #include "texture.h" #include "occlusionQueryContext.h" -#include "timerQueryContext.h" #include "loader.h" #include "shaderAttrib.h" #include "texGenAttrib.h" @@ -320,7 +319,8 @@ public: virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); - virtual PT(TimerQueryContext) issue_timer_query(int pstats_index); + virtual void issue_timer_query(int pstats_index); + virtual void issue_latency_query(int pstats_index); virtual void dispatch_compute(int size_x, int size_y, int size_z); @@ -363,8 +363,6 @@ 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(); @@ -445,6 +443,7 @@ public: #ifdef DO_PSTATS static void init_frame_pstats(); + PStatThread get_pstats_thread(); #endif protected: @@ -602,13 +601,6 @@ protected: #ifdef DO_PSTATS int _pstats_gpu_thread; bool _timer_queries_active; - PStatFrameData _pstats_gpu_data; - - int _last_query_frame; - int _last_num_queried; - // double _timer_delta; - typedef pdeque TimerQueryQueue; - TimerQueryQueue _pending_timer_queries; #endif bool _copy_texture_inverted; @@ -699,7 +691,6 @@ public: 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; diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 7abda2bac7..f1808dfcd9 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -39,7 +39,8 @@ GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsOutput *host) : GraphicsOutput(engine, pipe, name, fb_prop, win_prop, flags, gsg, host, true), _input_lock("GraphicsWindow::_input_lock"), - _properties_lock("GraphicsWindow::_properties_lock") + _properties_lock("GraphicsWindow::_properties_lock"), + _latency_pcollector(name + " latency") { #ifdef DO_MEMORY_USAGE MemoryUsage::update_type(this, this); @@ -610,6 +611,26 @@ close_window() { _is_valid = false; } +/** + * This function will be called within the draw thread after end_frame() has + * been called on all windows, to initiate the exchange of the front and back + * buffers. + * + * This should instruct the window to prepare for the flip at the next video + * sync, but it should not wait. + * + * We have the two separate functions, begin_flip() and end_flip(), to make it + * easier to flip all of the windows at the same time. + */ +void GraphicsWindow:: +begin_flip() { +#ifdef DO_PSTATS + if (_gsg->get_timer_queries_active()) { + _gsg->issue_latency_query(_latency_pcollector.get_index()); + } +#endif +} + /** * Opens the window right now. Called from the window thread. Returns true * if the window is successfully opened, or false if there was a problem. diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 12047ceea6..45b1438906 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -126,6 +126,8 @@ public: virtual void process_events(); virtual void set_properties_now(WindowProperties &properties); + virtual void begin_flip(); + protected: virtual void close_window(); virtual bool open_window(); @@ -152,6 +154,8 @@ protected: bool _got_expose_event; + PStatCollector _latency_pcollector; + private: LightReMutex _properties_lock; // protects _requested_properties, _rejected_properties, and _window_event. diff --git a/panda/src/display/pStatGPUTimer.h b/panda/src/display/pStatGPUTimer.h index 1e0accdc04..4b54e08b57 100644 --- a/panda/src/display/pStatGPUTimer.h +++ b/panda/src/display/pStatGPUTimer.h @@ -18,7 +18,6 @@ #include "pStatTimer.h" #include "pStatCollector.h" #include "config_pstatclient.h" -#include "timerQueryContext.h" class Thread; class GraphicsStateGuardian; diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index 5cc3d2cffb..66d7fcdd86 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -698,7 +698,7 @@ thread_main() { while (do_poll()) { // Keep doing stuff as long as there's something to do. _lock.release(); - PStatClient::thread_tick(_sync_name); + PStatClient::thread_tick(); Thread::consider_yield(); _lock.acquire(); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 11d66699c0..69ef795ee3 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -4162,12 +4162,21 @@ begin_frame(Thread *current_thread) { _primitive_batches_display_list_pcollector.clear_level(); #endif +#if defined(DO_PSTATS) && !defined(OPENGLES) + int frame_number = ClockObject::get_global_clock()->get_frame_count(current_thread); + if (_current_frame_timing == nullptr || + frame_number != _current_frame_timing->_frame_number) { + + _current_frame_timing = begin_frame_timing(frame_number); + } +#endif + #ifndef NDEBUG _show_texture_usage = false; if (gl_show_texture_usage) { // When this is true, then every other second, we show the usage textures // instead of the real textures. - double now = ClockObject::get_global_clock()->get_frame_time(); + double now = ClockObject::get_global_clock()->get_frame_time(current_thread); int this_second = (int)floor(now); if (this_second & 1) { _show_texture_usage = true; @@ -4190,16 +4199,6 @@ begin_frame(Thread *current_thread) { } #endif // NDEBUG -#ifdef DO_PSTATS - /*if (_supports_timer_query) { - // Measure the difference between the OpenGL clock and the PStats clock. - GLint64 time_ns; - _glGetInteger64v(GL_TIMESTAMP, &time_ns); - _timer_delta = time_ns * -0.000000001; - _timer_delta += PStatClient::get_global_pstats()->get_real_time(); - }*/ -#endif - #ifndef OPENGLES if (_current_properties->get_srgb_color()) { glEnable(GL_FRAMEBUFFER_SRGB); @@ -4406,6 +4405,127 @@ end_frame(Thread *current_thread) { } } +/** + * + */ +CLP(GraphicsStateGuardian)::FrameTiming *CLP(GraphicsStateGuardian):: +begin_frame_timing(int frame_number) { +#if defined(DO_PSTATS) && !defined(OPENGLES) + if (!_timer_queries_active) { + if (pstats_gpu_timing && _supports_timer_query && PStatClient::is_connected()) { + _timer_queries_active = true; + } else { + return nullptr; + } + } + + PStatClient *client = PStatClient::get_global_pstats(); + + _timer_queries_pcollector.clear_level(); + + if (_deleted_queries.size() < 128) { + // We'll need a lot of timer queries, so allocate a whole bunch up front. + size_t alloc_count = 128 - _deleted_queries.size(); + _deleted_queries.resize(_deleted_queries.size() + alloc_count); + _glGenQueries(alloc_count, _deleted_queries.data() + _deleted_queries.size() - alloc_count); + } + + // Issue a start query for collector 0, marking the start of this frame. + GLuint frame_query = _deleted_queries.back(); + _deleted_queries.pop_back(); + _glQueryCounter(frame_query, GL_TIMESTAMP); + + // Synchronize the GL time with the PStats clock. + GLint64 gl_time; + double cpu_time1 = client->get_real_time(); + _glGetInteger64v(GL_TIMESTAMP, &gl_time); + double cpu_time2 = client->get_real_time(); + double cpu_time = (cpu_time1 + cpu_time2) / 2.0; + + // Check if the results from the previous frame are available. We just need + // to check whether the last query for each frame is available. + while (!_frame_timings.empty()) { + const FrameTiming &frame = _frame_timings.front(); + GLuint last_query = frame._queries.back().first; + GLuint result; + _glGetQueryObjectuiv(last_query, GL_QUERY_RESULT_AVAILABLE, &result); + if (result == 0) { + // Not ready, so subsequent frames won't be, either. + break; + } + // We've got a frame whose timer queries are ready. + end_frame_timing(frame); + _frame_timings.pop_front(); + } + + FrameTiming frame; + frame._frame_number = frame_number; + frame._gpu_sync_time = gl_time; + frame._cpu_sync_time = cpu_time; + frame._queries.push_back(std::make_pair(frame_query, 0)); + _frame_timings.push_back(std::move(frame)); + + return &_frame_timings.back(); +#else + return nullptr; +#endif +} + +/** + * Gets the timer query results for the given frame and sends them to the + * PStats server. + */ +void CLP(GraphicsStateGuardian):: +end_frame_timing(const FrameTiming &frame) { +#if defined(DO_PSTATS) && !defined(OPENGLES) + // 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. + if (!PStatClient::is_connected()) { + _timer_queries_active = false; + return; + } + + PStatTimer timer(_wait_timer_pcollector); + + // We represent each GSG as one thread. In the future we may change this to + // representing each graphics device as one thread, but OpenGL doesn't really + // expose this information to us. + PStatThread gpu_thread = get_pstats_thread(); + + PStatFrameData frame_data; + size_t latency_ref_i = 0; + + for (auto &query : frame._queries) { + GLuint64 time_ns; + _glGetQueryObjectui64v(query.first, GL_QUERY_RESULT, &time_ns); + + if (query.second & 0x10000) { + // Latency query. + GLint64 ref = frame._latency_refs[latency_ref_i++]; + double time = ((GLint64)time_ns - ref) * 0.000001; + frame_data.add_level(query.second & 0x7fff, time); + } + else { + // Convert GL time to Panda time. + double time = ((GLint64)time_ns - frame._gpu_sync_time) * 0.000000001 + frame._cpu_sync_time; + if (query.second & 0x8000) { + frame_data.add_stop(query.second & 0x7fff, time); + } + else { + frame_data.add_start(query.second & 0x7fff, time); + } + } + } + + // The end time of the last collector is implicitly the frame's end time. + frame_data.add_stop(0, frame_data.get_end()); + gpu_thread.add_frame(frame._frame_number, frame_data); + + _timer_queries_pcollector.add_level_now(frame._queries.size()); +#endif +} + /** * Called before a sequence of draw_primitive() functions are called, this * should prepare the vertex data for rendering. It returns true if the @@ -7149,48 +7269,57 @@ end_occlusion_query() { * Adds a timer query to the command stream, associated with the given PStats * collector index. */ -PT(TimerQueryContext) CLP(GraphicsStateGuardian):: +void CLP(GraphicsStateGuardian):: issue_timer_query(int pstats_index) { #if defined(DO_PSTATS) && !defined(OPENGLES) - nassertr(_supports_timer_query, nullptr); - - PT(CLP(TimerQueryContext)) query; - - // Hack - if (pstats_index == _command_latency_pcollector.get_index()) { - query = new CLP(LatencyQueryContext)(this, pstats_index); - } else { - query = new CLP(TimerQueryContext)(this, pstats_index); + FrameTiming *frame = _current_frame_timing; + if (frame == nullptr) { + return; } - if (_deleted_queries.size() >= 1) { - query->_index = _deleted_queries.back(); - _deleted_queries.pop_back(); - } else { - _glGenQueries(1, &query->_index); + nassertv(_supports_timer_query); - if (GLCAT.is_spam()) { - GLCAT.spam() << "Generating query for " << pstats_index - << ": " << query->_index << "\n"; - } + if (_deleted_queries.empty()) { + // Allocate some number at a time, since we'll need a lot of these. + _deleted_queries.resize(_deleted_queries.size() + 16); + _glGenQueries(16, _deleted_queries.data() + _deleted_queries.size() - 16); } + GLuint index = _deleted_queries.back(); + _deleted_queries.pop_back(); + // Issue the timestamp query. - _glQueryCounter(query->_index, GL_TIMESTAMP); + _glQueryCounter(index, GL_TIMESTAMP); - if (_use_object_labels) { - // Assign a label to it based on the PStatCollector name. - const PStatClient *client = PStatClient::get_global_pstats(); - string name = client->get_collector_fullname(pstats_index & 0x7fff); - _glObjectLabel(GL_QUERY, query->_index, name.size(), name.data()); + //if (_use_object_labels) { + // // Assign a label to it based on the PStatCollector name. + // const PStatClient *client = PStatClient::get_global_pstats(); + // string name = client->get_collector_fullname(pstats_index & 0x7fff); + // _glObjectLabel(GL_QUERY, index, name.size(), name.data()); + //} + + frame->_queries.push_back(std::make_pair(index, pstats_index)); +#endif +} + +/** + * A latency query is a special type of timer query that measures the + * difference between CPU time and GPU time, ie. how far the GPU is behind in + * processing the commands being generated by the CPU right now. + */ +void CLP(GraphicsStateGuardian):: +issue_latency_query(int pstats_index) { +#if defined(DO_PSTATS) && !defined(OPENGLES) + FrameTiming *frame = _current_frame_timing; + if (frame == nullptr) { + return; } - _pending_timer_queries.push_back((TimerQueryContext *)query); + GLint64 time; + _glGetInteger64v(GL_TIMESTAMP, &time); + issue_timer_query(pstats_index | 0x10000); - return (TimerQueryContext *)query; - -#else - return nullptr; + frame->_latency_refs.push_back(time); #endif } @@ -11743,7 +11872,7 @@ set_state_and_transform(const RenderState *target, #endif _state_pcollector.add_level(1); - PStatGPUTimer timer1(this, _draw_set_state_pcollector); + PStatTimer timer1(_draw_set_state_pcollector); bool transform_changed = transform != _internal_transform; if (transform_changed) { @@ -11934,7 +12063,7 @@ set_state_and_transform(const RenderState *target, int texture_slot = TextureAttrib::get_class_slot(); if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) || !_state_mask.get_bit(texture_slot)) { - PStatGPUTimer timer(this, _draw_set_state_texture_pcollector); + //PStatGPUTimer timer(this, _draw_set_state_texture_pcollector); determine_target_texture(); do_issue_texture(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index c0af1f5228..ef8f262da7 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -293,6 +293,10 @@ public: virtual void end_scene(); virtual void end_frame(Thread *current_thread); + struct FrameTiming; + FrameTiming *begin_frame_timing(int frame_index); + void end_frame_timing(const FrameTiming &frame); + virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, const GeomVertexDataPipelineReader *data_reader, size_t num_instances, bool force); @@ -384,7 +388,8 @@ public: virtual PT(OcclusionQueryContext) end_occlusion_query(); #endif - virtual PT(TimerQueryContext) issue_timer_query(int pstats_index); + virtual void issue_timer_query(int pstats_index) final; + virtual void issue_latency_query(int pstats_index) final; #ifndef OPENGLES_1 virtual void dispatch_compute(int size_x, int size_y, int size_z); @@ -1148,6 +1153,20 @@ public: UsageTextures _usage_textures; #endif // NDEBUG +#if defined(DO_PSTATS) && !defined(OPENGLES) + struct FrameTiming { + int _frame_number; + GLint64 _gpu_sync_time; + double _cpu_sync_time; + pvector > _queries; + pvector _latency_refs; + }; + GLint64 _gpu_reference_time = 0; + double _cpu_reference_time; + pdeque _frame_timings; + FrameTiming *_current_frame_timing = nullptr; +#endif + BufferResidencyTracker _renderbuffer_residency; static PStatCollector _load_display_list_pcollector; @@ -1188,7 +1207,6 @@ private: friend class CLP(CgShaderContext); friend class CLP(GraphicsBuffer); friend class CLP(OcclusionQueryContext); - friend class CLP(TimerQueryContext); }; #include "glGraphicsStateGuardian_src.I" diff --git a/panda/src/glstuff/glLatencyQueryContext_src.I b/panda/src/glstuff/glLatencyQueryContext_src.I deleted file mode 100644 index adc45cdf5e..0000000000 --- a/panda/src/glstuff/glLatencyQueryContext_src.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 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 glLatencyQueryContext_src.I - * @author rdb - * @date 2014-09-24 - */ diff --git a/panda/src/glstuff/glLatencyQueryContext_src.cxx b/panda/src/glstuff/glLatencyQueryContext_src.cxx deleted file mode 100644 index a08ab74517..0000000000 --- a/panda/src/glstuff/glLatencyQueryContext_src.cxx +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 glLatencyQueryContext_src.cxx - * @author rdb - * @date 2014-09-24 - */ - -#ifndef OPENGLES // Timer queries not supported by OpenGL ES. - -TypeHandle CLP(LatencyQueryContext)::_type_handle; - -/** - * - */ -CLP(LatencyQueryContext):: -CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, - int pstats_index) : - CLP(TimerQueryContext)(glgsg, pstats_index), - _timestamp(0) -{ - glgsg->_glGetInteger64v(GL_TIMESTAMP, &_timestamp); -} - -/** - * Returns the timestamp that is the result of this timer query. There's no - * guarantee about which clock this uses, the only guarantee is that - * subtracting a start time from an end time should yield a time in seconds. - * If is_answer_ready() did not return true, this function may block before it - * returns. - * - * It is only valid to call this from the draw thread. - */ -double CLP(LatencyQueryContext):: -get_timestamp() const { - GLint64 time_ns; - _glgsg->_glGetQueryObjecti64v(_index, GL_QUERY_RESULT, &time_ns); - - return (time_ns - _timestamp) * 0.000000001; -} - -#endif // OPENGLES diff --git a/panda/src/glstuff/glLatencyQueryContext_src.h b/panda/src/glstuff/glLatencyQueryContext_src.h deleted file mode 100644 index 7c255cb5df..0000000000 --- a/panda/src/glstuff/glLatencyQueryContext_src.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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 glLatencyQueryContext_src.h - * @author rdb - * @date 2014-09-24 - */ - -class GraphicsStateGuardian; - -#ifndef OPENGLES // Timer queries not supported by OpenGL ES. - -/** - * This is a special variant of GLTimerQueryContext that measures the command - * latency, ie. the time it takes for the GPU to actually get to the commands - * we are issuing right now. - */ -class EXPCL_GL CLP(LatencyQueryContext) : public CLP(TimerQueryContext) { -public: - CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index); - - ALLOC_DELETED_CHAIN(CLP(LatencyQueryContext)); - - virtual double get_timestamp() const; - - GLint64 _timestamp; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - CLP(TimerQueryContext)::init_type(); - register_type(_type_handle, CLASSPREFIX_QUOTED "LatencyQueryContext", - CLP(TimerQueryContext)::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "glLatencyQueryContext_src.I" - -#endif // OPENGLES diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 8260d1430d..9d2fbd71e8 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2105,7 +2105,7 @@ set_state_and_transform(const RenderState *target_rs, */ void CLP(ShaderContext):: issue_parameters(int altered) { - PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector); + PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector); if (GLCAT.is_spam()) { GLCAT.spam() diff --git a/panda/src/glstuff/glTimerQueryContext_src.I b/panda/src/glstuff/glTimerQueryContext_src.I deleted file mode 100644 index c898c30261..0000000000 --- a/panda/src/glstuff/glTimerQueryContext_src.I +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 glTimerQueryContext_src.I - * @author rdb - * @date 2014-08-22 - */ - -/** - * - */ -INLINE CLP(TimerQueryContext):: -CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg, - int pstats_index) : - TimerQueryContext(pstats_index), - _glgsg(glgsg), - _index(0) -{ -} diff --git a/panda/src/glstuff/glTimerQueryContext_src.cxx b/panda/src/glstuff/glTimerQueryContext_src.cxx deleted file mode 100644 index a7b4ecb612..0000000000 --- a/panda/src/glstuff/glTimerQueryContext_src.cxx +++ /dev/null @@ -1,96 +0,0 @@ -/** - * 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 glTimerQueryContext_src.cxx - * @author rdb - * @date 2014-08-22 - */ - -#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; - -/** - * - */ -CLP(TimerQueryContext):: -~CLP(TimerQueryContext)() { - if (_index != 0) { - // Tell the GSG to recycle this index when it gets around to it. If it - // has already shut down, though, too bad. This means we never get to - // free this index, but presumably the app is already shutting down - // anyway. - if (auto glgsg = _glgsg.lock()) { - LightMutexHolder holder(glgsg->_lock); - glgsg->_deleted_queries.push_back(_index); - _index = 0; - } - } -} - -/** - * 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); -} - -/** - * 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(); -} - -/** - * Returns the timestamp that is the result of this timer query. There's no - * guarantee about which clock this uses, the only guarantee is that - * subtracting a start time from an end time should yield a time in seconds. - * If is_answer_ready() did not return true, this function may block before it - * returns. - * - * It is only valid to call this from the draw thread. - */ -double CLP(TimerQueryContext):: -get_timestamp() const { - GLuint64 time_ns; - - /*GLuint available; - _glgsg->_glGetQueryObjectuiv(_index[1], GL_QUERY_RESULT_AVAILABLE, &available); - if (available) { - // The answer is ready now. - do_get_timestamps(begin_ns, end_ns); - } else { - // The answer is not ready; this call will block. - PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector); - do_get_timestamps(begin_ns, end_ns); - }*/ - - _glgsg->_glGetQueryObjectui64v(_index, GL_QUERY_RESULT, &time_ns); - - return time_ns * 0.000000001; -} - -#endif // OPENGLES diff --git a/panda/src/glstuff/glTimerQueryContext_src.h b/panda/src/glstuff/glTimerQueryContext_src.h deleted file mode 100644 index 07709bc7af..0000000000 --- a/panda/src/glstuff/glTimerQueryContext_src.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 glTimerQueryContext_src.h - * @author rdb - * @date 2014-08-22 - */ - -#include "pandabase.h" -#include "timerQueryContext.h" -#include "deletedChain.h" -#include "clockObject.h" - -class GraphicsStateGuardian; - -#ifndef OPENGLES // Timer queries not supported by OpenGL ES. - -/** - * 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; - WPT(CLP(GraphicsStateGuardian)) _glgsg; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TimerQueryContext::init_type(); - register_type(_type_handle, CLASSPREFIX_QUOTED "TimerQueryContext", - TimerQueryContext::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "glTimerQueryContext_src.I" - -#endif // OPENGLES diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 2c388985f6..2d6db9f654 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -354,8 +354,6 @@ void CLP(init_classes)() { #ifndef OPENGLES CLP(OcclusionQueryContext)::init_type(); - CLP(TimerQueryContext)::init_type(); - CLP(LatencyQueryContext)::init_type(); #endif PandaSystem *ps = PandaSystem::get_global_ptr(); diff --git a/panda/src/glstuff/glstuff_src.cxx b/panda/src/glstuff/glstuff_src.cxx index 06abef89f4..fc281d1749 100644 --- a/panda/src/glstuff/glstuff_src.cxx +++ b/panda/src/glstuff/glstuff_src.cxx @@ -23,8 +23,6 @@ #include "glIndexBufferContext_src.cxx" #include "glBufferContext_src.cxx" #include "glOcclusionQueryContext_src.cxx" -#include "glTimerQueryContext_src.cxx" -#include "glLatencyQueryContext_src.cxx" #include "glGeomContext_src.cxx" #include "glGeomMunger_src.cxx" #include "glShaderContext_src.cxx" diff --git a/panda/src/glstuff/glstuff_src.h b/panda/src/glstuff/glstuff_src.h index 02e3208445..21ca8ad8fe 100644 --- a/panda/src/glstuff/glstuff_src.h +++ b/panda/src/glstuff/glstuff_src.h @@ -35,8 +35,6 @@ #include "glIndexBufferContext_src.h" #include "glBufferContext_src.h" #include "glOcclusionQueryContext_src.h" -#include "glTimerQueryContext_src.h" -#include "glLatencyQueryContext_src.h" #include "glGeomContext_src.h" #include "glGeomMunger_src.h" #include "glShaderContext_src.h" diff --git a/panda/src/gobj/CMakeLists.txt b/panda/src/gobj/CMakeLists.txt index 9565a73b0c..1835ffe3f8 100644 --- a/panda/src/gobj/CMakeLists.txt +++ b/panda/src/gobj/CMakeLists.txt @@ -61,7 +61,6 @@ set(P3GOBJ_HEADERS 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 @@ -141,7 +140,6 @@ set(P3GOBJ_SOURCES textureReloadRequest.cxx textureStage.cxx textureStagePool.cxx - timerQueryContext.cxx transformBlend.cxx transformBlendTable.cxx transformTable.cxx diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index df0990f69f..7ada146307 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -48,7 +48,6 @@ #include "textureReloadRequest.h" #include "textureStage.h" #include "textureContext.h" -#include "timerQueryContext.h" #include "samplerContext.h" #include "samplerState.h" #include "shader.h" @@ -626,7 +625,6 @@ ConfigureFn(config_gobj) { TexturePoolFilter::init_type(); TextureReloadRequest::init_type(); TextureStage::init_type(); - TimerQueryContext::init_type(); TransformBlend::init_type(); TransformBlendTable::init_type(); TransformTable::init_type(); diff --git a/panda/src/gobj/p3gobj_composite2.cxx b/panda/src/gobj/p3gobj_composite2.cxx index 56704b12b3..e0a6bcbd6c 100644 --- a/panda/src/gobj/p3gobj_composite2.cxx +++ b/panda/src/gobj/p3gobj_composite2.cxx @@ -20,7 +20,6 @@ #include "textureReloadRequest.cxx" #include "textureStage.cxx" #include "textureStagePool.cxx" -#include "timerQueryContext.cxx" #include "transformBlend.cxx" #include "transformBlendTable.cxx" #include "transformTable.cxx" diff --git a/panda/src/gobj/timerQueryContext.I b/panda/src/gobj/timerQueryContext.I deleted file mode 100644 index a542e96855..0000000000 --- a/panda/src/gobj/timerQueryContext.I +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 timerQueryContext.I - * @author rdb - * @date 2014-08-22 - */ - -/** - * - */ -INLINE TimerQueryContext:: -TimerQueryContext(int pstats_index) : - _pstats_index(pstats_index), - _frame_index(ClockObject::get_global_clock()->get_frame_count()) -{ -} diff --git a/panda/src/gobj/timerQueryContext.cxx b/panda/src/gobj/timerQueryContext.cxx deleted file mode 100644 index af69030ff4..0000000000 --- a/panda/src/gobj/timerQueryContext.cxx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * 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 timerQueryContext.cxx - * @author rdb - * @date 2014-08-22 - */ - -#include "timerQueryContext.h" - -TypeHandle TimerQueryContext::_type_handle; - -/** - * Returns the timestamp that is the result of this timer query. There's no - * guarantee about which clock this uses, the only guarantee is that - * subtracting a start time from an end time should yield a time in seconds. - * If is_answer_ready() did not return true, this function may block before it - * returns. - * - * It is only valid to call this from the draw thread. - */ -double TimerQueryContext:: -get_timestamp() const { - return 0.0; -} diff --git a/panda/src/gobj/timerQueryContext.h b/panda/src/gobj/timerQueryContext.h deleted file mode 100644 index 26fd32efbd..0000000000 --- a/panda/src/gobj/timerQueryContext.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * 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 timerQueryContext.h - * @author rdb - * @date 2014-08-22 - */ - -#ifndef TIMERQUERYCONTEXT_H -#define TIMERQUERYCONTEXT_H - -#include "pandabase.h" -#include "queryContext.h" -#include "clockObject.h" -#include "pStatCollector.h" - -/** - * - */ -class EXPCL_PANDA_GOBJ TimerQueryContext : public QueryContext { -public: - INLINE TimerQueryContext(int pstats_index); - - ALLOC_DELETED_CHAIN(TimerQueryContext); - - virtual double get_timestamp() const=0; - - int _frame_index; - int _pstats_index; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - QueryContext::init_type(); - register_type(_type_handle, "TimerQueryContext", - QueryContext::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; - - friend class PreparedGraphicsObjects; -}; - -#include "timerQueryContext.I" - -#endif diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx index 74e58edb58..7f48d39d0a 100644 --- a/panda/src/gobj/vertexDataPage.cxx +++ b/panda/src/gobj/vertexDataPage.cxx @@ -934,7 +934,7 @@ thread_main() { _tlock.acquire(); while (true) { - PStatClient::thread_tick(get_sync_name()); + PStatClient::thread_tick(); while (_manager->_pending_reads.empty() && _manager->_pending_writes.empty()) { diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 657badb048..593ebf8f20 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -403,6 +403,8 @@ client_main_tick() { return; } + ClockObject *clock = ClockObject::get_global_clock(); + _impl->client_main_tick(); MultiThingsByName::const_iterator ni = @@ -412,7 +414,8 @@ client_main_tick() { for (vector_int::const_iterator vi = indices.begin(); vi != indices.end(); ++vi) { - _impl->new_frame(*vi); + int frame_number = clock->get_frame_count(get_thread_object(*vi)); + _impl->new_frame(*vi, frame_number); } } } diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index 85d7e45f27..619c8fd33e 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -162,7 +162,7 @@ client_disconnect() { * data for the previous frame. */ void PStatClientImpl:: -new_frame(int thread_index) { +new_frame(int thread_index, int frame_number) { double frame_start = get_real_time(); nassertv(thread_index >= 0 && thread_index < _client->_num_threads); @@ -185,7 +185,6 @@ new_frame(int thread_index) { return; } - int frame_number = -1; PStatFrameData frame_data; if (!pthread->_frame_data.is_empty()) { @@ -205,11 +204,13 @@ new_frame(int thread_index) { } } pthread->_frame_data.swap(frame_data); - frame_number = pthread->_frame_number; + if (frame_number == -1) { + frame_number = pthread->_frame_number; + } } pthread->_frame_data.clear(); - pthread->_frame_number++; + pthread->_frame_number = frame_number + 1; _client->start(0, thread_index, frame_start); // Also record the time for the PStats operation itself. @@ -217,7 +218,7 @@ new_frame(int thread_index) { int pstats_index = PStatClient::_pstats_pcollector.get_index(); _client->start(pstats_index, current_thread_index, frame_start); - if (frame_number != -1) { + if (!frame_data.is_empty()) { transmit_frame_data(thread_index, frame_number, frame_data); } _client->stop(pstats_index, current_thread_index, get_real_time()); @@ -228,7 +229,7 @@ new_frame(int thread_index) { * data. */ void PStatClientImpl:: -add_frame(int thread_index, const PStatFrameData &frame_data) { +add_frame(int thread_index, int frame_number, const PStatFrameData &frame_data) { nassertv(thread_index >= 0 && thread_index < _client->_num_threads); PStatClient::InternalThread *pthread = _client->get_thread_ptr(thread_index); @@ -249,16 +250,12 @@ add_frame(int thread_index, const PStatFrameData &frame_data) { 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); - } + transmit_frame_data(thread_index, frame_number, frame_data); _client->stop(pstats_index, current_thread_index); } diff --git a/panda/src/pstatclient/pStatClientImpl.h b/panda/src/pstatclient/pStatClientImpl.h index 598a2e63e7..2b767838bc 100644 --- a/panda/src/pstatclient/pStatClientImpl.h +++ b/panda/src/pstatclient/pStatClientImpl.h @@ -65,8 +65,8 @@ public: INLINE void client_resume_after_pause(); - void new_frame(int thread_index); - void add_frame(int thread_index, const PStatFrameData &frame_data); + void new_frame(int thread_index, int frame_number = -1); + void add_frame(int thread_index, int frame_number, const PStatFrameData &frame_data); private: void transmit_frame_data(int thread_index, int frame_number, diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 30110fec69..03a93d7b59 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -220,7 +220,7 @@ static LevelCollectorProperties level_properties[] = { { 1, "PipelineCyclers:Dirty", { 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 }, + { 1, "window1 latency", { 0.8, 0.2, 0.0 }, "ms", 10, 1.0 / 1000.0 }, { 0, nullptr } }; diff --git a/panda/src/pstatclient/pStatThread.cxx b/panda/src/pstatclient/pStatThread.cxx index e263ac089a..0315b5591f 100644 --- a/panda/src/pstatclient/pStatThread.cxx +++ b/panda/src/pstatclient/pStatThread.cxx @@ -24,9 +24,9 @@ * threads with the indicated sync name. */ void PStatThread:: -new_frame() { +new_frame(int frame_number) { #ifdef DO_PSTATS - _client->get_impl()->new_frame(_index); + _client->get_impl()->new_frame(_index, frame_number); #endif } @@ -35,9 +35,9 @@ new_frame() { * data to send for this frame. */ void PStatThread:: -add_frame(const PStatFrameData &frame_data) { +add_frame(int frame_number, const PStatFrameData &frame_data) { #ifdef DO_PSTATS - _client->get_impl()->add_frame(_index, frame_data); + _client->get_impl()->add_frame(_index, frame_number, frame_data); #endif } diff --git a/panda/src/pstatclient/pStatThread.h b/panda/src/pstatclient/pStatThread.h index 74defa2a15..e3f4acb7e5 100644 --- a/panda/src/pstatclient/pStatThread.h +++ b/panda/src/pstatclient/pStatThread.h @@ -36,8 +36,8 @@ PUBLISHED: INLINE PStatThread(const PStatThread ©); INLINE void operator = (const PStatThread ©); - void new_frame(); - void add_frame(const PStatFrameData &frame_data); + void new_frame(int frame_number = -1); + void add_frame(int frame_number, const PStatFrameData &frame_data); Thread *get_thread() const; INLINE int get_index() const; diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index 1c4fbe9773..1af86c6b95 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -122,21 +122,6 @@ end_frame(FrameMode mode, Thread *current_thread) { } } -/** - * This function will be called within the draw thread after end_frame() has - * been called on all windows, to initiate the exchange of the front and back - * buffers. - * - * This should instruct the window to prepare for the flip at the next video - * sync, but it should not wait. - * - * We have the two separate functions, begin_flip() and end_flip(), to make it - * easier to flip all of the windows at the same time. - */ -void wglGraphicsWindow:: -begin_flip() { -} - /** * This function will be called within the draw thread after end_frame() has * been called on all windows, to initiate the exchange of the front and back diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h index 06a74aa501..66dc2d5878 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.h +++ b/panda/src/wgldisplay/wglGraphicsWindow.h @@ -34,7 +34,6 @@ public: virtual bool begin_frame(FrameMode mode, Thread *current_thread); virtual void end_frame(FrameMode mode, Thread *current_thread); - virtual void begin_flip(); virtual void ready_flip(); virtual void end_flip(); diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index ceef979b75..93b4ba989a 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -218,21 +218,6 @@ close_ime() { return; } -/** - * This function will be called within the draw thread after end_frame() has - * been called on all windows, to initiate the exchange of the front and back - * buffers. - * - * This should instruct the window to prepare for the flip at the next video - * sync, but it should not wait. - * - * We have the two separate functions, begin_flip() and end_flip(), to make it - * easier to flip all of the windows at the same time. - */ -void WinGraphicsWindow:: -begin_flip() { -} - /** * Do whatever processing is necessary to ensure that the window responds to * user events. Also, honor any requests recently made via diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h index ca33615219..d262249349 100644 --- a/panda/src/windisplay/winGraphicsWindow.h +++ b/panda/src/windisplay/winGraphicsWindow.h @@ -77,8 +77,6 @@ public: virtual void close_ime(); - virtual void begin_flip(); - virtual void process_events(); virtual void set_properties_now(WindowProperties &properties); void receive_windows_message(unsigned int msg, int wparam, int lparam); From 8b5fc7d835a8f0bde309d378a51f34747c6f204b Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 15:21:32 +0100 Subject: [PATCH 045/166] stdpy: Switch from deprecated ConditionVarFull to ConditionVar --- direct/src/stdpy/threading.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py index 3b83e7c691..c4062b48a9 100644 --- a/direct/src/stdpy/threading.py +++ b/direct/src/stdpy/threading.py @@ -216,8 +216,8 @@ class RLock(core.ReMutex): core.ReMutex.__init__(self, name) -class Condition(core.ConditionVarFull): - """ This class provides a wrapper around Panda's ConditionVarFull +class Condition(core.ConditionVar): + """ This class provides a wrapper around Panda's ConditionVar object. The wrapper is designed to emulate Python's own threading.Condition object. """ @@ -230,7 +230,7 @@ class Condition(core.ConditionVarFull): assert isinstance(lock, Lock) self.__lock = lock - core.ConditionVarFull.__init__(self, self.__lock) + core.ConditionVar.__init__(self, self.__lock) def acquire(self, *args, **kw): return self.__lock.acquire(*args, **kw) @@ -240,12 +240,12 @@ class Condition(core.ConditionVarFull): def wait(self, timeout = None): if timeout is None: - core.ConditionVarFull.wait(self) + core.ConditionVar.wait(self) else: - core.ConditionVarFull.wait(self, timeout) + core.ConditionVar.wait(self, timeout) def notifyAll(self): - core.ConditionVarFull.notifyAll(self) + core.ConditionVar.notifyAll(self) notify_all = notifyAll @@ -295,7 +295,7 @@ class Event: def __init__(self): self.__lock = core.Mutex("Python Event") - self.__cvar = core.ConditionVarFull(self.__lock) + self.__cvar = core.ConditionVar(self.__lock) self.__flag = False def is_set(self): From a33fcab8da0598b165f1c158a767332371ccccd2 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 15:22:10 +0100 Subject: [PATCH 046/166] tests: Switch from deprecated ConditionVarFull to ConditionVar --- tests/pipeline/test_condition_var.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/pipeline/test_condition_var.py b/tests/pipeline/test_condition_var.py index 4cfc6c226a..b2021418df 100644 --- a/tests/pipeline/test_condition_var.py +++ b/tests/pipeline/test_condition_var.py @@ -1,4 +1,4 @@ -from panda3d.core import Mutex, ConditionVarFull +from panda3d.core import Mutex, ConditionVar from panda3d import core from direct.stdpy import thread import pytest @@ -14,7 +14,7 @@ def yield_thread(): def test_cvar_notify(): # Just tests that notifying without waiting does no harm. m = Mutex() - cv = ConditionVarFull(m) + cv = ConditionVar(m) cv.notify() cv.notify_all() @@ -24,7 +24,7 @@ def test_cvar_notify(): def test_cvar_notify_locked(): # Tests the same thing, but with the lock held. m = Mutex() - cv = ConditionVarFull(m) + cv = ConditionVar(m) with m: cv.notify() @@ -41,7 +41,7 @@ def test_cvar_notify_locked(): def test_cvar_notify_thread(num_threads): # Tests notify() with some number of threads waiting. m = Mutex() - cv = ConditionVarFull(m) + cv = ConditionVar(m) # We prematurely notify, so that we can test that it's not doing anything. m.acquire() @@ -98,7 +98,7 @@ def test_cvar_notify_thread(num_threads): def test_cvar_notify_all_threads(num_threads): # Tests notify_all() with some number of threads waiting. m = Mutex() - cv = ConditionVarFull(m) + cv = ConditionVar(m) # We prematurely notify, so that we can test that it's not doing anything. m.acquire() From 759115fbc788859e85e02d6ee293208fd68c97c1 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 15:25:04 +0100 Subject: [PATCH 047/166] pstats: Fix crash when frame has only level data and no time data --- panda/src/pstatclient/pStatFrameData.I | 6 +++--- pandatool/src/pstatserver/pStatThreadData.cxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/panda/src/pstatclient/pStatFrameData.I b/panda/src/pstatclient/pStatFrameData.I index 47f9a99243..f64054643a 100644 --- a/panda/src/pstatclient/pStatFrameData.I +++ b/panda/src/pstatclient/pStatFrameData.I @@ -105,7 +105,7 @@ add_level(int index, double level) { */ INLINE double PStatFrameData:: get_start() const { - if (is_empty()) { + if (is_time_empty()) { return 0.0; } @@ -118,7 +118,7 @@ get_start() const { */ INLINE double PStatFrameData:: get_end() const { - nassertr(!is_empty(), 0.0); + nassertr(!is_time_empty(), 0.0); return _time_data.back()._value; } @@ -128,7 +128,7 @@ get_end() const { */ INLINE double PStatFrameData:: get_net_time() const { - nassertr(!is_empty(), 0.0); + nassertr(!is_time_empty(), 0.0); return _time_data.back()._value - _time_data.front()._value; } diff --git a/pandatool/src/pstatserver/pStatThreadData.cxx b/pandatool/src/pstatserver/pStatThreadData.cxx index 61cc92e88e..06da8a4c3c 100644 --- a/pandatool/src/pstatserver/pStatThreadData.cxx +++ b/pandatool/src/pstatserver/pStatThreadData.cxx @@ -267,7 +267,7 @@ record_new_frame(int frame_number, PStatFrameData *frame_data) { double oldest_allowable_time = time - _history; while (!_frames.empty() && (_frames.front() == nullptr || - _frames.front()->is_empty() || + _frames.front()->is_time_empty() || _frames.front()->get_start() < oldest_allowable_time)) { delete _frames.front(); _frames.pop_front(); From 284ffe9e835d4e432f43f723938706ca3580dcb6 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 15:25:52 +0100 Subject: [PATCH 048/166] pstats: Fix status bar when collector has level data on multiple threads Status bar now shows total across all threads, and double-clicking it opens strip charts for all the threads that have data for it --- pandatool/src/gtk-stats/gtkStatsMonitor.cxx | 19 +++++++++++++++++++ pandatool/src/win-stats/winStatsMonitor.cxx | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx index ff41ed3a3f..9a04cc523a 100644 --- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx +++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx @@ -637,6 +637,7 @@ update_status_bar() { if (thread_data == nullptr || thread_data->is_empty()) { return; } + int frame_number = thread_data->get_latest_frame_number(); const PStatFrameData &frame_data = thread_data->get_latest_frame(); pvector collectors; @@ -666,6 +667,13 @@ update_status_bar() { } } + // Add the value for other threads that have this collector. + for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) { + PStatView &view = get_level_view(collector, thread_index); + view.set_to_frame(frame_number); + value += view.get_net_value(); + } + const PStatCollectorDef &def = client_data->get_collector_def(collector); std::string text = def._name; text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); @@ -720,6 +728,17 @@ status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { monitor->open_strip_chart(0, collector, collector != 0); + + // Also open a strip chart for other threads with data for this + // collector. + if (collector != 0) { + for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) { + PStatView &view = monitor->get_level_view(collector, thread_index); + if (view.get_net_value() > 0.0) { + monitor->open_strip_chart(thread_index, collector, true); + } + } + } return TRUE; } else if (event->type == GDK_BUTTON_PRESS && event->button == 3 && index > 0) { diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx index b6a80c7151..b8fade17f0 100644 --- a/pandatool/src/win-stats/winStatsMonitor.cxx +++ b/pandatool/src/win-stats/winStatsMonitor.cxx @@ -712,6 +712,7 @@ update_status_bar() { if (thread_data == nullptr || thread_data->is_empty()) { return; } + int frame_number = thread_data->get_latest_frame_number(); const PStatFrameData &frame_data = thread_data->get_latest_frame(); // Gather the top-level collector list. @@ -734,6 +735,13 @@ update_status_bar() { } } + // Add the value for other threads that have this collector. + for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) { + PStatView &view = get_level_view(collector, thread_index); + view.set_to_frame(frame_number); + value += view.get_net_value(); + } + const PStatCollectorDef &def = client_data->get_collector_def(collector); std::string text = "\t" + def._name; text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units); @@ -944,6 +952,16 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { else if (mouse.dwItemSpec >= 1 && mouse.dwItemSpec <= _status_bar_collectors.size()) { int collector = _status_bar_collectors[mouse.dwItemSpec - 1]; open_strip_chart(0, collector, true); + + // Also open a strip chart for other threads with data for this + // collector. + const PStatClientData *client_data = get_client_data(); + for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) { + PStatView &view = get_level_view(collector, thread_index); + if (view.get_net_value() > 0.0) { + open_strip_chart(thread_index, collector, true); + } + } } return TRUE; } From 72c891c0df78c747c2fb0f76af0740067bddf61c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 17:00:47 +0100 Subject: [PATCH 049/166] display: Fix issues with PStats GPU timing: - Leaking queries by never reusing / releasing them - Clock synchronization was way off when driver waited on GPU during sync point --- panda/src/display/graphicsStateGuardian.cxx | 5 ++++- panda/src/glstuff/glGraphicsStateGuardian_src.cxx | 14 ++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 503fb841b3..d1c87c3ab2 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -2363,7 +2363,10 @@ calc_projection_mat(const Lens *lens) { */ bool GraphicsStateGuardian:: begin_frame(Thread *current_thread) { - _prepared_objects->begin_frame(this, current_thread); + { + PStatTimer timer(_prepare_pcollector); + _prepared_objects->begin_frame(this, current_thread); + } // We should reset the state to the default at the beginning of every frame. // Although this will incur additional overhead, particularly in a simple diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 69ef795ee3..29d5bedd5e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -4420,6 +4420,7 @@ begin_frame_timing(int frame_number) { } PStatClient *client = PStatClient::get_global_pstats(); + PStatTimer timer(_wait_timer_pcollector); _timer_queries_pcollector.clear_level(); @@ -4435,12 +4436,14 @@ begin_frame_timing(int frame_number) { _deleted_queries.pop_back(); _glQueryCounter(frame_query, GL_TIMESTAMP); - // Synchronize the GL time with the PStats clock. + // Synchronize the GL time with the PStats clock. Note that the driver may + // arbitrarily decide to wait a long time on this call if the queue is + // saturated with work. Experimentally, it seems that it queries the time + // *after* the wait, so we take the CPU time after it returns. If we find + // that some drivers do it differently, we may have to do multiple calls. GLint64 gl_time; - double cpu_time1 = client->get_real_time(); _glGetInteger64v(GL_TIMESTAMP, &gl_time); - double cpu_time2 = client->get_real_time(); - double cpu_time = (cpu_time1 + cpu_time2) / 2.0; + double cpu_time = client->get_real_time(); // Check if the results from the previous frame are available. We just need // to check whether the last query for each frame is available. @@ -4486,8 +4489,6 @@ end_frame_timing(const FrameTiming &frame) { return; } - PStatTimer timer(_wait_timer_pcollector); - // We represent each GSG as one thread. In the future we may change this to // representing each graphics device as one thread, but OpenGL doesn't really // expose this information to us. @@ -4516,6 +4517,7 @@ end_frame_timing(const FrameTiming &frame) { frame_data.add_start(query.second & 0x7fff, time); } } + _deleted_queries.push_back(query.first); } // The end time of the last collector is implicitly the frame's end time. From 7baeaf3809477558ed64a70194ba56643b59f715 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 17:02:42 +0100 Subject: [PATCH 050/166] event: New C++ AsyncTaskManager::add() no longer uses std::function std::function has unnecessary overhead, better to just create an AsyncTask subclass in-place storing the closure This obsoletes FunctionAsyncTask, it will be removed in a future commit --- panda/src/event/asyncTaskManager.I | 26 ++++++++++++++++++++++---- panda/src/event/asyncTaskManager.h | 3 ++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/panda/src/event/asyncTaskManager.I b/panda/src/event/asyncTaskManager.I index 6be7ef3508..976e445671 100644 --- a/panda/src/event/asyncTaskManager.I +++ b/panda/src/event/asyncTaskManager.I @@ -34,14 +34,32 @@ get_clock() { #ifndef CPPPARSER /** - * Adds a new task which calls the indicated function to the task manager. - * Returns the newly created FunctionAsyncTask object. + * Adds a new task to the task manager which calls the indicated callable. + * This method is defined as a more convenient alternative to subclassing + * AsyncTask. + * + * This given callable allowed to be any object defining a call operator that + * accepts an AsyncTask pointer and returns a DoneStatus. + * + * Returns the newly created AsyncTask object. * * @since 1.11.0 */ +template INLINE AsyncTask *AsyncTaskManager:: -add(const std::string &name, FunctionAsyncTask::TaskFunction function) { - AsyncTask *task = new FunctionAsyncTask(name, std::move(function)); +add(const std::string &name, Callable callable) { + class InlineTask final : public AsyncTask { + public: + InlineTask(Callable callable) : _callable(std::move(callable)) {} + + private: + virtual DoneStatus do_task() override final { + return _callable(this); + } + + Callable _callable; + }; + AsyncTask *task = new InlineTask(std::move(callable)); add(task); return task; } diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index 0982e5e2c7..00b5329e9e 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -66,7 +66,8 @@ PUBLISHED: void add(AsyncTask *task); #ifndef CPPPARSER - INLINE AsyncTask *add(const std::string &name, FunctionAsyncTask::TaskFunction function); + template + INLINE AsyncTask *add(const std::string &name, Callable callable); #endif bool has_task(AsyncTask *task) const; From b4d51c24e9d58251795d153fe1f762a643e39689 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 17:06:25 +0100 Subject: [PATCH 051/166] event: Remove FunctionAsyncTask To create a task from a lambda, it is more efficient to use the new AsyncTaskManager::add() short-hand which creates an AsyncTask subclass in-place. --- panda/src/event/CMakeLists.txt | 2 - panda/src/event/asyncTaskManager.h | 1 - panda/src/event/config_event.cxx | 2 - panda/src/event/functionAsyncTask.I | 83 -------------------------- panda/src/event/functionAsyncTask.cxx | 81 ------------------------- panda/src/event/functionAsyncTask.h | 83 -------------------------- panda/src/event/genericAsyncTask.h | 2 - panda/src/event/p3event_composite1.cxx | 1 - panda/src/gobj/texture.cxx | 5 +- 9 files changed, 2 insertions(+), 258 deletions(-) delete mode 100644 panda/src/event/functionAsyncTask.I delete mode 100644 panda/src/event/functionAsyncTask.cxx delete mode 100644 panda/src/event/functionAsyncTask.h diff --git a/panda/src/event/CMakeLists.txt b/panda/src/event/CMakeLists.txt index 47136d1b5e..b0464ff793 100644 --- a/panda/src/event/CMakeLists.txt +++ b/panda/src/event/CMakeLists.txt @@ -9,7 +9,6 @@ set(P3EVENT_HEADERS config_event.h buttonEvent.I buttonEvent.h buttonEventList.I buttonEventList.h - functionAsyncTask.h functionAsyncTask.I genericAsyncTask.h genericAsyncTask.I pointerEvent.I pointerEvent.h pointerEventList.I pointerEventList.h @@ -29,7 +28,6 @@ set(P3EVENT_SOURCES asyncTaskSequence.cxx buttonEvent.cxx buttonEventList.cxx - functionAsyncTask.cxx genericAsyncTask.cxx pointerEvent.cxx pointerEventList.cxx diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index 00b5329e9e..0028054ef7 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -30,7 +30,6 @@ #include "clockObject.h" #include "ordered_vector.h" #include "indirectCompareNames.h" -#include "functionAsyncTask.h" /** * A class to manage a loose queue of isolated tasks, which can be performed diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx index 93dc8d916b..ccae0d95ae 100644 --- a/panda/src/event/config_event.cxx +++ b/panda/src/event/config_event.cxx @@ -22,7 +22,6 @@ #include "event.h" #include "eventHandler.h" #include "eventParameter.h" -#include "functionAsyncTask.h" #include "genericAsyncTask.h" #include "pointerEventList.h" @@ -50,7 +49,6 @@ ConfigureFn(config_event) { EventHandler::init_type(); EventStoreInt::init_type("EventStoreInt"); EventStoreDouble::init_type("EventStoreDouble"); - FunctionAsyncTask::init_type(); GenericAsyncTask::init_type(); ButtonEventList::register_with_read_factory(); diff --git a/panda/src/event/functionAsyncTask.I b/panda/src/event/functionAsyncTask.I deleted file mode 100644 index aa413dacf0..0000000000 --- a/panda/src/event/functionAsyncTask.I +++ /dev/null @@ -1,83 +0,0 @@ -/** - * 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 functionAsyncTask.I - * @author rdb - * @date 2021-11-29 - */ - -/** - * - */ -INLINE FunctionAsyncTask:: -FunctionAsyncTask(const std::string &name) : - AsyncTask(name) -{ -} - -/** - * - */ -INLINE FunctionAsyncTask:: -FunctionAsyncTask(const std::string &name, FunctionAsyncTask::TaskFunction function) : - AsyncTask(name), - _function(std::move(function)) -{ -} - -/** - * Replaces the function that is called when the task runs. - */ -INLINE void FunctionAsyncTask:: -set_function(TaskFunction function) { - _function = std::move(function); -} - -/** - * Returns the function that is called when the task runs. - */ -INLINE const FunctionAsyncTask::TaskFunction &FunctionAsyncTask:: -get_function() const { - return _function; -} - -/** - * Replaces the function that is called when the task begins. This is an - * optional function. - */ -INLINE void FunctionAsyncTask:: -set_upon_birth(BirthFunction upon_birth) { - _upon_birth = std::move(upon_birth); -} - -/** - * Returns the function that is called when the task begins, or NULL if the - * function is not defined. - */ -INLINE const FunctionAsyncTask::BirthFunction &FunctionAsyncTask:: -get_upon_birth() const { - return _upon_birth; -} - -/** - * Replaces the function that is called when the task ends. This is an - * optional function. - */ -INLINE void FunctionAsyncTask:: -set_upon_death(FunctionAsyncTask::DeathFunction upon_death) { - _upon_death = upon_death; -} - -/** - * Returns the function that is called when the task ends, or NULL if the - * function is not defined. - */ -INLINE const FunctionAsyncTask::DeathFunction &FunctionAsyncTask:: -get_upon_death() const { - return _upon_death; -} diff --git a/panda/src/event/functionAsyncTask.cxx b/panda/src/event/functionAsyncTask.cxx deleted file mode 100644 index 54b591918d..0000000000 --- a/panda/src/event/functionAsyncTask.cxx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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 functionAsyncTask.cxx - * @author rdb - * @date 2021-11-29 - */ - -#include "functionAsyncTask.h" -#include "pnotify.h" - -#ifndef CPPPARSER - -TypeHandle FunctionAsyncTask::_type_handle; - -/** - * Override this function to return true if the task can be successfully - * executed, false if it cannot. Mainly intended as a sanity check when - * attempting to add the task to a task manager. - * - * This function is called with the lock held. - */ -bool FunctionAsyncTask:: -is_runnable() { - return !!_function; -} - -/** - * Override this function to do something useful for the task. - * - * This function is called with the lock *not* held. - */ -AsyncTask::DoneStatus FunctionAsyncTask:: -do_task() { - nassertr(_function, DS_interrupt); - return _function(this); -} - -/** - * Override this function to do something useful when the task has been added - * to the active queue. - * - * This function is called with the lock *not* held. - */ -void FunctionAsyncTask:: -upon_birth(AsyncTaskManager *manager) { - AsyncTask::upon_birth(manager); - - if (_upon_birth) { - _upon_birth(this); - } -} - -/** - * Override this function to do something useful when the task has been - * removed from the active queue. The parameter clean_exit is true if the - * task has been removed because it exited normally (returning DS_done), or - * false if it was removed for some other reason (e.g. - * AsyncTaskManager::remove()). By the time this method is called, _manager - * has been cleared, so the parameter manager indicates the original - * AsyncTaskManager that owned this task. - * - * The normal behavior is to throw the done_event only if clean_exit is true. - * - * This function is called with the lock *not* held. - */ -void FunctionAsyncTask:: -upon_death(AsyncTaskManager *manager, bool clean_exit) { - AsyncTask::upon_death(manager, clean_exit); - - if (_upon_death) { - _upon_death(this, clean_exit); - } -} - -#endif // CPPPARSER diff --git a/panda/src/event/functionAsyncTask.h b/panda/src/event/functionAsyncTask.h deleted file mode 100644 index 297d71c691..0000000000 --- a/panda/src/event/functionAsyncTask.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * 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 functionAsyncTask.h - * @author rdb - * @date 2021-11-29 - */ - -#ifndef FUNCTIONASYNCTASK_H -#define FUNCTIONASYNCTASK_H - -#include "pandabase.h" - -#include "asyncTask.h" - -#ifndef CPPPARSER -#include - -/** - * Associates a generic std::function (eg. a lambda) with an AsyncTask object. - * You can use this when you want to create an AsyncTask without having to - * subclass. - * - * @since 1.11.0 - */ -class EXPCL_PANDA_EVENT FunctionAsyncTask final : public AsyncTask { -public: - typedef std::function TaskFunction; - typedef std::function BirthFunction; - typedef std::function DeathFunction; - - INLINE FunctionAsyncTask(const std::string &name = std::string()); - INLINE FunctionAsyncTask(const std::string &name, TaskFunction function); - ALLOC_DELETED_CHAIN(FunctionAsyncTask); - - INLINE void set_function(TaskFunction function); - INLINE const TaskFunction &get_function() const; - - INLINE void set_upon_birth(BirthFunction function); - INLINE const BirthFunction &get_upon_birth() const; - - INLINE void set_upon_death(DeathFunction function); - INLINE const DeathFunction &get_upon_death() const; - -protected: - virtual bool is_runnable() override; - virtual DoneStatus do_task() override; - virtual void upon_birth(AsyncTaskManager *manager) override; - virtual void upon_death(AsyncTaskManager *manager, bool clean_exit) override; - -private: - TaskFunction _function; - BirthFunction _upon_birth; - DeathFunction _upon_death; - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - AsyncTask::init_type(); - register_type(_type_handle, "FunctionAsyncTask", - AsyncTask::get_class_type()); - } - virtual TypeHandle get_type() const override { - return get_class_type(); - } - virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} - -private: - static TypeHandle _type_handle; -}; - -#include "functionAsyncTask.I" - -#endif // CPPPARSER - -#endif diff --git a/panda/src/event/genericAsyncTask.h b/panda/src/event/genericAsyncTask.h index ce7f635156..f35d780e3b 100644 --- a/panda/src/event/genericAsyncTask.h +++ b/panda/src/event/genericAsyncTask.h @@ -22,8 +22,6 @@ * Associates a generic C-style function pointer with an AsyncTask object. * You can use this when you want to create an AsyncTask without having to * subclass. - * - * @deprecated See FunctionAsyncTask instead, which is more powerful. */ class EXPCL_PANDA_EVENT GenericAsyncTask : public AsyncTask { public: diff --git a/panda/src/event/p3event_composite1.cxx b/panda/src/event/p3event_composite1.cxx index 34fcb9a0c9..dc72d2b03f 100644 --- a/panda/src/event/p3event_composite1.cxx +++ b/panda/src/event/p3event_composite1.cxx @@ -7,7 +7,6 @@ #include "asyncTaskSequence.cxx" #include "buttonEvent.cxx" #include "buttonEventList.cxx" -#include "functionAsyncTask.cxx" #include "genericAsyncTask.cxx" #include "pointerEvent.cxx" #include "pointerEventList.cxx" diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 51b7f7b64f..1e53294813 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1065,7 +1065,7 @@ async_ensure_ram_image(bool allow_compression, int priority) { double delay = async_load_delay; // This texture has not yet been queued to be reloaded. Queue it up now. - task = new FunctionAsyncTask(task_name, [=](AsyncTask *task) { + task = task_mgr->add(task_name, [=](AsyncTask *task) { if (delay != 0.0) { Thread::sleep(delay); } @@ -1076,9 +1076,8 @@ async_ensure_ram_image(bool allow_compression, int priority) { } return AsyncTask::DS_done; }); - task->set_task_chain("texture_reload"); task->set_priority(priority); - task_mgr->add(task); + task->set_task_chain("texture_reload"); cdataw->_reload_task = task; return (AsyncFuture *)task; } From fd033e66f1cae1f59b26f0b1848d9a4230f99b65 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Feb 2022 17:28:57 +0100 Subject: [PATCH 052/166] pstats: Add support for profiling thread context switches Disabled by default, enable with `pstats-thread-profiling true` in Config.prc --- panda/src/pipeline/conditionVarPosixImpl.I | 4 + panda/src/pipeline/conditionVarPosixImpl.cxx | 10 ++ panda/src/pipeline/conditionVarPosixImpl.h | 5 + panda/src/pipeline/conditionVarWin32Impl.I | 8 + panda/src/pipeline/conditionVarWin32Impl.cxx | 3 + panda/src/pipeline/conditionVarWin32Impl.h | 3 + panda/src/pipeline/thread.I | 20 +++ panda/src/pipeline/thread.cxx | 3 + panda/src/pipeline/thread.h | 6 + panda/src/pipeline/threadDummyImpl.I | 8 + panda/src/pipeline/threadDummyImpl.h | 2 + panda/src/pipeline/threadPosixImpl.cxx | 25 +++ panda/src/pipeline/threadPosixImpl.h | 2 + panda/src/pipeline/threadSimpleImpl.cxx | 29 ++++ panda/src/pipeline/threadSimpleImpl.h | 5 + panda/src/pipeline/threadSimpleManager.cxx | 2 + panda/src/pipeline/threadWin32Impl.I | 3 +- panda/src/pipeline/threadWin32Impl.cxx | 59 +++++++ panda/src/pipeline/threadWin32Impl.h | 3 + panda/src/pstatclient/config_pstatclient.cxx | 5 + panda/src/pstatclient/config_pstatclient.h | 1 + panda/src/pstatclient/pStatClient.cxx | 32 ++++ panda/src/pstatclient/pStatClient.h | 4 + panda/src/pstatclient/pStatClientImpl.cxx | 159 +++++++++++++++++++ panda/src/pstatclient/pStatClientImpl.h | 2 + 25 files changed, 402 insertions(+), 1 deletion(-) diff --git a/panda/src/pipeline/conditionVarPosixImpl.I b/panda/src/pipeline/conditionVarPosixImpl.I index 0b3e533717..2f8e73e5f5 100644 --- a/panda/src/pipeline/conditionVarPosixImpl.I +++ b/panda/src/pipeline/conditionVarPosixImpl.I @@ -39,7 +39,11 @@ INLINE ConditionVarPosixImpl:: INLINE void ConditionVarPosixImpl:: wait() { TAU_PROFILE("ConditionVarPosixImpl::wait()", " ", TAU_USER); +#ifdef DO_PSTATS + int result = _wait_func(&_cvar, &_mutex._lock); +#else int result = pthread_cond_wait(&_cvar, &_mutex._lock); +#endif #ifndef NDEBUG if (result != 0) { pipeline_cat.error() diff --git a/panda/src/pipeline/conditionVarPosixImpl.cxx b/panda/src/pipeline/conditionVarPosixImpl.cxx index e2e527c369..0d48b3034b 100644 --- a/panda/src/pipeline/conditionVarPosixImpl.cxx +++ b/panda/src/pipeline/conditionVarPosixImpl.cxx @@ -18,6 +18,12 @@ #include "conditionVarPosixImpl.h" #include +int (*ConditionVarPosixImpl::_wait_func)(pthread_cond_t *, + pthread_mutex_t *) = &pthread_cond_wait; +int (*ConditionVarPosixImpl::_timedwait_func)(pthread_cond_t *, + pthread_mutex_t *, + const struct timespec *) = &pthread_cond_timedwait; + /** * */ @@ -41,7 +47,11 @@ wait(double timeout) { ++ts.tv_sec; } +#ifdef DO_PSTATS + int result = _timedwait_func(&_cvar, &_mutex._lock, &ts); +#else int result = pthread_cond_timedwait(&_cvar, &_mutex._lock, &ts); +#endif #ifndef NDEBUG if (result != 0 && result != ETIMEDOUT) { pipeline_cat.error() diff --git a/panda/src/pipeline/conditionVarPosixImpl.h b/panda/src/pipeline/conditionVarPosixImpl.h index e39a5a122b..56bc3e9acc 100644 --- a/panda/src/pipeline/conditionVarPosixImpl.h +++ b/panda/src/pipeline/conditionVarPosixImpl.h @@ -43,6 +43,11 @@ public: private: MutexPosixImpl &_mutex; pthread_cond_t _cvar; + + static int (*_wait_func)(pthread_cond_t *, pthread_mutex_t *); + static int (*_timedwait_func)(pthread_cond_t *, pthread_mutex_t *, + const struct timespec *); + friend class PStatClientImpl; }; #include "conditionVarPosixImpl.I" diff --git a/panda/src/pipeline/conditionVarWin32Impl.I b/panda/src/pipeline/conditionVarWin32Impl.I index 275acff593..56fb036266 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.I +++ b/panda/src/pipeline/conditionVarWin32Impl.I @@ -23,7 +23,11 @@ ConditionVarWin32Impl(MutexWin32Impl &mutex) : _mutex(mutex) { */ INLINE void ConditionVarWin32Impl:: wait() { +#ifdef DO_PSTATS + _wait_func(&_cvar, &_mutex._lock, INFINITE, 0); +#else SleepConditionVariableSRW(&_cvar, &_mutex._lock, INFINITE, 0); +#endif } /** @@ -31,7 +35,11 @@ wait() { */ INLINE void ConditionVarWin32Impl:: wait(double timeout) { +#ifdef DO_PSTATS + _wait_func(&_cvar, &_mutex._lock, (DWORD)(timeout * 1000.0), 0); +#else SleepConditionVariableSRW(&_cvar, &_mutex._lock, (DWORD)(timeout * 1000.0), 0); +#endif } /** diff --git a/panda/src/pipeline/conditionVarWin32Impl.cxx b/panda/src/pipeline/conditionVarWin32Impl.cxx index 5ba4165a0c..6f4776dfa0 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.cxx +++ b/panda/src/pipeline/conditionVarWin32Impl.cxx @@ -17,4 +17,7 @@ #include "conditionVarWin32Impl.h" +// This function gets replaced by PStats to measure the time spent waiting. +BOOL (*ConditionVarWin32Impl::_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG) = &SleepConditionVariableSRW; + #endif // _WIN32 diff --git a/panda/src/pipeline/conditionVarWin32Impl.h b/panda/src/pipeline/conditionVarWin32Impl.h index 5c8d4a91d3..12cbe053f7 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.h +++ b/panda/src/pipeline/conditionVarWin32Impl.h @@ -40,6 +40,9 @@ public: private: MutexWin32Impl &_mutex; CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT; + + static BOOL (*_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); + friend class PStatClientImpl; }; #include "conditionVarWin32Impl.I" diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index dc7001fd06..7fce66e6d5 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -191,7 +191,11 @@ is_simple_threads() { INLINE void Thread:: sleep(double seconds) { TAU_PROFILE("void Thread::sleep(double)", " ", TAU_USER); +#ifdef DO_PSTATS + _sleep_func(seconds); +#else ThreadImpl::sleep(seconds); +#endif } /** @@ -200,7 +204,11 @@ sleep(double seconds) { INLINE void Thread:: force_yield() { TAU_PROFILE("void Thread::yield()", " ", TAU_USER); +#ifdef DO_PSTATS + _yield_func(); +#else ThreadImpl::yield(); +#endif } /** @@ -214,6 +222,18 @@ consider_yield() { ThreadImpl::consider_yield(); } + +/** + * Returns thread statistics. The first number is the total number of context + * switches reported by the OS, and the second number is the number of + * involuntary context switches (ie. the thread was scheduled out by the OS), + * if known. + */ +INLINE bool Thread:: +get_context_switches(size_t &total, size_t &involuntary) { + return ThreadImpl::get_context_switches(total, involuntary); +} + /** * Returns true if the thread has been started, false if it has not, or if * join() has already been called. diff --git a/panda/src/pipeline/thread.cxx b/panda/src/pipeline/thread.cxx index 81152a8095..5f2cafda3a 100644 --- a/panda/src/pipeline/thread.cxx +++ b/panda/src/pipeline/thread.cxx @@ -22,6 +22,9 @@ Thread *Thread::_main_thread; Thread *Thread::_external_thread; TypeHandle Thread::_type_handle; +void (*Thread::_sleep_func)(double) = &ThreadImpl::sleep; +void (*Thread::_yield_func)() = &ThreadImpl::yield; + /** * Creates a new Thread object, but does not immediately start executing it. * This gives the caller a chance to store it in a PT(Thread) object, if diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 64f7622d99..55ea0cbb09 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -81,6 +81,8 @@ PUBLISHED: BLOCKING INLINE static void force_yield(); BLOCKING INLINE static void consider_yield(); + INLINE static bool get_context_switches(size_t &total, size_t &involuntary); + virtual void output(std::ostream &out) const; void output_blocker(std::ostream &out) const; static void write_status(std::ostream &out); @@ -162,6 +164,10 @@ private: static Thread *_main_thread; static Thread *_external_thread; + static void (*_sleep_func)(double); + static void (*_yield_func)(); + friend class PStatClientImpl; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/pipeline/threadDummyImpl.I b/panda/src/pipeline/threadDummyImpl.I index 7bab1ae408..64a02bd5a0 100644 --- a/panda/src/pipeline/threadDummyImpl.I +++ b/panda/src/pipeline/threadDummyImpl.I @@ -124,3 +124,11 @@ yield() { INLINE void ThreadDummyImpl:: consider_yield() { } + +/** + * + */ +INLINE bool ThreadDummyImpl:: +get_context_switches(size_t &, size_t &) { + return false; +} diff --git a/panda/src/pipeline/threadDummyImpl.h b/panda/src/pipeline/threadDummyImpl.h index c7cea7a3ca..ce3dea76bb 100644 --- a/panda/src/pipeline/threadDummyImpl.h +++ b/panda/src/pipeline/threadDummyImpl.h @@ -57,6 +57,8 @@ public: INLINE static void sleep(double seconds); INLINE static void yield(); INLINE static void consider_yield(); + + INLINE static bool get_context_switches(size_t &, size_t &); }; #include "threadDummyImpl.I" diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index d46660194f..9108825422 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -21,6 +21,11 @@ #include "config_pipeline.h" #include +// Used for getrusage(). +#include +#include +#include + #ifdef ANDROID #include "config_express.h" #include @@ -248,6 +253,26 @@ bind_java_thread() { } #endif // ANDROID +/** + * Returns the number of context switches that occurred on the current thread. + * The first number is the total number of context switches reported by the OS, + * and the second number is the number of involuntary context switches (ie. the + * thread was scheduled out by the OS), if known, otherwise zero. + * Returns true if context switch information was available, false otherwise. + */ +bool ThreadPosixImpl:: +get_context_switches(size_t &total, size_t &involuntary) { +#ifdef RUSAGE_THREAD + struct rusage usage; + if (getrusage(RUSAGE_THREAD, &usage) == 0) { + total = (size_t)usage.ru_nvcsw; + involuntary = (size_t)usage.ru_nivcsw; + return true; + } +#endif + return false; +} + /** * The entry point of each thread. */ diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index aa9a4e02ad..65d7f951ca 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -63,6 +63,8 @@ public: static void bind_java_thread(); #endif + static bool get_context_switches(size_t &total, size_t &involuntary); + private: static void *root_func(void *data); static Thread *init_current_thread(); diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx index 14d6f7561f..aeefd34d7e 100644 --- a/panda/src/pipeline/threadSimpleImpl.cxx +++ b/panda/src/pipeline/threadSimpleImpl.cxx @@ -55,6 +55,9 @@ ThreadSimpleImpl(Thread *parent_obj) : #ifdef WIN32 _win32_system_thread_id = 0; #endif + + _context_switches = 0; + _involuntary_context_switches = 0; } /** @@ -229,6 +232,32 @@ yield_this(bool volunteer) { } _manager->enqueue_ready(this, true); _manager->next_context(); + if (!volunteer) { + // Technically all context switches are voluntary here, but to make this + // distinction meaningful, we count it as involuntary on consider_yield(). + ++_involuntary_context_switches; + } +} + +/** + * Returns the number of context switches that occurred on the current thread. + * The first number is the total number of context switches reported by the OS, + * and the second number is the number of involuntary context switches (ie. the + * thread was scheduled out by the OS), if known, otherwise zero. + * Returns true if context switch information was available, false otherwise. + */ +bool ThreadSimpleImpl:: +get_context_switches(size_t &total, size_t &involuntary) { + ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr(); + if (manager->is_same_system_thread()) { + ThreadSimpleImpl *thread = manager->get_current_thread(); + if (thread != nullptr) { + total = thread->_context_switches; + involuntary = thread->_involuntary_context_switches; + return true; + } + } + return false; } /** diff --git a/panda/src/pipeline/threadSimpleImpl.h b/panda/src/pipeline/threadSimpleImpl.h index 5bd6af3fdc..5e747ea917 100644 --- a/panda/src/pipeline/threadSimpleImpl.h +++ b/panda/src/pipeline/threadSimpleImpl.h @@ -77,6 +77,8 @@ public: INLINE static void write_status(std::ostream &out); + static bool get_context_switches(size_t &total, size_t &involuntary); + private: static void st_begin_thread(void *data); void begin_thread(); @@ -142,6 +144,9 @@ private: DWORD _win32_system_thread_id; #endif + size_t _context_switches; + size_t _involuntary_context_switches; + friend class ThreadSimpleManager; }; diff --git a/panda/src/pipeline/threadSimpleManager.cxx b/panda/src/pipeline/threadSimpleManager.cxx index 861e19cd77..d28a4372f6 100644 --- a/panda/src/pipeline/threadSimpleManager.cxx +++ b/panda/src/pipeline/threadSimpleManager.cxx @@ -233,6 +233,8 @@ next_context() { unref_delete(finished_thread->_parent_obj); } + ++_current_thread->_context_switches; + // Mark the current thread's resume point. #ifdef HAVE_PYTHON diff --git a/panda/src/pipeline/threadWin32Impl.I b/panda/src/pipeline/threadWin32Impl.I index 1ed6ed50ae..14f912f917 100644 --- a/panda/src/pipeline/threadWin32Impl.I +++ b/panda/src/pipeline/threadWin32Impl.I @@ -17,7 +17,8 @@ INLINE ThreadWin32Impl:: ThreadWin32Impl(Thread *parent_obj) : _cv(_mutex), - _parent_obj(parent_obj) + _parent_obj(parent_obj), + _profiling(0) { _thread = 0; _joinable = false; diff --git a/panda/src/pipeline/threadWin32Impl.cxx b/panda/src/pipeline/threadWin32Impl.cxx index c151589a47..552c8b0453 100644 --- a/panda/src/pipeline/threadWin32Impl.cxx +++ b/panda/src/pipeline/threadWin32Impl.cxx @@ -20,9 +20,34 @@ #include "pointerTo.h" #include "config_pipeline.h" +#include + static thread_local Thread *_current_thread = nullptr; static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT; +#if _WIN32_WINNT < 0x0601 +// Requires Windows 7. +static DWORD (*EnableThreadProfiling)(HANDLE, DWORD, DWORD64, HANDLE *) = nullptr; +static DWORD (*DisableThreadProfiling)(HANDLE) = nullptr; +static DWORD (*ReadThreadProfilingData)(HANDLE, DWORD, PPERFORMANCE_DATA data) = nullptr; + +static bool init_thread_profiling() { + static bool inited = false; + if (!inited) { + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + EnableThreadProfiling = (decltype(EnableThreadProfiling))GetProcAddress(kernel32, "EnableThreadProfiling"); + DisableThreadProfiling = (decltype(DisableThreadProfiling))GetProcAddress(kernel32, "DisableThreadProfiling"); + ReadThreadProfilingData = (decltype(ReadThreadProfilingData))GetProcAddress(kernel32, "ReadThreadProfilingData"); + inited = true; + } + return (EnableThreadProfiling && DisableThreadProfiling && ReadThreadProfilingData); +} +#else +static bool init_thread_profiling() { + return true; +} +#endif + /** * Called by get_current_thread() if the current thread pointer is null; checks * whether it might be the main thread. @@ -170,6 +195,35 @@ bind_thread(Thread *thread) { _current_thread = thread; } +/** + * Returns the number of context switches that occurred on the current thread. + * The first number is the total number of context switches reported by the OS, + * and the second number is the number of involuntary context switches (ie. the + * thread was scheduled out by the OS), if known, otherwise zero. + * Returns true if context switch information was available, false otherwise. + */ +bool ThreadWin32Impl:: +get_context_switches(size_t &total, size_t &involuntary) { + Thread *thread = get_current_thread(); + ThreadWin32Impl *self = &thread->_impl; + + if (!self->_profiling && init_thread_profiling()) { + DWORD result = EnableThreadProfiling(GetCurrentThread(), THREAD_PROFILING_FLAG_DISPATCH, 0, &self->_profiling); + if (result != ERROR_SUCCESS) { + self->_profiling = 0; + return false; + } + } + + PERFORMANCE_DATA data = {sizeof(PERFORMANCE_DATA), PERFORMANCE_DATA_VERSION}; + if (ReadThreadProfilingData(self->_profiling, READ_THREAD_PROFILING_FLAG_DISPATCHING, &data) == ERROR_SUCCESS) { + total = data.ContextSwitchCount; + involuntary = 0; + return true; + } + return false; +} + /** * The entry point of each thread. */ @@ -212,6 +266,11 @@ root_func(LPVOID data) { self->_mutex.unlock(); } + if (self->_profiling != 0) { + DisableThreadProfiling(self->_profiling); + self->_profiling = 0; + } + // Now drop the parent object reference that we grabbed in start(). This // might delete the parent object, and in turn, delete the ThreadWin32Impl // object. diff --git a/panda/src/pipeline/threadWin32Impl.h b/panda/src/pipeline/threadWin32Impl.h index 64f64f31b6..3c9cfd275e 100644 --- a/panda/src/pipeline/threadWin32Impl.h +++ b/panda/src/pipeline/threadWin32Impl.h @@ -52,6 +52,8 @@ public: INLINE static void yield(); INLINE static void consider_yield(); + static bool get_context_switches(size_t &total, size_t &involuntary); + private: static DWORD WINAPI root_func(LPVOID data); @@ -69,6 +71,7 @@ private: DWORD _thread_id; bool _joinable; Status _status; + HANDLE _profiling; }; #include "threadWin32Impl.I" diff --git a/panda/src/pstatclient/config_pstatclient.cxx b/panda/src/pstatclient/config_pstatclient.cxx index 1335254590..5b97075bcc 100644 --- a/panda/src/pstatclient/config_pstatclient.cxx +++ b/panda/src/pstatclient/config_pstatclient.cxx @@ -82,6 +82,11 @@ ConfigVariableBool pstats_gpu_timing "is not usually an accurate reflectino of how long the actual " "operation takes on the video card.")); +ConfigVariableBool pstats_thread_profiling +("pstats-thread-profiling", false, + PRC_DESC("Set this true to query the system for thread statistics, such as " + "the number of context switches and time spent waiting.")); + // The rest are different in that they directly control the server, not the // client. ConfigVariableBool pstats_scroll_mode diff --git a/panda/src/pstatclient/config_pstatclient.h b/panda/src/pstatclient/config_pstatclient.h index 6de3c5c120..856031e1f2 100644 --- a/panda/src/pstatclient/config_pstatclient.h +++ b/panda/src/pstatclient/config_pstatclient.h @@ -38,6 +38,7 @@ 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_thread_profiling; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_scroll_mode; extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_history; diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 593ebf8f20..e08dff1605 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -905,6 +905,38 @@ stop(int collector_index, int thread_index, double as_of) { } } +/** + * Adds a pair of start/stop times in the given collector. Used in low-level + * code that knows there will not be any other collectors started and stopped + * in the meantime, and can be more efficient than a pair of start/stop times. + */ +void PStatClient:: +start_stop(int collector_index, int thread_index, double start, double stop) { + if (!client_is_connected()) { + return; + } + +#ifdef _DEBUG + nassertv(collector_index >= 0 && collector_index < get_num_collectors()); + nassertv(thread_index >= 0 && thread_index < get_num_threads()); +#endif + + Collector *collector = get_collector_ptr(collector_index); + InternalThread *thread = get_thread_ptr(thread_index); + + if (collector->is_active() && thread->_is_active) { + LightMutexHolder holder(thread->_thread_lock); + if (collector->_per_thread[thread_index]._nested_count == 0) { + // This collector wasn't already started in this thread; record a new + // data point. + if (thread->_thread_active) { + thread->_frame_data.add_start(collector_index, start); + thread->_frame_data.add_stop(collector_index, stop); + } + } + } +} + /** * Removes the level value from the indicated collector. The collector will * no longer be reported as having any particular level value. diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h index 9b17f208f3..d164cb29b1 100644 --- a/panda/src/pstatclient/pStatClient.h +++ b/panda/src/pstatclient/pStatClient.h @@ -129,6 +129,7 @@ private: void start(int collector_index, int thread_index, double as_of); void stop(int collector_index, int thread_index); void stop(int collector_index, int thread_index, double as_of); + void start_stop(int collector_index, int thread_index, double start, double stop); void clear_level(int collector_index, int thread_index); void set_level(int collector_index, int thread_index, double level); @@ -216,6 +217,8 @@ private: bool _is_active; int _frame_number; double _next_packet; + size_t _context_switches = 0; + size_t _involuntary_context_switches = 0; bool _thread_active; @@ -321,6 +324,7 @@ private: void start(int collector_index, int thread_index, double as_of); void stop(int collector_index, int thread_index); void stop(int collector_index, int thread_index, double as_of); + void start_stop(int collector_index, int thread_index, double start, double stop); void clear_level(int collector_index, int thread_index); void set_level(int collector_index, int thread_index, double level); diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx index 619c8fd33e..69415569fb 100644 --- a/panda/src/pstatclient/pStatClientImpl.cxx +++ b/panda/src/pstatclient/pStatClientImpl.cxx @@ -24,6 +24,8 @@ #include "config_pstatclient.h" #include "pStatProperties.h" #include "cmath.h" +#include "conditionVarWin32Impl.h" +#include "conditionVarPosixImpl.h" #include @@ -32,6 +34,15 @@ #include #endif +static PStatCollector _cswitch_pcollector("Context Switches"); +static PStatCollector _cswitch_sleep_pcollector("Context Switches:Sleep"); +static PStatCollector _cswitch_yield_pcollector("Context Switches:Yields"); +static PStatCollector _cswitch_cvar_pcollector("Context Switches:Condition Variable"); +static PStatCollector _cswitch_involuntary_pcollector("Context Switches:Involuntary"); +static PStatCollector _wait_sleep_pcollector("Wait:Sleep"); +static PStatCollector _wait_yield_pcollector("Wait:Yield"); +static PStatCollector _wait_cvar_pcollector("Wait:Condition Variable"); + /** * */ @@ -124,6 +135,106 @@ client_connect(std::string hostname, int port) { MutexDebug::increment_pstats(); #endif // DEBUG_THREADS + if (pstats_thread_profiling) { + // Replace the condition variable wait function with one that does PStats + // statistics. + _thread_profiling = true; + + Thread::_sleep_func = [] (double seconds) { + Thread *current_thread = Thread::get_current_thread(); + int thread_index = current_thread->get_pstats_index(); + if (thread_index >= 0) { + PStatClient *client = PStatClient::get_global_pstats(); + double start = client->get_real_time(); + ThreadImpl::sleep(seconds); + double stop = client->get_real_time(); + client->start_stop(_wait_sleep_pcollector.get_index(), thread_index, start, stop); + client->add_level(_cswitch_sleep_pcollector.get_index(), thread_index, 1); + } + else { + ThreadImpl::sleep(seconds); + } + }; + + Thread::_yield_func = [] () { + Thread *current_thread = Thread::get_current_thread(); + int thread_index = current_thread->get_pstats_index(); + if (thread_index >= 0) { + PStatClient *client = PStatClient::get_global_pstats(); + double start = client->get_real_time(); + ThreadImpl::yield(); + double stop = client->get_real_time(); + client->start_stop(_wait_yield_pcollector.get_index(), thread_index, start, stop); + client->add_level(_cswitch_yield_pcollector.get_index(), thread_index, 1); + } + else { + ThreadImpl::yield(); + } + }; + +#ifdef _WIN32 + ConditionVarWin32Impl::_wait_func = + [] (PCONDITION_VARIABLE cvar, PSRWLOCK lock, DWORD time, ULONG flags) { + Thread *current_thread = Thread::get_current_thread(); + int thread_index = current_thread->get_pstats_index(); + BOOL result; + if (thread_index >= 0) { + PStatClient *client = PStatClient::get_global_pstats(); + double start = client->get_real_time(); + result = SleepConditionVariableSRW(cvar, lock, time, flags); + double stop = client->get_real_time(); + client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop); + client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1); + } + else { + result = SleepConditionVariableSRW(cvar, lock, time, flags); + } + return result; + }; +#endif + +#ifdef HAVE_POSIX_THREADS + ConditionVarPosixImpl::_wait_func = + [] (pthread_cond_t *cvar, pthread_mutex_t *lock) { + Thread *current_thread = Thread::get_current_thread(); + int thread_index = current_thread->get_pstats_index(); + int result; + if (thread_index >= 0) { + PStatClient *client = PStatClient::get_global_pstats(); + double start = client->get_real_time(); + result = pthread_cond_wait(cvar, lock); + double stop = client->get_real_time(); + client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop); + client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1); + } + else { + result = pthread_cond_wait(cvar, lock); + } + return result; + }; + + ConditionVarPosixImpl::_timedwait_func = + [] (pthread_cond_t *cvar, pthread_mutex_t *lock, + const struct timespec *ts) { + Thread *current_thread = Thread::get_current_thread(); + int thread_index = current_thread->get_pstats_index(); + int result; + if (thread_index >= 0) { + PStatClient *client = PStatClient::get_global_pstats(); + double start = client->get_real_time(); + result = pthread_cond_timedwait(cvar, lock, ts); + double stop = client->get_real_time(); + client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop); + client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1); + } + else { + result = pthread_cond_timedwait(cvar, lock, ts); + } + return result; + }; +#endif + } + // Wait for the server hello. while (!_got_udp_port) { transmit_control_data(); @@ -137,6 +248,23 @@ client_connect(std::string hostname, int port) { */ void PStatClientImpl:: client_disconnect() { + if (_thread_profiling) { + // Switch the functions back to what they were. + Thread::_sleep_func = &ThreadImpl::sleep; + Thread::_yield_func = &ThreadImpl::yield; + +#ifdef _WIN32 + ConditionVarWin32Impl::_wait_func = &SleepConditionVariableSRW; +#endif + +#ifdef HAVE_POSIX_THREADS + ConditionVarPosixImpl::_wait_func = &pthread_cond_wait; + ConditionVarPosixImpl::_timedwait_func = &pthread_cond_timedwait; +#endif + + _thread_profiling = false; + } + if (_is_connected) { #ifdef DEBUG_THREADS MutexDebug::decrement_pstats(); @@ -207,6 +335,37 @@ new_frame(int thread_index, int frame_number) { if (frame_number == -1) { frame_number = pthread->_frame_number; } + + // Record the number of context switches on this thread. + if (_thread_profiling) { + size_t total, involuntary; + PT(Thread) thread = pthread->_thread.lock(); + if (thread != nullptr && thread->get_context_switches(total, involuntary)) { + size_t total_this_frame = total - pthread->_context_switches; + pthread->_context_switches = total; + frame_data.add_level(_cswitch_pcollector.get_index(), total_this_frame); + + if (involuntary != 0) { + size_t involuntary_this_frame = involuntary - pthread->_involuntary_context_switches; + pthread->_involuntary_context_switches = involuntary; + frame_data.add_level(_cswitch_involuntary_pcollector.get_index(), involuntary_this_frame); + } + } + _client->clear_level(_cswitch_sleep_pcollector.get_index(), thread_index); + _client->clear_level(_cswitch_yield_pcollector.get_index(), thread_index); + _client->clear_level(_cswitch_cvar_pcollector.get_index(), thread_index); + } + } + else if (_thread_profiling) { + // Record the initial number of context switches. + PT(Thread) thread = pthread->_thread.lock(); + if (thread != nullptr) { + thread->get_context_switches(pthread->_context_switches, + pthread->_involuntary_context_switches); + } + _client->clear_level(_cswitch_sleep_pcollector.get_index(), thread_index); + _client->clear_level(_cswitch_yield_pcollector.get_index(), thread_index); + _client->clear_level(_cswitch_cvar_pcollector.get_index(), thread_index); } pthread->_frame_data.clear(); diff --git a/panda/src/pstatclient/pStatClientImpl.h b/panda/src/pstatclient/pStatClientImpl.h index 2b767838bc..f758d4b262 100644 --- a/panda/src/pstatclient/pStatClientImpl.h +++ b/panda/src/pstatclient/pStatClientImpl.h @@ -111,6 +111,8 @@ private: double _udp_count_factor; unsigned int _tcp_count; unsigned int _udp_count; + + bool _thread_profiling = false; }; #include "pStatClientImpl.I" From 5196719f298a73ca9e57dde96da2ecbf2511574d Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 21:45:44 +0100 Subject: [PATCH 053/166] device: Fix XInput compile error compiling for newer Windows versions --- panda/src/device/xInputDevice.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx index bcc44cd3b1..70b7d5bf3f 100644 --- a/panda/src/device/xInputDevice.cxx +++ b/panda/src/device/xInputDevice.cxx @@ -70,7 +70,7 @@ // With MingW32 this raises the error: // Redefinition of '_XINPUT_BATTERY_INFORMATION' -#ifdef _MSC_VER +#if defined(_MSC_VER) && _WIN32_WINNT < 0x0602 typedef struct _XINPUT_BATTERY_INFORMATION { BYTE BatteryType; BYTE BatteryLevel; From c3ce8164bca1a677c206ab7432dff80b789534b4 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 18:40:04 +0100 Subject: [PATCH 054/166] dtoolbase: Add atomic wait and notify operations from C++20 Adds patomic_signed_lock_free, patomic_unsigned_lock_free, and patomic_flag with wait/notify methods modelled after C++20. Implemented using futexes, falling back to a mutex+condition variable hash table if not supported. (Currently the hash table has a fixed size of 64, which we could increase if necessary, but we really shouldn't even have a fraction of that number of simultaneously sleeping threads...) Other atomic types are unaffected at the moment, in part because futexes are really restricted to 32-bit ints on Linux anyway --- dtool/src/dtoolbase/CMakeLists.txt | 2 + .../src/dtoolbase/p3dtoolbase_composite2.cxx | 1 + dtool/src/dtoolbase/patomic.I | 219 +++++++++++++++++- dtool/src/dtoolbase/patomic.cxx | 168 ++++++++++++++ dtool/src/dtoolbase/patomic.h | 103 ++++++-- 5 files changed, 466 insertions(+), 27 deletions(-) create mode 100644 dtool/src/dtoolbase/patomic.cxx diff --git a/dtool/src/dtoolbase/CMakeLists.txt b/dtool/src/dtoolbase/CMakeLists.txt index ee00d3b050..f69125f6b4 100644 --- a/dtool/src/dtoolbase/CMakeLists.txt +++ b/dtool/src/dtoolbase/CMakeLists.txt @@ -46,6 +46,7 @@ set(P3DTOOLBASE_HEADERS typeRegistryNode.I typeRegistryNode.h typedObject.I typedObject.h pallocator.T pallocator.h + patomic.h patomic.I pdeque.h plist.h pmap.h pset.h pvector.h epvector.h lookup3.h @@ -70,6 +71,7 @@ set(P3DTOOLBASE_SOURCES mutexWin32Impl.cxx mutexSpinlockImpl.cxx neverFreeMemory.cxx + patomic.cxx pdtoa.cxx pstrtod.cxx register_type.cxx diff --git a/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx b/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx index 8cd964b4fd..1840bc7e25 100644 --- a/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx +++ b/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx @@ -1,3 +1,4 @@ +#include "patomic.cxx" #include "mutexPosixImpl.cxx" #include "mutexWin32Impl.cxx" #include "mutexSpinlockImpl.cxx" diff --git a/dtool/src/dtoolbase/patomic.I b/dtool/src/dtoolbase/patomic.I index dbb3934da0..cb6e1880b6 100644 --- a/dtool/src/dtoolbase/patomic.I +++ b/dtool/src/dtoolbase/patomic.I @@ -11,6 +11,7 @@ * @date 2022-01-28 */ +#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) /** * Value initializer. */ @@ -247,15 +248,99 @@ operator ^=(T arg) noexcept { return _value ^= arg; } +#endif // defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) /** - * Sets the flag to true and returns the previous value. + * Initializes the variable to zero (according to C++20 semantics, NOT C++11 + * semantics!) */ -ALWAYS_INLINE bool patomic_flag:: -test_and_set(std::memory_order order) noexcept { - bool value = __internal_flag; - __internal_flag = true; - return value; +constexpr patomic_unsigned_lock_free:: +patomic_unsigned_lock_free() noexcept : + patomic(0u) { +} + +/** + * Initializes the variable to the given value. + */ +constexpr patomic_unsigned_lock_free:: +patomic_unsigned_lock_free(uint32_t desired) noexcept : + patomic(desired) { +} + +/** + * Waits until the value is no longer equal to the given value. + */ +ALWAYS_INLINE void patomic_unsigned_lock_free:: +wait(uint32_t old, std::memory_order order) const noexcept { + if (load(order) == old) { + patomic_wait((const volatile uint32_t *)this, old); + } +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_unsigned_lock_free:: +notify_one() noexcept { + patomic_notify_one((volatile uint32_t *)this); +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_unsigned_lock_free:: +notify_all() noexcept { + patomic_notify_all((volatile uint32_t *)this); +} + +/** + * Initializes the variable to zero (according to C++20 semantics, NOT C++11 + * semantics!) + */ +constexpr patomic_signed_lock_free:: +patomic_signed_lock_free() noexcept : + patomic(0) { +} + +/** + * Initializes the variable to the given value. + */ +constexpr patomic_signed_lock_free:: +patomic_signed_lock_free(int32_t desired) noexcept : + patomic(desired) { +} + +/** + * Waits until the value is no longer equal to the given value. + */ +ALWAYS_INLINE void patomic_signed_lock_free:: +wait(int32_t old, std::memory_order order) const noexcept { + if (load(order) == old) { + patomic_wait((const volatile int32_t *)this, old); + } +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_signed_lock_free:: +notify_one() noexcept { + patomic_notify_one((volatile int32_t *)this); +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_signed_lock_free:: +notify_all() noexcept { + patomic_notify_all((volatile int32_t *)this); +} + +/** + * Allows assignment from ATOMIC_FLAG_INIT. + */ +constexpr patomic_flag:: +patomic_flag(bool desired) noexcept : _value(desired) { } /** @@ -263,5 +348,125 @@ test_and_set(std::memory_order order) noexcept { */ ALWAYS_INLINE void patomic_flag:: clear(std::memory_order order) noexcept { - __internal_flag = false; + _value.store(0u, order); +} + +/** + * Sets the flag to true and returns the previous value. + */ +ALWAYS_INLINE bool patomic_flag:: +test_and_set(std::memory_order order) noexcept { + return (bool)_value.exchange(1u, order); +} + +/** + * Returns the current value of the flag. + */ +ALWAYS_INLINE bool patomic_flag:: +test(std::memory_order order) const noexcept { + return (bool)_value.load(order); +} + +/** + * Waits until the value is no longer equal to the given value. + */ +ALWAYS_INLINE void patomic_flag:: +wait(bool old, std::memory_order order) const noexcept { + _value.wait(old, order); +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_flag:: +notify_one() noexcept { + _value.notify_one(); +} + +/** + * Wakes up at least one thread waiting for the value to change. + */ +ALWAYS_INLINE void patomic_flag:: +notify_all() noexcept { + _value.notify_all(); +} + +/** + * + */ +ALWAYS_INLINE void +patomic_wait(const volatile int32_t *value, int32_t old) { + patomic_wait((const volatile uint32_t *)value, (uint32_t)old); +} + +/** + * + */ +ALWAYS_INLINE void +patomic_notify_one(volatile int32_t *value) { + patomic_notify_one((volatile uint32_t *)value); +} + +/** + * + */ +ALWAYS_INLINE void +patomic_notify_all(volatile int32_t *value) { + patomic_notify_all((volatile uint32_t *)value); +} + +/** + * + */ +ALWAYS_INLINE void +patomic_wait(const volatile uint32_t *value, uint32_t old) { +#ifdef __linux__ + while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) { + syscall(SYS_futex, old, FUTEX_WAIT_PRIVATE, old, 0, 0, 0); + } +//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8 +// while (*value == old) { +// WaitOnAddress((volatile void *)value, &old, sizeof(uint32_t), INFINITE); +// } +#elif defined(_WIN32) + while (*value == old) { + _patomic_wait_func((volatile void *)value, &old, sizeof(uint32_t), INFINITE); + } +#elif defined(HAVE_POSIX_THREADS) + _patomic_wait(value, old); +#else + while (*value == old); +#endif +} + +/** + * + */ +ALWAYS_INLINE void +patomic_notify_one(volatile uint32_t *value) { +#ifdef __linux__ + syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0); +//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8 +// WakeByAddressSingle((void *)value); +#elif defined(_WIN32) + _patomic_wake_one_func((void *)value); +#elif defined(HAVE_POSIX_THREADS) + _patomic_notify_all(value); +#endif +} + +/** + * + */ +ALWAYS_INLINE void +patomic_notify_all(volatile uint32_t *value) { +#ifdef __linux__ + syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, INT_MAX, 0, 0, 0); +//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8 +// WakeByAddressAll((void *)value); +#elif defined(_WIN32) + _patomic_wake_all_func((void *)value); +#elif defined(HAVE_POSIX_THREADS) + _patomic_notify_all(value); +#endif } diff --git a/dtool/src/dtoolbase/patomic.cxx b/dtool/src/dtoolbase/patomic.cxx new file mode 100644 index 0000000000..5258021efd --- /dev/null +++ b/dtool/src/dtoolbase/patomic.cxx @@ -0,0 +1,168 @@ +/** + * 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 patomic.cxx + * @author rdb + * @date 2022-02-23 + */ + +#include "patomic.h" + +#include + +static_assert(sizeof(patomic_unsigned_lock_free) == sizeof(uint32_t), + "expected atomic uint32_t to have same size as uint32_t"); +static_assert(sizeof(patomic_signed_lock_free) == sizeof(int32_t), + "expected atomic int32_t to have same size as int32_t"); +static_assert(sizeof(uint32_t) == sizeof(int32_t), + "expected int32_t to have same size as uint32_t"); + +#if !defined(CPPPARSER) && defined(_WIN32) + +// On Windows 7, we try to load the Windows 8 functions dynamically, and +// fall back to a condition variable table if they aren't available. +static BOOL initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout); +static void dummy_wake(PVOID addr) {} + +BOOL (*_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD) = &initialize_wait; +void (*_patomic_wake_one_func)(PVOID) = &dummy_wake; +void (*_patomic_wake_all_func)(PVOID) = &dummy_wake; + +// Randomly pick an entry into the wait table based on the hash of the address. +// It's possible to get hash collision, but that's not so bad, it just means +// that the other thread will get a spurious wakeup. +struct alignas(64) WaitTableEntry { + SRWLOCK _lock = SRWLOCK_INIT; + CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT; + DWORD _waiters = 0; +}; +static WaitTableEntry _wait_table[64] = {}; +static const size_t _wait_hash_mask = 63; + +/** + * Emulates WakeByAddressSingle for Windows Vista and 7. + */ +static void +emulated_wake(PVOID addr) { + size_t i = std::hash{}(addr) & (sizeof(_wait_table) / sizeof(WaitTableEntry) - 1); + WaitTableEntry &entry = _wait_table[i]; + AcquireSRWLockExclusive(&entry._lock); + DWORD num_waiters = entry._waiters; + ReleaseSRWLockExclusive(&entry._lock); + if (num_waiters > 0) { + // We have to wake up all the threads, even if only one of them is for this + // address. Some of them will get a spurious wakeup, but that's OK. + WakeAllConditionVariable(&entry._cvar); + } +} + +/** + * Emulates WaitOnAddress for Windows Vista and 7. Only supports aligned + * 32-bit values. + */ +static BOOL +emulated_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) { + assert(size == sizeof(LONG)); + + LONG cmpval = *(LONG *)cmp; + if (*(LONG *)addr != cmpval) { + return TRUE; + } + + size_t i = std::hash{}(addr) & _wait_hash_mask; + WaitTableEntry &entry = _wait_table[i]; + AcquireSRWLockExclusive(&entry._lock); + ++entry._waiters; + while (*(LONG *)addr == cmpval) { + if (SleepConditionVariableSRW(&entry._cvar, &entry._lock, timeout, 0) != 0) { + // Timeout. + --entry._waiters; + ReleaseSRWLockExclusive(&entry._lock); + return FALSE; + } + } + --entry._waiters; + ReleaseSRWLockExclusive(&entry._lock); + return TRUE; +} + +/** + * Initially assigned to the wait function slot to initialize the function + * pointers. + */ +static BOOL +initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) { + // There's a chance of a race here, with two threads trying to initialize the + // functions at the same time. That's OK, because they should all produce + // the same results, and the stores to the function pointers are atomic. + HMODULE lib = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll"); + if (lib) { + auto wait_func = (decltype(_patomic_wait_func))GetProcAddress(lib, "WaitOnAddress"); + auto wake_one_func = (decltype(_patomic_wake_one_func))GetProcAddress(lib, "WakeByAddressSingle"); + auto wake_all_func = (decltype(_patomic_wake_all_func))GetProcAddress(lib, "WakeByAddressAll"); + if (wait_func && wake_one_func && wake_all_func) { + // Make sure that the wake function is guaranteed to be visible to other + // threads by the time we assign the wait function. + _patomic_wake_one_func = wake_one_func; + _patomic_wake_all_func = wake_all_func; + patomic_thread_fence(std::memory_order_release); + _patomic_wait_func = wait_func; + return wait_func(addr, cmp, size, timeout); + } + } + + // We don't have Windows 8's functions, use the emulated wait and wake funcs. + _patomic_wake_one_func = &emulated_wake; + _patomic_wake_all_func = &emulated_wake; + patomic_thread_fence(std::memory_order_release); + _patomic_wait_func = &emulated_wait; + + return emulated_wait(addr, cmp, size, timeout); +} + +#elif !defined(CPPPARSER) && !defined(__linux__) && defined(HAVE_POSIX_THREADS) + +// Same as above, but using pthreads. +struct alignas(64) WaitTableEntry { + pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t _cvar = PTHREAD_COND_INITIALIZER; + unsigned int _waiters = 0; +}; +static WaitTableEntry _wait_table[64]; +static const size_t _wait_hash_mask = 63; + +/** + * + */ +void +_patomic_wait(const volatile uint32_t *value, uint32_t old) { + WaitTableEntry &entry = _wait_table[std::hash{}(value) & _wait_hash_mask]; + pthread_mutex_lock(&entry._lock); + ++entry._waiters; + while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) { + pthread_cond_wait(&entry._cvar, &entry._lock); + } + --entry._waiters; + pthread_mutex_unlock(&entry._lock); +} + +/** + * + */ +void +_patomic_notify_all(volatile uint32_t *value) { + WaitTableEntry &entry = _wait_table[std::hash{}(value) & _wait_hash_mask]; + pthread_mutex_lock(&entry._lock); + unsigned int num_waiters = entry._waiters; + pthread_mutex_unlock(&entry._lock); + if (num_waiters > 0) { + pthread_cond_broadcast(&entry._cvar); + } +} + +#endif diff --git a/dtool/src/dtoolbase/patomic.h b/dtool/src/dtoolbase/patomic.h index 903391cf3e..f12f1a956a 100644 --- a/dtool/src/dtoolbase/patomic.h +++ b/dtool/src/dtoolbase/patomic.h @@ -19,6 +19,19 @@ #include +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifdef __linux__ +#include +#include +#include +#endif + #if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) /** @@ -73,36 +86,86 @@ private: T _value; }; -/** - * Dummy implementation of std::atomic_flag that does not do any atomic - * operations. - */ -struct EXPCL_DTOOL_DTOOLBASE patomic_flag { - constexpr patomic_flag() noexcept = default; - - patomic_flag(const patomic_flag &) = delete; - patomic_flag &operator=(const patomic_flag &) = delete; - - ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept; - ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept; - - bool __internal_flag = false; -}; - #define patomic_thread_fence(order) (std::atomic_signal_fence((order))) -#include "patomic.I" - #else // We're using real threading, so use the real implementation. template using patomic = std::atomic; -typedef std::atomic_flag patomic_flag; - #define patomic_thread_fence(order) (std::atomic_thread_fence((order))) #endif +/** + * Implementation of atomic_unsigned_lock_free with C++20 semantics. + */ +class EXPCL_DTOOL_DTOOLBASE patomic_unsigned_lock_free : public patomic { +public: + constexpr patomic_unsigned_lock_free() noexcept; + constexpr patomic_unsigned_lock_free(uint32_t desired) noexcept; + + INLINE void wait(uint32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept; + ALWAYS_INLINE void notify_one() noexcept; + ALWAYS_INLINE void notify_all() noexcept; +}; + +/** + * Implementation of atomic_signed_lock_free with C++20 semantics. + */ +class EXPCL_DTOOL_DTOOLBASE patomic_signed_lock_free : public patomic { +public: + constexpr patomic_signed_lock_free() noexcept; + constexpr patomic_signed_lock_free(int32_t desired) noexcept; + + INLINE void wait(int32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept; + ALWAYS_INLINE void notify_one() noexcept; + ALWAYS_INLINE void notify_all() noexcept; +}; + +/** + * Implementation of atomic_flag with C++20 semantics. + */ +class EXPCL_DTOOL_DTOOLBASE patomic_flag { +public: + constexpr patomic_flag() noexcept = default; + constexpr patomic_flag(bool desired) noexcept; + + patomic_flag(const patomic_flag &) = delete; + patomic_flag &operator=(const patomic_flag &) = delete; + + ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept; + ALWAYS_INLINE bool test(std::memory_order order = std::memory_order_seq_cst) const noexcept; + + ALWAYS_INLINE void wait(bool old, std::memory_order order = std::memory_order_seq_cst) const noexcept; + ALWAYS_INLINE void notify_one() noexcept; + ALWAYS_INLINE void notify_all() noexcept; + +private: + patomic_unsigned_lock_free _value { 0u }; +}; + +#ifndef CPPPARSER +ALWAYS_INLINE void patomic_wait(const volatile int32_t *value, int32_t old); +ALWAYS_INLINE void patomic_notify_one(volatile int32_t *value); +ALWAYS_INLINE void patomic_notify_all(volatile int32_t *value); + +ALWAYS_INLINE void patomic_wait(const volatile uint32_t *value, uint32_t old); +ALWAYS_INLINE void patomic_notify_one(volatile uint32_t *value); +ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value); + +#ifdef _WIN32 +EXPCL_DTOOL_DTOOLBASE extern BOOL (*_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD); +EXPCL_DTOOL_DTOOLBASE extern void (*_patomic_wake_one_func)(PVOID); +EXPCL_DTOOL_DTOOLBASE extern void (*_patomic_wake_all_func)(PVOID); +#elif !defined(__linux__) && defined(HAVE_POSIX_THREADS) +EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old); +EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value); +#endif + +#include "patomic.I" +#endif // CPPPARSER + #endif From 70c49a6416b34c002711fb753e5ac61c4d5e3cd7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 21:41:53 +0100 Subject: [PATCH 055/166] pipeline: Add Thread::relax() for more efficient busy waiting Equivalent to cpu_relax() or the pause instruction on x86 --- panda/src/pipeline/thread.I | 14 ++++++++++++++ panda/src/pipeline/thread.h | 1 + 2 files changed, 15 insertions(+) diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index 7fce66e6d5..c262808675 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -222,6 +222,20 @@ consider_yield() { ThreadImpl::consider_yield(); } +/** + * Equivalent to the pause instruction on x86 or the yield instruction on ARM, + * to be called in spin loops. + */ +INLINE void Thread:: +relax() { +#ifdef _MSC_VER + YieldProcessor(); +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64)) + __asm__ __volatile__("pause"); +#elif defined(__arm__) || defined(__aarch64__) + __asm__ __volatile__ ("yield" ::: "memory"); +#endif +} /** * Returns thread statistics. The first number is the total number of context diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 55ea0cbb09..ec7ada4971 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -80,6 +80,7 @@ PUBLISHED: BLOCKING INLINE static void force_yield(); BLOCKING INLINE static void consider_yield(); + BLOCKING INLINE static void relax(); INLINE static bool get_context_switches(size_t &total, size_t &involuntary); From cb8563acac8c738f9213214bf35f32ee6e14ba45 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 21:25:43 +0100 Subject: [PATCH 056/166] event: Update AsyncFuture to use new atomics implementation With explicit barriers, and the non-timeout version of wait() is now significantly more efficient by using the new futexes if available --- panda/src/audio/audioLoadRequest.I | 5 +- panda/src/event/asyncFuture.I | 45 +++++--- panda/src/event/asyncFuture.cxx | 142 ++++++++++++++---------- panda/src/event/asyncFuture.h | 9 +- panda/src/gobj/animateVerticesRequest.I | 2 +- panda/src/gobj/textureReloadRequest.I | 2 +- panda/src/pgraph/modelFlattenRequest.I | 2 +- panda/src/pgraph/modelLoadRequest.I | 2 +- panda/src/pgraph/modelSaveRequest.I | 2 +- 9 files changed, 125 insertions(+), 86 deletions(-) diff --git a/panda/src/audio/audioLoadRequest.I b/panda/src/audio/audioLoadRequest.I index 019501aa77..f5d9b8f5f8 100644 --- a/panda/src/audio/audioLoadRequest.I +++ b/panda/src/audio/audioLoadRequest.I @@ -59,7 +59,7 @@ get_positional() const { */ INLINE bool AudioLoadRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } /** @@ -70,6 +70,5 @@ is_ready() const { */ INLINE AudioSound *AudioLoadRequest:: get_sound() const { - nassertr_always(done(), nullptr); - return (AudioSound *)_result; + return (AudioSound *)get_result(); } diff --git a/panda/src/event/asyncFuture.I b/panda/src/event/asyncFuture.I index c38b04d880..201e103c3e 100644 --- a/panda/src/event/asyncFuture.I +++ b/panda/src/event/asyncFuture.I @@ -27,7 +27,9 @@ AsyncFuture() : */ INLINE bool AsyncFuture:: done() const { - return (FutureState)AtomicAdjust::get(_future_state) >= FS_finished; + // Not an acquire barrier because the caller may not even care about the + // result. Instead, a fence is put in get_result(). + return _future_state.load(std::memory_order_relaxed) >= FS_finished; } /** @@ -35,7 +37,7 @@ done() const { */ INLINE bool AsyncFuture:: cancelled() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_cancelled; + return _future_state.load(std::memory_order_relaxed) == FS_cancelled; } /** @@ -65,7 +67,7 @@ INLINE TypedObject *AsyncFuture:: get_result() const { // This is thread safe, since _result may no longer be modified after the // state is changed to "done". - nassertr_always(done(), nullptr); + nassertr_always(_future_state.load(std::memory_order_acquire) >= FS_finished, nullptr); return _result; } @@ -77,10 +79,14 @@ INLINE void AsyncFuture:: get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const { // This is thread safe, since _result may no longer be modified after the // state is changed to "done". - nassertd(done()) { +#ifdef NDEBUG + patomic_thread_fence(std::memory_order_acquire); +#else + nassertd(_future_state.load(std::memory_order_acquire) >= FS_finished) { ptr = nullptr; ref_ptr = nullptr; } +#endif ptr = _result; ref_ptr = _result_ref.p(); } @@ -124,7 +130,7 @@ INLINE AsyncFuture *AsyncFuture:: gather(Futures futures) { if (futures.empty()) { AsyncFuture *fut = new AsyncFuture; - fut->_future_state = (AtomicAdjust::Integer)FS_finished; + fut->_future_state.store(FS_finished, std::memory_order_relaxed); return fut; } else if (futures.size() == 1) { return futures[0].p(); @@ -167,10 +173,18 @@ try_lock_pending() { INLINE void AsyncFuture:: unlock(FutureState new_state) { nassertv(new_state != FS_locked_pending); - FutureState orig_state = (FutureState)AtomicAdjust::set(_future_state, (AtomicAdjust::Integer)new_state); + FutureState orig_state = (FutureState)_future_state.exchange(new_state, std::memory_order_release); nassertv(orig_state == FS_locked_pending); } +/** + * Atomically returns the current state. + */ +INLINE AsyncFuture::FutureState AsyncFuture:: +get_future_state() const { + return (FutureState)_future_state.load(std::memory_order_relaxed); +} + /** * Atomically changes the future state from pending to another state. Returns * true if successful, false if the future was already done. @@ -179,19 +193,18 @@ unlock(FutureState new_state) { */ INLINE bool AsyncFuture:: set_future_state(FutureState state) { - FutureState orig_state = (FutureState) - AtomicAdjust::compare_and_exchange( - _future_state, - (AtomicAdjust::Integer)FS_pending, - (AtomicAdjust::Integer)state); + patomic_unsigned_lock_free::value_type orig_state = FS_pending; + if (_future_state.compare_exchange_strong(orig_state, state, + std::memory_order_relaxed)) { + return true; + } #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) while (orig_state == FS_locked_pending) { - Thread::force_yield(); - orig_state = (FutureState)AtomicAdjust::compare_and_exchange( - _future_state, - (AtomicAdjust::Integer)FS_pending, - (AtomicAdjust::Integer)state); + Thread::relax(); + orig_state = FS_pending; + _future_state.compare_exchange_weak(orig_state, state, + std::memory_order_relaxed); } #else nassertr(orig_state != FS_locked_pending, false); diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx index 45bcc0e392..d8b8b91d97 100644 --- a/panda/src/event/asyncFuture.cxx +++ b/panda/src/event/asyncFuture.cxx @@ -17,6 +17,7 @@ #include "config_event.h" #include "pStatTimer.h" #include "throw_event.h" +#include "trueClock.h" TypeHandle AsyncFuture::_type_handle; TypeHandle AsyncGatheringFuture::_type_handle; @@ -51,6 +52,11 @@ AsyncFuture:: * future was already done. Either way, done() will return true after this * call returns. * + * Please note that calling this is not a guarantee that the operation + * corresponding this future does not run. It could already be in the process + * of running, or perhaps not respond to a cancel signal. All this guarantees + * is that the future is marked as done when this call returns. + * * In the case of a task, this is equivalent to remove(). */ bool AsyncFuture:: @@ -71,8 +77,7 @@ cancel() { void AsyncFuture:: output(std::ostream &out) const { out << get_type(); - FutureState state = (FutureState)AtomicAdjust::get(_future_state); - switch (state) { + switch (get_future_state()) { case FS_pending: case FS_locked_pending: out << " (pending)"; @@ -94,47 +99,66 @@ output(std::ostream &out) const { */ void AsyncFuture:: wait() { - if (done()) { - return; + if (!done()) { + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting for future " << *this << "\n"; + } + + FutureState state = get_future_state(); + while (state < FS_finished) { + if (state == FS_locked_pending) { + // If it's locked, someone is probably about to finish it, so don't + // go to sleep. + do { + Thread::relax(); + state = get_future_state(); + } + while (state == FS_locked_pending); + + if (state >= FS_finished) { + return; + } + } + + // Go to sleep. + _future_state.wait(state, std::memory_order_relaxed); + state = get_future_state(); + } } - PStatTimer timer(AsyncTaskChain::_wait_pcollector); - if (task_cat.is_debug()) { - task_cat.debug() - << "Waiting for future " << *this << "\n"; - } - - // Continue to yield while the future isn't done. It may be more efficient - // to use a condition variable, but let's not add the extra complexity - // unless we're sure that we need it. - do { - Thread::force_yield(); - } while (!done()); + // Let's make wait() an acquire op, so that people can use a future for + // synchronization. + patomic_thread_fence(std::memory_order_acquire); } /** - * Waits until the future is done, or until the timeout is reached. + * Waits until the future is done, or until the timeout is reached. Note that + * this can be considerably less efficient than wait() without a timeout, so + * it's generally not a good idea to use this unless you really need to. */ void AsyncFuture:: wait(double timeout) { - if (done()) { - return; - } + if (!done()) { + PStatTimer timer(AsyncTaskChain::_wait_pcollector); + if (task_cat.is_debug()) { + task_cat.debug() + << "Waiting up to " << timeout << " seconds for future " << *this << "\n"; + } - PStatTimer timer(AsyncTaskChain::_wait_pcollector); - if (task_cat.is_debug()) { - task_cat.debug() - << "Waiting up to " << timeout << " seconds for future " << *this << "\n"; - } + // Continue to yield while the future isn't done. It may be more efficient + // to use a condition variable, but let's not add the extra complexity + // unless we're sure that we need it. + TrueClock *clock = TrueClock::get_global_ptr(); + double end = clock->get_short_time() + timeout; + do { + Thread::relax(); + } + while (!done() && clock->get_short_time() < end); - // Continue to yield while the future isn't done. It may be more efficient - // to use a condition variable, but let's not add the extra complexity - // unless we're sure that we need it. - ClockObject *clock = ClockObject::get_global_clock(); - double end = clock->get_real_time() + timeout; - do { - Thread::force_yield(); - } while (!done() && clock->get_real_time() < end); + patomic_thread_fence(std::memory_order_acquire); + } } /** @@ -144,8 +168,12 @@ wait(double timeout) { */ void AsyncFuture:: notify_done(bool clean_exit) { + patomic_thread_fence(std::memory_order_acquire); nassertv(done()); + // Let any calls to wait() know that we're done. + _future_state.notify_all(); + // This will only be called by the thread that managed to set the // _future_state away from the "pending" state, so this is thread safe. @@ -159,7 +187,7 @@ notify_done(bool clean_exit) { // It's a gathering future. Decrease the pending count on it, and if // we're the last one, call notify_done() on it. AsyncGatheringFuture *gather = (AsyncGatheringFuture *)fut; - if (!AtomicAdjust::dec(gather->_num_pending)) { + if (gather->_num_pending.fetch_sub(1, std::memory_order_relaxed) == 1) { if (gather->set_future_state(FS_finished)) { gather->notify_done(true); } @@ -223,22 +251,22 @@ void AsyncFuture:: set_result(TypedObject *ptr, ReferenceCount *ref_ptr) { // We don't strictly need to lock the future since only one thread is // allowed to call set_result(), but we might as well. - FutureState orig_state = (FutureState)AtomicAdjust:: - compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending, - (AtomicAdjust::Integer)FS_locked_pending); - + patomic_unsigned_lock_free::value_type orig_state = FS_pending; + if (!_future_state.compare_exchange_strong(orig_state, FS_locked_pending, + std::memory_order_relaxed)) { #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) - while (orig_state == FS_locked_pending) { - Thread::force_yield(); - orig_state = (FutureState)AtomicAdjust:: - compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending, - (AtomicAdjust::Integer)FS_locked_pending); - } + while (orig_state == FS_locked_pending) { + Thread::relax(); + orig_state = FS_pending; + _future_state.compare_exchange_weak(orig_state, FS_locked_pending, + std::memory_order_relaxed); + } #else - // We can't lose control between now and calling unlock() if we're using a - // cooperative threading model. - nassertv(orig_state != FS_locked_pending); + // We can't lose control between now and calling unlock() if we're using a + // cooperative threading model. + nassertv(false); #endif + } if (orig_state == FS_pending) { _result = ptr; @@ -247,15 +275,15 @@ set_result(TypedObject *ptr, ReferenceCount *ref_ptr) { // OK, now our thread owns the _waiting vector et al. notify_done(true); - - } else if (orig_state == FS_cancelled) { + } + else if (orig_state == FS_cancelled) { // This was originally illegal, but there is a chance that the future was // cancelled while another thread was setting the result. So, we drop // this, but we can issue a warning. task_cat.warning() << "Ignoring set_result() called on cancelled " << *this << "\n"; - - } else { + } + else { task_cat.error() << "set_result() was called on finished " << *this << "\n"; } @@ -371,9 +399,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) : bool any_pending = false; - AsyncFuture::Futures::const_iterator it; - for (it = _futures.begin(); it != _futures.end(); ++it) { - AsyncFuture *fut = *it; + for (AsyncFuture *fut : _futures) { // If this returns true, the future is not yet done and we need to // register ourselves with it. This creates a circular reference, but it // is resolved when the future is completed or cancelled. @@ -382,7 +408,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) : _manager = fut->_manager; } fut->_waiting.push_back((AsyncFuture *)this); - AtomicAdjust::inc(_num_pending); + _num_pending.fetch_add(1, std::memory_order_relaxed); fut->unlock(); any_pending = true; } @@ -391,7 +417,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) : // Start in the done state if all the futures we were passed are done. // Note that it is only safe to set this member in this manner if indeed // no other future holds a reference to us. - _future_state = (AtomicAdjust::Integer)FS_finished; + _future_state.store(FS_finished, std::memory_order_relaxed); } } @@ -411,7 +437,7 @@ cancel() { // Temporarily increase the pending count so that the notify_done() // callbacks won't end up causing it to be set to "finished". - AtomicAdjust::inc(_num_pending); + _num_pending.fetch_add(1, std::memory_order_relaxed); bool any_cancelled = false; for (AsyncFuture *fut : _futures) { @@ -422,7 +448,7 @@ cancel() { // If all the futures were cancelled, change state of this future to // "cancelled" and call the notify_done() callbacks. - if (!AtomicAdjust::dec(_num_pending)) { + if (_num_pending.fetch_sub(1, std::memory_order_relaxed) == 1) { if (set_future_state(FS_cancelled)) { notify_done(false); } diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index 73abe9a3a3..20c42ce4d4 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -18,7 +18,7 @@ #include "typedReferenceCount.h" #include "typedWritableReferenceCount.h" #include "eventParameter.h" -#include "atomicAdjust.h" +#include "patomic.h" class AsyncTaskManager; class AsyncTask; @@ -110,7 +110,7 @@ private: void wake_task(AsyncTask *task); protected: - enum FutureState { + enum FutureState : patomic_unsigned_lock_free::value_type { // Pending states FS_pending, FS_locked_pending, @@ -121,12 +121,13 @@ protected: }; INLINE bool try_lock_pending(); INLINE void unlock(FutureState new_state = FS_pending); + INLINE FutureState get_future_state() const; INLINE bool set_future_state(FutureState state); AsyncTaskManager *_manager; TypedObject *_result; PT(ReferenceCount) _result_ref; - AtomicAdjust::Integer _future_state; + patomic_unsigned_lock_free _future_state; std::string _done_event; @@ -176,7 +177,7 @@ public: private: const Futures _futures; - AtomicAdjust::Integer _num_pending; + patomic _num_pending; friend class AsyncFuture; diff --git a/panda/src/gobj/animateVerticesRequest.I b/panda/src/gobj/animateVerticesRequest.I index eb6ee589d0..f278f9c6d3 100644 --- a/panda/src/gobj/animateVerticesRequest.I +++ b/panda/src/gobj/animateVerticesRequest.I @@ -27,5 +27,5 @@ AnimateVerticesRequest(GeomVertexData *geom_vertex_data) : */ INLINE bool AnimateVerticesRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } diff --git a/panda/src/gobj/textureReloadRequest.I b/panda/src/gobj/textureReloadRequest.I index 1b958ac6ef..05ac192cfa 100644 --- a/panda/src/gobj/textureReloadRequest.I +++ b/panda/src/gobj/textureReloadRequest.I @@ -62,5 +62,5 @@ get_allow_compressed() const { */ INLINE bool TextureReloadRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } diff --git a/panda/src/pgraph/modelFlattenRequest.I b/panda/src/pgraph/modelFlattenRequest.I index 75f177e6a8..d63ede05e4 100644 --- a/panda/src/pgraph/modelFlattenRequest.I +++ b/panda/src/pgraph/modelFlattenRequest.I @@ -39,7 +39,7 @@ get_orig() const { */ INLINE bool ModelFlattenRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } /** diff --git a/panda/src/pgraph/modelLoadRequest.I b/panda/src/pgraph/modelLoadRequest.I index 3fb37a1bdc..0f01d01e08 100644 --- a/panda/src/pgraph/modelLoadRequest.I +++ b/panda/src/pgraph/modelLoadRequest.I @@ -46,7 +46,7 @@ get_loader() const { */ INLINE bool ModelLoadRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } /** diff --git a/panda/src/pgraph/modelSaveRequest.I b/panda/src/pgraph/modelSaveRequest.I index e84b8929af..524f09f956 100644 --- a/panda/src/pgraph/modelSaveRequest.I +++ b/panda/src/pgraph/modelSaveRequest.I @@ -54,7 +54,7 @@ get_loader() const { */ INLINE bool ModelSaveRequest:: is_ready() const { - return (FutureState)AtomicAdjust::get(_future_state) == FS_finished; + return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished; } /** From c2d088f232579e4f09e4c24d117ff76463be6f24 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 21:49:35 +0100 Subject: [PATCH 057/166] pipeline: Don't use Sleep(1) to yield on Windows Use Sleep(0) instead. Sleep(0) is not guaranteed to yield, which is a problem, but Sleep(1) can easily take up to 16 ms, which is really unacceptable except in very low-priority thread. But really, you shouldn't be relying on force_yield() for anything except with the SIMPLE_THREADS model. There is also SwitchToThread(), but in fact it is even weaker than Sleep(0). --- panda/src/pipeline/threadWin32Impl.I | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pipeline/threadWin32Impl.I b/panda/src/pipeline/threadWin32Impl.I index 14f912f917..ca236b78f0 100644 --- a/panda/src/pipeline/threadWin32Impl.I +++ b/panda/src/pipeline/threadWin32Impl.I @@ -76,7 +76,7 @@ sleep(double seconds) { */ INLINE void ThreadWin32Impl:: yield() { - Sleep(1); + Sleep(0); } /** From ba4173b32cd2fdaf4f8321faaccedf373f9fcf44 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 23 Feb 2022 22:40:47 +0100 Subject: [PATCH 058/166] pgraph: Add constexpr to CacheStats constructor --- panda/src/pgraph/cacheStats.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pgraph/cacheStats.h b/panda/src/pgraph/cacheStats.h index 2812bbed4e..b415843e34 100644 --- a/panda/src/pgraph/cacheStats.h +++ b/panda/src/pgraph/cacheStats.h @@ -25,7 +25,7 @@ */ class EXPCL_PANDA_PGRAPH CacheStats { public: - CacheStats() = default; + constexpr CacheStats() = default; void init(); void reset(double now); void write(std::ostream &out, const char *name) const; From c3562852124d24d5cedb8fca0277687bb0873531 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Feb 2022 11:31:46 +0100 Subject: [PATCH 059/166] dtoolbase: Compilation fix for broken STLs without atomic::value_type --- dtool/src/dtoolbase/patomic.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dtool/src/dtoolbase/patomic.h b/dtool/src/dtoolbase/patomic.h index f12f1a956a..f7f99dfc55 100644 --- a/dtool/src/dtoolbase/patomic.h +++ b/dtool/src/dtoolbase/patomic.h @@ -103,6 +103,8 @@ using patomic = std::atomic; */ class EXPCL_DTOOL_DTOOLBASE patomic_unsigned_lock_free : public patomic { public: + typedef uint32_t value_type; + constexpr patomic_unsigned_lock_free() noexcept; constexpr patomic_unsigned_lock_free(uint32_t desired) noexcept; @@ -116,6 +118,8 @@ public: */ class EXPCL_DTOOL_DTOOLBASE patomic_signed_lock_free : public patomic { public: + typedef int32_t value_type; + constexpr patomic_signed_lock_free() noexcept; constexpr patomic_signed_lock_free(int32_t desired) noexcept; From 6bc22d1822ab02b464057f76563de7230dd93cec Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Feb 2022 11:38:17 +0100 Subject: [PATCH 060/166] pgraph: Rewrite inefficient prev_transform tracking mechanism The previous system was causing a lot of lock contention when transforms are modified in the Cull thread. The new implementation doesn't use a linked list or lock at all, but a simple atomically incrementing integer that indicates that the prev transforms have changed. set_transform() reads this and backs up the prev transform the first time a transform is modified after reset_all_prev_transforms() is called. --- panda/src/pgraph/pandaNode.I | 53 +++++++---------- panda/src/pgraph/pandaNode.cxx | 101 ++++++++++----------------------- panda/src/pgraph/pandaNode.h | 12 ++-- 3 files changed, 57 insertions(+), 109 deletions(-) diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index bf961413c5..a79fb75a5c 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -326,7 +326,13 @@ clear_transform(Thread *current_thread) { INLINE CPT(TransformState) PandaNode:: get_prev_transform(Thread *current_thread) const { CDReader cdata(_cycler, current_thread); - return cdata->_prev_transform.p(); + if (_prev_transform_valid == _reset_prev_transform_seq) { + return cdata->_prev_transform.p(); + } else { + // If these values are different, someone called reset_prev_transform(), + // and we haven't changed our transform since then. + return cdata->_transform.p(); + } } /** @@ -334,10 +340,19 @@ get_prev_transform(Thread *current_thread) const { * indicates its _prev_transform is different from its _transform value (in * pipeline stage 0). In this case, the node will be visited by * reset_prev_transform(). + * + * @deprecated Simply check prev_transform != transform instead. */ INLINE bool PandaNode:: has_dirty_prev_transform() const { - return _dirty_prev_transform; + CDStageReader cdata(_cycler, 0); + if (_prev_transform_valid == _reset_prev_transform_seq) { + return cdata->_prev_transform != cdata->_transform; + } else { + // If these values are different, someone called reset_prev_transform(), + // and we haven't changed our transform since then. + return false; + } } /** @@ -701,34 +716,6 @@ verify_child_no_cycles(PandaNode *child_node) { return true; } -/** - * Sets the dirty_prev_transform flag, and adds the node to the - * _dirty_prev_transforms chain. Assumes _dirty_prev_transforms._lock is - * already held. - */ -INLINE void PandaNode:: -do_set_dirty_prev_transform() { - nassertv(_dirty_prev_transforms._lock.debug_is_locked()); - if (!_dirty_prev_transform) { - LinkedListNode::insert_before(&_dirty_prev_transforms); - _dirty_prev_transform = true; - } -} - -/** - * Clears the dirty_prev_transform flag, and removes the node from the - * _dirty_prev_transforms chain. Assumes _dirty_prev_transforms._lock is - * already held. - */ -INLINE void PandaNode:: -do_clear_dirty_prev_transform() { - nassertv(_dirty_prev_transforms._lock.debug_is_locked()); - if (_dirty_prev_transform) { - LinkedListNode::remove_from_list(); - _dirty_prev_transform = false; - } -} - /** * */ @@ -1513,7 +1500,11 @@ get_transform() const { */ const TransformState *PandaNodePipelineReader:: get_prev_transform() const { - return _cdata->_prev_transform; + if (_node->_prev_transform_valid == PandaNode::_reset_prev_transform_seq) { + return _cdata->_prev_transform; + } else { + return _cdata->_transform; + } } /** diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 31dee668bd..00f0d76143 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -42,10 +42,9 @@ TypeHandle PandaNode::BamReaderAuxDataDown::_type_handle; PandaNode::SceneRootFunc *PandaNode::_scene_root_func; -PandaNodeChain PandaNode::_dirty_prev_transforms("_dirty_prev_transforms"); +UpdateSeq PandaNode::_reset_prev_transform_seq; DrawMask PandaNode::_overall_bit = DrawMask::bit(31); -PStatCollector PandaNode::_reset_prev_pcollector("App:Collisions:Reset"); PStatCollector PandaNode::_update_bounds_pcollector("*:Bounds"); TypeHandle PandaNode::_type_handle; @@ -78,7 +77,7 @@ PandaNode:: PandaNode(const string &name) : Namable(name), _paths_lock("PandaNode::_paths_lock"), - _dirty_prev_transform(false) + _prev_transform_valid(_reset_prev_transform_seq) { if (pgraph_cat.is_debug()) { pgraph_cat.debug() @@ -100,12 +99,6 @@ PandaNode:: << "Destructing " << (void *)this << ", " << get_name() << "\n"; } - if (_dirty_prev_transform) { - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - do_clear_dirty_prev_transform(); - } - // We shouldn't have any parents left by the time we destruct, or there's a // refcount fault somewhere. @@ -133,7 +126,7 @@ PandaNode(const PandaNode ©) : TypedWritableReferenceCount(copy), Namable(copy), _paths_lock("PandaNode::_paths_lock"), - _dirty_prev_transform(false), + _prev_transform_valid(_reset_prev_transform_seq), _python_tag_data(copy._python_tag_data), _unexpected_change_flags(0) { @@ -145,18 +138,16 @@ PandaNode(const PandaNode ©) : MemoryUsage::update_type(this, this); #endif - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - // Copy the other node's state. { CDReader copy_cdata(copy._cycler); CDWriter cdata(_cycler, true); cdata->_state = copy_cdata->_state; cdata->_transform = copy_cdata->_transform; - cdata->_prev_transform = copy_cdata->_prev_transform; - if (cdata->_transform != cdata->_prev_transform) { - do_set_dirty_prev_transform(); + if (copy._prev_transform_valid == _reset_prev_transform_seq) { + cdata->_prev_transform = copy_cdata->_prev_transform; + } else { + cdata->_prev_transform = copy_cdata->_transform; } cdata->_effects = copy_cdata->_effects; @@ -1061,24 +1052,23 @@ void PandaNode:: set_transform(const TransformState *transform, Thread *current_thread) { nassertv(!transform->is_invalid()); - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - // Apply this operation to the current stage as well as to all upstream // stages. bool any_changed = false; OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); if (cdata->_transform != transform) { + if (pipeline_stage == 0) { + // Back up the previous transform. + if (_prev_transform_valid != _reset_prev_transform_seq) { + cdata->_prev_transform = std::move(cdata->_transform); + _prev_transform_valid = _reset_prev_transform_seq; + } + } + cdata->_transform = transform; cdata->set_fancy_bit(FB_transform, !transform->is_identity()); any_changed = true; - - if (pipeline_stage == 0) { - if (cdata->_transform != cdata->_prev_transform) { - do_set_dirty_prev_transform(); - } - } } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); @@ -1099,20 +1089,13 @@ void PandaNode:: set_prev_transform(const TransformState *transform, Thread *current_thread) { nassertv(!transform->is_invalid()); - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - // Apply this operation to the current stage as well as to all upstream // stages. OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { CDStageWriter cdata(_cycler, pipeline_stage, current_thread); cdata->_prev_transform = transform; if (pipeline_stage == 0) { - if (cdata->_transform != cdata->_prev_transform) { - do_set_dirty_prev_transform(); - } else { - do_clear_dirty_prev_transform(); - } + _prev_transform_valid = _reset_prev_transform_seq; } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); @@ -1126,10 +1109,6 @@ set_prev_transform(const TransformState *transform, Thread *current_thread) { */ void PandaNode:: reset_prev_transform(Thread *current_thread) { - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - do_clear_dirty_prev_transform(); - // Apply this operation to the current stage as well as to all upstream // stages. @@ -1138,41 +1117,22 @@ reset_prev_transform(Thread *current_thread) { cdata->_prev_transform = cdata->_transform; } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); - mark_bam_modified(); } /** - * Visits all nodes in the world with the _dirty_prev_transform flag--that is, - * all nodes whose _prev_transform is different from the _transform in - * pipeline stage 0--and resets the _prev_transform to be the same as - * _transform. + * Makes sure that all nodes reset their prev_transform value to be the same as + * their transform value. This should be called at the start of each frame. */ void PandaNode:: reset_all_prev_transform(Thread *current_thread) { nassertv(current_thread->get_pipeline_stage() == 0); - PStatTimer timer(_reset_prev_pcollector, current_thread); - LightMutexHolder holder(_dirty_prev_transforms._lock); - - LinkedListNode *list_node = _dirty_prev_transforms._next; - while (list_node != &_dirty_prev_transforms) { - PandaNode *panda_node = (PandaNode *)list_node; - nassertv(panda_node->_dirty_prev_transform); - panda_node->_dirty_prev_transform = false; - - CDStageWriter cdata(panda_node->_cycler, 0, current_thread); - cdata->_prev_transform = cdata->_transform; - - list_node = panda_node->_next; -#ifndef NDEBUG - panda_node->_prev = nullptr; - panda_node->_next = nullptr; -#endif // NDEBUG - panda_node->mark_bam_modified(); - } - - _dirty_prev_transforms._prev = &_dirty_prev_transforms; - _dirty_prev_transforms._next = &_dirty_prev_transforms; + // Rather than keeping a linked list of all nodes that have changed their + // transform, we simply increment this counter. All the nodes compare this + // value to their own _prev_transform_valid value, and if it's different, + // they should disregard their _prev_transform field and assume it's the same + // as their _transform. + ++_reset_prev_transform_seq; } /** @@ -1345,9 +1305,6 @@ copy_all_properties(PandaNode *other) { return; } - // Need to have this held before we grab any other locks. - LightMutexHolder holder(_dirty_prev_transforms._lock); - bool any_transform_changed = false; bool any_state_changed = false; bool any_draw_mask_changed = false; @@ -1368,7 +1325,11 @@ copy_all_properties(PandaNode *other) { } cdataw->_transform = cdatar->_transform; - cdataw->_prev_transform = cdatar->_prev_transform; + if (other->_prev_transform_valid == _reset_prev_transform_seq) { + cdataw->_prev_transform = cdatar->_prev_transform; + } else { + cdataw->_prev_transform = cdatar->_transform; + } cdataw->_state = cdatar->_state; cdataw->_effects = cdatar->_effects; cdataw->_draw_control_mask = cdatar->_draw_control_mask; @@ -1390,9 +1351,7 @@ copy_all_properties(PandaNode *other) { (cdatar->_fancy_bits & change_bits); if (pipeline_stage == 0) { - if (cdataw->_transform != cdataw->_prev_transform) { - do_set_dirty_prev_transform(); - } + _prev_transform_valid = _reset_prev_transform_seq; } } CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index d078839b7e..76449859ae 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -63,7 +63,7 @@ class GraphicsStateGuardianBase; * properties. */ class EXPCL_PANDA_PGRAPH PandaNode : public TypedWritableReferenceCount, - public Namable, public LinkedListNode { + public Namable { PUBLISHED: explicit PandaNode(const std::string &name); virtual ~PandaNode(); @@ -449,9 +449,6 @@ private: void fix_path_lengths(int pipeline_stage, Thread *current_thread); void r_list_descendants(std::ostream &out, int indent_level) const; - INLINE void do_set_dirty_prev_transform(); - INLINE void do_clear_dirty_prev_transform(); - public: // This must be declared public so that VC6 will allow the nested CData // class to access it. @@ -540,8 +537,10 @@ private: Paths _paths; LightReMutex _paths_lock; - bool _dirty_prev_transform; - static PandaNodeChain _dirty_prev_transforms; + // This is not part of CData because we only care about modifications to the + // transform in the App stage. + UpdateSeq _prev_transform_valid; + static UpdateSeq _reset_prev_transform_seq; // This is used to maintain a table of keyed data on each node, for the // user's purposes. @@ -713,7 +712,6 @@ private: static DrawMask _overall_bit; - static PStatCollector _reset_prev_pcollector; static PStatCollector _update_bounds_pcollector; PUBLISHED: From 5695d1a719c5446ac2b56df957b225b689269891 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Feb 2022 11:43:11 +0100 Subject: [PATCH 061/166] tests: Add separate unit test for AsyncFuture.wait() with timeout Since it's using a different implementation than the no-timeout version now --- tests/event/test_futures.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/event/test_futures.py b/tests/event/test_futures.py index db09d8c677..13531db55b 100644 --- a/tests/event/test_futures.py +++ b/tests/event/test_futures.py @@ -88,6 +88,30 @@ def test_future_wait(): assert fut.result() is None +@pytest.mark.skipif(not core.Thread.is_threading_supported(), + reason="Threading support disabled") +def test_future_wait_timeout(): + threading = pytest.importorskip("direct.stdpy.threading") + + fut = core.AsyncFuture() + + # Launch a thread to set the result value. + def thread_main(): + time.sleep(0.001) + fut.set_result(None) + + thread = threading.Thread(target=thread_main) + + assert not fut.done() + thread.start() + + assert fut.result(1.0) is None + + assert fut.done() + assert not fut.cancelled() + assert fut.result() is None + + @pytest.mark.skipif(not core.Thread.is_threading_supported(), reason="Threading support disabled") def test_future_wait_cancel(): From 4df8c86590a2eefefe891d150f66ef401be4ded1 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Feb 2022 11:43:44 +0100 Subject: [PATCH 062/166] tests: Add unit test for PandaNode prev_transform tracking mechanism --- tests/pgraph/test_pandanode.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/pgraph/test_pandanode.py diff --git a/tests/pgraph/test_pandanode.py b/tests/pgraph/test_pandanode.py new file mode 100644 index 0000000000..28e684c04b --- /dev/null +++ b/tests/pgraph/test_pandanode.py @@ -0,0 +1,36 @@ +from panda3d.core import PandaNode, TransformState + + +def test_node_prev_transform(): + identity = TransformState.make_identity() + t1 = TransformState.make_pos((1, 0, 0)) + t2 = TransformState.make_pos((2, 0, 0)) + t3 = TransformState.make_pos((3, 0, 0)) + + node = PandaNode("node") + assert node.transform == identity + assert node.prev_transform == identity + assert not node.has_dirty_prev_transform() + + node.transform = t1 + assert node.transform == t1 + assert node.prev_transform == identity + assert node.has_dirty_prev_transform() + + node.transform = t2 + assert node.transform == t2 + assert node.prev_transform == identity + assert node.has_dirty_prev_transform() + + node.reset_prev_transform() + assert node.transform == t2 + assert node.prev_transform == t2 + assert not node.has_dirty_prev_transform() + + node.transform = t3 + assert node.prev_transform == t2 + assert node.has_dirty_prev_transform() + PandaNode.reset_all_prev_transform() + assert node.transform == t3 + assert node.prev_transform == t3 + assert not node.has_dirty_prev_transform() From a08e42d0156d07a88243da9d848f2f0f786d0748 Mon Sep 17 00:00:00 2001 From: Disyer Date: Sun, 6 Feb 2022 01:13:12 +0200 Subject: [PATCH 063/166] makepanda: Create 7-zip debug symbol archives by default, if available 7-zip archives will only be created if 7-zip is available during the build phase. When 7-zip is unavailable, ZIP archives will be created as a fallback. Benchmarks: - Default ZIP compression: ~23.5 seconds, 162 MB - 7-zip compression: ~7.5 seconds, 108 MB - 7-zip compression, --lzma set: ~44 seconds, 88 MB - 7-zip compression, solid archive: ~5 minutes, 83 MB (not implemented) Closes #1261 --- makepanda/makepackage.py | 57 +++++++++++++++++++++++++++++++------- makepanda/makepandacore.py | 20 +++++++++++++ 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py index 81a64abd44..19b91381da 100755 --- a/makepanda/makepackage.py +++ b/makepanda/makepackage.py @@ -189,31 +189,65 @@ def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwa oscmd(cmd) -def MakeDebugSymbolArchive(zipname, dirname): - outputdir = GetOutputDir() - +def MakeDebugSymbolZipArchive(zipname): import zipfile - zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED) + outputdir = GetOutputDir() + zip = zipfile.ZipFile(zipname + '.zip', 'w', zipfile.ZIP_DEFLATED) for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')): - zip.write(fn, dirname + '/bin/' + os.path.basename(fn)) + zip.write(fn, 'bin/' + os.path.basename(fn)) for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')): - zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn)) + zip.write(fn, 'panda3d/' + os.path.basename(fn)) for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')): - zip.write(fn, dirname + '/plugins/' + os.path.basename(fn)) + zip.write(fn, 'plugins/' + os.path.basename(fn)) for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')): - zip.write(fn, dirname + '/python/' + os.path.basename(fn)) + zip.write(fn, 'python/' + os.path.basename(fn)) for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')): - zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn)) + zip.write(fn, 'python/DLLs/' + os.path.basename(fn)) zip.close() +def MakeDebugSymbolSevenZipArchive(zipname, compressor): + zipname += '.7z' + flags = ['-t7z', '-y'] + + if compressor == 'zlib': + # This will still build an LZMA2 archive by default, + # but will complete significantly faster. + flags.extend(['-mx=3']) + + # Remove the old archive before proceeding. + if os.path.exists(zipname): + os.remove(zipname) + + outputdir = GetOutputDir() + + # We'll be creating the archive inside the output + # directory, so we need the relative path to the archive + zipname = os.path.relpath(zipname, outputdir) + + # Create a 7-zip archive, including all *.pdb files + # that are not in the tmp folder + cmd = [GetSevenZip(), 'a'] + cmd.extend(flags) + cmd.extend(['-ir!*.pdb', '-x!' + os.path.join('tmp', '*'), zipname]) + + subprocess.call(cmd, stdout=subprocess.DEVNULL, cwd=outputdir) + + +def MakeDebugSymbolArchive(zipname, compressor): + if HasSevenZip(): + MakeDebugSymbolSevenZipArchive(zipname, compressor) + else: + MakeDebugSymbolZipArchive(zipname) + + def MakeInstallerLinux(version, debversion=None, rpmversion=None, rpmrelease=1, python_versions=[], **kwargs): outputdir = GetOutputDir() @@ -969,6 +1003,7 @@ def MakeInstallerAndroid(version, **kwargs): def MakeInstaller(version, **kwargs): target = GetTarget() + if target == 'windows': dir = kwargs.pop('installdir', None) if dir is None: @@ -991,8 +1026,10 @@ def MakeInstaller(version, **kwargs): if GetTargetArch() == 'x64': fn += '-x64' + compressor = kwargs.get('compressor') + MakeInstallerNSIS(version, fn + '.exe', title, dir, **kwargs) - MakeDebugSymbolArchive(fn + '-pdb.zip', dir) + MakeDebugSymbolArchive(fn + '-pdb', compressor) elif target == 'linux': MakeInstallerLinux(version, **kwargs) elif target == 'darwin': diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 99a983c63c..f0e2708401 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -572,6 +572,26 @@ def GetFlexVersion(): Warn("Unable to detect flex version") return (0, 0, 0) +SEVENZIP = None +def GetSevenZip(): + global SEVENZIP + if SEVENZIP is not None: + return SEVENZIP + + win_util = os.path.join(GetThirdpartyBase(), 'win-util') + if GetHost() == 'windows' and os.path.isdir(win_util): + SEVENZIP = GetThirdpartyBase() + "/win-util/7za.exe" + elif LocateBinary('7z'): + SEVENZIP = '7z' + else: + # We don't strictly need it, so don't give an error + return None + + return SEVENZIP + +def HasSevenZip(): + return GetSevenZip() is not None + ######################################################################## ## ## LocateBinary From 7ccf38a94825b545e48d1908081732825e64f63d Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Feb 2022 14:13:11 +0100 Subject: [PATCH 064/166] pgraph: Remove obsolete PandaNodeChain class Obsoleted by 6bc22d1822ab02b464057f76563de7230dd93cec --- panda/src/pgraph/CMakeLists.txt | 2 -- panda/src/pgraph/p3pgraph_composite3.cxx | 1 - panda/src/pgraph/pandaNode.h | 1 - panda/src/pgraph/pandaNodeChain.I | 31 ------------------ panda/src/pgraph/pandaNodeChain.cxx | 14 --------- panda/src/pgraph/pandaNodeChain.h | 40 ------------------------ 6 files changed, 89 deletions(-) delete mode 100644 panda/src/pgraph/pandaNodeChain.I delete mode 100644 panda/src/pgraph/pandaNodeChain.cxx delete mode 100644 panda/src/pgraph/pandaNodeChain.h diff --git a/panda/src/pgraph/CMakeLists.txt b/panda/src/pgraph/CMakeLists.txt index 85c041f01b..8319e31863 100644 --- a/panda/src/pgraph/CMakeLists.txt +++ b/panda/src/pgraph/CMakeLists.txt @@ -66,7 +66,6 @@ set(P3PGRAPH_HEADERS occluderEffect.I occluderEffect.h occluderNode.I occluderNode.h pandaNode.I pandaNode.h - pandaNodeChain.I pandaNodeChain.h planeNode.I planeNode.h paramNodePath.I paramNodePath.h polylightEffect.I polylightEffect.h @@ -169,7 +168,6 @@ set(P3PGRAPH_SOURCES occluderEffect.cxx occluderNode.cxx pandaNode.cxx - pandaNodeChain.cxx planeNode.cxx paramNodePath.cxx polylightEffect.cxx diff --git a/panda/src/pgraph/p3pgraph_composite3.cxx b/panda/src/pgraph/p3pgraph_composite3.cxx index 18be4cb35e..712ed2f778 100644 --- a/panda/src/pgraph/p3pgraph_composite3.cxx +++ b/panda/src/pgraph/p3pgraph_composite3.cxx @@ -21,7 +21,6 @@ #include "occluderEffect.cxx" #include "occluderNode.cxx" #include "pandaNode.cxx" -#include "pandaNodeChain.cxx" #include "paramNodePath.cxx" #include "planeNode.cxx" #include "polylightEffect.cxx" diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 76449859ae..7542fae076 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -39,7 +39,6 @@ #include "pnotify.h" #include "updateSeq.h" #include "deletedChain.h" -#include "pandaNodeChain.h" #include "pStatCollector.h" #include "copyOnWriteObject.h" #include "copyOnWritePointer.h" diff --git a/panda/src/pgraph/pandaNodeChain.I b/panda/src/pgraph/pandaNodeChain.I deleted file mode 100644 index c53beec7e7..0000000000 --- a/panda/src/pgraph/pandaNodeChain.I +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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 pandaNodeChain.I - * @author drose - * @date 2006-04-21 - */ - -/** - * - */ -INLINE PandaNodeChain:: -PandaNodeChain(const char *lock_name) : - LinkedListNode(true), // This object is the root of a list of PandaNodes. - _lock(lock_name) -{ -} - -/** - * - */ -INLINE PandaNodeChain:: -~PandaNodeChain() { - _next = nullptr; - _prev = nullptr; -} diff --git a/panda/src/pgraph/pandaNodeChain.cxx b/panda/src/pgraph/pandaNodeChain.cxx deleted file mode 100644 index d52fb2b4f0..0000000000 --- a/panda/src/pgraph/pandaNodeChain.cxx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 pandaNodeChain.cxx - * @author drose - * @date 2006-04-21 - */ - -#include "pandaNodeChain.h" diff --git a/panda/src/pgraph/pandaNodeChain.h b/panda/src/pgraph/pandaNodeChain.h deleted file mode 100644 index 002a30041c..0000000000 --- a/panda/src/pgraph/pandaNodeChain.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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 pandaNodeChain.h - * @author drose - * @date 2006-04-21 - */ - -#ifndef PANDANODECHAIN_H -#define PANDANODECHAIN_H - -#include "pandabase.h" -#include "linkedListNode.h" -#include "lightMutex.h" - -class PandaNode; - -/** - * This class maintains a linked list of PandaNodes. It's used to maintain a - * list of PandaNodes whose _prev_transform is different from their _transform - * (in pipeline stage 0). - */ -class EXPCL_PANDA_PGRAPH PandaNodeChain : private LinkedListNode { -public: - INLINE PandaNodeChain(const char *lock_name); - INLINE ~PandaNodeChain(); - - LightMutex _lock; - - friend class PandaNode; -}; - -#include "pandaNodeChain.I" - -#endif From cfc11ba430e9c5e71339ac4b38295fdb4020cac8 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 25 Feb 2022 11:20:55 +0100 Subject: [PATCH 065/166] dist: Fix issue when deploy_libs in .whl have inconsistent suffixes --- direct/src/dist/commands.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 4a628d292d..cd31b3073e 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -1030,12 +1030,9 @@ class build_apps(setuptools.Command): self.warn("Detected use of tkinter, but tkinter is not specified in requirements.txt!") # Copy extension modules - whl_modules = [] - whl_modules_ext = '' + whl_modules = {} if use_wheels: # Get the module libs - whl_modules = [] - for i in p3dwhl.namelist(): if not i.startswith('deploy_libs/'): continue @@ -1050,8 +1047,7 @@ class build_apps(setuptools.Command): base = os.path.basename(i) module, _, ext = base.partition('.') - whl_modules.append(module) - whl_modules_ext = ext + whl_modules[module] = i # Make sure to copy any builtins that have shared objects in the # deploy libs, assuming they are not already in freezer_extras. @@ -1103,7 +1099,7 @@ class build_apps(setuptools.Command): else: # Builtin module, but might not be builtin in wheel libs, so double check if module in whl_modules: - source_path = os.path.join(p3dwhlfn, 'deploy_libs/{}.{}'.format(module, whl_modules_ext))#'{0}/deploy_libs/{1}.{2}'.format(p3dwhlfn, module, whl_modules_ext) + source_path = os.path.join(p3dwhlfn, whl_modules[module]) basename = os.path.basename(source_path) #XXX should we remove python version string here too? else: From 583c9f1857a9a253aca676fc347f53f989149fdf Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 12:19:13 +0100 Subject: [PATCH 066/166] pipeline: Fix issues with calling convention on 32-bit Windows --- dtool/src/dtoolbase/patomic.h | 6 +++--- panda/src/pipeline/conditionVarWin32Impl.h | 2 +- panda/src/pipeline/threadWin32Impl.cxx | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dtool/src/dtoolbase/patomic.h b/dtool/src/dtoolbase/patomic.h index f7f99dfc55..604d028392 100644 --- a/dtool/src/dtoolbase/patomic.h +++ b/dtool/src/dtoolbase/patomic.h @@ -161,9 +161,9 @@ ALWAYS_INLINE void patomic_notify_one(volatile uint32_t *value); ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value); #ifdef _WIN32 -EXPCL_DTOOL_DTOOLBASE extern BOOL (*_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD); -EXPCL_DTOOL_DTOOLBASE extern void (*_patomic_wake_one_func)(PVOID); -EXPCL_DTOOL_DTOOLBASE extern void (*_patomic_wake_all_func)(PVOID); +EXPCL_DTOOL_DTOOLBASE extern BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD); +EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_one_func)(PVOID); +EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_all_func)(PVOID); #elif !defined(__linux__) && defined(HAVE_POSIX_THREADS) EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old); EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value); diff --git a/panda/src/pipeline/conditionVarWin32Impl.h b/panda/src/pipeline/conditionVarWin32Impl.h index 12cbe053f7..5ec0ab0c08 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.h +++ b/panda/src/pipeline/conditionVarWin32Impl.h @@ -41,7 +41,7 @@ private: MutexWin32Impl &_mutex; CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT; - static BOOL (*_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); + static BOOL (__stdcall *_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); friend class PStatClientImpl; }; diff --git a/panda/src/pipeline/threadWin32Impl.cxx b/panda/src/pipeline/threadWin32Impl.cxx index 552c8b0453..22bf5cab79 100644 --- a/panda/src/pipeline/threadWin32Impl.cxx +++ b/panda/src/pipeline/threadWin32Impl.cxx @@ -27,9 +27,9 @@ static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT; #if _WIN32_WINNT < 0x0601 // Requires Windows 7. -static DWORD (*EnableThreadProfiling)(HANDLE, DWORD, DWORD64, HANDLE *) = nullptr; -static DWORD (*DisableThreadProfiling)(HANDLE) = nullptr; -static DWORD (*ReadThreadProfilingData)(HANDLE, DWORD, PPERFORMANCE_DATA data) = nullptr; +static DWORD (__stdcall *EnableThreadProfiling)(HANDLE, DWORD, DWORD64, HANDLE *) = nullptr; +static DWORD (__stdcall *DisableThreadProfiling)(HANDLE) = nullptr; +static DWORD (__stdcall *ReadThreadProfilingData)(HANDLE, DWORD, PPERFORMANCE_DATA data) = nullptr; static bool init_thread_profiling() { static bool inited = false; From 70415af210dc9bc4d94d9a27d87fdff8137ad38c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 15:12:38 +0100 Subject: [PATCH 067/166] dtoolutil: Set system malloc tag to mimalloc when enabled --- dtool/src/dtoolutil/pandaSystem.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dtool/src/dtoolutil/pandaSystem.cxx b/dtool/src/dtoolutil/pandaSystem.cxx index fa09e065ca..44f4f2dc60 100644 --- a/dtool/src/dtoolutil/pandaSystem.cxx +++ b/dtool/src/dtoolutil/pandaSystem.cxx @@ -58,6 +58,8 @@ PandaSystem() : set_system_tag("system", "malloc", "dlmalloc"); #elif defined(USE_MEMORY_PTMALLOC2) set_system_tag("system", "malloc", "ptmalloc2"); +#elif defined(USE_MEMORY_MIMALLOC) + set_system_tag("system", "malloc", "mimalloc"); #else set_system_tag("system", "malloc", "malloc"); #endif From ac3aa64d336b16150e63078a7a7e4de1f1a7a3e5 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 15:13:30 +0100 Subject: [PATCH 068/166] express: Add docstring to DatagramIterator about Datagram lifetime Related to #1262 [skip ci] --- panda/src/express/datagramIterator.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panda/src/express/datagramIterator.h b/panda/src/express/datagramIterator.h index 867c7de58c..5137022c5f 100644 --- a/panda/src/express/datagramIterator.h +++ b/panda/src/express/datagramIterator.h @@ -23,6 +23,9 @@ * A class to retrieve the individual data elements previously stored in a * Datagram. Elements may be retrieved one at a time; it is up to the caller * to know the correct type and order of each element. + * + * Note that it is the responsibility of the caller to ensure that the datagram + * object is not destructed while this DatagramIterator is in use. */ class EXPCL_PANDA_EXPRESS DatagramIterator { public: From 2f3561a48e71ff587adb5a607529008ef6e3dcaa Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 13:21:35 +0100 Subject: [PATCH 069/166] makepanda: Update for building against newer OpenEXR on non-Windows --- makepanda/makepanda.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 8dea9c9684..e4a3cfccd5 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -867,7 +867,6 @@ if (COMPILER=="GCC"): SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") SmartPkgEnable("SQUISH", "", ("squish"), "squish.h") SmartPkgEnable("TIFF", "libtiff-4", ("tiff"), "tiff.h") - SmartPkgEnable("OPENEXR", "OpenEXR", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h")) SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) @@ -893,6 +892,23 @@ if (COMPILER=="GCC"): for ffmpeg_lib in ffmpeg_libs: LibName("FFMPEG", "-Wl,--exclude-libs,%s.a" % (ffmpeg_lib)) + if not PkgSkip("OPENEXR"): + # OpenEXR libraries have different names depending on the version. + openexr_libdir = os.path.join(GetThirdpartyDir(), "openexr", "lib") + openexr_incs = ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h") + if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_1.a")): + SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_1", "IlmThread-3_1", "Imath-3_1", "Iex-3_1"), openexr_incs) + if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_0.a")): + SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_0", "IlmThread-3_0", "Imath-3_0", "Iex-3_0"), openexr_incs) + elif os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR.a")): + SmartPkgEnable("OPENEXR", "", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs) + elif os.path.isfile(os.path.join(openexr_libdir, "libIlmImf.a")): + SmartPkgEnable("OPENEXR", "", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), openexr_incs) + else: + # Find it in the system, preferably using pkg-config, otherwise + # using the OpenEXR 3 naming scheme. + SmartPkgEnable("OPENEXR", "OpenEXR", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs) + if GetTarget() != "darwin": for fcollada_lib in fcollada_libs: LibName("FCOLLADA", "-Wl,--exclude-libs,lib%s.a" % (fcollada_lib)) @@ -924,6 +940,9 @@ if (COMPILER=="GCC"): LibName("OPENEXR", "-Wl,--exclude-libs,libIlmImfUtil.a") LibName("OPENEXR", "-Wl,--exclude-libs,libIlmThread.a") LibName("OPENEXR", "-Wl,--exclude-libs,libImath.a") + LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXR.a") + LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRCore.a") + LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRUtil.a") if not PkgSkip("VORBIS"): LibName("VORBIS", "-Wl,--exclude-libs,libogg.a") From 794968dcc551b82404224a4400691287561df961 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 13:49:42 +0100 Subject: [PATCH 070/166] makepanda: Fix error building Maya client programs on macOS --- makepanda/makepanda.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index e4a3cfccd5..28e39ae484 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6045,17 +6045,21 @@ for VER in MAYAVERSIONS: TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS) if MAYA_BUILT: + OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', 'NOARCH:ARM64'] + TargetAdd('mayaprogs_mayaConversionClient.obj', opts=OPTS, input='mayaConversionClient.cxx') TargetAdd('maya2egg_mayaToEggClient.obj', opts=OPTS, input='mayaToEggClient.cxx') TargetAdd('maya2egg_client.exe', input='mayaprogs_mayaConversionClient.obj') TargetAdd('maya2egg_client.exe', input='maya2egg_mayaToEggClient.obj') TargetAdd('maya2egg_client.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('maya2egg_client.exe', opts=['NOARCH:ARM64']) TargetAdd('egg2maya_eggToMayaClient.obj', opts=OPTS, input='eggToMayaClient.cxx') TargetAdd('egg2maya_client.exe', input='mayaprogs_mayaConversionClient.obj') TargetAdd('egg2maya_client.exe', input='egg2maya_eggToMayaClient.obj') TargetAdd('egg2maya_client.exe', input=COMMON_EGG2X_LIBS) + TargetAdd('egg2maya_client.exe', opts=['NOARCH:ARM64']) # # DIRECTORY: contrib/src/ai/ From 5cfd8db95cd796eb14d0e32187e3da29834e47d2 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 13:21:14 +0100 Subject: [PATCH 071/166] glxdisplay: Update panda_glxext.h --- panda/src/glxdisplay/panda_glxext.h | 489 ++++++++++++++++------------ 1 file changed, 286 insertions(+), 203 deletions(-) diff --git a/panda/src/glxdisplay/panda_glxext.h b/panda/src/glxdisplay/panda_glxext.h index 5208302929..05a697c8ec 100644 --- a/panda/src/glxdisplay/panda_glxext.h +++ b/panda/src/glxdisplay/panda_glxext.h @@ -1,42 +1,21 @@ -#ifndef panda__glxext_h_ -#define panda__glxext_h_ 1 +#ifndef __glx_glxext_h_ +#define __glx_glxext_h_ 1 #ifdef __cplusplus extern "C" { #endif /* -** Copyright (c) 2013-2014 The Khronos Group Inc. +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT ** -** 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 -** without limitation the rights to use, copy, modify, merge, publish, -** 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. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ -/* ** This header is generated from the Khronos OpenGL / OpenGL ES XML ** API Registry. The current version of the Registry, generator scripts ** used to make the header, and the header can be found at -** http://www.opengl.org/registry/ -** -** Khronos $Revision$ on $Date$ +** https://github.com/KhronosGroup/OpenGL-Registry */ -#define GLX_GLXEXT_VERSION 20140416 +#define GLX_GLXEXT_VERSION 20211115 /* Generated C header for: * API: glx @@ -109,41 +88,41 @@ typedef XID GLXPbuffer; #define GLX_PBUFFER 0x8023 #define GLX_PBUFFER_HEIGHT 0x8040 #define GLX_PBUFFER_WIDTH 0x8041 -typedef GLXFBConfig *( *PFNGLXGETFBCONFIGSPROC) (X11_Display *dpy, int screen, int *nelements); -typedef GLXFBConfig *( *PFNGLXCHOOSEFBCONFIGPROC) (X11_Display *dpy, int screen, const int *attrib_list, int *nelements); -typedef int ( *PFNGLXGETFBCONFIGATTRIBPROC) (X11_Display *dpy, GLXFBConfig config, int attribute, int *value); -typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGPROC) (X11_Display *dpy, GLXFBConfig config); -typedef GLXWindow ( *PFNGLXCREATEWINDOWPROC) (X11_Display *dpy, GLXFBConfig config, X11_Window win, const int *attrib_list); -typedef void ( *PFNGLXDESTROYWINDOWPROC) (X11_Display *dpy, GLXWindow win); -typedef GLXPixmap ( *PFNGLXCREATEPIXMAPPROC) (X11_Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); -typedef void ( *PFNGLXDESTROYPIXMAPPROC) (X11_Display *dpy, GLXPixmap pixmap); -typedef GLXPbuffer ( *PFNGLXCREATEPBUFFERPROC) (X11_Display *dpy, GLXFBConfig config, const int *attrib_list); -typedef void ( *PFNGLXDESTROYPBUFFERPROC) (X11_Display *dpy, GLXPbuffer pbuf); -typedef void ( *PFNGLXQUERYDRAWABLEPROC) (X11_Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); -typedef GLXContext ( *PFNGLXCREATENEWCONTEXTPROC) (X11_Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); -typedef Bool ( *PFNGLXMAKECONTEXTCURRENTPROC) (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef GLXFBConfig *( *PFNGLXGETFBCONFIGSPROC) (Display *dpy, int screen, int *nelements); +typedef GLXFBConfig *( *PFNGLXCHOOSEFBCONFIGPROC) (Display *dpy, int screen, const int *attrib_list, int *nelements); +typedef int ( *PFNGLXGETFBCONFIGATTRIBPROC) (Display *dpy, GLXFBConfig config, int attribute, int *value); +typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGPROC) (Display *dpy, GLXFBConfig config); +typedef GLXWindow ( *PFNGLXCREATEWINDOWPROC) (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list); +typedef void ( *PFNGLXDESTROYWINDOWPROC) (Display *dpy, GLXWindow win); +typedef GLXPixmap ( *PFNGLXCREATEPIXMAPPROC) (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); +typedef void ( *PFNGLXDESTROYPIXMAPPROC) (Display *dpy, GLXPixmap pixmap); +typedef GLXPbuffer ( *PFNGLXCREATEPBUFFERPROC) (Display *dpy, GLXFBConfig config, const int *attrib_list); +typedef void ( *PFNGLXDESTROYPBUFFERPROC) (Display *dpy, GLXPbuffer pbuf); +typedef void ( *PFNGLXQUERYDRAWABLEPROC) (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); +typedef GLXContext ( *PFNGLXCREATENEWCONTEXTPROC) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); +typedef Bool ( *PFNGLXMAKECONTEXTCURRENTPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLEPROC) (void); -typedef int ( *PFNGLXQUERYCONTEXTPROC) (X11_Display *dpy, GLXContext ctx, int attribute, int *value); -typedef void ( *PFNGLXSELECTEVENTPROC) (X11_Display *dpy, GLXDrawable draw, unsigned long event_mask); -typedef void ( *PFNGLXGETSELECTEDEVENTPROC) (X11_Display *dpy, GLXDrawable draw, unsigned long *event_mask); +typedef int ( *PFNGLXQUERYCONTEXTPROC) (Display *dpy, GLXContext ctx, int attribute, int *value); +typedef void ( *PFNGLXSELECTEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long event_mask); +typedef void ( *PFNGLXGETSELECTEDEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long *event_mask); #ifdef GLX_GLXEXT_PROTOTYPES -GLXFBConfig *glXGetFBConfigs (X11_Display *dpy, int screen, int *nelements); -GLXFBConfig *glXChooseFBConfig (X11_Display *dpy, int screen, const int *attrib_list, int *nelements); -int glXGetFBConfigAttrib (X11_Display *dpy, GLXFBConfig config, int attribute, int *value); -XVisualInfo *glXGetVisualFromFBConfig (X11_Display *dpy, GLXFBConfig config); -GLXWindow glXCreateWindow (X11_Display *dpy, GLXFBConfig config, X11_Window win, const int *attrib_list); -void glXDestroyWindow (X11_Display *dpy, GLXWindow win); -GLXPixmap glXCreatePixmap (X11_Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); -void glXDestroyPixmap (X11_Display *dpy, GLXPixmap pixmap); -GLXPbuffer glXCreatePbuffer (X11_Display *dpy, GLXFBConfig config, const int *attrib_list); -void glXDestroyPbuffer (X11_Display *dpy, GLXPbuffer pbuf); -void glXQueryDrawable (X11_Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); -GLXContext glXCreateNewContext (X11_Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); -Bool glXMakeContextCurrent (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +GLXFBConfig *glXGetFBConfigs (Display *dpy, int screen, int *nelements); +GLXFBConfig *glXChooseFBConfig (Display *dpy, int screen, const int *attrib_list, int *nelements); +int glXGetFBConfigAttrib (Display *dpy, GLXFBConfig config, int attribute, int *value); +XVisualInfo *glXGetVisualFromFBConfig (Display *dpy, GLXFBConfig config); +GLXWindow glXCreateWindow (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list); +void glXDestroyWindow (Display *dpy, GLXWindow win); +GLXPixmap glXCreatePixmap (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); +void glXDestroyPixmap (Display *dpy, GLXPixmap pixmap); +GLXPbuffer glXCreatePbuffer (Display *dpy, GLXFBConfig config, const int *attrib_list); +void glXDestroyPbuffer (Display *dpy, GLXPbuffer pbuf); +void glXQueryDrawable (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); +GLXContext glXCreateNewContext (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); +Bool glXMakeContextCurrent (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); GLXDrawable glXGetCurrentReadDrawable (void); -int glXQueryContext (X11_Display *dpy, GLXContext ctx, int attribute, int *value); -void glXSelectEvent (X11_Display *dpy, GLXDrawable draw, unsigned long event_mask); -void glXGetSelectedEvent (X11_Display *dpy, GLXDrawable draw, unsigned long *event_mask); +int glXQueryContext (Display *dpy, GLXContext ctx, int attribute, int *value); +void glXSelectEvent (Display *dpy, GLXDrawable draw, unsigned long event_mask); +void glXGetSelectedEvent (Display *dpy, GLXDrawable draw, unsigned long *event_mask); #endif #endif /* GLX_VERSION_1_3 */ @@ -158,6 +137,13 @@ __GLXextFuncPtr glXGetProcAddress (const GLubyte *procName); #endif #endif /* GLX_VERSION_1_4 */ +#ifndef GLX_ARB_context_flush_control +#define GLX_ARB_context_flush_control 1 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* GLX_ARB_context_flush_control */ + #ifndef GLX_ARB_create_context #define GLX_ARB_create_context 1 #define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 @@ -165,12 +151,17 @@ __GLXextFuncPtr glXGetProcAddress (const GLubyte *procName); #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 #define GLX_CONTEXT_FLAGS_ARB 0x2094 -typedef GLXContext ( *PFNGLXCREATECONTEXTATTRIBSARBPROC) (X11_Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +typedef GLXContext ( *PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); #ifdef GLX_GLXEXT_PROTOTYPES -GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +GLXContext glXCreateContextAttribsARB (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); #endif #endif /* GLX_ARB_create_context */ +#ifndef GLX_ARB_create_context_no_error +#define GLX_ARB_create_context_no_error 1 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* GLX_ARB_create_context_no_error */ + #ifndef GLX_ARB_create_context_profile #define GLX_ARB_create_context_profile 1 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 @@ -199,7 +190,6 @@ GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLX #ifndef GLX_ARB_get_proc_address #define GLX_ARB_get_proc_address 1 -typedef void (*__GLXextFuncPtr)(void); typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName); #ifdef GLX_GLXEXT_PROTOTYPES __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); @@ -244,6 +234,26 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); #define GLX_GPU_NUM_SIMD_AMD 0x21A6 #define GLX_GPU_NUM_RB_AMD 0x21A7 #define GLX_GPU_NUM_SPI_AMD 0x21A8 +typedef unsigned int ( *PFNGLXGETGPUIDSAMDPROC) (unsigned int maxCount, unsigned int *ids); +typedef int ( *PFNGLXGETGPUINFOAMDPROC) (unsigned int id, int property, GLenum dataType, unsigned int size, void *data); +typedef unsigned int ( *PFNGLXGETCONTEXTGPUIDAMDPROC) (GLXContext ctx); +typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC) (unsigned int id, GLXContext share_list); +typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (unsigned int id, GLXContext share_context, const int *attribList); +typedef Bool ( *PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC) (GLXContext ctx); +typedef Bool ( *PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (GLXContext ctx); +typedef GLXContext ( *PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); +typedef void ( *PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC) (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GLX_GLXEXT_PROTOTYPES +unsigned int glXGetGPUIDsAMD (unsigned int maxCount, unsigned int *ids); +int glXGetGPUInfoAMD (unsigned int id, int property, GLenum dataType, unsigned int size, void *data); +unsigned int glXGetContextGPUIDAMD (GLXContext ctx); +GLXContext glXCreateAssociatedContextAMD (unsigned int id, GLXContext share_list); +GLXContext glXCreateAssociatedContextAttribsAMD (unsigned int id, GLXContext share_context, const int *attribList); +Bool glXDeleteAssociatedContextAMD (GLXContext ctx); +Bool glXMakeAssociatedContextCurrentAMD (GLXContext ctx); +GLXContext glXGetCurrentAssociatedContextAMD (void); +void glXBlitContextFramebufferAMD (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif #endif /* GLX_AMD_gpu_association */ #ifndef GLX_EXT_buffer_age @@ -251,6 +261,14 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); #define GLX_BACK_BUFFER_AGE_EXT 0x20F4 #endif /* GLX_EXT_buffer_age */ +#ifndef GLX_EXT_context_priority +#define GLX_EXT_context_priority 1 +#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 +#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 +#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 +#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 +#endif /* GLX_EXT_context_priority */ + #ifndef GLX_EXT_create_context_es2_profile #define GLX_EXT_create_context_es2_profile 1 #define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 @@ -272,13 +290,45 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); #define GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20B2 #endif /* GLX_EXT_framebuffer_sRGB */ +#ifndef GLX_EXT_get_drawable_type +#define GLX_EXT_get_drawable_type 1 +#endif /* GLX_EXT_get_drawable_type */ + +#ifndef GLX_EXT_import_context +#define GLX_EXT_import_context 1 +#define GLX_SHARE_CONTEXT_EXT 0x800A +#define GLX_VISUAL_ID_EXT 0x800B +#define GLX_SCREEN_EXT 0x800C +typedef Display *( *PFNGLXGETCURRENTDISPLAYEXTPROC) (void); +typedef int ( *PFNGLXQUERYCONTEXTINFOEXTPROC) (Display *dpy, GLXContext context, int attribute, int *value); +typedef GLXContextID ( *PFNGLXGETCONTEXTIDEXTPROC) (const GLXContext context); +typedef GLXContext ( *PFNGLXIMPORTCONTEXTEXTPROC) (Display *dpy, GLXContextID contextID); +typedef void ( *PFNGLXFREECONTEXTEXTPROC) (Display *dpy, GLXContext context); +#ifdef GLX_GLXEXT_PROTOTYPES +Display *glXGetCurrentDisplayEXT (void); +int glXQueryContextInfoEXT (Display *dpy, GLXContext context, int attribute, int *value); +GLXContextID glXGetContextIDEXT (const GLXContext context); +GLXContext glXImportContextEXT (Display *dpy, GLXContextID contextID); +void glXFreeContextEXT (Display *dpy, GLXContext context); +#endif +#endif /* GLX_EXT_import_context */ + +#ifndef GLX_EXT_libglvnd +#define GLX_EXT_libglvnd 1 +#define GLX_VENDOR_NAMES_EXT 0x20F6 +#endif /* GLX_EXT_libglvnd */ + +#ifndef GLX_EXT_no_config_context +#define GLX_EXT_no_config_context 1 +#endif /* GLX_EXT_no_config_context */ + #ifndef GLX_EXT_stereo_tree #define GLX_EXT_stereo_tree 1 typedef struct { int type; unsigned long serial; Bool send_event; - X11_Display *display; + Display *display; int extension; int evtype; GLXDrawable window; @@ -293,9 +343,9 @@ typedef struct { #define GLX_EXT_swap_control 1 #define GLX_SWAP_INTERVAL_EXT 0x20F1 #define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 -typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int interval); +typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); #ifdef GLX_GLXEXT_PROTOTYPES -void glXSwapIntervalEXT (X11_Display *dpy, GLXDrawable drawable, int interval); +void glXSwapIntervalEXT (Display *dpy, GLXDrawable drawable, int interval); #endif #endif /* GLX_EXT_swap_control */ @@ -339,11 +389,11 @@ void glXSwapIntervalEXT (X11_Display *dpy, GLXDrawable drawable, int interval); #define GLX_AUX7_EXT 0x20E9 #define GLX_AUX8_EXT 0x20EA #define GLX_AUX9_EXT 0x20EB -typedef void ( *PFNGLXBINDTEXIMAGEEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); -typedef void ( *PFNGLXRELEASETEXIMAGEEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int buffer); +typedef void ( *PFNGLXBINDTEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); +typedef void ( *PFNGLXRELEASETEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer); #ifdef GLX_GLXEXT_PROTOTYPES -void glXBindTexImageEXT (X11_Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); -void glXReleaseTexImageEXT (X11_Display *dpy, GLXDrawable drawable, int buffer); +void glXBindTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); +void glXReleaseTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer); #endif #endif /* GLX_EXT_texture_from_pixmap */ @@ -392,17 +442,17 @@ unsigned int glXGetAGPOffsetMESA (const void *pointer); #ifndef GLX_MESA_copy_sub_buffer #define GLX_MESA_copy_sub_buffer 1 -typedef void ( *PFNGLXCOPYSUBBUFFERMESAPROC) (X11_Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +typedef void ( *PFNGLXCOPYSUBBUFFERMESAPROC) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); #ifdef GLX_GLXEXT_PROTOTYPES -void glXCopySubBufferMESA (X11_Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +void glXCopySubBufferMESA (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); #endif #endif /* GLX_MESA_copy_sub_buffer */ #ifndef GLX_MESA_pixmap_colormap #define GLX_MESA_pixmap_colormap 1 -typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPMESAPROC) (X11_Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); +typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPMESAPROC) (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); #ifdef GLX_GLXEXT_PROTOTYPES -GLXPixmap glXCreateGLXPixmapMESA (X11_Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); +GLXPixmap glXCreateGLXPixmapMESA (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); #endif #endif /* GLX_MESA_pixmap_colormap */ @@ -419,24 +469,23 @@ GLXPixmap glXCreateGLXPixmapMESA (X11_Display *dpy, XVisualInfo *visual, Pixmap #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D -#define GLX_RENDERER_ID_MESA 0x818E typedef Bool ( *PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value); typedef const char *( *PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC) (int attribute); -typedef Bool ( *PFNGLXQUERYRENDERERINTEGERMESAPROC) (X11_Display *dpy, int screen, int renderer, int attribute, unsigned int *value); -typedef const char *( *PFNGLXQUERYRENDERERSTRINGMESAPROC) (X11_Display *dpy, int screen, int renderer, int attribute); +typedef Bool ( *PFNGLXQUERYRENDERERINTEGERMESAPROC) (Display *dpy, int screen, int renderer, int attribute, unsigned int *value); +typedef const char *( *PFNGLXQUERYRENDERERSTRINGMESAPROC) (Display *dpy, int screen, int renderer, int attribute); #ifdef GLX_GLXEXT_PROTOTYPES Bool glXQueryCurrentRendererIntegerMESA (int attribute, unsigned int *value); const char *glXQueryCurrentRendererStringMESA (int attribute); -Bool glXQueryRendererIntegerMESA (X11_Display *dpy, int screen, int renderer, int attribute, unsigned int *value); -const char *glXQueryRendererStringMESA (X11_Display *dpy, int screen, int renderer, int attribute); +Bool glXQueryRendererIntegerMESA (Display *dpy, int screen, int renderer, int attribute, unsigned int *value); +const char *glXQueryRendererStringMESA (Display *dpy, int screen, int renderer, int attribute); #endif #endif /* GLX_MESA_query_renderer */ #ifndef GLX_MESA_release_buffers #define GLX_MESA_release_buffers 1 -typedef Bool ( *PFNGLXRELEASEBUFFERSMESAPROC) (X11_Display *dpy, GLXDrawable drawable); +typedef Bool ( *PFNGLXRELEASEBUFFERSMESAPROC) (Display *dpy, GLXDrawable drawable); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXReleaseBuffersMESA (X11_Display *dpy, GLXDrawable drawable); +Bool glXReleaseBuffersMESA (Display *dpy, GLXDrawable drawable); #endif #endif /* GLX_MESA_release_buffers */ @@ -444,25 +493,45 @@ Bool glXReleaseBuffersMESA (X11_Display *dpy, GLXDrawable drawable); #define GLX_MESA_set_3dfx_mode 1 #define GLX_3DFX_WINDOW_MODE_MESA 0x1 #define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2 -typedef Bool ( *PFNGLXSET3DFXMODEMESAPROC) (int mode); +typedef GLboolean ( *PFNGLXSET3DFXMODEMESAPROC) (GLint mode); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXSet3DfxModeMESA (int mode); +GLboolean glXSet3DfxModeMESA (GLint mode); #endif #endif /* GLX_MESA_set_3dfx_mode */ +#ifndef GLX_MESA_swap_control +#define GLX_MESA_swap_control 1 +typedef int ( *PFNGLXGETSWAPINTERVALMESAPROC) (void); +typedef int ( *PFNGLXSWAPINTERVALMESAPROC) (unsigned int interval); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXGetSwapIntervalMESA (void); +int glXSwapIntervalMESA (unsigned int interval); +#endif +#endif /* GLX_MESA_swap_control */ + +#ifndef GLX_NV_copy_buffer +#define GLX_NV_copy_buffer 1 +typedef void ( *PFNGLXCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void ( *PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +void glXNamedCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif +#endif /* GLX_NV_copy_buffer */ + #ifndef GLX_NV_copy_image #define GLX_NV_copy_image 1 -typedef void ( *PFNGLXCOPYIMAGESUBDATANVPROC) (X11_Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +typedef void ( *PFNGLXCOPYIMAGESUBDATANVPROC) (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); #ifdef GLX_GLXEXT_PROTOTYPES -void glXCopyImageSubDataNV (X11_Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +void glXCopyImageSubDataNV (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); #endif #endif /* GLX_NV_copy_image */ #ifndef GLX_NV_delay_before_swap #define GLX_NV_delay_before_swap 1 -typedef Bool ( *PFNGLXDELAYBEFORESWAPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLfloat seconds); +typedef Bool ( *PFNGLXDELAYBEFORESWAPNVPROC) (Display *dpy, GLXDrawable drawable, GLfloat seconds); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat seconds); +Bool glXDelayBeforeSwapNV (Display *dpy, GLXDrawable drawable, GLfloat seconds); #endif #endif /* GLX_NV_delay_before_swap */ @@ -471,6 +540,15 @@ Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat secon #define GLX_FLOAT_COMPONENTS_NV 0x20B0 #endif /* GLX_NV_float_buffer */ +#ifndef GLX_NV_multigpu_context +#define GLX_NV_multigpu_context 1 +#define GLX_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define GLX_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define GLX_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* GLX_NV_multigpu_context */ + #ifndef GLX_NV_multisample_coverage #define GLX_NV_multisample_coverage 1 #define GLX_COVERAGE_SAMPLES_NV 100001 @@ -480,29 +558,34 @@ Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat secon #ifndef GLX_NV_present_video #define GLX_NV_present_video 1 #define GLX_NUM_VIDEO_SLOTS_NV 0x20F0 -typedef unsigned int *( *PFNGLXENUMERATEVIDEODEVICESNVPROC) (X11_Display *dpy, int screen, int *nelements); -typedef int ( *PFNGLXBINDVIDEODEVICENVPROC) (X11_Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); +typedef unsigned int *( *PFNGLXENUMERATEVIDEODEVICESNVPROC) (Display *dpy, int screen, int *nelements); +typedef int ( *PFNGLXBINDVIDEODEVICENVPROC) (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); #ifdef GLX_GLXEXT_PROTOTYPES -unsigned int *glXEnumerateVideoDevicesNV (X11_Display *dpy, int screen, int *nelements); -int glXBindVideoDeviceNV (X11_Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); +unsigned int *glXEnumerateVideoDevicesNV (Display *dpy, int screen, int *nelements); +int glXBindVideoDeviceNV (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); #endif #endif /* GLX_NV_present_video */ +#ifndef GLX_NV_robustness_video_memory_purge +#define GLX_NV_robustness_video_memory_purge 1 +#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 +#endif /* GLX_NV_robustness_video_memory_purge */ + #ifndef GLX_NV_swap_group #define GLX_NV_swap_group 1 -typedef Bool ( *PFNGLXJOINSWAPGROUPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLuint group); -typedef Bool ( *PFNGLXBINDSWAPBARRIERNVPROC) (X11_Display *dpy, GLuint group, GLuint barrier); -typedef Bool ( *PFNGLXQUERYSWAPGROUPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); -typedef Bool ( *PFNGLXQUERYMAXSWAPGROUPSNVPROC) (X11_Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); -typedef Bool ( *PFNGLXQUERYFRAMECOUNTNVPROC) (X11_Display *dpy, int screen, GLuint *count); -typedef Bool ( *PFNGLXRESETFRAMECOUNTNVPROC) (X11_Display *dpy, int screen); +typedef Bool ( *PFNGLXJOINSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint group); +typedef Bool ( *PFNGLXBINDSWAPBARRIERNVPROC) (Display *dpy, GLuint group, GLuint barrier); +typedef Bool ( *PFNGLXQUERYSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); +typedef Bool ( *PFNGLXQUERYMAXSWAPGROUPSNVPROC) (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); +typedef Bool ( *PFNGLXQUERYFRAMECOUNTNVPROC) (Display *dpy, int screen, GLuint *count); +typedef Bool ( *PFNGLXRESETFRAMECOUNTNVPROC) (Display *dpy, int screen); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXJoinSwapGroupNV (X11_Display *dpy, GLXDrawable drawable, GLuint group); -Bool glXBindSwapBarrierNV (X11_Display *dpy, GLuint group, GLuint barrier); -Bool glXQuerySwapGroupNV (X11_Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); -Bool glXQueryMaxSwapGroupsNV (X11_Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); -Bool glXQueryFrameCountNV (X11_Display *dpy, int screen, GLuint *count); -Bool glXResetFrameCountNV (X11_Display *dpy, int screen); +Bool glXJoinSwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint group); +Bool glXBindSwapBarrierNV (Display *dpy, GLuint group, GLuint barrier); +Bool glXQuerySwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); +Bool glXQueryMaxSwapGroupsNV (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); +Bool glXQueryFrameCountNV (Display *dpy, int screen, GLuint *count); +Bool glXResetFrameCountNV (Display *dpy, int screen); #endif #endif /* GLX_NV_swap_group */ @@ -512,22 +595,22 @@ typedef XID GLXVideoCaptureDeviceNV; #define GLX_DEVICE_ID_NV 0x20CD #define GLX_UNIQUE_ID_NV 0x20CE #define GLX_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF -typedef int ( *PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); -typedef GLXVideoCaptureDeviceNV *( *PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) (X11_Display *dpy, int screen, int *nelements); -typedef void ( *PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device); -typedef int ( *PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); -typedef void ( *PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device); +typedef int ( *PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); +typedef GLXVideoCaptureDeviceNV *( *PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) (Display *dpy, int screen, int *nelements); +typedef void ( *PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device); +typedef int ( *PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); +typedef void ( *PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device); #ifdef GLX_GLXEXT_PROTOTYPES -int glXBindVideoCaptureDeviceNV (X11_Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); -GLXVideoCaptureDeviceNV *glXEnumerateVideoCaptureDevicesNV (X11_Display *dpy, int screen, int *nelements); -void glXLockVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device); -int glXQueryVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); -void glXReleaseVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device); +int glXBindVideoCaptureDeviceNV (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); +GLXVideoCaptureDeviceNV *glXEnumerateVideoCaptureDevicesNV (Display *dpy, int screen, int *nelements); +void glXLockVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device); +int glXQueryVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); +void glXReleaseVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device); #endif #endif /* GLX_NV_video_capture */ -#ifndef GLX_NV_video_output -#define GLX_NV_video_output 1 +#ifndef GLX_NV_video_out +#define GLX_NV_video_out 1 typedef unsigned int GLXVideoDeviceNV; #define GLX_VIDEO_OUT_COLOR_NV 0x20C3 #define GLX_VIDEO_OUT_ALPHA_NV 0x20C4 @@ -539,21 +622,21 @@ typedef unsigned int GLXVideoDeviceNV; #define GLX_VIDEO_OUT_FIELD_2_NV 0x20CA #define GLX_VIDEO_OUT_STACKED_FIELDS_1_2_NV 0x20CB #define GLX_VIDEO_OUT_STACKED_FIELDS_2_1_NV 0x20CC -typedef int ( *PFNGLXGETVIDEODEVICENVPROC) (X11_Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); -typedef int ( *PFNGLXRELEASEVIDEODEVICENVPROC) (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); -typedef int ( *PFNGLXBINDVIDEOIMAGENVPROC) (X11_Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); -typedef int ( *PFNGLXRELEASEVIDEOIMAGENVPROC) (X11_Display *dpy, GLXPbuffer pbuf); -typedef int ( *PFNGLXSENDPBUFFERTOVIDEONVPROC) (X11_Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); -typedef int ( *PFNGLXGETVIDEOINFONVPROC) (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +typedef int ( *PFNGLXGETVIDEODEVICENVPROC) (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); +typedef int ( *PFNGLXRELEASEVIDEODEVICENVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); +typedef int ( *PFNGLXBINDVIDEOIMAGENVPROC) (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); +typedef int ( *PFNGLXRELEASEVIDEOIMAGENVPROC) (Display *dpy, GLXPbuffer pbuf); +typedef int ( *PFNGLXSENDPBUFFERTOVIDEONVPROC) (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); +typedef int ( *PFNGLXGETVIDEOINFONVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); #ifdef GLX_GLXEXT_PROTOTYPES -int glXGetVideoDeviceNV (X11_Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); -int glXReleaseVideoDeviceNV (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); -int glXBindVideoImageNV (X11_Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); -int glXReleaseVideoImageNV (X11_Display *dpy, GLXPbuffer pbuf); -int glXSendPbufferToVideoNV (X11_Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); -int glXGetVideoInfoNV (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +int glXGetVideoDeviceNV (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); +int glXReleaseVideoDeviceNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); +int glXBindVideoImageNV (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); +int glXReleaseVideoImageNV (Display *dpy, GLXPbuffer pbuf); +int glXSendPbufferToVideoNV (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); +int glXGetVideoInfoNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); #endif -#endif /* GLX_NV_video_output */ +#endif /* GLX_NV_video_out */ #ifndef GLX_OML_swap_method #define GLX_OML_swap_method 1 @@ -602,17 +685,17 @@ typedef unsigned __int64 uint64_t; #include #endif #endif -typedef Bool ( *PFNGLXGETSYNCVALUESOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); -typedef Bool ( *PFNGLXGETMSCRATEOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); -typedef int64_t ( *PFNGLXSWAPBUFFERSMSCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); -typedef Bool ( *PFNGLXWAITFORMSCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); -typedef Bool ( *PFNGLXWAITFORSBCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); +typedef Bool ( *PFNGLXGETSYNCVALUESOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); +typedef Bool ( *PFNGLXGETMSCRATEOMLPROC) (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); +typedef int64_t ( *PFNGLXSWAPBUFFERSMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); +typedef Bool ( *PFNGLXWAITFORMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); +typedef Bool ( *PFNGLXWAITFORSBCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXGetSyncValuesOML (X11_Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); -Bool glXGetMscRateOML (X11_Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); -int64_t glXSwapBuffersMscOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); -Bool glXWaitForMscOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); -Bool glXWaitForSbcOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); +Bool glXGetSyncValuesOML (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); +Bool glXGetMscRateOML (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); +int64_t glXSwapBuffersMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); +Bool glXWaitForMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); +Bool glXWaitForSbcOML (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); #endif #endif /* GLX_OML_sync_control */ @@ -638,9 +721,9 @@ Bool glXWaitForSbcOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_sb typedef XID GLXPbufferSGIX; #ifdef _DM_BUFFER_H_ #define GLX_DIGITAL_MEDIA_PBUFFER_SGIX 0x8024 -typedef Bool ( *PFNGLXASSOCIATEDMPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); +typedef Bool ( *PFNGLXASSOCIATEDMPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXAssociateDMPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); +Bool glXAssociateDMPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); #endif #endif /* _DM_BUFFER_H_ */ #endif /* GLX_SGIX_dmbuffer */ @@ -658,19 +741,19 @@ typedef struct __GLXFBConfigRec *GLXFBConfigSGIX; #define GLX_FBCONFIG_ID_SGIX 0x8013 #define GLX_RGBA_TYPE_SGIX 0x8014 #define GLX_COLOR_INDEX_TYPE_SGIX 0x8015 -typedef int ( *PFNGLXGETFBCONFIGATTRIBSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); -typedef GLXFBConfigSGIX *( *PFNGLXCHOOSEFBCONFIGSGIXPROC) (X11_Display *dpy, int screen, int *attrib_list, int *nelements); -typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); -typedef GLXContext ( *PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); -typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config); -typedef GLXFBConfigSGIX ( *PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) (X11_Display *dpy, XVisualInfo *vis); +typedef int ( *PFNGLXGETFBCONFIGATTRIBSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); +typedef GLXFBConfigSGIX *( *PFNGLXCHOOSEFBCONFIGSGIXPROC) (Display *dpy, int screen, int *attrib_list, int *nelements); +typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); +typedef GLXContext ( *PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); +typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config); +typedef GLXFBConfigSGIX ( *PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) (Display *dpy, XVisualInfo *vis); #ifdef GLX_GLXEXT_PROTOTYPES -int glXGetFBConfigAttribSGIX (X11_Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); -GLXFBConfigSGIX *glXChooseFBConfigSGIX (X11_Display *dpy, int screen, int *attrib_list, int *nelements); -GLXPixmap glXCreateGLXPixmapWithConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); -GLXContext glXCreateContextWithConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); -XVisualInfo *glXGetVisualFromFBConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config); -GLXFBConfigSGIX glXGetFBConfigFromVisualSGIX (X11_Display *dpy, XVisualInfo *vis); +int glXGetFBConfigAttribSGIX (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); +GLXFBConfigSGIX *glXChooseFBConfigSGIX (Display *dpy, int screen, int *attrib_list, int *nelements); +GLXPixmap glXCreateGLXPixmapWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); +GLXContext glXCreateContextWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); +XVisualInfo *glXGetVisualFromFBConfigSGIX (Display *dpy, GLXFBConfigSGIX config); +GLXFBConfigSGIX glXGetFBConfigFromVisualSGIX (Display *dpy, XVisualInfo *vis); #endif #endif /* GLX_SGIX_fbconfig */ @@ -705,23 +788,23 @@ typedef struct { #define GLX_HYPERPIPE_STEREO_SGIX 0x00000003 #define GLX_HYPERPIPE_PIXEL_AVERAGE_SGIX 0x00000004 #define GLX_HYPERPIPE_ID_SGIX 0x8030 -typedef GLXHyperpipeNetworkSGIX *( *PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) (X11_Display *dpy, int *npipes); -typedef int ( *PFNGLXHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); -typedef GLXHyperpipeConfigSGIX *( *PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int hpId, int *npipes); -typedef int ( *PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int hpId); -typedef int ( *PFNGLXBINDHYPERPIPESGIXPROC) (X11_Display *dpy, int hpId); -typedef int ( *PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); -typedef int ( *PFNGLXHYPERPIPEATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList); -typedef int ( *PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); +typedef GLXHyperpipeNetworkSGIX *( *PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) (Display *dpy, int *npipes); +typedef int ( *PFNGLXHYPERPIPECONFIGSGIXPROC) (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); +typedef GLXHyperpipeConfigSGIX *( *PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId, int *npipes); +typedef int ( *PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId); +typedef int ( *PFNGLXBINDHYPERPIPESGIXPROC) (Display *dpy, int hpId); +typedef int ( *PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); +typedef int ( *PFNGLXHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList); +typedef int ( *PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); #ifdef GLX_GLXEXT_PROTOTYPES -GLXHyperpipeNetworkSGIX *glXQueryHyperpipeNetworkSGIX (X11_Display *dpy, int *npipes); -int glXHyperpipeConfigSGIX (X11_Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); -GLXHyperpipeConfigSGIX *glXQueryHyperpipeConfigSGIX (X11_Display *dpy, int hpId, int *npipes); -int glXDestroyHyperpipeConfigSGIX (X11_Display *dpy, int hpId); -int glXBindHyperpipeSGIX (X11_Display *dpy, int hpId); -int glXQueryHyperpipeBestAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); -int glXHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList); -int glXQueryHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); +GLXHyperpipeNetworkSGIX *glXQueryHyperpipeNetworkSGIX (Display *dpy, int *npipes); +int glXHyperpipeConfigSGIX (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); +GLXHyperpipeConfigSGIX *glXQueryHyperpipeConfigSGIX (Display *dpy, int hpId, int *npipes); +int glXDestroyHyperpipeConfigSGIX (Display *dpy, int hpId); +int glXBindHyperpipeSGIX (Display *dpy, int hpId); +int glXQueryHyperpipeBestAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); +int glXHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList); +int glXQueryHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); #endif #endif /* GLX_SGIX_hyperpipe */ @@ -752,35 +835,35 @@ int glXQueryHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, in #define GLX_SAVED_SGIX 0x8021 #define GLX_WINDOW_SGIX 0x8022 #define GLX_PBUFFER_SGIX 0x8023 -typedef GLXPbufferSGIX ( *PFNGLXCREATEGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); -typedef void ( *PFNGLXDESTROYGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuf); -typedef int ( *PFNGLXQUERYGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); -typedef void ( *PFNGLXSELECTEVENTSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, unsigned long mask); -typedef void ( *PFNGLXGETSELECTEDEVENTSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, unsigned long *mask); +typedef GLXPbufferSGIX ( *PFNGLXCREATEGLXPBUFFERSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); +typedef void ( *PFNGLXDESTROYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf); +typedef void ( *PFNGLXQUERYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); +typedef void ( *PFNGLXSELECTEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long mask); +typedef void ( *PFNGLXGETSELECTEDEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long *mask); #ifdef GLX_GLXEXT_PROTOTYPES -GLXPbufferSGIX glXCreateGLXPbufferSGIX (X11_Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); -void glXDestroyGLXPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuf); -int glXQueryGLXPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); -void glXSelectEventSGIX (X11_Display *dpy, GLXDrawable drawable, unsigned long mask); -void glXGetSelectedEventSGIX (X11_Display *dpy, GLXDrawable drawable, unsigned long *mask); +GLXPbufferSGIX glXCreateGLXPbufferSGIX (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); +void glXDestroyGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf); +void glXQueryGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); +void glXSelectEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long mask); +void glXGetSelectedEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long *mask); #endif #endif /* GLX_SGIX_pbuffer */ #ifndef GLX_SGIX_swap_barrier #define GLX_SGIX_swap_barrier 1 -typedef void ( *PFNGLXBINDSWAPBARRIERSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, int barrier); -typedef Bool ( *PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) (X11_Display *dpy, int screen, int *max); +typedef void ( *PFNGLXBINDSWAPBARRIERSGIXPROC) (Display *dpy, GLXDrawable drawable, int barrier); +typedef Bool ( *PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) (Display *dpy, int screen, int *max); #ifdef GLX_GLXEXT_PROTOTYPES -void glXBindSwapBarrierSGIX (X11_Display *dpy, GLXDrawable drawable, int barrier); -Bool glXQueryMaxSwapBarriersSGIX (X11_Display *dpy, int screen, int *max); +void glXBindSwapBarrierSGIX (Display *dpy, GLXDrawable drawable, int barrier); +Bool glXQueryMaxSwapBarriersSGIX (Display *dpy, int screen, int *max); #endif #endif /* GLX_SGIX_swap_barrier */ #ifndef GLX_SGIX_swap_group #define GLX_SGIX_swap_group 1 -typedef void ( *PFNGLXJOINSWAPGROUPSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, GLXDrawable member); +typedef void ( *PFNGLXJOINSWAPGROUPSGIXPROC) (Display *dpy, GLXDrawable drawable, GLXDrawable member); #ifdef GLX_GLXEXT_PROTOTYPES -void glXJoinSwapGroupSGIX (X11_Display *dpy, GLXDrawable drawable, GLXDrawable member); +void glXJoinSwapGroupSGIX (Display *dpy, GLXDrawable drawable, GLXDrawable member); #endif #endif /* GLX_SGIX_swap_group */ @@ -788,17 +871,17 @@ void glXJoinSwapGroupSGIX (X11_Display *dpy, GLXDrawable drawable, GLXDrawable m #define GLX_SGIX_video_resize 1 #define GLX_SYNC_FRAME_SGIX 0x00000000 #define GLX_SYNC_SWAP_SGIX 0x00000001 -typedef int ( *PFNGLXBINDCHANNELTOWINDOWSGIXPROC) (X11_Display *display, int screen, int channel, X11_Window window); -typedef int ( *PFNGLXCHANNELRECTSGIXPROC) (X11_Display *display, int screen, int channel, int x, int y, int w, int h); -typedef int ( *PFNGLXQUERYCHANNELRECTSGIXPROC) (X11_Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); -typedef int ( *PFNGLXQUERYCHANNELDELTASSGIXPROC) (X11_Display *display, int screen, int channel, int *x, int *y, int *w, int *h); -typedef int ( *PFNGLXCHANNELRECTSYNCSGIXPROC) (X11_Display *display, int screen, int channel, GLenum synctype); +typedef int ( *PFNGLXBINDCHANNELTOWINDOWSGIXPROC) (Display *display, int screen, int channel, Window window); +typedef int ( *PFNGLXCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int x, int y, int w, int h); +typedef int ( *PFNGLXQUERYCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); +typedef int ( *PFNGLXQUERYCHANNELDELTASSGIXPROC) (Display *display, int screen, int channel, int *x, int *y, int *w, int *h); +typedef int ( *PFNGLXCHANNELRECTSYNCSGIXPROC) (Display *display, int screen, int channel, GLenum synctype); #ifdef GLX_GLXEXT_PROTOTYPES -int glXBindChannelToWindowSGIX (X11_Display *display, int screen, int channel, X11_Window window); -int glXChannelRectSGIX (X11_Display *display, int screen, int channel, int x, int y, int w, int h); -int glXQueryChannelRectSGIX (X11_Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); -int glXQueryChannelDeltasSGIX (X11_Display *display, int screen, int channel, int *x, int *y, int *w, int *h); -int glXChannelRectSyncSGIX (X11_Display *display, int screen, int channel, GLenum synctype); +int glXBindChannelToWindowSGIX (Display *display, int screen, int channel, Window window); +int glXChannelRectSGIX (Display *display, int screen, int channel, int x, int y, int w, int h); +int glXQueryChannelRectSGIX (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); +int glXQueryChannelDeltasSGIX (Display *display, int screen, int channel, int *x, int *y, int *w, int *h); +int glXChannelRectSyncSGIX (Display *display, int screen, int channel, GLenum synctype); #endif #endif /* GLX_SGIX_video_resize */ @@ -806,11 +889,11 @@ int glXChannelRectSyncSGIX (X11_Display *display, int screen, int channel, GLenu #define GLX_SGIX_video_source 1 typedef XID GLXVideoSourceSGIX; #ifdef _VL_H -typedef GLXVideoSourceSGIX ( *PFNGLXCREATEGLXVIDEOSOURCESGIXPROC) (X11_Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); -typedef void ( *PFNGLXDESTROYGLXVIDEOSOURCESGIXPROC) (X11_Display *dpy, GLXVideoSourceSGIX glxvideosource); +typedef GLXVideoSourceSGIX ( *PFNGLXCREATEGLXVIDEOSOURCESGIXPROC) (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); +typedef void ( *PFNGLXDESTROYGLXVIDEOSOURCESGIXPROC) (Display *dpy, GLXVideoSourceSGIX glxvideosource); #ifdef GLX_GLXEXT_PROTOTYPES -GLXVideoSourceSGIX glXCreateGLXVideoSourceSGIX (X11_Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); -void glXDestroyGLXVideoSourceSGIX (X11_Display *dpy, GLXVideoSourceSGIX glxvideosource); +GLXVideoSourceSGIX glXCreateGLXVideoSourceSGIX (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); +void glXDestroyGLXVideoSourceSGIX (Display *dpy, GLXVideoSourceSGIX glxvideosource); #endif #endif /* _VL_H */ #endif /* GLX_SGIX_video_source */ @@ -822,18 +905,18 @@ void glXDestroyGLXVideoSourceSGIX (X11_Display *dpy, GLXVideoSourceSGIX glxvideo #ifndef GLX_SGI_cushion #define GLX_SGI_cushion 1 -typedef void ( *PFNGLXCUSHIONSGIPROC) (X11_Display *dpy, X11_Window window, float cushion); +typedef void ( *PFNGLXCUSHIONSGIPROC) (Display *dpy, Window window, float cushion); #ifdef GLX_GLXEXT_PROTOTYPES -void glXCushionSGI (X11_Display *dpy, X11_Window window, float cushion); +void glXCushionSGI (Display *dpy, Window window, float cushion); #endif #endif /* GLX_SGI_cushion */ #ifndef GLX_SGI_make_current_read #define GLX_SGI_make_current_read 1 -typedef Bool ( *PFNGLXMAKECURRENTREADSGIPROC) (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef Bool ( *PFNGLXMAKECURRENTREADSGIPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLESGIPROC) (void); #ifdef GLX_GLXEXT_PROTOTYPES -Bool glXMakeCurrentReadSGI (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +Bool glXMakeCurrentReadSGI (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); GLXDrawable glXGetCurrentReadDrawableSGI (void); #endif #endif /* GLX_SGI_make_current_read */ @@ -858,9 +941,9 @@ int glXWaitVideoSyncSGI (int divisor, int remainder, unsigned int *count); #ifndef GLX_SUN_get_transparent_index #define GLX_SUN_get_transparent_index 1 -typedef Status ( *PFNGLXGETTRANSPARENTINDEXSUNPROC) (X11_Display *dpy, X11_Window overlay, X11_Window underlay, long *pTransparentIndex); +typedef Status ( *PFNGLXGETTRANSPARENTINDEXSUNPROC) (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex); #ifdef GLX_GLXEXT_PROTOTYPES -Status glXGetTransparentIndexSUN (X11_Display *dpy, X11_Window overlay, X11_Window underlay, long *pTransparentIndex); +Status glXGetTransparentIndexSUN (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex); #endif #endif /* GLX_SUN_get_transparent_index */ From dc05889be26527c1732485e1838c2004698c8e6e Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 12:22:25 +0100 Subject: [PATCH 072/166] dtoolutil: Don't use $HOME in Filename::get_home_directory() on Windows This variable isn't used on Windows systems --- dtool/src/dtoolutil/filename.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index b3b84a84d7..c51119c54d 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -477,7 +477,8 @@ get_home_directory() { if (AtomicAdjust::get_ptr(_home_directory) == nullptr) { Filename home_directory; - // In all environments, check $HOME first. + // In all environments except Windows, check $HOME first. +#ifndef _WIN32 char *home = getenv("HOME"); if (home != nullptr) { Filename dirname = from_os_specific(home); @@ -487,6 +488,7 @@ get_home_directory() { } } } +#endif if (home_directory.empty()) { #ifdef _WIN32 From 21377c8de5a97b0665135938f11132145520da7c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 12:47:31 +0100 Subject: [PATCH 073/166] Use secure versions of CRT getenv etc. when compiling with MSVC --- dtool/src/dtoolutil/executionEnvironment.cxx | 45 +++++++++++++++++--- dtool/src/dtoolutil/filename.cxx | 12 ++++++ dtool/src/dtoolutil/panda_getopt_impl.cxx | 12 +++++- dtool/src/dtoolutil/pfstreamBuf.cxx | 7 ++- dtool/src/interrogate/interrogate.cxx | 7 +++ panda/src/downloadertools/multify.cxx | 7 +++ panda/src/express/zipArchive.cxx | 12 ++++++ 7 files changed, 90 insertions(+), 12 deletions(-) diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index 4214505faf..d39ae70a4e 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -259,10 +259,14 @@ ns_has_environment_variable(const string &var) const { return true; } -#ifndef PREREAD_ENVIRONMENT - return getenv(var.c_str()) != nullptr; -#else +#ifdef PREREAD_ENVIRONMENT return false; +#elif defined(_MSC_VER) + size_t size = 0; + getenv_s(&size, nullptr, 0, var.c_str()); + return size != 0; +#else + return getenv(var.c_str()) != nullptr; #endif } @@ -301,11 +305,24 @@ ns_get_environment_variable(const string &var) const { } #ifndef PREREAD_ENVIRONMENT +#ifdef _MSC_VER + std::string value(128, '\0'); + size_t size = value.size(); + while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) { + value.resize(size); + } + if (size != 0) { + // Strip off the trailing null byte. + value.resize(size - 1); + return value; + } +#else const char *def = getenv(var.c_str()); if (def != nullptr) { return def; } #endif +#endif #ifdef _WIN32 // On Windows only, we also simulate several standard folder names as @@ -414,14 +431,15 @@ ns_get_environment_variable(const string &var) const { void ExecutionEnvironment:: ns_set_environment_variable(const string &var, const string &value) { _variables[var] = value; + +#ifdef _MSC_VER + _putenv_s(var.c_str(), value.c_str()); +#else string putstr = var + "=" + value; // putenv() requires us to malloc a new C-style string. char *put = (char *)malloc(putstr.length() + 1); strcpy(put, putstr.c_str()); -#ifdef _MSC_VER - _putenv(put); -#else putenv(put); #endif } @@ -447,12 +465,27 @@ ns_clear_shadow(const string &var) { #ifdef PREREAD_ENVIRONMENT // Now we have to replace the value in the table. +#ifdef _MSC_VER + std::string value(128, '\0'); + size_t size = value.size(); + while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) { + value.resize(size); + } + if (size != 0) { + // Strip off the trailing null byte. + value.resize(size - 1); + (*vi).second = std::move(value); + } else { + _variables.erase(vi); + } +#else const char *def = getenv(var.c_str()); if (def != nullptr) { (*vi).second = def; } else { _variables.erase(vi); } +#endif #endif // PREREAD_ENVIRONMENT } diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index c51119c54d..f676220158 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -152,10 +152,22 @@ get_panda_root() { if (panda_root == nullptr) { panda_root = new string; + +#ifdef _MSC_VER + char *envvar = nullptr; + size_t size = 0; + while (getenv_s(&size, envvar, size, "PANDA_ROOT") == ERANGE) { + envvar = (char *)alloca(size); + } + if (size != 0) { + (*panda_root) = front_to_back_slash(envvar); + } +#else const char *envvar = getenv("PANDA_ROOT"); if (envvar != nullptr) { (*panda_root) = front_to_back_slash(envvar); } +#endif // Ensure the string ends in a backslash. If PANDA_ROOT is empty or // undefined, this function must return a single backslash--not an empty diff --git a/dtool/src/dtoolutil/panda_getopt_impl.cxx b/dtool/src/dtoolutil/panda_getopt_impl.cxx index 2779a7906a..f2ca4ed65c 100644 --- a/dtool/src/dtoolutil/panda_getopt_impl.cxx +++ b/dtool/src/dtoolutil/panda_getopt_impl.cxx @@ -140,6 +140,10 @@ PandaGetopt(int argc, char *const argv[], const char *optstring, // _options[0] is used for invalid characters. _options.push_back(Option('?', no_argument)); +#ifdef _MSC_VER + size_t size; +#endif + if (optstring[0] == '-') { // RETURN_IN_ORDER: Non-option arguments (operands) are handled as if they // were the argument to an option with the value 1 ('\001'). @@ -154,8 +158,12 @@ PandaGetopt(int argc, char *const argv[], const char *optstring, // argument is reached, or when the element of argv is "--". ++optstring; _require_order = true; - - } else if (getenv("POSIXLY_CORRECT") != nullptr) { + } +#ifdef _MSC_VER + else if (getenv_s(&size, nullptr, 0, "POSIXLY_CORRECT") == 0 && size != 0) { +#else + else if (getenv("POSIXLY_CORRECT") != nullptr) { +#endif // REQUIRE_ORDER. _require_order = true; diff --git a/dtool/src/dtoolutil/pfstreamBuf.cxx b/dtool/src/dtoolutil/pfstreamBuf.cxx index a5585ee3cd..b2fcf2838e 100644 --- a/dtool/src/dtoolutil/pfstreamBuf.cxx +++ b/dtool/src/dtoolutil/pfstreamBuf.cxx @@ -331,8 +331,9 @@ open_pipe(const string &cmd) { // Both WinExec() and CreateProcess() want a non-const char pointer. Maybe // they change it, and maybe they don't. I'm not taking chances. - char *cmdline = new char[cmd.length() + 1]; - strcpy(cmdline, cmd.c_str()); + char *cmdline = (char *)alloca(cmd.size() + 1); + memcpy(cmdline, cmd.data(), cmd.size()); + cmdline[cmd.size()] = 0; // We should be using CreateProcess() instead of WinExec(), but that seems // to be likely to crash Win98. WinExec() seems better behaved, and it's @@ -345,8 +346,6 @@ open_pipe(const string &cmd) { // Don't return yet, since we still need to clean up. } - delete[] cmdline; - // Now restore our own stdout, up here in the parent process. if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) { #ifndef NDEBUG diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index 8a9d9fe113..0c15cb4a3a 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -547,8 +547,15 @@ main(int argc, char **argv) { // We allow overriding this value by setting SOURCE_DATE_EPOCH to support // reproducible builds. int file_identifier; +#ifdef _MSC_VER + char source_date_epoch[64]; + size_t source_date_epoch_size = 0; + if (getenv_s(&source_date_epoch_size, source_date_epoch, + sizeof(source_date_epoch), "SOURCE_DATE_EPOCH"), source_date_epoch_size > 1) { +#else const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); if (source_date_epoch != nullptr && source_date_epoch[0] != 0) { +#endif file_identifier = atoi(source_date_epoch); } else { file_identifier = time(nullptr); diff --git a/panda/src/downloadertools/multify.cxx b/panda/src/downloadertools/multify.cxx index d649992b40..da3789d3cb 100644 --- a/panda/src/downloadertools/multify.cxx +++ b/panda/src/downloadertools/multify.cxx @@ -792,8 +792,15 @@ main(int argc, char **argv) { } } +#ifdef _MSC_VER + char source_date_epoch_str[64]; + size_t source_date_epoch_size = 0; + if (getenv_s(&source_date_epoch_size, source_date_epoch_str, + sizeof(source_date_epoch_str), "SOURCE_DATE_EPOCH"), source_date_epoch_size > 1) { +#else const char *source_date_epoch_str = getenv("SOURCE_DATE_EPOCH"); if (source_date_epoch_str != nullptr && source_date_epoch_str[0] != 0) { +#endif source_date_epoch = (time_t)strtoll(source_date_epoch_str, nullptr, 10); } diff --git a/panda/src/express/zipArchive.cxx b/panda/src/express/zipArchive.cxx index 64cc2ec497..fb232a7b40 100644 --- a/panda/src/express/zipArchive.cxx +++ b/panda/src/express/zipArchive.cxx @@ -2018,7 +2018,13 @@ write_index(std::ostream &write, streampos &fpos) { if (_timestamp > dos_epoch) { // Convert from UNIX timestamp to DOS/FAT timestamp. +#ifdef _MSC_VER + struct tm time_data; + struct tm *time = &time_data; + localtime_s(time, &_timestamp); +#else struct tm *time = localtime(&_timestamp); +#endif writer.add_uint16((time->tm_sec >> 1) | (time->tm_min << 5) | (time->tm_hour << 11)); @@ -2120,7 +2126,13 @@ write_header(std::ostream &write, std::streampos &fpos) { if (_timestamp > 315532800) { // Convert from UNIX timestamp to DOS/FAT timestamp. +#ifdef _MSC_VER + struct tm time_data; + struct tm *time = &time_data; + localtime_s(time, &_timestamp); +#else struct tm *time = localtime(&_timestamp); +#endif writer.add_uint16((time->tm_sec >> 1) | (time->tm_min << 5) | (time->tm_hour << 11)); From a0be50c769e913a2ebbca97bcba17a0b41c2fd5f Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 13:16:33 +0100 Subject: [PATCH 074/166] general: Fix assorted compiler warnings --- .../src/distributed/cConnectionRepository.cxx | 9 +- dtool/metalibs/dtoolconfig/pydtool.cxx | 278 +++++++++--------- dtool/src/cppparser/cppManifest.cxx | 4 +- dtool/src/cppparser/cppManifest.h | 2 +- dtool/src/cppparser/cppNamespace.cxx | 4 +- dtool/src/cppparser/cppStructType.cxx | 4 +- dtool/src/dtoolbase/patomic.I | 4 +- dtool/src/dtoolbase/typeHandle_ext.cxx | 1 - dtool/src/interrogate/interfaceMaker.cxx | 2 +- .../interfaceMakerPythonNative.cxx | 4 +- .../interrogate/interfaceMakerPythonObj.cxx | 4 +- .../interfaceMakerPythonSimple.cxx | 4 +- .../src/bullet/bulletConvexPointCloudShape.I | 4 +- panda/src/bullet/bulletTriangleMeshShape.cxx | 4 +- panda/src/chan/animControl.cxx | 4 +- panda/src/char/character.cxx | 14 +- panda/src/collide/collisionBox.cxx | 2 +- panda/src/collide/collisionHandlerEvent.I | 9 - panda/src/collide/collisionHandlerEvent.h | 1 - .../collide/collisionHandlerPhysical_ext.cxx | 1 - panda/src/collide/collisionLevelStateBase.I | 14 +- panda/src/collide/collisionTraverser.cxx | 6 +- panda/src/collide/collisionTraverser_ext.cxx | 1 - panda/src/device/evdevInputDevice.cxx | 8 +- panda/src/display/displayRegion.cxx | 8 +- panda/src/display/graphicsOutput.cxx | 4 +- panda/src/display/graphicsStateGuardian.cxx | 14 +- panda/src/display/graphicsWindow.cxx | 4 +- panda/src/display/standardMunger.cxx | 6 +- panda/src/egg/eggMesherEdge.I | 12 - panda/src/egg/eggMesherEdge.h | 1 - panda/src/egg2pg/eggSaver.cxx | 2 +- panda/src/event/eventHandler.I | 2 +- panda/src/event/pythonTask.cxx | 2 +- panda/src/express/referenceCount.I | 8 +- panda/src/express/zipArchive.cxx | 9 +- panda/src/ffmpeg/ffmpegAudioCursor.cxx | 4 +- panda/src/ffmpeg/ffmpegVideoCursor.cxx | 2 +- panda/src/glstuff/glGraphicsBuffer_src.cxx | 8 +- panda/src/glstuff/glShaderContext_src.cxx | 10 + panda/src/gobj/geomVertexArrayData.cxx | 8 +- panda/src/gobj/geomVertexColumn.I | 4 +- panda/src/gobj/geomVertexData.I | 8 +- panda/src/gobj/geomVertexData.cxx | 4 +- panda/src/gobj/samplerState.I | 10 +- panda/src/gobj/texture.cxx | 10 +- panda/src/grutil/heightfieldTesselator.cxx | 4 +- panda/src/grutil/shaderTerrainMesh.cxx | 6 +- panda/src/mathutil/perlinNoise2.cxx | 6 +- panda/src/pgraph/camera.cxx | 6 +- panda/src/pgraph/cullableObject.cxx | 4 +- panda/src/pgraph/nodePath.I | 4 +- panda/src/pgraph/renderState.I | 16 - panda/src/pgraph/renderState.h | 3 - panda/src/pgraph/shaderInput.I | 168 +++++------ panda/src/pgraph/shaderInput.cxx | 12 +- panda/src/pgraph/textureAttrib.I | 4 +- panda/src/pgraph/transformState.I | 16 - panda/src/pgraph/transformState.h | 3 - panda/src/pgraphnodes/fadeLodNode.cxx | 10 +- panda/src/pgraphnodes/lightLensNode.cxx | 12 +- panda/src/pgraphnodes/lodNode.cxx | 2 +- panda/src/pgui/pgItem.cxx | 4 +- panda/src/pipeline/pipeline.cxx | 4 +- panda/src/pstatclient/config_pstatclient.cxx | 2 +- panda/src/pstatclient/pStatClient.cxx | 4 +- panda/src/pstatclient/pStatTimer.h | 2 +- panda/src/putil/clockObject.cxx | 2 +- .../tinydisplay/tinyGraphicsStateGuardian.cxx | 9 + panda/src/tinydisplay/ztriangle.h | 2 +- panda/src/x11display/x11GraphicsPipe.cxx | 2 +- pandatool/src/egg-qtess/isoPlacer.cxx | 2 - .../src/palettizer/textureProperties.cxx | 8 + 73 files changed, 402 insertions(+), 443 deletions(-) diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index cbd3057488..60579024c8 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -58,19 +58,18 @@ CConnectionRepository(bool has_owner_view, bool threaded_net) : _bdc(4096000,4096000,1400), _native(false), #endif + _has_owner_view(has_owner_view), + _handle_c_updates(true), _client_datagram(true), _handle_datagrams_internally(handle_datagrams_internally), _simulated_disconnect(false), _verbose(distributed_cat.is_spam()), + _in_quiet_zone(0), _time_warning(0.0), -// _msg_channels(), _msg_sender(0), _msg_type(0), - _has_owner_view(has_owner_view), - _handle_c_updates(true), _want_message_bundling(true), - _bundling_msgs(0), - _in_quiet_zone(0) + _bundling_msgs(0) { #if defined(HAVE_NET) && defined(SIMULATE_NETWORK_DELAY) if (min_lag != 0.0 || max_lag != 0.0) { diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index 39719b385d..8de4bdd94e 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -2449,145 +2449,145 @@ _inP07ytw_15(PyObject *, PyObject *args) { static PyMethodDef python_simple_funcs[] = { - { "interrogate_add_search_directory", &_inP07yttbRf, METH_VARARGS }, - { "interrogate_add_search_path", &_inP07ytda_g, METH_VARARGS }, - { "interrogate_error_flag", &_inP07yt4RgX, METH_VARARGS }, - { "interrogate_number_of_manifests", &_inP07yt3Gip, METH_VARARGS }, - { "interrogate_get_manifest", &_inP07ytRKDz, METH_VARARGS }, - { "interrogate_get_manifest_by_name", &_inP07ytgZ9N, METH_VARARGS }, - { "interrogate_manifest_name", &_inP07ytFnRZ, METH_VARARGS }, - { "interrogate_manifest_definition", &_inP07ytg0Qv, METH_VARARGS }, - { "interrogate_manifest_has_type", &_inP07yttrqw, METH_VARARGS }, - { "interrogate_manifest_get_type", &_inP07ytdmpW, METH_VARARGS }, - { "interrogate_manifest_has_getter", &_inP07ytUYgQ, METH_VARARGS }, - { "interrogate_manifest_getter", &_inP07yt0k7F, METH_VARARGS }, - { "interrogate_manifest_has_int_value", &_inP07ytfIsr, METH_VARARGS }, - { "interrogate_manifest_get_int_value", &_inP07ytvysR, METH_VARARGS }, - { "interrogate_element_name", &_inP07ytYQ_2, METH_VARARGS }, - { "interrogate_element_scoped_name", &_inP07yt3kdv, METH_VARARGS }, - { "interrogate_element_has_comment", &_inP07ytew01, METH_VARARGS }, - { "interrogate_element_comment", &_inP07ytQna7, METH_VARARGS }, - { "interrogate_get_element_by_name", &_inP07ytkg95, METH_VARARGS }, - { "interrogate_get_element_by_scoped_name", &_inP07ytluRc, METH_VARARGS }, - { "interrogate_element_type", &_inP07yttHdM, METH_VARARGS }, - { "interrogate_element_has_getter", &_inP07ytDId0, METH_VARARGS }, - { "interrogate_element_getter", &_inP07ytHuAm, METH_VARARGS }, - { "interrogate_element_has_setter", &_inP07yt_xr0, METH_VARARGS }, - { "interrogate_element_setter", &_inP07ytH5qp, METH_VARARGS }, - { "interrogate_element_is_sequence", &_inP07ytq45U, METH_VARARGS }, - { "interrogate_element_is_mapping", &_inP07yt6IPa, METH_VARARGS }, - { "interrogate_number_of_globals", &_inP07ytU2_B, METH_VARARGS }, - { "interrogate_get_global", &_inP07ytHFO2, METH_VARARGS }, - { "interrogate_number_of_global_functions", &_inP07ytcfjm, METH_VARARGS }, - { "interrogate_get_global_function", &_inP07yt3Sjw, METH_VARARGS }, - { "interrogate_number_of_functions", &_inP07ytgJcX, METH_VARARGS }, - { "interrogate_get_function", &_inP07ytYlw6, METH_VARARGS }, - { "interrogate_function_name", &_inP07ytsmnz, METH_VARARGS }, - { "interrogate_function_scoped_name", &_inP07ytxQ10, METH_VARARGS }, - { "interrogate_function_has_comment", &_inP07yt6gPB, METH_VARARGS }, - { "interrogate_function_comment", &_inP07ytISgV, METH_VARARGS }, - { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS }, - { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS }, - { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS }, - { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS }, - { "interrogate_function_module_name", &_inP07ytwpYd, METH_VARARGS }, - { "interrogate_function_has_library_name", &_inP07ytOfNh, METH_VARARGS }, - { "interrogate_function_library_name", &_inP07ytf5_U, METH_VARARGS }, - { "interrogate_function_is_virtual", &_inP07ytL3ZB, METH_VARARGS }, - { "interrogate_function_number_of_c_wrappers", &_inP07ytXw0I, METH_VARARGS }, - { "interrogate_function_c_wrapper", &_inP07yt3zru, METH_VARARGS }, - { "interrogate_function_number_of_python_wrappers", &_inP07ytRrg2, METH_VARARGS }, - { "interrogate_function_python_wrapper", &_inP07ytEJCx, METH_VARARGS }, - { "interrogate_wrapper_name", &_inP07ytWAZr, METH_VARARGS }, - { "interrogate_wrapper_is_callable_by_name", &_inP07ytrD_M, METH_VARARGS }, - { "interrogate_wrapper_has_comment", &_inP07ytjolz, METH_VARARGS }, - { "interrogate_wrapper_comment", &_inP07ytt_JD, METH_VARARGS }, - { "interrogate_wrapper_has_return_value", &_inP07ytwEts, METH_VARARGS }, - { "interrogate_wrapper_return_type", &_inP07ytrJWs, METH_VARARGS }, - { "interrogate_wrapper_caller_manages_return_value", &_inP07ytpmFD, METH_VARARGS }, - { "interrogate_wrapper_return_value_destructor", &_inP07ytyYUX, METH_VARARGS }, - { "interrogate_wrapper_number_of_parameters", &_inP07yt54dn, METH_VARARGS }, - { "interrogate_wrapper_parameter_type", &_inP07ytGMpW, METH_VARARGS }, - { "interrogate_wrapper_parameter_has_name", &_inP07ytNuBV, METH_VARARGS }, - { "interrogate_wrapper_parameter_name", &_inP07yt9UwA, METH_VARARGS }, - { "interrogate_wrapper_parameter_is_this", &_inP07yt3FDt, METH_VARARGS }, - { "interrogate_wrapper_has_pointer", &_inP07ytf513, METH_VARARGS }, - { "interrogate_wrapper_pointer", &_inP07ytsqGH, METH_VARARGS }, - { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS }, - { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS }, - { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS }, - { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS }, - { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS }, - { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS }, - { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS }, - { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS }, - { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS }, - { "interrogate_get_global_type", &_inP07ytMT0z, METH_VARARGS }, - { "interrogate_number_of_types", &_inP07ytiW3v, METH_VARARGS }, - { "interrogate_get_type", &_inP07yt4Px8, METH_VARARGS }, - { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS }, - { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS }, - { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS }, - { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS }, - { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS }, - { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS }, - { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS }, - { "interrogate_type_is_nested", &_inP07ytXGxx, METH_VARARGS }, - { "interrogate_type_outer_class", &_inP07ytj04Z, METH_VARARGS }, - { "interrogate_type_has_comment", &_inP07ytEOv4, METH_VARARGS }, - { "interrogate_type_comment", &_inP07ytpCqJ, METH_VARARGS }, - { "interrogate_type_has_module_name", &_inP07yt_Pz3, METH_VARARGS }, - { "interrogate_type_module_name", &_inP07ytt_06, METH_VARARGS }, - { "interrogate_type_has_library_name", &_inP07ytmuPs, METH_VARARGS }, - { "interrogate_type_library_name", &_inP07ytvM8B, METH_VARARGS }, - { "interrogate_type_is_atomic", &_inP07ytap97, METH_VARARGS }, - { "interrogate_type_atomic_token", &_inP07yt0o8D, METH_VARARGS }, - { "interrogate_type_is_unsigned", &_inP07ytOoQ2, METH_VARARGS }, - { "interrogate_type_is_signed", &_inP07ytKuFh, METH_VARARGS }, - { "interrogate_type_is_long", &_inP07yto5L6, METH_VARARGS }, - { "interrogate_type_is_longlong", &_inP07ytzgKK, METH_VARARGS }, - { "interrogate_type_is_short", &_inP07yt0FIF, METH_VARARGS }, - { "interrogate_type_is_wrapped", &_inP07ytZqvD, METH_VARARGS }, - { "interrogate_type_is_pointer", &_inP07ytDyRd, METH_VARARGS }, - { "interrogate_type_is_const", &_inP07ytMnKa, METH_VARARGS }, - { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS }, - { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS }, - { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS }, - { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS }, - { "interrogate_type_enum_value_name", &_inP07ytbyPY, METH_VARARGS }, - { "interrogate_type_enum_value_scoped_name", &_inP07ytAaT6, METH_VARARGS }, - { "interrogate_type_enum_value_comment", &_inP07ytgL9q, METH_VARARGS }, - { "interrogate_type_enum_value", &_inP07ytWB97, METH_VARARGS }, - { "interrogate_type_is_struct", &_inP07ytDUAl, METH_VARARGS }, - { "interrogate_type_is_class", &_inP07yt1_Kf, METH_VARARGS }, - { "interrogate_type_is_union", &_inP07yt98lD, METH_VARARGS }, - { "interrogate_type_is_fully_defined", &_inP07yt9SHr, METH_VARARGS }, - { "interrogate_type_is_unpublished", &_inP07ytdiZP, METH_VARARGS }, - { "interrogate_type_number_of_constructors", &_inP07ytTdER, METH_VARARGS }, - { "interrogate_type_get_constructor", &_inP07ytYO56, METH_VARARGS }, - { "interrogate_type_has_destructor", &_inP07ytxtCG, METH_VARARGS }, - { "interrogate_type_destructor_is_inherited", &_inP07yt_EB2, METH_VARARGS }, - { "interrogate_type_get_destructor", &_inP07ytEG1l, METH_VARARGS }, - { "interrogate_type_number_of_elements", &_inP07yt7tUq, METH_VARARGS }, - { "interrogate_type_get_element", &_inP07ytyStU, METH_VARARGS }, - { "interrogate_type_number_of_methods", &_inP07ytdM85, METH_VARARGS }, - { "interrogate_type_get_method", &_inP07ytk_GN, METH_VARARGS }, - { "interrogate_type_number_of_make_seqs", &_inP07yt8QjG, METH_VARARGS }, - { "interrogate_type_get_make_seq", &_inP07ytyMtj, METH_VARARGS }, - { "interrogate_type_number_of_casts", &_inP07ytHDtN, METH_VARARGS }, - { "interrogate_type_get_cast", &_inP07ytHFjA, METH_VARARGS }, - { "interrogate_type_number_of_derivations", &_inP07yt_NPR, METH_VARARGS }, - { "interrogate_type_get_derivation", &_inP07ytcTOH, METH_VARARGS }, - { "interrogate_type_derivation_has_upcast", &_inP07ythdU7, METH_VARARGS }, - { "interrogate_type_get_upcast", &_inP07ytQPxU, METH_VARARGS }, - { "interrogate_type_derivation_downcast_is_impossible", &_inP07ytO7Pz, METH_VARARGS }, - { "interrogate_type_derivation_has_downcast", &_inP07ytvu_E, METH_VARARGS }, - { "interrogate_type_get_downcast", &_inP07ytxGUt, METH_VARARGS }, - { "interrogate_type_number_of_nested_types", &_inP07ytzM1P, METH_VARARGS }, - { "interrogate_type_get_nested_type", &_inP07ytoY5L, METH_VARARGS }, - { "interrogate_request_database", &_inP07yte_7S, METH_VARARGS }, - { "interrogate_request_module", &_inP07ytw_15, METH_VARARGS }, - { nullptr, nullptr } + { "interrogate_add_search_directory", &_inP07yttbRf, METH_VARARGS, nullptr }, + { "interrogate_add_search_path", &_inP07ytda_g, METH_VARARGS, nullptr }, + { "interrogate_error_flag", &_inP07yt4RgX, METH_VARARGS, nullptr }, + { "interrogate_number_of_manifests", &_inP07yt3Gip, METH_VARARGS, nullptr }, + { "interrogate_get_manifest", &_inP07ytRKDz, METH_VARARGS, nullptr }, + { "interrogate_get_manifest_by_name", &_inP07ytgZ9N, METH_VARARGS, nullptr }, + { "interrogate_manifest_name", &_inP07ytFnRZ, METH_VARARGS, nullptr }, + { "interrogate_manifest_definition", &_inP07ytg0Qv, METH_VARARGS, nullptr }, + { "interrogate_manifest_has_type", &_inP07yttrqw, METH_VARARGS, nullptr }, + { "interrogate_manifest_get_type", &_inP07ytdmpW, METH_VARARGS, nullptr }, + { "interrogate_manifest_has_getter", &_inP07ytUYgQ, METH_VARARGS, nullptr }, + { "interrogate_manifest_getter", &_inP07yt0k7F, METH_VARARGS, nullptr }, + { "interrogate_manifest_has_int_value", &_inP07ytfIsr, METH_VARARGS, nullptr }, + { "interrogate_manifest_get_int_value", &_inP07ytvysR, METH_VARARGS, nullptr }, + { "interrogate_element_name", &_inP07ytYQ_2, METH_VARARGS, nullptr }, + { "interrogate_element_scoped_name", &_inP07yt3kdv, METH_VARARGS, nullptr }, + { "interrogate_element_has_comment", &_inP07ytew01, METH_VARARGS, nullptr }, + { "interrogate_element_comment", &_inP07ytQna7, METH_VARARGS, nullptr }, + { "interrogate_get_element_by_name", &_inP07ytkg95, METH_VARARGS, nullptr }, + { "interrogate_get_element_by_scoped_name", &_inP07ytluRc, METH_VARARGS, nullptr }, + { "interrogate_element_type", &_inP07yttHdM, METH_VARARGS, nullptr }, + { "interrogate_element_has_getter", &_inP07ytDId0, METH_VARARGS, nullptr }, + { "interrogate_element_getter", &_inP07ytHuAm, METH_VARARGS, nullptr }, + { "interrogate_element_has_setter", &_inP07yt_xr0, METH_VARARGS, nullptr }, + { "interrogate_element_setter", &_inP07ytH5qp, METH_VARARGS, nullptr }, + { "interrogate_element_is_sequence", &_inP07ytq45U, METH_VARARGS, nullptr }, + { "interrogate_element_is_mapping", &_inP07yt6IPa, METH_VARARGS, nullptr }, + { "interrogate_number_of_globals", &_inP07ytU2_B, METH_VARARGS, nullptr }, + { "interrogate_get_global", &_inP07ytHFO2, METH_VARARGS, nullptr }, + { "interrogate_number_of_global_functions", &_inP07ytcfjm, METH_VARARGS, nullptr }, + { "interrogate_get_global_function", &_inP07yt3Sjw, METH_VARARGS, nullptr }, + { "interrogate_number_of_functions", &_inP07ytgJcX, METH_VARARGS, nullptr }, + { "interrogate_get_function", &_inP07ytYlw6, METH_VARARGS, nullptr }, + { "interrogate_function_name", &_inP07ytsmnz, METH_VARARGS, nullptr }, + { "interrogate_function_scoped_name", &_inP07ytxQ10, METH_VARARGS, nullptr }, + { "interrogate_function_has_comment", &_inP07yt6gPB, METH_VARARGS, nullptr }, + { "interrogate_function_comment", &_inP07ytISgV, METH_VARARGS, nullptr }, + { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS, nullptr }, + { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS, nullptr }, + { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS, nullptr }, + { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS, nullptr }, + { "interrogate_function_module_name", &_inP07ytwpYd, METH_VARARGS, nullptr }, + { "interrogate_function_has_library_name", &_inP07ytOfNh, METH_VARARGS, nullptr }, + { "interrogate_function_library_name", &_inP07ytf5_U, METH_VARARGS, nullptr }, + { "interrogate_function_is_virtual", &_inP07ytL3ZB, METH_VARARGS, nullptr }, + { "interrogate_function_number_of_c_wrappers", &_inP07ytXw0I, METH_VARARGS, nullptr }, + { "interrogate_function_c_wrapper", &_inP07yt3zru, METH_VARARGS, nullptr }, + { "interrogate_function_number_of_python_wrappers", &_inP07ytRrg2, METH_VARARGS, nullptr }, + { "interrogate_function_python_wrapper", &_inP07ytEJCx, METH_VARARGS, nullptr }, + { "interrogate_wrapper_name", &_inP07ytWAZr, METH_VARARGS, nullptr }, + { "interrogate_wrapper_is_callable_by_name", &_inP07ytrD_M, METH_VARARGS, nullptr }, + { "interrogate_wrapper_has_comment", &_inP07ytjolz, METH_VARARGS, nullptr }, + { "interrogate_wrapper_comment", &_inP07ytt_JD, METH_VARARGS, nullptr }, + { "interrogate_wrapper_has_return_value", &_inP07ytwEts, METH_VARARGS, nullptr }, + { "interrogate_wrapper_return_type", &_inP07ytrJWs, METH_VARARGS, nullptr }, + { "interrogate_wrapper_caller_manages_return_value", &_inP07ytpmFD, METH_VARARGS, nullptr }, + { "interrogate_wrapper_return_value_destructor", &_inP07ytyYUX, METH_VARARGS, nullptr }, + { "interrogate_wrapper_number_of_parameters", &_inP07yt54dn, METH_VARARGS, nullptr }, + { "interrogate_wrapper_parameter_type", &_inP07ytGMpW, METH_VARARGS, nullptr }, + { "interrogate_wrapper_parameter_has_name", &_inP07ytNuBV, METH_VARARGS, nullptr }, + { "interrogate_wrapper_parameter_name", &_inP07yt9UwA, METH_VARARGS, nullptr }, + { "interrogate_wrapper_parameter_is_this", &_inP07yt3FDt, METH_VARARGS, nullptr }, + { "interrogate_wrapper_has_pointer", &_inP07ytf513, METH_VARARGS, nullptr }, + { "interrogate_wrapper_pointer", &_inP07ytsqGH, METH_VARARGS, nullptr }, + { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS, nullptr }, + { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS, nullptr }, + { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS, nullptr }, + { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS, nullptr }, + { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS, nullptr }, + { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS, nullptr }, + { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS, nullptr }, + { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS, nullptr }, + { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS, nullptr }, + { "interrogate_get_global_type", &_inP07ytMT0z, METH_VARARGS, nullptr }, + { "interrogate_number_of_types", &_inP07ytiW3v, METH_VARARGS, nullptr }, + { "interrogate_get_type", &_inP07yt4Px8, METH_VARARGS, nullptr }, + { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS, nullptr }, + { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS, nullptr }, + { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS, nullptr }, + { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS, nullptr }, + { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS, nullptr }, + { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS, nullptr }, + { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS, nullptr }, + { "interrogate_type_is_nested", &_inP07ytXGxx, METH_VARARGS, nullptr }, + { "interrogate_type_outer_class", &_inP07ytj04Z, METH_VARARGS, nullptr }, + { "interrogate_type_has_comment", &_inP07ytEOv4, METH_VARARGS, nullptr }, + { "interrogate_type_comment", &_inP07ytpCqJ, METH_VARARGS, nullptr }, + { "interrogate_type_has_module_name", &_inP07yt_Pz3, METH_VARARGS, nullptr }, + { "interrogate_type_module_name", &_inP07ytt_06, METH_VARARGS, nullptr }, + { "interrogate_type_has_library_name", &_inP07ytmuPs, METH_VARARGS, nullptr }, + { "interrogate_type_library_name", &_inP07ytvM8B, METH_VARARGS, nullptr }, + { "interrogate_type_is_atomic", &_inP07ytap97, METH_VARARGS, nullptr }, + { "interrogate_type_atomic_token", &_inP07yt0o8D, METH_VARARGS, nullptr }, + { "interrogate_type_is_unsigned", &_inP07ytOoQ2, METH_VARARGS, nullptr }, + { "interrogate_type_is_signed", &_inP07ytKuFh, METH_VARARGS, nullptr }, + { "interrogate_type_is_long", &_inP07yto5L6, METH_VARARGS, nullptr }, + { "interrogate_type_is_longlong", &_inP07ytzgKK, METH_VARARGS, nullptr }, + { "interrogate_type_is_short", &_inP07yt0FIF, METH_VARARGS, nullptr }, + { "interrogate_type_is_wrapped", &_inP07ytZqvD, METH_VARARGS, nullptr }, + { "interrogate_type_is_pointer", &_inP07ytDyRd, METH_VARARGS, nullptr }, + { "interrogate_type_is_const", &_inP07ytMnKa, METH_VARARGS, nullptr }, + { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS, nullptr }, + { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS, nullptr }, + { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS, nullptr }, + { "interrogate_type_enum_value_name", &_inP07ytbyPY, METH_VARARGS, nullptr }, + { "interrogate_type_enum_value_scoped_name", &_inP07ytAaT6, METH_VARARGS, nullptr }, + { "interrogate_type_enum_value_comment", &_inP07ytgL9q, METH_VARARGS, nullptr }, + { "interrogate_type_enum_value", &_inP07ytWB97, METH_VARARGS, nullptr }, + { "interrogate_type_is_struct", &_inP07ytDUAl, METH_VARARGS, nullptr }, + { "interrogate_type_is_class", &_inP07yt1_Kf, METH_VARARGS, nullptr }, + { "interrogate_type_is_union", &_inP07yt98lD, METH_VARARGS, nullptr }, + { "interrogate_type_is_fully_defined", &_inP07yt9SHr, METH_VARARGS, nullptr }, + { "interrogate_type_is_unpublished", &_inP07ytdiZP, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_constructors", &_inP07ytTdER, METH_VARARGS, nullptr }, + { "interrogate_type_get_constructor", &_inP07ytYO56, METH_VARARGS, nullptr }, + { "interrogate_type_has_destructor", &_inP07ytxtCG, METH_VARARGS, nullptr }, + { "interrogate_type_destructor_is_inherited", &_inP07yt_EB2, METH_VARARGS, nullptr }, + { "interrogate_type_get_destructor", &_inP07ytEG1l, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_elements", &_inP07yt7tUq, METH_VARARGS, nullptr }, + { "interrogate_type_get_element", &_inP07ytyStU, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_methods", &_inP07ytdM85, METH_VARARGS, nullptr }, + { "interrogate_type_get_method", &_inP07ytk_GN, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_make_seqs", &_inP07yt8QjG, METH_VARARGS, nullptr }, + { "interrogate_type_get_make_seq", &_inP07ytyMtj, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_casts", &_inP07ytHDtN, METH_VARARGS, nullptr }, + { "interrogate_type_get_cast", &_inP07ytHFjA, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_derivations", &_inP07yt_NPR, METH_VARARGS, nullptr }, + { "interrogate_type_get_derivation", &_inP07ytcTOH, METH_VARARGS, nullptr }, + { "interrogate_type_derivation_has_upcast", &_inP07ythdU7, METH_VARARGS, nullptr }, + { "interrogate_type_get_upcast", &_inP07ytQPxU, METH_VARARGS, nullptr }, + { "interrogate_type_derivation_downcast_is_impossible", &_inP07ytO7Pz, METH_VARARGS, nullptr }, + { "interrogate_type_derivation_has_downcast", &_inP07ytvu_E, METH_VARARGS, nullptr }, + { "interrogate_type_get_downcast", &_inP07ytxGUt, METH_VARARGS, nullptr }, + { "interrogate_type_number_of_nested_types", &_inP07ytzM1P, METH_VARARGS, nullptr }, + { "interrogate_type_get_nested_type", &_inP07ytoY5L, METH_VARARGS, nullptr }, + { "interrogate_request_database", &_inP07yte_7S, METH_VARARGS, nullptr }, + { "interrogate_request_module", &_inP07ytw_15, METH_VARARGS, nullptr }, + { nullptr, nullptr, 0, nullptr } }; #if PY_MAJOR_VERSION >= 3 diff --git a/dtool/src/cppparser/cppManifest.cxx b/dtool/src/cppparser/cppManifest.cxx index ae8784cdf9..197213df36 100644 --- a/dtool/src/cppparser/cppManifest.cxx +++ b/dtool/src/cppparser/cppManifest.cxx @@ -226,8 +226,8 @@ output(std::ostream &out) const { out << "$1"; } - for (int i = 1; i < _num_parameters; ++i) { - if (_variadic_param == i) { + for (size_t i = 1; i < _num_parameters; ++i) { + if (_variadic_param == (int)i) { out << ", ..."; } else { out << ", $" << i + 1; diff --git a/dtool/src/cppparser/cppManifest.h b/dtool/src/cppparser/cppManifest.h index 486b287728..3fe0f1b4a1 100644 --- a/dtool/src/cppparser/cppManifest.h +++ b/dtool/src/cppparser/cppManifest.h @@ -43,7 +43,7 @@ public: std::string _name; bool _has_parameters; - int _num_parameters; + size_t _num_parameters; int _variadic_param; cppyyltype _loc; CPPExpression *_expr; diff --git a/dtool/src/cppparser/cppNamespace.cxx b/dtool/src/cppparser/cppNamespace.cxx index 9a6f608f49..a72f4b093d 100644 --- a/dtool/src/cppparser/cppNamespace.cxx +++ b/dtool/src/cppparser/cppNamespace.cxx @@ -22,9 +22,9 @@ CPPNamespace:: CPPNamespace(CPPIdentifier *ident, CPPScope *scope, const CPPFile &file) : CPPDeclaration(file), + _is_inline(false), _ident(ident), - _scope(scope), - _is_inline(false) + _scope(scope) { } diff --git a/dtool/src/cppparser/cppStructType.cxx b/dtool/src/cppparser/cppStructType.cxx index 745abab586..e08d4a4a4e 100644 --- a/dtool/src/cppparser/cppStructType.cxx +++ b/dtool/src/cppparser/cppStructType.cxx @@ -58,8 +58,8 @@ CPPStructType(const CPPStructType ©) : CPPExtensionType(copy), _scope(copy._scope), _incomplete(copy._incomplete), - _derivation(copy._derivation), - _final(copy._final) + _final(copy._final), + _derivation(copy._derivation) { _subst_decl_recursive_protect = false; } diff --git a/dtool/src/dtoolbase/patomic.I b/dtool/src/dtoolbase/patomic.I index cb6e1880b6..80ebcd4de3 100644 --- a/dtool/src/dtoolbase/patomic.I +++ b/dtool/src/dtoolbase/patomic.I @@ -356,7 +356,7 @@ clear(std::memory_order order) noexcept { */ ALWAYS_INLINE bool patomic_flag:: test_and_set(std::memory_order order) noexcept { - return (bool)_value.exchange(1u, order); + return _value.exchange(1u, order) != 0u; } /** @@ -364,7 +364,7 @@ test_and_set(std::memory_order order) noexcept { */ ALWAYS_INLINE bool patomic_flag:: test(std::memory_order order) const noexcept { - return (bool)_value.load(order); + return _value.load(order) != 0u; } /** diff --git a/dtool/src/dtoolbase/typeHandle_ext.cxx b/dtool/src/dtoolbase/typeHandle_ext.cxx index be4b1e654c..cefe62e154 100644 --- a/dtool/src/dtoolbase/typeHandle_ext.cxx +++ b/dtool/src/dtoolbase/typeHandle_ext.cxx @@ -38,7 +38,6 @@ make(PyTypeObject *tp) { PyObject *Extension:: __reduce__() const { extern struct Dtool_PyTypedObject Dtool_TypeHandle; - extern struct Dtool_PyTypedObject Dtool_TypeRegistry; if (!*_this) { PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_TypeHandle, "none"); diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index 0d4d56dfc1..867ec25ac2 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -77,8 +77,8 @@ InterfaceMaker::Function:: */ InterfaceMaker::MakeSeq:: MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) : - _name(name), _imake_seq(imake_seq), + _name(name), _length_getter(nullptr), _element_getter(nullptr) { diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 45ecf1b0fc..a617ba4e76 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -2510,11 +2510,11 @@ write_module_class(ostream &out, Object *obj) { out << " if (arg2 != nullptr && arg2 != Py_None) {\n"; out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n"; write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4, - true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true); + true, true, AT_varargs, return_flags | RF_decref_args, true); out << " Py_DECREF(args);\n"; out << " } else {\n"; write_function_forset(out, one_param_remaps, 1, 1, expected_params, 4, - true, true, AT_single_arg, RF_pyobject | RF_err_null, true); + true, true, AT_single_arg, return_flags, true); out << " }\n\n"; out << " if (!_PyErr_OCCURRED()) {\n"; diff --git a/dtool/src/interrogate/interfaceMakerPythonObj.cxx b/dtool/src/interrogate/interfaceMakerPythonObj.cxx index fa0e35eaea..4182b0c9a1 100644 --- a/dtool/src/interrogate/interfaceMakerPythonObj.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonObj.cxx @@ -102,10 +102,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { ++fi) { Function *func = (*fi); out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name - << ", METH_VARARGS },\n"; + << ", METH_VARARGS, nullptr },\n"; } } - out << " { nullptr, nullptr }\n" + out << " { nullptr, nullptr, 0, nullptr }\n" << "};\n\n" << "#if PY_MAJOR_VERSION >= 3\n" diff --git a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx b/dtool/src/interrogate/interfaceMakerPythonSimple.cxx index 8a160216e8..cda042efd7 100644 --- a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonSimple.cxx @@ -89,10 +89,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); out << " { \"" << remap->_reported_name << "\", &" - << remap->_wrapper_name << ", METH_VARARGS },\n"; + << remap->_wrapper_name << ", METH_VARARGS, nullptr },\n"; } } - out << " { nullptr, nullptr }\n" + out << " { nullptr, nullptr, 0, nullptr }\n" << "};\n\n" << "#if PY_MAJOR_VERSION >= 3\n" diff --git a/panda/src/bullet/bulletConvexPointCloudShape.I b/panda/src/bullet/bulletConvexPointCloudShape.I index 47e0b62704..a3e7d00e18 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.I +++ b/panda/src/bullet/bulletConvexPointCloudShape.I @@ -16,8 +16,8 @@ */ INLINE BulletConvexPointCloudShape:: BulletConvexPointCloudShape() : - _scale(1), - _shape(nullptr) { + _shape(nullptr), + _scale(1) { } /** diff --git a/panda/src/bullet/bulletTriangleMeshShape.cxx b/panda/src/bullet/bulletTriangleMeshShape.cxx index 688bccd478..77fe9ddcbb 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.cxx +++ b/panda/src/bullet/bulletTriangleMeshShape.cxx @@ -29,9 +29,9 @@ TypeHandle BulletTriangleMeshShape::_type_handle; */ BulletTriangleMeshShape:: BulletTriangleMeshShape() : - _mesh(nullptr), - _gimpact_shape(nullptr), _bvh_shape(nullptr), + _gimpact_shape(nullptr), + _mesh(nullptr), _dynamic(false), _compress(false), _bvh(false) { diff --git a/panda/src/chan/animControl.cxx b/panda/src/chan/animControl.cxx index df7fb26498..dd7cb78c5d 100644 --- a/panda/src/chan/animControl.cxx +++ b/panda/src/chan/animControl.cxx @@ -32,8 +32,8 @@ AnimControl(const std::string &name, PartBundle *part, Namable(name), _pending_lock(name), _pending_cvar(_pending_lock), - _bound_joints(BitArray::all_on()), - _part(part) + _part(part), + _bound_joints(BitArray::all_on()) { #ifdef DO_MEMORY_USAGE MemoryUsage::update_type(this, get_class_type()); diff --git a/panda/src/char/character.cxx b/panda/src/char/character.cxx index bf16135484..eef46d4713 100644 --- a/panda/src/char/character.cxx +++ b/panda/src/char/character.cxx @@ -38,16 +38,16 @@ PStatCollector Character::_animation_pcollector("*:Animation"); Character:: Character(const Character ©, bool copy_bundles) : PartBundleNode(copy), + _last_auto_update(-1.0), + _view_frame(-1), + _view_distance2(0.0f), _lod_center(copy._lod_center), _lod_far_distance(copy._lod_far_distance), _lod_near_distance(copy._lod_near_distance), _lod_delay_factor(copy._lod_delay_factor), _do_lod_animation(copy._do_lod_animation), _joints_pcollector(copy._joints_pcollector), - _skinning_pcollector(copy._skinning_pcollector), - _last_auto_update(-1.0), - _view_frame(-1), - _view_distance2(0.0f) + _skinning_pcollector(copy._skinning_pcollector) { set_cull_callback(); @@ -75,11 +75,11 @@ Character(const Character ©, bool copy_bundles) : Character:: Character(const std::string &name) : PartBundleNode(name, new CharacterJointBundle(name)), - _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"), - _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices"), _last_auto_update(-1.0), _view_frame(-1), - _view_distance2(0.0f) + _view_distance2(0.0f), + _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"), + _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices") { set_cull_callback(); clear_lod_animation(); diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index e44f8834bf..278f8516ff 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -506,7 +506,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const { LParabola local_p(parabola->get_parabola()); local_p.xform(wrt_mat); - PN_stdfloat t = INT_MAX; + PN_stdfloat t = FLT_MAX; PN_stdfloat t1, t2; int intersecting_face = -1; for (int i = 0; i < get_num_planes(); i++) { diff --git a/panda/src/collide/collisionHandlerEvent.I b/panda/src/collide/collisionHandlerEvent.I index 13303de921..217978e117 100644 --- a/panda/src/collide/collisionHandlerEvent.I +++ b/panda/src/collide/collisionHandlerEvent.I @@ -25,15 +25,6 @@ operator () (const PT(CollisionEntry) &a, return a->get_into_node_path() < b->get_into_node_path(); } -/** - * The assignment operator does absolutely nothing, since this is just a - * function object class that stores no data. We define it just to quiet up - * g++ in -Wall mode. - */ -INLINE void CollisionHandlerEvent::SortEntries:: -operator = (const CollisionHandlerEvent::SortEntries &) { -} - /** * Removes all of the previously-added in patterns. See add_in_pattern. */ diff --git a/panda/src/collide/collisionHandlerEvent.h b/panda/src/collide/collisionHandlerEvent.h index e0f8e30997..115ad520b6 100644 --- a/panda/src/collide/collisionHandlerEvent.h +++ b/panda/src/collide/collisionHandlerEvent.h @@ -90,7 +90,6 @@ protected: INLINE bool operator () (const PT(CollisionEntry) &a, const PT(CollisionEntry) &b) const; - INLINE void operator = (const SortEntries &other); }; typedef pset Colliding; diff --git a/panda/src/collide/collisionHandlerPhysical_ext.cxx b/panda/src/collide/collisionHandlerPhysical_ext.cxx index 7539f3f847..023d7ba718 100644 --- a/panda/src/collide/collisionHandlerPhysical_ext.cxx +++ b/panda/src/collide/collisionHandlerPhysical_ext.cxx @@ -21,7 +21,6 @@ */ PyObject *Extension:: __reduce__(PyObject *self) const { - extern struct Dtool_PyTypedObject Dtool_Datagram; extern struct Dtool_PyTypedObject Dtool_NodePath; // Create a tuple with all the NodePath pointers. diff --git a/panda/src/collide/collisionLevelStateBase.I b/panda/src/collide/collisionLevelStateBase.I index 5b649838f7..0410a4f229 100644 --- a/panda/src/collide/collisionLevelStateBase.I +++ b/panda/src/collide/collisionLevelStateBase.I @@ -31,8 +31,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child) _node_path(parent._node_path, child), _colliders(parent._colliders), _include_mask(parent._include_mask), - _local_bounds(parent._local_bounds), - _node_gbv(child->get_bounds()->as_geometric_bounding_volume()) + _node_gbv(child->get_bounds()->as_geometric_bounding_volume()), + _local_bounds(parent._local_bounds) { } @@ -44,8 +44,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, const PandaNode:: _node_path(parent._node_path, child.get_child()), _colliders(parent._colliders), _include_mask(parent._include_mask), - _local_bounds(parent._local_bounds), - _node_gbv(child.get_bounds()) + _node_gbv(child.get_bounds()), + _local_bounds(parent._local_bounds) { } @@ -57,9 +57,9 @@ CollisionLevelStateBase(const CollisionLevelStateBase ©) : _node_path(copy._node_path), _colliders(copy._colliders), _include_mask(copy._include_mask), + _node_gbv(copy._node_gbv), _local_bounds(copy._local_bounds), - _parent_bounds(copy._parent_bounds), - _node_gbv(copy._node_gbv) + _parent_bounds(copy._parent_bounds) { } @@ -71,9 +71,9 @@ operator = (const CollisionLevelStateBase ©) { _node_path = copy._node_path; _colliders = copy._colliders; _include_mask = copy._include_mask; + _node_gbv = copy._node_gbv; _local_bounds = copy._local_bounds; _parent_bounds = copy._parent_bounds; - _node_gbv = copy._node_gbv; } /** diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index 23459d88f3..50f187ad77 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -647,7 +647,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) { // child. int index = node->get_visible_child(); PandaNode::Children children = node->get_children(); - if (index >= 0 && index < children.get_num_children()) { + if (index >= 0 && (size_t)index < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(index); CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child); if (!mask.is_zero()) { @@ -856,7 +856,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) { // child. int index = node->get_visible_child(); PandaNode::Children children = node->get_children(); - if (index >= 0 && index < children.get_num_children()) { + if (index >= 0 && (size_t)index < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(index); CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child); if (!mask.is_zero()) { @@ -1065,7 +1065,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) { // child. int index = node->get_visible_child(); PandaNode::Children children = node->get_children(); - if (index >= 0 && index < children.get_num_children()) { + if (index >= 0 && (size_t)index < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(index); CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child); if (!mask.is_zero()) { diff --git a/panda/src/collide/collisionTraverser_ext.cxx b/panda/src/collide/collisionTraverser_ext.cxx index a982f41029..f297b289a8 100644 --- a/panda/src/collide/collisionTraverser_ext.cxx +++ b/panda/src/collide/collisionTraverser_ext.cxx @@ -21,7 +21,6 @@ PyObject *Extension:: __getstate__() const { extern struct Dtool_PyTypedObject Dtool_CollisionHandler; - extern struct Dtool_PyTypedObject Dtool_CollisionTraverser; extern struct Dtool_PyTypedObject Dtool_NodePath; const std::string &name = _this->get_name(); diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx index 05507086b1..f43fc3c03d 100644 --- a/panda/src/device/evdevInputDevice.cxx +++ b/panda/src/device/evdevInputDevice.cxx @@ -126,8 +126,9 @@ TypeHandle EvdevInputDevice::_type_handle; EvdevInputDevice:: EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) : _manager(manager), - _index(index), _fd(-1), + _quirks(0), + _index(index), _can_write(false), _ff_id(-1), _ff_playing(false), @@ -138,9 +139,8 @@ EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) : _dpad_left_button(-1), _dpad_up_button(-1), _ltrigger_code(-1), - _rtrigger_code(-1), - _quirks(0) { - + _rtrigger_code(-1) +{ char path[64]; sprintf(path, "/dev/input/event%zd", index); diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index b29d490d03..84c852d472 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -731,6 +731,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, */ DisplayRegion::CData:: CData() : + _depth_range(0, 1), _lens_index(0), _camera_node(nullptr), _active(true), @@ -738,8 +739,7 @@ CData() : _stereo_channel(Lens::SC_mono), _tex_view_offset(0), _target_tex_page(-1), - _scissor_enabled(true), - _depth_range(0, 1) + _scissor_enabled(true) { _regions.push_back(Region()); } @@ -750,6 +750,7 @@ CData() : DisplayRegion::CData:: CData(const DisplayRegion::CData ©) : _regions(copy._regions), + _depth_range(copy._depth_range), _lens_index(copy._lens_index), _camera(copy._camera), _camera_node(copy._camera_node), @@ -758,8 +759,7 @@ CData(const DisplayRegion::CData ©) : _stereo_channel(copy._stereo_channel), _tex_view_offset(copy._tex_view_offset), _target_tex_page(copy._target_tex_page), - _scissor_enabled(copy._scissor_enabled), - _depth_range(copy._depth_range) + _scissor_enabled(copy._scissor_enabled) { } diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 58d4099a45..e6b27bee36 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -75,10 +75,10 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsOutput *host, bool default_stereo_flags) : _lock("GraphicsOutput"), + _size(0, 0), _cull_window_pcollector(_cull_pcollector, name), _draw_window_pcollector(_draw_pcollector, name), - _clear_window_pcollector(_draw_window_pcollector, "Clear"), - _size(0, 0) + _clear_window_pcollector(_draw_window_pcollector, "Clear") { #ifdef DO_MEMORY_USAGE MemoryUsage::update_type(this, this); diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index d1c87c3ab2..89d4e7b73f 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -2035,7 +2035,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2058,7 +2058,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2087,7 +2087,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2117,7 +2117,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2140,7 +2140,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2170,7 +2170,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -2191,7 +2191,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, { const TextureAttrib *texattrib; if (_target_rs->get_attrib(texattrib)) { - size_t si = 0; + int si = 0; for (int i = 0; i < texattrib->get_num_on_stages(); ++i) { TextureStage *stage = texattrib->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index f1808dfcd9..e81a1a944d 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -39,8 +39,8 @@ GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsOutput *host) : GraphicsOutput(engine, pipe, name, fb_prop, win_prop, flags, gsg, host, true), _input_lock("GraphicsWindow::_input_lock"), - _properties_lock("GraphicsWindow::_properties_lock"), - _latency_pcollector(name + " latency") + _latency_pcollector(name + " latency"), + _properties_lock("GraphicsWindow::_properties_lock") { #ifdef DO_MEMORY_USAGE MemoryUsage::update_type(this, this); diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index 10b66762a9..fd0ff83fcf 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -37,11 +37,11 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, _num_components(num_components), _numeric_type(numeric_type), _contents(contents), - _munge_color(false), - _munge_color_scale(false), _auto_shader(false), _shader_skinning(false), - _remove_material(false) + _remove_material(false), + _munge_color(false), + _munge_color_scale(false) { const ShaderAttrib *shader_attrib; state->get_attrib_def(shader_attrib); diff --git a/panda/src/egg/eggMesherEdge.I b/panda/src/egg/eggMesherEdge.I index dbd37f0b70..4b8d74e4cf 100644 --- a/panda/src/egg/eggMesherEdge.I +++ b/panda/src/egg/eggMesherEdge.I @@ -20,18 +20,6 @@ EggMesherEdge(int vi_a, int vi_b) : _vi_a(vi_a), _vi_b(vi_b) { _opposite = nullptr; } -/** - * - */ -INLINE EggMesherEdge:: -EggMesherEdge(const EggMesherEdge ©) : - _vi_a(copy._vi_a), - _vi_b(copy._vi_b), - _strips(copy._strips), - _opposite(copy._opposite) -{ -} - /** * Returns true if the edge contains the indicated vertex index, false * otherwise. diff --git a/panda/src/egg/eggMesherEdge.h b/panda/src/egg/eggMesherEdge.h index 17e463b79d..6be14550fe 100644 --- a/panda/src/egg/eggMesherEdge.h +++ b/panda/src/egg/eggMesherEdge.h @@ -29,7 +29,6 @@ class EggMesherStrip; class EXPCL_PANDA_EGG EggMesherEdge { public: INLINE EggMesherEdge(int vi_a, int vi_b); - INLINE EggMesherEdge(const EggMesherEdge ©); void remove(EggMesherStrip *strip); void change_strip(EggMesherStrip *from, EggMesherStrip *to); diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index c0c690f122..db431a8eb2 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -803,7 +803,7 @@ convert_primitive(const GeomVertexData *vertex_data, const TexMatrixAttrib *tma = nullptr; net_state->get_attrib(tma); - for (size_t i = 0; i < ta->get_num_on_stages(); ++i) { + for (size_t i = 0; i < (size_t)ta->get_num_on_stages(); ++i) { TextureStage *tex_stage = ta->get_on_stage(i); EggTexture *egg_tex = get_egg_texture(ta->get_on_texture(tex_stage)); diff --git a/panda/src/event/eventHandler.I b/panda/src/event/eventHandler.I index eda0b022ea..b9458af08e 100644 --- a/panda/src/event/eventHandler.I +++ b/panda/src/event/eventHandler.I @@ -19,7 +19,7 @@ INLINE EventHandler *EventHandler:: get_global_event_handler(EventQueue *queue) { // The event queue parameter is present for now, for backward compatibility, // but it is ignored. - if (_global_event_handler == 0) { + if (_global_event_handler == nullptr) { make_global_event_handler(); } return _global_event_handler; diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index d9c28b4611..78e739243c 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -40,13 +40,13 @@ PythonTask(PyObject *func_or_coro, const std::string &name) : _args(nullptr), _upon_death(nullptr), _owner(nullptr), - _registered_to_owner(false), _exception(nullptr), _exc_value(nullptr), _exc_traceback(nullptr), _generator(nullptr), _fut_waiter(nullptr), _ignore_return(false), + _registered_to_owner(false), _retrieved_exception(false) { nassertv(func_or_coro != nullptr); diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I index 0344ed3839..dd7e39274c 100644 --- a/panda/src/express/referenceCount.I +++ b/panda/src/express/referenceCount.I @@ -28,8 +28,8 @@ TypeHandle RefCountObj::_type_handle; */ INLINE ReferenceCount:: ReferenceCount() : - _weak_list(nullptr), - _ref_count(0) { + _ref_count(0), + _weak_list(nullptr) { #ifdef DO_MEMORY_USAGE MemoryUsage::record_pointer(this); #endif @@ -45,8 +45,8 @@ ReferenceCount() : */ INLINE ReferenceCount:: ReferenceCount(const ReferenceCount &) : - _weak_list(nullptr), - _ref_count(0) { + _ref_count(0), + _weak_list(nullptr) { #ifdef DO_MEMORY_USAGE MemoryUsage::record_pointer(this); #endif diff --git a/panda/src/express/zipArchive.cxx b/panda/src/express/zipArchive.cxx index fb232a7b40..94fa0a2533 100644 --- a/panda/src/express/zipArchive.cxx +++ b/panda/src/express/zipArchive.cxx @@ -1461,7 +1461,6 @@ read_index() { uint64_t cdir_entries = 0; uint64_t cdir_offset = 0; - uint64_t cdir_size = 0; uint32_t comment_length = 0; std::streampos eocd_offset = 0; bool found = false; @@ -1482,7 +1481,7 @@ read_index() { eocd_offset = read->tellg() - (std::streamoff)4; reader.skip_bytes(6); cdir_entries = reader.get_uint16(); - cdir_size = reader.get_uint32(); + /*cdir_size = */reader.get_uint32(); cdir_offset = reader.get_uint32(); if (comment_length > 0) { _comment = reader.get_fixed_string(comment_length); @@ -1526,7 +1525,7 @@ read_index() { if (reader.get_uint32() == 0x06064b50) { reader.skip_bytes(20); cdir_entries = reader.get_uint64(); - cdir_size = reader.get_uint64(); + /*cdir_size = */reader.get_uint64(); cdir_offset = reader.get_uint64(); } else { express_cat.info() @@ -1702,9 +1701,9 @@ read_index(std::istream &read) { return false; } - uint16_t version = reader.get_uint8(); + /*uint16_t version = */reader.get_uint8(); _system = reader.get_uint8(); - uint16_t min_version = reader.get_uint16(); + /*uint16_t min_version = */reader.get_uint16(); _flags = reader.get_uint16(); _compression_method = (CompressionMethod)reader.get_uint16(); { diff --git a/panda/src/ffmpeg/ffmpegAudioCursor.cxx b/panda/src/ffmpeg/ffmpegAudioCursor.cxx index 47a00fbee9..8d2c6d2aec 100644 --- a/panda/src/ffmpeg/ffmpegAudioCursor.cxx +++ b/panda/src/ffmpeg/ffmpegAudioCursor.cxx @@ -50,10 +50,10 @@ FfmpegAudioCursor(FfmpegAudio *src) : _packet_data(nullptr), _format_ctx(nullptr), _audio_ctx(nullptr), - _resample_ctx(nullptr), + _frame(nullptr), _buffer(nullptr), _buffer_alloc(nullptr), - _frame(nullptr) + _resample_ctx(nullptr) { if (!_ffvfile.open_vfs(_filename)) { cleanup(); diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index 66d7fcdd86..aa47af9163 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -47,6 +47,7 @@ FfmpegVideoCursor:: FfmpegVideoCursor() : _max_readahead_frames(0), _thread_priority(ffmpeg_thread_priority), + _pixel_format((int)AV_PIX_FMT_NONE), _lock("FfmpegVideoCursor::_lock"), _action_cvar(_lock), _thread_status(TS_stopped), @@ -55,7 +56,6 @@ FfmpegVideoCursor() : _format_ctx(nullptr), _video_ctx(nullptr), _convert_ctx(nullptr), - _pixel_format((int)AV_PIX_FMT_NONE), _video_index(-1), _frame(nullptr), _frame_out(nullptr), diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 289ca4530a..c8641ebe6e 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -30,12 +30,12 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe, GraphicsStateGuardian *gsg, GraphicsOutput *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"), _requested_multisamples(0), _requested_coverage_samples(0), - _rb_context(nullptr) + _rb_context(nullptr), + _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"), + _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"), + _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples") { // A FBO doesn't have a back buffer. _draw_buffer_type = RenderBuffer::T_front; diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 9d2fbd71e8..520f2478e3 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2689,7 +2689,9 @@ update_shader_texture_bindings(ShaderContext *prev) { return; } +#ifndef OPENGLES GLbitfield barriers = 0; +#endif // First bind all the 'image units'; a bit of an esoteric OpenGL feature // right now. @@ -2738,9 +2740,11 @@ update_shader_texture_bindings(ShaderContext *prev) { _glgsg->update_texture(gtc, true); gl_tex = gtc->_index; +#ifndef OPENGLES if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) { barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; } +#endif } } input._writable = false; @@ -2858,6 +2862,12 @@ update_shader_texture_bindings(ShaderContext *prev) { << "Sampler type of GLSL shader input p3d_LightSource[" << spec._stage << "].shadowMap does not match type of texture " << *tex << ".\n"; break; + + default: + GLCAT.error() + << "Sampler type of GLSL shader input does not match type of " + "texture " << *tex << ".\n"; + break; } // TODO: also check whether shadow sampler textures have shadow filter // enabled. diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index 75a9a5db20..ae45b50f56 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -83,8 +83,8 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format, GeomVertexArrayData::UsageHint usage_hint) : SimpleLruPage(0), _array_format(array_format), - _cycler(CData(usage_hint)), - _contexts(nullptr) + _contexts(nullptr), + _cycler(CData(usage_hint)) { set_lru_size(0); nassertv(_array_format->is_registered()); @@ -98,8 +98,8 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : CopyOnWriteObject(copy), SimpleLruPage(copy), _array_format(copy._array_format), - _cycler(copy._cycler), - _contexts(nullptr) + _contexts(nullptr), + _cycler(copy._cycler) { copy.mark_used_lru(); diff --git a/panda/src/gobj/geomVertexColumn.I b/panda/src/gobj/geomVertexColumn.I index b382071eac..042a95ce5a 100644 --- a/panda/src/gobj/geomVertexColumn.I +++ b/panda/src/gobj/geomVertexColumn.I @@ -30,11 +30,11 @@ GeomVertexColumn(CPT_InternalName name, int num_components, int element_stride) : _name(std::move(name)), _num_components(num_components), + _num_elements(num_elements), _numeric_type(numeric_type), _contents(contents), _start(start), _column_alignment(column_alignment), - _num_elements(num_elements), _element_stride(element_stride), _packer(nullptr) { @@ -48,11 +48,11 @@ INLINE GeomVertexColumn:: GeomVertexColumn(const GeomVertexColumn ©) : _name(copy._name), _num_components(copy._num_components), + _num_elements(copy._num_elements), _numeric_type(copy._numeric_type), _contents(copy._contents), _start(copy._start), _column_alignment(copy._column_alignment), - _num_elements(copy._num_elements), _element_stride(copy._element_stride), _packer(nullptr) { diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I index 66f8a44b93..dd84cbb7cb 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -605,8 +605,8 @@ CacheEntry(GeomVertexData *source, CacheKey &&key) noexcept : */ INLINE GeomVertexData::CData:: CData() : - _format(nullptr), - _usage_hint(UH_unspecified) + _usage_hint(UH_unspecified), + _format(nullptr) { } @@ -615,8 +615,8 @@ CData() : */ INLINE GeomVertexData::CData:: CData(const GeomVertexFormat *format, GeomVertexData::UsageHint usage_hint) : - _format(format), - _usage_hint(usage_hint) + _usage_hint(usage_hint), + _format(format) { size_t num_arrays = format->get_num_arrays(); for (size_t i = 0; i < num_arrays; ++i) { diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 65dd962902..8f05651e6d 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -66,11 +66,11 @@ GeomVertexData(const std::string &name, const GeomVertexFormat *format, GeomVertexData::UsageHint usage_hint) : _name(name), + _cycler(GeomVertexData::CData(format, usage_hint)), _char_pcollector(PStatCollector(_animation_pcollector, name)), _skinning_pcollector(_char_pcollector, "Skinning"), _morphs_pcollector(_char_pcollector, "Morphs"), - _blends_pcollector(_char_pcollector, "Calc blends"), - _cycler(GeomVertexData::CData(format, usage_hint)) + _blends_pcollector(_char_pcollector, "Calc blends") { nassertv(format->is_registered()); } diff --git a/panda/src/gobj/samplerState.I b/panda/src/gobj/samplerState.I index ae5f9c9e2c..a3b0dabf73 100644 --- a/panda/src/gobj/samplerState.I +++ b/panda/src/gobj/samplerState.I @@ -17,14 +17,14 @@ INLINE SamplerState:: SamplerState() : _border_color(0, 0, 0, 1), - _wrap_u(WM_repeat), - _wrap_v(WM_repeat), - _wrap_w(WM_repeat), - _minfilter(FT_default), - _magfilter(FT_default), _min_lod(-1000), _max_lod(1000), _lod_bias(0), + _minfilter(FT_default), + _magfilter(FT_default), + _wrap_u(WM_repeat), + _wrap_v(WM_repeat), + _wrap_w(WM_repeat), _anisotropic_degree(0) { } diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 1e53294813..ad2df755c3 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1116,7 +1116,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { if (cdata->_component_width == 1) { if (format == "RGBA" && cdata->_num_components == 4) { imgsize *= 4; - for (int p = 0; p < imgsize; p += 4) { + for (size_t p = 0; p < imgsize; p += 4) { newdata[p + 2] = image[p ]; newdata[p + 1] = image[p + 1]; newdata[p ] = image[p + 2]; @@ -1127,7 +1127,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { } if (format == "RGB" && cdata->_num_components == 3) { imgsize *= 3; - for (int p = 0; p < imgsize; p += 3) { + for (size_t p = 0; p < imgsize; p += 3) { newdata[p + 2] = image[p ]; newdata[p + 1] = image[p + 1]; newdata[p ] = image[p + 2]; @@ -1138,13 +1138,13 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { if (format == "A" && cdata->_num_components != 3) { // We can generally rely on alpha to be the last component. int component = cdata->_num_components - 1; - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { newdata[component] = image[p]; } do_set_ram_image(cdata, newdata); return; } - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (unsigned char s = 0; s < format.size(); ++s) { signed char component = -1; if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) { @@ -1176,7 +1176,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { do_set_ram_image(cdata, newdata); return; } - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (unsigned char s = 0; s < format.size(); ++s) { signed char component = -1; if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) { diff --git a/panda/src/grutil/heightfieldTesselator.cxx b/panda/src/grutil/heightfieldTesselator.cxx index 58d8324521..5c9089dba8 100644 --- a/panda/src/grutil/heightfieldTesselator.cxx +++ b/panda/src/grutil/heightfieldTesselator.cxx @@ -161,10 +161,10 @@ generate() { PT(PandaNode) result = new PandaNode(get_name()); NodePath root(result); - int total = 0; + //int total = 0; for (int y=0; y= 0 && X + 1 < _index.size(), make_nan(0.0)); + nassertr(X >= 0 && (size_t)X + 1 < _index.size(), make_nan(0.0)); int A = _index[X] + Y; int B = _index[X + 1] + Y; - nassertr(A >= 0 && A + 1 < _index.size(), make_nan(0.0)); - nassertr(B >= 0 && B + 1 < _index.size(), make_nan(0.0)); + nassertr(A >= 0 && (size_t)A + 1 < _index.size(), make_nan(0.0)); + nassertr(B >= 0 && (size_t)B + 1 < _index.size(), make_nan(0.0)); // and add blended results from 4 corners of square. double result = diff --git a/panda/src/pgraph/camera.cxx b/panda/src/pgraph/camera.cxx index ddf8006862..4d5f7f6602 100644 --- a/panda/src/pgraph/camera.cxx +++ b/panda/src/pgraph/camera.cxx @@ -28,8 +28,8 @@ Camera(const string &name, Lens *lens) : LensNode(name, lens), _active(true), _camera_mask(~PandaNode::get_overall_bit()), - _initial_state(RenderState::make_empty()), - _lod_scale(1) + _lod_scale(1), + _initial_state(RenderState::make_empty()) { } @@ -42,8 +42,8 @@ Camera(const Camera ©) : _active(copy._active), _scene(copy._scene), _camera_mask(copy._camera_mask), - _initial_state(copy._initial_state), _lod_scale(copy._lod_scale), + _initial_state(copy._initial_state), _tag_state_key(copy._tag_state_key), _tag_states(copy._tag_states) { diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 575def2e22..07d0dea1c1 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -360,7 +360,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) { for (size_t ai = 0; ai < sformat._format->get_num_arrays(); ++ai) { const GeomVertexArrayFormat *aformat = sformat._format->get_array(ai); - for (size_t ci = 0; ci < aformat->get_num_columns(); ++ci) { + for (size_t ci = 0; ci < (size_t)aformat->get_num_columns(); ++ci) { const GeomVertexColumn *column = aformat->get_column(ci); const InternalName *name = column->get_name(); if (name != InternalName::get_vertex() && @@ -436,7 +436,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) { pvector copies; const GeomVertexArrayFormat *aformat = new_format->get_array(0); - for (size_t ci = 0; ci < aformat->get_num_columns(); ++ci) { + for (size_t ci = 0; ci < (size_t)aformat->get_num_columns(); ++ci) { const GeomVertexColumn *column = aformat->get_column(ci); const InternalName *name = column->get_name(); if (name != InternalName::get_vertex() && diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index 0034b2a8d7..ae0236f427 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -16,8 +16,8 @@ */ INLINE NodePath:: NodePath() : - _error_type(ET_ok), - _backup_key(0) + _backup_key(0), + _error_type(ET_ok) { } diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index b722a4cc07..1398332c5e 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -386,22 +386,6 @@ consider_update_pstats(int old_referenced_bits) const { #endif // DO_PSTATS } -/** - * - */ -INLINE RenderState::Composition:: -Composition() { -} - -/** - * - */ -INLINE RenderState::Composition:: -Composition(const RenderState::Composition ©) : - _result(copy._result) -{ -} - /** * */ diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index f096dfee27..340eb5dac1 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -253,9 +253,6 @@ private: // two involved RenderState objects. class Composition { public: - INLINE Composition(); - INLINE Composition(const Composition ©); - // _result is reference counted if and only if it is not the same pointer // as this. const RenderState *_result; diff --git a/panda/src/pgraph/shaderInput.I b/panda/src/pgraph/shaderInput.I index 2ace5d614b..1aa2b9e583 100644 --- a/panda/src/pgraph/shaderInput.I +++ b/panda/src/pgraph/shaderInput.I @@ -19,8 +19,8 @@ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, int priority) : _name(std::move(name)), - _type(M_invalid), - _priority(priority) + _priority(priority), + _type(M_invalid) { } @@ -30,9 +30,9 @@ ShaderInput(CPT_InternalName name, int priority) : INLINE ShaderInput:: ShaderInput(CPT_InternalName name, Texture *tex, int priority) : _name(std::move(name)), - _type(M_texture), + _value(tex), _priority(priority), - _value(tex) + _type(M_texture) { } @@ -42,9 +42,9 @@ ShaderInput(CPT_InternalName name, Texture *tex, int priority) : INLINE ShaderInput:: ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) : _name(std::move(name)), - _type(M_param), + _value(param), _priority(priority), - _value(param) + _type(M_param) { } @@ -54,9 +54,9 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) : INLINE ShaderInput:: ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) : _name(std::move(name)), - _type(M_buffer), + _value(buf), _priority(priority), - _value(buf) + _type(M_buffer) { } @@ -65,10 +65,10 @@ ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -77,10 +77,10 @@ ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -89,10 +89,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -101,10 +101,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase2f &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -113,11 +113,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2f &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase4f &vec, int priority) : - _name(std::move(name)), - _type(M_vector), - _priority(priority), + _stored_vector(LCAST(PN_stdfloat, vec)), _stored_ptr(vec), - _stored_vector(LCAST(PN_stdfloat, vec)) + _name(std::move(name)), + _priority(priority), + _type(M_vector) { } @@ -126,11 +126,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4f &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase3f &vec, int priority) : - _name(std::move(name)), - _type(M_vector), - _priority(priority), + _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0), _stored_ptr(vec), - _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_vector) { } @@ -139,11 +139,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3f &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase2f &vec, int priority) : - _name(std::move(name)), - _type(M_vector), - _priority(priority), + _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0), _stored_ptr(vec), - _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_vector) { } @@ -152,10 +152,10 @@ ShaderInput(CPT_InternalName name, const LVecBase2f &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LMatrix4f &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -164,10 +164,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix4f &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LMatrix3f &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -176,10 +176,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix3f &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LMatrix4f &mat, int priority) : + _stored_ptr(mat), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(mat) + _type(M_numeric) { } @@ -188,10 +188,10 @@ ShaderInput(CPT_InternalName name, const LMatrix4f &mat, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LMatrix3f &mat, int priority) : + _stored_ptr(mat), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(mat) + _type(M_numeric) { } @@ -200,10 +200,10 @@ ShaderInput(CPT_InternalName name, const LMatrix3f &mat, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_double &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -212,10 +212,10 @@ ShaderInput(CPT_InternalName name, const PTA_double &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase4d &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -224,10 +224,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4d &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase3d &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -236,10 +236,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3d &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase2d &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -248,11 +248,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2d &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase4d &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector(LCAST(PN_stdfloat, vec)), _stored_ptr(vec), - _stored_vector(LCAST(PN_stdfloat, vec)) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } @@ -261,11 +261,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4d &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase3d &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0), _stored_ptr(vec), - _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } @@ -274,11 +274,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3d &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase2d &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0), _stored_ptr(vec), - _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } @@ -287,10 +287,10 @@ ShaderInput(CPT_InternalName name, const LVecBase2d &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LMatrix4d &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -299,10 +299,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix4d &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LMatrix3d &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -311,10 +311,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix3d &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LMatrix4d &mat, int priority) : + _stored_ptr(mat), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(mat) + _type(M_numeric) { } @@ -323,10 +323,10 @@ ShaderInput(CPT_InternalName name, const LMatrix4d &mat, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LMatrix3d &mat, int priority) : + _stored_ptr(mat), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(mat) + _type(M_numeric) { } @@ -335,10 +335,10 @@ ShaderInput(CPT_InternalName name, const LMatrix3d &mat, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_int &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -347,10 +347,10 @@ ShaderInput(CPT_InternalName name, const PTA_int &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase4i &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -359,10 +359,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4i &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase3i &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -371,10 +371,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3i &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const PTA_LVecBase2i &ptr, int priority) : + _stored_ptr(ptr), _name(std::move(name)), - _type(M_numeric), _priority(priority), - _stored_ptr(ptr) + _type(M_numeric) { } @@ -383,11 +383,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2i &ptr, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase4i &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), (int)vec.get_w()), _stored_ptr(vec), - _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), (int)vec.get_w()) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } @@ -396,11 +396,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4i &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), 0.0), _stored_ptr(vec), - _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } @@ -409,11 +409,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority) : */ INLINE ShaderInput:: ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) : - _name(std::move(name)), - _type(M_numeric), - _priority(priority), + _stored_vector((int)vec.get_x(), (int)vec.get_y(), 0.0, 0.0), _stored_ptr(vec), - _stored_vector((int)vec.get_x(), (int)vec.get_y(), 0.0, 0.0) + _name(std::move(name)), + _priority(priority), + _type(M_numeric) { } diff --git a/panda/src/pgraph/shaderInput.cxx b/panda/src/pgraph/shaderInput.cxx index ddb390f05d..9730351a3d 100644 --- a/panda/src/pgraph/shaderInput.cxx +++ b/panda/src/pgraph/shaderInput.cxx @@ -31,9 +31,9 @@ get_blank() { ShaderInput:: ShaderInput(CPT_InternalName name, const NodePath &np, int priority) : _name(std::move(name)), - _type(M_nodepath), + _value(new ParamNodePath(np)), _priority(priority), - _value(new ParamNodePath(np)) + _type(M_nodepath) { } @@ -43,9 +43,9 @@ ShaderInput(CPT_InternalName name, const NodePath &np, int priority) : ShaderInput:: ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z, int n, int priority) : _name(std::move(name)), - _type(M_texture_image), + _value(new ParamTextureImage(tex, read, write, z, n)), _priority(priority), - _value(new ParamTextureImage(tex, read, write, z, n)) + _type(M_texture_image) { } @@ -55,9 +55,9 @@ ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z, i ShaderInput:: ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority) : _name(std::move(name)), - _type(M_texture_sampler), + _value(new ParamTextureSampler(tex, sampler)), _priority(priority), - _value(new ParamTextureSampler(tex, sampler)) + _type(M_texture_sampler) { } diff --git a/panda/src/pgraph/textureAttrib.I b/panda/src/pgraph/textureAttrib.I index c3d40bafbe..3814755033 100644 --- a/panda/src/pgraph/textureAttrib.I +++ b/panda/src/pgraph/textureAttrib.I @@ -236,9 +236,9 @@ StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) : // Yeah, we cast away the constness here. Just too much trouble to deal // with it properly. _stage((TextureStage *)stage), + _has_sampler(false), _implicit_sort(implicit_sort), - _override(override), - _has_sampler(false) + _override(override) { } diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index 6097430e91..c05e7cc6c9 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -1050,22 +1050,6 @@ consider_update_pstats(int old_referenced_bits) const { #endif // DO_PSTATS } -/** - * - */ -INLINE TransformState::Composition:: -Composition() { -} - -/** - * - */ -INLINE TransformState::Composition:: -Composition(const TransformState::Composition ©) : - _result(copy._result) -{ -} - /** * */ diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 288d26b315..80bdcf5adf 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -282,9 +282,6 @@ private: // object destructs. class Composition { public: - INLINE Composition(); - INLINE Composition(const Composition ©); - // _result is reference counted if and only if it is not the same pointer // as this. const TransformState *_result; diff --git a/panda/src/pgraphnodes/fadeLodNode.cxx b/panda/src/pgraphnodes/fadeLodNode.cxx index 265594279b..3223dcc0f8 100644 --- a/panda/src/pgraphnodes/fadeLodNode.cxx +++ b/panda/src/pgraphnodes/fadeLodNode.cxx @@ -168,13 +168,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (elapsed < half_fade_time) { // FIRST HALF OF FADE Fade the new LOD in with z writing off Keep // drawing the old LOD opaque with z writing on - if (out_child >= 0 && out_child < children.get_num_children()) { + if (out_child >= 0 && (size_t)out_child < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(out_child); trav->traverse_down(data, child, data._state->compose(get_fade_1_old_state())); } - if (in_child >= 0 && in_child < children.get_num_children()) { + if (in_child >= 0 && (size_t)in_child < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(in_child); PN_stdfloat in_alpha = elapsed / half_fade_time; trav->traverse_down(data, child, @@ -184,13 +184,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { } else { // SECOND HALF OF FADE: Fade out the old LOD with z write off and // draw the opaque new LOD with z write on - if (in_child >= 0 && in_child < children.get_num_children()) { + if (in_child >= 0 && (size_t)in_child < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(in_child); trav->traverse_down(data, child, data._state->compose(get_fade_2_new_state())); } - if (out_child >= 0 && out_child < children.get_num_children()) { + if (out_child >= 0 && (size_t)out_child < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(out_child); PN_stdfloat out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time; trav->traverse_down(data, child, @@ -206,7 +206,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // just drawing one child of the LOD. int index = ldata->_fade_in; Children children = get_children(); - if (index >= 0 && index < children.get_num_children()) { + if (index >= 0 && (size_t)index < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(index); trav->traverse_down(data, child, data._state); } diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx index aaa290e08b..0aabcb07b4 100644 --- a/panda/src/pgraphnodes/lightLensNode.cxx +++ b/panda/src/pgraphnodes/lightLensNode.cxx @@ -30,8 +30,8 @@ LightLensNode:: LightLensNode(const std::string &name, Lens *lens) : Camera(name, lens), _has_specular_color(false), - _attrib_count(0), - _used_by_auto_shader(false) + _used_by_auto_shader(false), + _attrib_count(0) { set_active(false); _shadow_caster = false; @@ -63,12 +63,12 @@ LightLensNode:: LightLensNode(const LightLensNode ©) : Light(copy), Camera(copy), - _shadow_caster(copy._shadow_caster), _sb_size(copy._sb_size), - _sb_sort(-10), + _shadow_caster(copy._shadow_caster), _has_specular_color(copy._has_specular_color), - _attrib_count(0), - _used_by_auto_shader(false) + _sb_sort(-10), + _used_by_auto_shader(false), + _attrib_count(0) { if (_shadow_caster) { setup_shadow_map(); diff --git a/panda/src/pgraphnodes/lodNode.cxx b/panda/src/pgraphnodes/lodNode.cxx index a5574dcaa8..fe595f5d3e 100644 --- a/panda/src/pgraphnodes/lodNode.cxx +++ b/panda/src/pgraphnodes/lodNode.cxx @@ -430,7 +430,7 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) { // This switch level is in range. Draw its children in the funny // wireframe mode. Children children = get_children(); - if (index < children.get_num_children()) { + if ((size_t)index < children.get_num_children()) { const PandaNode::DownConnection &child = children.get_child_connection(index); trav->traverse_down(data, child, data._state->compose(sw.get_viz_model_state())); } diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index 8c410814ed..7d2e884f82 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -62,9 +62,9 @@ PGItem(const string &name) : _notify(nullptr), _has_frame(false), _frame(0, 0, 0, 0), - _region(new PGMouseWatcherRegion(this)), _state(0), - _flags(0) + _flags(0), + _region(new PGMouseWatcherRegion(this)) { set_cull_callback(); set_renderable(); diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx index 641b2c6e2c..30c1c33b5e 100644 --- a/panda/src/pipeline/pipeline.cxx +++ b/panda/src/pipeline/pipeline.cxx @@ -26,9 +26,9 @@ Pipeline(const std::string &name, int num_stages) : Namable(name), #ifdef THREADED_PIPELINE _num_stages(num_stages), + _next_cycle_seq(1), _cycle_lock("Pipeline cycle"), - _lock("Pipeline"), - _next_cycle_seq(1) + _lock("Pipeline") #else _num_stages(1) #endif diff --git a/panda/src/pstatclient/config_pstatclient.cxx b/panda/src/pstatclient/config_pstatclient.cxx index 5b97075bcc..7f9443cdb4 100644 --- a/panda/src/pstatclient/config_pstatclient.cxx +++ b/panda/src/pstatclient/config_pstatclient.cxx @@ -12,7 +12,7 @@ */ #include "config_pstatclient.h" - +#include "pStatTimer.h" #include "dconfig.h" #if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_PANDA_PSTATCLIENT) diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index e08dff1605..906cd87e6b 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -1097,7 +1097,7 @@ stop_clock_wait() { void PStatClient:: add_collector(PStatClient::Collector *collector) { int num_collectors = get_num_collectors(); - if (num_collectors >= _collectors_size) { + if (num_collectors >= (int)_collectors_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 not protected by // the lock. @@ -1136,7 +1136,7 @@ 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) { + if (num_threads >= (int)_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 not protected by // the lock. diff --git a/panda/src/pstatclient/pStatTimer.h b/panda/src/pstatclient/pStatTimer.h index 4cfca5a7d2..b7a32f27e2 100644 --- a/panda/src/pstatclient/pStatTimer.h +++ b/panda/src/pstatclient/pStatTimer.h @@ -27,7 +27,7 @@ class Thread; * and when the PStatTimer variable goes out of scope (for instance, at the * end of the function), it will automatically stop the Collector. */ -class PStatTimer { +class EXPCL_PANDA_PSTATCLIENT PStatTimer { public: #ifdef DO_PSTATS INLINE PStatTimer(PStatCollector &collector); diff --git a/panda/src/putil/clockObject.cxx b/panda/src/putil/clockObject.cxx index 0eb72647e4..d275390258 100644 --- a/panda/src/putil/clockObject.cxx +++ b/panda/src/putil/clockObject.cxx @@ -32,7 +32,7 @@ TypeHandle ClockObject::_type_handle; * */ ClockObject:: -ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) { +ClockObject(Mode mode) : _mode(mode), _ticks(get_class_type()) { _true_clock = TrueClock::get_global_ptr(); _start_short_time = _true_clock->get_short_time(); diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 8442cae415..0e2af03bdd 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -3040,6 +3040,15 @@ get_color_blend_op(ColorBlendAttrib::Operand operand) { case ColorBlendAttrib::O_incoming_color_saturate: return 1; + case ColorBlendAttrib::O_incoming1_color: + return 1; + case ColorBlendAttrib::O_one_minus_incoming1_color: + return 0; + case ColorBlendAttrib::O_incoming1_alpha: + return 1; + case ColorBlendAttrib::O_one_minus_incoming1_alpha: + return 0; + case ColorBlendAttrib::O_color_scale: return 10; case ColorBlendAttrib::O_one_minus_color_scale: diff --git a/panda/src/tinydisplay/ztriangle.h b/panda/src/tinydisplay/ztriangle.h index 72f3c1ae11..891074f68a 100644 --- a/panda/src/tinydisplay/ztriangle.h +++ b/panda/src/tinydisplay/ztriangle.h @@ -351,7 +351,7 @@ unsigned int z,zz; #endif #ifdef INTERP_RGB - unsigned int or1,og1,ob1,oa1; + UNUSED unsigned int or1,og1,ob1,oa1; #endif #ifdef INTERP_ST unsigned int s,t; diff --git a/panda/src/x11display/x11GraphicsPipe.cxx b/panda/src/x11display/x11GraphicsPipe.cxx index 6d42d399df..fc76161018 100644 --- a/panda/src/x11display/x11GraphicsPipe.cxx +++ b/panda/src/x11display/x11GraphicsPipe.cxx @@ -35,8 +35,8 @@ LightReMutex x11GraphicsPipe::_x_mutex; */ x11GraphicsPipe:: x11GraphicsPipe(const std::string &display) : - _have_xrandr(false), _xcursor_size(-1), + _have_xrandr(false), _XF86DGADirectVideo(nullptr) { std::string display_spec = display; diff --git a/pandatool/src/egg-qtess/isoPlacer.cxx b/pandatool/src/egg-qtess/isoPlacer.cxx index f37588f463..0f7a6a3fc1 100644 --- a/pandatool/src/egg-qtess/isoPlacer.cxx +++ b/pandatool/src/egg-qtess/isoPlacer.cxx @@ -106,8 +106,6 @@ get_scores(int subdiv, int across, double ratio, */ void IsoPlacer:: place(int count, pvector &iso_points) { - int i; - // Count up the average curvature. /* double avg_curve = 0.0; diff --git a/pandatool/src/palettizer/textureProperties.cxx b/pandatool/src/palettizer/textureProperties.cxx index 30d102f0a0..1268405ce8 100644 --- a/pandatool/src/palettizer/textureProperties.cxx +++ b/pandatool/src/palettizer/textureProperties.cxx @@ -292,6 +292,8 @@ fully_define() { case EggTexture::F_luminance: case EggTexture::F_luminance_alpha: case EggTexture::F_luminance_alphamask: + case EggTexture::F_srgb: + case EggTexture::F_srgb_alpha: break; case EggTexture::F_rgba12: @@ -598,6 +600,12 @@ get_format_string(EggTexture::Format format) { case EggTexture::F_luminance: return "l"; + + case EggTexture::F_srgb: + return "sc"; + + case EggTexture::F_srgb_alpha: + return "sa"; } return "x"; From f60c55f45007bd0efd750bed5cde6a641b63adf0 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 13:43:21 +0100 Subject: [PATCH 075/166] makepanda: Enable additional warning messages Also error on missing return statement in non-void function, since this is pretty much always an error and can be dangerous if not caught --- makepanda/makepanda.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 28e39ae484..5e3e42b4a9 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1211,7 +1211,7 @@ def CompileCxx(obj,src,opts): cmd = "cl " if GetTargetArch() == 'x64': cmd += "/favor:blend " - cmd += "/wd4996 /wd4275 /wd4273 " + cmd += "/wd4996 " # Set the minimum version to Windows Vista. cmd += "/DWINVER=0x600 " @@ -1261,7 +1261,7 @@ def CompileCxx(obj,src,opts): cmd = "icl " if GetTargetArch() == 'x64': cmd += "/favor:blend " - cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 " + cmd += "/wd4996 /wd4267 /wd4101 " cmd += "/DWINVER=0x600 " cmd += "/Fo" + obj + " /c" for x in ipath: cmd += " /I" + x @@ -1458,10 +1458,7 @@ def CompileCxx(obj,src,opts): if (optlevel==4): cmd += " -O3 -DNDEBUG" # Enable more warnings. - cmd += " -Wall -Wno-unused-function" - - if not src.endswith(".c"): - cmd += " -Wno-reorder" + cmd += " -Wall -Wno-unused-function -Werror=return-type" # Ignore unused variables in NDEBUG builds, often used in asserts. if optlevel == 4: From c917a9e1a1e29c3b4a9e5c827717cb348eea1380 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 14:44:24 +0100 Subject: [PATCH 076/166] distributed: PyDatagramIterator now retains reference to Datagram Fixes #1262 --- direct/src/distributed/PyDatagramIterator.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/direct/src/distributed/PyDatagramIterator.py b/direct/src/distributed/PyDatagramIterator.py index bd27469c41..13c810e90d 100755 --- a/direct/src/distributed/PyDatagramIterator.py +++ b/direct/src/distributed/PyDatagramIterator.py @@ -30,6 +30,21 @@ class PyDatagramIterator(DatagramIterator): getChannel = DatagramIterator.getUint64 + def __init__(self, datagram=None, offset=0): + if datagram is not None: + super().__init__(datagram, offset) + + # Retain a reference to it so that it doesn't get deleted. + self.__datagram = datagram + else: + super().__init__() + + def getDatagram(self): + return self.__datagram + + def get_datagram(self): + return self.__datagram + def getArg(self, subatomicType, divisor=1): # Import the type numbers if divisor == 1: From aaa51df759fb00ebbb011dd6b1940aa8c5f8ac1e Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 1 Mar 2022 16:09:30 +0100 Subject: [PATCH 077/166] makepanda: Remove `-undefined dynamic_lookup` for OpenEXR on macOS --- makepanda/makepanda.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5e3e42b4a9..ce8d63b64b 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1069,8 +1069,6 @@ if (COMPILER=="GCC"): LibName("FFMPEG", "-undefined dynamic_lookup") if not PkgSkip("ASSIMP"): LibName("ASSIMP", "-undefined dynamic_lookup") - if not PkgSkip("OPENEXR"): - LibName("OPENEXR", "-undefined dynamic_lookup") if not PkgSkip("VRPN"): LibName("VRPN", "-undefined dynamic_lookup") From 7da3395a8a654932adece1f9338359bc24c6b8dd Mon Sep 17 00:00:00 2001 From: Disyer Date: Tue, 1 Mar 2022 23:07:20 +0200 Subject: [PATCH 078/166] mayaprogs: Resolve Maya conversion server memory leak by storing managers on the heap --- .../src/mayaprogs/mayaConversionClient.cxx | 29 +++++------ .../src/mayaprogs/mayaConversionClient.h | 6 +-- .../src/mayaprogs/mayaConversionServer.cxx | 50 +++++++------------ .../src/mayaprogs/mayaConversionServer.h | 9 ++-- 4 files changed, 38 insertions(+), 56 deletions(-) diff --git a/pandatool/src/mayaprogs/mayaConversionClient.cxx b/pandatool/src/mayaprogs/mayaConversionClient.cxx index 900b7ca0d4..553e473ee0 100644 --- a/pandatool/src/mayaprogs/mayaConversionClient.cxx +++ b/pandatool/src/mayaprogs/mayaConversionClient.cxx @@ -19,11 +19,8 @@ * Initializes the Maya conversion client. */ MayaConversionClient:: -MayaConversionClient() +MayaConversionClient() : _qReader(&_qManager, 0), _cWriter(&_qManager, 0) { - _qManager = new QueuedConnectionManager(); - _qReader = new QueuedConnectionReader(_qManager, 0); - _cWriter = new ConnectionWriter(_qManager, 0); } /** @@ -44,12 +41,12 @@ bool MayaConversionClient:: connect(NetAddress server) { if (_conn) { // Remove this connection from the readers list - _qReader->remove_connection(_conn); + _qReader.remove_connection(_conn); _conn = nullptr; } // Attempt to open a connection - _conn = _qManager->open_TCP_client_connection(server, 0); + _conn = _qManager.open_TCP_client_connection(server, 0); if (!_conn || _conn.is_null()) { // This connection could not be opened @@ -57,7 +54,7 @@ connect(NetAddress server) { } // Add this connection to the readers list - _qReader->add_connection(_conn); + _qReader.add_connection(_conn); return true; } @@ -94,17 +91,17 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer:: datagram.add_uint8(conversion_type); // Send the conversion request - if (!_cWriter->send(datagram, _conn) || !_conn->flush()) { + if (!_cWriter.send(datagram, _conn) || !_conn->flush()) { nout << "Failed to send workload to server process.\n"; return false; } // Wait for a response - while (_conn->get_socket()->Active() && !_qReader->data_available()) { - _qReader->poll(); + while (_conn->get_socket()->Active() && !_qReader.data_available()) { + _qReader.poll(); } - if (!_qReader->data_available()) { + if (!_qReader.data_available()) { // No response has been given by the server. nout << "No response has been given by the conversion server.\n"; return false; @@ -113,7 +110,7 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer:: NetDatagram response; // Let's read the response now! - if (!_qReader->get_data(response)) { + if (!_qReader.get_data(response)) { nout << "The conversion response could not be read.\n"; return false; } @@ -147,13 +144,13 @@ close() { } while (true) { - _qReader->data_available(); + _qReader.data_available(); - if (_qManager->reset_connection_available()) { + if (_qManager.reset_connection_available()) { PT(Connection) connection; - if (_qManager->get_reset_connection(connection)) { - _qManager->close_connection(_conn); + if (_qManager.get_reset_connection(connection)) { + _qManager.close_connection(_conn); _conn = nullptr; return; } diff --git a/pandatool/src/mayaprogs/mayaConversionClient.h b/pandatool/src/mayaprogs/mayaConversionClient.h index bd524a07eb..90afe037d1 100644 --- a/pandatool/src/mayaprogs/mayaConversionClient.h +++ b/pandatool/src/mayaprogs/mayaConversionClient.h @@ -44,9 +44,9 @@ public: int main(int argc, char *argv[], MayaConversionServer::ConversionType conversion_type); private: - QueuedConnectionManager *_qManager; - QueuedConnectionReader *_qReader; - ConnectionWriter *_cWriter; + QueuedConnectionManager _qManager; + QueuedConnectionReader _qReader; + ConnectionWriter _cWriter; PT(Connection) _conn; }; diff --git a/pandatool/src/mayaprogs/mayaConversionServer.cxx b/pandatool/src/mayaprogs/mayaConversionServer.cxx index 0d6b7e8056..757070ff73 100644 --- a/pandatool/src/mayaprogs/mayaConversionServer.cxx +++ b/pandatool/src/mayaprogs/mayaConversionServer.cxx @@ -26,22 +26,8 @@ * Initializes the Maya conversion server. */ MayaConversionServer:: -MayaConversionServer() { - _qManager = new QueuedConnectionManager(); - _qListener = new QueuedConnectionListener(_qManager, 0); - _qReader = new QueuedConnectionReader(_qManager, 0); - _cWriter = new ConnectionWriter(_qManager, 0); -} - -/** - * Cleans up the connection managers for the Maya conversion server. - */ -MayaConversionServer:: -~MayaConversionServer() { - delete _qManager; - delete _qReader; - delete _qListener; - delete _cWriter; +MayaConversionServer() : _qListener(&_qManager, 0), _qReader(&_qManager, 0), + _cWriter(&_qManager, 0) { } /** @@ -51,39 +37,39 @@ MayaConversionServer:: void MayaConversionServer:: poll() { // Listen for new connections - _qListener->poll(); + _qListener.poll(); // If we have a new connection from a client create a new connection pointer // and add it to the reader list - if (_qListener->new_connection_available()) { + if (_qListener.new_connection_available()) { PT(Connection) rendezvous; PT(Connection) connection; NetAddress address; - if (_qListener->get_new_connection(rendezvous, address, connection)) { - _qReader->add_connection(connection); + if (_qListener.get_new_connection(rendezvous, address, connection)) { + _qReader.add_connection(connection); _clients.insert(connection); } } // Check for reset clients - if (_qManager->reset_connection_available()) { + if (_qManager.reset_connection_available()) { PT(Connection) connection; - if (_qManager->get_reset_connection(connection)) { + if (_qManager.get_reset_connection(connection)) { _clients.erase(connection); - _qManager->close_connection(connection); + _qManager.close_connection(connection); } } // Poll the readers (created above) and if they have data process it - _qReader->poll(); + _qReader.poll(); - if (_qReader->data_available()) { + if (_qReader.data_available()) { // Grab the incoming data and unpack it NetDatagram datagram; - if (_qReader->get_data(datagram)) { + if (_qReader.get_data(datagram)) { DatagramIterator data(datagram); // First data should be the "argc" (argument count) from the client @@ -182,7 +168,7 @@ poll() { response.add_bool(converted); // Send the response - if (!_cWriter->send(response, datagram.get_connection())) { + if (!_cWriter.send(response, datagram.get_connection())) { // Looks like we couldn't send the response nout << "Could not send response to the client.\n"; } @@ -199,13 +185,13 @@ poll() { } // Clean up the malloc'd pointer pointer free(cargv); - } // qReader->get_data + } // qReader.get_data Clients::iterator ci; for (ci = _clients.begin(); ci != _clients.end(); ++ci) { - _qManager->close_connection(*ci); + _qManager.close_connection(*ci); } - } // qReader->data_available + } // qReader.data_available } // poll /** @@ -214,7 +200,7 @@ poll() { void MayaConversionServer:: listen() { // Open a rendezvous port for receiving new connections from the client - PT(Connection) rend = _qManager->open_TCP_server_rendezvous(4242, 100); + PT(Connection) rend = _qManager.open_TCP_server_rendezvous(4242, 100); if (rend.is_null()) { nout << "Port opening failed!\n"; @@ -224,7 +210,7 @@ listen() { nout << "Server opened on port 4242, waiting for requests...\n"; // Add this connection to the listeners list - _qListener->add_connection(rend); + _qListener.add_connection(rend); // Main loop. Keep polling for connections, but don't eat up all the CPU. while (true) { diff --git a/pandatool/src/mayaprogs/mayaConversionServer.h b/pandatool/src/mayaprogs/mayaConversionServer.h index 10212676a8..3b933ae16e 100644 --- a/pandatool/src/mayaprogs/mayaConversionServer.h +++ b/pandatool/src/mayaprogs/mayaConversionServer.h @@ -40,7 +40,6 @@ public: }; MayaConversionServer(); - ~MayaConversionServer(); void listen(); void poll(); @@ -49,10 +48,10 @@ protected: typedef pset< PT(Connection) > Clients; Clients _clients; - QueuedConnectionManager *_qManager; - QueuedConnectionListener *_qListener; - QueuedConnectionReader *_qReader; - ConnectionWriter *_cWriter; + QueuedConnectionManager _qManager; + QueuedConnectionListener _qListener; + QueuedConnectionReader _qReader; + ConnectionWriter _cWriter; }; #endif From ac50aa5dded6642efb24fca493f6daa4ed7e7e87 Mon Sep 17 00:00:00 2001 From: Disyer Date: Tue, 1 Mar 2022 23:25:28 +0200 Subject: [PATCH 079/166] mayaprogs: Switch connection manager variable names to snake case --- .../src/mayaprogs/mayaConversionClient.cxx | 26 +++++++-------- .../src/mayaprogs/mayaConversionClient.h | 6 ++-- .../src/mayaprogs/mayaConversionServer.cxx | 32 +++++++++---------- .../src/mayaprogs/mayaConversionServer.h | 8 ++--- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/pandatool/src/mayaprogs/mayaConversionClient.cxx b/pandatool/src/mayaprogs/mayaConversionClient.cxx index 553e473ee0..83735f3186 100644 --- a/pandatool/src/mayaprogs/mayaConversionClient.cxx +++ b/pandatool/src/mayaprogs/mayaConversionClient.cxx @@ -19,7 +19,7 @@ * Initializes the Maya conversion client. */ MayaConversionClient:: -MayaConversionClient() : _qReader(&_qManager, 0), _cWriter(&_qManager, 0) +MayaConversionClient() : _reader(&_manager, 0), _writer(&_manager, 0) { } @@ -41,12 +41,12 @@ bool MayaConversionClient:: connect(NetAddress server) { if (_conn) { // Remove this connection from the readers list - _qReader.remove_connection(_conn); + _reader.remove_connection(_conn); _conn = nullptr; } // Attempt to open a connection - _conn = _qManager.open_TCP_client_connection(server, 0); + _conn = _manager.open_TCP_client_connection(server, 0); if (!_conn || _conn.is_null()) { // This connection could not be opened @@ -54,7 +54,7 @@ connect(NetAddress server) { } // Add this connection to the readers list - _qReader.add_connection(_conn); + _reader.add_connection(_conn); return true; } @@ -91,17 +91,17 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer:: datagram.add_uint8(conversion_type); // Send the conversion request - if (!_cWriter.send(datagram, _conn) || !_conn->flush()) { + if (!_writer.send(datagram, _conn) || !_conn->flush()) { nout << "Failed to send workload to server process.\n"; return false; } // Wait for a response - while (_conn->get_socket()->Active() && !_qReader.data_available()) { - _qReader.poll(); + while (_conn->get_socket()->Active() && !_reader.data_available()) { + _reader.poll(); } - if (!_qReader.data_available()) { + if (!_reader.data_available()) { // No response has been given by the server. nout << "No response has been given by the conversion server.\n"; return false; @@ -110,7 +110,7 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer:: NetDatagram response; // Let's read the response now! - if (!_qReader.get_data(response)) { + if (!_reader.get_data(response)) { nout << "The conversion response could not be read.\n"; return false; } @@ -144,13 +144,13 @@ close() { } while (true) { - _qReader.data_available(); + _reader.data_available(); - if (_qManager.reset_connection_available()) { + if (_manager.reset_connection_available()) { PT(Connection) connection; - if (_qManager.get_reset_connection(connection)) { - _qManager.close_connection(_conn); + if (_manager.get_reset_connection(connection)) { + _manager.close_connection(_conn); _conn = nullptr; return; } diff --git a/pandatool/src/mayaprogs/mayaConversionClient.h b/pandatool/src/mayaprogs/mayaConversionClient.h index 90afe037d1..2685c7166d 100644 --- a/pandatool/src/mayaprogs/mayaConversionClient.h +++ b/pandatool/src/mayaprogs/mayaConversionClient.h @@ -44,9 +44,9 @@ public: int main(int argc, char *argv[], MayaConversionServer::ConversionType conversion_type); private: - QueuedConnectionManager _qManager; - QueuedConnectionReader _qReader; - ConnectionWriter _cWriter; + QueuedConnectionManager _manager; + QueuedConnectionReader _reader; + ConnectionWriter _writer; PT(Connection) _conn; }; diff --git a/pandatool/src/mayaprogs/mayaConversionServer.cxx b/pandatool/src/mayaprogs/mayaConversionServer.cxx index 757070ff73..3557082ace 100644 --- a/pandatool/src/mayaprogs/mayaConversionServer.cxx +++ b/pandatool/src/mayaprogs/mayaConversionServer.cxx @@ -26,8 +26,8 @@ * Initializes the Maya conversion server. */ MayaConversionServer:: -MayaConversionServer() : _qListener(&_qManager, 0), _qReader(&_qManager, 0), - _cWriter(&_qManager, 0) { +MayaConversionServer() : _listener(&_manager, 0), _reader(&_manager, 0), + _writer(&_manager, 0) { } /** @@ -37,39 +37,39 @@ MayaConversionServer() : _qListener(&_qManager, 0), _qReader(&_qManager, 0), void MayaConversionServer:: poll() { // Listen for new connections - _qListener.poll(); + _listener.poll(); // If we have a new connection from a client create a new connection pointer // and add it to the reader list - if (_qListener.new_connection_available()) { + if (_listener.new_connection_available()) { PT(Connection) rendezvous; PT(Connection) connection; NetAddress address; - if (_qListener.get_new_connection(rendezvous, address, connection)) { - _qReader.add_connection(connection); + if (_listener.get_new_connection(rendezvous, address, connection)) { + _reader.add_connection(connection); _clients.insert(connection); } } // Check for reset clients - if (_qManager.reset_connection_available()) { + if (_manager.reset_connection_available()) { PT(Connection) connection; - if (_qManager.get_reset_connection(connection)) { + if (_manager.get_reset_connection(connection)) { _clients.erase(connection); - _qManager.close_connection(connection); + _manager.close_connection(connection); } } // Poll the readers (created above) and if they have data process it - _qReader.poll(); + _reader.poll(); - if (_qReader.data_available()) { + if (_reader.data_available()) { // Grab the incoming data and unpack it NetDatagram datagram; - if (_qReader.get_data(datagram)) { + if (_reader.get_data(datagram)) { DatagramIterator data(datagram); // First data should be the "argc" (argument count) from the client @@ -168,7 +168,7 @@ poll() { response.add_bool(converted); // Send the response - if (!_cWriter.send(response, datagram.get_connection())) { + if (!_writer.send(response, datagram.get_connection())) { // Looks like we couldn't send the response nout << "Could not send response to the client.\n"; } @@ -189,7 +189,7 @@ poll() { Clients::iterator ci; for (ci = _clients.begin(); ci != _clients.end(); ++ci) { - _qManager.close_connection(*ci); + _manager.close_connection(*ci); } } // qReader.data_available } // poll @@ -200,7 +200,7 @@ poll() { void MayaConversionServer:: listen() { // Open a rendezvous port for receiving new connections from the client - PT(Connection) rend = _qManager.open_TCP_server_rendezvous(4242, 100); + PT(Connection) rend = _manager.open_TCP_server_rendezvous(4242, 100); if (rend.is_null()) { nout << "Port opening failed!\n"; @@ -210,7 +210,7 @@ listen() { nout << "Server opened on port 4242, waiting for requests...\n"; // Add this connection to the listeners list - _qListener.add_connection(rend); + _listener.add_connection(rend); // Main loop. Keep polling for connections, but don't eat up all the CPU. while (true) { diff --git a/pandatool/src/mayaprogs/mayaConversionServer.h b/pandatool/src/mayaprogs/mayaConversionServer.h index 3b933ae16e..c5d89910e0 100644 --- a/pandatool/src/mayaprogs/mayaConversionServer.h +++ b/pandatool/src/mayaprogs/mayaConversionServer.h @@ -48,10 +48,10 @@ protected: typedef pset< PT(Connection) > Clients; Clients _clients; - QueuedConnectionManager _qManager; - QueuedConnectionListener _qListener; - QueuedConnectionReader _qReader; - ConnectionWriter _cWriter; + QueuedConnectionManager _manager; + QueuedConnectionListener _listener; + QueuedConnectionReader _reader; + ConnectionWriter _writer; }; #endif From 3a120c4d6891d64e2f7b35217bc81385fd3389aa Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 09:45:22 +0100 Subject: [PATCH 080/166] CMake: Update warning flags to match makepanda (see f60c55f) --- dtool/CompilerFlags.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtool/CompilerFlags.cmake b/dtool/CompilerFlags.cmake index a947d50927..8b909ef3ed 100644 --- a/dtool/CompilerFlags.cmake +++ b/dtool/CompilerFlags.cmake @@ -138,7 +138,7 @@ endif() if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") set(global_flags - "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing") + "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing -Werror=return-type") set(release_flags "-Wno-unused-variable") if(NOT MSVC) @@ -154,7 +154,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${global_flags}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags} -Wno-reorder") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${release_flags}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${release_flags}") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${release_flags}") From 370b635534ef9e1083f9008400462b6c7d77cdc9 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 09:46:52 +0100 Subject: [PATCH 081/166] dtoolbase: Fix missing __stdcall when compiling for 32-bit Windows --- dtool/src/dtoolbase/patomic.cxx | 16 ++++++++-------- panda/src/pipeline/conditionVarWin32Impl.cxx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dtool/src/dtoolbase/patomic.cxx b/dtool/src/dtoolbase/patomic.cxx index 5258021efd..58d52a7e42 100644 --- a/dtool/src/dtoolbase/patomic.cxx +++ b/dtool/src/dtoolbase/patomic.cxx @@ -26,12 +26,12 @@ static_assert(sizeof(uint32_t) == sizeof(int32_t), // On Windows 7, we try to load the Windows 8 functions dynamically, and // fall back to a condition variable table if they aren't available. -static BOOL initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout); -static void dummy_wake(PVOID addr) {} +static BOOL __stdcall initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout); +static void __stdcall dummy_wake(PVOID addr) {} -BOOL (*_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD) = &initialize_wait; -void (*_patomic_wake_one_func)(PVOID) = &dummy_wake; -void (*_patomic_wake_all_func)(PVOID) = &dummy_wake; +BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD) = &initialize_wait; +void (__stdcall *_patomic_wake_one_func)(PVOID) = &dummy_wake; +void (__stdcall *_patomic_wake_all_func)(PVOID) = &dummy_wake; // Randomly pick an entry into the wait table based on the hash of the address. // It's possible to get hash collision, but that's not so bad, it just means @@ -47,7 +47,7 @@ static const size_t _wait_hash_mask = 63; /** * Emulates WakeByAddressSingle for Windows Vista and 7. */ -static void +static void __stdcall emulated_wake(PVOID addr) { size_t i = std::hash{}(addr) & (sizeof(_wait_table) / sizeof(WaitTableEntry) - 1); WaitTableEntry &entry = _wait_table[i]; @@ -65,7 +65,7 @@ emulated_wake(PVOID addr) { * Emulates WaitOnAddress for Windows Vista and 7. Only supports aligned * 32-bit values. */ -static BOOL +static BOOL __stdcall emulated_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) { assert(size == sizeof(LONG)); @@ -95,7 +95,7 @@ emulated_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) { * Initially assigned to the wait function slot to initialize the function * pointers. */ -static BOOL +static BOOL __stdcall initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) { // There's a chance of a race here, with two threads trying to initialize the // functions at the same time. That's OK, because they should all produce diff --git a/panda/src/pipeline/conditionVarWin32Impl.cxx b/panda/src/pipeline/conditionVarWin32Impl.cxx index 6f4776dfa0..7c1b05d6e0 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.cxx +++ b/panda/src/pipeline/conditionVarWin32Impl.cxx @@ -18,6 +18,6 @@ #include "conditionVarWin32Impl.h" // This function gets replaced by PStats to measure the time spent waiting. -BOOL (*ConditionVarWin32Impl::_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG) = &SleepConditionVariableSRW; +BOOL (__stdcall *ConditionVarWin32Impl::_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG) = &SleepConditionVariableSRW; #endif // _WIN32 From 46c1b887eaf5b15767e6c4719a42dfbe9219aa8a Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 09:54:37 +0100 Subject: [PATCH 082/166] Fix heap alignment with SSE2 on 32-bit Windows by inheriting MemoryBase Fixes #510 --- contrib/src/rplight/shadowSource.h | 10 +++++----- direct/src/motiontrail/cMotionTrail.h | 3 ++- dtool/src/prc/configFlags.h | 1 + dtool/src/prc/configVariableBase.h | 3 ++- dtool/src/prc/configVariableCore.h | 3 ++- panda/src/bullet/bulletAllHitsRayResult.h | 5 +++-- panda/src/bullet/bulletClosestHitRayResult.h | 3 ++- panda/src/bullet/bulletClosestHitSweepResult.h | 3 ++- panda/src/device/inputDeviceManager.h | 2 +- panda/src/device/trackerData.h | 2 +- panda/src/egg/eggTransform.h | 3 ++- panda/src/gobj/samplerState.h | 4 ++-- panda/src/grutil/shaderTerrainMesh.h | 8 ++++---- panda/src/linmath/lmatrix4_src.h | 2 +- panda/src/linmath/lvecBase4_src.h | 2 +- panda/src/mathutil/perlinNoise.h | 3 ++- panda/src/mathutil/triangulator.h | 3 ++- panda/src/pgraph/shaderInput.h | 2 +- panda/src/pnmimage/pnmImageHeader.h | 3 ++- panda/src/text/textGraphic.h | 3 ++- panda/src/text/textProperties.h | 3 ++- 21 files changed, 42 insertions(+), 29 deletions(-) diff --git a/contrib/src/rplight/shadowSource.h b/contrib/src/rplight/shadowSource.h index 84b00a197b..3f3d15fe15 100644 --- a/contrib/src/rplight/shadowSource.h +++ b/contrib/src/rplight/shadowSource.h @@ -30,6 +30,7 @@ #include "pandabase.h" #include "luse.h" +#include "memoryBase.h" #include "transformState.h" #include "look_at.h" #include "compose_matrix.h" @@ -48,7 +49,7 @@ * and a view-projection matrix. The shadow manager regenerates the shadow maps * using the data from the shadow sources. */ -class ShadowSource { +class ShadowSource : public MemoryBase { public: ShadowSource(); @@ -78,14 +79,13 @@ public: inline const BoundingSphere& get_bounds() const; private: - int _slot; - bool _needs_update; - size_t _resolution; LMatrix4 _mvp; LVecBase4i _region; LVecBase4 _region_uv; - BoundingSphere _bounds; + int _slot; + bool _needs_update; + size_t _resolution; }; #include "shadowSource.I" diff --git a/direct/src/motiontrail/cMotionTrail.h b/direct/src/motiontrail/cMotionTrail.h index fdb5ba1f56..bb42001ffc 100644 --- a/direct/src/motiontrail/cMotionTrail.h +++ b/direct/src/motiontrail/cMotionTrail.h @@ -21,11 +21,12 @@ #include "geomVertexWriter.h" #include "geomTriangles.h" #include "luse.h" +#include "memoryBase.h" #include "nurbsCurveEvaluator.h" #include "plist.h" #include "epvector.h" -class CMotionTrailVertex { +class CMotionTrailVertex : public MemoryBase { public: LPoint4 _vertex; LVecBase4 _start_color; diff --git a/dtool/src/prc/configFlags.h b/dtool/src/prc/configFlags.h index f4b95757d6..c034760667 100644 --- a/dtool/src/prc/configFlags.h +++ b/dtool/src/prc/configFlags.h @@ -17,6 +17,7 @@ #include "dtoolbase.h" #include "numeric_types.h" #include "atomicAdjust.h" +#include "memoryBase.h" /** * This class is the base class of both ConfigVariable and ConfigVariableCore. diff --git a/dtool/src/prc/configVariableBase.h b/dtool/src/prc/configVariableBase.h index ac256f1e8d..56ff035e5c 100644 --- a/dtool/src/prc/configVariableBase.h +++ b/dtool/src/prc/configVariableBase.h @@ -21,6 +21,7 @@ #include "configVariableManager.h" #include "vector_string.h" #include "pset.h" +#include "memoryBase.h" // Use this macro to wrap around a description passed to a ConfigVariable // constructor. This allows the description to be completely compiled out, so @@ -42,7 +43,7 @@ * and/or ConfigDeclaration, more or less duplicating the interface presented * there. */ -class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags { +class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags, public MemoryBase { protected: INLINE ConfigVariableBase(const std::string &name, ValueType type); ConfigVariableBase(const std::string &name, ValueType type, diff --git a/dtool/src/prc/configVariableCore.h b/dtool/src/prc/configVariableCore.h index 8849cc401b..d9dcb7658d 100644 --- a/dtool/src/prc/configVariableCore.h +++ b/dtool/src/prc/configVariableCore.h @@ -18,6 +18,7 @@ #include "configFlags.h" #include "configPageManager.h" #include "pnotify.h" +#include "memoryBase.h" #include @@ -31,7 +32,7 @@ class ConfigDeclaration; * make() method, which may return a shared instance. Once created, these * objects are never destructed. */ -class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags { +class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags, public MemoryBase { private: ConfigVariableCore(const std::string &name); ConfigVariableCore(const ConfigVariableCore &templ, const std::string &name); diff --git a/panda/src/bullet/bulletAllHitsRayResult.h b/panda/src/bullet/bulletAllHitsRayResult.h index 0153e424a8..88ebdfcdc0 100644 --- a/panda/src/bullet/bulletAllHitsRayResult.h +++ b/panda/src/bullet/bulletAllHitsRayResult.h @@ -20,13 +20,14 @@ #include "bullet_utils.h" #include "luse.h" +#include "memoryBase.h" #include "pandaNode.h" #include "collideMask.h" /** * */ -struct EXPCL_PANDABULLET BulletRayHit { +struct EXPCL_PANDABULLET BulletRayHit : public MemoryBase { PUBLISHED: INLINE static BulletRayHit empty(); @@ -61,7 +62,7 @@ private: /** * */ -struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback { +struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback, public MemoryBase { PUBLISHED: INLINE static BulletAllHitsRayResult empty(); diff --git a/panda/src/bullet/bulletClosestHitRayResult.h b/panda/src/bullet/bulletClosestHitRayResult.h index fd351d3aac..8cd043a22e 100644 --- a/panda/src/bullet/bulletClosestHitRayResult.h +++ b/panda/src/bullet/bulletClosestHitRayResult.h @@ -20,13 +20,14 @@ #include "bullet_utils.h" #include "luse.h" +#include "memoryBase.h" #include "pandaNode.h" #include "collideMask.h" /** * */ -struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback { +struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback, public MemoryBase { PUBLISHED: INLINE static BulletClosestHitRayResult empty(); diff --git a/panda/src/bullet/bulletClosestHitSweepResult.h b/panda/src/bullet/bulletClosestHitSweepResult.h index 2581cd34ff..4cdb09da20 100644 --- a/panda/src/bullet/bulletClosestHitSweepResult.h +++ b/panda/src/bullet/bulletClosestHitSweepResult.h @@ -20,13 +20,14 @@ #include "bullet_utils.h" #include "luse.h" +#include "memoryBase.h" #include "pandaNode.h" #include "collideMask.h" /** * */ -struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback { +struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback, public MemoryBase { PUBLISHED: INLINE static BulletClosestHitSweepResult empty(); diff --git a/panda/src/device/inputDeviceManager.h b/panda/src/device/inputDeviceManager.h index 66381c79e0..f82167c7bb 100644 --- a/panda/src/device/inputDeviceManager.h +++ b/panda/src/device/inputDeviceManager.h @@ -30,7 +30,7 @@ class WinRawInputDevice; * * @since 1.10.0 */ -class EXPCL_PANDA_DEVICE InputDeviceManager { +class EXPCL_PANDA_DEVICE InputDeviceManager : public MemoryBase { protected: InputDeviceManager(); ~InputDeviceManager() = default; diff --git a/panda/src/device/trackerData.h b/panda/src/device/trackerData.h index 7fdd756bb6..aba9fb6420 100644 --- a/panda/src/device/trackerData.h +++ b/panda/src/device/trackerData.h @@ -20,7 +20,7 @@ /** * Stores the kinds of data that a tracker might output. */ -class EXPCL_PANDA_DEVICE TrackerData { +class EXPCL_PANDA_DEVICE TrackerData : public MemoryBase { public: INLINE TrackerData(); INLINE TrackerData(const TrackerData ©); diff --git a/panda/src/egg/eggTransform.h b/panda/src/egg/eggTransform.h index 671a3b1155..529b84d174 100644 --- a/panda/src/egg/eggTransform.h +++ b/panda/src/egg/eggTransform.h @@ -16,6 +16,7 @@ #include "pandabase.h" #include "luse.h" +#include "memoryBase.h" #include "eggObject.h" /** @@ -26,7 +27,7 @@ * This may be either a 3-d transform, and therefore described by a 4x4 * matrix, or a 2-d transform, described by a 3x3 matrix. */ -class EXPCL_PANDA_EGG EggTransform { +class EXPCL_PANDA_EGG EggTransform : public MemoryBase { PUBLISHED: EggTransform(); EggTransform(const EggTransform ©); diff --git a/panda/src/gobj/samplerState.h b/panda/src/gobj/samplerState.h index bef695cd83..c161d48206 100644 --- a/panda/src/gobj/samplerState.h +++ b/panda/src/gobj/samplerState.h @@ -17,8 +17,8 @@ #include "pandabase.h" #include "typedObject.h" -#include "namable.h" #include "luse.h" +#include "memoryBase.h" #include "numeric_types.h" #include "bamReader.h" #include "config_gobj.h" @@ -33,7 +33,7 @@ class SamplerContext; * can be used to sample the same texture using different settings in * different places. */ -class EXPCL_PANDA_GOBJ SamplerState { +class EXPCL_PANDA_GOBJ SamplerState : public MemoryBase { PUBLISHED: enum FilterType { // Mag Filter and Min Filter diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index a0b8541e85..a0e0180bdd 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -102,7 +102,7 @@ private: Thread *current_thread) const; // Chunk data - struct Chunk { + struct Chunk : public MemoryBase { // Depth, starting at 0 size_t depth; @@ -115,12 +115,12 @@ private: // Children, in the order (0, 0) (1, 0) (0, 1) (1, 1) Chunk* children[4]; - // Chunk heights, used for culling - PN_stdfloat avg_height, min_height, max_height; - // Edge heights, used for lod computation, in the same order as the children LVector4 edges; + // Chunk heights, used for culling + PN_stdfloat avg_height, min_height, max_height; + // Last CLOD factor, stored while computing LOD, used for seamless transitions between lods PN_stdfloat last_clod; diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index e3cec2090f..5400f0872b 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -16,7 +16,7 @@ class FLOATNAME(UnalignedLMatrix4); /** * This is a 4-by-4 transform matrix. */ -class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LMatrix4) { +class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LMatrix4) : public MemoryBase { public: typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index 129b427c2d..8e9fe9a81d 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -20,7 +20,7 @@ class FLOATNAME(UnalignedLVecBase4); /** * This is the base class for all three-component vectors and points. */ -class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LVecBase4) { +class EXPCL_PANDA_LINMATH ALIGN_LINMATH FLOATNAME(LVecBase4) : public MemoryBase { PUBLISHED: typedef FLOATTYPE numeric_type; typedef const FLOATTYPE *iterator; diff --git a/panda/src/mathutil/perlinNoise.h b/panda/src/mathutil/perlinNoise.h index a109c43d56..aafd9c2712 100644 --- a/panda/src/mathutil/perlinNoise.h +++ b/panda/src/mathutil/perlinNoise.h @@ -18,6 +18,7 @@ #include "pvector.h" #include "vector_int.h" #include "luse.h" +#include "memoryBase.h" #include "randomizer.h" /** @@ -25,7 +26,7 @@ * dimensions of Perlin noise implementation. The base class just collects * the common functionality. */ -class EXPCL_PANDA_MATHUTIL PerlinNoise { +class EXPCL_PANDA_MATHUTIL PerlinNoise : public MemoryBase { protected: PerlinNoise(int table_size, unsigned long seed); PerlinNoise(const PerlinNoise ©); diff --git a/panda/src/mathutil/triangulator.h b/panda/src/mathutil/triangulator.h index 0514917cfd..f0c7ca4c75 100644 --- a/panda/src/mathutil/triangulator.h +++ b/panda/src/mathutil/triangulator.h @@ -16,6 +16,7 @@ #include "pandabase.h" #include "luse.h" +#include "memoryBase.h" #include "vector_int.h" /** @@ -29,7 +30,7 @@ * * It works strictly on 2-d points. See Triangulator3 for 3-d points. */ -class EXPCL_PANDA_MATHUTIL Triangulator { +class EXPCL_PANDA_MATHUTIL Triangulator : public MemoryBase { PUBLISHED: Triangulator(); diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index dbbf617ff0..2901ccb60d 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -37,7 +37,7 @@ * This is a small container class that can hold any one of the value types * that can be passed as input to a shader. */ -class EXPCL_PANDA_PGRAPH ShaderInput { +class EXPCL_PANDA_PGRAPH ShaderInput : public MemoryBase { PUBLISHED: // Used when binding texture images. enum AccessFlags { diff --git a/panda/src/pnmimage/pnmImageHeader.h b/panda/src/pnmimage/pnmImageHeader.h index 7030e06d2e..6e21ddca98 100644 --- a/panda/src/pnmimage/pnmImageHeader.h +++ b/panda/src/pnmimage/pnmImageHeader.h @@ -20,6 +20,7 @@ #include "typedObject.h" #include "filename.h" +#include "memoryBase.h" #include "pnotify.h" #include "pmap.h" #include "pvector.h" @@ -37,7 +38,7 @@ class PNMWriter; * image except the image data itself. It's the sort of information you * typically read from the image file's header. */ -class EXPCL_PANDA_PNMIMAGE PNMImageHeader { +class EXPCL_PANDA_PNMIMAGE PNMImageHeader : public MemoryBase { PUBLISHED: INLINE PNMImageHeader(); INLINE PNMImageHeader(const PNMImageHeader ©); diff --git a/panda/src/text/textGraphic.h b/panda/src/text/textGraphic.h index d8a3e1a476..9d6115ef5e 100644 --- a/panda/src/text/textGraphic.h +++ b/panda/src/text/textGraphic.h @@ -17,6 +17,7 @@ #include "pandabase.h" #include "config_text.h" +#include "memoryBase.h" #include "nodePath.h" /** @@ -34,7 +35,7 @@ * within this rectangle, but if it does not, it may visually overlap with * nearby text. */ -class EXPCL_PANDA_TEXT TextGraphic { +class EXPCL_PANDA_TEXT TextGraphic : public MemoryBase { PUBLISHED: INLINE TextGraphic(); INLINE explicit TextGraphic(const NodePath &model, const LVecBase4 &frame); diff --git a/panda/src/text/textProperties.h b/panda/src/text/textProperties.h index aca592e30b..cc6c0b4992 100644 --- a/panda/src/text/textProperties.h +++ b/panda/src/text/textProperties.h @@ -18,6 +18,7 @@ #include "config_text.h" #include "luse.h" +#include "memoryBase.h" #include "textFont.h" #include "pointerTo.h" #include "renderState.h" @@ -38,7 +39,7 @@ * the string; each nested TextProperties structure modifies the appearance of * subsequent text within the block. */ -class EXPCL_PANDA_TEXT TextProperties { +class EXPCL_PANDA_TEXT TextProperties : public MemoryBase { PUBLISHED: enum Alignment { A_left, From 2334f48e8955f713ed095ee9abd9795ae1a51376 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 09:58:00 +0100 Subject: [PATCH 083/166] makepanda: Always use /BIGOBJ when compiling with Eigen Anything from pgraph onward needs it due to the size of Eigen, but let's just always pass it so we don't constantly have to chase compiler errors --- makepanda/makepanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ce8d63b64b..f11797c44f 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1241,7 +1241,7 @@ def CompileCxx(obj,src,opts): if (building): cmd += " /DBUILDING_" + building - if ("BIGOBJ" in opts) or GetTargetArch() == 'x64': + if ("BIGOBJ" in opts) or GetTargetArch() == 'x64' or not PkgSkip("EIGEN"): cmd += " /bigobj" cmd += " /Zm300" From ad187b29f826b80dd5b331a5283d43ee8f277911 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 10:01:57 +0100 Subject: [PATCH 084/166] makepanda: Force choose correct extension suffix for Windows Even when cross-compiling for 32-bit using a 64-bit copy of Python, and vice versa --- makepanda/makepandacore.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 0b7225432e..405ce75fc0 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -3489,11 +3489,17 @@ def SetOrigExt(x, v): ORIG_EXT[x] = v def GetExtensionSuffix(): - if sys.version_info >= (3, 0): + target = GetTarget() + + if sys.version_info >= (3, 5) and target == 'windows': + if GetTargetArch() == 'x64': + return '.cp%d%d-win_amd64.pyd' % (sys.version_info[:2]) + else: + return '.cp%d%d-win32.pyd' % (sys.version_info[:2]) + elif sys.version_info >= (3, 0): import _imp return _imp.extension_suffixes()[0] - target = GetTarget() if target == 'windows': return '.pyd' else: From 930e5da43816bb60bd4968618bd0368465d6f3f3 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 10:42:45 +0100 Subject: [PATCH 085/166] texture: Fix get_ram_image_as() with 3D and multiview textures Fixes #1277 --- panda/src/gobj/texture.cxx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index b0ad695ba1..318eaf59c9 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1072,13 +1072,13 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { if (format == "A" && cdata->_num_components != 3) { // We can generally rely on alpha to be the last component. int component = cdata->_num_components - 1; - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { newdata[component] = image[p]; } do_set_ram_image(cdata, newdata); return; } - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (uchar s = 0; s < format.size(); ++s) { signed char component = -1; if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) { @@ -1110,7 +1110,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) { do_set_ram_image(cdata, newdata); return; } - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (uchar s = 0; s < format.size(); ++s) { signed char component = -1; if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) { @@ -7405,7 +7405,8 @@ get_ram_image_as(const string &requested_format) { gobj_cat.error() << "Couldn't find an uncompressed RAM image!\n"; return CPTA_uchar(get_class_type()); } - int imgsize = cdata->_x_size * cdata->_y_size; + size_t imgsize = (size_t)cdata->_x_size * (size_t)cdata->_y_size * + (size_t)cdata->_z_size * (size_t)cdata->_num_views; nassertr(cdata->_num_components > 0 && cdata->_num_components <= 4, CPTA_uchar(get_class_type())); nassertr(data.size() == (size_t)(cdata->_component_width * cdata->_num_components * imgsize), CPTA_uchar(get_class_type())); @@ -7443,7 +7444,7 @@ get_ram_image_as(const string &requested_format) { const uint32_t *src = (const uint32_t *)data.p(); uint32_t *dst = (uint32_t *)newdata.p(); - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { uint32_t v = *src++; *dst++ = ((v & 0xff00ff00u)) | ((v & 0x00ff0000u) >> 16) | @@ -7530,14 +7531,14 @@ get_ram_image_as(const string &requested_format) { } if (format == "A" && cdata->_num_components != 3) { // We can generally rely on alpha to be the last component. - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { dst[p] = src[alpha]; src += cdata->_num_components; } return newdata; } // Fallback case for other 8-bit-per-channel formats. - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (size_t i = 0; i < format.size(); ++i) { if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) { *dst++ = src[0]; @@ -7563,7 +7564,7 @@ get_ram_image_as(const string &requested_format) { } // The slow and general case. - for (int p = 0; p < imgsize; ++p) { + for (size_t p = 0; p < imgsize; ++p) { for (size_t i = 0; i < format.size(); ++i) { int component = 0; if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) { From 76fb49252c158b534943eac5e5be69d643fce4e7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 10:45:57 +0100 Subject: [PATCH 086/166] dist: Fix regression with data_dir NameError in build_apps Fixes #1276 Also let's consistently use the term "assets" for all the application data (and not the gaming-specific term "game files") and use the term "data" for package data only --- direct/src/dist/commands.py | 19 ++++++++++++------- makepanda/makepandacore.py | 7 ++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index cd31b3073e..5af82606a3 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -513,7 +513,9 @@ class build_apps(setuptools.Command): else: # e.g. x86, x86_64, mips, mips64 suffix = '_' + abi.replace('-', '_') - self.build_binaries(lib_dir, platform + suffix) + # We end up copying the data multiple times to the same + # directory, but that's probably fine for now. + self.build_binaries(platform + suffix, lib_dir, data_dir) # Write out the icons to the res directory. for appname, icon in self.icon_objects.items(): @@ -532,13 +534,13 @@ class build_apps(setuptools.Command): if icon.getLargestSize() >= 192: icon.writeSize(192, os.path.join(res_dir, 'mipmap-xxxhdpi-v4', basename)) - self.build_data(data_dir, platform) + self.build_assets(platform, data_dir) # Generate an AndroidManifest.xml self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml')) else: - self.build_binaries(build_dir, platform) - self.build_data(build_dir, platform) + self.build_binaries(platform, build_dir, build_dir) + self.build_assets(platform, build_dir) # Bundle into an .app on macOS if self.macos_main_app and 'macosx' in platform: @@ -750,7 +752,7 @@ class build_apps(setuptools.Command): with open(path, 'wb') as fh: tree.write(fh, encoding='utf-8', xml_declaration=True) - def build_binaries(self, binary_dir, platform): + def build_binaries(self, platform, binary_dir, data_dir=None): """ Builds the binary data for the given platform. """ use_wheels = True @@ -1121,6 +1123,9 @@ class build_apps(setuptools.Command): os.path.join(binary_dir, '..', '..', 'classes.dex')) # Extract any other data files from dependency packages. + if data_dir is None: + return + for module, datadesc in self.package_data_dirs.items(): if module not in freezer_modules: continue @@ -1161,11 +1166,11 @@ class build_apps(setuptools.Command): else: self.copy(source_path, target_path) - def build_data(self, data_dir, platform): + def build_assets(self, platform, data_dir): """ Builds the data files for the given platform. """ # Copy Game Files - self.announce('Copying game files for platform: {}'.format(platform), distutils.log.INFO) + self.announce('Copying assets for platform: {}'.format(platform), distutils.log.INFO) ignore_copy_list = [ '**/__pycache__/**', '**/*.pyc', diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index f0e2708401..25565db8f1 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -3366,7 +3366,12 @@ def SetOrigExt(x, v): ORIG_EXT[x] = v def GetExtensionSuffix(): - if CrossCompiling(): + if GetTarget() == 'windows': + if GetTargetArch() == 'x64': + return '.cp%d%d-win_amd64.pyd' % (sys.version_info[:2]) + else: + return '.cp%d%d-win32.pyd' % (sys.version_info[:2]) + elif CrossCompiling(): return '.{0}.so'.format(GetPythonABI()) else: import _imp From 21cfb8dba5b4e90ad99171e0ffe9142bccd0c9df Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 14:15:05 +0100 Subject: [PATCH 087/166] readme: Update instructions for building for Android [skip ci] --- README.md | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0213db141d..5e7a358e30 100644 --- a/README.md +++ b/README.md @@ -177,35 +177,32 @@ directory which you can install using `pkg install`. Android ------- -Note: building on Android is very experimental and not guaranteed to work. +Although it's possible to build Panda3D on an Android device itself using the +[termux](https://termux.com/) shell, the recommended route is to cross-compile +.whl files using the SDK and NDK, which can then be used by the `build_apps` +command to build a Python application into an .apk or .aab bundle. You will +need to get the latest thirdparty packages, which can be obtained from the +artifacts page of the last successful run here: -You can experimentally build the Android Python runner via the [termux](https://termux.com/) -shell. You will need to install [Termux](https://play.google.com/store/apps/details?id=com.termux) -and [Termux API](https://play.google.com/store/apps/details?id=com.termux.api) -from the Play Store. Many of the dependencies can be installed by running the -following command in the Termux shell: +https://github.com/rdb/panda3d-thirdparty/actions?query=branch%3Amain+is%3Asuccess+event%3Apush + +This does not include Python at the moment, which can be extracted from +[this archive](https://rdb.name/thirdparty-android.tar.gz) instead. + +These commands show how to compile wheels for the supported Android ABIs: ```bash -pkg install python ndk-sysroot clang bison freetype harfbuzz libpng eigen openal-soft opusfile libvorbis assimp libopus ecj dx patchelf aapt apksigner libcrypt openssl pkg-config +export ANDROID_SDK_ROOT=/home/rdb/local/android +python3.8 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel +python3.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel +python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel +python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-19 --threads 6 --wheel ``` -Then, you can build the .apk using this command: +It is now possible to use the generated wheels with `build_apps`, as explained +on this page: -```bash -python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer -``` - -You can install the generated panda3d.apk by browsing to the panda3d folder -using a file manager. You may need to copy it to `/sdcard` to be able to -access it from other apps. - -To launch a Python program from Termux, you can use the `run_python.sh` script -inside the `panda/src/android` directory. It will launch Python in a separate -activity, load it with the Python script you passed as argument, and use a -socket for returning the command-line output to the Termux shell. Do note -that this requires the Python application to reside on the SD card and that -Termux needs to be set up with access to the SD card (using the -`termux-setup-storage` command). +https://discourse.panda3d.org/t/deployment-for-android/28226 Running Tests ============= From 8cdac14db3bf803b59fed167f1480a3a34826089 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 2 Mar 2022 14:38:02 +0100 Subject: [PATCH 088/166] collide: First pass at reducing memory overhead of CollisionBox This is just the low-hanging fruit, there are a lot more gains to be realized. --- panda/src/collide/collisionBox.I | 40 +-------- panda/src/collide/collisionBox.cxx | 131 ++++++++++++----------------- panda/src/collide/collisionBox.h | 17 ++-- 3 files changed, 63 insertions(+), 125 deletions(-) diff --git a/panda/src/collide/collisionBox.I b/panda/src/collide/collisionBox.I index 62c58cbceb..86473473cf 100644 --- a/panda/src/collide/collisionBox.I +++ b/panda/src/collide/collisionBox.I @@ -17,11 +17,10 @@ */ INLINE CollisionBox:: CollisionBox(const LPoint3 ¢er, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) : - _center(center), _x(x), _y(y), _z(z) + _center(center) { - _min = LPoint3(_center.get_x() - _x, _center.get_y() - _y, _center.get_z() - _z); - _max = LPoint3(_center.get_x() + _x, _center.get_y() + _y, _center.get_z() + _z); - _radius = sqrt(_x*_x + _y*_y + _z*_z); + _min = LPoint3(_center.get_x() - x, _center.get_y() - y, _center.get_z() - z); + _max = LPoint3(_center.get_x() + x, _center.get_y() + y, _center.get_z() + z); for(int v = 0; v < 8; v++) _vertex[v] = get_point_aabb(v); for(int p = 0; p < 6; p++) @@ -37,10 +36,6 @@ CollisionBox(const LPoint3 &min, const LPoint3 &max) : _min(min), _max(max) { _center = (_min + _max) / 2; - _x = _center.get_x() - _min.get_x(); - _y = _center.get_y() - _min.get_y(); - _z = _center.get_z() - _min.get_z(); - _radius = sqrt(_x*_x + _y*_y + _z*_z); for(int v = 0; v < 8; v++) _vertex[v] = get_point_aabb(v); for(int p = 0; p < 6; p++) @@ -63,11 +58,7 @@ CollisionBox(const CollisionBox ©) : CollisionSolid(copy), _center(copy._center), _min(copy._min), - _max(copy._max), - _x(copy._x ), - _y(copy._y ), - _z(copy._z ), - _radius(copy._radius ) + _max(copy._max) { for(int v = 0; v < 8; v++) _vertex[v] = copy._vertex[v]; @@ -152,7 +143,6 @@ get_point(int n) const { return _vertex[n]; } - /** * Returns the nth vertex of the Axis Aligned Bounding Box. */ @@ -243,20 +233,6 @@ calc_to_3d_mat(LMatrix4 &to_3d_mat,int plane) const { to_3d_mat.set_row(3, get_plane(plane).get_point()); } -/** - * Fills the indicated matrix with the appropriate rotation transform to move - * points from the 2-d plane into the 3-d (X, 0, Z) plane. - * - * This is essentially similar to calc_to_3d_mat, except that the matrix is - * rederived from whatever is stored in _to_2d_mat, guaranteeing that it will - * match whatever algorithm produced that one, even if it was produced on a - * different machine with different numerical precision. - */ -INLINE void CollisionBox:: -rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const { - to_3d_mat.invert_from(_to_2d_mat[plane]); -} - /** * Extrude the indicated point in the polygon's 2-d definition space back into * 3-d coordinates. @@ -295,11 +271,3 @@ operator = (const CollisionBox::PointDef ©) { _p = copy._p; _v = copy._v; } - -/** - * returns the points that form the nth plane - */ -INLINE CollisionBox::Points CollisionBox:: -get_plane_points(int n) { - return _points[n]; -} diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index 278f8516ff..cb1d9a3c92 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -66,14 +66,11 @@ make_copy() { * Compute parameters for each of the box's sides */ void CollisionBox:: -setup_box(){ - for(int plane = 0; plane < 6; plane++) { - LPoint3 array[4]; - array[0] = get_point(plane_def[plane][0]); - array[1] = get_point(plane_def[plane][1]); - array[2] = get_point(plane_def[plane][2]); - array[3] = get_point(plane_def[plane][3]); - setup_points(array, array+4, plane); +setup_box() { + assert(sizeof(_points) / sizeof(_points[0]) == 6); + assert(sizeof(_points[0]) / sizeof(_points[0][0]) == 4); + for (int plane = 0; plane < 6; plane++) { + setup_points(plane); } } @@ -81,11 +78,8 @@ setup_box(){ * Computes the plane and 2d projection of points that make up this side. */ void CollisionBox:: -setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) { - int num_points = end - begin; - nassertv(num_points >= 3); - - _points[plane].clear(); +setup_points(int plane) { + PointDef *points = _points[plane]; // Construct a matrix that rotates the points from the (X,0,Z) plane into // the 3-d plane. @@ -96,32 +90,15 @@ setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) { _to_2d_mat[plane].invert_from(to_3d_mat); // Now project all of the points onto the 2-d plane. - - const LPoint3 *pi; - for (pi = begin; pi != end; ++pi) { - LPoint3 point = (*pi) * _to_2d_mat[plane]; - _points[plane].push_back(PointDef(point[0], point[2])); + for (size_t i = 0; i < 4; ++i) { + LPoint3 point = get_point(plane_def[plane][i]) * _to_2d_mat[plane]; + points[i] = PointDef(point[0], point[2]); } - nassertv(_points[plane].size() >= 3); - -#ifndef NDEBUG - /* - // Now make sure the points define a convex polygon. - if (is_concave()) { - collide_cat.error() << "Invalid concave CollisionPolygon defined:\n"; - const LPoint3 *pi; - for (pi = begin; pi != end; ++pi) { - collide_cat.error(false) << " " << (*pi) << "\n"; + for (size_t i = 0; i < 4; i++) { + points[i]._v = points[(i + 1) % 4]._p - points[i]._p; + points[i]._v.normalize(); } - collide_cat.error(false) - << " normal " << normal << " with length " << normal.length() << "\n"; - _points.clear(); - } - */ -#endif - - compute_vectors(_points[plane]); } /** @@ -146,10 +123,6 @@ xform(const LMatrix4 &mat) { for(int p = 0; p < 6 ; p++) { _planes[p] = set_plane(p); } - _x = _vertex[0].get_x() - _center.get_x(); - _y = _vertex[0].get_y() - _center.get_y(); - _z = _vertex[0].get_z() - _center.get_z(); - _radius = sqrt(_x * _x + _y * _y + _z * _z); setup_box(); mark_viz_stale(); mark_internal_bounds_stale(); @@ -196,7 +169,11 @@ output(std::ostream &out) const { */ PT(BoundingVolume) CollisionBox:: compute_internal_bounds() const { - return new BoundingSphere(_center, _radius); + PN_stdfloat x = _vertex[0].get_x() - _center.get_x(); + PN_stdfloat y = _vertex[0].get_y() - _center.get_y(); + PN_stdfloat z = _vertex[0].get_z() - _center.get_z(); + PN_stdfloat radius = sqrt(x * x + y * y + z * z); + return new BoundingSphere(_center, radius); } /** @@ -233,10 +210,8 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { LVector3 normal; for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) { - plane = get_plane( ip ); - if (_points[ip].size() < 3) { - continue; - } + plane = get_plane(ip); + if (wrt_prev_space != wrt_space) { // If we have a delta between the previous position and the current // position, we use that to determine some more properties of the @@ -322,17 +297,17 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { Points new_points; if (apply_clip_plane(new_points, cpa, entry.get_into_node_path().get_net_transform(),ip)) { // All points are behind the clip plane; just do the default test. - edge_dist = dist_to_polygon(p, _points[ip]); + edge_dist = dist_to_polygon(p, _points[ip], 4); } else if (new_points.empty()) { // The polygon is completely clipped. continue; } else { // Test against the clipped polygon. - edge_dist = dist_to_polygon(p, new_points); + edge_dist = dist_to_polygon(p, new_points.data(), new_points.size()); } } else { // No clip plane is in effect. Do the default test. - edge_dist = dist_to_polygon(p, _points[ip]); + edge_dist = dist_to_polygon(p, _points[ip], 4); } max_dist = from_radius; @@ -1129,13 +1104,13 @@ apply_clip_plane(CollisionBox::Points &new_points, LPlane plane = plane_node->get_plane() * new_transform->get_mat(); if (first_plane) { first_plane = false; - if (!clip_polygon(new_points, _points[plane_no], plane, plane_no)) { + if (!clip_polygon(new_points, _points[plane_no], 4, plane, plane_no)) { all_in = false; } } else { Points last_points; last_points.swap(new_points); - if (!clip_polygon(new_points, last_points, plane, plane_no)) { + if (!clip_polygon(new_points, last_points.data(), last_points.size(), plane, plane_no)) { all_in = false; } } @@ -1158,10 +1133,10 @@ apply_clip_plane(CollisionBox::Points &new_points, */ bool CollisionBox:: clip_polygon(CollisionBox::Points &new_points, - const CollisionBox::Points &source_points, + const PointDef *source_points, size_t num_source_points, const LPlane &plane, int plane_no) const { new_points.clear(); - if (source_points.empty()) { + if (num_source_points == 0) { return true; } @@ -1173,7 +1148,7 @@ clip_polygon(CollisionBox::Points &new_points, if (plane.dist_to_plane(get_plane(plane_no).get_point()) < 0.0) { // A point within the polygon is behind the clipping plane: the polygon // is all in. - new_points = source_points; + new_points.insert(new_points.end(), source_points, source_points + num_source_points); return true; } return false; @@ -1194,14 +1169,13 @@ clip_polygon(CollisionBox::Points &new_points, // We might increase the number of vertices by as many as 1, if the plane // clips off exactly one corner. (We might also decrease the number of // vertices, or keep them the same number.) - new_points.reserve(source_points.size() + 1); + new_points.reserve(num_source_points + 1); - LPoint2 last_point = source_points.back()._p; + LPoint2 last_point = source_points[num_source_points - 1]._p; bool last_is_in = !is_right(last_point - from2d, delta2d); bool all_in = last_is_in; - Points::const_iterator pi; - for (pi = source_points.begin(); pi != source_points.end(); ++pi) { - const LPoint2 &this_point = (*pi)._p; + for (size_t pi = 0; pi < num_source_points; ++pi) { + const LPoint2 &this_point = source_points[pi]._p; bool this_is_in = !is_right(this_point - from2d, delta2d); // There appears to be a compiler bug in gcc 4.0: we need to extract this @@ -1234,15 +1208,13 @@ clip_polygon(CollisionBox::Points &new_points, return all_in; } - /** * Returns the linear distance from the 2-d point to the nearest part of the * polygon defined by the points vector. The result is negative if the point * is within the polygon. */ PN_stdfloat CollisionBox:: -dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const { - +dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const { // We know that that the polygon is convex and is defined with the points in // counterclockwise order. Therefore, we simply compare the signed distance // to each line segment; we ignore any negative values, and take the minimum @@ -1254,10 +1226,9 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const { bool got_dist = false; PN_stdfloat best_dist = -1.0f; - size_t num_points = points.size(); for (size_t i = 0; i < num_points - 1; ++i) { PN_stdfloat d = dist_to_line_segment(p, points[i]._p, points[i + 1]._p, - points[i]._v); + points[i]._v); if (d >= 0.0f) { if (!got_dist || d < best_dist) { best_dist = d; @@ -1267,7 +1238,7 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const { } PN_stdfloat d = dist_to_line_segment(p, points[num_points - 1]._p, points[0]._p, - points[num_points - 1]._v); + points[num_points - 1]._v); if (d >= 0.0f) { if (!got_dist || d < best_dist) { best_dist = d; @@ -1450,10 +1421,14 @@ write_datagram(BamWriter *manager, Datagram &me) { for(int i=0; i < 8; i++) { _vertex[i].write_datagram(me); } - me.add_stdfloat(_radius); - me.add_stdfloat(_x); - me.add_stdfloat(_y); - me.add_stdfloat(_z); + PN_stdfloat x = _vertex[0].get_x() - _center.get_x(); + PN_stdfloat y = _vertex[0].get_y() - _center.get_y(); + PN_stdfloat z = _vertex[0].get_z() - _center.get_z(); + PN_stdfloat radius = sqrt(x * x + y * y + z * z); + me.add_stdfloat(radius); + me.add_stdfloat(x); + me.add_stdfloat(y); + me.add_stdfloat(z); for(int i=0; i < 6; i++) { _planes[i].write_datagram(me); } @@ -1461,8 +1436,8 @@ write_datagram(BamWriter *manager, Datagram &me) { _to_2d_mat[i].write_datagram(me); } for(int i=0; i < 6; i++) { - me.add_uint16(_points[i].size()); - for (size_t j = 0; j < _points[i].size(); j++) { + me.add_uint16(4); + for (size_t j = 0; j < 4; j++) { _points[i][j]._p.write_datagram(me); _points[i][j]._v.write_datagram(me); } @@ -1497,10 +1472,10 @@ fillin(DatagramIterator& scan, BamReader* manager) { for(int i=0; i < 8; i++) { _vertex[i].read_datagram(scan); } - _radius = scan.get_stdfloat(); - _x = scan.get_stdfloat(); - _y = scan.get_stdfloat(); - _z = scan.get_stdfloat(); + scan.get_stdfloat(); + scan.get_stdfloat(); + scan.get_stdfloat(); + scan.get_stdfloat(); for(int i=0; i < 6; i++) { _planes[i].read_datagram(scan); } @@ -1509,12 +1484,10 @@ fillin(DatagramIterator& scan, BamReader* manager) { } for(int i=0; i < 6; i++) { size_t size = scan.get_uint16(); + nassertv(size == 4); for (size_t j = 0; j < size; j++) { - LPoint2 p; - LVector2 v; - p.read_datagram(scan); - v.read_datagram(scan); - _points[i].push_back(PointDef(p, v)); + _points[i][j]._p.read_datagram(scan); + _points[i][j]._v.read_datagram(scan); } } } diff --git a/panda/src/collide/collisionBox.h b/panda/src/collide/collisionBox.h index 4bf96f0212..69a0e13889 100644 --- a/panda/src/collide/collisionBox.h +++ b/panda/src/collide/collisionBox.h @@ -99,7 +99,6 @@ private: LPoint3 _center; LPoint3 _min; LPoint3 _max; - PN_stdfloat _x, _y, _z, _radius; LPoint3 _vertex[8]; // Each of the Eight Vertices of the Box LPlane _planes[6]; //Points to each of the six sides of the Box @@ -119,6 +118,7 @@ private: public: class PointDef { public: + PointDef() = default; INLINE PointDef(const LPoint2 &p, const LVector2 &v); INLINE PointDef(PN_stdfloat x, PN_stdfloat y); INLINE PointDef(const PointDef ©); @@ -134,25 +134,22 @@ public: const Points &points) const; bool point_is_inside(const LPoint2 &p, const Points &points) const; - PN_stdfloat dist_to_polygon(const LPoint2 &p, const Points &points) const; + PN_stdfloat dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const; - void setup_points(const LPoint3 *begin, const LPoint3 *end, int plane); + void setup_points(int plane); INLINE LPoint2 to_2d(const LVecBase3 &point3d, int plane) const; INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const; - INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const; INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat); - bool clip_polygon(Points &new_points, const Points &source_points, - const LPlane &plane,int plane_no) const; + bool clip_polygon(Points &new_points, const PointDef *source_points, + size_t num_source_points, const LPlane &plane, + int plane_no) const; bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa, const TransformState *net_transform, int plane_no) const; private: - Points _points[6]; // one set of points for each of the six planes that make up the box + PointDef _points[6][4]; // one set of points for each of the six planes that make up the box LMatrix4 _to_2d_mat[6]; -public: - INLINE Points get_plane_points( int n ); - public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &me); From 657a8f890c2633909531fd532ed787038672fd98 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 10:37:48 +0100 Subject: [PATCH 089/166] interrogate: Squelch weird "Manifests" output from interrogate_module --- dtool/src/interrogate/interfaceMaker.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index 867ec25ac2..3c5d7f6b57 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -237,7 +237,7 @@ generate_wrappers() { int num_global_elements = idb->get_num_global_elements(); for (int gi = 0; gi < num_global_elements; ++gi) { - printf(" Global Type = %d", gi); + //printf(" Global Type = %d", gi); TypeIndex type_index = idb->get_global_element(gi); record_object(type_index); } @@ -256,12 +256,12 @@ generate_wrappers() { FunctionIndex func_index = iman.get_getter(); record_function(dummy_type, func_index); } - printf(" Manifests %d\n", mi); + //printf(" Manifests %d\n", mi); } int num_elements = idb->get_num_global_elements(); for (int ei = 0; ei < num_elements; ei++) { - printf(" Element %d\n", ei); + //printf(" Element %d\n", ei); ElementIndex element_index = idb->get_global_element(ei); const InterrogateElement &ielement = idb->get_element(element_index); From 6b9dea3e3069c3ef05b26c3db9cc0863c125e312 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 10:46:02 +0100 Subject: [PATCH 090/166] cleanup: Fix comparison between pointer and 0 (instead of nullptr) --- panda/src/dxgsg9/dxShaderContext9.cxx | 2 +- panda/src/dxgsg9/wdxGraphicsBuffer9.cxx | 4 ++-- panda/src/dxgsg9/wdxGraphicsWindow9.cxx | 2 +- panda/src/glstuff/glCgShaderContext_src.cxx | 6 +++--- panda/src/glstuff/glGraphicsBuffer_src.cxx | 6 +++--- panda/src/gobj/material.I | 2 +- panda/src/wgldisplay/wglGraphicsBuffer.cxx | 14 +++++++------- panda/src/wgldisplay/wglGraphicsWindow.cxx | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/panda/src/dxgsg9/dxShaderContext9.cxx b/panda/src/dxgsg9/dxShaderContext9.cxx index 6c7a15d313..dd9b8f8233 100644 --- a/panda/src/dxgsg9/dxShaderContext9.cxx +++ b/panda/src/dxgsg9/dxShaderContext9.cxx @@ -693,7 +693,7 @@ update_shader_texture_bindings(DXShaderContext9 *prev, GSG *gsg) { continue; } - if (spec._suffix != 0) { + if (spec._suffix != nullptr) { // The suffix feature is inefficient. It is a temporary hack. tex = tex->load_related(spec._suffix); } diff --git a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx b/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx index 81fce0c90b..86a8c9ee41 100644 --- a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx @@ -266,7 +266,7 @@ rebuild_bitplanes() { // Decide how big the bitplanes should be. - if ((_host != 0)&&(_creation_flags & GraphicsPipe::BF_size_track_host)) { + if (_host != nullptr && (_creation_flags & GraphicsPipe::BF_size_track_host) != 0) { if (_host->get_size() != _size) { set_size_and_recalc(_host->get_x_size(), _host->get_y_size()); @@ -739,7 +739,7 @@ bool wdxGraphicsBuffer9:: open_buffer() { // GSG creationinitialization. - if (_gsg == 0) { + if (_gsg == nullptr) { // The code below doesn't support creating a GSG on the fly. Just error // out for now. _dxgsg = new DXGraphicsStateGuardian9(_engine, _pipe); // _gsg = _dxgsg; diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx index ef70d24879..b1694b5613 100644 --- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx @@ -261,7 +261,7 @@ open_window() { static ConfigVariableBool always_discard_device("always-discard-device", true); bool discard_device = always_discard_device; - if (_gsg == 0) { + if (_gsg == nullptr) { _dxgsg = new DXGraphicsStateGuardian9(_engine, _pipe); _gsg = _dxgsg; } else { diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 7d65fdd976..e4c3aedf36 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -1113,14 +1113,14 @@ update_shader_texture_bindings(ShaderContext *prev) { continue; } - if (spec._suffix != 0) { + if (spec._suffix != nullptr) { // The suffix feature is inefficient. It is a temporary hack. - if (tex == 0) { + if (tex == nullptr) { continue; } tex = tex->load_related(spec._suffix); } - if ((tex == 0) || (tex->get_texture_type() != spec._desired_type)) { + if (tex == nullptr || tex->get_texture_type() != spec._desired_type) { continue; } diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index c8641ebe6e..dbaf610d5a 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -360,7 +360,7 @@ check_fbo() { void CLP(GraphicsBuffer):: rebuild_bitplanes() { check_host_valid(); - if (_gsg == 0) { + if (_gsg == nullptr) { return; } @@ -1661,7 +1661,7 @@ close_buffer() { check_host_valid(); - if (_gsg == 0) { + if (_gsg == nullptr) { return; } @@ -1824,7 +1824,7 @@ unregister_shared_depth_buffer(GraphicsOutput *graphics_output) { */ void CLP(GraphicsBuffer):: report_my_errors(int line, const char *file) { - if (_gsg == 0) { + if (_gsg == nullptr) { GLenum error_code = glGetError(); if (error_code != GL_NO_ERROR) { GLCAT.error() << file << ", line " << line << ": GL error " << (int)error_code << "\n"; diff --git a/panda/src/gobj/material.I b/panda/src/gobj/material.I index dcaef86980..bd717f835c 100644 --- a/panda/src/gobj/material.I +++ b/panda/src/gobj/material.I @@ -58,7 +58,7 @@ INLINE Material:: */ INLINE Material *Material:: get_default() { - if (_default == 0) { + if (_default == nullptr) { _default = new Material("default"); } return _default; diff --git a/panda/src/wgldisplay/wglGraphicsBuffer.cxx b/panda/src/wgldisplay/wglGraphicsBuffer.cxx index aacedf0ae3..207fa7eeaf 100644 --- a/panda/src/wgldisplay/wglGraphicsBuffer.cxx +++ b/panda/src/wgldisplay/wglGraphicsBuffer.cxx @@ -157,7 +157,7 @@ bind_texture_to_pbuffer() { if (tex_index >= 0) { const RenderTexture &rt = cdata->_textures[tex_index]; Texture *tex = rt._texture; - if ((_pbuffer_bound != 0)&&(_pbuffer_bound != tex)) { + if (_pbuffer_bound != nullptr && _pbuffer_bound != tex) { _pbuffer_bound->release(wglgsg->get_prepared_objects()); _pbuffer_bound = 0; } @@ -188,7 +188,7 @@ bind_texture_to_pbuffer() { } _pbuffer_bound = tex; } else { - if (_pbuffer_bound != 0) { + if (_pbuffer_bound != nullptr) { _pbuffer_bound->release(wglgsg->get_prepared_objects()); _pbuffer_bound = 0; } @@ -292,7 +292,7 @@ open_buffer() { // GSG creationinitialization. wglGraphicsStateGuardian *wglgsg; - if (_gsg == 0) { + if (_gsg == nullptr) { // There is no old gsg. Create a new one. wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr); wglgsg->choose_pixel_format(_fb_properties, true); @@ -355,16 +355,16 @@ open_buffer() { */ void wglGraphicsBuffer:: release_pbuffer() { - if (_gsg == 0) { + if (_gsg == nullptr) { return; } wglGraphicsStateGuardian *wglgsg; DCAST_INTO_V(wglgsg, _gsg); - if (_pbuffer_bound != 0) { + if (_pbuffer_bound != nullptr) { _pbuffer_bound->release(wglgsg->get_prepared_objects()); - _pbuffer_bound = 0; + _pbuffer_bound.clear(); } wglGraphicsPipe::wgl_make_current(0, 0, nullptr); if (_pbuffer_dc) { @@ -420,7 +420,7 @@ rebuild_bitplanes() { // Determine what pbuffer attributes are needed for currently-applicable // textures. - if ((_host != 0)&&(_creation_flags & GraphicsPipe::BF_size_track_host)) { + if (_host != nullptr && (_creation_flags & GraphicsPipe::BF_size_track_host) != 0) { if (_host->get_size() != _size) { set_size_and_recalc(_host->get_x_size(), _host->get_y_size()); diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index 1af86c6b95..c79ea4eb9f 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -200,7 +200,7 @@ open_window() { // GSG creationinitialization. wglGraphicsStateGuardian *wglgsg; - if (_gsg == 0) { + if (_gsg == nullptr) { // There is no old gsg. Create a new one. wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr); wglgsg->choose_pixel_format(_fb_properties, false); From 72f98ec30db842140c8207b6b22d9e8f87a7a75b Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 11:17:52 +0100 Subject: [PATCH 091/166] gobj: Delete assignment operator of Geom* classes They are unused and it would probably be a bad idea to try to use them. --- panda/src/gobj/geom.cxx | 20 ------------------- panda/src/gobj/geom.h | 3 ++- panda/src/gobj/geomPrimitive.cxx | 11 ----------- panda/src/gobj/geomPrimitive.h | 3 ++- panda/src/gobj/geomVertexArrayData.cxx | 24 ----------------------- panda/src/gobj/geomVertexArrayData.h | 3 ++- panda/src/gobj/geomVertexData.cxx | 27 -------------------------- panda/src/gobj/geomVertexData.h | 3 ++- panda/src/text/geomTextGlyph.cxx | 9 --------- panda/src/text/geomTextGlyph.h | 3 ++- 10 files changed, 10 insertions(+), 96 deletions(-) diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 9fedfbaef1..5733c4bee0 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -62,26 +62,6 @@ Geom(const Geom ©) : { } -/** - * The copy assignment operator is not pipeline-safe. This will completely - * obliterate all stages of the pipeline, so don't do it for a Geom that is - * actively being used for rendering. - */ -void Geom:: -operator = (const Geom ©) { - CopyOnWriteObject::operator = (copy); - - clear_cache(); - - _cycler = copy._cycler; - - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - mark_internal_bounds_stale(cdata); - } - CLOSE_ITERATE_ALL_STAGES(_cycler); -} - /** * */ diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 425d2b0a0a..073805e7ca 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -62,10 +62,11 @@ protected: Geom(const Geom ©); PUBLISHED: - void operator = (const Geom ©); virtual ~Geom(); ALLOC_DELETED_CHAIN(Geom); + void operator = (const Geom ©) = delete; + virtual Geom *make_copy() const; INLINE PrimitiveType get_primitive_type() const; diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 97f5306110..8e9b9039c0 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -77,17 +77,6 @@ GeomPrimitive(const GeomPrimitive ©) : { } -/** - * The copy assignment operator is not pipeline-safe. This will completely - * obliterate all stages of the pipeline, so don't do it for a GeomPrimitive - * that is actively being used for rendering. - */ -void GeomPrimitive:: -operator = (const GeomPrimitive ©) { - CopyOnWriteObject::operator = (copy); - _cycler = copy._cycler; -} - /** * */ diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index 03d8a314aa..dab0a3b830 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -61,10 +61,11 @@ protected: PUBLISHED: explicit GeomPrimitive(UsageHint usage_hint); GeomPrimitive(const GeomPrimitive ©); - void operator = (const GeomPrimitive ©); virtual ~GeomPrimitive(); ALLOC_DELETED_CHAIN(GeomPrimitive); + void operator = (const GeomPrimitive ©) = delete; + virtual PT(GeomPrimitive) make_copy() const=0; virtual PrimitiveType get_primitive_type() const=0; diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index ae45b50f56..9b07969e46 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -107,30 +107,6 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : nassertv(_array_format->is_registered()); } -/** - * The copy assignment operator is not pipeline-safe. This will completely - * obliterate all stages of the pipeline, so don't do it for a - * GeomVertexArrayData that is actively being used for rendering. - */ -void GeomVertexArrayData:: -operator = (const GeomVertexArrayData ©) { - CopyOnWriteObject::operator = (copy); - SimpleLruPage::operator = (copy); - - copy.mark_used_lru(); - - _array_format = copy._array_format; - _cycler = copy._cycler; - - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_modified = Geom::get_next_modified(); - } - CLOSE_ITERATE_ALL_STAGES(_cycler); - - nassertv(_array_format->is_registered()); -} - /** * */ diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 917b27c71a..e7f1fa750d 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -66,10 +66,11 @@ PUBLISHED: explicit GeomVertexArrayData(const GeomVertexArrayFormat *array_format, UsageHint usage_hint); GeomVertexArrayData(const GeomVertexArrayData ©); - void operator = (const GeomVertexArrayData ©); virtual ~GeomVertexArrayData(); ALLOC_DELETED_CHAIN(GeomVertexArrayData); + void operator = (const GeomVertexArrayData ©) = delete; + int compare_to(const GeomVertexArrayData &other) const; INLINE const GeomVertexArrayFormat *get_array_format() const; diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 8f05651e6d..2345fbef6f 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -136,33 +136,6 @@ GeomVertexData(const GeomVertexData ©, CLOSE_ITERATE_ALL_STAGES(_cycler); } -/** - * The copy assignment operator is not pipeline-safe. This will completely - * obliterate all stages of the pipeline, so don't do it for a GeomVertexData - * that is actively being used for rendering. - */ -void GeomVertexData:: -operator = (const GeomVertexData ©) { - CopyOnWriteObject::operator = (copy); - - clear_cache(); - - _name = copy._name; - _cycler = copy._cycler; - _char_pcollector = copy._char_pcollector; - _skinning_pcollector = copy._skinning_pcollector; - _morphs_pcollector = copy._morphs_pcollector; - _blends_pcollector = copy._blends_pcollector; - - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_modified = Geom::get_next_modified(); - cdata->_animated_vertices = nullptr; - cdata->_animated_vertices_modified = UpdateSeq(); - } - CLOSE_ITERATE_ALL_STAGES(_cycler); -} - /** * */ diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 84f2e279aa..63a9024190 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -78,10 +78,11 @@ PUBLISHED: GeomVertexData(const GeomVertexData ©); explicit GeomVertexData(const GeomVertexData ©, const GeomVertexFormat *format); - void operator = (const GeomVertexData ©); virtual ~GeomVertexData(); ALLOC_DELETED_CHAIN(GeomVertexData); + void operator = (const GeomVertexData ©) = delete; + int compare_to(const GeomVertexData &other) const; INLINE const std::string &get_name() const; diff --git a/panda/src/text/geomTextGlyph.cxx b/panda/src/text/geomTextGlyph.cxx index ecd1a2173c..87751532f9 100644 --- a/panda/src/text/geomTextGlyph.cxx +++ b/panda/src/text/geomTextGlyph.cxx @@ -67,15 +67,6 @@ GeomTextGlyph(const Geom ©, const TextGlyph *glyph) : } } -/** - * - */ -void GeomTextGlyph:: -operator = (const GeomTextGlyph ©) { - Geom::operator = (copy); - _glyphs = copy._glyphs; -} - /** * */ diff --git a/panda/src/text/geomTextGlyph.h b/panda/src/text/geomTextGlyph.h index 7e697c1405..e9fa1245ae 100644 --- a/panda/src/text/geomTextGlyph.h +++ b/panda/src/text/geomTextGlyph.h @@ -30,10 +30,11 @@ public: GeomTextGlyph(const GeomVertexData *data); GeomTextGlyph(const GeomTextGlyph ©); GeomTextGlyph(const Geom ©, const TextGlyph *glyph); - void operator = (const GeomTextGlyph ©); virtual ~GeomTextGlyph(); ALLOC_DELETED_CHAIN(GeomTextGlyph); + void operator = (const GeomTextGlyph ©) = delete; + virtual Geom *make_copy() const; virtual bool copy_primitives_from(const Geom *other); void count_geom(const Geom *other); From 37c48ea8290a2d07a69cc2a938263e1dfb849017 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Mar 2022 11:27:19 +0100 Subject: [PATCH 092/166] dist: Helpful error for invalid android_abis value --- direct/src/dist/commands.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 5af82606a3..3083fd33a1 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -465,6 +465,12 @@ class build_apps(setuptools.Command): elif not self.android_abis: self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'] + supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86-64', 'mips', 'mips64' + unsupported_abis = set(self.android_abis) - set(supported_abis) + if unsupported_abis: + raise ValueError(f'Unrecognized value(s) for android_abis: {", ".join(unsupported_abis)}\n' + f'Valid ABIs are: {", ".join(supported_abis)}') + self.icon_objects = {} for app, iconpaths in self.icons.items(): if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple): From 573df4b320f596f29a04f8ba39c51f1eeec24178 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Mar 2022 11:32:08 +0100 Subject: [PATCH 093/166] dist: Add more resource codes for AndroidManifest.xml values --- direct/src/dist/_android.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/direct/src/dist/_android.py b/direct/src/dist/_android.py index 6214626a57..3ea7d8ea76 100644 --- a/direct/src/dist/_android.py +++ b/direct/src/dist/_android.py @@ -169,9 +169,11 @@ ANDROID_ATTRIBUTES = { 'alwaysRetainTaskState': bool_resource(0x1010203), 'clearTaskOnLaunch': bool_resource(0x1010015), 'debuggable': bool_resource(0x0101000f), + 'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"), 'configChanges': flag_resource(0x0101001f, mcc=0x0001, mnc=0x0002, locale=0x0004, touchscreen=0x0008, keyboard=0x0010, keyboardHidden=0x0020, navigation=0x0040, orientation=0x0080, screenLayout=0x0100, uiMode=0x0200, screenSize=0x0400, smallestScreenSize=0x0800, layoutDirection=0x2000, fontScale=0x40000000), 'enabled': bool_resource(0x101000e), 'excludeFromRecents': bool_resource(0x1010017), + 'exported': bool_resource(0x1010010), 'extractNativeLibs': bool_resource(0x10104ea), 'finishOnTaskLaunch': bool_resource(0x1010014), 'fullBackupContent': bool_resource(0x10104eb), @@ -189,9 +191,12 @@ ANDROID_ATTRIBUTES = { 'minSdkVersion': int_resource(0x101020c), 'multiprocess': bool_resource(0x1010013), 'name': str_resource(0x1010003), + 'noHistory': bool_resource(0x101022d), 'pathPattern': str_resource(0x101002c), + 'resizeableActivity': bool_resource(0x10104f6), 'required': bool_resource(0x101028e), 'scheme': str_resource(0x1010027), + 'screenOrientation': enum_resource(0x101001e, 'landscape', 'portrait', 'user', 'behind', 'sensor', 'nosensor', 'sensorLandscape', 'sensorPortrait', 'reverseLandscape', 'reversePortrait', 'fullSensor', 'userLandscape', 'userPortrait', 'fullUser', 'locked'), 'stateNotNeeded': bool_resource(0x1010016), 'supportsRtl': bool_resource(0x010103af), 'supportsUploading': bool_resource(0x101029b), From 683c54938c246395d4ae372c5952878083c1ce4e Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 4 Mar 2022 12:02:01 +0100 Subject: [PATCH 094/166] task: Don't capture SIGINT on Android There's no point to doing that, and doing so restricts the ability to run Python in a separate thread --- direct/src/task/Task.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index f946ca9122..a6363e3644 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -16,11 +16,17 @@ from direct.showbase.MessengerGlobal import messenger import types import random import importlib +import sys -try: - import _signal as signal -except ImportError: +# On Android, there's no use handling SIGINT, and in fact we can't, since we +# run the application in a separate thread from the main thread. +if hasattr(sys, 'getandroidapilevel'): signal = None +else: + try: + import _signal as signal + except ImportError: + signal = None from panda3d.core import * from direct.extensions_native import HTTPChannel_extensions @@ -31,7 +37,6 @@ def print_exc_plus(): Print the usual traceback information, followed by a listing of all the local variables in each frame. """ - import sys import traceback tb = sys.exc_info()[2] @@ -1271,7 +1276,6 @@ class TaskManager: if __debug__: def checkLeak(): - import sys import gc gc.enable() from direct.showbase.DirectObject import DirectObject From 59f422c056d83c362c873cbd4afe68bbb3b6d033 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:04:09 +0100 Subject: [PATCH 095/166] CMake: Support thirdparty packages on systems other than Windows/macOS --- dtool/Package.cmake | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dtool/Package.cmake b/dtool/Package.cmake index 293b1d7090..02d5558956 100644 --- a/dtool/Package.cmake +++ b/dtool/Package.cmake @@ -1,5 +1,5 @@ set(_thirdparty_dir_default "${PROJECT_SOURCE_DIR}/thirdparty") -if(NOT (APPLE OR WIN32) OR NOT IS_DIRECTORY "${_thirdparty_dir_default}") +if(NOT IS_DIRECTORY "${_thirdparty_dir_default}") set(_thirdparty_dir_default "") endif() @@ -47,6 +47,27 @@ if(THIRDPARTY_DIRECTORY) set(BISON_ROOT "${THIRDPARTY_DIRECTORY}/win-util") set(FLEX_ROOT "${THIRDPARTY_DIRECTORY}/win-util") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-arm64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-x64) + else() + set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-a) + endif() + + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-arm64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-x64) + else() + set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-a) + endif() + + elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(_package_dir ${THIRDPARTY_DIRECTORY}/android-libs-${CMAKE_ANDROID_ARCH}) + else() message(FATAL_ERROR "You can't use THIRDPARTY_DIRECTORY on this platform. Unset it to continue.") From e716dba8d42ef20b491ab95d6a2a1601c756138a Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:07:05 +0100 Subject: [PATCH 096/166] CMake: Update FindLibSquish.cmake, support looking in thirdparty dir --- cmake/modules/FindLibSquish.cmake | 86 +++++++++++++++++++------------ dtool/Package.cmake | 2 +- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/cmake/modules/FindLibSquish.cmake b/cmake/modules/FindLibSquish.cmake index 3399d81ae7..2ff949abd0 100644 --- a/cmake/modules/FindLibSquish.cmake +++ b/cmake/modules/FindLibSquish.cmake @@ -14,42 +14,62 @@ # LIBSQUISH_DEBUG_LIBRARY - the filepath of the libsquish debug library # -# Find the libsquish include files -find_path(LIBSQUISH_INCLUDE_DIR - NAMES "squish.h" - PATHS "/usr/include" - "/usr/local/include" - "/sw/include" - "/opt/include" - "/opt/local/include" - "/opt/csw/include" - PATH_SUFFIXES "" "cppunit" -) +if(LibSquish_ROOT) + # Search exclusively under the root + find_path(LIBSQUISH_INCLUDE_DIR + NAMES "squish.h" + PATHS ${LibSquish_ROOT} + PATH_SUFFIXES "include" + ) -# Find the libsquish library built for release -find_library(LIBSQUISH_RELEASE_LIBRARY - NAMES "squish" "libsquish" - PATHS "/usr" - "/usr/local" - "/usr/freeware" - "/sw" - "/opt" - "/opt/csw" - PATH_SUFFIXES "lib" "lib32" "lib64" -) + find_library(LIBSQUISH_RELEASE_LIBRARY + NAMES "squish" "libsquish" + PATHS ${LibSquish_ROOT} + PATH_SUFFIXES "lib" + ) -# Find the libsquish library built for debug -find_library(LIBSQUISH_DEBUG_LIBRARY - NAMES "squishd" "libsquishd" - PATHS "/usr" - "/usr/local" - "/usr/freeware" - "/sw" - "/opt" - "/opt/csw" - PATH_SUFFIXES "lib" "lib32" "lib64" -) + find_library(LIBSQUISH_DEBUG_LIBRARY + NAMES "squishd" "libsquishd" + PATHS ${LibSquish_ROOT} + PATH_SUFFIXES "lib" + ) +else() + # Find the libsquish include files + find_path(LIBSQUISH_INCLUDE_DIR + NAMES "squish.h" + PATHS "/usr/include" + "/usr/local/include" + "/sw/include" + "/opt/include" + "/opt/local/include" + "/opt/csw/include" + PATH_SUFFIXES "" "cppunit" + ) + # Find the libsquish library built for release + find_library(LIBSQUISH_RELEASE_LIBRARY + NAMES "squish" "libsquish" + PATHS "/usr" + "/usr/local" + "/usr/freeware" + "/sw" + "/opt" + "/opt/csw" + PATH_SUFFIXES "lib" "lib32" "lib64" + ) + + # Find the libsquish library built for debug + find_library(LIBSQUISH_DEBUG_LIBRARY + NAMES "squishd" "libsquishd" + PATHS "/usr" + "/usr/local" + "/usr/freeware" + "/sw" + "/opt" + "/opt/csw" + PATH_SUFFIXES "lib" "lib32" "lib64" + ) +endif() mark_as_advanced(LIBSQUISH_INCLUDE_DIR) mark_as_advanced(LIBSQUISH_RELEASE_LIBRARY) diff --git a/dtool/Package.cmake b/dtool/Package.cmake index 02d5558956..f98283d8ee 100644 --- a/dtool/Package.cmake +++ b/dtool/Package.cmake @@ -397,7 +397,7 @@ package_option(OpenEXR "Enable support for loading .exr images.") package_status(OpenEXR "OpenEXR") # libsquish -find_package(LibSquish QUIET) +find_package(LibSquish QUIET MODULE) package_option(SQUISH "Enables support for automatic compression of DXT textures." From 264747d213044b30305be5abb7bbf648a9d55677 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:35:57 +0100 Subject: [PATCH 097/166] CMake: Drop support for CMake versions below 3.13 We are already using target_link_options, and while it's possible to keep supporting older versions, it doesn't sound worth it. I can revert this out if someone gives me a really good reason to. --- CMakeLists.txt | 34 +++----------- cmake/macros/BuildMetalib.cmake | 77 -------------------------------- cmake/macros/PackageConfig.cmake | 17 +------ dtool/CompilerFlags.cmake | 33 ++------------ dtool/Package.cmake | 13 ------ dtool/PandaVersion.cmake | 4 -- 6 files changed, 10 insertions(+), 168 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8e251090c..5249ac8b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,36 +1,14 @@ -cmake_minimum_required(VERSION 3.0.2) +cmake_minimum_required(VERSION 3.13) set(CMAKE_DISABLE_SOURCE_CHANGES ON) # Must go before project() below set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Must go before project() below -if(CMAKE_VERSION VERSION_GREATER "3.11" OR POLICY CMP0072) - # Prefer GLVND over libGL when available; this will be enabled by default - # once the minimum CMake version is at least 3.11. - cmake_policy(SET CMP0072 NEW) -endif() - -if(CMAKE_VERSION VERSION_GREATER "3.12" OR POLICY CMP0074) - # Needed for THIRDPARTY_DIRECTORY support; this will be enabled by default - # once the minimum CMake version is at least 3.12. - cmake_policy(SET CMP0074 NEW) -endif() - if(POLICY CMP0091) # Needed for CMake to pass /MD flag properly with non-VC generators. cmake_policy(SET CMP0091 NEW) endif() # Determine whether we are using a multi-config generator. -if(CMAKE_VERSION VERSION_GREATER "3.8") - get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -else() - message(WARNING "Multi-configuration builds may not work properly when using -a CMake < 3.9. Making a guess if this is a multi-config generator.") - if(DEFINED CMAKE_CONFIGURATION_TYPES) - set(IS_MULTICONFIG ON) - else() - set(IS_MULTICONFIG OFF) - endif() -endif() +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) # Set the default CMAKE_BUILD_TYPE before calling project(). if(IS_MULTICONFIG) @@ -89,11 +67,9 @@ string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INT set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros/") -if(CMAKE_VERSION VERSION_GREATER "3.8") - # When using the Xcode generator, don't append the platform name to the - # intermediate configuration directory. - set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF) -endif() +# When using the Xcode generator, don't append the platform name to the +# intermediate configuration directory. +set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF) # Include modules builtin to CMake include(GNUInstallDirs) # Defines CMAKE_INSTALL_ variables diff --git a/cmake/macros/BuildMetalib.cmake b/cmake/macros/BuildMetalib.cmake index 079b794b85..8ac7a418a6 100644 --- a/cmake/macros/BuildMetalib.cmake +++ b/cmake/macros/BuildMetalib.cmake @@ -5,83 +5,6 @@ # instead just an agglomeration of the various component libraries that get # linked into them. A library of libraries - a "metalibrary." -# -# Function: target_link_libraries(...) -# -# Overrides CMake's target_link_libraries() to support "linking" object -# libraries. This is a partial reimplementation of CMake commit dc38970f83, -# which is only available in CMake 3.12+ -# -if(CMAKE_VERSION VERSION_LESS "3.12") - function(target_link_libraries target) - get_target_property(target_type "${target}" TYPE) - if(NOT target_type STREQUAL "OBJECT_LIBRARY") - _target_link_libraries("${target}" ${ARGN}) - return() - endif() - - foreach(library ${ARGN}) - # This is a quick and dirty regex to tell targets apart from other stuff. - # It just checks if it's alphanumeric and starts with p3/panda. - if(library MATCHES "^(PKG::|p3|panda)[A-Za-z0-9]*$") - # We need to add "library"'s include directories to "target" - # (and transitively to INTERFACE_INCLUDE_DIRECTORIES so further - # dependencies will work) - set(include_directories "$") - set_property(TARGET "${target}" APPEND PROPERTY INCLUDE_DIRECTORIES "${include_directories}") - set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${include_directories}") - - # SYSTEM include directories should still be reported as SYSTEM, so - # that warnings from those includes are suppressed - set(sys_include_directories - "$") - target_include_directories("${target}" SYSTEM PUBLIC "${sys_include_directories}") - - # And for INTERFACE_COMPILE_DEFINITIONS as well - set(compile_definitions "$") - set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}") - set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS "${compile_definitions}") - - # Build up some generator expressions for determining whether `library` - # is a component library or not. - if(library MATCHES ".*::.*") - # "::" messes up CMake's genex parser; fortunately, a library whose - # name contains that is either an interface library or alias, and - # definitely not a component - set(is_component 0) - set(name_of_component "") - set(name_of_non_component "${library}") - - else() - set(is_component "$") - - # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :( - set(is_object "$,OBJECT_LIBRARY>") - set(is_component "$>") - - set(name_of_component "$<${is_component}:$>") - set(name_of_non_component "$<$:$>") - - endif() - - # Libraries are only linked transitively if they aren't components. - set_property(TARGET "${target}" APPEND PROPERTY - INTERFACE_LINK_LIBRARIES "${name_of_non_component}") - - else() - # This is a file path to an out-of-tree library - this needs to be - # recorded so that the metalib can link them. (They aren't needed at - # all for the object libraries themselves, so they don't have to work - # transitively.) - set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}") - - endif() - - endforeach(library) - - endfunction(target_link_libraries) -endif() - # # Function: add_component_library(target [SYMBOL building_symbol] # [SOURCES] [[NOINIT]/[INIT func [header]]]) diff --git a/cmake/macros/PackageConfig.cmake b/cmake/macros/PackageConfig.cmake index ac020f7866..873c93d098 100644 --- a/cmake/macros/PackageConfig.cmake +++ b/cmake/macros/PackageConfig.cmake @@ -176,18 +176,6 @@ function(package_option name) # Create the INTERFACE library used to depend on this package. add_library(PKG::${name} INTERFACE IMPORTED GLOBAL) - # Explicitly record the package's include directories as system include - # directories. CMake does do this automatically for INTERFACE libraries, but - # it does it by discovering all transitive links first, then reading - # INTERFACE_INCLUDE_DIRECTORIES for those which are INTERFACE libraries. So, - # this would be broken for the metalib system (pre CMake 3.12) which doesn't - # "link" the object libraries. - if(CMAKE_VERSION VERSION_LESS "3.12") - set_target_properties(PKG::${name} PROPERTIES - INTERFACE_SYSTEM_INCLUDE_DIRECTORIES - "$") - endif() - # If the option actually is enabled, populate the INTERFACE library created above if(HAVE_${name}) set(use_variables ON) @@ -412,9 +400,8 @@ function(export_packages filename) endforeach(config) endif() - elseif(CMAKE_VERSION VERSION_GREATER "3.8") - # This is an INTERFACE_LIBRARY, and CMake is new enough to support - # IMPORTED_IMPLIB + else() + # This is an INTERFACE_LIBRARY. get_target_property(imported_libname "${head}" IMPORTED_LIBNAME) if(imported_libname) list(APPEND libraries ${imported_libname}) diff --git a/dtool/CompilerFlags.cmake b/dtool/CompilerFlags.cmake index 8b909ef3ed..b2ec74b980 100644 --- a/dtool/CompilerFlags.cmake +++ b/dtool/CompilerFlags.cmake @@ -48,21 +48,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GCC") endif() -# Panda3D is now a C++11 project. Newer versions of CMake support this out of -# the box; for older versions we take a shot in the dark: -if(CMAKE_VERSION VERSION_LESS "3.1") - check_cxx_compiler_flag("-std=gnu++11" COMPILER_SUPPORTS_CXX11) - if(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x") - endif() - -else() - set(CMAKE_CXX_STANDARD 11) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - -endif() +# Panda3D is now a C++11 project. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # Set certain CMake flags we expect set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) @@ -110,21 +98,6 @@ if(APPLE) set(CMAKE_SHARED_MODULE_SUFFIX ".dylib") endif() -# We want the output structured like build/CONFIG/bin, not build/bin/CONFIG per -# the default for multi-configuration generators. In CMake 3.4+, it switches -# automatically if the *_OUTPUT_DIRECTORY property contains a generator -# expresssion, but as of this writing we support as early as CMake 3.0.2. -# -# So, let's just do this: -if(CMAKE_VERSION VERSION_LESS "3.4") - foreach(_type RUNTIME ARCHIVE LIBRARY) - foreach(_config ${CMAKE_CONFIGURATION_TYPES}) - string(TOUPPER "${_config}" _config) - set(CMAKE_${_type}_OUTPUT_DIRECTORY_${_config} "${CMAKE_${_type}_OUTPUT_DIRECTORY}") - endforeach(_config) - endforeach(_type) -endif() - # Set warning levels if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3") diff --git a/dtool/Package.cmake b/dtool/Package.cmake index f98283d8ee..5ac4278cf0 100644 --- a/dtool/Package.cmake +++ b/dtool/Package.cmake @@ -211,19 +211,6 @@ if(Python_FOUND) set(PYTHON_INCLUDE_DIRS ${Python_INCLUDE_DIRS}) set(PYTHON_LIBRARY_DIRS ${Python_LIBRARY_DIRS}) set(PYTHON_VERSION_STRING ${Python_VERSION}) - -elseif(CMAKE_VERSION VERSION_LESS "3.12") - find_package(PythonInterp ${WANT_PYTHON_VERSION} QUIET) - find_package(PythonLibs ${PYTHON_VERSION_STRING} QUIET) - - if(PYTHONLIBS_FOUND) - set(PYTHON_FOUND ON) - - if(NOT PYTHON_VERSION_STRING) - set(PYTHON_VERSION_STRING ${PYTHONLIBS_VERSION_STRING}) - endif() - endif() - endif() if(CMAKE_VERSION VERSION_LESS "3.15") diff --git a/dtool/PandaVersion.cmake b/dtool/PandaVersion.cmake index e27bcbd95d..3813d4ccde 100644 --- a/dtool/PandaVersion.cmake +++ b/dtool/PandaVersion.cmake @@ -46,10 +46,6 @@ math(EXPR PANDA_NUMERIC_VERSION "${PROJECT_VERSION_MAJOR}*1000000 + ${PROJECT_VE # If SOURCE_DATE_EPOCH is set, it affects PandaSystem::get_build_date() if(DEFINED ENV{SOURCE_DATE_EPOCH}) - if(CMAKE_VERSION VERSION_LESS "3.8") - message(FATAL_ERROR "CMake 3.8 is required to support SOURCE_DATE_EPOCH properly.") - endif() - string(TIMESTAMP _build_date "%b %d %Y %H:%M:%S" UTC) # CMake doesn't support %e, replace leading zero in day with space From e8f3565af27bc7bb95210ebf62f9db875a4da515 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:41:48 +0100 Subject: [PATCH 098/166] CMake: Transfer target_link_options from component libs to metalibs --- cmake/macros/BuildMetalib.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake/macros/BuildMetalib.cmake b/cmake/macros/BuildMetalib.cmake index 8ac7a418a6..b13c68b204 100644 --- a/cmake/macros/BuildMetalib.cmake +++ b/cmake/macros/BuildMetalib.cmake @@ -228,6 +228,7 @@ function(add_metalib target_name) set(interface_defines) set(includes) set(libs) + set(link_options) set(component_init_funcs "") foreach(component ${components}) if(NOT TARGET "${component}") @@ -315,6 +316,16 @@ function(add_metalib target_name) endif() endforeach(component_library) + # All the linker options applied to an individual component get applied + # when building the metalib (for things like --exclude-libs). + get_target_property(component_link_options "${component}" LINK_OPTIONS) + foreach(component_link_option ${component_link_options}) + if(component_link_option) + list(APPEND link_options "${component_link_option}") + + endif() + endforeach(component_link_option) + # Consume this component's objects list(APPEND sources "$") @@ -342,6 +353,7 @@ function(add_metalib target_name) PRIVATE ${private_defines} INTERFACE ${interface_defines}) target_link_libraries("${target_name}" ${libs}) + target_link_options("${target_name}" PRIVATE ${link_options}) target_include_directories("${target_name}" PUBLIC ${includes} INTERFACE "$/${CMAKE_INSTALL_INCLUDEDIR}/panda3d>") From 6ea1e8d65cdee9ee5d61f95df1c103e6fb08f53e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:42:42 +0100 Subject: [PATCH 099/166] CMake: Update OpenEXR package handling to use new targets --- cmake/modules/FindOpenEXR.cmake | 88 --------------------------------- dtool/Package.cmake | 12 ++++- 2 files changed, 10 insertions(+), 90 deletions(-) delete mode 100644 cmake/modules/FindOpenEXR.cmake diff --git a/cmake/modules/FindOpenEXR.cmake b/cmake/modules/FindOpenEXR.cmake deleted file mode 100644 index 365ceec733..0000000000 --- a/cmake/modules/FindOpenEXR.cmake +++ /dev/null @@ -1,88 +0,0 @@ -# Filename: FindOpenEXR.cmake -# Authors: CFSworks (5 Nov, 2018) -# -# Usage: -# find_package(OpenEXR [REQUIRED] [QUIET]) -# -# Once done this will define: -# OPENEXR_FOUND - system has OpenEXR -# OPENEXR_INCLUDE_DIR - the include directory containing OpenEXR header files -# OPENEXR_LIBRARIES - the path to the OpenEXR libraries -# - -find_path(OPENEXR_INCLUDE_DIR - "ImfVersion.h" - PATH_SUFFIXES "OpenEXR") - -mark_as_advanced(OPENEXR_INCLUDE_DIR) - -find_library(OPENEXR_imf_LIBRARY - NAMES "IlmImf") - -if(OPENEXR_imf_LIBRARY) - get_filename_component(_imf_dir "${OPENEXR_imf_LIBRARY}" DIRECTORY) - find_library(OPENEXR_imfutil_LIBRARY - NAMES "IlmImfUtil" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - find_library(OPENEXR_ilmthread_LIBRARY - NAMES "IlmThread" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - find_library(OPENEXR_iex_LIBRARY - NAMES "Iex" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - find_library(OPENEXR_iexmath_LIBRARY - NAMES "IexMath" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - find_library(OPENEXR_imath_LIBRARY - NAMES "Imath" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - find_library(OPENEXR_half_LIBRARY - NAMES "Half" - PATHS "${_imf_dir}" - NO_DEFAULT_PATH) - - unset(_imf_dir) -endif() - -mark_as_advanced( - OPENEXR_imf_LIBRARY - OPENEXR_imfutil_LIBRARY - OPENEXR_ilmthread_LIBRARY - OPENEXR_iex_LIBRARY - OPENEXR_iexmath_LIBRARY - OPENEXR_imath_LIBRARY - OPENEXR_half_LIBRARY -) - -set(OPENEXR_LIBRARIES - ${OPENEXR_imf_LIBRARY} - ${OPENEXR_imfutil_LIBRARY} - ${OPENEXR_ilmthread_LIBRARY} - ${OPENEXR_iex_LIBRARY} - ${OPENEXR_iexmath_LIBRARY} - ${OPENEXR_imath_LIBRARY} - ${OPENEXR_half_LIBRARY} -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenEXR DEFAULT_MSG - OPENEXR_INCLUDE_DIR OPENEXR_LIBRARIES - - OPENEXR_imf_LIBRARY - OPENEXR_imfutil_LIBRARY - OPENEXR_ilmthread_LIBRARY - OPENEXR_iex_LIBRARY - OPENEXR_iexmath_LIBRARY - OPENEXR_imath_LIBRARY - OPENEXR_half_LIBRARY -) diff --git a/dtool/Package.cmake b/dtool/Package.cmake index 5ac4278cf0..10e653f6d4 100644 --- a/dtool/Package.cmake +++ b/dtool/Package.cmake @@ -377,9 +377,17 @@ package_option(TIFF "Enable support for loading .tif images.") package_status(TIFF "libtiff") # OpenEXR -find_package(OpenEXR QUIET MODULE) +find_package(OpenEXR QUIET) -package_option(OpenEXR "Enable support for loading .exr images.") +if (TARGET OpenEXR::IlmImf AND NOT TARGET OpenEXR::OpenEXR) + package_option(OpenEXR + "Enable support for loading .exr images." + IMPORTED_AS OpenEXR::IlmImf) +else() + package_option(OpenEXR + "Enable support for loading .exr images." + IMPORTED_AS OpenEXR::OpenEXR) +endif() package_status(OpenEXR "OpenEXR") From c62d2319e0f0cb4e6b193d7def55624788e2ac7a Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 14:54:27 +0100 Subject: [PATCH 100/166] CMake: Add --exclude-libs flags to static thirdparty libraries Matching makepanda, this avoids symbol conflicts and may have optimization benefits. This is a temporary hack until CMake 3.24 is released, which offers a cleaner way of doing this. --- dtool/src/dtoolbase/CMakeLists.txt | 5 +++++ dtool/src/prc/CMakeLists.txt | 6 ++++++ panda/src/audiotraits/CMakeLists.txt | 5 +++++ panda/src/ffmpeg/CMakeLists.txt | 15 +++++++++++++ panda/src/gobj/CMakeLists.txt | 5 +++++ panda/src/movies/CMakeLists.txt | 18 ++++++++++++++++ panda/src/pnmimagetypes/CMakeLists.txt | 29 ++++++++++++++++++++++++++ panda/src/vision/CMakeLists.txt | 6 ++++++ panda/src/vrpn/CMakeLists.txt | 6 ++++++ pandatool/src/assimp/CMakeLists.txt | 6 ++++++ 10 files changed, 101 insertions(+) diff --git a/dtool/src/dtoolbase/CMakeLists.txt b/dtool/src/dtoolbase/CMakeLists.txt index f69125f6b4..72d69b7e07 100644 --- a/dtool/src/dtoolbase/CMakeLists.txt +++ b/dtool/src/dtoolbase/CMakeLists.txt @@ -97,6 +97,11 @@ target_include_directories(p3dtoolbase PUBLIC target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS PKG::MIMALLOC) target_interrogate(p3dtoolbase ${P3DTOOLBASE_SOURCES} EXTENSIONS ${P3DTOOLBASE_IGATEEXT}) +if(HAVE_MIMALLOC AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + target_link_options(p3dtoolbase PRIVATE "LINKER:--exclude-libs,libmimalloc.a") +endif() + if(NOT BUILD_METALIBS) install(TARGETS p3dtoolbase EXPORT Core COMPONENT Core diff --git a/dtool/src/prc/CMakeLists.txt b/dtool/src/prc/CMakeLists.txt index 2a97c4665a..f5731e9e0f 100644 --- a/dtool/src/prc/CMakeLists.txt +++ b/dtool/src/prc/CMakeLists.txt @@ -94,6 +94,12 @@ if(ANDROID) target_link_libraries(p3prc log) endif() +if(HAVE_OPENSSL AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libssl.a") + target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libcrypto.a") +endif() + install(TARGETS p3prc EXPORT Core COMPONENT Core DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/panda/src/audiotraits/CMakeLists.txt b/panda/src/audiotraits/CMakeLists.txt index c90b9dcbae..419bc66a88 100644 --- a/panda/src/audiotraits/CMakeLists.txt +++ b/panda/src/audiotraits/CMakeLists.txt @@ -60,6 +60,11 @@ if(HAVE_OPENAL) set_target_properties(p3openal_audio PROPERTIES DEFINE_SYMBOL BUILDING_OPENAL_AUDIO) target_link_libraries(p3openal_audio panda PKG::OPENAL) + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # When statically linking OpenAL, keep its symbols private to this module. + target_link_options(p3openal_audio PRIVATE "LINKER:--exclude-libs,libopenal.a") + endif() + install(TARGETS p3openal_audio EXPORT OpenAL COMPONENT OpenAL DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/panda/src/ffmpeg/CMakeLists.txt b/panda/src/ffmpeg/CMakeLists.txt index a78a3d2cdc..494289f5a7 100644 --- a/panda/src/ffmpeg/CMakeLists.txt +++ b/panda/src/ffmpeg/CMakeLists.txt @@ -26,12 +26,27 @@ set_target_properties(p3ffmpeg PROPERTIES DEFINE_SYMBOL BUILDING_FFMPEG) target_link_libraries(p3ffmpeg panda PKG::FFMPEG PKG::SWSCALE PKG::SWRESAMPLE) +# Do not re-export symbols from these libraries. +if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavcodec.a") + target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavformat.a") + target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavutil.a") +endif() + if(HAVE_SWSCALE) target_compile_definitions(p3ffmpeg PRIVATE HAVE_SWSCALE) + + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libswscale.a") + endif() endif() if(HAVE_SWRESAMPLE) target_compile_definitions(p3ffmpeg PRIVATE HAVE_SWRESAMPLE) + + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libswresample.a") + endif() endif() install(TARGETS p3ffmpeg diff --git a/panda/src/gobj/CMakeLists.txt b/panda/src/gobj/CMakeLists.txt index 1835ffe3f8..c8cfd64b8f 100644 --- a/panda/src/gobj/CMakeLists.txt +++ b/panda/src/gobj/CMakeLists.txt @@ -180,6 +180,11 @@ target_interrogate(p3gobj ALL EXTENSIONS ${P3GOBJ_IGATEEXT}) if(HAVE_SQUISH) target_compile_definitions(p3gobj PRIVATE HAVE_SQUISH) + + if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Keep symbols from libsquish internal. + target_link_options(p3gobj PRIVATE "LINKER:--exclude-libs,libsquish.a") + endif() endif() if(PHAVE_LOCKF) diff --git a/panda/src/movies/CMakeLists.txt b/panda/src/movies/CMakeLists.txt index 30e01d254a..fc61423d18 100644 --- a/panda/src/movies/CMakeLists.txt +++ b/panda/src/movies/CMakeLists.txt @@ -51,6 +51,24 @@ target_link_libraries(p3movies p3pstatclient p3gobj p3pandabase pandaexpress PKG::VORBIS PKG::OPUS) target_interrogate(p3movies ALL) +if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + if(HAVE_OPUS OR HAVE_VORBIS) + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libogg.a") + endif() + + if(HAVE_VORBIS) + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbis.a") + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbisenc.a") + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbisfile.a") + endif() + + if(HAVE_OPUS) + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libopus.a") + target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libopusfile.a") + endif() +endif() + if(NOT BUILD_METALIBS) install(TARGETS p3movies EXPORT Core COMPONENT Core diff --git a/panda/src/pnmimagetypes/CMakeLists.txt b/panda/src/pnmimagetypes/CMakeLists.txt index 84c8ceeab7..2045b7130d 100644 --- a/panda/src/pnmimagetypes/CMakeLists.txt +++ b/panda/src/pnmimagetypes/CMakeLists.txt @@ -39,6 +39,35 @@ target_link_libraries(p3pnmimagetypes p3pnmimage PKG::JPEG PKG::TIFF PKG::PNG PKG::OPENEXR) set_target_properties(p3pnmimagetypes PROPERTIES CXX_EXCEPTIONS ON) +if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + if(HAVE_JPEG) + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libjpeg.a") + endif() + + if(HAVE_TIFF) + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libtiff.a") + endif() + + if(HAVE_PNG) + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libpng.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libpng16.a") + endif() + + if(HAVE_OPENEXR) + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libHalf.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIex.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIexMath.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmImf.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmImfUtil.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmThread.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libImath.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXR.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXRCore.a") + target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXRUtil.a") + endif() +endif() + if(NOT BUILD_METALIBS) install(TARGETS p3pnmimagetypes EXPORT Core COMPONENT Core diff --git a/panda/src/vision/CMakeLists.txt b/panda/src/vision/CMakeLists.txt index 284e764807..c1505ad950 100644 --- a/panda/src/vision/CMakeLists.txt +++ b/panda/src/vision/CMakeLists.txt @@ -43,6 +43,12 @@ if(HAVE_FFMPEG) target_compile_definitions(p3vision PRIVATE HAVE_FFMPEG) endif() +if(HAVE_ARTOOLKIT AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + target_link_options(p3vision PRIVATE "LINKER:--exclude-libs,libAR.a") + target_link_options(p3vision PRIVATE "LINKER:--exclude-libs,libARMulti.a") +endif() + install(TARGETS p3vision EXPORT Vision COMPONENT Vision DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/panda/src/vrpn/CMakeLists.txt b/panda/src/vrpn/CMakeLists.txt index 11e5a5fc61..47010a9d79 100644 --- a/panda/src/vrpn/CMakeLists.txt +++ b/panda/src/vrpn/CMakeLists.txt @@ -37,6 +37,12 @@ target_interrogate(p3vrpn ALL) set_target_properties(p3vrpn PROPERTIES CXX_EXCEPTIONS ON) +if(HAVE_VRPN AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + target_link_options(p3vrpn PRIVATE "LINKER:--exclude-libs,libvrpn.a") + target_link_options(p3vrpn PRIVATE "LINKER:--exclude-libs,libquat.a") +endif() + install(TARGETS p3vrpn EXPORT VRPN COMPONENT VRPN DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/pandatool/src/assimp/CMakeLists.txt b/pandatool/src/assimp/CMakeLists.txt index 536d15ec39..5b88e0eff6 100644 --- a/pandatool/src/assimp/CMakeLists.txt +++ b/pandatool/src/assimp/CMakeLists.txt @@ -26,6 +26,12 @@ set_target_properties(p3assimp PROPERTIES DEFINE_SYMBOL BUILDING_ASSIMP) target_link_libraries(p3assimp PRIVATE p3pandatoolbase) target_link_libraries(p3assimp PUBLIC PKG::ASSIMP) +if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$") + # Do not re-export symbols from these libraries. + target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libassimp.a") + target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libIrrXML.a") +endif() + if(BUILD_SHARED_LIBS) # We can't install this if we're doing a static build, because it depends on # a static library that isn't installed. From 345676970359d3f57a24753c5824d2c34081cedf Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 15:51:32 +0100 Subject: [PATCH 101/166] gobj: Fix crash in PythonTexturePoolFilter --- panda/src/gobj/pythonTexturePoolFilter.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/gobj/pythonTexturePoolFilter.cxx b/panda/src/gobj/pythonTexturePoolFilter.cxx index f758e1a9c2..8111c1c4a3 100644 --- a/panda/src/gobj/pythonTexturePoolFilter.cxx +++ b/panda/src/gobj/pythonTexturePoolFilter.cxx @@ -67,6 +67,7 @@ init(PyObject *tex_filter) { nassertr(_post_load_func == nullptr, false); _pre_load_func = PyObject_GetAttrString(tex_filter, "pre_load"); + PyErr_Clear(); _post_load_func = PyObject_GetAttrString(tex_filter, "post_load"); PyErr_Clear(); From 54750847179c62990051a6a2bd798b621fdb75d7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 15:56:28 +0100 Subject: [PATCH 102/166] build: Enable -fno-semantic-interposition for GCC This matches the more optimized clang behavior for -fPIC --- dtool/CompilerFlags.cmake | 7 +++++++ makepanda/makepanda.py | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/dtool/CompilerFlags.cmake b/dtool/CompilerFlags.cmake index b2ec74b980..0b6945878e 100644 --- a/dtool/CompilerFlags.cmake +++ b/dtool/CompilerFlags.cmake @@ -218,3 +218,10 @@ if(NOT MSVC) add_compile_options("-fvisibility=hidden") endif() endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + check_cxx_compiler_flag("-fno-semantic-interposition" COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION) + if(COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION) + add_compile_options("-fno-semantic-interposition") + endif() +endif() diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index f11797c44f..ee011366ba 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1370,6 +1370,10 @@ def CompileCxx(obj,src,opts): if 'NOARCH:' + arch.upper() not in opts: cmd += " -arch %s" % arch + elif 'clang' not in GetCXX().split('/')[-1]: + # Enable interprocedural optimizations in GCC. + cmd += " -fno-semantic-interposition" + if "SYSROOT" in SDK: if GetTarget() != "android": cmd += ' --sysroot=%s' % (SDK["SYSROOT"]) From 3d31f117e09a0f286ef875596f6046e78698286d Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Mar 2022 16:22:58 +0100 Subject: [PATCH 103/166] express: Fix compilation error with GCC --- panda/src/express/weakPointerToBase.I | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/panda/src/express/weakPointerToBase.I b/panda/src/express/weakPointerToBase.I index 11b04dabfe..41206e0994 100644 --- a/panda/src/express/weakPointerToBase.I +++ b/panda/src/express/weakPointerToBase.I @@ -33,9 +33,7 @@ WeakPointerToBase(To *ptr) { template INLINE WeakPointerToBase:: WeakPointerToBase(const PointerToBase ©) { - // This double-casting is a bit of a cheat to get around the inheritance - // issue--it's difficult to declare a template class to be a friend. - To *ptr = (To *)((const WeakPointerToBase *)©)->_void_ptr; + To *ptr = (To *)copy._void_ptr; _void_ptr = ptr; if (ptr != nullptr) { _weak_ref = ptr->weak_ref(); From 98d70147bd32e4948a171612c23ab68e6307287e Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 16:48:46 +0100 Subject: [PATCH 104/166] pipeline: Fix Thread::bind_thread() assertion on Android --- panda/src/pipeline/threadPosixImpl.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 9108825422..371c7464ad 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -337,7 +337,7 @@ root_func(void *data) { } /** - * Called by get_current_thread() if the current therad pointer is null; checks + * Called by get_current_thread() if the current thread pointer is null; checks * whether it might be the main thread. */ Thread *ThreadPosixImpl:: @@ -347,7 +347,6 @@ init_current_thread() { thread = Thread::get_main_thread(); _current_thread = thread; } - nassertr(thread != nullptr, nullptr); return thread; } From 218f2af7fbd3ac86dd78d7f50d0da651677acef8 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 16:50:05 +0100 Subject: [PATCH 105/166] dist: Support .* imports inside wheels No longer try to import modules directly (we can do this if we really have to, but then we have to load it from the proper location) since we don't want to grab the system version of the package which may not be present or may be a different version. Support discovering .* imports inside .whl files that are on sys.path. --- direct/src/dist/FreezeTool.py | 140 ++++++++++++++++++---------------- direct/src/dist/commands.py | 2 +- 2 files changed, 75 insertions(+), 67 deletions(-) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index db22e9bf02..1cb7ff1006 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -791,10 +791,6 @@ class Freezer: # default object will be created when it is needed. self.cenv = None - # This is the search path to use for Python modules. Leave it - # to the default value of None to use sys.path. - self.path = path - # The filename extension to append to the source file before # compiling. self.sourceExtension = '.c' @@ -843,16 +839,14 @@ class Freezer: # builds. It can be explicitly included if desired. self.modules['doctest'] = self.ModuleDef('doctest', exclude = True) - self.mf = None - # Actually, make sure we know how to find all of the # already-imported modules. (Some of them might do their own # special path mangling.) for moduleName, module in list(sys.modules.items()): if module and getattr(module, '__path__', None) is not None: - path = list(getattr(module, '__path__')) - if path: - modulefinder.AddPackagePath(moduleName, path[0]) + modPath = list(getattr(module, '__path__')) + if modPath: + modulefinder.AddPackagePath(moduleName, modPath[0]) # Module with non-obvious dependencies self.hiddenImports = defaultHiddenImports.copy() @@ -861,14 +855,14 @@ class Freezer: # Suffix/extension for Python C extension modules if self.platform == PandaSystem.getPlatform(): - self.moduleSuffixes = imp.get_suffixes() + suffixes = imp.get_suffixes() # Set extension for Python files to binary mode - for i, suffix in enumerate(self.moduleSuffixes): + for i, suffix in enumerate(suffixes): if suffix[2] == imp.PY_SOURCE: - self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE) + suffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE) else: - self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)] + suffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)] abi_version = '{0}{1}'.format(*sys.version_info) abi_flags = '' @@ -876,7 +870,7 @@ class Freezer: abi_flags += 'm' if 'linux' in self.platform: - self.moduleSuffixes += [ + suffixes += [ ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3), ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3), ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3), @@ -884,24 +878,26 @@ class Freezer: ] elif 'win' in self.platform: # ABI flags are not appended on Windows. - self.moduleSuffixes += [ + suffixes += [ ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3), ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3), ('.pyd', 'rb', 3), ] elif 'mac' in self.platform: - self.moduleSuffixes += [ + suffixes += [ ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3), ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3), ('.so', 'rb', 3), ] else: # FreeBSD et al. - self.moduleSuffixes += [ + suffixes += [ ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3), ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3), ('.so', 'rb', 3), ] + self.mf = PandaModuleFinder(excludes=['doctest'], suffixes=suffixes, path=path) + def excludeFrom(self, freezer): """ Excludes all modules that have already been processed by the indicated FreezeTool. This is equivalent to passing the @@ -921,8 +917,6 @@ class Freezer: allowChildren is true, the children of the indicated module may still be included.""" - assert self.mf is None - self.modules[moduleName] = self.ModuleDef( moduleName, exclude = True, forbid = forbid, allowChildren = allowChildren, @@ -947,24 +941,6 @@ class Freezer: files can be found. If the module is a .py file and not a directory, returns None. """ - # First, try to import the module directly. That's the most - # reliable answer, if it works. - try: - module = __import__(moduleName) - except: - print("couldn't import %s" % (moduleName)) - module = None - - if module is not None: - for symbol in moduleName.split('.')[1:]: - module = getattr(module, symbol) - if hasattr(module, '__path__'): - return module.__path__ - - # If it didn't work--maybe the module is unimportable because - # it makes certain assumptions about the builtins, or - # whatever--then just look for file on disk. That's usually - # good enough. path = None baseName = moduleName if '.' in baseName: @@ -974,34 +950,20 @@ class Freezer: return None try: - file, pathname, description = imp.find_module(baseName, path) + file, pathname, description = self.mf.find_module(baseName, path) except ImportError: return None - if not os.path.isdir(pathname): + if not self.mf._dir_exists(pathname): return None + return [pathname] def getModuleStar(self, moduleName): """ Looks for the indicated directory module and returns the __all__ member: the list of symbols within the module. """ - # First, try to import the module directly. That's the most - # reliable answer, if it works. - try: - module = __import__(moduleName) - except: - print("couldn't import %s" % (moduleName)) - module = None - - if module is not None: - for symbol in moduleName.split('.')[1:]: - module = getattr(module, symbol) - if hasattr(module, '__all__'): - return module.__all__ - - # If it didn't work, just open the directory and scan for *.py - # files. + # Open the directory and scan for *.py files. path = None baseName = moduleName if '.' in baseName: @@ -1011,16 +973,16 @@ class Freezer: return None try: - file, pathname, description = imp.find_module(baseName, path) + file, pathname, description = self.mf.find_module(baseName, path) except ImportError: return None - if not os.path.isdir(pathname): + if not self.mf._dir_exists(pathname): return None # Scan the directory, looking for .py files. modules = [] - for basename in sorted(os.listdir(pathname)): + for basename in sorted(self.mf._listdir(pathname)): if basename.endswith('.py') and basename != '__init__.py': modules.append(basename[:-3]) @@ -1054,8 +1016,8 @@ class Freezer: modulePath = self.getModulePath(topName) if modulePath: for dirname in modulePath: - for basename in sorted(os.listdir(dirname)): - if os.path.exists(os.path.join(dirname, basename, '__init__.py')): + for basename in sorted(self.mf._listdir(dirname)): + if self.mf._file_exists(os.path.join(dirname, basename, '__init__.py')): parentName = '%s.%s' % (topName, basename) newParentName = '%s.%s' % (newTopName, basename) if self.getModulePath(parentName): @@ -1100,8 +1062,6 @@ class Freezer: directories within a particular directory. """ - assert self.mf is None - if not newName: newName = moduleName @@ -1122,8 +1082,6 @@ class Freezer: to done(), you may not add any more modules until you call reset(). """ - assert self.mf is None - # If we are building an exe, we also need to implicitly # bring in Python's startup modules. if addStartupModules: @@ -1165,7 +1123,9 @@ class Freezer: else: includes.append(mdef) - self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path) + # Add the excludes to the ModuleFinder. + for exclude in excludeDict: + self.mf.excludes.append(exclude) # Attempt to import the explicit modules into the modulefinder. @@ -2428,6 +2388,17 @@ class PandaModuleFinder(modulefinder.ModuleFinder): return None + def _file_exists(self, path): + if os.path.exists(path): + return os.path.isfile(path) + + fh = self._open_file(path, 'rb') + if fh: + fh.close() + return True + + return False + def _dir_exists(self, path): """Returns True if the given directory exists, either on disk or inside a wheel.""" @@ -2466,6 +2437,43 @@ class PandaModuleFinder(modulefinder.ModuleFinder): return False + def _listdir(self, path): + """Lists files in the given directory if it exists.""" + + if os.path.isdir(path): + return os.listdir(path) + + # Is there a zip file along the path? + dir, dirname = os.path.split(path.rstrip(os.path.sep + '/')) + fn = dirname + while dirname: + if os.path.isfile(dir): + # Okay, this is actually a file. Is it a zip file? + if dir in self._zip_files: + # Yes, and we've previously opened this. + zip = self._zip_files[dir] + elif zipfile.is_zipfile(dir): + zip = zipfile.ZipFile(dir) + self._zip_files[dir] = zip + else: + # It's not a directory or zip file. + return [] + + # List files whose path start with our directory name. + prefix = fn.replace(os.path.sep, '/') + '/' + result = [] + for name in zip.namelist(): + if name.startswith(prefix) and '/' not in name[len(prefix):]: + result.append(name[len(prefix):]) + + return result + + # Look at the parent directory. + dir, dirname = os.path.split(dir) + fn = os.path.join(dirname, fn) + + return [] + def load_module(self, fqname, fp, pathname, file_info): """Copied from ModuleFinder.load_module with fixes to handle sending bytes to compile() for PY_SOURCE types. Sending bytes to compile allows it to @@ -2712,7 +2720,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder): modules = {} for dir in m.__path__: try: - names = os.listdir(dir) + names = self._listdir(dir) except OSError: self.msg(2, "can't list directory", dir) continue diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 3083fd33a1..8441bfe93a 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -1023,7 +1023,7 @@ class build_apps(setuptools.Command): freezer_extras.update(freezer.extras) freezer_modules.update(freezer.getAllModuleNames()) - for suffix in freezer.moduleSuffixes: + for suffix in freezer.mf.suffixes: if suffix[2] == imp.C_EXTENSION: ext_suffixes.add(suffix[0]) From 5c03cd59fbccc4f84627f26aea2cfd2fadc31a73 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 16:52:12 +0100 Subject: [PATCH 106/166] dist: Add special support for hidden imports of plyer module --- direct/src/dist/FreezeTool.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 1cb7ff1006..a4f3bde4fb 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -80,6 +80,7 @@ defaultHiddenImports = { ], 'pandas.compat': ['lzma', 'cmath'], 'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'], + 'plyer': ['plyer.platforms'], } @@ -853,6 +854,20 @@ class Freezer: if hiddenImports is not None: self.hiddenImports.update(hiddenImports) + # Special hack for plyer, which has platform-specific hidden imports + plyer_platform = None + if self.platform.startswith('android'): + plyer_platform = 'android' + elif self.platform.startswith('linux'): + plyer_platform = 'linux' + elif self.platform.startswith('mac'): + plyer_platform = 'macosx' + elif self.platform.startswith('win'): + plyer_platform = 'win' + + if plyer_platform: + self.hiddenImports['plyer'].append(f'plyer.platforms.{plyer_platform}.*') + # Suffix/extension for Python C extension modules if self.platform == PandaSystem.getPlatform(): suffixes = imp.get_suffixes() From bb68abdd598ed0d23c8265a1d29bde0c3589b124 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 16:55:37 +0100 Subject: [PATCH 107/166] makepanda: Ignore system imports when scanning .java files --- makepanda/makepandacore.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 25565db8f1..5c0ba76623 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -877,8 +877,11 @@ def JavaGetImports(path): imports = [] try: for match in JavaImportRegex.finditer(source, 0): - impname = match.group(1) - imports.append(impname.strip()) + impname = match.group(1).strip() + if not impname.startswith('java.') and \ + not impname.startswith('dalvik.') and \ + not impname.startswith('android.'): + imports.append(impname.strip()) except: print("Failed to determine dependencies of \"" + path +"\".") raise From 83038146b3dabb8a9f1166791e607b696e0446a3 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 16:56:02 +0100 Subject: [PATCH 108/166] makepanda: Fix dependency problem in threaded mode This seems to happen when TargetAdd calls are specified out-of-order --- makepanda/makepanda.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ee011366ba..849e085a15 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6264,6 +6264,8 @@ def ParallelMake(tasklist): tasklist = extras sys.stdout.flush() if tasksqueued == 0: + if len(tasklist) > 0: + continue break donetask = donequeue.get() if donetask == 0: From e02a9989fbefceb93e8897565df6445d72041b36 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 17:45:51 +0100 Subject: [PATCH 109/166] android: Changes to add compatibility with pyjnius/plyer --- direct/src/dist/commands.py | 5 +++- makepanda/makepanda.py | 3 +++ panda/src/android/PythonActivity.java | 10 ++++++-- .../deploy-stub/NativeInvocationHandler.java | 25 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 pandatool/src/deploy-stub/NativeInvocationHandler.java diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 8441bfe93a..23e949ada6 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -157,6 +157,9 @@ from android_log import write as android_log_write sys.frozen = True sys.platform = "android" +# Temporary hack for plyer to detect Android, see kivy/plyer#670 +os.environ['ANDROID_ARGUMENT'] = '' + # Replace stdout/stderr with something that writes to the Android log. class AndroidLogStream: @@ -735,7 +738,7 @@ class build_apps(setuptools.Command): for appname in self.gui_apps: activity = ET.SubElement(application, 'activity') - activity.set('android:name', 'org.panda3d.android.PandaActivity') + activity.set('android:name', 'org.panda3d.android.PythonActivity') activity.set('android:label', appname) activity.set('android:theme', '@android:style/Theme.NoTitleBar') activity.set('android:configChanges', 'orientation|keyboardHidden') diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 849e085a15..83b0105bc8 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6136,6 +6136,9 @@ if PkgSkip("PYTHON") == 0: PyTargetAdd('deploy-stubw.exe', input='deploy-stubw.obj') PyTargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'DEPLOYSTUB', 'NOICON']) elif GetTarget() == 'android': + TargetAdd('org/jnius/NativeInvocationHandler.class', opts=OPTS, input='NativeInvocationHandler.java') + TargetAdd('classes.dex', input='org/jnius/NativeInvocationHandler.class') + PyTargetAdd('deploy-stubw_android_main.obj', opts=OPTS, input='android_main.cxx') PyTargetAdd('deploy-stubw_android_log.obj', opts=OPTS, input='android_log.c') PyTargetAdd('libdeploy-stubw.dll', input='android_native_app_glue.obj') diff --git a/panda/src/android/PythonActivity.java b/panda/src/android/PythonActivity.java index 0d282d84a5..731ed4d54c 100644 --- a/panda/src/android/PythonActivity.java +++ b/panda/src/android/PythonActivity.java @@ -16,8 +16,14 @@ package org.panda3d.android; import org.panda3d.android.PandaActivity; /** - * This is only declared as a separate class from PandaActivity so that we - * can have two separate activity definitions in ApplicationManifest.xml. + * Extends PandaActivity with some things that are useful in a Python + * application. */ public class PythonActivity extends PandaActivity { + // This is required by plyer. + public static PythonActivity mActivity; + + public PythonActivity() { + mActivity = this; + } } diff --git a/pandatool/src/deploy-stub/NativeInvocationHandler.java b/pandatool/src/deploy-stub/NativeInvocationHandler.java new file mode 100644 index 0000000000..2a10837328 --- /dev/null +++ b/pandatool/src/deploy-stub/NativeInvocationHandler.java @@ -0,0 +1,25 @@ +package org.jnius; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +/** + * Special support for pyjnius. + */ +public class NativeInvocationHandler implements InvocationHandler { + private long _ptr; + + public NativeInvocationHandler(long ptr) { + _ptr = ptr; + } + + public long getPythonObjectPointer() { + return _ptr; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + return invoke0(proxy, method, args); + } + + native Object invoke0(Object proxy, Method method, Object[] args); +} From b610a6492e0c4354c8a272f1490000bb4c0f82e7 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 12 Mar 2022 18:19:51 +0100 Subject: [PATCH 110/166] dist: Don't set sys.platform to "android" on Android Let's just do what upstream does, so that there are no surprises. --- direct/src/dist/commands.py | 1 - 1 file changed, 1 deletion(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 23e949ada6..02289a4ee8 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -155,7 +155,6 @@ from android_log import write as android_log_write sys.frozen = True -sys.platform = "android" # Temporary hack for plyer to detect Android, see kivy/plyer#670 os.environ['ANDROID_ARGUMENT'] = '' From cafcdede5fcd30a04c989d954b0d2fec90a4e204 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 15 Mar 2022 11:20:00 +0100 Subject: [PATCH 111/166] dist: Disable _bootlocale injection for Python 3.10+ Python 3.10 removed the _bootlocale module (see bpo-42208) [skip ci] --- direct/src/dist/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index e710366177..e55ed8a2cd 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -157,7 +157,7 @@ from _frozen_importlib import _imp, FrozenImporter sys.frozen = True -if sys.platform == 'win32': +if sys.platform == 'win32' and sys.version_info < (3, 10): # Make sure the preferred encoding is something we actually support. import _bootlocale enc = _bootlocale.getpreferredencoding().lower() From e272de87084aa70b9fcfebd646f9a8fb7f59e6ff Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 17 Mar 2022 09:23:01 +0100 Subject: [PATCH 112/166] dist: Fix accidental error message with x86_64 ABI with Android [skip ci] --- direct/src/dist/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 02289a4ee8..7019fbfef2 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -459,13 +459,13 @@ class build_apps(setuptools.Command): if self.android_abis: for abi in self.android_abis: - assert abi not in ('mips64', 'x86_64', 'arm64-v8a'), \ + assert abi not in ('mips64', 'x86-64', 'arm64-v8a'), \ f'{abi} was not a valid Android ABI before Android 21!' else: self.android_abis = ['armeabi-v7a', 'x86'] elif not self.android_abis: - self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'] + self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86-64', 'x86'] supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86-64', 'mips', 'mips64' unsupported_abis = set(self.android_abis) - set(supported_abis) From 3084fcd8dc8fa2c29ea5b58860ea8b060e6e46aa Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 17 Mar 2022 13:29:03 +0100 Subject: [PATCH 113/166] dist: Correct fix for android x86_64 ABI [skip ci] --- direct/src/dist/commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 7019fbfef2..432375c6c1 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -459,15 +459,15 @@ class build_apps(setuptools.Command): if self.android_abis: for abi in self.android_abis: - assert abi not in ('mips64', 'x86-64', 'arm64-v8a'), \ + assert abi not in ('mips64', 'x86_64', 'arm64-v8a'), \ f'{abi} was not a valid Android ABI before Android 21!' else: self.android_abis = ['armeabi-v7a', 'x86'] elif not self.android_abis: - self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86-64', 'x86'] + self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'] - supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86-64', 'mips', 'mips64' + supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64' unsupported_abis = set(self.android_abis) - set(supported_abis) if unsupported_abis: raise ValueError(f'Unrecognized value(s) for android_abis: {", ".join(unsupported_abis)}\n' From 3f901243f2e0fe6fd744ff55c03d4c78f8ffc759 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 2 Apr 2022 20:40:58 +0200 Subject: [PATCH 114/166] display: Add additional spam prints --- panda/src/display/graphicsEngine.cxx | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index ef24abd226..c93228b7c5 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1433,6 +1433,10 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, GraphicsOutput *win = wlist[wi]; if (win->is_active() && win->get_gsg()->is_active()) { if (win->flip_ready()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Flipping window " << win->get_name() << "\n"; + } { PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); win->begin_flip(); @@ -1452,6 +1456,11 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, gsg->pop_group_marker(); } + if (display_cat.is_spam()) { + display_cat.spam() + << "Culling and 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++) { PT(DisplayRegion) dr = win->get_active_display_region(i); @@ -1463,6 +1472,10 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, if (_auto_flip) { if (win->flip_ready()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Flipping window " << win->get_name() << "\n"; + } { PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread); win->begin_flip(); @@ -1559,6 +1572,11 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) { for (size_t wi = 0; wi < wlist_size; ++wi) { GraphicsOutput *win = wlist[wi]; if (win->is_active() && win->get_gsg()->is_active()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Culling window " << win->get_name() << "\n"; + } + GraphicsStateGuardian *gsg = win->get_gsg(); PStatTimer timer(win->get_cull_window_pcollector(), current_thread); int num_display_regions = win->get_num_active_display_regions(); @@ -1658,6 +1676,10 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { GraphicsOutput *host = win->get_host(); if (host->flip_ready()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Flipping window " << win->get_name() << "\n"; + } { // We can't use a PStatGPUTimer before begin_frame, so when using // GPU timing, it is advisable to set auto-flip to #t. @@ -1705,6 +1727,10 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { #endif if (win->flip_ready()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Flipping window " << win->get_name() << "\n"; + } { // begin_flip doesn't do anything interesting, let's not waste // two timer queries on that. @@ -1780,6 +1806,11 @@ flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) { for (i = 0; i < num_windows; ++i) { GraphicsOutput *win = wlist[i]; if (win->flip_ready()) { + if (display_cat.is_spam()) { + display_cat.spam() + << "Flipping window " << win->get_name() << "\n"; + } + nassertv(warray_count < num_windows); warray[warray_count] = win; ++warray_count; @@ -2186,6 +2217,11 @@ void GraphicsEngine:: do_resort_windows() { _windows_sorted = true; + if (display_cat.is_spam()) { + display_cat.spam() + << "Re-sorting window list.\n"; + } + _app.resort_windows(); Threads::const_iterator ti; for (ti = _threads.begin(); ti != _threads.end(); ++ti) { From aab149e75aa02aad4eaefdab0894820a8232062c Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 2 Apr 2022 20:42:04 +0200 Subject: [PATCH 115/166] windisplay: Add debug prints listing display devices and monitors --- panda/src/windisplay/winGraphicsPipe.cxx | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/panda/src/windisplay/winGraphicsPipe.cxx b/panda/src/windisplay/winGraphicsPipe.cxx index b89d022ca8..eb29048c65 100644 --- a/panda/src/windisplay/winGraphicsPipe.cxx +++ b/panda/src/windisplay/winGraphicsPipe.cxx @@ -259,6 +259,71 @@ WinGraphicsPipe() { } } + if (windisplay_cat.is_debug()) { + windisplay_cat.debug() + << "Detected display devices:\n"; + + DISPLAY_DEVICEA device; + device.cb = sizeof(device); + for (DWORD devnum = 0; EnumDisplayDevicesA(nullptr, devnum, &device, 0); ++devnum) { + std::ostream &out = windisplay_cat.debug(); + out << " " << device.DeviceName << " [" << device.DeviceString << "]"; + if (device.StateFlags & DISPLAY_DEVICE_ACTIVE) { + out << " (active)"; + } + if (device.StateFlags & DISPLAY_DEVICE_MULTI_DRIVER) { + out << " (multi-driver)"; + } + if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { + out << " (primary)"; + } + if (device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) { + out << " (mirroring)"; + } + if (device.StateFlags & DISPLAY_DEVICE_REMOVABLE) { + out << " (removable)"; + } + out << "\n"; + } + + int nmonitor = GetSystemMetrics(SM_CMONITORS); + windisplay_cat.debug() + << "Detected " << nmonitor << " monitors, " + << (GetSystemMetrics(SM_SAMEDISPLAYFORMAT) != 0 ? "" : "NOT ") + << "sharing same display format:\n"; + + EnumDisplayMonitors( + nullptr, + nullptr, + [](HMONITOR monitor, HDC dc, LPRECT rect, LPARAM param) -> BOOL { + MONITORINFOEXA info; + info.cbSize = sizeof(info); + if (GetMonitorInfoA(monitor, &info)) { + std::ostream &out = windisplay_cat.debug() << " "; + + DISPLAY_DEVICEA device; + device.cb = sizeof(device); + device.StateFlags = 0; + if (EnumDisplayDevicesA(info.szDevice, 0, &device, 0)) { + out << device.DeviceName << " [" << device.DeviceString << "]"; + } + else { + out << info.szDevice << " (device enum failed)"; + } + + if (info.dwFlags & MONITORINFOF_PRIMARY) { + out << " (primary)"; + } + if (info.rcWork.left != 0 || info.rcWork.top != 0) { + out << " (at " << info.rcWork.left << "x" << info.rcWork.top << ")"; + } + out << "\n"; + } + return TRUE; + }, + 0); + } + #ifdef HAVE_DX9 // Use D3D to get display info. This is disabled by default as it is slow. if (request_dxdisplay_information) { From 646611bfa6cf706adb216e26804a61f403383770 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 4 Apr 2022 09:40:42 +0200 Subject: [PATCH 116/166] workflow: Update GitHub CI builder to Windows 2019 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdc640961a..a73a2992e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" strategy: matrix: - os: [ubuntu-18.04, windows-2016, macOS-10.15] + os: [ubuntu-18.04, windows-2019, macOS-10.15] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 @@ -38,7 +38,7 @@ jobs: - name: Build Python 3.9 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 + 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.9 shell: bash run: | @@ -51,7 +51,7 @@ jobs: - name: Build Python 3.8 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 + 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.8 shell: bash run: | @@ -64,7 +64,7 @@ jobs: - name: Build Python 3.7 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 + 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.7 shell: bash run: | From e7dd93d0d485055f2ebed8444088756060d6742f Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 4 Apr 2022 10:28:18 +0200 Subject: [PATCH 117/166] filter: Fix CommonFilters with non-default coordinate system Fixes #1289 --- direct/src/filter/CommonFilters.py | 20 +++++++++++++++----- direct/src/filter/filterBloomI.py | 3 ++- direct/src/filter/filterBloomX.py | 5 +++-- direct/src/filter/filterBloomY.py | 3 ++- direct/src/filter/filterBlurX.py | 8 ++++---- direct/src/filter/filterBlurY.py | 8 ++++---- direct/src/filter/filterCopy.py | 5 +++-- direct/src/filter/filterDown4.py | 5 +++-- 8 files changed, 36 insertions(+), 21 deletions(-) diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 25715d7167..ce4b9078c8 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -29,6 +29,7 @@ from panda3d.core import Filename from panda3d.core import AuxBitplaneAttrib from panda3d.core import Texture, Shader, ATSNone from panda3d.core import FrameBufferProperties +from panda3d.core import getDefaultCoordinateSystem, CS_zup_right, CS_zup_left import os CARTOON_BODY=""" @@ -51,6 +52,7 @@ o_color = lerp(o_color, k_cartooncolor, cartoon_thresh); SSAO_BODY="""//Cg void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoord : TEXCOORD0, out float2 l_texcoordD : TEXCOORD1, @@ -60,9 +62,9 @@ void vshader(float4 vtx_position : POSITION, uniform float4x4 mat_modelproj) { l_position = mul(mat_modelproj, vtx_position); - l_texcoord = vtx_position.xz; - l_texcoordD = (vtx_position.xz * texpad_depth.xy) + texpad_depth.xy; - l_texcoordN = (vtx_position.xz * texpad_normal.xy) + texpad_normal.xy; + l_texcoord = vtx_texcoord; + l_texcoordD = vtx_texcoord * texpad_depth.xy * 2; + l_texcoordN = vtx_texcoord * texpad_normal.xy * 2; } float3 sphere[16] = float3[](float3(0.53812504, 0.18565957, -0.43192),float3(0.13790712, 0.24864247, 0.44301823),float3(0.33715037, 0.56794053, -0.005789503),float3(-0.6999805, -0.04511441, -0.0019965635),float3(0.06896307, -0.15983082, -0.85477847),float3(0.056099437, 0.006954967, -0.1843352),float3(-0.014653638, 0.14027752, 0.0762037),float3(0.010019933, -0.1924225, -0.034443386),float3(-0.35775623, -0.5301969, -0.43581226),float3(-0.3169221, 0.106360726, 0.015860917),float3(0.010350345, -0.58698344, 0.0046293875),float3(-0.08972908, -0.49408212, 0.3287904),float3(0.7119986, -0.0154690035, -0.09183723),float3(-0.053382345, 0.059675813, -0.5411899),float3(0.035267662, -0.063188605, 0.54602677),float3(-0.47761092, 0.2847911, -0.0271716)); @@ -292,11 +294,19 @@ class CommonFilters: text += "{\n" text += " l_position = mul(mat_modelproj, vtx_position);\n" + # The card is oriented differently depending on our chosen + # coordinate system. We could just use vtx_texcoord, but this + # saves on an additional variable. + if getDefaultCoordinateSystem() in (CS_zup_right, CS_zup_left): + pos = "vtx_position.xz" + else: + pos = "vtx_position.xy" + for texcoord, padTex in texcoordPadding.items(): if padTex is None: - text += " %s = vtx_position.xz * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord) + text += " %s = %s * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord, pos) else: - text += " %s = (vtx_position.xz * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, padTex, padTex) + text += " %s = (%s * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, pos, padTex, padTex) if ("HalfPixelShift" in configuration): text += " %s += texpix_tx%s.xy * 0.5;\n" % (texcoord, padTex) diff --git a/direct/src/filter/filterBloomI.py b/direct/src/filter/filterBloomI.py index 6c44fb3ecb..ddb44c4ad0 100644 --- a/direct/src/filter/filterBloomI.py +++ b/direct/src/filter/filterBloomI.py @@ -32,6 +32,7 @@ BLOOM_I = """ void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoordNW : TEXCOORD0, out float2 l_texcoordNE : TEXCOORD1, @@ -42,7 +43,7 @@ void vshader(float4 vtx_position : POSITION, uniform float4x4 mat_modelproj) { l_position=mul(mat_modelproj, vtx_position); - float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; + float2 c = vtx_texcoord * texpad_src.xy * 2; float4 offs = texpix_src * 0.5; l_texcoordNW = c + float2( offs.x, -offs.y); l_texcoordNE = c + float2( offs.x, offs.y); diff --git a/direct/src/filter/filterBloomX.py b/direct/src/filter/filterBloomX.py index 220cd776b7..c7e40ab326 100644 --- a/direct/src/filter/filterBloomX.py +++ b/direct/src/filter/filterBloomX.py @@ -2,6 +2,7 @@ BLOOM_X = """ //Cg void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float4 l_texcoord0 : TEXCOORD0, out float4 l_texcoord1 : TEXCOORD1, @@ -10,8 +11,8 @@ void vshader(float4 vtx_position : POSITION, uniform float4 texpix_src, uniform float4x4 mat_modelproj) { - l_position=mul(mat_modelproj, vtx_position); - float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; + l_position = mul(mat_modelproj, vtx_position); + float2 c = vtx_texcoord * texpad_src.xy * 2; float offset = texpix_src.x; float pad = texpad_src.x * 2; l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y); diff --git a/direct/src/filter/filterBloomY.py b/direct/src/filter/filterBloomY.py index 18e2885b1e..0b6cc9d5f8 100644 --- a/direct/src/filter/filterBloomY.py +++ b/direct/src/filter/filterBloomY.py @@ -2,6 +2,7 @@ BLOOM_Y = """ //Cg void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float4 l_texcoord0 : TEXCOORD0, out float4 l_texcoord1 : TEXCOORD1, @@ -11,7 +12,7 @@ void vshader(float4 vtx_position : POSITION, uniform float4x4 mat_modelproj) { l_position=mul(mat_modelproj, vtx_position); - float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; + float2 c = vtx_texcoord * texpad_src.xy * 2; float offset = texpix_src.y; float pad = texpad_src.y * 2; l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x); diff --git a/direct/src/filter/filterBlurX.py b/direct/src/filter/filterBlurX.py index b472a5d7a7..426c1dc9a7 100644 --- a/direct/src/filter/filterBlurX.py +++ b/direct/src/filter/filterBlurX.py @@ -4,14 +4,14 @@ BLUR_X = """ //Cg profile arbvp1 arbfp1 void vshader(float4 vtx_position : POSITION, - float2 vtx_texcoord0 : TEXCOORD0, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, - out float2 l_texcoord0 : TEXCOORD0, + out float2 l_texcoord0 : TEXCOORD0, uniform float4 texpad_src, uniform float4x4 mat_modelproj) { - l_position=mul(mat_modelproj, vtx_position); - l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy; + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord * texpad_src.xy * 2; } diff --git a/direct/src/filter/filterBlurY.py b/direct/src/filter/filterBlurY.py index 173c7a73b5..00d0980b30 100644 --- a/direct/src/filter/filterBlurY.py +++ b/direct/src/filter/filterBlurY.py @@ -4,14 +4,14 @@ BLUR_Y = """ //Cg profile arbvp1 arbfp1 void vshader(float4 vtx_position : POSITION, - float2 vtx_texcoord0 : TEXCOORD0, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, - out float2 l_texcoord0 : TEXCOORD0, + out float2 l_texcoord0 : TEXCOORD0, uniform float4 texpad_src, uniform float4x4 mat_modelproj) { - l_position=mul(mat_modelproj, vtx_position); - l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy; + l_position = mul(mat_modelproj, vtx_position); + l_texcoord0 = vtx_texcoord * texpad_src.xy * 2; } diff --git a/direct/src/filter/filterCopy.py b/direct/src/filter/filterCopy.py index d9770fb04b..dee3dadace 100644 --- a/direct/src/filter/filterCopy.py +++ b/direct/src/filter/filterCopy.py @@ -3,13 +3,14 @@ COPY = """ void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoord : TEXCOORD0, uniform float4 texpad_src, uniform float4x4 mat_modelproj) { - l_position=mul(mat_modelproj, vtx_position); - l_texcoord = (vtx_position.xz * texpad_src.xy) + texpad_src.xy; + l_position = mul(mat_modelproj, vtx_position); + l_texcoord = vtx_texcoord * texpad_src.xy * 2; } void fshader(float2 l_texcoord : TEXCOORD0, diff --git a/direct/src/filter/filterDown4.py b/direct/src/filter/filterDown4.py index 14595dbbd7..d0c7ab4d2c 100644 --- a/direct/src/filter/filterDown4.py +++ b/direct/src/filter/filterDown4.py @@ -2,6 +2,7 @@ DOWN_4 = """ //Cg void vshader(float4 vtx_position : POSITION, + float2 vtx_texcoord : TEXCOORD0, out float4 l_position : POSITION, out float2 l_texcoordNW : TEXCOORD0, out float2 l_texcoordNE : TEXCOORD1, @@ -11,8 +12,8 @@ void vshader(float4 vtx_position : POSITION, uniform float4 texpix_src, uniform float4x4 mat_modelproj) { - l_position=mul(mat_modelproj, vtx_position); - float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy; + l_position = mul(mat_modelproj, vtx_position); + float2 c = vtx_texcoord * texpad_src.xy * 2; l_texcoordNW = c + float2( texpix_src.x, -texpix_src.y); l_texcoordNE = c + float2( texpix_src.x, texpix_src.y); l_texcoordSW = c + float2(-texpix_src.x, -texpix_src.y); From 98314da00ff9d1d0ef567f1a82796862462f6540 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 4 Apr 2022 10:30:35 +0200 Subject: [PATCH 118/166] showbase: Fix BufferViewer frame in non-standard coordinate systems --- direct/src/showbase/BufferViewer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py index ec46e0ff62..ed2fc87733 100644 --- a/direct/src/showbase/BufferViewer.py +++ b/direct/src/showbase/BufferViewer.py @@ -240,10 +240,10 @@ class BufferViewer(DirectObject): offsetx = (ringoffset[ring]*2.0) / float(sizex) offsety = (ringoffset[ring]*2.0) / float(sizey) bright = ringbright[ring] - vwriter.addData3f(-1-offsetx, 0, -1-offsety) - vwriter.addData3f(1+offsetx, 0, -1-offsety) - vwriter.addData3f(1+offsetx, 0, 1+offsety) - vwriter.addData3f(-1-offsetx, 0, 1+offsety) + vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, -1 - offsety)) + vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, -1 - offsety)) + vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, 1 + offsety)) + vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, 1 + offsety)) cwriter.addData3f(bright, bright, bright) cwriter.addData3f(bright, bright, bright) cwriter.addData3f(bright, bright, bright) From 5ceaf66079451b08c66c28cff6f975e479b45c4a Mon Sep 17 00:00:00 2001 From: Geraldo Nascimento Date: Tue, 3 May 2022 18:56:28 -0300 Subject: [PATCH 119/166] v4l: O_NONBLOCK flag should be OR'ed to O_RDWR or mmap will fail below Closes #1299 --- panda/src/vision/webcamVideoCursorV4L.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/vision/webcamVideoCursorV4L.cxx b/panda/src/vision/webcamVideoCursorV4L.cxx index 197bda3228..1939b16912 100644 --- a/panda/src/vision/webcamVideoCursorV4L.cxx +++ b/panda/src/vision/webcamVideoCursorV4L.cxx @@ -217,7 +217,7 @@ WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) { int mode = O_RDWR; if (!v4l_blocking) { - mode = O_NONBLOCK; + mode |= O_NONBLOCK; } _fd = open(src->_device.c_str(), mode); From d98f9666930bd22b88b8485b0e3aeb16095d1f7f Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 5 May 2022 10:38:25 +0200 Subject: [PATCH 120/166] display: Extra spam message about what window a flip is done for --- panda/src/display/graphicsEngine.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index c93228b7c5..4fcbd323a2 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1678,7 +1678,8 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { if (host->flip_ready()) { if (display_cat.is_spam()) { display_cat.spam() - << "Flipping window " << win->get_name() << "\n"; + << "Flipping window " << host->get_name() + << " before drawing window " << win->get_name() << "\n"; } { // We can't use a PStatGPUTimer before begin_frame, so when using From d2fc682fd73e09cd356253e9141c5988b1a978a5 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 5 May 2022 10:40:22 +0200 Subject: [PATCH 121/166] glgsg: Support floating-point FBOs in OpenGL ES 2+ See issue #1296 --- panda/src/glstuff/glGraphicsBuffer_src.cxx | 53 +++++++++++++++++++ .../glstuff/glGraphicsStateGuardian_src.cxx | 8 ++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 35a4e2f5db..ea573a13e0 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -861,6 +861,50 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, // case RTP_stencil: gl_format = GL_STENCIL_INDEX8; break default: if (_fb_properties.get_alpha_bits() == 0) { +#ifndef OPENGLES_1 + if (_fb_properties.get_float_color() && + glgsg->has_extension("GL_EXT_color_buffer_float")) { + // This extension supports the full range of floating-point formats. + if (_fb_properties.get_color_bits() > 16 * 3 || + _fb_properties.get_red_bits() > 16 || + _fb_properties.get_green_bits() > 16 || + _fb_properties.get_blue_bits() > 16) { + // 32-bit, which is always floating-point. + if (_fb_properties.get_blue_bits() > 0 || + _fb_properties.get_color_bits() == 1 || + _fb_properties.get_color_bits() > 32 * 2) { + gl_format = GL_RGB32F; + } else if (_fb_properties.get_green_bits() > 0 || + _fb_properties.get_color_bits() > 32) { + gl_format = GL_RG32F; + } else { + gl_format = GL_R32F; + } + } else { + // 16-bit floating-point. + if (_fb_properties.get_blue_bits() > 10 || + _fb_properties.get_color_bits() == 1 || + _fb_properties.get_color_bits() > 32) { + gl_format = GL_RGB16F; + } else if (_fb_properties.get_blue_bits() > 0) { + if (_fb_properties.get_red_bits() > 11 || + _fb_properties.get_green_bits() > 11) { + gl_format = GL_RGB16F; + } else { + gl_format = GL_R11F_G11F_B10F; + } + } else if (_fb_properties.get_green_bits() > 0 || + _fb_properties.get_color_bits() > 16) { + gl_format = GL_RG16F; + } else { + gl_format = GL_R16F; + } + } + } else if (_fb_properties.get_float_color() && + glgsg->has_extension("GL_EXT_color_buffer_half_float")) { + gl_format = GL_RGB16F_EXT; + } else +#endif if (_fb_properties.get_color_bits() <= 16) { gl_format = GL_RGB565_OES; } else if (_fb_properties.get_color_bits() <= 24) { @@ -868,6 +912,15 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } else { gl_format = GL_RGB10_EXT; } +#ifndef OPENGLES_1 + } else if (_fb_properties.get_float_color() && + _fb_properties.get_color_bits() > 16 * 3 && + glgsg->has_extension("GL_EXT_color_buffer_float")) { + gl_format = GL_RGBA32F_EXT; + } else if (_fb_properties.get_float_color() && + glgsg->has_extension("GL_EXT_color_buffer_half_float")) { + gl_format = GL_RGBA16F_EXT; +#endif } else if (_fb_properties.get_color_bits() == 0) { gl_format = GL_ALPHA8_EXT; } else if (_fb_properties.get_color_bits() <= 12 diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 4884fb9c20..c536ed6747 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -10322,9 +10322,15 @@ get_internal_image_format(Texture *tex, bool force_sized) const { } else { return GL_RGBA16_SNORM; } +#elif !defined(OPENGLES_1) + case Texture::F_rgba16: + return GL_RGBA16F; +#endif // OPENGLES + +#ifndef OPENGLES_1 case Texture::F_rgba32: return GL_RGBA32F; -#endif // OPENGLES +#endif case Texture::F_rgb: switch (component_type) { From 59755a043eb8b98b4e17b13e756d4255998f0ef6 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 5 May 2022 10:44:00 +0200 Subject: [PATCH 122/166] distributed: Fix regression from c917a9e (which was fix for #1262) With that fix, getDatagram would return the wrong result after the datagram was reassigned in C++ --- direct/src/distributed/PyDatagramIterator.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/direct/src/distributed/PyDatagramIterator.py b/direct/src/distributed/PyDatagramIterator.py index 13c810e90d..591734c0dc 100755 --- a/direct/src/distributed/PyDatagramIterator.py +++ b/direct/src/distributed/PyDatagramIterator.py @@ -35,15 +35,13 @@ class PyDatagramIterator(DatagramIterator): super().__init__(datagram, offset) # Retain a reference to it so that it doesn't get deleted. - self.__datagram = datagram + self.__initialDatagram = datagram else: super().__init__() - def getDatagram(self): - return self.__datagram - - def get_datagram(self): - return self.__datagram + def assign(self, datagram, offset = 0): + super().assign(datagram, offset) + self.__initialDatagram = datagram def getArg(self, subatomicType, divisor=1): # Import the type numbers From 0538106d52b5169ff4a8eec90ae3c3bf468d6d28 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 13:54:03 +0200 Subject: [PATCH 123/166] glgsg: Fix half float color buffers with GLES 3 / WebGL 2 Part of fix for #1296 --- panda/src/glstuff/glGraphicsBuffer_src.cxx | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index ea573a13e0..983e7cef23 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -857,6 +857,26 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, gl_format = GL_DEPTH_COMPONENT16; } break; +#ifndef OPENGLES_1 + case RTP_aux_hrgba_0: + case RTP_aux_hrgba_1: + case RTP_aux_hrgba_2: + case RTP_aux_hrgba_3: + case RTP_aux_float_0: + case RTP_aux_float_1: + case RTP_aux_float_2: + case RTP_aux_float_3: + if (glgsg->has_extension("GL_EXT_color_buffer_float")) { + if (slot >= RTP_aux_float_0 && slot <= RTP_aux_float_3) { + gl_format = GL_RGBA32F; + } else { + gl_format = GL_RGBA16F; + } + } + else if (glgsg->has_extension("GL_EXT_color_buffer_half_float")) { + gl_format = GL_RGBA16F_EXT; + } +#endif // NB: we currently use RTP_stencil to store the right eye for stereo. // case RTP_stencil: gl_format = GL_STENCIL_INDEX8; break default: @@ -914,9 +934,12 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, } #ifndef OPENGLES_1 } else if (_fb_properties.get_float_color() && - _fb_properties.get_color_bits() > 16 * 3 && glgsg->has_extension("GL_EXT_color_buffer_float")) { - gl_format = GL_RGBA32F_EXT; + if (_fb_properties.get_color_bits() > 16 * 3) { + gl_format = GL_RGBA32F; + } else { + gl_format = GL_RGBA16F; + } } else if (_fb_properties.get_float_color() && glgsg->has_extension("GL_EXT_color_buffer_half_float")) { gl_format = GL_RGBA16F_EXT; From fbea0056f5bc5ae3d0f292331640114ede9471f9 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 14:08:21 +0200 Subject: [PATCH 124/166] general: Allow compiling Panda headers on Windows without NOMINMAX --- dtool/src/prc/notifyCategory.I | 2 +- panda/src/device/inputDevice.I | 2 +- panda/src/display/drawableRegion.I | 2 +- panda/src/display/frameBufferProperties.I | 9 ++--- panda/src/display/graphicsOutput.I | 24 ++++++------- panda/src/display/graphicsStateGuardian.I | 4 +-- panda/src/egg/eggMesherEdge.I | 2 +- panda/src/express/multifile.I | 2 +- panda/src/express/pointerToArray.I | 12 +++---- panda/src/express/pointerToArray_ext.I | 12 +++---- panda/src/gobj/geomVertexArrayData.I | 4 +-- panda/src/gobj/geomVertexWriter.I | 4 +-- panda/src/gobj/paramTexture.I | 2 +- panda/src/gobj/textureContext.I | 4 +-- panda/src/grutil/geoMipTerrain.I | 4 +-- panda/src/linmath/lvector3_src.I | 4 +-- panda/src/parametrics/nurbsBasisVector.I | 2 +- .../parametrics/parametricCurveCollection.I | 2 +- .../src/particlesystem/baseParticleRenderer.I | 2 +- panda/src/pgui/pgEntry.I | 2 +- panda/src/pgui/pgSliderBar.I | 2 +- panda/src/pipeline/pipeline.I | 2 +- panda/src/pipeline/thread.I | 2 +- panda/src/pnmimage/convert_srgb.I | 8 ++--- panda/src/pnmimage/pfmFile.I | 8 ++--- panda/src/pnmimage/pnmImage.I | 36 +++++++++---------- panda/src/putil/clockObject.I | 2 +- 27 files changed, 81 insertions(+), 80 deletions(-) diff --git a/dtool/src/prc/notifyCategory.I b/dtool/src/prc/notifyCategory.I index 74c1965778..4f513722f4 100644 --- a/dtool/src/prc/notifyCategory.I +++ b/dtool/src/prc/notifyCategory.I @@ -49,7 +49,7 @@ set_severity(NotifySeverity severity) { _severity = severity; #else // enforce the no-debug, no-spam rule. - _severity = std::max(severity, NS_info); + _severity = (std::max)(severity, NS_info); #endif invalidate_cache(); } diff --git a/panda/src/device/inputDevice.I b/panda/src/device/inputDevice.I index 532e77f796..3bf6aa9001 100644 --- a/panda/src/device/inputDevice.I +++ b/panda/src/device/inputDevice.I @@ -344,7 +344,7 @@ is_axis_known(size_t index) const { INLINE void InputDevice:: set_vibration(double strong, double weak) { LightMutexHolder holder(_lock); - do_set_vibration(std::max(std::min(strong, 1.0), 0.0), std::max(std::min(weak, 1.0), 0.0)); + do_set_vibration((std::max)((std::min)(strong, 1.0), 0.0), (std::max)((std::min)(weak, 1.0), 0.0)); } /** diff --git a/panda/src/display/drawableRegion.I b/panda/src/display/drawableRegion.I index 71fc1658cc..dcaa4ba276 100644 --- a/panda/src/display/drawableRegion.I +++ b/panda/src/display/drawableRegion.I @@ -242,7 +242,7 @@ INLINE void DrawableRegion:: update_pixel_factor() { PN_stdfloat new_pixel_factor; if (supports_pixel_zoom()) { - new_pixel_factor = (PN_stdfloat)1 / sqrt(std::max(_pixel_zoom, (PN_stdfloat)1.0)); + new_pixel_factor = (PN_stdfloat)1 / sqrt((std::max)(_pixel_zoom, (PN_stdfloat)1.0)); } else { new_pixel_factor = 1; } diff --git a/panda/src/display/frameBufferProperties.I b/panda/src/display/frameBufferProperties.I index 756a98c0be..af69f86fee 100644 --- a/panda/src/display/frameBufferProperties.I +++ b/panda/src/display/frameBufferProperties.I @@ -57,10 +57,11 @@ get_depth_bits() const { */ INLINE int FrameBufferProperties:: get_color_bits() const { - return std::max(_property[FBP_color_bits], - _property[FBP_red_bits] + - _property[FBP_green_bits] + - _property[FBP_blue_bits]); + return (std::max)( + _property[FBP_color_bits], + _property[FBP_red_bits] + + _property[FBP_green_bits] + + _property[FBP_blue_bits]); } /** diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I index 992f634dd9..1f4e2d87a4 100644 --- a/panda/src/display/graphicsOutput.I +++ b/panda/src/display/graphicsOutput.I @@ -167,8 +167,8 @@ get_y_size() const { */ INLINE LVecBase2i GraphicsOutput:: get_fb_size() const { - return LVecBase2i(std::max(int(_size.get_x() * get_pixel_factor()), 1), - std::max(int(_size.get_y() * get_pixel_factor()), 1)); + return LVecBase2i((std::max)(int(_size.get_x() * get_pixel_factor()), 1), + (std::max)(int(_size.get_y() * get_pixel_factor()), 1)); } /** @@ -178,7 +178,7 @@ get_fb_size() const { */ INLINE int GraphicsOutput:: get_fb_x_size() const { - return std::max(int(_size.get_x() * get_pixel_factor()), 1); + return (std::max)(int(_size.get_x() * get_pixel_factor()), 1); } /** @@ -188,7 +188,7 @@ get_fb_x_size() const { */ INLINE int GraphicsOutput:: get_fb_y_size() const { - return std::max(int(_size.get_y() * get_pixel_factor()), 1); + return (std::max)(int(_size.get_y() * get_pixel_factor()), 1); } /** @@ -200,8 +200,8 @@ INLINE LVecBase2i GraphicsOutput:: get_sbs_left_size() const { PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0]; PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2]; - return LVecBase2i(std::max(int(_size.get_x() * left_w), 1), - std::max(int(_size.get_y() * left_h), 1)); + return LVecBase2i((std::max)(int(_size.get_x() * left_w), 1), + (std::max)(int(_size.get_y() * left_h), 1)); } /** @@ -212,7 +212,7 @@ get_sbs_left_size() const { INLINE int GraphicsOutput:: get_sbs_left_x_size() const { PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0]; - return std::max(int(_size.get_x() * left_w), 1); + return (std::max)(int(_size.get_x() * left_w), 1); } /** @@ -223,7 +223,7 @@ get_sbs_left_x_size() const { INLINE int GraphicsOutput:: get_sbs_left_y_size() const { PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2]; - return std::max(int(_size.get_y() * left_h), 1); + return (std::max)(int(_size.get_y() * left_h), 1); } /** @@ -235,8 +235,8 @@ INLINE LVecBase2i GraphicsOutput:: get_sbs_right_size() const { PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0]; PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2]; - return LVecBase2i(std::max(int(_size.get_x() * right_w), 1), - std::max(int(_size.get_y() * right_h), 1)); + return LVecBase2i((std::max)(int(_size.get_x() * right_w), 1), + (std::max)(int(_size.get_y() * right_h), 1)); } /** @@ -247,7 +247,7 @@ get_sbs_right_size() const { INLINE int GraphicsOutput:: get_sbs_right_x_size() const { PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0]; - return std::max(int(_size.get_x() * right_w), 1); + return (std::max)(int(_size.get_x() * right_w), 1); } /** @@ -258,7 +258,7 @@ get_sbs_right_x_size() const { INLINE int GraphicsOutput:: get_sbs_right_y_size() const { PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2]; - return std::max(int(_size.get_y() * right_h), 1); + return (std::max)(int(_size.get_y() * right_h), 1); } /** diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index f14a0fb087..09dd8c60b4 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -258,7 +258,7 @@ get_max_vertices_per_primitive() const { INLINE int GraphicsStateGuardian:: get_max_texture_stages() const { if (max_texture_stages > 0) { - return std::min(_max_texture_stages, (int)max_texture_stages); + return (std::min)(_max_texture_stages, (int)max_texture_stages); } return _max_texture_stages; } @@ -695,7 +695,7 @@ get_timer_queries_active() const { INLINE int GraphicsStateGuardian:: get_max_color_targets() const { if (max_color_targets > 0) { - return std::min(_max_color_targets, (int)max_color_targets); + return (std::min)(_max_color_targets, (int)max_color_targets); } return _max_color_targets; } diff --git a/panda/src/egg/eggMesherEdge.I b/panda/src/egg/eggMesherEdge.I index dbd37f0b70..b3520aa7bc 100644 --- a/panda/src/egg/eggMesherEdge.I +++ b/panda/src/egg/eggMesherEdge.I @@ -58,7 +58,7 @@ matches(const EggMesherEdge &other) const { */ INLINE EggMesherEdge *EggMesherEdge:: common_ptr() { - return std::min(this, _opposite); + return (std::min)(this, _opposite); } /** diff --git a/panda/src/express/multifile.I b/panda/src/express/multifile.I index 21462cc467..d418c77598 100644 --- a/panda/src/express/multifile.I +++ b/panda/src/express/multifile.I @@ -431,6 +431,6 @@ is_cert_special() const { */ INLINE std::streampos Multifile::Subfile:: get_last_byte_pos() const { - return std::max(_index_start + (std::streampos)_index_length, + return (std::max)(_index_start + (std::streampos)_index_length, _data_start + (std::streampos)_data_length) - (std::streampos)1; } diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I index 5fb560b695..da0f4ea5ae 100644 --- a/panda/src/express/pointerToArray.I +++ b/panda/src/express/pointerToArray.I @@ -471,9 +471,9 @@ set_data(const std::string &data) { template INLINE std::string PointerToArray:: get_subdata(size_type n, size_type count) const { - n = std::min(n, size()); - count = std::max(count, n); - count = std::min(count, size() - n); + n = (std::min)(n, size()); + count = (std::max)(count, n); + count = (std::min)(count, size() - n); return std::string((const char *)(p() + n), sizeof(Element) * count); } @@ -965,9 +965,9 @@ get_data() const { template INLINE std::string ConstPointerToArray:: get_subdata(size_type n, size_type count) const { - n = std::min(n, size()); - count = std::max(count, n); - count = std::min(count, size() - n); + n = (std::min)(n, size()); + count = (std::max)(count, n); + count = (std::min)(count, size() - n); return std::string((const char *)(p() + n), sizeof(Element) * count); } diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index b64f973a5f..425f4d87e3 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -249,9 +249,9 @@ set_data(PyObject *data) { template INLINE PyObject *Extension >:: get_subdata(size_t n, size_t count) const { - n = std::min(n, this->_this->size()); - count = std::max(count, n); - count = std::min(count, this->_this->size() - n); + n = (std::min)(n, this->_this->size()); + count = (std::max)(count, n); + count = (std::min)(count, this->_this->size() - n); #if PY_MAJOR_VERSION >= 3 return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); #else @@ -313,9 +313,9 @@ get_data() const { template INLINE PyObject *Extension >:: get_subdata(size_t n, size_t count) const { - n = std::min(n, this->_this->size()); - count = std::max(count, n); - count = std::min(count, this->_this->size() - n); + n = (std::min)(n, this->_this->size()); + count = (std::max)(count, n); + count = (std::min)(count, this->_this->size() - n); #if PY_MAJOR_VERSION >= 3 return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count); #else diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index 30e5c590e8..8c1bfdc6fd 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -535,8 +535,8 @@ get_data() const { INLINE vector_uchar GeomVertexArrayDataHandle:: get_subdata(size_t start, size_t size) const { mark_used(); - start = std::min(start, _cdata->_buffer.get_size()); - size = std::min(size, _cdata->_buffer.get_size() - start); + start = (std::min)(start, _cdata->_buffer.get_size()); + size = (std::min)(size, _cdata->_buffer.get_size() - start); const unsigned char *ptr = _cdata->_buffer.get_read_pointer(true) + start; return vector_uchar(ptr, ptr + size); } diff --git a/panda/src/gobj/geomVertexWriter.I b/panda/src/gobj/geomVertexWriter.I index b55fdd5f4a..9795262150 100644 --- a/panda/src/gobj/geomVertexWriter.I +++ b/panda/src/gobj/geomVertexWriter.I @@ -1382,13 +1382,13 @@ inc_add_pointer() { _handle = nullptr; GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread); writer.check_array_writers(); - writer.set_num_rows(std::max(write_row + 1, writer.get_num_rows())); + writer.set_num_rows((std::max)(write_row + 1, writer.get_num_rows())); _handle = writer.get_array_writer(_array); } else { // Otherwise, we can get away with modifying only the one array we're // using. - _handle->set_num_rows(std::max(write_row + 1, _handle->get_num_rows())); + _handle->set_num_rows((std::max)(write_row + 1, _handle->get_num_rows())); } set_pointer(write_row); diff --git a/panda/src/gobj/paramTexture.I b/panda/src/gobj/paramTexture.I index c8235778e8..059a4ed33a 100644 --- a/panda/src/gobj/paramTexture.I +++ b/panda/src/gobj/paramTexture.I @@ -55,7 +55,7 @@ INLINE ParamTextureImage:: ParamTextureImage(Texture *tex, bool read, bool write, int z, int n) : _texture(tex), _access(0), - _bind_level(std::min(n, 127)), + _bind_level((std::min)(n, 127)), _bind_layer(z) { if (read) { diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I index 2c58bc7b07..ae07c96852 100644 --- a/panda/src/gobj/textureContext.I +++ b/panda/src/gobj/textureContext.I @@ -122,7 +122,7 @@ mark_loaded() { // _data_size_bytes = _data->get_texture_size_bytes(); _properties_modified = get_texture()->get_properties_modified(); _image_modified = get_texture()->get_image_modified(); - update_modified(std::max(_properties_modified, _image_modified)); + update_modified((std::max)(_properties_modified, _image_modified)); // Assume the texture is now resident. set_resident(true); @@ -136,7 +136,7 @@ INLINE void TextureContext:: mark_simple_loaded() { _properties_modified = get_texture()->get_properties_modified(); _simple_image_modified = get_texture()->get_simple_image_modified(); - update_modified(std::max(_properties_modified, _simple_image_modified)); + update_modified((std::max)(_properties_modified, _simple_image_modified)); // The texture's not exactly resident now, but some part of it is. set_resident(true); diff --git a/panda/src/grutil/geoMipTerrain.I b/panda/src/grutil/geoMipTerrain.I index 7eeeddab7a..18dda7240e 100644 --- a/panda/src/grutil/geoMipTerrain.I +++ b/panda/src/grutil/geoMipTerrain.I @@ -479,8 +479,8 @@ get_border_stitching() { */ INLINE double GeoMipTerrain:: get_pixel_value(int x, int y) { - x = std::max(std::min(x,int(_xsize-1)),0); - y = std::max(std::min(y,int(_ysize-1)),0); + x = (std::max)((std::min)(x,int(_xsize-1)),0); + y = (std::max)((std::min)(y,int(_ysize-1)),0); if (_heightfield.is_grayscale()) { return double(_heightfield.get_bright(x, y)); } else { diff --git a/panda/src/linmath/lvector3_src.I b/panda/src/linmath/lvector3_src.I index 3aa198c513..99d28ac156 100644 --- a/panda/src/linmath/lvector3_src.I +++ b/panda/src/linmath/lvector3_src.I @@ -183,10 +183,10 @@ angle_rad(const FLOATNAME(LVector3) &other) const { // poorly as dot(other) approaches 1.0. if (dot(other) < 0.0f) { FLOATTYPE a = ((*this)+other).length() / 2.0f; - return MathNumbers::cpi((FLOATTYPE)0.0f) - 2.0f * casin(std::min(a, (FLOATTYPE)1.0)); + return MathNumbers::cpi((FLOATTYPE)0.0f) - 2.0f * casin((std::min)(a, (FLOATTYPE)1.0)); } else { FLOATTYPE a = ((*this)-other).length() / 2.0f; - return 2.0f * casin(std::min(a, (FLOATTYPE)1.0)); + return 2.0f * casin((std::min)(a, (FLOATTYPE)1.0)); } } diff --git a/panda/src/parametrics/nurbsBasisVector.I b/panda/src/parametrics/nurbsBasisVector.I index 3628d7564b..8bb907f8e9 100644 --- a/panda/src/parametrics/nurbsBasisVector.I +++ b/panda/src/parametrics/nurbsBasisVector.I @@ -110,5 +110,5 @@ scale_t(int segment, PN_stdfloat t) const { PN_stdfloat from = _segments[segment]._from; PN_stdfloat to = _segments[segment]._to; t = (t - from) / (to - from); - return std::min(std::max(t, (PN_stdfloat)0.0), (PN_stdfloat)1.0); + return (std::min)((std::max)(t, (PN_stdfloat)0.0), (PN_stdfloat)1.0); } diff --git a/panda/src/parametrics/parametricCurveCollection.I b/panda/src/parametrics/parametricCurveCollection.I index 711b43b67b..99ebe9e63c 100644 --- a/panda/src/parametrics/parametricCurveCollection.I +++ b/panda/src/parametrics/parametricCurveCollection.I @@ -42,7 +42,7 @@ get_curve(int index) const { */ INLINE void ParametricCurveCollection:: add_curve(ParametricCurve *curve, int index) { - insert_curve(std::max(index, 0), curve); + insert_curve((std::max)(index, 0), curve); } /** diff --git a/panda/src/particlesystem/baseParticleRenderer.I b/panda/src/particlesystem/baseParticleRenderer.I index 7da2037edd..29dd3878aa 100644 --- a/panda/src/particlesystem/baseParticleRenderer.I +++ b/panda/src/particlesystem/baseParticleRenderer.I @@ -97,7 +97,7 @@ get_cur_alpha(BaseParticle* bp) { return bp->get_parameterized_age(); case PR_ALPHA_IN_OUT: - return 2.0 * std::min(bp->get_parameterized_age(), + return 2.0 * (std::min)(bp->get_parameterized_age(), 1.0f - bp->get_parameterized_age()); case PR_ALPHA_USER: diff --git a/panda/src/pgui/pgEntry.I b/panda/src/pgui/pgEntry.I index e398b42b54..e758a7ff8c 100644 --- a/panda/src/pgui/pgEntry.I +++ b/panda/src/pgui/pgEntry.I @@ -558,7 +558,7 @@ set_wtext(const std::wstring &wtext) { update_text(); } #endif - set_cursor_position(std::min(_cursor_position, _text.get_num_characters())); + set_cursor_position((std::min)(_cursor_position, _text.get_num_characters())); return ret; } diff --git a/panda/src/pgui/pgSliderBar.I b/panda/src/pgui/pgSliderBar.I index da5b97c153..8b1ae04685 100644 --- a/panda/src/pgui/pgSliderBar.I +++ b/panda/src/pgui/pgSliderBar.I @@ -381,7 +381,7 @@ get_adjust_event() const { */ INLINE void PGSliderBar:: internal_set_ratio(PN_stdfloat ratio) { - _ratio = std::max(std::min(ratio, (PN_stdfloat)1.0), (PN_stdfloat)0.0); + _ratio = (std::max)((std::min)(ratio, (PN_stdfloat)1.0), (PN_stdfloat)0.0); _needs_reposition = true; adjust(); } diff --git a/panda/src/pipeline/pipeline.I b/panda/src/pipeline/pipeline.I index 2116a030e6..b6c9366e1a 100644 --- a/panda/src/pipeline/pipeline.I +++ b/panda/src/pipeline/pipeline.I @@ -27,7 +27,7 @@ get_render_pipeline() { */ INLINE void Pipeline:: set_min_stages(int min_stages) { - set_num_stages(std::max(min_stages, get_num_stages())); + set_num_stages((std::max)(min_stages, get_num_stages())); } /** diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index dc7001fd06..d2a82b4585 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -75,7 +75,7 @@ get_pipeline_stage() const { */ INLINE void Thread:: set_min_pipeline_stage(int min_pipeline_stage) { - set_pipeline_stage(std::max(_pipeline_stage, min_pipeline_stage)); + set_pipeline_stage((std::max)(_pipeline_stage, min_pipeline_stage)); } /** diff --git a/panda/src/pnmimage/convert_srgb.I b/panda/src/pnmimage/convert_srgb.I index 27d6bbf802..da183149f9 100644 --- a/panda/src/pnmimage/convert_srgb.I +++ b/panda/src/pnmimage/convert_srgb.I @@ -44,8 +44,8 @@ INLINE unsigned char decode_sRGB_uchar(unsigned char val) { */ INLINE unsigned char decode_sRGB_uchar(float val) { return (val <= 0.04045f) - ? (unsigned char)(std::max(0.f, val) * (255.f / 12.92f) + 0.5f) - : (unsigned char)(cpow((std::min(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f); + ? (unsigned char)((std::max)(0.f, val) * (255.f / 12.92f) + 0.5f) + : (unsigned char)(cpow(((std::min)(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f); } /** @@ -99,8 +99,8 @@ encode_sRGB_uchar(float val) { return encode_sRGB_uchar_sse2(val); #else return (val < 0.0031308f) - ? (unsigned char) (std::max(0.f, val) * 3294.6f + 0.5f) - : (unsigned char) (269.025f * cpow(std::min(val, 1.f), 0.41666f) - 13.525f); + ? (unsigned char) ((std::max)(0.f, val) * 3294.6f + 0.5f) + : (unsigned char) (269.025f * cpow((std::min)(val, 1.f), 0.41666f) - 13.525f); #endif } diff --git a/panda/src/pnmimage/pfmFile.I b/panda/src/pnmimage/pfmFile.I index 0ba12061ee..a3fe7c3004 100644 --- a/panda/src/pnmimage/pfmFile.I +++ b/panda/src/pnmimage/pfmFile.I @@ -587,12 +587,12 @@ setup_sub_image(const PfmFile ©, int &xto, int &yto, yto = 0; } - x_size = std::min(x_size, copy.get_x_size() - xfrom); - y_size = std::min(y_size, copy.get_y_size() - yfrom); + x_size = (std::min)(x_size, copy.get_x_size() - xfrom); + y_size = (std::min)(y_size, copy.get_y_size() - yfrom); xmin = xto; ymin = yto; - xmax = std::min(xmin + x_size, get_x_size()); - ymax = std::min(ymin + y_size, get_y_size()); + xmax = (std::min)(xmin + x_size, get_x_size()); + ymax = (std::min)(ymin + y_size, get_y_size()); } diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I index 07862ab40a..9b396df1ad 100644 --- a/panda/src/pnmimage/pnmImage.I +++ b/panda/src/pnmimage/pnmImage.I @@ -68,7 +68,7 @@ INLINE PNMImage:: */ INLINE xelval PNMImage:: clamp_val(int input_value) const { - return (xelval)std::min(std::max(0, input_value), (int)get_maxval()); + return (xelval)(std::min)((std::max)(0, input_value), (int)get_maxval()); } /** @@ -113,9 +113,9 @@ to_val(const LRGBColorf &value) const { case XE_scRGB_alpha: { LRGBColorf scaled = value * 8192.f + 4096.5f; - col.r = std::min(std::max(0, (int)scaled[0]), 65535); - col.g = std::min(std::max(0, (int)scaled[1]), 65535); - col.b = std::min(std::max(0, (int)scaled[2]), 65535); + col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535); + col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535); + col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535); } break; } @@ -131,7 +131,7 @@ to_val(float input_value) const { switch (_xel_encoding) { case XE_generic: case XE_generic_alpha: - return (int)(std::min(1.0f, std::max(0.0f, input_value)) * get_maxval() + 0.5f); + return (int)((std::min)(1.0f, (std::max)(0.0f, input_value)) * get_maxval() + 0.5f); case XE_generic_sRGB: case XE_generic_sRGB_alpha: @@ -148,7 +148,7 @@ to_val(float input_value) const { case XE_scRGB: case XE_scRGB_alpha: - return std::min(std::max(0, (int)((8192 * input_value) + 4096.5f)), 65535); + return (std::min)((std::max)(0, (int)((8192 * input_value) + 4096.5f)), 65535); default: return 0; @@ -210,7 +210,7 @@ from_val(xelval input_value) const { switch (_xel_encoding) { case XE_generic: case XE_generic_alpha: - return std::min((float)input_value * _inv_maxval, 1.0f); + return (std::min)((float)input_value * _inv_maxval, 1.0f); case XE_generic_sRGB: case XE_generic_sRGB_alpha: @@ -735,19 +735,19 @@ set_xel_a(int x, int y, const LColorf &value) { case XE_scRGB: { LColorf scaled = value * 8192.0f + 4096.5f; - col.r = std::min(std::max(0, (int)scaled[0]), 65535); - col.g = std::min(std::max(0, (int)scaled[1]), 65535); - col.b = std::min(std::max(0, (int)scaled[2]), 65535); + col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535); + col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535); + col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535); } break; case XE_scRGB_alpha: { LColorf scaled = value * 8192.0f + 4096.5f; - col.r = std::min(std::max(0, (int)scaled[0]), 65535); - col.g = std::min(std::max(0, (int)scaled[1]), 65535); - col.b = std::min(std::max(0, (int)scaled[2]), 65535); - alpha_row(y)[x] = std::min(std::max(0, (int)(value[3] * 65535 + 0.5f)), 65535); + col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535); + col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535); + col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535); + alpha_row(y)[x] = (std::min)((std::max)(0, (int)(value[3] * 65535 + 0.5f)), 65535); } break; } @@ -1224,14 +1224,14 @@ setup_sub_image(const PNMImage ©, int &xto, int &yto, yto = 0; } - x_size = std::min(x_size, copy.get_x_size() - xfrom); - y_size = std::min(y_size, copy.get_y_size() - yfrom); + x_size = (std::min)(x_size, copy.get_x_size() - xfrom); + y_size = (std::min)(y_size, copy.get_y_size() - yfrom); xmin = xto; ymin = yto; - xmax = std::min(xmin + x_size, get_x_size()); - ymax = std::min(ymin + y_size, get_y_size()); + xmax = (std::min)(xmin + x_size, get_x_size()); + ymax = (std::min)(ymin + y_size, get_y_size()); } /** diff --git a/panda/src/putil/clockObject.I b/panda/src/putil/clockObject.I index b2cb2975d1..4166c45e59 100644 --- a/panda/src/putil/clockObject.I +++ b/panda/src/putil/clockObject.I @@ -110,7 +110,7 @@ INLINE double ClockObject:: get_dt(Thread *current_thread) const { CDReader cdata(_cycler, current_thread); if (_max_dt > 0.0) { - return std::min(_max_dt, cdata->_dt); + return (std::min)(_max_dt, cdata->_dt); } return cdata->_dt; } From 42a19860d5aac59ae0d91a72d5f758895498008f Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 14:11:51 +0200 Subject: [PATCH 125/166] Fix errors when compiling headers with MinGW --- dtool/src/dtoolbase/mutexImpl.h | 2 +- dtool/src/dtoolbase/mutexWin32Impl.cxx | 4 ++-- dtool/src/dtoolbase/mutexWin32Impl.h | 4 ++-- dtool/src/dtoolbase/selectThreadImpl.h | 2 +- panda/src/pipeline/conditionVarFullWin32Impl.cxx | 4 ++-- panda/src/pipeline/conditionVarFullWin32Impl.h | 4 ++-- panda/src/pipeline/conditionVarImpl.h | 2 +- panda/src/pipeline/conditionVarWin32Impl.cxx | 4 ++-- panda/src/pipeline/conditionVarWin32Impl.h | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dtool/src/dtoolbase/mutexImpl.h b/dtool/src/dtoolbase/mutexImpl.h index f309634ffa..2e741dfe81 100644 --- a/dtool/src/dtoolbase/mutexImpl.h +++ b/dtool/src/dtoolbase/mutexImpl.h @@ -49,7 +49,7 @@ typedef ReMutexPosixImpl ReMutexImpl; // Also define what a true OS-provided lock will be, even if we don't have // threading enabled in the build. Sometimes we need to interface with an // external program or something that wants real locks. -#if defined(WIN32_VC) +#if defined(WIN32_VC) || (defined(_WIN32) && !defined(HAVE_POSIX_THREADS)) #include "mutexWin32Impl.h" typedef MutexWin32Impl TrueMutexImpl; diff --git a/dtool/src/dtoolbase/mutexWin32Impl.cxx b/dtool/src/dtoolbase/mutexWin32Impl.cxx index 594f6c0962..ba25ec1552 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.cxx +++ b/dtool/src/dtoolbase/mutexWin32Impl.cxx @@ -13,7 +13,7 @@ #include "selectThreadImpl.h" -#ifdef WIN32_VC +#ifdef _WIN32 #include "mutexWin32Impl.h" @@ -25,4 +25,4 @@ MutexWin32Impl() { InitializeCriticalSectionAndSpinCount(&_lock, 4000); } -#endif // WIN32_VC +#endif // _WIN32 diff --git a/dtool/src/dtoolbase/mutexWin32Impl.h b/dtool/src/dtoolbase/mutexWin32Impl.h index 550d4944ab..c508b289e9 100644 --- a/dtool/src/dtoolbase/mutexWin32Impl.h +++ b/dtool/src/dtoolbase/mutexWin32Impl.h @@ -17,7 +17,7 @@ #include "dtoolbase.h" #include "selectThreadImpl.h" -#ifdef WIN32_VC +#ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif @@ -47,6 +47,6 @@ private: #include "mutexWin32Impl.I" -#endif // WIN32_VC +#endif // _WIN32 #endif diff --git a/dtool/src/dtoolbase/selectThreadImpl.h b/dtool/src/dtoolbase/selectThreadImpl.h index 743ea98af7..af5a5db44a 100644 --- a/dtool/src/dtoolbase/selectThreadImpl.h +++ b/dtool/src/dtoolbase/selectThreadImpl.h @@ -47,7 +47,7 @@ #undef TVOLATILE #define TVOLATILE -#elif defined(WIN32_VC) +#elif defined(WIN32_VC) || (defined(_WIN32) && !defined(HAVE_POSIX_THREADS)) // In Windows, use the native threading library. #define THREAD_WIN32_IMPL 1 diff --git a/panda/src/pipeline/conditionVarFullWin32Impl.cxx b/panda/src/pipeline/conditionVarFullWin32Impl.cxx index 6d664735d6..a6442e2b06 100644 --- a/panda/src/pipeline/conditionVarFullWin32Impl.cxx +++ b/panda/src/pipeline/conditionVarFullWin32Impl.cxx @@ -13,8 +13,8 @@ #include "selectThreadImpl.h" -#if defined(WIN32_VC) || defined(WIN64_VC) +#ifdef _WIN32 #include "conditionVarFullWin32Impl.h" -#endif // WIN32_VC +#endif // _WIN32 diff --git a/panda/src/pipeline/conditionVarFullWin32Impl.h b/panda/src/pipeline/conditionVarFullWin32Impl.h index a42d4d86a8..4ec0dac63c 100644 --- a/panda/src/pipeline/conditionVarFullWin32Impl.h +++ b/panda/src/pipeline/conditionVarFullWin32Impl.h @@ -17,7 +17,7 @@ #include "pandabase.h" #include "selectThreadImpl.h" -#if defined(WIN32_VC) +#ifdef _WIN32 #include "mutexWin32Impl.h" #include "pnotify.h" @@ -58,6 +58,6 @@ private: #include "conditionVarFullWin32Impl.I" -#endif // WIN32_VC +#endif // _WIN32 #endif diff --git a/panda/src/pipeline/conditionVarImpl.h b/panda/src/pipeline/conditionVarImpl.h index 7837622ba6..f0dcb12f3e 100644 --- a/panda/src/pipeline/conditionVarImpl.h +++ b/panda/src/pipeline/conditionVarImpl.h @@ -50,7 +50,7 @@ typedef ConditionVarPosixImpl ConditionVarFullImpl; #endif -#if defined(WIN32_VC) +#if defined(WIN32_VC) || (defined(_WIN32) && !defined(HAVE_POSIX_THREADS)) #include "conditionVarWin32Impl.h" typedef ConditionVarWin32Impl TrueConditionVarImpl; diff --git a/panda/src/pipeline/conditionVarWin32Impl.cxx b/panda/src/pipeline/conditionVarWin32Impl.cxx index 9d3a95e0b1..5ba4165a0c 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.cxx +++ b/panda/src/pipeline/conditionVarWin32Impl.cxx @@ -13,8 +13,8 @@ #include "selectThreadImpl.h" -#if defined(WIN32_VC) || defined(WIN64_VC) +#ifdef _WIN32 #include "conditionVarWin32Impl.h" -#endif // WIN32_VC +#endif // _WIN32 diff --git a/panda/src/pipeline/conditionVarWin32Impl.h b/panda/src/pipeline/conditionVarWin32Impl.h index 7ed6513d6e..99bdc074dc 100644 --- a/panda/src/pipeline/conditionVarWin32Impl.h +++ b/panda/src/pipeline/conditionVarWin32Impl.h @@ -17,7 +17,7 @@ #include "pandabase.h" #include "selectThreadImpl.h" -#if defined(WIN32_VC) +#ifdef _WIN32 #include "mutexWin32Impl.h" #include "pnotify.h" @@ -50,6 +50,6 @@ private: #include "conditionVarWin32Impl.I" -#endif // WIN32_VC +#endif // _WIN32 #endif From e3cf5be500529286b58c53cbd1678bbde3e8a79e Mon Sep 17 00:00:00 2001 From: Sergei Korotkov Date: Tue, 10 May 2022 12:07:44 +0300 Subject: [PATCH 126/166] filter: Remove the silly dependency on the ShowBase instance Closes #1302 --- direct/src/filter/FilterManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py index ad0f72cca8..c353602b55 100644 --- a/direct/src/filter/FilterManager.py +++ b/direct/src/filter/FilterManager.py @@ -322,7 +322,7 @@ class FilterManager(DirectObject): props.setAuxRgba(1) if (auxtex1 != None): props.setAuxRgba(2) - buffer=base.graphicsEngine.makeOutput( + buffer=self.engine.makeOutput( self.win.getPipe(), name, -1, props, winprops, GraphicsPipe.BFRefuseWindow | GraphicsPipe.BFResizeable, self.win.getGsg(), self.win) From 6fb2acc9cd5e2b9624e1a6f43c2e708d81c6d35a Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 14:19:17 +0200 Subject: [PATCH 127/166] makepanda: Always build tinydisplay, even without X11, for offline rendering Closes #1288 Co-authored-by: Brian Gontowski --- makepanda/makepanda.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index e66768aa5b..afff30627a 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -5568,8 +5568,10 @@ if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and no # DIRECTORY: panda/src/tinydisplay/ # -if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) and PkgSkip("TINYDISPLAY")==0): - OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY', 'X11'] +if not RUNTIME and not PkgSkip("TINYDISPLAY"): + OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY'] + if not PkgSkip("X11"): + OPTS += ['X11'] TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx') TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx') TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx') @@ -5584,7 +5586,7 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) elif GetTarget() == 'windows': TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll') TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM']) - else: + elif not PkgSkip("X11"): TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj') TargetAdd('libp3tinydisplay.dll', opts=['X11']) TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj') From 6ab6acfbaf0173c96f0fd3c3e254cdf8755d4d31 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 15:24:58 +0200 Subject: [PATCH 128/166] interrogate: Do not write wrappers taking rvalue reference to interrogatedb These are not actually exported by the Python binding generator anyway --- dtool/src/interrogate/interrogateBuilder.cxx | 7 ++++ dtool/src/interrogate/typeManager.cxx | 40 ++++++++++++++++++++ dtool/src/interrogate/typeManager.h | 1 + 3 files changed, 48 insertions(+) diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 4735157d1d..f06d6e10d0 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1074,6 +1074,10 @@ scan_function(CPPInstance *function) { return; } + if (TypeManager::involves_rvalue_reference(ftype)) { + return; + } + get_function(function, "", nullptr, scope, InterrogateFunction::F_global); @@ -2986,6 +2990,9 @@ define_method(CPPInstance *function, InterrogateType &itype, // If it isn't, we should publish this method anyway. } + if (TypeManager::involves_rvalue_reference(ftype)) { + return; + } FunctionIndex index = get_function(function, "", struct_type, scope, 0); if (index != 0) { diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx index dba10f4520..828a33ef53 100644 --- a/dtool/src/interrogate/typeManager.cxx +++ b/dtool/src/interrogate/typeManager.cxx @@ -2015,6 +2015,46 @@ involves_protected(CPPType *type) { } } +/** + * Returns true if the type involves an rvalue reference. + */ +bool TypeManager:: +involves_rvalue_reference(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return involves_rvalue_reference(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return type->as_reference_type()->_value_category == CPPReferenceType::VC_rvalue; + + case CPPDeclaration::ST_pointer: + return involves_rvalue_reference(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_function: + { + CPPFunctionType *ftype = type->as_function_type(); + if (involves_rvalue_reference(ftype->_return_type)) { + return true; + } + const CPPParameterList::Parameters ¶ms = + ftype->_parameters->_parameters; + CPPParameterList::Parameters::const_iterator pi; + for (pi = params.begin(); pi != params.end(); ++pi) { + if (involves_rvalue_reference((*pi)->_type)) { + return true; + } + } + return false; + } + + case CPPDeclaration::ST_typedef: + return involves_rvalue_reference(type->as_typedef_type()->_type); + + default: + return false; + } +} + /** * Returns the type this pointer type points to. */ diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h index f1aa2908b1..e9161dbd31 100644 --- a/dtool/src/interrogate/typeManager.h +++ b/dtool/src/interrogate/typeManager.h @@ -118,6 +118,7 @@ public: static bool is_handle(CPPType *type); static bool involves_unpublished(CPPType *type); static bool involves_protected(CPPType *type); + static bool involves_rvalue_reference(CPPType *type); static bool is_ostream(CPPType *type); static bool is_pointer_to_ostream(CPPType *type); From 3d55945535798958e907341d0148c9ee8c037238 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 15:40:02 +0200 Subject: [PATCH 129/166] interrogatedb: Add new functions to interrogate_interface.h: - `interrogate_function_is_constructor()` - `interrogate_function_is_destructor()` - `interrogate_wrapper_parameter_is_optional()` - `interrogate_wrapper_parameter_is_scoped_enum()` - `interrogate_wrapper_parameter_is_final()` Only `interrogate_wrapper_parameter_is_optional()` requires a rebuild of the database with the new changes. --- dtool/metalibs/dtoolconfig/pydtool.cxx | 357 +++++++++++------- dtool/src/interrogate/functionRemap.cxx | 3 + dtool/src/interrogate/interrogateBuilder.cxx | 18 +- .../src/interrogatedb/interrogateDatabase.cxx | 11 + dtool/src/interrogatedb/interrogateFunction.I | 16 + dtool/src/interrogatedb/interrogateFunction.h | 5 + .../interrogateFunctionWrapper.I | 11 + .../interrogateFunctionWrapper.h | 2 + .../interrogatedb/interrogate_interface.cxx | 30 ++ .../src/interrogatedb/interrogate_interface.h | 5 + 10 files changed, 317 insertions(+), 141 deletions(-) diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index 39719b385d..5eca11ea8c 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -60,6 +60,8 @@ static PyObject *_inP07ytISgV(PyObject *self, PyObject *args); static PyObject *_inP07ytH3bx(PyObject *self, PyObject *args); static PyObject *_inP07ytzeUk(PyObject *self, PyObject *args); static PyObject *_inP07ytUeI5(PyObject *self, PyObject *args); +static PyObject *_inP07ytJAAI(PyObject *self, PyObject *args); +static PyObject *_inP07yt0UXw(PyObject *self, PyObject *args); static PyObject *_inP07ytuSvx(PyObject *self, PyObject *args); static PyObject *_inP07ytwpYd(PyObject *self, PyObject *args); static PyObject *_inP07ytOfNh(PyObject *self, PyObject *args); @@ -82,6 +84,7 @@ static PyObject *_inP07ytGMpW(PyObject *self, PyObject *args); static PyObject *_inP07ytNuBV(PyObject *self, PyObject *args); static PyObject *_inP07yt9UwA(PyObject *self, PyObject *args); static PyObject *_inP07yt3FDt(PyObject *self, PyObject *args); +static PyObject *_inP07ytDgOY(PyObject *self, PyObject *args); static PyObject *_inP07ytf513(PyObject *self, PyObject *args); static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args); static PyObject *_inP07yt7shV(PyObject *self, PyObject *args); @@ -124,6 +127,7 @@ static PyObject *_inP07ytMnKa(PyObject *self, PyObject *args); static PyObject *_inP07ytRtji(PyObject *self, PyObject *args); static PyObject *_inP07ytCnbQ(PyObject *self, PyObject *args); static PyObject *_inP07ytdUVN(PyObject *self, PyObject *args); +static PyObject *_inP07ytZtNk(PyObject *self, PyObject *args); static PyObject *_inP07ytihbt(PyObject *self, PyObject *args); static PyObject *_inP07ytbyPY(PyObject *self, PyObject *args); static PyObject *_inP07ytAaT6(PyObject *self, PyObject *args); @@ -149,6 +153,7 @@ static PyObject *_inP07ytHDtN(PyObject *self, PyObject *args); static PyObject *_inP07ytHFjA(PyObject *self, PyObject *args); static PyObject *_inP07yt_NPR(PyObject *self, PyObject *args); static PyObject *_inP07ytcTOH(PyObject *self, PyObject *args); +static PyObject *_inP07ytC5Uk(PyObject *self, PyObject *args); static PyObject *_inP07ythdU7(PyObject *self, PyObject *args); static PyObject *_inP07ytQPxU(PyObject *self, PyObject *args); static PyObject *_inP07ytO7Pz(PyObject *self, PyObject *args); @@ -168,7 +173,7 @@ static PyObject * _inP07yttbRf(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ::interrogate_add_search_directory((char const *)param0); + (::interrogate_add_search_directory)((char const *)param0); return Py_BuildValue(""); } return nullptr; @@ -182,7 +187,7 @@ static PyObject * _inP07ytda_g(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ::interrogate_add_search_path((char const *)param0); + (::interrogate_add_search_path)((char const *)param0); return Py_BuildValue(""); } return nullptr; @@ -195,7 +200,7 @@ _inP07ytda_g(PyObject *, PyObject *args) { static PyObject * _inP07yt4RgX(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - bool return_value = ::interrogate_error_flag(); + bool return_value = (::interrogate_error_flag)(); return PyBool_FromLong(return_value); } return nullptr; @@ -208,7 +213,7 @@ _inP07yt4RgX(PyObject *, PyObject *args) { static PyObject * _inP07yt3Gip(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_manifests(); + int return_value = (::interrogate_number_of_manifests)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -226,7 +231,7 @@ static PyObject * _inP07ytRKDz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - ManifestIndex return_value = ::interrogate_get_manifest((int)param0); + ManifestIndex return_value = (::interrogate_get_manifest)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -244,7 +249,7 @@ static PyObject * _inP07ytgZ9N(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ManifestIndex return_value = ::interrogate_get_manifest_by_name((char const *)param0); + ManifestIndex return_value = (::interrogate_get_manifest_by_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -262,7 +267,7 @@ static PyObject * _inP07ytFnRZ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_manifest_name((ManifestIndex)param0); + char const *return_value = (::interrogate_manifest_name)((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -280,7 +285,7 @@ static PyObject * _inP07ytg0Qv(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_manifest_definition((ManifestIndex)param0); + char const *return_value = (::interrogate_manifest_definition)((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -298,7 +303,7 @@ static PyObject * _inP07yttrqw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_manifest_has_type((ManifestIndex)param0); + bool return_value = (::interrogate_manifest_has_type)((ManifestIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -312,7 +317,7 @@ static PyObject * _inP07ytdmpW(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_manifest_get_type((ManifestIndex)param0); + TypeIndex return_value = (::interrogate_manifest_get_type)((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -330,7 +335,7 @@ static PyObject * _inP07ytUYgQ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_manifest_has_getter((ManifestIndex)param0); + bool return_value = (::interrogate_manifest_has_getter)((ManifestIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -344,7 +349,7 @@ static PyObject * _inP07yt0k7F(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_manifest_getter((ManifestIndex)param0); + FunctionIndex return_value = (::interrogate_manifest_getter)((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -362,7 +367,7 @@ static PyObject * _inP07ytfIsr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_manifest_has_int_value((ManifestIndex)param0); + bool return_value = (::interrogate_manifest_has_int_value)((ManifestIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -376,7 +381,7 @@ static PyObject * _inP07ytvysR(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_manifest_get_int_value((ManifestIndex)param0); + int return_value = (::interrogate_manifest_get_int_value)((ManifestIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -394,7 +399,7 @@ static PyObject * _inP07ytYQ_2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_element_name((ElementIndex)param0); + char const *return_value = (::interrogate_element_name)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -412,7 +417,7 @@ static PyObject * _inP07yt3kdv(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_element_scoped_name((ElementIndex)param0); + char const *return_value = (::interrogate_element_scoped_name)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -430,7 +435,7 @@ static PyObject * _inP07ytew01(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_element_has_comment((ElementIndex)param0); + bool return_value = (::interrogate_element_has_comment)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -444,7 +449,7 @@ static PyObject * _inP07ytQna7(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_element_comment((ElementIndex)param0); + char const *return_value = (::interrogate_element_comment)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -462,7 +467,7 @@ static PyObject * _inP07ytkg95(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ElementIndex return_value = ::interrogate_get_element_by_name((char const *)param0); + ElementIndex return_value = (::interrogate_get_element_by_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -480,7 +485,7 @@ static PyObject * _inP07ytluRc(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ElementIndex return_value = ::interrogate_get_element_by_scoped_name((char const *)param0); + ElementIndex return_value = (::interrogate_get_element_by_scoped_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -498,7 +503,7 @@ static PyObject * _inP07yttHdM(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_element_type((ElementIndex)param0); + TypeIndex return_value = (::interrogate_element_type)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -516,7 +521,7 @@ static PyObject * _inP07ytDId0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_element_has_getter((ElementIndex)param0); + bool return_value = (::interrogate_element_has_getter)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -530,7 +535,7 @@ static PyObject * _inP07ytHuAm(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_element_getter((ElementIndex)param0); + FunctionIndex return_value = (::interrogate_element_getter)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -548,7 +553,7 @@ static PyObject * _inP07yt_xr0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_element_has_setter((ElementIndex)param0); + bool return_value = (::interrogate_element_has_setter)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -562,7 +567,7 @@ static PyObject * _inP07ytH5qp(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_element_setter((ElementIndex)param0); + FunctionIndex return_value = (::interrogate_element_setter)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -580,7 +585,7 @@ static PyObject * _inP07ytq45U(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_element_is_sequence((ElementIndex)param0); + bool return_value = (::interrogate_element_is_sequence)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -594,7 +599,7 @@ static PyObject * _inP07yt6IPa(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_element_is_mapping((ElementIndex)param0); + bool return_value = (::interrogate_element_is_mapping)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -607,7 +612,7 @@ _inP07yt6IPa(PyObject *, PyObject *args) { static PyObject * _inP07ytU2_B(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_globals(); + int return_value = (::interrogate_number_of_globals)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -625,7 +630,7 @@ static PyObject * _inP07ytHFO2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - ElementIndex return_value = ::interrogate_get_global((int)param0); + ElementIndex return_value = (::interrogate_get_global)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -642,7 +647,7 @@ _inP07ytHFO2(PyObject *, PyObject *args) { static PyObject * _inP07ytcfjm(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_global_functions(); + int return_value = (::interrogate_number_of_global_functions)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -660,7 +665,7 @@ static PyObject * _inP07yt3Sjw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_get_global_function((int)param0); + FunctionIndex return_value = (::interrogate_get_global_function)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -677,7 +682,7 @@ _inP07yt3Sjw(PyObject *, PyObject *args) { static PyObject * _inP07ytgJcX(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_functions(); + int return_value = (::interrogate_number_of_functions)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -695,7 +700,7 @@ static PyObject * _inP07ytYlw6(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_get_function((int)param0); + FunctionIndex return_value = (::interrogate_get_function)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -713,7 +718,7 @@ static PyObject * _inP07ytsmnz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_name((FunctionIndex)param0); + char const *return_value = (::interrogate_function_name)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -731,7 +736,7 @@ static PyObject * _inP07ytxQ10(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_scoped_name((FunctionIndex)param0); + char const *return_value = (::interrogate_function_scoped_name)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -749,7 +754,7 @@ static PyObject * _inP07yt6gPB(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_function_has_comment((FunctionIndex)param0); + bool return_value = (::interrogate_function_has_comment)((FunctionIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -763,7 +768,7 @@ static PyObject * _inP07ytISgV(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_comment((FunctionIndex)param0); + char const *return_value = (::interrogate_function_comment)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -781,7 +786,7 @@ static PyObject * _inP07ytH3bx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_prototype((FunctionIndex)param0); + char const *return_value = (::interrogate_function_prototype)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -799,7 +804,7 @@ static PyObject * _inP07ytzeUk(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_function_is_method((FunctionIndex)param0); + bool return_value = (::interrogate_function_is_method)((FunctionIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -813,7 +818,7 @@ static PyObject * _inP07ytUeI5(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_function_class((FunctionIndex)param0); + TypeIndex return_value = (::interrogate_function_class)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -823,6 +828,34 @@ _inP07ytUeI5(PyObject *, PyObject *args) { return nullptr; } +/* + * Python simple wrapper for + * bool interrogate_function_is_constructor(FunctionIndex function) + */ +static PyObject * +_inP07ytJAAI(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_function_is_constructor)((FunctionIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_function_is_destructor(FunctionIndex function) + */ +static PyObject * +_inP07yt0UXw(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_function_is_destructor)((FunctionIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + /* * Python simple wrapper for * bool interrogate_function_has_module_name(FunctionIndex function) @@ -831,7 +864,7 @@ static PyObject * _inP07ytuSvx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_function_has_module_name((FunctionIndex)param0); + bool return_value = (::interrogate_function_has_module_name)((FunctionIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -845,7 +878,7 @@ static PyObject * _inP07ytwpYd(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_module_name((FunctionIndex)param0); + char const *return_value = (::interrogate_function_module_name)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -863,7 +896,7 @@ static PyObject * _inP07ytOfNh(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_function_has_library_name((FunctionIndex)param0); + bool return_value = (::interrogate_function_has_library_name)((FunctionIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -877,7 +910,7 @@ static PyObject * _inP07ytf5_U(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_function_library_name((FunctionIndex)param0); + char const *return_value = (::interrogate_function_library_name)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -895,7 +928,7 @@ static PyObject * _inP07ytL3ZB(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_function_is_virtual((FunctionIndex)param0); + bool return_value = (::interrogate_function_is_virtual)((FunctionIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -909,7 +942,7 @@ static PyObject * _inP07ytXw0I(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_function_number_of_c_wrappers((FunctionIndex)param0); + int return_value = (::interrogate_function_number_of_c_wrappers)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -928,7 +961,7 @@ _inP07yt3zru(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionWrapperIndex return_value = ::interrogate_function_c_wrapper((FunctionIndex)param0, (int)param1); + FunctionWrapperIndex return_value = (::interrogate_function_c_wrapper)((FunctionIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -946,7 +979,7 @@ static PyObject * _inP07ytRrg2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_function_number_of_python_wrappers((FunctionIndex)param0); + int return_value = (::interrogate_function_number_of_python_wrappers)((FunctionIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -965,7 +998,7 @@ _inP07ytEJCx(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionWrapperIndex return_value = ::interrogate_function_python_wrapper((FunctionIndex)param0, (int)param1); + FunctionWrapperIndex return_value = (::interrogate_function_python_wrapper)((FunctionIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -983,7 +1016,7 @@ static PyObject * _inP07ytWAZr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_wrapper_name((FunctionWrapperIndex)param0); + char const *return_value = (::interrogate_wrapper_name)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1001,7 +1034,7 @@ static PyObject * _inP07ytrD_M(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_wrapper_is_callable_by_name((FunctionWrapperIndex)param0); + bool return_value = (::interrogate_wrapper_is_callable_by_name)((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1015,7 +1048,7 @@ static PyObject * _inP07ytjolz(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_wrapper_has_comment((FunctionWrapperIndex)param0); + bool return_value = (::interrogate_wrapper_has_comment)((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1029,7 +1062,7 @@ static PyObject * _inP07ytt_JD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_wrapper_comment((FunctionWrapperIndex)param0); + char const *return_value = (::interrogate_wrapper_comment)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1047,7 +1080,7 @@ static PyObject * _inP07ytwEts(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_wrapper_has_return_value((FunctionWrapperIndex)param0); + bool return_value = (::interrogate_wrapper_has_return_value)((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1061,7 +1094,7 @@ static PyObject * _inP07ytrJWs(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_wrapper_return_type((FunctionWrapperIndex)param0); + TypeIndex return_value = (::interrogate_wrapper_return_type)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1079,7 +1112,7 @@ static PyObject * _inP07ytpmFD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_wrapper_caller_manages_return_value((FunctionWrapperIndex)param0); + bool return_value = (::interrogate_wrapper_caller_manages_return_value)((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1093,7 +1126,7 @@ static PyObject * _inP07ytyYUX(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_wrapper_return_value_destructor((FunctionWrapperIndex)param0); + FunctionIndex return_value = (::interrogate_wrapper_return_value_destructor)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1111,7 +1144,7 @@ static PyObject * _inP07yt54dn(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_wrapper_number_of_parameters((FunctionWrapperIndex)param0); + int return_value = (::interrogate_wrapper_number_of_parameters)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1130,7 +1163,7 @@ _inP07ytGMpW(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = ::interrogate_wrapper_parameter_type((FunctionWrapperIndex)param0, (int)param1); + TypeIndex return_value = (::interrogate_wrapper_parameter_type)((FunctionWrapperIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1149,7 +1182,7 @@ _inP07ytNuBV(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = ::interrogate_wrapper_parameter_has_name((FunctionWrapperIndex)param0, (int)param1); + bool return_value = (::interrogate_wrapper_parameter_has_name)((FunctionWrapperIndex)param0, (int)param1); return PyBool_FromLong(return_value); } return nullptr; @@ -1164,7 +1197,7 @@ _inP07yt9UwA(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = ::interrogate_wrapper_parameter_name((FunctionWrapperIndex)param0, (int)param1); + char const *return_value = (::interrogate_wrapper_parameter_name)((FunctionWrapperIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1183,7 +1216,22 @@ _inP07yt3FDt(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = ::interrogate_wrapper_parameter_is_this((FunctionWrapperIndex)param0, (int)param1); + bool return_value = (::interrogate_wrapper_parameter_is_this)((FunctionWrapperIndex)param0, (int)param1); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n) + */ +static PyObject * +_inP07ytDgOY(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = (::interrogate_wrapper_parameter_is_optional)((FunctionWrapperIndex)param0, (int)param1); return PyBool_FromLong(return_value); } return nullptr; @@ -1197,7 +1245,7 @@ static PyObject * _inP07ytf513(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_wrapper_has_pointer((FunctionWrapperIndex)param0); + bool return_value = (::interrogate_wrapper_has_pointer)((FunctionWrapperIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1211,7 +1259,7 @@ static PyObject * _inP07ytsqGH(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - ::interrogate_wrapper_pointer((FunctionWrapperIndex)param0); + (::interrogate_wrapper_pointer)((FunctionWrapperIndex)param0); return Py_BuildValue(""); } return nullptr; @@ -1225,7 +1273,7 @@ static PyObject * _inP07yt7shV(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_wrapper_unique_name((FunctionWrapperIndex)param0); + char const *return_value = (::interrogate_wrapper_unique_name)((FunctionWrapperIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1243,7 +1291,7 @@ static PyObject * _inP07ytA1eF(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - FunctionWrapperIndex return_value = ::interrogate_get_wrapper_by_unique_name((char const *)param0); + FunctionWrapperIndex return_value = (::interrogate_get_wrapper_by_unique_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1261,7 +1309,7 @@ static PyObject * _inP07yt776V(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_make_seq_seq_name((MakeSeqIndex)param0); + char const *return_value = (::interrogate_make_seq_seq_name)((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1279,7 +1327,7 @@ static PyObject * _inP07ytryup(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_make_seq_scoped_name((MakeSeqIndex)param0); + char const *return_value = (::interrogate_make_seq_scoped_name)((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1297,7 +1345,7 @@ static PyObject * _inP07ytiytI(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_make_seq_has_comment((ElementIndex)param0); + bool return_value = (::interrogate_make_seq_has_comment)((ElementIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1311,7 +1359,7 @@ static PyObject * _inP07ytZc07(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_make_seq_comment((ElementIndex)param0); + char const *return_value = (::interrogate_make_seq_comment)((ElementIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1329,7 +1377,7 @@ static PyObject * _inP07ytfaH0(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_make_seq_num_name((MakeSeqIndex)param0); + char const *return_value = (::interrogate_make_seq_num_name)((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1347,7 +1395,7 @@ static PyObject * _inP07ytGB9D(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_make_seq_element_name((MakeSeqIndex)param0); + char const *return_value = (::interrogate_make_seq_element_name)((MakeSeqIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1364,7 +1412,7 @@ _inP07ytGB9D(PyObject *, PyObject *args) { static PyObject * _inP07ytsxxs(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_global_types(); + int return_value = (::interrogate_number_of_global_types)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1382,7 +1430,7 @@ static PyObject * _inP07ytMT0z(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_get_global_type((int)param0); + TypeIndex return_value = (::interrogate_get_global_type)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1399,7 +1447,7 @@ _inP07ytMT0z(PyObject *, PyObject *args) { static PyObject * _inP07ytiW3v(PyObject *, PyObject *args) { if (PyArg_ParseTuple(args, "")) { - int return_value = ::interrogate_number_of_types(); + int return_value = (::interrogate_number_of_types)(); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1417,7 +1465,7 @@ static PyObject * _inP07yt4Px8(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_get_type((int)param0); + TypeIndex return_value = (::interrogate_get_type)((int)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1435,7 +1483,7 @@ static PyObject * _inP07ytNHcs(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = ::interrogate_get_type_by_name((char const *)param0); + TypeIndex return_value = (::interrogate_get_type_by_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1453,7 +1501,7 @@ static PyObject * _inP07ytqHrb(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = ::interrogate_get_type_by_scoped_name((char const *)param0); + TypeIndex return_value = (::interrogate_get_type_by_scoped_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1471,7 +1519,7 @@ static PyObject * _inP07ytaOqq(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - TypeIndex return_value = ::interrogate_get_type_by_true_name((char const *)param0); + TypeIndex return_value = (::interrogate_get_type_by_true_name)((char const *)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1489,7 +1537,7 @@ static PyObject * _inP07ytpTBb(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_global((TypeIndex)param0); + bool return_value = (::interrogate_type_is_global)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1503,7 +1551,7 @@ static PyObject * _inP07ytqWOw(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_name((TypeIndex)param0); + char const *return_value = (::interrogate_type_name)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1521,7 +1569,7 @@ static PyObject * _inP07ytHu7x(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_scoped_name((TypeIndex)param0); + char const *return_value = (::interrogate_type_scoped_name)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1539,7 +1587,7 @@ static PyObject * _inP07ytwGnA(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_true_name((TypeIndex)param0); + char const *return_value = (::interrogate_type_true_name)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1557,7 +1605,7 @@ static PyObject * _inP07ytXGxx(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_nested((TypeIndex)param0); + bool return_value = (::interrogate_type_is_nested)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1571,7 +1619,7 @@ static PyObject * _inP07ytj04Z(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_type_outer_class((TypeIndex)param0); + TypeIndex return_value = (::interrogate_type_outer_class)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1589,7 +1637,7 @@ static PyObject * _inP07ytEOv4(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_has_comment((TypeIndex)param0); + bool return_value = (::interrogate_type_has_comment)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1603,7 +1651,7 @@ static PyObject * _inP07ytpCqJ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_comment((TypeIndex)param0); + char const *return_value = (::interrogate_type_comment)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1621,7 +1669,7 @@ static PyObject * _inP07yt_Pz3(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_has_module_name((TypeIndex)param0); + bool return_value = (::interrogate_type_has_module_name)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1635,7 +1683,7 @@ static PyObject * _inP07ytt_06(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_module_name((TypeIndex)param0); + char const *return_value = (::interrogate_type_module_name)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1653,7 +1701,7 @@ static PyObject * _inP07ytmuPs(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_has_library_name((TypeIndex)param0); + bool return_value = (::interrogate_type_has_library_name)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1667,7 +1715,7 @@ static PyObject * _inP07ytvM8B(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - char const *return_value = ::interrogate_type_library_name((TypeIndex)param0); + char const *return_value = (::interrogate_type_library_name)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1685,7 +1733,7 @@ static PyObject * _inP07ytap97(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_atomic((TypeIndex)param0); + bool return_value = (::interrogate_type_is_atomic)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1699,7 +1747,7 @@ static PyObject * _inP07yt0o8D(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - AtomicToken return_value = ::interrogate_type_atomic_token((TypeIndex)param0); + AtomicToken return_value = (::interrogate_type_atomic_token)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1717,7 +1765,7 @@ static PyObject * _inP07ytOoQ2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_unsigned((TypeIndex)param0); + bool return_value = (::interrogate_type_is_unsigned)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1731,7 +1779,7 @@ static PyObject * _inP07ytKuFh(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_signed((TypeIndex)param0); + bool return_value = (::interrogate_type_is_signed)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1745,7 +1793,7 @@ static PyObject * _inP07yto5L6(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_long((TypeIndex)param0); + bool return_value = (::interrogate_type_is_long)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1759,7 +1807,7 @@ static PyObject * _inP07ytzgKK(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_longlong((TypeIndex)param0); + bool return_value = (::interrogate_type_is_longlong)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1773,7 +1821,7 @@ static PyObject * _inP07yt0FIF(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_short((TypeIndex)param0); + bool return_value = (::interrogate_type_is_short)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1787,7 +1835,7 @@ static PyObject * _inP07ytZqvD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_wrapped((TypeIndex)param0); + bool return_value = (::interrogate_type_is_wrapped)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1801,7 +1849,7 @@ static PyObject * _inP07ytDyRd(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_pointer((TypeIndex)param0); + bool return_value = (::interrogate_type_is_pointer)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1815,7 +1863,7 @@ static PyObject * _inP07ytMnKa(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_const((TypeIndex)param0); + bool return_value = (::interrogate_type_is_const)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1829,7 +1877,7 @@ static PyObject * _inP07ytRtji(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_typedef((TypeIndex)param0); + bool return_value = (::interrogate_type_is_typedef)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1843,7 +1891,7 @@ static PyObject * _inP07ytCnbQ(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - TypeIndex return_value = ::interrogate_type_wrapped_type((TypeIndex)param0); + TypeIndex return_value = (::interrogate_type_wrapped_type)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1861,7 +1909,21 @@ static PyObject * _inP07ytdUVN(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_enum((TypeIndex)param0); + bool return_value = (::interrogate_type_is_enum)((TypeIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_type_is_scoped_enum(TypeIndex type) + */ +static PyObject * +_inP07ytZtNk(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_type_is_scoped_enum)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1875,7 +1937,7 @@ static PyObject * _inP07ytihbt(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_enum_values((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_enum_values)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1894,7 +1956,7 @@ _inP07ytbyPY(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = ::interrogate_type_enum_value_name((TypeIndex)param0, (int)param1); + char const *return_value = (::interrogate_type_enum_value_name)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1913,7 +1975,7 @@ _inP07ytAaT6(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = ::interrogate_type_enum_value_scoped_name((TypeIndex)param0, (int)param1); + char const *return_value = (::interrogate_type_enum_value_scoped_name)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1932,7 +1994,7 @@ _inP07ytgL9q(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - char const *return_value = ::interrogate_type_enum_value_comment((TypeIndex)param0, (int)param1); + char const *return_value = (::interrogate_type_enum_value_comment)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromString(return_value); #else @@ -1951,7 +2013,7 @@ _inP07ytWB97(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - int return_value = ::interrogate_type_enum_value((TypeIndex)param0, (int)param1); + int return_value = (::interrogate_type_enum_value)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -1969,7 +2031,7 @@ static PyObject * _inP07ytDUAl(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_struct((TypeIndex)param0); + bool return_value = (::interrogate_type_is_struct)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1983,7 +2045,7 @@ static PyObject * _inP07yt1_Kf(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_class((TypeIndex)param0); + bool return_value = (::interrogate_type_is_class)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -1997,7 +2059,7 @@ static PyObject * _inP07yt98lD(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_union((TypeIndex)param0); + bool return_value = (::interrogate_type_is_union)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -2011,7 +2073,7 @@ static PyObject * _inP07yt9SHr(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_fully_defined((TypeIndex)param0); + bool return_value = (::interrogate_type_is_fully_defined)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -2025,7 +2087,7 @@ static PyObject * _inP07ytdiZP(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_is_unpublished((TypeIndex)param0); + bool return_value = (::interrogate_type_is_unpublished)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -2039,7 +2101,7 @@ static PyObject * _inP07ytTdER(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_constructors((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_constructors)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2058,7 +2120,7 @@ _inP07ytYO56(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = ::interrogate_type_get_constructor((TypeIndex)param0, (int)param1); + FunctionIndex return_value = (::interrogate_type_get_constructor)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2076,7 +2138,7 @@ static PyObject * _inP07ytxtCG(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_has_destructor((TypeIndex)param0); + bool return_value = (::interrogate_type_has_destructor)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -2090,7 +2152,7 @@ static PyObject * _inP07yt_EB2(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - bool return_value = ::interrogate_type_destructor_is_inherited((TypeIndex)param0); + bool return_value = (::interrogate_type_destructor_is_inherited)((TypeIndex)param0); return PyBool_FromLong(return_value); } return nullptr; @@ -2104,7 +2166,7 @@ static PyObject * _inP07ytEG1l(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - FunctionIndex return_value = ::interrogate_type_get_destructor((TypeIndex)param0); + FunctionIndex return_value = (::interrogate_type_get_destructor)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2122,7 +2184,7 @@ static PyObject * _inP07yt7tUq(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_elements((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_elements)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2141,7 +2203,7 @@ _inP07ytyStU(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - ElementIndex return_value = ::interrogate_type_get_element((TypeIndex)param0, (int)param1); + ElementIndex return_value = (::interrogate_type_get_element)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2159,7 +2221,7 @@ static PyObject * _inP07ytdM85(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_methods((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_methods)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2178,7 +2240,7 @@ _inP07ytk_GN(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = ::interrogate_type_get_method((TypeIndex)param0, (int)param1); + FunctionIndex return_value = (::interrogate_type_get_method)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2196,7 +2258,7 @@ static PyObject * _inP07yt8QjG(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_make_seqs((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_make_seqs)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2215,7 +2277,7 @@ _inP07ytyMtj(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - MakeSeqIndex return_value = ::interrogate_type_get_make_seq((TypeIndex)param0, (int)param1); + MakeSeqIndex return_value = (::interrogate_type_get_make_seq)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2233,7 +2295,7 @@ static PyObject * _inP07ytHDtN(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_casts((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_casts)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2252,7 +2314,7 @@ _inP07ytHFjA(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = ::interrogate_type_get_cast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = (::interrogate_type_get_cast)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2270,7 +2332,7 @@ static PyObject * _inP07yt_NPR(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_derivations((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_derivations)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2289,7 +2351,7 @@ _inP07ytcTOH(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = ::interrogate_type_get_derivation((TypeIndex)param0, (int)param1); + TypeIndex return_value = (::interrogate_type_get_derivation)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2299,6 +2361,20 @@ _inP07ytcTOH(PyObject *, PyObject *args) { return nullptr; } +/* + * Python simple wrapper for + * bool interrogate_type_is_final(TypeIndex type) + */ +static PyObject * +_inP07ytC5Uk(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_type_is_final)((TypeIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + /* * Python simple wrapper for * bool interrogate_type_derivation_has_upcast(TypeIndex type, int n) @@ -2308,7 +2384,7 @@ _inP07ythdU7(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = ::interrogate_type_derivation_has_upcast((TypeIndex)param0, (int)param1); + bool return_value = (::interrogate_type_derivation_has_upcast)((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } return nullptr; @@ -2323,7 +2399,7 @@ _inP07ytQPxU(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = ::interrogate_type_get_upcast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = (::interrogate_type_get_upcast)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2342,7 +2418,7 @@ _inP07ytO7Pz(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = ::interrogate_type_derivation_downcast_is_impossible((TypeIndex)param0, (int)param1); + bool return_value = (::interrogate_type_derivation_downcast_is_impossible)((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } return nullptr; @@ -2357,7 +2433,7 @@ _inP07ytvu_E(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - bool return_value = ::interrogate_type_derivation_has_downcast((TypeIndex)param0, (int)param1); + bool return_value = (::interrogate_type_derivation_has_downcast)((TypeIndex)param0, (int)param1); return PyBool_FromLong(return_value); } return nullptr; @@ -2372,7 +2448,7 @@ _inP07ytxGUt(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - FunctionIndex return_value = ::interrogate_type_get_downcast((TypeIndex)param0, (int)param1); + FunctionIndex return_value = (::interrogate_type_get_downcast)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2390,7 +2466,7 @@ static PyObject * _inP07ytzM1P(PyObject *, PyObject *args) { int param0; if (PyArg_ParseTuple(args, "i", ¶m0)) { - int return_value = ::interrogate_type_number_of_nested_types((TypeIndex)param0); + int return_value = (::interrogate_type_number_of_nested_types)((TypeIndex)param0); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2409,7 +2485,7 @@ _inP07ytoY5L(PyObject *, PyObject *args) { int param0; int param1; if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { - TypeIndex return_value = ::interrogate_type_get_nested_type((TypeIndex)param0, (int)param1); + TypeIndex return_value = (::interrogate_type_get_nested_type)((TypeIndex)param0, (int)param1); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(return_value); #else @@ -2427,7 +2503,7 @@ static PyObject * _inP07yte_7S(PyObject *, PyObject *args) { char *param0; if (PyArg_ParseTuple(args, "s", ¶m0)) { - ::interrogate_request_database((char const *)param0); + (::interrogate_request_database)((char const *)param0); return Py_BuildValue(""); } return nullptr; @@ -2441,7 +2517,7 @@ static PyObject * _inP07ytw_15(PyObject *, PyObject *args) { Py_ssize_t param0; if (PyArg_ParseTuple(args, "n", ¶m0)) { - ::interrogate_request_module((InterrogateModuleDef *)param0); + (::interrogate_request_module)((InterrogateModuleDef *)param0); return Py_BuildValue(""); } return nullptr; @@ -2489,6 +2565,8 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS }, { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS }, { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS }, + { "interrogate_function_is_constructor", &_inP07ytJAAI, METH_VARARGS }, + { "interrogate_function_is_destructor", &_inP07yt0UXw, METH_VARARGS }, { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS }, { "interrogate_function_module_name", &_inP07ytwpYd, METH_VARARGS }, { "interrogate_function_has_library_name", &_inP07ytOfNh, METH_VARARGS }, @@ -2511,6 +2589,7 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_wrapper_parameter_has_name", &_inP07ytNuBV, METH_VARARGS }, { "interrogate_wrapper_parameter_name", &_inP07yt9UwA, METH_VARARGS }, { "interrogate_wrapper_parameter_is_this", &_inP07yt3FDt, METH_VARARGS }, + { "interrogate_wrapper_parameter_is_optional", &_inP07ytDgOY, METH_VARARGS }, { "interrogate_wrapper_has_pointer", &_inP07ytf513, METH_VARARGS }, { "interrogate_wrapper_pointer", &_inP07ytsqGH, METH_VARARGS }, { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS }, @@ -2553,6 +2632,7 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS }, { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS }, { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS }, + { "interrogate_type_is_scoped_enum", &_inP07ytZtNk, METH_VARARGS }, { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS }, { "interrogate_type_enum_value_name", &_inP07ytbyPY, METH_VARARGS }, { "interrogate_type_enum_value_scoped_name", &_inP07ytAaT6, METH_VARARGS }, @@ -2578,6 +2658,7 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_type_get_cast", &_inP07ytHFjA, METH_VARARGS }, { "interrogate_type_number_of_derivations", &_inP07yt_NPR, METH_VARARGS }, { "interrogate_type_get_derivation", &_inP07ytcTOH, METH_VARARGS }, + { "interrogate_type_is_final", &_inP07ytC5Uk, METH_VARARGS }, { "interrogate_type_derivation_has_upcast", &_inP07ythdU7, METH_VARARGS }, { "interrogate_type_get_upcast", &_inP07ytQPxU, METH_VARARGS }, { "interrogate_type_derivation_downcast_is_impossible", &_inP07ytO7Pz, METH_VARARGS }, diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index e0dec969c0..cb8f9906a8 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -327,6 +327,9 @@ make_wrapper_entry(FunctionIndex function_index) { if ((*pi)._has_name) { param._parameter_flags |= InterrogateFunctionWrapper::PF_has_name; } + if ((*pi)._remap->has_default_value()) { + param._parameter_flags |= InterrogateFunctionWrapper::PF_is_optional; + } iwrapper._parameters.push_back(param); } diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index f06d6e10d0..1cc4aad3ff 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1777,6 +1777,16 @@ get_function(CPPInstance *function, string description, ifunction->_flags |= InterrogateFunction::F_operator_typecast; } + if (ftype->_flags & CPPFunctionType::F_constructor) { + // This is a constructor. + ifunction->_flags |= InterrogateFunction::F_constructor; + } + + if (ftype->_flags & CPPFunctionType::F_destructor) { + // This is a destructor. + ifunction->_flags |= InterrogateFunction::F_destructor; + } + if (function->_storage_class & CPPInstance::SC_virtual) { // This is a virtual function. ifunction->_flags |= InterrogateFunction::F_virtual; @@ -2749,7 +2759,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted; function->_vis = V_published; - FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0); + FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), + InterrogateFunction::F_constructor); if (find(itype._constructors.begin(), itype._constructors.end(), index) == itype._constructors.end()) { itype._constructors.push_back(index); @@ -2775,7 +2786,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted; function->_vis = V_published; - FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0); + FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), + InterrogateFunction::F_constructor); if (find(itype._constructors.begin(), itype._constructors.end(), index) == itype._constructors.end()) { itype._constructors.push_back(index); @@ -2816,7 +2828,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, itype._destructor = get_function(function, "", cpptype, cpptype->get_scope(), - 0); + InterrogateFunction::F_destructor); itype._flags |= InterrogateType::F_implicit_destructor; } } diff --git a/dtool/src/interrogatedb/interrogateDatabase.cxx b/dtool/src/interrogatedb/interrogateDatabase.cxx index a8e7b37a6c..afdb7a11f7 100644 --- a/dtool/src/interrogatedb/interrogateDatabase.cxx +++ b/dtool/src/interrogatedb/interrogateDatabase.cxx @@ -968,6 +968,17 @@ read_new(std::istream &in, InterrogateModuleDef *def) { add_type(index, type); num_types--; + + // Older versions of interrogate were not setting these flags. + const InterrogateType &itype = get_type(index); + FunctionIndex dtor = itype.get_destructor(); + if (dtor != 0) { + update_function(dtor)._flags |= InterrogateFunction::F_destructor; + } + for (int i = 0; i < itype.number_of_constructors(); ++i) { + FunctionIndex ctor = itype.get_constructor(i); + update_function(ctor)._flags |= InterrogateFunction::F_constructor; + } } } diff --git a/dtool/src/interrogatedb/interrogateFunction.I b/dtool/src/interrogatedb/interrogateFunction.I index 3834e60782..6c36fec5d3 100644 --- a/dtool/src/interrogatedb/interrogateFunction.I +++ b/dtool/src/interrogatedb/interrogateFunction.I @@ -54,6 +54,22 @@ is_operator_typecast() const { return (_flags & F_operator_typecast) != 0; } +/** + * Returns true if the function is a constructor. + */ +INLINE bool InterrogateFunction:: +is_constructor() const { + return (_flags & F_constructor) != 0; +} + +/** + * Returns true if the function is a destructor. + */ +INLINE bool InterrogateFunction:: +is_destructor() const { + return (_flags & F_destructor) != 0; +} + /** * Return the class that owns the method, if is_method() returns true. */ diff --git a/dtool/src/interrogatedb/interrogateFunction.h b/dtool/src/interrogatedb/interrogateFunction.h index 1e3a251032..b3736069d7 100644 --- a/dtool/src/interrogatedb/interrogateFunction.h +++ b/dtool/src/interrogatedb/interrogateFunction.h @@ -38,6 +38,8 @@ public: INLINE bool is_method() const; INLINE bool is_unary_op() const; INLINE bool is_operator_typecast() const; + INLINE bool is_constructor() const; + INLINE bool is_destructor() const; INLINE TypeIndex get_class() const; INLINE bool has_scoped_name() const; @@ -70,6 +72,8 @@ private: F_setter = 0x0020, F_unary_op = 0x0040, F_operator_typecast = 0x0080, + F_constructor = 0x0100, + F_destructor = 0x0200, }; int _flags; @@ -97,6 +101,7 @@ public: std::string _expression; friend class InterrogateBuilder; + friend class InterrogateDatabase; friend class InterfaceMakerC; friend class InterfaceMakerPythonSimple; friend class InterfaceMakerPythonNative; diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.I b/dtool/src/interrogatedb/interrogateFunctionWrapper.I index d636e32ebf..b0ec398623 100644 --- a/dtool/src/interrogatedb/interrogateFunctionWrapper.I +++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.I @@ -148,6 +148,17 @@ parameter_is_this(int n) const { return false; } +/** + * + */ +INLINE bool InterrogateFunctionWrapper:: +parameter_is_optional(int n) const { + if (n >= 0 && n < (int)_parameters.size()) { + return (_parameters[n]._parameter_flags & PF_is_optional) != 0; + } + return false; +} + /** * */ diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.h b/dtool/src/interrogatedb/interrogateFunctionWrapper.h index 43b01a9a79..29e59ed259 100644 --- a/dtool/src/interrogatedb/interrogateFunctionWrapper.h +++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.h @@ -45,6 +45,7 @@ public: INLINE bool parameter_has_name(int n) const; INLINE const std::string ¶meter_get_name(int n) const; INLINE bool parameter_is_this(int n) const; + INLINE bool parameter_is_optional(int n) const; INLINE const std::string &get_unique_name() const; @@ -66,6 +67,7 @@ private: enum ParameterFlags { PF_has_name = 0x0001, PF_is_this = 0x0002, + PF_is_optional = 0x0004, }; int _flags; diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index 3fc2053855..9f52b2b873 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -266,6 +266,18 @@ interrogate_function_class(FunctionIndex function) { return InterrogateDatabase::get_ptr()->get_function(function).get_class(); } +bool +interrogate_function_is_constructor(FunctionIndex function) { + // cerr << "interrogate_function_is_constructor(" << function << ")\n"; + return InterrogateDatabase::get_ptr()->get_function(function).is_constructor(); +} + +bool +interrogate_function_is_destructor(FunctionIndex function) { + // cerr << "interrogate_function_is_destructor(" << function << ")\n"; + return InterrogateDatabase::get_ptr()->get_function(function).is_destructor(); +} + bool interrogate_function_has_module_name(FunctionIndex function) { // cerr << "interrogate_function_has_module_name(" << function << ")\n"; @@ -411,6 +423,12 @@ interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n) { return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_this(n); } +bool +interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n) { + // cerr << "interrogate_wrapper_is_optional(" << wrapper << ", " << n << ")\n"; + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_optional(n); +} + bool interrogate_wrapper_has_pointer(FunctionWrapperIndex wrapper) { // cerr << "interrogate_wrapper_has_pointer(" << wrapper << ")\n"; @@ -674,6 +692,12 @@ interrogate_type_is_enum(TypeIndex type) { return InterrogateDatabase::get_ptr()->get_type(type).is_enum(); } +bool +interrogate_type_is_scoped_enum(TypeIndex type) { + // cerr << "interrogate_type_is_scoped_enum(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_scoped_enum(); +} + int interrogate_type_number_of_enum_values(TypeIndex type) { // cerr << "interrogate_type_number_of_enum_values(" << type << ")\n"; @@ -828,6 +852,12 @@ interrogate_type_get_derivation(TypeIndex type, int n) { return InterrogateDatabase::get_ptr()->get_type(type).get_derivation(n); } +bool +interrogate_type_is_final(TypeIndex type) { + // cerr << "interrogate_type_is_final(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_final(); +} + bool interrogate_type_derivation_has_upcast(TypeIndex type, int n) { // cerr << "interrogate_type_derivation_has_upcast(" << type << ", " << n << diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index cc88af041d..df17f09e75 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -210,6 +210,8 @@ EXPCL_INTERROGATEDB const char *interrogate_function_prototype(FunctionIndex fun // if the function is a class method. EXPCL_INTERROGATEDB bool interrogate_function_is_method(FunctionIndex function); EXPCL_INTERROGATEDB TypeIndex interrogate_function_class(FunctionIndex function); +EXPCL_INTERROGATEDB bool interrogate_function_is_constructor(FunctionIndex function); +EXPCL_INTERROGATEDB bool interrogate_function_is_destructor(FunctionIndex function); // This returns the module name reported for the function, if available. EXPCL_INTERROGATEDB bool interrogate_function_has_module_name(FunctionIndex function); @@ -299,6 +301,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_wrapper_parameter_type(FunctionWrapper EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_has_name(FunctionWrapperIndex wrapper, int n); EXPCL_INTERROGATEDB const char *interrogate_wrapper_parameter_name(FunctionWrapperIndex wrapper, int n); EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n); +EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n); // This returns a pointer to a function that may be called to invoke the // function, if the -fptrs option to return function pointers was specified to @@ -423,6 +426,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_type_wrapped_type(TypeIndex type); // If interrogate_type_is_enum() returns true, this is an enumerated type, // which means it may take any one of a number of named integer values. EXPCL_INTERROGATEDB bool interrogate_type_is_enum(TypeIndex type); +EXPCL_INTERROGATEDB bool interrogate_type_is_scoped_enum(TypeIndex type); EXPCL_INTERROGATEDB int interrogate_type_number_of_enum_values(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_name(TypeIndex type, int n); EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_scoped_name(TypeIndex type, int n); @@ -499,6 +503,7 @@ EXPCL_INTERROGATEDB FunctionIndex interrogate_type_get_cast(TypeIndex type, int // list of base classes for this particular type. EXPCL_INTERROGATEDB int interrogate_type_number_of_derivations(TypeIndex type); EXPCL_INTERROGATEDB TypeIndex interrogate_type_get_derivation(TypeIndex type, int n); +EXPCL_INTERROGATEDB bool interrogate_type_is_final(TypeIndex type); // For each base class, we might need to define an explicit upcast or downcast // operation to convert the pointer to the derived class to an appropriate From 671b16eb416c40c1982efe19a33e282ed7454a86 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 10 May 2022 16:21:40 +0200 Subject: [PATCH 130/166] express: Fix vestigial reference to WIN32_VC macro --- panda/src/express/zipArchive.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/express/zipArchive.cxx b/panda/src/express/zipArchive.cxx index 94fa0a2533..06bd4f9e28 100644 --- a/panda/src/express/zipArchive.cxx +++ b/panda/src/express/zipArchive.cxx @@ -1060,7 +1060,7 @@ close_read_subfile(std::istream *stream) { // stream pointer does not call the appropriate global delete function; // instead apparently calling the system delete function. So we call the // delete function by hand instead. -#if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) +#if defined(__GNUC__) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) stream->~istream(); (*global_operator_delete)(stream); #else From b5d615b2231d15d22a9943be28d617740cb26a1d Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 17 May 2022 10:10:45 +0200 Subject: [PATCH 131/166] mathutil: Fix broken docstrings --- panda/src/mathutil/frustum_src.I | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/panda/src/mathutil/frustum_src.I b/panda/src/mathutil/frustum_src.I index f884b29f45..a0f1a8f9d9 100644 --- a/panda/src/mathutil/frustum_src.I +++ b/panda/src/mathutil/frustum_src.I @@ -66,12 +66,7 @@ make_ortho(FLOATTYPE fnear, FLOATTYPE ffar, FLOATTYPE l, FLOATTYPE r, } /** - * Behaves like gluPerspective (Aspect = width/height, Yfov in degrees) aspect - * +------------+ | | 1 | | yfov | | - * +------------+ - * - * -------+------ \ | \ | \ | \ | \ | \| W yfov - * + * Behaves like gluPerspective (Aspect = width/height, Yfov in degrees) */ INLINE_MATHUTIL void FLOATNAME(LFrustum):: make_perspective_hfov(FLOATTYPE hfov, FLOATTYPE aspect, FLOATTYPE fnear, @@ -84,7 +79,9 @@ make_perspective_hfov(FLOATTYPE hfov, FLOATTYPE aspect, FLOATTYPE fnear, _b = -_t; } - +/** + * + */ INLINE_MATHUTIL void FLOATNAME(LFrustum):: make_perspective_vfov(FLOATTYPE yfov, FLOATTYPE aspect, FLOATTYPE fnear, FLOATTYPE ffar) { @@ -96,7 +93,9 @@ make_perspective_vfov(FLOATTYPE yfov, FLOATTYPE aspect, FLOATTYPE fnear, _l = -_r; } - +/** + * + */ INLINE_MATHUTIL void FLOATNAME(LFrustum):: make_perspective(FLOATTYPE xfov, FLOATTYPE yfov, FLOATTYPE fnear, FLOATTYPE ffar) { From 29c25a541c8a8d642766e63aba12d263843c6de4 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 17 May 2022 11:52:19 +0200 Subject: [PATCH 132/166] egl: Add egl-device-index config var for selecting EGL device Fixes #1306 --- panda/src/egldisplay/eglGraphicsPipe.cxx | 113 +++++++++++++++++------ 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/panda/src/egldisplay/eglGraphicsPipe.cxx b/panda/src/egldisplay/eglGraphicsPipe.cxx index a75aec5170..6d4a58adc5 100644 --- a/panda/src/egldisplay/eglGraphicsPipe.cxx +++ b/panda/src/egldisplay/eglGraphicsPipe.cxx @@ -21,6 +21,12 @@ #include +static ConfigVariableInt egl_device_index +("egl-device-index", -1, + PRC_DESC("Selects which EGL device index is used to create the EGL display in " + "a headless configuration. The special value -1 selects the default " + "device.")); + TypeHandle eglGraphicsPipe::_type_handle; /** @@ -30,6 +36,8 @@ eglGraphicsPipe:: eglGraphicsPipe() { // Check for client extensions. vector_string extensions; + bool supports_platform_device = false; + bool supports_device_enumeration = false; const char *ext_ptr = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (ext_ptr != nullptr) { extract_words(ext_ptr, extensions); @@ -42,6 +50,13 @@ eglGraphicsPipe() { out << " " << extension << "\n"; } } + + if (std::find(extensions.begin(), extensions.end(), "EGL_EXT_platform_device") != extensions.end()) { + supports_platform_device = true; + } + if (std::find(extensions.begin(), extensions.end(), "EGL_EXT_device_enumeration") != extensions.end()) { + supports_device_enumeration = true; + } } else if (egldisplay_cat.is_debug()) { eglGetError(); @@ -51,23 +66,10 @@ eglGraphicsPipe() { EGLint major, minor; - //NB. if the X11 display failed to open, _display will be 0, which is a valid - // input to eglGetDisplay - it means to open the default display. -#ifdef USE_X11 - _egl_display = eglGetDisplay((NativeDisplayType) _display); -#else - _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); -#endif - if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) { - egldisplay_cat.warning() - << "Couldn't initialize the default EGL display: " - << get_egl_error_string(eglGetError()) << "\n"; - _egl_display = EGL_NO_DISPLAY; - } - - if (!_egl_display && - std::find(extensions.begin(), extensions.end(), "EGL_EXT_platform_device") != extensions.end() && - std::find(extensions.begin(), extensions.end(), "EGL_EXT_device_enumeration") != extensions.end()) { + int index = egl_device_index.get_value(); + if (index >= 0 && supports_platform_device && supports_device_enumeration) { + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); @@ -79,23 +81,74 @@ eglGraphicsPipe() { EGLDeviceEXT *devices = (EGLDeviceEXT *)alloca(sizeof(EGLDeviceEXT) * num_devices); eglQueryDevicesEXT(num_devices, devices, &num_devices); - if (egldisplay_cat.is_debug()) { - egldisplay_cat.debug() - << "Found " << num_devices << " EGL devices.\n"; + if (index >= num_devices) { + egldisplay_cat.error() + << "Requested EGL device index " << index << " does not exist (" + << "there are only " << num_devices << " devices)\n"; + _is_valid = false; + return; } - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = - (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (egldisplay_cat.is_debug()) { + egldisplay_cat.debug() + << "Found " << num_devices << " EGL devices, using device index " + << index << ".\n"; + } - if (eglGetPlatformDisplayEXT != nullptr) { - for (EGLint i = 0; i < num_devices && !_egl_display; ++i) { - _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr); + _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[index], nullptr); - if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) { - egldisplay_cat.warning() - << "Couldn't initialize EGL platform display " << i << ": " - << get_egl_error_string(eglGetError()) << "\n"; - _egl_display = EGL_NO_DISPLAY; + if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) { + egldisplay_cat.error() + << "Couldn't initialize EGL platform display " << index << ": " + << get_egl_error_string(eglGetError()) << "\n"; + _egl_display = EGL_NO_DISPLAY; + } + } + } + else { + //NB. if the X11 display failed to open, _display will be 0, which is a valid + // input to eglGetDisplay - it means to open the default display. + #ifdef USE_X11 + _egl_display = eglGetDisplay((NativeDisplayType) _display); + #else + _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + #endif + if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) { + egldisplay_cat.warning() + << "Couldn't initialize the default EGL display: " + << get_egl_error_string(eglGetError()) << "\n"; + _egl_display = EGL_NO_DISPLAY; + } + + if (!_egl_display && supports_platform_device && supports_device_enumeration) { + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = + (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); + + EGLint num_devices = 0; + if (eglQueryDevicesEXT != nullptr && + eglQueryDevicesEXT(0, nullptr, &num_devices) && + num_devices > 0) { + EGLDeviceEXT *devices = (EGLDeviceEXT *)alloca(sizeof(EGLDeviceEXT) * num_devices); + eglQueryDevicesEXT(num_devices, devices, &num_devices); + + if (egldisplay_cat.is_debug()) { + egldisplay_cat.debug() + << "Found " << num_devices << " EGL devices.\n"; + } + + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if (eglGetPlatformDisplayEXT != nullptr) { + for (EGLint i = 0; i < num_devices && !_egl_display; ++i) { + _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr); + + if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) { + egldisplay_cat.warning() + << "Couldn't initialize EGL platform display " << i << ": " + << get_egl_error_string(eglGetError()) << "\n"; + _egl_display = EGL_NO_DISPLAY; + } } } } From 36e34294cf4b196fddedb219b04857267f42f5c5 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 19 May 2022 10:00:37 +0200 Subject: [PATCH 133/166] display: Update docstring for `WindowProperties::set_mouse_mode()` [skip ci] Fixes #1307 --- panda/src/display/windowProperties.I | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/panda/src/display/windowProperties.I b/panda/src/display/windowProperties.I index 71a5132224..51f398c666 100644 --- a/panda/src/display/windowProperties.I +++ b/panda/src/display/windowProperties.I @@ -679,19 +679,18 @@ clear_z_order() { * mouse can move outside the window and the mouse coordinates are relative to * its position in the window. * - * M_relative (OSX or Unix/X11 only): a mode where only relative movements are - * reported; particularly useful for FPS-style mouse movements where you have - * hidden the mouse pointer and are are more interested in how fast the mouse - * is moving, rather than precisely where the pointer is hovering. - * - * This has no effect on Windows. On Unix/X11, this requires the Xxf86dga - * extension to be available. - * * M_confined: this mode reports absolute mouse positions, but confines the - * mouse pointer to the window boundary. It can portably replace M_relative - * for an FPS, but you need to periodically move the pointer to the center of - * the window and track movement deltas. + * mouse pointer to the window boundary. The reported mouse positions will + * never be outside of the window boundary. * + * M_relative: a mode where only relative movements are reported; particularly + * useful for FPS-style mouse movements where you have hidden the mouse + * pointer and are are more interested in how fast the mouse is moving, rather + * than precisely where the pointer is hovering. The reported positions still + * appear to be absolute, but they can go toward negative or positive infinity + * without being constrained by the window (or screen) dimensions. Since the + * position of the mouse cursor becomes meaningless in this mode, it is + * recommended to combine this with the cursor_hidden flag. */ INLINE void WindowProperties:: set_mouse_mode(MouseMode mode) { From c24a15ed408287a0eafa036d6252aa207049f882 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 15:12:06 +0200 Subject: [PATCH 134/166] task: Fix missing `taskMgr` reference when using managed tasks --- direct/src/showbase/DirectObject.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/direct/src/showbase/DirectObject.py b/direct/src/showbase/DirectObject.py index 0833b1890a..261fc3049c 100644 --- a/direct/src/showbase/DirectObject.py +++ b/direct/src/showbase/DirectObject.py @@ -44,16 +44,18 @@ class DirectObject: #This function must be used if you want a managed task def addTask(self, *args, **kwargs): - if(not hasattr(self,"_taskList")): + from direct.task.TaskManagerGlobal import taskMgr + if not hasattr(self, "_taskList"): self._taskList = {} - kwargs['owner']=self + kwargs['owner'] = self task = taskMgr.add(*args, **kwargs) return task def doMethodLater(self, *args, **kwargs): - if(not hasattr(self,"_taskList")): - self._taskList ={} - kwargs['owner']=self + from direct.task.TaskManagerGlobal import taskMgr + if not hasattr(self, "_taskList"): + self._taskList = {} + kwargs['owner'] = self task = taskMgr.doMethodLater(*args, **kwargs) return task @@ -69,7 +71,7 @@ class DirectObject: taskOrName.remove() def removeAllTasks(self): - if hasattr(self,'_taskList'): + if hasattr(self, '_taskList'): for task in list(self._taskList.values()): task.remove() From 9ab460c9009804b31e24ea7688fe0065a1dff9ae Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 15:13:57 +0200 Subject: [PATCH 135/166] event: Fix memory leak in debug check of `task.set_owner(...)` Fixes #1328 --- panda/src/event/pythonTask.cxx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index 07fca8bd38..f214ee73d6 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -209,10 +209,22 @@ set_owner(PyObject *owner) { #ifndef NDEBUG if (owner != Py_None) { PyObject *add = PyObject_GetAttrString(owner, "_addTask"); + PyErr_Clear(); PyObject *clear = PyObject_GetAttrString(owner, "_clearTask"); + PyErr_Clear(); - if (add == nullptr || !PyCallable_Check(add) || - clear == nullptr || !PyCallable_Check(clear)) { + bool valid_add = false; + if (add != nullptr) { + valid_add = PyCallable_Check(add); + Py_DECREF(add); + } + bool valid_clear = false; + if (clear != nullptr) { + valid_clear = PyCallable_Check(clear); + Py_DECREF(clear); + } + + if (!valid_add || !valid_clear) { Dtool_Raise_TypeError("owner object should have _addTask and _clearTask methods"); return; } From c325eabb9d4a0a7f903843e1f51b1ed6c170323d Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 15:15:02 +0200 Subject: [PATCH 136/166] glgsg: Fix texture format selection when using `T_half_float` component type --- .../src/glstuff/glGraphicsStateGuardian_src.cxx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index c536ed6747..43982312f0 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -10249,7 +10249,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const { case Texture::F_rgba: case Texture::F_rgbm: #ifndef OPENGLES_1 - if (component_type == Texture::T_float) { + if (component_type == Texture::T_float || + component_type == Texture::T_half_float) { return GL_RGBA16F; } else #endif @@ -10315,7 +10316,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #endif // OPENGLES #ifndef OPENGLES case Texture::F_rgba16: - if (component_type == Texture::T_float) { + if (component_type == Texture::T_float || + component_type == Texture::T_half_float) { return GL_RGBA16F; } else if (Texture::is_unsigned(component_type)) { return GL_RGBA16; @@ -10335,6 +10337,7 @@ get_internal_image_format(Texture *tex, bool force_sized) const { case Texture::F_rgb: switch (component_type) { case Texture::T_float: return GL_RGB16F; + case Texture::T_half_float: return GL_RGB16F; #ifndef OPENGLES case Texture::T_unsigned_short: return GL_RGB16; case Texture::T_short: return GL_RGB16_SNORM; @@ -10371,7 +10374,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const { case Texture::F_rgb12: return GL_RGB12; case Texture::F_rgb16: - if (component_type == Texture::T_float) { + if (component_type == Texture::T_float || + component_type == Texture::T_half_float) { return GL_RGB16F; } else if (Texture::is_unsigned(component_type)) { return GL_RGB16; @@ -10394,7 +10398,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const { return GL_RG16F_EXT; #elif !defined(OPENGLES_1) case Texture::F_r16: - if (component_type == Texture::T_float) { + if (component_type == Texture::T_float || + component_type == Texture::T_half_float) { return GL_R16F; } else if (Texture::is_unsigned(component_type)) { return GL_R16; @@ -10402,7 +10407,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const { return GL_R16_SNORM; } case Texture::F_rg16: - if (component_type == Texture::T_float) { + if (component_type == Texture::T_float || + component_type == Texture::T_half_float) { return GL_RG16F; } else if (Texture::is_unsigned(component_type)) { return GL_RG16; From 91dd802de682f17089f995dfb146516b23d0b36e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 16:07:48 +0200 Subject: [PATCH 137/166] dist: Include `_sysconfigdata` module properly Fixes #1326 --- direct/src/dist/FreezeTool.py | 12 ++++++++++++ makepanda/makewheel.py | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 1e5554afa9..6c9d382a5d 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -1215,6 +1215,18 @@ class Freezer: else: self.__loadModule(self.ModuleDef(modname, implicit = True)) + # Special case for sysconfig, which depends on a platform-specific + # sysconfigdata module on POSIX systems. + if 'sysconfig' in self.mf.modules: + if sys.version_info >= (3, 6): + if 'linux' in self.platform: + arch = self.platform.split('_', 1)[1] + self.__loadModule(self.ModuleDef('_sysconfigdata__linux_' + arch + '-linux-gnu', implicit=True)) + elif 'mac' in self.platform: + self.__loadModule(self.ModuleDef('_sysconfigdata__darwin_darwin', implicit=True)) + elif 'linux' in self.platform or 'mac' in self.platform: + self.__loadModule(self.ModuleDef('_sysconfigdata', implicit=True)) + # Now, any new modules we found get added to the export list. for origName in list(self.mf.modules.keys()): if origName not in origToNewName: diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 2e1b18ec45..fbcace6b56 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -687,6 +687,21 @@ __version__ = '{0}' whl.write_file(target_path, source_path) + # Include the special sysconfigdata module. + if os.name == 'posix': + import sysconfig + + if hasattr(sysconfig, '_get_sysconfigdata_name'): + modname = sysconfig._get_sysconfigdata_name() + '.py' + else: + modname = '_sysconfigdata.py' + + for entry in sys.path: + source_path = os.path.join(entry, modname) + if os.path.isfile(source_path): + whl.write_file('deploy_libs/' + modname, source_path) + break + # Add plug-ins. for lib in PLUGIN_LIBS: plugin_name = 'lib' + lib From 5493a0d5fc055439f3452fba8fe785f4cf7d5d0e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 16:39:35 +0200 Subject: [PATCH 138/166] glgsg: Fix PStats GPU timing not working with newer NVIDIA drivers Fixes #1320 --- .../glstuff/glGraphicsStateGuardian_src.cxx | 3 +++ .../src/glstuff/glGraphicsStateGuardian_src.h | 4 ++++ .../src/glstuff/glLatencyQueryContext_src.cxx | 22 ++----------------- panda/src/glstuff/glLatencyQueryContext_src.h | 4 ---- panda/src/glstuff/glTimerQueryContext_src.I | 3 ++- panda/src/glstuff/glTimerQueryContext_src.cxx | 2 +- panda/src/glstuff/glTimerQueryContext_src.h | 1 + 7 files changed, 13 insertions(+), 26 deletions(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 43982312f0..fc304d8a96 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2717,6 +2717,8 @@ reset() { _glGetInteger64v = (PFNGLGETINTEGER64VPROC) get_extension_func("glGetInteger64v"); + + _glGetInteger64v(GL_TIMESTAMP, &_timer_query_epoch); } #endif @@ -6889,6 +6891,7 @@ issue_timer_query(int pstats_index) { query = new CLP(LatencyQueryContext)(this, pstats_index); } else { query = new CLP(TimerQueryContext)(this, pstats_index); + query->_epoch = _timer_query_epoch; } if (_deleted_queries.size() >= 1) { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 6457cc920c..b56cbf1774 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -1139,6 +1139,10 @@ public: BufferResidencyTracker _renderbuffer_residency; +#ifndef OPENGLES + GLint64 _timer_query_epoch = 0; +#endif + static PStatCollector _load_display_list_pcollector; static PStatCollector _primitive_batches_display_list_pcollector; static PStatCollector _vertices_display_list_pcollector; diff --git a/panda/src/glstuff/glLatencyQueryContext_src.cxx b/panda/src/glstuff/glLatencyQueryContext_src.cxx index a08ab74517..183f508c65 100644 --- a/panda/src/glstuff/glLatencyQueryContext_src.cxx +++ b/panda/src/glstuff/glLatencyQueryContext_src.cxx @@ -21,27 +21,9 @@ TypeHandle CLP(LatencyQueryContext)::_type_handle; CLP(LatencyQueryContext):: CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index) : - CLP(TimerQueryContext)(glgsg, pstats_index), - _timestamp(0) + CLP(TimerQueryContext)(glgsg, pstats_index) { - glgsg->_glGetInteger64v(GL_TIMESTAMP, &_timestamp); -} - -/** - * 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; + glgsg->_glGetInteger64v(GL_TIMESTAMP, &_epoch); } #endif // OPENGLES diff --git a/panda/src/glstuff/glLatencyQueryContext_src.h b/panda/src/glstuff/glLatencyQueryContext_src.h index 7c255cb5df..a3ee025641 100644 --- a/panda/src/glstuff/glLatencyQueryContext_src.h +++ b/panda/src/glstuff/glLatencyQueryContext_src.h @@ -26,10 +26,6 @@ public: ALLOC_DELETED_CHAIN(CLP(LatencyQueryContext)); - virtual double get_timestamp() const; - - GLint64 _timestamp; - public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/glstuff/glTimerQueryContext_src.I b/panda/src/glstuff/glTimerQueryContext_src.I index c898c30261..2d89904b68 100644 --- a/panda/src/glstuff/glTimerQueryContext_src.I +++ b/panda/src/glstuff/glTimerQueryContext_src.I @@ -19,6 +19,7 @@ CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index) : TimerQueryContext(pstats_index), _glgsg(glgsg), - _index(0) + _index(0), + _epoch(0) { } diff --git a/panda/src/glstuff/glTimerQueryContext_src.cxx b/panda/src/glstuff/glTimerQueryContext_src.cxx index a7b4ecb612..22712f7f50 100644 --- a/panda/src/glstuff/glTimerQueryContext_src.cxx +++ b/panda/src/glstuff/glTimerQueryContext_src.cxx @@ -90,7 +90,7 @@ get_timestamp() const { _glgsg->_glGetQueryObjectui64v(_index, GL_QUERY_RESULT, &time_ns); - return time_ns * 0.000000001; + return (time_ns - _epoch) * 0.000000001; } #endif // OPENGLES diff --git a/panda/src/glstuff/glTimerQueryContext_src.h b/panda/src/glstuff/glTimerQueryContext_src.h index 07709bc7af..8591b55cc0 100644 --- a/panda/src/glstuff/glTimerQueryContext_src.h +++ b/panda/src/glstuff/glTimerQueryContext_src.h @@ -39,6 +39,7 @@ public: GLuint _index; WPT(CLP(GraphicsStateGuardian)) _glgsg; + GLint64 _epoch; public: static TypeHandle get_class_type() { From 3fc579c7d4b6db1c2ea4f35dec66d9e9842a5bad Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 29 Jun 2022 17:03:44 +0200 Subject: [PATCH 139/166] tinydisplay: Implement resizeability of offscreen buffers Fixes #1322 --- panda/src/tinydisplay/tinyGraphicsBuffer.cxx | 9 +++++++++ panda/src/tinydisplay/tinyGraphicsBuffer.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/panda/src/tinydisplay/tinyGraphicsBuffer.cxx b/panda/src/tinydisplay/tinyGraphicsBuffer.cxx index ea8e8241a6..47c0327f1b 100644 --- a/panda/src/tinydisplay/tinyGraphicsBuffer.cxx +++ b/panda/src/tinydisplay/tinyGraphicsBuffer.cxx @@ -89,6 +89,15 @@ end_frame(FrameMode mode, Thread *current_thread) { } } +/** + * + */ +void TinyGraphicsBuffer:: +set_size(int x, int y) { + GraphicsBuffer::set_size(x, y); + create_frame_buffer(); +} + /** * Closes the buffer right now. Called from the buffer thread. */ diff --git a/panda/src/tinydisplay/tinyGraphicsBuffer.h b/panda/src/tinydisplay/tinyGraphicsBuffer.h index 79c6e8c5e5..e1fe665959 100644 --- a/panda/src/tinydisplay/tinyGraphicsBuffer.h +++ b/panda/src/tinydisplay/tinyGraphicsBuffer.h @@ -35,6 +35,8 @@ public: virtual bool begin_frame(FrameMode mode, Thread *current_thread); virtual void end_frame(FrameMode mode, Thread *current_thread); + virtual void set_size(int x, int y); + INLINE ZBuffer *get_frame_buffer(); protected: From 33691d72ecc04d450b9a8a5e6114b8341fafe77a Mon Sep 17 00:00:00 2001 From: "Stephen A. Imhoff" Date: Wed, 29 Jun 2022 17:06:19 +0200 Subject: [PATCH 140/166] bullet: Fix assertion when reconstructing BulletConvexHullShape from bam Fixes #1251 Closes #1252 --- panda/src/bullet/bulletConvexHullShape.cxx | 2 +- tests/bullet/test_bullet_bam.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/panda/src/bullet/bulletConvexHullShape.cxx b/panda/src/bullet/bulletConvexHullShape.cxx index b61722e773..89ec5b2034 100644 --- a/panda/src/bullet/bulletConvexHullShape.cxx +++ b/panda/src/bullet/bulletConvexHullShape.cxx @@ -194,7 +194,7 @@ make_from_bam(const FactoryParams ¶ms) { void BulletConvexHullShape:: fillin(DatagramIterator &scan, BamReader *manager) { BulletShape::fillin(scan, manager); - nassertv(_shape == nullptr); + nassertv(_shape); _shape->setMargin(scan.get_stdfloat()); unsigned int num_points = scan.get_uint32(); diff --git a/tests/bullet/test_bullet_bam.py b/tests/bullet/test_bullet_bam.py index ae20f2d47a..d26a6c4117 100644 --- a/tests/bullet/test_bullet_bam.py +++ b/tests/bullet/test_bullet_bam.py @@ -133,6 +133,26 @@ def test_sphere_shape(): assert shape.radius == shape2.radius +def test_convex_shape(): + shape = bullet.BulletConvexHullShape() + shape.add_array([ + (-1.0, -1.0, -1.0), + (1.0, -1.0, -1.0), + (-1.0, 1.0, -1.0), + (-1.0, -1.0, 1.0), + (1.0, 1.0, -1.0), + (1.0, -1.0, 1.0), + (-1.0, 1.0, 1.0), + (1.0, 1.0, 1.0), + ]) + shape.margin = 0.5 + + shape2 = reconstruct(shape) + + assert type(shape) is type(shape2) + assert shape.margin == shape2.margin + + def test_ghost(): node = bullet.BulletGhostNode("some ghost node") From 2208cc8bff5d4cb52aee51fc3675f9484fb05b6b Mon Sep 17 00:00:00 2001 From: pmp-p Date: Tue, 14 Jun 2022 09:55:29 +0200 Subject: [PATCH 141/166] pipeline: support android no thread build Closes #1323 --- panda/src/pipeline/threadDummyImpl.I | 10 +++++++ panda/src/pipeline/threadDummyImpl.cxx | 39 ++++++++++++++++++++++++++ panda/src/pipeline/threadDummyImpl.h | 16 +++++++++++ 3 files changed, 65 insertions(+) diff --git a/panda/src/pipeline/threadDummyImpl.I b/panda/src/pipeline/threadDummyImpl.I index 64a02bd5a0..85ab2263a4 100644 --- a/panda/src/pipeline/threadDummyImpl.I +++ b/panda/src/pipeline/threadDummyImpl.I @@ -132,3 +132,13 @@ INLINE bool ThreadDummyImpl:: get_context_switches(size_t &, size_t &) { return false; } + +#ifdef ANDROID +/** + * Returns the JNIEnv object for the current thread. + */ +INLINE JNIEnv *ThreadDummyImpl:: +get_jni_env() const { + return _jni_env; +} +#endif diff --git a/panda/src/pipeline/threadDummyImpl.cxx b/panda/src/pipeline/threadDummyImpl.cxx index e5d0d0642c..1e45434302 100644 --- a/panda/src/pipeline/threadDummyImpl.cxx +++ b/panda/src/pipeline/threadDummyImpl.cxx @@ -25,6 +25,13 @@ #include #endif +#ifdef ANDROID +#include "config_express.h" +#include + +static JavaVM *java_vm = nullptr; +#endif + /** * */ @@ -48,4 +55,36 @@ get_current_thread() { return Thread::get_main_thread(); } +#ifdef ANDROID +/** + * Attaches the thread to the Java virtual machine. If this returns true, a + * JNIEnv pointer can be acquired using get_jni_env(). + */ +bool ThreadDummyImpl:: +attach_java_vm() { + assert(java_vm != nullptr); + JNIEnv *env; + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_2; + args.name = "Main"; + args.group = nullptr; + if (java_vm->AttachCurrentThread(&env, &args) != 0) { + thread_cat.error() + << "Failed to attach Java VM to thread "; + _jni_env = nullptr; + return false; + } + _jni_env = env; + return true; +} + +/** + * Binds the Panda thread to the current thread, assuming that the current + * thread is already a valid attached Java thread. Called by JNI_OnLoad. + */ +void ThreadDummyImpl:: +bind_java_thread() { +} +#endif // ANDROID + #endif // THREAD_DUMMY_IMPL diff --git a/panda/src/pipeline/threadDummyImpl.h b/panda/src/pipeline/threadDummyImpl.h index ce3dea76bb..3e1b7a8ab7 100644 --- a/panda/src/pipeline/threadDummyImpl.h +++ b/panda/src/pipeline/threadDummyImpl.h @@ -31,6 +31,11 @@ class Thread; #include // For Sleep(). #endif +#ifdef ANDROID +#include +typedef struct _JNIEnv _jni_env; +#endif + /** * A fake thread implementation for single-threaded applications. This simply * fails whenever you try to start a thread. @@ -58,7 +63,18 @@ public: INLINE static void yield(); INLINE static void consider_yield(); +#ifdef ANDROID + INLINE JNIEnv *get_jni_env() const; + bool attach_java_vm(); + static void bind_java_thread(); +#endif + INLINE static bool get_context_switches(size_t &, size_t &); + +private: +#ifdef ANDROID + JNIEnv *_jni_env = nullptr; +#endif }; #include "threadDummyImpl.I" From d79709f0044534e5b87a8d5d0a7722a49b185c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derzsi=20D=C3=A1niel?= Date: Wed, 20 Jul 2022 23:50:06 +0300 Subject: [PATCH 142/166] express: Add support for bytes multifile encryption passwords (#1334) --- panda/src/express/CMakeLists.txt | 2 + panda/src/express/multifile.h | 8 +- panda/src/express/multifile_ext.I | 79 +++++++++++++++++++ panda/src/express/multifile_ext.h | 40 ++++++++++ panda/src/express/p3express_ext_composite.cxx | 1 + tests/express/test_multifile.py | 13 +++ 6 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 panda/src/express/multifile_ext.I create mode 100644 panda/src/express/multifile_ext.h diff --git a/panda/src/express/CMakeLists.txt b/panda/src/express/CMakeLists.txt index 3168123ebc..3ffc45a1e5 100644 --- a/panda/src/express/CMakeLists.txt +++ b/panda/src/express/CMakeLists.txt @@ -141,6 +141,8 @@ set(P3EXPRESS_IGATEEXT virtualFileSystem_ext.h virtualFile_ext.cxx virtualFile_ext.h + multifile_ext.h + multifile_ext.I ) composite_sources(p3express P3EXPRESS_SOURCES) diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 99b5242127..b344eed9fd 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -69,8 +69,6 @@ PUBLISHED: INLINE void set_encryption_flag(bool flag); INLINE bool get_encryption_flag() const; - INLINE void set_encryption_password(const std::string &encryption_password); - INLINE const std::string &get_encryption_password() const; INLINE void set_encryption_algorithm(const std::string &encryption_algorithm); INLINE const std::string &get_encryption_algorithm() const; @@ -86,6 +84,9 @@ PUBLISHED: std::string update_subfile(const std::string &subfile_name, const Filename &filename, int compression_level); + EXTENSION(INLINE PyObject *set_encryption_password(PyObject *encryption_password) const); + EXTENSION(INLINE PyObject *get_encryption_password() const); + #ifdef HAVE_OPENSSL bool add_signature(const Filename &certificate, const Filename &chain, @@ -143,6 +144,9 @@ PUBLISHED: INLINE const std::string &get_header_prefix() const; public: + INLINE void set_encryption_password(const std::string &encryption_password); + INLINE const std::string &get_encryption_password() const; + #ifdef HAVE_OPENSSL class CertRecord { public: diff --git a/panda/src/express/multifile_ext.I b/panda/src/express/multifile_ext.I new file mode 100644 index 0000000000..68e427af2e --- /dev/null +++ b/panda/src/express/multifile_ext.I @@ -0,0 +1,79 @@ +/** + * 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 multifile_ext.I + * @author Derzsi Daniel + * @date 2022-07-20 + */ + +#include + +/** + * Specifies the password, either as a Python string or a Python bytes object, + * that will be used to encrypt subfiles subsequently added to the multifile + */ +INLINE PyObject *Extension:: +set_encryption_password(PyObject *encryption_password) const { + Py_ssize_t pass_len; + + // Have we been passed a string? + if (PyUnicode_Check(encryption_password)) { + const char *pass_str = PyUnicode_AsUTF8AndSize(encryption_password, &pass_len); + _this->set_encryption_password(std::string(pass_str, pass_len)); + return Dtool_Return_None(); + } + + // Have we been passed a bytes object? + if (PyBytes_Check(encryption_password)) { + char *pass_str; + + if (PyBytes_AsStringAndSize(encryption_password, &pass_str, &pass_len) < 0) { + PyErr_SetString(PyExc_TypeError, "A valid bytes object is required."); + return NULL; + } + + // It is dangerous to use null bytes inside the encryption password. + // OpenSSL will cut off the password prematurely at the first null byte + // encountered. + if (memchr(pass_str, '\0', pass_len) != NULL) { + PyErr_SetString(PyExc_ValueError, "The password must not contain null bytes."); + return NULL; + } + + _this->set_encryption_password(std::string(pass_str, pass_len)); + return Dtool_Return_None(); + } + + return Dtool_Raise_BadArgumentsError( + "set_encryption_password(const Multifile self, str encryption_password)\n" + ); +} + +/** + * Returns the password that will be used to encrypt subfiles subsequently + * added to the multifile, either as a Python string (when possible) or a + * Python bytes object. + */ +INLINE PyObject *Extension:: +get_encryption_password() const { + std::string password = _this->get_encryption_password(); + const char *pass_str = password.c_str(); + Py_ssize_t pass_len = password.length(); + + // First, attempt to decode it as an UTF-8 string... + PyObject *result = PyUnicode_DecodeUTF8(pass_str, pass_len, NULL); + + if (PyErr_Occurred()) { + // This password cannot be decoded as an UTF-8 string, so let's + // return it as a bytes object. + PyErr_Clear(); + result = PyBytes_FromStringAndSize(pass_str, pass_len); + } + + return result; +} diff --git a/panda/src/express/multifile_ext.h b/panda/src/express/multifile_ext.h new file mode 100644 index 0000000000..91a7083bfe --- /dev/null +++ b/panda/src/express/multifile_ext.h @@ -0,0 +1,40 @@ +/** + * 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 multifile_ext.h + * @author Derzsi Daniel + * @date 2022-07-20 + */ + +#ifndef MULTIFILE_EXT_H +#define MULTIFILE_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "multifile.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for Multifile, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + INLINE PyObject *set_encryption_password(PyObject *encryption_password) const; + INLINE PyObject *get_encryption_password() const; +}; + +#include "multifile_ext.I" + +#endif // HAVE_PYTHON + +#endif // MULTIFILE_EXT_H diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx index a8f3311753..90901de263 100644 --- a/panda/src/express/p3express_ext_composite.cxx +++ b/panda/src/express/p3express_ext_composite.cxx @@ -3,3 +3,4 @@ #include "stringStream_ext.cxx" #include "virtualFileSystem_ext.cxx" #include "virtualFile_ext.cxx" +#include "multifile_ext.h" diff --git a/tests/express/test_multifile.py b/tests/express/test_multifile.py index 8618688eb2..10e0ae6fc0 100644 --- a/tests/express/test_multifile.py +++ b/tests/express/test_multifile.py @@ -10,3 +10,16 @@ def test_multifile_read_empty(): assert m.is_read_valid() assert m.get_num_subfiles() == 0 m.close() + + +def test_multifile_password(): + m = Multifile() + + m.set_encryption_password('Panda3D rocks!') + assert m.get_encryption_password() == 'Panda3D rocks!' + + m.set_encryption_password(b'Panda3D is awesome!') + assert m.get_encryption_password() == 'Panda3D is awesome!' + + m.set_encryption_password(b'\xc4\x97\xa1\x01\x85\xb6') + assert m.get_encryption_password() == b'\xc4\x97\xa1\x01\x85\xb6' From cb400b5e170e845fa18a246ccc10f56bd7b3691c Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Jul 2022 21:37:44 +0200 Subject: [PATCH 143/166] interrogatedb: Add functions to obtain getters of make-seqs --- dtool/src/interrogatedb/interrogate_interface.cxx | 13 ++++++++++++- dtool/src/interrogatedb/interrogate_interface.h | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index 9f52b2b873..38f03bad11 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -494,11 +494,22 @@ interrogate_make_seq_num_name(MakeSeqIndex make_seq) { const char * interrogate_make_seq_element_name(MakeSeqIndex make_seq) { // cerr << "interrogate_make_seq_element_name(" << make_seq << ")\n"; - static string result; FunctionIndex function = InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter(); return interrogate_function_name(function); } +FunctionIndex +interrogate_make_seq_num_getter(MakeSeqIndex make_seq) { + // cerr << "interrogate_make_seq_num_getter(" << make_seq << ")\n"; + return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_length_getter(); +} + +FunctionIndex +interrogate_make_seq_element_getter(MakeSeqIndex make_seq) { + // cerr << "interrogate_make_seq_element_getter(" << make_seq << ")\n"; + return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter(); +} + int interrogate_number_of_global_types() { // cerr << "interrogate_number_of_global_types()\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index df17f09e75..fa44a00f2c 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -348,7 +348,8 @@ EXPCL_INTERROGATEDB const char *interrogate_make_seq_comment(ElementIndex elemen EXPCL_INTERROGATEDB const char *interrogate_make_seq_num_name(MakeSeqIndex make_seq); // The name of the real method that returns the nth element, e.g. "get_thing" EXPCL_INTERROGATEDB const char *interrogate_make_seq_element_name(MakeSeqIndex make_seq); - +EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_num_getter(MakeSeqIndex make_seq); +EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_element_getter(MakeSeqIndex make_seq); // Types From 89ee20bcf70ae748fb0e3ac8ac49b0e3f1e3e536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derzsi=20D=C3=A1niel?= Date: Thu, 21 Jul 2022 19:12:13 +0300 Subject: [PATCH 144/166] distributed: Import inspect only during DC file read Closes #1336 --- direct/src/distributed/ConnectionRepository.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index d77802c7ff..bca270ca70 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/direct/src/distributed/ConnectionRepository.py @@ -7,7 +7,6 @@ from direct.distributed.DoCollectionManager import DoCollectionManager from direct.showbase import GarbageReport from .PyDatagramIterator import PyDatagramIterator -import inspect import gc __all__ = ["ConnectionRepository", "GCTrigger"] @@ -309,6 +308,8 @@ class ConnectionRepository( # Now get the class definition for the classes named in the DC # file. + import inspect + for i in range(dcFile.getNumClasses()): dclass = dcFile.getClass(i) number = dclass.getNumber() From d2e8835fb4e045321b3a180e7750da8cfab79896 Mon Sep 17 00:00:00 2001 From: jakemcf22 <108971885+jakemcf22@users.noreply.github.com> Date: Fri, 19 Aug 2022 08:08:29 -0700 Subject: [PATCH 145/166] Update README.md (#1339) [skip ci] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e7a358e30..1cac845fd6 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ You will also need to install the [Windows SDK](https://developer.microsoft.com/ and if you intend to target Windows Vista, you will also need the [Windows 8.1 SDK](https://go.microsoft.com/fwlink/p/?LinkId=323507). -You will also need to have the third-party dependency libraries available for +You will also need the thirdparty dependency libraries available for the build scripts to use. These are available from one of these two URLs, 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 @@ -177,7 +177,7 @@ directory which you can install using `pkg install`. Android ------- -Although it's possible to build Panda3D on an Android device itself using the +Although it's possible to build Panda3D on an Android device using the [termux](https://termux.com/) shell, the recommended route is to cross-compile .whl files using the SDK and NDK, which can then be used by the `build_apps` command to build a Python application into an .apk or .aab bundle. You will From c966a6898c65365340ea1f88f70b0ae4864f7422 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 09:22:40 +0200 Subject: [PATCH 146/166] makepanda: Fix naming of wheels for macOS 12.0 --- makepanda/makepanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index afff30627a..b44e6a72b0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -451,7 +451,7 @@ elif target == 'darwin': else: maj, min = platform.mac_ver()[0].split('.')[:2] osxver = int(maj), int(min) - if osxver[0] == 11: + if osxver[0] >= 11: # I think Python pins minor version to 0 from macOS 11 onward osxver = (osxver[0], 0) From 8617eb917c9009f1a540b4d789e261a50e3a5387 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 10:28:08 +0200 Subject: [PATCH 147/166] makepanda: Force DT_RPATH instead of DT_RUNPATH for deploy-stub Fixes #1358 --- makepanda/makepanda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index b44e6a72b0..f8ff2e1725 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6979,7 +6979,7 @@ if PkgSkip("PYTHON") == 0: if GetTarget() == 'linux' or GetTarget() == 'freebsd': # Setup rpath so libs can be found in the same directory as the deployed game - LibName('DEPLOYSTUB', "-Wl,-rpath,\\$ORIGIN") + LibName('DEPLOYSTUB', "-Wl,--disable-new-dtags,-rpath,\\$ORIGIN") LibName('DEPLOYSTUB', "-Wl,-z,origin") LibName('DEPLOYSTUB', "-rdynamic") PyTargetAdd('deploy-stub.exe', input='deploy-stub.obj') From 3e220e4fb442775b123f03a27a4db4f051d16b15 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 11:15:27 +0200 Subject: [PATCH 148/166] deploy-stub: Set `Py_OptimizeFlag` to 2 for Python 3.2+ This fixes the value of `sys.flags.optimize`, as we always build Python code with `optimize=2` in Python 3.2 and above In the long run we need a better solution, see #1363 Closes #1359 Fixes #1343 --- pandatool/src/deploy-stub/deploy-stub.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index 1bcbdc1cad..fcd9e59e6f 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -441,6 +441,10 @@ int Py_FrozenMain(int argc, char **argv) Py_NoSiteFlag = 0; Py_NoUserSiteDirectory = 1; +#if PY_VERSION_HEX >= 0x03020000 + Py_OptimizeFlag = 2; +#endif + #ifndef NDEBUG if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') inspect = 1; From 899cbb9fff3f7e77bb69f6d62d1247884d6891c0 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 11:37:49 +0200 Subject: [PATCH 149/166] shadow: Add shadow-cube-map-filter setting, disabled by default Enabling this will enable the `FT_shadow` filter for cube maps, which doesn't work with Cg shaders (incl. shader generator) but does with custom GLSL shaders This will be enabled by default once the shaderpipeline branch is merged Fixes #1332 --- panda/src/display/config_display.cxx | 6 ++++++ panda/src/display/config_display.h | 1 + panda/src/display/graphicsStateGuardian.cxx | 11 +++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 2cb2f40c27..58caa6af2a 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -463,6 +463,12 @@ ConfigVariableInt shadow_depth_bits PRC_DESC("The minimum number of depth buffer bits requested when rendering " "shadow maps. Set this to 32 for more depth resolution in shadow " "maps.")); +ConfigVariableBool shadow_cube_map_filter +("shadow-cube-map-filter", false, + PRC_DESC("If true, Panda enables hardware depth map comparison mode for " + "point lights, if supported. If false, does not. Keep this set to " + "false if you want the shader generator to work correctly for point " + "light shadows.")); ConfigVariableColor background_color ("background-color", "0.41 0.41 0.41 0.0", diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index fb4da08b8d..a76c473c91 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -103,6 +103,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableInt accum_bits; extern EXPCL_PANDA_DISPLAY ConfigVariableInt multisamples; extern EXPCL_PANDA_DISPLAY ConfigVariableInt back_buffers; extern EXPCL_PANDA_DISPLAY ConfigVariableInt shadow_depth_bits; +extern EXPCL_PANDA_DISPLAY ConfigVariableBool shadow_cube_map_filter; extern EXPCL_PANDA_DISPLAY ConfigVariableDouble pixel_zoom; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 9ea3ab1f90..b1f0961658 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -3580,8 +3580,15 @@ get_dummy_shadow_map(Texture::TextureType texture_type) const { dummy_cube->setup_cube_map(1, Texture::T_unsigned_byte, Texture::F_depth_component); dummy_cube->set_clear_color(1); // Note: cube map shadow filtering doesn't seem to work in Cg. - dummy_cube->set_minfilter(SamplerState::FT_linear); - dummy_cube->set_magfilter(SamplerState::FT_linear); + // That is why it is currently disabled by default, but it can be + // overridden in Config.prc for apps that have custom GLSL shaders. + if (shadow_cube_map_filter && get_supports_shadow_filter()) { + dummy_cube->set_minfilter(SamplerState::FT_shadow); + dummy_cube->set_magfilter(SamplerState::FT_shadow); + } else { + dummy_cube->set_minfilter(SamplerState::FT_linear); + dummy_cube->set_magfilter(SamplerState::FT_linear); + } } return dummy_cube; } From d7c602d2030e5cda1990b0cafafaeeac831caa55 Mon Sep 17 00:00:00 2001 From: Disyer Date: Tue, 26 Jul 2022 18:22:13 +0300 Subject: [PATCH 150/166] ffmpeg: Resolve segmentation fault when statically linking ffmpeg Closes #1340 --- panda/src/ffmpeg/ffmpegAudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/ffmpeg/ffmpegAudio.h b/panda/src/ffmpeg/ffmpegAudio.h index 6559f59ff5..fe22f93c23 100644 --- a/panda/src/ffmpeg/ffmpegAudio.h +++ b/panda/src/ffmpeg/ffmpegAudio.h @@ -38,7 +38,7 @@ public: return _type_handle; } static void init_type() { - TypedWritableReferenceCount::init_type(); + MovieAudio::init_type(); register_type(_type_handle, "FfmpegAudio", MovieAudio::get_class_type()); } From 813490b2c7f0ed39d1893c093698691e9afa083c Mon Sep 17 00:00:00 2001 From: WMOkiishi Date: Wed, 31 Aug 2022 11:52:18 +0200 Subject: [PATCH 151/166] interrogatedb: Add functions to interrogate_interface.h: - `interrogate_function_is_unary_op` - `interrogate_function_is_operator_typecast` - `interrogate_type_is_array` - `interrogate_type_array_size` Closes #1362 --- dtool/metalibs/dtoolconfig/pydtool.cxx | 108 ++++++++++++++++++ .../interrogatedb/interrogate_interface.cxx | 25 ++++ .../src/interrogatedb/interrogate_interface.h | 6 + 3 files changed, 139 insertions(+) diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index 5eca11ea8c..fa5474a18c 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -60,6 +60,8 @@ static PyObject *_inP07ytISgV(PyObject *self, PyObject *args); static PyObject *_inP07ytH3bx(PyObject *self, PyObject *args); static PyObject *_inP07ytzeUk(PyObject *self, PyObject *args); static PyObject *_inP07ytUeI5(PyObject *self, PyObject *args); +static PyObject *_inP07ytbmxJ(PyObject *self, PyObject *args); +static PyObject *_inP07ytY8Lc(PyObject *self, PyObject *args); static PyObject *_inP07ytJAAI(PyObject *self, PyObject *args); static PyObject *_inP07yt0UXw(PyObject *self, PyObject *args); static PyObject *_inP07ytuSvx(PyObject *self, PyObject *args); @@ -95,6 +97,8 @@ static PyObject *_inP07ytiytI(PyObject *self, PyObject *args); static PyObject *_inP07ytZc07(PyObject *self, PyObject *args); static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args); static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args); +static PyObject *_inP07ytrppS(PyObject *self, PyObject *args); +static PyObject *_inP07ytO50x(PyObject *self, PyObject *args); static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args); static PyObject *_inP07ytMT0z(PyObject *self, PyObject *args); static PyObject *_inP07ytiW3v(PyObject *self, PyObject *args); @@ -126,6 +130,8 @@ static PyObject *_inP07ytDyRd(PyObject *self, PyObject *args); static PyObject *_inP07ytMnKa(PyObject *self, PyObject *args); static PyObject *_inP07ytRtji(PyObject *self, PyObject *args); static PyObject *_inP07ytCnbQ(PyObject *self, PyObject *args); +static PyObject *_inP07ytoxqc(PyObject *self, PyObject *args); +static PyObject *_inP07ytZQIS(PyObject *self, PyObject *args); static PyObject *_inP07ytdUVN(PyObject *self, PyObject *args); static PyObject *_inP07ytZtNk(PyObject *self, PyObject *args); static PyObject *_inP07ytihbt(PyObject *self, PyObject *args); @@ -828,6 +834,34 @@ _inP07ytUeI5(PyObject *, PyObject *args) { return nullptr; } +/* + * Python simple wrapper for + * bool interrogate_function_is_unary_op(FunctionIndex function) + */ +static PyObject * +_inP07ytbmxJ(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_function_is_unary_op)((FunctionIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * bool interrogate_function_is_operator_typecast(FunctionIndex function) + */ +static PyObject * +_inP07ytY8Lc(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_function_is_operator_typecast)((FunctionIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + /* * Python simple wrapper for * bool interrogate_function_is_constructor(FunctionIndex function) @@ -1405,6 +1439,42 @@ _inP07ytGB9D(PyObject *, PyObject *args) { return nullptr; } +/* + * Python simple wrapper for + * FunctionIndex interrogate_make_seq_num_getter(MakeSeqIndex make_seq) + */ +static PyObject * +_inP07ytrppS(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + FunctionIndex return_value = (::interrogate_make_seq_num_getter)((MakeSeqIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(return_value); +#else + return PyInt_FromLong(return_value); +#endif + } + return nullptr; +} + +/* + * Python simple wrapper for + * FunctionIndex interrogate_make_seq_element_getter(MakeSeqIndex make_seq) + */ +static PyObject * +_inP07ytO50x(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + FunctionIndex return_value = (::interrogate_make_seq_element_getter)((MakeSeqIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(return_value); +#else + return PyInt_FromLong(return_value); +#endif + } + return nullptr; +} + /* * Python simple wrapper for * int interrogate_number_of_global_types(void) @@ -1901,6 +1971,38 @@ _inP07ytCnbQ(PyObject *, PyObject *args) { return nullptr; } +/* + * Python simple wrapper for + * bool interrogate_type_is_array(TypeIndex type) + */ +static PyObject * +_inP07ytoxqc(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = (::interrogate_type_is_array)((TypeIndex)param0); + return PyBool_FromLong(return_value); + } + return nullptr; +} + +/* + * Python simple wrapper for + * int interrogate_type_array_size(TypeIndex type) + */ +static PyObject * +_inP07ytZQIS(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = (::interrogate_type_array_size)((TypeIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong(return_value); +#else + return PyInt_FromLong(return_value); +#endif + } + return nullptr; +} + /* * Python simple wrapper for * bool interrogate_type_is_enum(TypeIndex type) @@ -2565,6 +2667,8 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS }, { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS }, { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS }, + { "interrogate_function_is_unary_op", &_inP07ytbmxJ, METH_VARARGS }, + { "interrogate_function_is_operator_typecast", &_inP07ytY8Lc, METH_VARARGS }, { "interrogate_function_is_constructor", &_inP07ytJAAI, METH_VARARGS }, { "interrogate_function_is_destructor", &_inP07yt0UXw, METH_VARARGS }, { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS }, @@ -2600,6 +2704,8 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS }, { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS }, { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS }, + { "interrogate_make_seq_num_getter", &_inP07ytrppS, METH_VARARGS }, + { "interrogate_make_seq_element_getter", &_inP07ytO50x, METH_VARARGS }, { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS }, { "interrogate_get_global_type", &_inP07ytMT0z, METH_VARARGS }, { "interrogate_number_of_types", &_inP07ytiW3v, METH_VARARGS }, @@ -2631,6 +2737,8 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_type_is_const", &_inP07ytMnKa, METH_VARARGS }, { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS }, { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS }, + { "interrogate_type_is_array", &_inP07ytoxqc, METH_VARARGS }, + { "interrogate_type_array_size", &_inP07ytZQIS, METH_VARARGS }, { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS }, { "interrogate_type_is_scoped_enum", &_inP07ytZtNk, METH_VARARGS }, { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS }, diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index 38f03bad11..d7f4ce58dd 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -266,6 +266,19 @@ interrogate_function_class(FunctionIndex function) { return InterrogateDatabase::get_ptr()->get_function(function).get_class(); } +bool +interrogate_function_is_unary_op(FunctionIndex function) { + // cerr << "interrogate_function_is_unary_op(" << function << ")\n"; + return InterrogateDatabase::get_ptr()->get_function(function).is_unary_op(); +} + +bool +interrogate_function_is_operator_typecast(FunctionIndex function) { + // cerr << "interrogate_function_is_operator_typecast(" << function << + // ")\n"; + return InterrogateDatabase::get_ptr()->get_function(function).is_operator_typecast(); +} + bool interrogate_function_is_constructor(FunctionIndex function) { // cerr << "interrogate_function_is_constructor(" << function << ")\n"; @@ -697,6 +710,18 @@ interrogate_type_wrapped_type(TypeIndex type) { return InterrogateDatabase::get_ptr()->get_type(type).get_wrapped_type(); } +bool +interrogate_type_is_array(TypeIndex type) { + // cerr << "interrogate_type_is_array(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_array(); +} + +int +interrogate_type_array_size(TypeIndex type) { + // cerr << "interrogate_type_array_size(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).get_array_size(); +} + bool interrogate_type_is_enum(TypeIndex type) { // cerr << "interrogate_type_is_enum(" << type << ")\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index fa44a00f2c..a44f9fef92 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -210,6 +210,8 @@ EXPCL_INTERROGATEDB const char *interrogate_function_prototype(FunctionIndex fun // if the function is a class method. EXPCL_INTERROGATEDB bool interrogate_function_is_method(FunctionIndex function); EXPCL_INTERROGATEDB TypeIndex interrogate_function_class(FunctionIndex function); +EXPCL_INTERROGATEDB bool interrogate_function_is_unary_op(FunctionIndex function); +EXPCL_INTERROGATEDB bool interrogate_function_is_operator_typecast(FunctionIndex function); EXPCL_INTERROGATEDB bool interrogate_function_is_constructor(FunctionIndex function); EXPCL_INTERROGATEDB bool interrogate_function_is_destructor(FunctionIndex function); @@ -424,6 +426,10 @@ EXPCL_INTERROGATEDB bool interrogate_type_is_const(TypeIndex type); EXPCL_INTERROGATEDB bool interrogate_type_is_typedef(TypeIndex type); EXPCL_INTERROGATEDB TypeIndex interrogate_type_wrapped_type(TypeIndex type); +// If interrogate_type_is_array() returns true, this is an array type. +EXPCL_INTERROGATEDB bool interrogate_type_is_array(TypeIndex type); +EXPCL_INTERROGATEDB int interrogate_type_array_size(TypeIndex type); + // If interrogate_type_is_enum() returns true, this is an enumerated type, // which means it may take any one of a number of named integer values. EXPCL_INTERROGATEDB bool interrogate_type_is_enum(TypeIndex type); From ba8c1f032533259ddd68c68dd8e3b93ab841a17b Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 13:50:10 +0200 Subject: [PATCH 152/166] dist: Fix finding sysconfigdata module in Python 3.6 and 3.7 Also fix fatal error when sysconfigdata module isn't found (may be use of older wheels), just report it as a missing module Fixes #1326 for Python 3.6 and 3.7 --- direct/src/dist/FreezeTool.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 6c9d382a5d..f50b0cd927 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -1217,22 +1217,31 @@ class Freezer: # Special case for sysconfig, which depends on a platform-specific # sysconfigdata module on POSIX systems. - if 'sysconfig' in self.mf.modules: + missing = [] + if 'sysconfig' in self.mf.modules and \ + ('linux' in self.platform or 'mac' in self.platform): + modname = '_sysconfigdata' if sys.version_info >= (3, 6): + modname += '_' + if sys.version_info < (3, 8): + modname += 'm' + if 'linux' in self.platform: arch = self.platform.split('_', 1)[1] - self.__loadModule(self.ModuleDef('_sysconfigdata__linux_' + arch + '-linux-gnu', implicit=True)) + modname += '_linux_' + arch + '-linux-gnu' elif 'mac' in self.platform: - self.__loadModule(self.ModuleDef('_sysconfigdata__darwin_darwin', implicit=True)) - elif 'linux' in self.platform or 'mac' in self.platform: - self.__loadModule(self.ModuleDef('_sysconfigdata', implicit=True)) + modname += '_darwin_darwin' + + try: + self.__loadModule(self.ModuleDef(modname, implicit=True)) + except: + missing.append(modname) # Now, any new modules we found get added to the export list. for origName in list(self.mf.modules.keys()): if origName not in origToNewName: self.modules[origName] = self.ModuleDef(origName, implicit = True) - missing = [] for origName in self.mf.any_missing_maybe()[0]: if origName in startupModules: continue From 88ba7badd4e2b7fec012c0df1997230ecfacab2e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 31 Aug 2022 16:17:18 +0200 Subject: [PATCH 153/166] collide: Fix false negative when sphere is fully inside box Fixes #1335 --- panda/src/collide/collisionBox.cxx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx index 168844f37a..3e80650d6b 100644 --- a/panda/src/collide/collisionBox.cxx +++ b/panda/src/collide/collisionBox.cxx @@ -230,10 +230,12 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { bool intersect; LPlane plane; LVector3 normal; + bool fully_inside = true; for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) { plane = get_plane( ip ); if (_points[ip].size() < 3) { + fully_inside = false; continue; } if (wrt_prev_space != wrt_space) { @@ -248,6 +250,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { // moving in the same direction as the plane's normal. PN_stdfloat dot = delta.dot(plane.get_normal()); if (dot > 0.1f) { + fully_inside = false; continue; // no intersection } @@ -304,13 +307,19 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { if (!plane.intersects_line(dist, from_center, -(plane.get_normal()))) { // No intersection with plane? This means the plane's effective normal // was within the plane itself. A useless polygon. + fully_inside = false; continue; } - if (dist > from_radius || dist < -from_radius) { - // No intersection with the plane. + if (dist > from_radius) { + // Fully outside this plane, there can not be an intersection. + return nullptr; + } + if (dist < -from_radius) { + // Fully inside this plane. continue; } + fully_inside = false; LPoint2 p = to_2d(from_center - dist * plane.get_normal(), ip); PN_stdfloat edge_dist = 0.0f; @@ -366,8 +375,9 @@ test_intersection_from_sphere(const CollisionEntry &entry) const { } intersect = true; } - if( !intersect ) + if (!fully_inside && !intersect) { return nullptr; + } if (collide_cat.is_debug()) { collide_cat.debug() From 105f9abbfae7993a0c05ff7e0a676127d181bab2 Mon Sep 17 00:00:00 2001 From: LD Date: Sun, 6 Mar 2022 20:27:56 +0100 Subject: [PATCH 154/166] cocoadisplay: Trigger handle_move_event() when a resize event is received to also update the origin of the window if needed --- panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm index 7e39748bd1..17fae241f3 100644 --- a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm +++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm @@ -30,6 +30,7 @@ - (void) windowDidResize:(NSNotification *)notification { // Forcing a move event is unfortunately necessary because Cocoa does not // call windowDidMove in case of window zooms. + _graphicsWindow->handle_move_event(); _graphicsWindow->handle_resize_event(); } From 69bf5fa626fda0b10dd552f776fb2364184d1bd4 Mon Sep 17 00:00:00 2001 From: LD Date: Mon, 30 May 2022 21:52:51 +0200 Subject: [PATCH 155/166] cocoadisplay: Remove overzealous coordinates transform performed on mouse position --- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 0b973a0317..747951108f 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -139,10 +139,10 @@ move_pointer(int device, int x, int y) { if (device == 0) { CGPoint point; if (_properties.get_fullscreen()) { - point = CGPointMake(x, y + 1); + point = CGPointMake(x, y); } else { point = CGPointMake(x + _properties.get_x_origin(), - y + _properties.get_y_origin() + 1); + y + _properties.get_y_origin()); } // I don't know what the difference between these two methods is. if @@ -1966,9 +1966,8 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { } } - // Strangely enough, in Cocoa, mouse Y coordinates are 1-based. nx = x; - ny = y - 1; + ny = y; } else { // We received deltas, so add it to the current mouse position. @@ -1985,10 +1984,10 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { ny = std::max(0., std::min((double) get_y_size() - 1, ny)); if (_properties.get_fullscreen()) { - point = CGPointMake(nx, ny + 1); + point = CGPointMake(nx, ny); } else { point = CGPointMake(nx + _properties.get_x_origin(), - ny + _properties.get_y_origin() + 1); + ny + _properties.get_y_origin()); } if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) { From 102da5bc35b2e1bc5a9c27e19d3120dff32b484d Mon Sep 17 00:00:00 2001 From: LD Date: Tue, 31 May 2022 21:13:50 +0200 Subject: [PATCH 156/166] cocoadisplay: Don't use position delta for confined mouse mode as it lead to invalid estimation of the pointer position --- panda/src/cocoadisplay/cocoaPandaView.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm index 6a3f48b39a..963b1905ff 100644 --- a/panda/src/cocoadisplay/cocoaPandaView.mm +++ b/panda/src/cocoadisplay/cocoaPandaView.mm @@ -121,9 +121,7 @@ NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; BOOL inside = [self mouse:loc inRect:[self bounds]]; - // the correlation between mouse deltas and location are "debounced" - // apparently, so send deltas for both relative and confined modes - if (_graphicsWindow->get_properties().get_mouse_mode() != WindowProperties::M_absolute) { + if (_graphicsWindow->get_properties().get_mouse_mode() == WindowProperties::M_relative) { _graphicsWindow->handle_mouse_moved_event(inside, [event deltaX], [event deltaY], false); } else { _graphicsWindow->handle_mouse_moved_event(inside, loc.x, loc.y, true); From 12ae2973ae876b54c03ac966244f64c0ea7eb358 Mon Sep 17 00:00:00 2001 From: LD Date: Tue, 31 May 2022 21:17:11 +0200 Subject: [PATCH 157/166] cocoadisplay: Disable the event suppression interval when moving the position of the mouse pointer --- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 747951108f..90699ef26d 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -145,9 +145,11 @@ move_pointer(int device, int x, int y) { y + _properties.get_y_origin()); } - // I don't know what the difference between these two methods is. if - // (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) { - if (CGDisplayMoveCursorToPoint(_display, point) == kCGErrorSuccess) { + if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) { + //After moving (or warping) the mouse position, CG starts an event + // suppression interval during which no more mouse events can occur + // This interval can be interupted by the following call : + CGAssociateMouseAndMouseCursorPosition(YES); // Generate a mouse event. NSPoint pos = [_window mouseLocationOutsideOfEventStream]; NSPoint loc = [_view convertPoint:pos fromView:nil]; @@ -1991,6 +1993,10 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) { } if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) { + //After moving (or warping) the mouse position, CG starts an event + // suppression interval during which no more mouse events can occur + // This interval can be interupted by the following call : + CGAssociateMouseAndMouseCursorPosition(YES); in_window = true; } else { cocoadisplay_cat.warning() << "Failed to return mouse pointer to window\n"; From 5443377d8b1020145f36a560e36efa52c7993b19 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 1 Sep 2022 18:11:59 +0200 Subject: [PATCH 158/166] doc: Update release notes for 1.10.12 [skip ci] --- doc/ReleaseNotes | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index a2499ce00d..1314ad901f 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -1,3 +1,57 @@ +----------------------- RELEASE 1.10.12 ----------------------- + +Recommended maintenance release containing primarily bug fixes. + +Windowing +* Windows: Fix origin not respected when switching to windowed mode +* macOS: Fix origin not being updated when resizing window +* macOS: Fix off-by-one errors with mouse cursor position +* macOS: Fix issues with confined mouse mode +* macOS: Fix events being suppressed when moving the mouse pointer +* macOS: Invert horizontal scroll, set `cocoa-invert-wheel-x true` to revert + +Rendering +* Add `shadow-cube-map-filter` setting to enable cube map shadow filtering +* Support floating-point FBOs in OpenGL ES 2+ +* Fix texture format selection in OpenGL with T_half_float component type +* Added `egl-device-index` config var to select EGL device +* Offscreen windows in tinydisplay renderer are now resizeable +* CommonFilters now supports alternative coordinate systems +* Fix BufferViewer frame when using a different coordinate system + +Deployment +* Fix _bootlocale error in deployed application on Windows with Python 3.10 +* Include _sysconfigdata module properly when using sysconfig module +* Fix building deploy-stub on platforms that use DT_RUNPATH instead of DT_RPATH +* `sys.flags.optimize` is now set to 2 in Python 3.2 and above + +Miscellaneous +* `Texture::get_ram_image_as()` fixed for 3D textures +* Fix PStats GPU timing not working with newer NVIDIA drivers +* Fix false negative in collision test when sphere is fully inside box +* Resolve segmentation fault when statically linking ffmpeg module +* Fix issue with failed mmap when using WebcamVideo on Linux +* macOS: Keyboard/mouse devices are no longer enumerated by default +* Fix repr of LPlane class +* Remove dependency on ShowBase in FilterManager +* Many new functions added to interrogatedb module to query additional info +* Interrogate no longer writes wrappers with rvalue references to interrogatedb +* PStats on Linux: Fix mouse motion detected outside strip chart graph area +* Fix assertion when reading bam file with Bullet convex hull shape +* Fix memory leak when specifying owner of a task +* Add additional helpful debug/spam prints to display code + +Build +* Support building with OpenSSL 1.1.1 on Windows +* Support building with OpenEXR 3.0 or 3.1 on Windows +* Fix errors when compiling Panda headers with MinGW +* Allow compiling Panda headers on Windows without NOMINMAX set +* Fix wheel platform tag on manylinux aarch64 +* Experimentally allow building with mimalloc on Windows +* Makepanda records cache timestamps as integers instead of floats +* Makepanda can now also build tinydisplay on Linux without X11 +* Fix naming of built wheels when building for macOS 12 + ----------------------- RELEASE 1.10.11 ----------------------- Maintenance release containing assorted bug fixes and minor improvements. From 1f6545c8852e1abeba5de95728c840899a1fd359 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 1 Sep 2022 18:16:55 +0200 Subject: [PATCH 159/166] readme: Update version number to 1.10.12 [skip ci] --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f1ac3a0931..fd73771869 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Installing Panda3D ================== The latest Panda3D SDK can be downloaded from -[this page](https://www.panda3d.org/download/sdk-1-10-11/). +[this page](https://www.panda3d.org/download/sdk-1-10-12/). If you are familiar with installing Python packages, you can use the following command: @@ -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.11/panda3d-1.10.11-tools-win64.zip -- https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win32.zip +- 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 After acquiring these dependencies, you can build Panda3D from the command prompt using the following command. Change the `--msvc-version` option based @@ -136,7 +136,7 @@ macOS ----- On macOS, you will need to download a set of precompiled thirdparty packages in order to -compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-mac.tar.gz). +compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-mac.tar.gz). After placing the thirdparty directory inside the panda3d source directory, you may build Panda3D using a command like the following: From 590531a0b2d3e5b3f02712041d4c03c1dd40a2be Mon Sep 17 00:00:00 2001 From: "Paul m. p. P" Date: Mon, 7 Feb 2022 19:33:19 +0100 Subject: [PATCH 160/166] py_panda: Fix compilation issue with Python 3.11 (Cherry-picked from 833ad89ebad58395d0af0b7ec08538e5e4308265) --- dtool/src/interrogatedb/py_panda.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 11c86ae251..6190b6e642 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -754,7 +754,7 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) { if (callable == nullptr) { return nullptr; } - PyObject *result = _PyObject_CallNoArg(callable); + PyObject *result = PyObject_CallNoArgs(callable); Py_DECREF(callable); return result; } @@ -778,7 +778,7 @@ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) { if (callable == nullptr) { return nullptr; } - PyObject *result = _PyObject_CallNoArg(callable); + PyObject *result = PyObject_CallNoArgs(callable); Py_DECREF(callable); return result; } From 48b0819cdad1a9cba2aae0718ec2c8ed06f1e9d8 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 1 Sep 2022 19:41:37 +0200 Subject: [PATCH 161/166] rocket: Don't try to build Boost binding code for Python 3 libRocket doesn't support Python 3 anyway, and it has an error compiling with Python 3.11 [skip ci] --- doc/ReleaseNotes | 1 + panda/src/rocket/rocketRegion_ext.cxx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 1314ad901f..fa0afb6e20 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -42,6 +42,7 @@ Miscellaneous * Add additional helpful debug/spam prints to display code Build +* Support building with Python 3.11 * Support building with OpenSSL 1.1.1 on Windows * Support building with OpenEXR 3.0 or 3.1 on Windows * Fix errors when compiling Panda headers with MinGW diff --git a/panda/src/rocket/rocketRegion_ext.cxx b/panda/src/rocket/rocketRegion_ext.cxx index 84acb44aa0..f7ff0e6519 100644 --- a/panda/src/rocket/rocketRegion_ext.cxx +++ b/panda/src/rocket/rocketRegion_ext.cxx @@ -16,7 +16,7 @@ #ifdef HAVE_PYTHON -#ifndef CPPPARSER +#if !defined(CPPPARSER) && PY_MAJOR_VERSION < 3 #define HAVE_LONG_LONG 1 #include #include @@ -30,6 +30,7 @@ */ PyObject* Extension:: get_context() const { +#if PY_MAJOR_VERSION < 3 try { Rocket::Core::Context* context = _this->get_context(); python::object py_context = Rocket::Core::Python::Utilities::MakeObject(context); @@ -44,6 +45,7 @@ get_context() const { (void)e; // Return NULL, which will trigger the exception in Python } +#endif return nullptr; } From 2bd34a806d57a8b80fc5f66dd6c8089c8f3b9f7b Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 2 Sep 2022 07:52:42 +0200 Subject: [PATCH 162/166] dist: Add hidden imports for scipy --- direct/src/dist/FreezeTool.py | 6 ++++++ doc/ReleaseNotes | 1 + 2 files changed, 7 insertions(+) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index f50b0cd927..db7ac1092c 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -84,6 +84,12 @@ hiddenImports = { ], 'pandas.compat': ['lzma', 'cmath'], 'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'], + 'scipy.linalg': ['scipy.linalg.cython_blas', 'scipy.linalg.cython_lapack'], + 'scipy.sparse.csgraph': ['scipy.sparse.csgraph._validation'], + 'scipy.spatial._qhull': ['scipy._lib.messagestream'], + 'scipy.spatial.transform._rotation': ['scipy.spatial.transform._rotation_groups'], + 'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'], + 'scipy.stats._stats': ['scipy.special.cython_special'], } if sys.version_info >= (3,): diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index fa0afb6e20..53aa89a4bb 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -24,6 +24,7 @@ Deployment * Include _sysconfigdata module properly when using sysconfig module * Fix building deploy-stub on platforms that use DT_RUNPATH instead of DT_RPATH * `sys.flags.optimize` is now set to 2 in Python 3.2 and above +* Fix import errors when using scipy Miscellaneous * `Texture::get_ram_image_as()` fixed for 3D textures From 50a34900c3d2cca150361e4e43393c1dce08a192 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 2 Sep 2022 07:56:10 +0200 Subject: [PATCH 163/166] Add Python 3.11 to setup.cfg [skip ci] --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 5d70295f57..b15a37a0f1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,6 +23,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Topic :: Games/Entertainment Topic :: Multimedia From e30d88d9dbb74da923f660a76e8d34e8ac732e3d Mon Sep 17 00:00:00 2001 From: LD Date: Sat, 21 May 2022 21:23:53 +0200 Subject: [PATCH 164/166] showbase: Fix regression with BufferViewer in double prec build Regression was introduced in 98314da00ff9d1d0ef567f1a82796862462f6540 Use explicitly Vec3F in calls to addData3f to avoid crash on double precision builds Closes #1365 --- direct/src/showbase/BufferViewer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py index ed2fc87733..4af8b164b9 100644 --- a/direct/src/showbase/BufferViewer.py +++ b/direct/src/showbase/BufferViewer.py @@ -240,10 +240,10 @@ class BufferViewer(DirectObject): offsetx = (ringoffset[ring]*2.0) / float(sizex) offsety = (ringoffset[ring]*2.0) / float(sizey) bright = ringbright[ring] - vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, -1 - offsety)) - vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, -1 - offsety)) - vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, 1 + offsety)) - vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, 1 + offsety)) + vwriter.addData3f(Vec3F.rfu(-1 - offsetx, 0, -1 - offsety)) + vwriter.addData3f(Vec3F.rfu( 1 + offsetx, 0, -1 - offsety)) + vwriter.addData3f(Vec3F.rfu( 1 + offsetx, 0, 1 + offsety)) + vwriter.addData3f(Vec3F.rfu(-1 - offsetx, 0, 1 + offsety)) cwriter.addData3f(bright, bright, bright) cwriter.addData3f(bright, bright, bright) cwriter.addData3f(bright, bright, bright) From 67110156d8e7fa71980cf813455193f8b3494204 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 2 Sep 2022 23:09:11 +0200 Subject: [PATCH 165/166] Bump version number on release/1.10.x branch to 1.10.13 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b15a37a0f1..b77a116c14 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = Panda3D -version = 1.10.12 +version = 1.10.13 url = https://www.panda3d.org/ description = Panda3D is a framework for 3D rendering and game development for Python and C++ programs. license = Modified BSD License From f243d9983fdc32cabd28fd43858ea3e8d32983c2 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 2 Sep 2022 23:10:37 +0200 Subject: [PATCH 166/166] CMake: Add `--disable-new-dtags` linker option for deploy-stub This forces the use of DT_RPATH instead of DT_RUNPATH, see #1358 and 8617eb917c9009f1a540b4d789e261a50e3a5387 --- pandatool/src/deploy-stub/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandatool/src/deploy-stub/CMakeLists.txt b/pandatool/src/deploy-stub/CMakeLists.txt index 851bc9ac4c..1c7a37017f 100644 --- a/pandatool/src/deploy-stub/CMakeLists.txt +++ b/pandatool/src/deploy-stub/CMakeLists.txt @@ -17,7 +17,7 @@ elseif(IS_LINUX OR IS_FREEBSD) set_target_properties(deploy-stub PROPERTIES INSTALL_RPATH "$ORIGIN" BUILD_WITH_INSTALL_RPATH ON) - target_link_options(deploy-stub PRIVATE -Wl,-z,origin -rdynamic) + target_link_options(deploy-stub PRIVATE -Wl,--disable-new-dtags -Wl,-z,origin -rdynamic) endif() target_link_libraries(deploy-stub Python::Python)