From dae26c2e4b2fe7abd842bf3c76f15a0d7b2305dd Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 8 Dec 2022 15:20:14 +0100 Subject: [PATCH 1/8] cocoadisplay: Fix fullscreen regression on Intel macs Partially backs out 619050bb32a6b154e48af968ca9372278292cd61 Fixes #1316 (for good this time hopefully) --- panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 55 ++++--------------- 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 1b564c0eb1..4a001912ff 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -43,35 +43,12 @@ #import #import -#include - TypeHandle CocoaGraphicsWindow::_type_handle; #ifndef MAC_OS_X_VERSION_10_15 #define NSAppKitVersionNumber10_14 1671 #endif -/** - * Returns true if this is an arm64-based mac. - */ -static int is_arm64_mac() { -#ifdef __aarch64__ - return 1; -#elif defined(__x86_64__) - // Running in Rosetta 2? - static int ret = -1; - if (ret < 0) { - size_t size = sizeof(ret); - if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) { - ret = 0; - } - } - return ret; -#else - return 0; -#endif -} - /** * */ @@ -211,29 +188,17 @@ begin_frame(FrameMode mode, Thread *current_thread) { cocoagsg->lock_context(); // Set the drawable. - if (_properties.get_fullscreen() && !is_arm64_mac()) { - // Fullscreen. Note that this call doesn't work with the newer - // Metal-based OpenGL drivers. - CGLError err = CGLSetFullScreenOnDisplay((CGLContextObj) [cocoagsg->_context CGLContextObj], CGDisplayIDToOpenGLDisplayMask(_display)); - if (err != kCGLNoError) { - cocoadisplay_cat.error() - << "Failed call to CGLSetFullScreenOnDisplay with display mask " - << CGDisplayIDToOpenGLDisplayMask(_display) << ": " << CGLErrorString(err) << "\n"; - return false; - } - } else { - // Although not recommended, it is technically possible to use the same - // context with multiple different-sized windows. If that happens, the - // context needs to be updated accordingly. - if ([cocoagsg->_context view] != _view) { - // XXX I'm not 100% sure that changing the view requires it to update. - _context_needs_update = true; - [cocoagsg->_context setView:_view]; + // Although not recommended, it is technically possible to use the same + // context with multiple different-sized windows. If that happens, the + // context needs to be updated accordingly. + if ([cocoagsg->_context view] != _view) { + // XXX I'm not 100% sure that changing the view requires it to update. + _context_needs_update = true; + [cocoagsg->_context setView:_view]; - if (cocoadisplay_cat.is_spam()) { - cocoadisplay_cat.spam() - << "Switching context to view " << _view << "\n"; - } + if (cocoadisplay_cat.is_spam()) { + cocoadisplay_cat.spam() + << "Switching context to view " << _view << "\n"; } } From 1375d387254fe9adc2e0a736d2af742f25c1cff2 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 9 Dec 2022 14:00:43 +0100 Subject: [PATCH 2/8] showbase: Fix assorted issues with GarbageReport: * Use of removed `types.InstanceType` * New-style classes are not reported very neatly * Extreme amount of digits in report due to float division Fixes #1304 --- direct/src/showbase/GarbageReport.py | 17 +++++++++-------- direct/src/showbase/JobManager.py | 2 ++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index 7d268bf450..76eb79e702 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -3,11 +3,12 @@ __all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger'] from direct.directnotify.DirectNotifyGlobal import directNotify -from direct.showbase.PythonUtil import fastRepr -from direct.showbase.PythonUtil import AlphabetCounter +from direct.showbase.PythonUtil import fastRepr, AlphabetCounter, itype from direct.showbase.Job import Job +from direct.showbase.JobManagerGlobal import jobMgr +from direct.showbase.MessengerGlobal import messenger +from panda3d.core import ConfigVariableBool import gc -import types import sys GarbageCycleCountAnnounceEvent = 'announceGarbageCycleDesc2num' @@ -215,7 +216,7 @@ class GarbageReport(Job): startIndex = 0 # + 1 to include a reference back to the first object endIndex = numObjs + 1 - if type(objs[-1]) is types.InstanceType and type(objs[0]) is dict: + if type(objs[0]) is dict and hasattr(objs[-1], '__dict__'): startIndex -= 1 endIndex -= 1 @@ -224,7 +225,7 @@ class GarbageReport(Job): numToSkip -= 1 continue obj = objs[index] - if type(obj) is types.InstanceType: + if hasattr(obj, '__dict__'): if not objAlreadyRepresented: cycleBySyntax += '%s' % obj.__class__.__name__ cycleBySyntax += '.' @@ -309,7 +310,7 @@ class GarbageReport(Job): while n > 0: yield None digits += 1 - n /= 10 + n = n // 10 digits = digits format = '%0' + '%s' % digits + 'i:%s \t%s' @@ -564,7 +565,7 @@ class _CFGLGlobals: def checkForGarbageLeaks(): gc.collect() numGarbage = len(gc.garbage) - if (numGarbage > 0 and config.GetBool('auto-garbage-logging', 0)): + if numGarbage > 0 and ConfigVariableBool('auto-garbage-logging', False): if (numGarbage != _CFGLGlobals.LastNumGarbage): print("") gr = GarbageReport('found garbage', threaded=False, collect=False) @@ -574,7 +575,7 @@ def checkForGarbageLeaks(): messenger.send(GarbageCycleCountAnnounceEvent, [gr.getDesc2numDict()]) gr.destroy() notify = directNotify.newCategory("GarbageDetect") - if config.GetBool('allow-garbage-cycles', 1): + if ConfigVariableBool('allow-garbage-cycles', True): func = notify.warning else: func = notify.error diff --git a/direct/src/showbase/JobManager.py b/direct/src/showbase/JobManager.py index 0c2ab09bbf..f96673033a 100755 --- a/direct/src/showbase/JobManager.py +++ b/direct/src/showbase/JobManager.py @@ -2,6 +2,8 @@ from direct.directnotify.DirectNotifyGlobal import directNotify from direct.task.TaskManagerGlobal import taskMgr from direct.showbase.Job import Job from direct.showbase.PythonUtil import getBase +from direct.showbase.MessengerGlobal import messenger + class JobManager: """ From a9e5d84d27141dcbbbe8261948c15c6632cbba04 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 9 Dec 2022 17:15:45 +0100 Subject: [PATCH 3/8] notify: Fix crash when using set_ostream_ptr from Python Also allow passing in None Fixes #1371 (and maybe also #319) --- dtool/src/prc/notify_ext.cxx | 48 +++++++++++++++++++++++++++ dtool/src/prc/notify_ext.h | 37 +++++++++++++++++++++ dtool/src/prc/p3prc_ext_composite.cxx | 1 + dtool/src/prc/pnotify.h | 4 +++ 4 files changed, 90 insertions(+) create mode 100644 dtool/src/prc/notify_ext.cxx create mode 100644 dtool/src/prc/notify_ext.h diff --git a/dtool/src/prc/notify_ext.cxx b/dtool/src/prc/notify_ext.cxx new file mode 100644 index 0000000000..b3dd5a57cf --- /dev/null +++ b/dtool/src/prc/notify_ext.cxx @@ -0,0 +1,48 @@ +/** + * 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 notify_ext.cxx + * @author rdb + * @date 2022-12-09 + */ + +#include "notify_ext.h" + +#ifdef HAVE_PYTHON + +/** + * Changes the ostream that all subsequent Notify messages will be written to. + * If the previous ostream was set with delete_later = true, this will delete + * the previous ostream. If ostream_ptr is None, this resets the default to + * cerr. + */ +void Extension:: +set_ostream_ptr(PyObject *ostream_ptr, bool delete_later) { + extern struct Dtool_PyTypedObject Dtool_std_ostream; + + if (ostream_ptr == Py_None) { + _this->set_ostream_ptr(nullptr, false); + return; + } + + std::ostream *ptr = (std::ostream *)DTOOL_Call_GetPointerThisClass(ostream_ptr, &Dtool_std_ostream, 1, "Notify.set_ostream_ptr", false, true); + if (ptr == nullptr) { + return; + } + + // Since we now have a reference to this class on the C++ end, make sure + // that the ostream isn't being destructed when its Python wrapper expires. + // Note that this may cause a memory leak if delete_later is not set, but + // since these pointers are usually set once for the rest of time, this is + // considered less of a problem than having the Python object destroy the + // object while C++ is still using it. See GitHub #1371. + ((Dtool_PyInstDef *)ostream_ptr)->_memory_rules = false; + _this->set_ostream_ptr(ptr, delete_later); +} + +#endif // HAVE_PYTHON diff --git a/dtool/src/prc/notify_ext.h b/dtool/src/prc/notify_ext.h new file mode 100644 index 0000000000..ced2ac193f --- /dev/null +++ b/dtool/src/prc/notify_ext.h @@ -0,0 +1,37 @@ +/** + * 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 notify_ext.h + * @author rdb + * @date 2022-12-09 + */ + +#ifndef NOTIFY_EXT_H +#define NOTIFY_EXT_H + +#include "dtoolbase.h" + +#ifdef HAVE_PYTHON + +#include "extension.h" +#include "pnotify.h" +#include "py_panda.h" + +/** + * This class defines the extension methods for Notify, which are called + * instead of any C++ methods with the same prototype. + */ +template<> +class Extension : public ExtensionBase { +public: + void set_ostream_ptr(PyObject *ostream_ptr, bool delete_later); +}; + +#endif // HAVE_PYTHON + +#endif // NOTIFY_EXT_H diff --git a/dtool/src/prc/p3prc_ext_composite.cxx b/dtool/src/prc/p3prc_ext_composite.cxx index 223ef504cd..9cec7a1e2d 100644 --- a/dtool/src/prc/p3prc_ext_composite.cxx +++ b/dtool/src/prc/p3prc_ext_composite.cxx @@ -1,2 +1,3 @@ +#include "notify_ext.cxx" #include "streamReader_ext.cxx" #include "streamWriter_ext.cxx" diff --git a/dtool/src/prc/pnotify.h b/dtool/src/prc/pnotify.h index 6fe488862e..8ae8a48a08 100644 --- a/dtool/src/prc/pnotify.h +++ b/dtool/src/prc/pnotify.h @@ -35,7 +35,11 @@ PUBLISHED: Notify(); ~Notify(); +#if defined(CPPPARSER) && defined(HAVE_PYTHON) + EXTEND void set_ostream_ptr(PyObject *ostream_ptr, bool delete_later); +#else void set_ostream_ptr(std::ostream *ostream_ptr, bool delete_later); +#endif std::ostream *get_ostream_ptr() const; typedef bool AssertHandler(const char *expression, int line, From 75b12fe6e2dde568ea063a05db5feedfce8aa494 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Dec 2022 19:06:19 +0100 Subject: [PATCH 4/8] express: Fix a comment in datagram.cxx [skip ci] --- panda/src/express/datagram.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/panda/src/express/datagram.cxx b/panda/src/express/datagram.cxx index de7d4089dc..9ba36b8add 100644 --- a/panda/src/express/datagram.cxx +++ b/panda/src/express/datagram.cxx @@ -113,8 +113,8 @@ pad_bytes(size_t size) { // Now append the data. // It is very important that we *don't* do this reserve() operation. See - // the further comments in append_data(), below. _data.reserve(_data.size() - // + size); + // the further comments in append_data(), below. + //_data.reserve(_data.size() + size); while (size > 0) { _data.push_back('\0'); @@ -146,7 +146,8 @@ append_data(const void *data, size_t size) { // actually slows it down on Windows, which takes the reserve() request as a // fixed size the array should be set to (!) instead of as a minimum size to // guarantee. This forces the array to reallocate itself with *every* call - // to append_data! _data.reserve(_data.size() + size); + // to append_data! + //_data.reserve(_data.size() + size); _data.v().insert(_data.v().end(), (const unsigned char *)data, (const unsigned char *)data + size); From d48e23f234ced8351fec6f688bd9e17cd29f21ee Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Dec 2022 19:07:28 +0100 Subject: [PATCH 5/8] pstats: Indicate how to disable rate limit --- panda/src/pstatclient/config_pstatclient.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/panda/src/pstatclient/config_pstatclient.cxx b/panda/src/pstatclient/config_pstatclient.cxx index 51ab439e2a..aee2a29bae 100644 --- a/panda/src/pstatclient/config_pstatclient.cxx +++ b/panda/src/pstatclient/config_pstatclient.cxx @@ -33,7 +33,8 @@ ConfigVariableDouble pstats_max_rate ("pstats-max-rate", 1000.0, PRC_DESC("The maximum number of packets per second, per thread, to send " "to the remote PStats server. A packet is defined as a single " - "UDP packet, or each 1024 bytes of a TCP message.")); + "UDP packet, or each 1024 bytes of a TCP message. Set this to a " + "negative number to disable the limit.")); ConfigVariableBool pstats_threaded_write ("pstats-threaded-write", true, From 3254c6d329f040120eb32a26c8a0c8bf9f3d286d Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Dec 2022 19:08:49 +0100 Subject: [PATCH 6/8] pstats: Optimize PStatFrameData (de-)serialization This can be slow (hundreds of us, up to multiple ms) with large amounts of data points, this makes it an order of magnitude more efficient --- panda/src/pstatclient/pStatFrameData.cxx | 92 ++++++++++++++++++------ 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/panda/src/pstatclient/pStatFrameData.cxx b/panda/src/pstatclient/pStatFrameData.cxx index 9c58f8d29d..d847568e25 100644 --- a/panda/src/pstatclient/pStatFrameData.cxx +++ b/panda/src/pstatclient/pStatFrameData.cxx @@ -34,7 +34,6 @@ sort_time() { */ bool PStatFrameData:: write_datagram(Datagram &destination, PStatClient *client) const { - Data::const_iterator di; if (_time_data.size() >= 65536 || _level_data.size() >= 65536) { pstats_cat.info() << "Dropping frame with " << _time_data.size() @@ -43,16 +42,62 @@ write_datagram(Datagram &destination, PStatClient *client) const { return false; } +#if !defined(WORDS_BIGENDIAN) || defined(__GNUC__) + // Hand-roll this, significantly more efficient for many data points + size_t size = (_time_data.size() + _level_data.size()) * 6 + 4; + PTA_uchar array = destination.modify_array(); + size_t offset = array.size(); + array.resize(offset + size); + unsigned char *data = &array[0] + offset; + + uint16_t *ptr = (uint16_t *)data; + +#ifdef WORDS_BIGENDIAN + *ptr++ = __builtin_bswap16(_time_data.size()); + + for (const DataPoint &dp : _time_data) { + *ptr++ = __builtin_bswap16(dp._index); + PN_float32 v = (PN_float32)dp._value; + *(uint32_t *)ptr = __builtin_bswap32(reinterpret_cast(v)); + ptr += 2; + } + + *ptr++ = __builtin_bswap16(_level_data.size()); + for (const DataPoint &dp : _level_data) { + *ptr++ = __builtin_bswap16(dp._index); + PN_float32 v = (PN_float32)dp._value; + *(uint32_t *)ptr = __builtin_bswap32(reinterpret_cast(v)); + ptr += 2; + } +#else + *ptr++ = _time_data.size(); + + for (const DataPoint &dp : _time_data) { + *ptr++ = dp._index; + *(PN_float32 *)ptr = dp._value; + ptr += 2; + } + + *ptr++ = _level_data.size(); + for (const DataPoint &dp : _level_data) { + *ptr++ = dp._index; + *(PN_float32 *)ptr = dp._value; + ptr += 2; + } +#endif + +#else destination.add_uint16(_time_data.size()); - for (di = _time_data.begin(); di != _time_data.end(); ++di) { - destination.add_uint16((*di)._index); - destination.add_float32((*di)._value); + for (const DataPoint &dp : _time_data) { + destination.add_uint16(dp._index); + destination.add_float32(dp._value); } destination.add_uint16(_level_data.size()); - for (di = _level_data.begin(); di != _level_data.end(); ++di) { - destination.add_uint16((*di)._index); - destination.add_float32((*di)._value); + for (const DataPoint &dp : _level_data) { + destination.add_uint16(dp._index); + destination.add_float32(dp._value); } +#endif return true; } @@ -64,22 +109,25 @@ void PStatFrameData:: read_datagram(DatagramIterator &source, PStatClientVersion *) { clear(); - int i; - int time_size = source.get_uint16(); - for (i = 0; i < time_size; i++) { - nassertv(source.get_remaining_size() > 0); - DataPoint dp; - dp._index = source.get_uint16(); - dp._value = source.get_float32(); - _time_data.push_back(dp); + { + size_t time_size = source.get_uint16(); + _time_data.resize(time_size); + for (DataPoint &dp : _time_data) { + nassertv(source.get_remaining_size() > 0); + dp._index = source.get_uint16(); + dp._value = source.get_float32(); + } } - int level_size = source.get_uint16(); - for (i = 0; i < level_size; i++) { - nassertv(source.get_remaining_size() > 0); - DataPoint dp; - dp._index = source.get_uint16(); - dp._value = source.get_float32(); - _level_data.push_back(dp); + + { + size_t level_size = source.get_uint16(); + _level_data.resize(level_size); + for (DataPoint &dp : _level_data) { + nassertv(source.get_remaining_size() > 0); + dp._index = source.get_uint16(); + dp._value = source.get_float32(); + } } + nassertv(source.get_remaining_size() == 0); } From fb14c295256fc380a5f69ed628ac3c0460666a72 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Dec 2022 19:28:03 +0100 Subject: [PATCH 7/8] pstats: Server can handle new protocol version 2.3 It changes the following things: * Changes the counts in the PStatFrameData from uint16 to uint32, since I was hitting the value limit in some heavy frames with Python profiling enabled - no good reason for this limitation, so this allows removing it later * Adds a T_expire_thread message, which I can use later to fix #450 These features are not used on the client side, but will be used on master. Adding these changes here now makes it possible to use a master client with a 1.10.13 version of the server (can be useful if you can't compile Panda on the host). --- .../pstatclient/pStatClientControlMessage.cxx | 8 ++++++++ .../src/pstatclient/pStatClientControlMessage.h | 1 + panda/src/pstatclient/pStatFrameData.cxx | 16 +++++++++++++--- panda/src/pstatclient/pStatProperties.cxx | 1 + pandatool/src/pstatserver/pStatReader.cxx | 6 +++++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/panda/src/pstatclient/pStatClientControlMessage.cxx b/panda/src/pstatclient/pStatClientControlMessage.cxx index e7fd765547..5be7fc79f4 100644 --- a/panda/src/pstatclient/pStatClientControlMessage.cxx +++ b/panda/src/pstatclient/pStatClientControlMessage.cxx @@ -60,6 +60,10 @@ encode(Datagram &datagram) const { } break; + case T_expire_thread: + datagram.add_uint16(_first_thread_index); + break; + default: pstats_cat.error() << "Invalid PStatClientControlMessage::Type " << (int)_type << "\n"; @@ -111,6 +115,10 @@ decode(const Datagram &datagram, PStatClientVersion *version) { } break; + case T_expire_thread: + _first_thread_index = source.get_uint16(); + break; + case T_datagram: // Not, strictly speaking, a control message. return false; diff --git a/panda/src/pstatclient/pStatClientControlMessage.h b/panda/src/pstatclient/pStatClientControlMessage.h index 97e7dede98..0cfa572385 100644 --- a/panda/src/pstatclient/pStatClientControlMessage.h +++ b/panda/src/pstatclient/pStatClientControlMessage.h @@ -39,6 +39,7 @@ public: T_hello, T_define_collectors, T_define_threads, + T_expire_thread, T_invalid }; diff --git a/panda/src/pstatclient/pStatFrameData.cxx b/panda/src/pstatclient/pStatFrameData.cxx index d847568e25..af4c71f73e 100644 --- a/panda/src/pstatclient/pStatFrameData.cxx +++ b/panda/src/pstatclient/pStatFrameData.cxx @@ -106,11 +106,16 @@ write_datagram(Datagram &destination, PStatClient *client) const { * Extracts the FrameData definition from the datagram. */ void PStatFrameData:: -read_datagram(DatagramIterator &source, PStatClientVersion *) { +read_datagram(DatagramIterator &source, PStatClientVersion *version) { clear(); { - size_t time_size = source.get_uint16(); + size_t time_size; + if (version->is_at_least(3, 2)) { + time_size = source.get_uint32(); + } else { + time_size = source.get_uint16(); + } _time_data.resize(time_size); for (DataPoint &dp : _time_data) { nassertv(source.get_remaining_size() > 0); @@ -120,7 +125,12 @@ read_datagram(DatagramIterator &source, PStatClientVersion *) { } { - size_t level_size = source.get_uint16(); + size_t level_size; + if (version->is_at_least(3, 2)) { + level_size = source.get_uint32(); + } else { + level_size = source.get_uint16(); + } _level_data.resize(level_size); for (DataPoint &dp : _level_data) { nassertv(source.get_remaining_size() > 0); diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 9537f50a90..13b36f2ddd 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -31,6 +31,7 @@ static const int current_pstat_minor_version = 0; // Incremented to 2.1 on 5/21/01 to add support for TCP frame data. // Incremented to 3.0 on 4/28/05 to bump TCP headers to 32 bits. // Incremented to 3.1 on 11/29/22 to support nested start/stop pairs. +// Incremented to 3.2 on 12/10/22 to use 32-bit data counts, T_expire_thread. /** * Returns the current major version number of the PStats protocol. This is diff --git a/pandatool/src/pstatserver/pStatReader.cxx b/pandatool/src/pstatserver/pStatReader.cxx index 7c1fe6fca5..463e298f7b 100644 --- a/pandatool/src/pstatserver/pStatReader.cxx +++ b/pandatool/src/pstatserver/pStatReader.cxx @@ -194,7 +194,7 @@ handle_client_control_message(const PStatClientControlMessage &message) { if (message._major_version != server_major_version || (message._major_version == server_major_version && message._minor_version > server_minor_version && - (message._major_version != 3 || message._minor_version != 1))) { + (message._major_version != 3 || message._minor_version > 2))) { _monitor->bad_version(message._client_hostname, message._client_progname, message._major_version, message._minor_version, server_major_version, server_minor_version); @@ -225,6 +225,10 @@ handle_client_control_message(const PStatClientControlMessage &message) { } break; + case PStatClientControlMessage::T_expire_thread: + // Ignore for now. + break; + default: nout << "Invalid control message received from client.\n"; } From cd984732b76238c24acb3f872e1231148aaaa43c Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Dec 2022 19:38:11 +0100 Subject: [PATCH 8/8] pgraph: Make `bounds_type` property writable --- panda/src/pgraph/pandaNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 325b5ea508..83b34e709d 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -290,7 +290,7 @@ PUBLISHED: // bounding volumes. void set_bounds_type(BoundingVolume::BoundsType bounds_type); BoundingVolume::BoundsType get_bounds_type() const; - MAKE_PROPERTY(bounds_type, get_bounds_type); + MAKE_PROPERTY(bounds_type, get_bounds_type, set_bounds_type); void set_bounds(const BoundingVolume *volume); void set_bound(const BoundingVolume *volume);