From ba9ea8ea2736a3b0731b30ef5d40a37833b0ce15 Mon Sep 17 00:00:00 2001 From: Younguk Kim Date: Wed, 19 Sep 2018 09:55:29 +0900 Subject: [PATCH 01/18] chan: add missing export macro --- panda/src/chan/partBundle.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index f8fda4bd47..7e9d21259d 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -248,8 +248,8 @@ inline std::ostream &operator <<(std::ostream &out, const PartBundle &bundle) { return out; } -std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type); -std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type); +EXPCL_PANDA_CHAN std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type); +EXPCL_PANDA_CHAN std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type); #include "partBundle.I" From b64e850539a9df1f26f06c359e01db227089ffd6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 19 Sep 2018 13:16:45 -0600 Subject: [PATCH 02/18] egg(2pg): Fix missing EXPCL_PANDA_EGG(2PG) --- panda/src/egg/eggMesher.h | 2 +- panda/src/egg/eggMesherEdge.h | 2 +- panda/src/egg/eggMesherFanMaker.h | 2 +- panda/src/egg/eggMesherStrip.h | 2 +- panda/src/egg2pg/eggBinner.h | 2 +- panda/src/egg2pg/eggLoader.h | 2 +- panda/src/egg2pg/eggRenderState.h | 2 +- panda/src/egg2pg/eggSaver.h | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/panda/src/egg/eggMesher.h b/panda/src/egg/eggMesher.h index 5d1a3e0070..5b4d9b63d1 100644 --- a/panda/src/egg/eggMesher.h +++ b/panda/src/egg/eggMesher.h @@ -30,7 +30,7 @@ * connectivity, and generates a set of EggTriangleStrips that represent the * same geometry. */ -class EggMesher { +class EXPCL_PANDA_EGG EggMesher { public: EggMesher(); diff --git a/panda/src/egg/eggMesherEdge.h b/panda/src/egg/eggMesherEdge.h index 5ef30c1c69..17e463b79d 100644 --- a/panda/src/egg/eggMesherEdge.h +++ b/panda/src/egg/eggMesherEdge.h @@ -26,7 +26,7 @@ class EggMesherStrip; * connected triangles. The edge is actually represented as a pair of vertex * indices into the same vertex pool. */ -class EggMesherEdge { +class EXPCL_PANDA_EGG EggMesherEdge { public: INLINE EggMesherEdge(int vi_a, int vi_b); INLINE EggMesherEdge(const EggMesherEdge ©); diff --git a/panda/src/egg/eggMesherFanMaker.h b/panda/src/egg/eggMesherFanMaker.h index f320b2858b..894462b493 100644 --- a/panda/src/egg/eggMesherFanMaker.h +++ b/panda/src/egg/eggMesherFanMaker.h @@ -31,7 +31,7 @@ class EggMesher; * This class is used by EggMesher::find_fans() to attempt to make an * EggTriangleFan out of the polygons connected to the indicated vertex. */ -class EggMesherFanMaker { +class EXPCL_PANDA_EGG EggMesherFanMaker { public: typedef plist Edges; typedef plist Strips; diff --git a/panda/src/egg/eggMesherStrip.h b/panda/src/egg/eggMesherStrip.h index 6064388ce0..72a2e54fa3 100644 --- a/panda/src/egg/eggMesherStrip.h +++ b/panda/src/egg/eggMesherStrip.h @@ -27,7 +27,7 @@ class EggMesherEdge; * mesher. It might also represent a single polygon such as a triangle or * quad, since that's how strips generally start out. */ -class EggMesherStrip { +class EXPCL_PANDA_EGG EggMesherStrip { public: enum PrimType { PT_poly, diff --git a/panda/src/egg2pg/eggBinner.h b/panda/src/egg2pg/eggBinner.h index a7e3c10894..007f434190 100644 --- a/panda/src/egg2pg/eggBinner.h +++ b/panda/src/egg2pg/eggBinner.h @@ -27,7 +27,7 @@ class EggLoader; * It is used to collect similar polygons together for a Geom, as well as to * group related LOD children together under a single LOD node. */ -class EggBinner : public EggBinMaker { +class EXPCL_PANDA_EGG2PG EggBinner : public EggBinMaker { public: // The BinNumber serves to identify why a particular EggBin was created. enum BinNumber { diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index aa36475e05..eb81317bd8 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -64,7 +64,7 @@ class CharacterMaker; * * This class isn't exported from this package. */ -class EggLoader { +class EXPCL_PANDA_EGG2PG EggLoader { public: EggLoader(); EggLoader(const EggData *data); diff --git a/panda/src/egg2pg/eggRenderState.h b/panda/src/egg2pg/eggRenderState.h index 44993dffcf..82af78be70 100644 --- a/panda/src/egg2pg/eggRenderState.h +++ b/panda/src/egg2pg/eggRenderState.h @@ -36,7 +36,7 @@ class EggMaterial; * should be assigned to each primitive. It is assigned to EggPrimitive * objects via the EggBinner. */ -class EggRenderState : public EggUserData { +class EXPCL_PANDA_EGG2PG EggRenderState : public EggUserData { public: INLINE EggRenderState(EggLoader &loader); INLINE void add_attrib(const RenderAttrib *attrib); diff --git a/panda/src/egg2pg/eggSaver.h b/panda/src/egg2pg/eggSaver.h index 8041d346cc..48a9693dec 100644 --- a/panda/src/egg2pg/eggSaver.h +++ b/panda/src/egg2pg/eggSaver.h @@ -50,7 +50,7 @@ class EggVertex; * complete (some Panda or egg constructs are not fully supported by this * class). */ -class EggSaver { +class EXPCL_PANDA_EGG2PG EggSaver { PUBLISHED: EggSaver(EggData *data = nullptr); From aacafe7be3cd8de64190535b3b454c5146bc69a7 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 23 Sep 2018 13:03:47 +0200 Subject: [PATCH 03/18] dtoolutil: give DSearchPath a defaulted move constructor --- dtool/src/dtoolutil/dSearchPath.cxx | 31 ----------------------------- dtool/src/dtoolutil/dSearchPath.h | 11 ++++++---- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/dtool/src/dtoolutil/dSearchPath.cxx b/dtool/src/dtoolutil/dSearchPath.cxx index 4a094f1d98..c17b3d61ec 100644 --- a/dtool/src/dtoolutil/dSearchPath.cxx +++ b/dtool/src/dtoolutil/dSearchPath.cxx @@ -116,13 +116,6 @@ write(ostream &out, int indent_level) const { } } -/** - * Creates an empty search path. - */ -DSearchPath:: -DSearchPath() { -} - /** * */ @@ -139,30 +132,6 @@ DSearchPath(const Filename &directory) { append_directory(directory); } -/** - * - */ -DSearchPath:: -DSearchPath(const DSearchPath ©) : - _directories(copy._directories) -{ -} - -/** - * - */ -void DSearchPath:: -operator = (const DSearchPath ©) { - _directories = copy._directories; -} - -/** - * - */ -DSearchPath:: -~DSearchPath() { -} - /** * Removes all the directories from the search list. */ diff --git a/dtool/src/dtoolutil/dSearchPath.h b/dtool/src/dtoolutil/dSearchPath.h index 8cb769c378..7258a71f26 100644 --- a/dtool/src/dtoolutil/dSearchPath.h +++ b/dtool/src/dtoolutil/dSearchPath.h @@ -52,12 +52,15 @@ PUBLISHED: Files _files; }; - DSearchPath(); + DSearchPath() = default; DSearchPath(const std::string &path, const std::string &separator = std::string()); DSearchPath(const Filename &directory); - DSearchPath(const DSearchPath ©); - void operator = (const DSearchPath ©); - ~DSearchPath(); + DSearchPath(const DSearchPath ©) = default; + DSearchPath(DSearchPath &&from) = default; + ~DSearchPath() = default; + + DSearchPath &operator = (const DSearchPath ©) = default; + DSearchPath &operator = (DSearchPath &&from) = default; void clear(); void append_directory(const Filename &directory); From d6b7abedfe9d03c483f1973e34995c9201da46d0 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 23 Sep 2018 13:05:52 +0200 Subject: [PATCH 04/18] prc: fix some race conditions querying bool and searchpath vars This is not perfect, and we need to more thoroughly address thread safety in the PRC system, but it will nonetheless address a lot of the race condition issues when querying these variables from two threads at the same time. --- dtool/src/prc/configDeclaration.cxx | 37 ++++++++++++++++++++++ dtool/src/prc/configDeclaration.h | 3 ++ dtool/src/prc/configVariableBool.cxx | 16 ++++++++-- dtool/src/prc/configVariableFilename.cxx | 10 +----- dtool/src/prc/configVariableSearchPath.I | 22 +++++++++++-- dtool/src/prc/configVariableSearchPath.cxx | 13 ++------ dtool/src/prc/configVariableSearchPath.h | 5 +-- 7 files changed, 80 insertions(+), 26 deletions(-) diff --git a/dtool/src/prc/configDeclaration.cxx b/dtool/src/prc/configDeclaration.cxx index 7cbfa5ba6e..b401df73d4 100644 --- a/dtool/src/prc/configDeclaration.cxx +++ b/dtool/src/prc/configDeclaration.cxx @@ -16,6 +16,8 @@ #include "config_prc.h" #include "pstrtod.h" #include "string_utils.h" +#include "executionEnvironment.h" +#include "mutexImpl.h" using std::string; @@ -131,6 +133,41 @@ set_double_word(size_t n, double value) { invalidate_cache(); } +/** + * Interprets the string value as a filename and returns it, with any + * variables expanded. + */ +Filename ConfigDeclaration:: +get_filename_value() const { + // Since we are about to set THIS_PRC_DIR globally, we need to ensure that + // no two threads call this method at the same time. + // NB. MSVC doesn't guarantee that this mutex is initialized in a + // thread-safe manner. But chances are that the first time this is called + // is at static init time, when there is no risk of data races. + static MutexImpl lock; + + string str = _string_value; + + // Are there any variables to be expanded? + if (str.find('$') != string::npos) { + Filename page_filename(_page->get_name()); + Filename page_dirname = page_filename.get_dirname(); + + lock.lock(); + ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); + str = ExecutionEnvironment::expand_string(str); + ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); + lock.unlock(); + } + + Filename fn; + if (!str.empty()) { + fn = Filename::from_os_specific(str); + fn.make_true_case(); + } + return fn; +} + /** * */ diff --git a/dtool/src/prc/configDeclaration.h b/dtool/src/prc/configDeclaration.h index 38129670d5..11cb97728a 100644 --- a/dtool/src/prc/configDeclaration.h +++ b/dtool/src/prc/configDeclaration.h @@ -19,6 +19,7 @@ #include "configPage.h" #include "vector_string.h" #include "numeric_types.h" +#include "filename.h" #include @@ -68,6 +69,8 @@ PUBLISHED: void set_int64_word(size_t n, int64_t value); void set_double_word(size_t n, double value); + Filename get_filename_value() const; + INLINE int get_decl_seq() const; void output(std::ostream &out) const; diff --git a/dtool/src/prc/configVariableBool.cxx b/dtool/src/prc/configVariableBool.cxx index 7fb604642d..701f40c65d 100644 --- a/dtool/src/prc/configVariableBool.cxx +++ b/dtool/src/prc/configVariableBool.cxx @@ -18,6 +18,18 @@ */ void ConfigVariableBool:: reload_value() const { - mark_cache_valid(_local_modified); - _cache = get_bool_word(0); + // NB. MSVC doesn't guarantee that this mutex is initialized in a + // thread-safe manner. But chances are that the first time this is called + // is at static init time, when there is no risk of data races. + static MutexImpl lock; + lock.lock(); + + // We check again for cache validity since another thread may have beaten + // us to the punch while we were waiting for the lock. + if (!is_cache_valid(_local_modified)) { + _cache = get_bool_word(0); + mark_cache_valid(_local_modified); + } + + lock.unlock(); } diff --git a/dtool/src/prc/configVariableFilename.cxx b/dtool/src/prc/configVariableFilename.cxx index 4c8941babd..1015a32e66 100644 --- a/dtool/src/prc/configVariableFilename.cxx +++ b/dtool/src/prc/configVariableFilename.cxx @@ -29,17 +29,9 @@ reload_cache() { // us to the punch while we were waiting for the lock. if (!is_cache_valid(_local_modified)) { nassertv(_core != nullptr); - const ConfigDeclaration *decl = _core->get_declaration(0); - const ConfigPage *page = decl->get_page(); - - Filename page_filename(page->get_name()); - Filename page_dirname = page_filename.get_dirname(); - ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); - - _cache = Filename::expand_from(decl->get_string_value()); - ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); + _cache = decl->get_filename_value(); mark_cache_valid(_local_modified); } lock.unlock(); diff --git a/dtool/src/prc/configVariableSearchPath.I b/dtool/src/prc/configVariableSearchPath.I index 46e542e24c..89a9ac7df9 100644 --- a/dtool/src/prc/configVariableSearchPath.I +++ b/dtool/src/prc/configVariableSearchPath.I @@ -93,20 +93,24 @@ INLINE ConfigVariableSearchPath:: * Returns the variable's value. */ INLINE ConfigVariableSearchPath:: -operator const DSearchPath & () const { +operator DSearchPath () const { return get_value(); } /** * */ -INLINE const DSearchPath &ConfigVariableSearchPath:: +INLINE DSearchPath ConfigVariableSearchPath:: get_value() const { TAU_PROFILE("const DSearchPath &ConfigVariableSearchPath::get_value() const", " ", TAU_USER); + DSearchPath value; + _lock.lock(); if (!is_cache_valid(_local_modified)) { ((ConfigVariableSearchPath *)this)->reload_search_path(); } - return _cache; + value = _cache; + _lock.unlock(); + return value; } /** @@ -123,6 +127,7 @@ get_default_value() const { */ INLINE bool ConfigVariableSearchPath:: clear_local_value() { + _lock.lock(); nassertr(_core != nullptr, false); bool any_to_clear = !_prefix.is_empty() || _postfix.is_empty(); @@ -134,6 +139,7 @@ clear_local_value() { } _local_modified = initial_invalid_cache(); + _lock.unlock(); return any_to_clear; } @@ -151,8 +157,10 @@ clear() { */ INLINE void ConfigVariableSearchPath:: append_directory(const Filename &directory) { + _lock.lock(); _postfix.append_directory(directory); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -160,8 +168,10 @@ append_directory(const Filename &directory) { */ INLINE void ConfigVariableSearchPath:: prepend_directory(const Filename &directory) { + _lock.lock(); _prefix.prepend_directory(directory); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -170,8 +180,10 @@ prepend_directory(const Filename &directory) { */ INLINE void ConfigVariableSearchPath:: append_path(const std::string &path, const std::string &separator) { + _lock.lock(); _postfix.append_path(path, separator); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -180,8 +192,10 @@ append_path(const std::string &path, const std::string &separator) { */ INLINE void ConfigVariableSearchPath:: append_path(const DSearchPath &path) { + _lock.lock(); _postfix.append_path(path); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** @@ -190,8 +204,10 @@ append_path(const DSearchPath &path) { */ INLINE void ConfigVariableSearchPath:: prepend_path(const DSearchPath &path) { + _lock.lock(); _prefix.prepend_path(path); _local_modified = initial_invalid_cache(); + _lock.unlock(); } /** diff --git a/dtool/src/prc/configVariableSearchPath.cxx b/dtool/src/prc/configVariableSearchPath.cxx index 2231626a7a..32620253b3 100644 --- a/dtool/src/prc/configVariableSearchPath.cxx +++ b/dtool/src/prc/configVariableSearchPath.cxx @@ -27,17 +27,10 @@ reload_search_path() { size_t num_unique_references = _core->get_num_unique_references(); for (size_t i = 0; i < num_unique_references; i++) { const ConfigDeclaration *decl = _core->get_unique_reference(i); - const ConfigPage *page = decl->get_page(); - Filename page_filename(page->get_name()); - Filename page_dirname = page_filename.get_dirname(); - ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific()); - std::string expanded = ExecutionEnvironment::expand_string(decl->get_string_value()); - ExecutionEnvironment::clear_shadow("THIS_PRC_DIR"); - if (!expanded.empty()) { - Filename dir = Filename::from_os_specific(expanded); - dir.make_true_case(); - _cache.append_directory(dir); + Filename fn = decl->get_filename_value(); + if (!fn.empty()) { + _cache.append_directory(std::move(fn)); } } diff --git a/dtool/src/prc/configVariableSearchPath.h b/dtool/src/prc/configVariableSearchPath.h index 02a4fd65c3..12ad1c54c9 100644 --- a/dtool/src/prc/configVariableSearchPath.h +++ b/dtool/src/prc/configVariableSearchPath.h @@ -48,8 +48,8 @@ PUBLISHED: int flags = 0); INLINE ~ConfigVariableSearchPath(); - INLINE operator const DSearchPath & () const; - INLINE const DSearchPath &get_value() const; + INLINE operator DSearchPath () const; + INLINE DSearchPath get_value() const; INLINE const DSearchPath &get_default_value() const; MAKE_PROPERTY(value, get_value); MAKE_PROPERTY(default_value, get_default_value); @@ -81,6 +81,7 @@ PUBLISHED: private: void reload_search_path(); + mutable MutexImpl _lock; DSearchPath _default_value; DSearchPath _prefix, _postfix; From 77724f49dc4036fe5b3151a5fd2f44d354fc1ae1 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 23 Sep 2018 13:50:06 +0200 Subject: [PATCH 05/18] dtoolbase: remove TypeHandle::_none symbol, no longer needed --- dtool/src/dtoolbase/typeHandle.cxx | 3 --- dtool/src/dtoolbase/typeHandle.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx index 1b96352723..15fac6b60f 100644 --- a/dtool/src/dtoolbase/typeHandle.cxx +++ b/dtool/src/dtoolbase/typeHandle.cxx @@ -15,9 +15,6 @@ #include "typeRegistryNode.h" #include "atomicAdjust.h" -// This is initialized to zero by static initialization. -TypeHandle TypeHandle::_none; - /** * Returns the total allocated memory used by objects of this type, for the * indicated memory class. This is only updated if track-memory-usage is set diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index da66074750..97dc445443 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -147,9 +147,6 @@ public: private: constexpr TypeHandle(int index); - // Only kept temporarily for ABI compatibility. - static TypeHandle _none; - int _index; friend class TypeRegistry; }; From a3a7c0cf9da82be9ab59e164f7a05b5715eb333b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 23 Sep 2018 14:21:19 +0200 Subject: [PATCH 06/18] parser-inc: add more POSIX system header stubs --- dtool/src/parser-inc/dirent.h | 3 +++ dtool/src/parser-inc/sys/inotify.h | 1 + dtool/src/parser-inc/sys/ioctl.h | 3 +++ dtool/src/parser-inc/sys/mman.h | 3 +++ dtool/src/parser-inc/sys/select.h | 1 + dtool/src/parser-inc/sys/sysinfo.h | 1 + 6 files changed, 12 insertions(+) create mode 100644 dtool/src/parser-inc/dirent.h create mode 100644 dtool/src/parser-inc/sys/inotify.h create mode 100644 dtool/src/parser-inc/sys/ioctl.h create mode 100644 dtool/src/parser-inc/sys/mman.h create mode 100644 dtool/src/parser-inc/sys/select.h create mode 100644 dtool/src/parser-inc/sys/sysinfo.h diff --git a/dtool/src/parser-inc/dirent.h b/dtool/src/parser-inc/dirent.h new file mode 100644 index 0000000000..5c0877bfcd --- /dev/null +++ b/dtool/src/parser-inc/dirent.h @@ -0,0 +1,3 @@ +typedef struct __dirstream DIR; +struct dirent; +typedef unsigned long ino_t; diff --git a/dtool/src/parser-inc/sys/inotify.h b/dtool/src/parser-inc/sys/inotify.h new file mode 100644 index 0000000000..52ef884e79 --- /dev/null +++ b/dtool/src/parser-inc/sys/inotify.h @@ -0,0 +1 @@ +struct inotify_event; diff --git a/dtool/src/parser-inc/sys/ioctl.h b/dtool/src/parser-inc/sys/ioctl.h new file mode 100644 index 0000000000..d57d62d983 --- /dev/null +++ b/dtool/src/parser-inc/sys/ioctl.h @@ -0,0 +1,3 @@ +struct winsize; +struct termio; + diff --git a/dtool/src/parser-inc/sys/mman.h b/dtool/src/parser-inc/sys/mman.h new file mode 100644 index 0000000000..c540c66b78 --- /dev/null +++ b/dtool/src/parser-inc/sys/mman.h @@ -0,0 +1,3 @@ +#include + +struct posix_typed_mem_info; diff --git a/dtool/src/parser-inc/sys/select.h b/dtool/src/parser-inc/sys/select.h new file mode 100644 index 0000000000..18a03a58e7 --- /dev/null +++ b/dtool/src/parser-inc/sys/select.h @@ -0,0 +1 @@ +#include diff --git a/dtool/src/parser-inc/sys/sysinfo.h b/dtool/src/parser-inc/sys/sysinfo.h new file mode 100644 index 0000000000..72e2bef1b2 --- /dev/null +++ b/dtool/src/parser-inc/sys/sysinfo.h @@ -0,0 +1 @@ +struct sysinfo; From 3ac50a23473ce251019da9fe3a880763cf9471bf Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 23 Sep 2018 14:22:41 +0200 Subject: [PATCH 07/18] movies: fix crash on simultaneous threaded audio/video load --- panda/src/movies/movieTypeRegistry.cxx | 15 +++++++++++++++ panda/src/movies/movieTypeRegistry.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 012deff1cd..51468ac2a4 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -29,6 +29,8 @@ PT(MovieAudio) MovieTypeRegistry:: make_audio(const Filename &name) { string ext = downcase(name.get_extension()); + _audio_lock.lock(); + // Make sure that the list of audio types has been read in. load_audio_types(); @@ -41,6 +43,7 @@ make_audio(const Filename &name) { // Explicit extension is preferred over catch-all. if (_audio_type_registry.count(ext)) { MakeAudioFunc func = _audio_type_registry[ext]; + _audio_lock.unlock(); return (*func)(name); } @@ -53,12 +56,14 @@ make_audio(const Filename &name) { if (_audio_type_registry.count("*")) { MakeAudioFunc func = _audio_type_registry["*"]; + _audio_lock.unlock(); return (*func)(name); } movies_cat.error() << "Support for audio files with extension ." << ext << " was not enabled.\n"; + _audio_lock.unlock(); return new MovieAudio("Load-Failure Stub"); } @@ -68,6 +73,7 @@ make_audio(const Filename &name) { */ void MovieTypeRegistry:: register_audio_type(MakeAudioFunc func, const string &extensions) { + ReMutexHolder holder(_audio_lock); vector_string words; extract_words(downcase(extensions), words); @@ -89,6 +95,7 @@ register_audio_type(MakeAudioFunc func, const string &extensions) { */ void MovieTypeRegistry:: load_audio_types() { + ReMutexHolder holder(_audio_lock); static bool audio_types_loaded = false; if (!audio_types_loaded) { @@ -145,6 +152,8 @@ PT(MovieVideo) MovieTypeRegistry:: make_video(const Filename &name) { string ext = downcase(name.get_extension()); + _video_lock.lock(); + // Make sure that the list of video types has been read in. load_video_types(); @@ -157,6 +166,7 @@ make_video(const Filename &name) { // Explicit extension is preferred over catch-all. if (_video_type_registry.count(ext)) { MakeVideoFunc func = _video_type_registry[ext]; + _video_lock.unlock(); return (*func)(name); } @@ -169,12 +179,14 @@ make_video(const Filename &name) { if (_video_type_registry.count("*")) { MakeVideoFunc func = _video_type_registry["*"]; + _video_lock.unlock(); return (*func)(name); } movies_cat.error() << "Support for video files with extension ." << ext << " was not enabled.\n"; + _video_lock.unlock(); return new MovieVideo("Load-Failure Stub"); } @@ -184,6 +196,7 @@ make_video(const Filename &name) { */ void MovieTypeRegistry:: register_video_type(MakeVideoFunc func, const string &extensions) { + ReMutexHolder holder(_video_lock); vector_string words; extract_words(downcase(extensions), words); @@ -205,6 +218,7 @@ register_video_type(MakeVideoFunc func, const string &extensions) { */ void MovieTypeRegistry:: load_video_types() { + ReMutexHolder holder(_video_lock); static bool video_types_loaded = false; if (!video_types_loaded) { @@ -259,6 +273,7 @@ load_video_types() { */ void MovieTypeRegistry:: load_movie_library(const string &name) { + ReMutexHolder holder(_video_lock); Filename dlname = Filename::dso_filename("lib" + name + ".so"); movies_cat.info() << "loading video type module: " << name << endl; diff --git a/panda/src/movies/movieTypeRegistry.h b/panda/src/movies/movieTypeRegistry.h index ee5fc14da2..c9ddb53f15 100644 --- a/panda/src/movies/movieTypeRegistry.h +++ b/panda/src/movies/movieTypeRegistry.h @@ -19,6 +19,7 @@ #include "movieVideo.h" #include "filename.h" #include "pmap.h" +#include "reMutex.h" /** * This class records the different types of MovieAudio and MovieVideo that @@ -43,9 +44,11 @@ public: private: static MovieTypeRegistry *_global_ptr; + ReMutex _audio_lock; pmap _audio_type_registry; pmap _deferred_audio_types; + ReMutex _video_lock; pmap _video_type_registry; pmap _deferred_video_types; }; From 5457d76b947273bf0eae561b2de3e52fc1df08a4 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 11:08:22 +0200 Subject: [PATCH 08/18] text: slight perf improvement for TextNode card/frame generation --- panda/src/text/textNode.cxx | 109 +++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index 92001d7b40..a68ebba812 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -255,11 +255,16 @@ is_whitespace(wchar_t character) const { */ PN_stdfloat TextNode:: calc_width(const std::wstring &line) const { + TextFont *font = get_font(); + if (font == nullptr) { + return 0.0f; + } + PN_stdfloat width = 0.0f; std::wstring::const_iterator si; for (si = line.begin(); si != line.end(); ++si) { - width += calc_width(*si); + width += TextAssembler::calc_width(*si, *this); } return width; @@ -730,15 +735,16 @@ make_frame() { CPT(RenderState) state = RenderState::make(thick); PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3(), _usage_hint); + vdata->unclean_set_num_rows(4); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); - vertex.add_data3(left, 0.0f, top); - vertex.add_data3(left, 0.0f, bottom); - vertex.add_data3(right, 0.0f, bottom); - vertex.add_data3(right, 0.0f, top); + vertex.set_data3(left, 0.0f, top); + vertex.set_data3(left, 0.0f, bottom); + vertex.set_data3(right, 0.0f, bottom); + vertex.set_data3(right, 0.0f, top); - PT(GeomLinestrips) frame = new GeomLinestrips(get_usage_hint()); + PT(GeomLinestrips) frame = new GeomLinestrips(_usage_hint); frame->add_consecutive_vertices(0, 4); frame->add_vertex(0); frame->close_primitive(); @@ -772,19 +778,20 @@ make_card() { PN_stdfloat top = dimensions[3]; PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3t2(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3t2(), _usage_hint); + vdata->unclean_set_num_rows(4); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); - vertex.add_data3(left, 0.0f, top); - vertex.add_data3(left, 0.0f, bottom); - vertex.add_data3(right, 0.0f, top); - vertex.add_data3(right, 0.0f, bottom); + vertex.set_data3(left, 0.0f, top); + vertex.set_data3(left, 0.0f, bottom); + vertex.set_data3(right, 0.0f, top); + vertex.set_data3(right, 0.0f, bottom); - texcoord.add_data2(0.0f, 1.0f); - texcoord.add_data2(0.0f, 0.0f); - texcoord.add_data2(1.0f, 1.0f); - texcoord.add_data2(1.0f, 0.0f); + texcoord.set_data2(0.0f, 1.0f); + texcoord.set_data2(0.0f, 0.0f); + texcoord.set_data2(1.0f, 1.0f); + texcoord.set_data2(1.0f, 0.0f); PT(GeomTristrips) card = new GeomTristrips(get_usage_hint()); card->add_consecutive_vertices(0, 4); @@ -820,57 +827,59 @@ make_card_with_border() { */ PT(GeomVertexData) vdata = new GeomVertexData - ("text", GeomVertexFormat::get_v3t2(), get_usage_hint()); + ("text", GeomVertexFormat::get_v3t2(), _usage_hint); + vdata->unclean_set_num_rows(16); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); // verts 1,2,3,4 - vertex.add_data3(left, 0.02, top); - vertex.add_data3(left, 0.02, top - _card_border_size); - vertex.add_data3(left + _card_border_size, 0.02, top); - vertex.add_data3(left + _card_border_size, 0.02, + vertex.set_data3(left, 0.02, top); + vertex.set_data3(left, 0.02, top - _card_border_size); + vertex.set_data3(left + _card_border_size, 0.02, top); + vertex.set_data3(left + _card_border_size, 0.02, top - _card_border_size); // verts 5,6,7,8 - vertex.add_data3(right - _card_border_size, 0.02, top); - vertex.add_data3(right - _card_border_size, 0.02, + vertex.set_data3(right - _card_border_size, 0.02, top); + vertex.set_data3(right - _card_border_size, 0.02, top - _card_border_size); - vertex.add_data3(right, 0.02, top); - vertex.add_data3(right, 0.02, top - _card_border_size); + vertex.set_data3(right, 0.02, top); + vertex.set_data3(right, 0.02, top - _card_border_size); // verts 9,10,11,12 - vertex.add_data3(left, 0.02, bottom + _card_border_size); - vertex.add_data3(left, 0.02, bottom); - vertex.add_data3(left + _card_border_size, 0.02, + vertex.set_data3(left, 0.02, bottom + _card_border_size); + vertex.set_data3(left, 0.02, bottom); + vertex.set_data3(left + _card_border_size, 0.02, bottom + _card_border_size); - vertex.add_data3(left + _card_border_size, 0.02, bottom); + vertex.set_data3(left + _card_border_size, 0.02, bottom); // verts 13,14,15,16 - vertex.add_data3(right - _card_border_size, 0.02, + vertex.set_data3(right - _card_border_size, 0.02, bottom + _card_border_size); - vertex.add_data3(right - _card_border_size, 0.02, bottom); - vertex.add_data3(right, 0.02, bottom + _card_border_size); - vertex.add_data3(right, 0.02, bottom); + vertex.set_data3(right - _card_border_size, 0.02, bottom); + vertex.set_data3(right, 0.02, bottom + _card_border_size); + vertex.set_data3(right, 0.02, bottom); - texcoord.add_data2(0.0f, 1.0f); //1 - texcoord.add_data2(0.0f, 1.0f - _card_border_uv_portion); //2 - texcoord.add_data2(0.0f + _card_border_uv_portion, 1.0f); //3 - texcoord.add_data2(0.0f + _card_border_uv_portion, + texcoord.set_data2(0.0f, 1.0f); //1 + texcoord.set_data2(0.0f, 1.0f - _card_border_uv_portion); //2 + texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f); //3 + texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f - _card_border_uv_portion); //4 - texcoord.add_data2(1.0f -_card_border_uv_portion, 1.0f); //5 - texcoord.add_data2(1.0f -_card_border_uv_portion, + texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f); //5 + texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f - _card_border_uv_portion); //6 - texcoord.add_data2(1.0f, 1.0f); //7 - texcoord.add_data2(1.0f, 1.0f - _card_border_uv_portion); //8 + texcoord.set_data2(1.0f, 1.0f); //7 + texcoord.set_data2(1.0f, 1.0f - _card_border_uv_portion); //8 - texcoord.add_data2(0.0f, _card_border_uv_portion); //9 - texcoord.add_data2(0.0f, 0.0f); //10 - texcoord.add_data2(_card_border_uv_portion, _card_border_uv_portion); //11 - texcoord.add_data2(_card_border_uv_portion, 0.0f); //12 + texcoord.set_data2(0.0f, _card_border_uv_portion); //9 + texcoord.set_data2(0.0f, 0.0f); //10 + texcoord.set_data2(_card_border_uv_portion, _card_border_uv_portion); //11 + texcoord.set_data2(_card_border_uv_portion, 0.0f); //12 - texcoord.add_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13 - texcoord.add_data2(1.0f - _card_border_uv_portion, 0.0f);//14 - texcoord.add_data2(1.0f, _card_border_uv_portion);//15 - texcoord.add_data2(1.0f, 0.0f);//16 + texcoord.set_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13 + texcoord.set_data2(1.0f - _card_border_uv_portion, 0.0f);//14 + texcoord.set_data2(1.0f, _card_border_uv_portion);//15 + texcoord.set_data2(1.0f, 0.0f);//16 - PT(GeomTristrips) card = new GeomTristrips(get_usage_hint()); + PT(GeomTristrips) card = new GeomTristrips(_usage_hint); + card->reserve_num_vertices(24); // tristrip #1 card->add_consecutive_vertices(0, 8); From 1e084e0b2b16046ed4d996b086880497d64d3ad2 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 11:35:16 +0200 Subject: [PATCH 09/18] text: add thread safety to TextNode This does not 100% cover all the base class TextProperties, however, so you still need to be careful not to access those from two threads at once. --- panda/src/text/textNode.I | 170 +++++++++-- panda/src/text/textNode.cxx | 595 +++++++++++++++++++----------------- panda/src/text/textNode.h | 10 +- 3 files changed, 462 insertions(+), 313 deletions(-) diff --git a/panda/src/text/textNode.I b/panda/src/text/textNode.I index 313685c504..8070504c6b 100644 --- a/panda/src/text/textNode.I +++ b/panda/src/text/textNode.I @@ -33,6 +33,7 @@ get_line_height() const { */ INLINE void TextNode:: set_max_rows(int max_rows) { + MutexHolder holder(_lock); _max_rows = max_rows; invalidate_with_measure(); } @@ -43,6 +44,7 @@ set_max_rows(int max_rows) { */ INLINE void TextNode:: clear_max_rows() { + MutexHolder holder(_lock); _max_rows = 0; invalidate_with_measure(); } @@ -53,6 +55,7 @@ clear_max_rows() { */ INLINE bool TextNode:: has_max_rows() const { + MutexHolder holder(_lock); return _max_rows > 0; } @@ -62,6 +65,7 @@ has_max_rows() const { */ INLINE int TextNode:: get_max_rows() const { + MutexHolder holder(_lock); return _max_rows; } @@ -71,6 +75,7 @@ get_max_rows() const { */ INLINE bool TextNode:: has_overflow() const { + MutexHolder holder(_lock); check_measure(); return (_flags & F_has_overflow) != 0; } @@ -88,6 +93,7 @@ set_frame_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_frame_color(const LColor &frame_color) { + MutexHolder holder(_lock); if (_frame_color != frame_color) { _frame_color = frame_color; invalidate_no_measure(); @@ -99,6 +105,7 @@ set_frame_color(const LColor &frame_color) { */ INLINE LColor TextNode:: get_frame_color() const { + MutexHolder holder(_lock); return _frame_color; } @@ -107,7 +114,8 @@ get_frame_color() const { */ INLINE void TextNode:: set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) { - if (!has_card_border() || _card_border_size != size || _card_border_uv_portion != uv_portion) { + MutexHolder holder(_lock); + if ((_flags & F_has_card_border) == 0 || _card_border_size != size || _card_border_uv_portion != uv_portion) { _flags |= F_has_card_border; _card_border_size = size; _card_border_uv_portion = uv_portion; @@ -120,7 +128,8 @@ set_card_border(PN_stdfloat size, PN_stdfloat uv_portion) { */ INLINE void TextNode:: clear_card_border() { - if (has_card_border()) { + MutexHolder holder(_lock); + if (_flags & F_has_card_border) { _flags &= ~F_has_card_border; invalidate_no_measure(); } @@ -131,6 +140,7 @@ clear_card_border() { */ INLINE PN_stdfloat TextNode:: get_card_border_size() const { + MutexHolder holder(_lock); return _card_border_size; } @@ -139,6 +149,7 @@ get_card_border_size() const { */ INLINE PN_stdfloat TextNode:: get_card_border_uv_portion() const { + MutexHolder holder(_lock); return _card_border_uv_portion; } @@ -147,6 +158,7 @@ get_card_border_uv_portion() const { */ INLINE bool TextNode:: has_card_border() const { + MutexHolder holder(_lock); return (_flags & F_has_card_border) != 0; } @@ -163,6 +175,7 @@ set_card_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_card_color(const LColor &card_color) { + MutexHolder holder(_lock); if (_card_color != card_color) { _card_color = card_color; invalidate_no_measure(); @@ -174,6 +187,7 @@ set_card_color(const LColor &card_color) { */ INLINE LColor TextNode:: get_card_color() const { + MutexHolder holder(_lock); return _card_color; } @@ -185,7 +199,8 @@ set_card_texture(Texture *card_texture) { if (card_texture == nullptr) { clear_card_texture(); } else { - if (!has_card_texture() || _card_texture != card_texture) { + MutexHolder holder(_lock); + if ((_flags & F_has_card_texture) == 0 || _card_texture != card_texture) { _flags |= F_has_card_texture; _card_texture = card_texture; invalidate_no_measure(); @@ -198,7 +213,8 @@ set_card_texture(Texture *card_texture) { */ INLINE void TextNode:: clear_card_texture() { - if (has_card_texture()) { + MutexHolder holder(_lock); + if (_flags & F_has_card_texture) { _flags &= ~F_has_card_texture; _card_texture = nullptr; invalidate_no_measure(); @@ -210,6 +226,7 @@ clear_card_texture() { */ INLINE bool TextNode:: has_card_texture() const { + MutexHolder holder(_lock); return (_flags & F_has_card_texture) != 0; } @@ -218,6 +235,7 @@ has_card_texture() const { */ INLINE Texture *TextNode:: get_card_texture() const { + MutexHolder holder(_lock); return _card_texture; } @@ -229,6 +247,7 @@ get_card_texture() const { */ INLINE void TextNode:: set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= (F_has_frame | F_frame_as_margin); _frame_ul.set(left, top); _frame_lr.set(right, bottom); @@ -243,6 +262,7 @@ set_frame_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_ */ INLINE void TextNode:: set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= F_has_frame; _flags &= ~F_frame_as_margin; _frame_ul.set(left, top); @@ -255,6 +275,7 @@ set_frame_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_std */ INLINE void TextNode:: clear_frame() { + MutexHolder holder(_lock); _flags &= ~F_has_frame; invalidate_no_measure(); } @@ -264,6 +285,7 @@ clear_frame() { */ INLINE bool TextNode:: has_frame() const { + MutexHolder holder(_lock); return (_flags & F_has_frame) != 0; } @@ -276,7 +298,8 @@ has_frame() const { */ INLINE bool TextNode:: is_frame_as_margin() const { - nassertr(has_frame(), false); + MutexHolder holder(_lock); + nassertr((_flags & F_has_frame) != 0, false); return (_flags & F_frame_as_margin) != 0; } @@ -288,7 +311,8 @@ is_frame_as_margin() const { */ INLINE LVecBase4 TextNode:: get_frame_as_set() const { - nassertr(has_frame(), LVecBase4(0.0, 0.0, 0.0, 0.0)); + MutexHolder holder(_lock); + nassertr((_flags & F_has_frame) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0)); return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]); } @@ -303,18 +327,20 @@ get_frame_as_set() const { */ INLINE LVecBase4 TextNode:: get_frame_actual() const { - if (!has_frame()) { + MutexHolder holder(_lock); + if (_flags & F_has_frame) { + if (_flags & F_frame_as_margin) { + check_measure(); + return LVecBase4(_text_ul[0] - _frame_ul[0], + _text_lr[0] + _frame_lr[0], + _text_lr[1] - _frame_lr[1], + _text_ul[1] + _frame_ul[1]); + } else { + return LVecBase4(_frame_ul[0], _frame_lr[0], _frame_lr[1], _frame_ul[1]); + } + } else { check_measure(); return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]); - - } else if (is_frame_as_margin()) { - check_measure(); - return LVecBase4(_text_ul[0] - _frame_ul[0], - _text_lr[0] + _frame_lr[0], - _text_lr[1] - _frame_lr[1], - _text_ul[1] + _frame_ul[1]); - } else { - return get_frame_as_set(); } } @@ -323,6 +349,7 @@ get_frame_actual() const { */ INLINE void TextNode:: set_frame_line_width(PN_stdfloat frame_width) { + MutexHolder holder(_lock); _frame_width = frame_width; invalidate_no_measure(); } @@ -332,6 +359,7 @@ set_frame_line_width(PN_stdfloat frame_width) { */ INLINE PN_stdfloat TextNode:: get_frame_line_width() const { + MutexHolder holder(_lock); return _frame_width; } @@ -342,6 +370,7 @@ get_frame_line_width() const { */ INLINE void TextNode:: set_frame_corners(bool corners) { + MutexHolder holder(_lock); if (corners) { _flags |= F_frame_corners; } else { @@ -355,6 +384,7 @@ set_frame_corners(bool corners) { */ INLINE bool TextNode:: get_frame_corners() const { + MutexHolder holder(_lock); return (_flags & F_frame_corners) != 0; } @@ -366,6 +396,7 @@ get_frame_corners() const { */ INLINE void TextNode:: set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= (F_has_card | F_card_as_margin); _card_ul.set(left, top); _card_lr.set(right, bottom); @@ -380,6 +411,7 @@ set_card_as_margin(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_s */ INLINE void TextNode:: set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top) { + MutexHolder holder(_lock); _flags |= F_has_card; _flags &= ~F_card_as_margin; _card_ul.set(left, top); @@ -394,6 +426,7 @@ set_card_actual(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdf */ INLINE void TextNode:: set_card_decal(bool card_decal) { + MutexHolder holder(_lock); if (card_decal) { _flags |= F_card_decal; } else { @@ -407,6 +440,7 @@ set_card_decal(bool card_decal) { */ INLINE void TextNode:: clear_card() { + MutexHolder holder(_lock); _flags &= ~F_has_card; invalidate_no_measure(); } @@ -416,6 +450,7 @@ clear_card() { */ INLINE bool TextNode:: has_card() const { + MutexHolder holder(_lock); return (_flags & F_has_card) != 0; } @@ -424,6 +459,7 @@ has_card() const { */ INLINE bool TextNode:: get_card_decal() const { + MutexHolder holder(_lock); return (_flags & F_card_decal) != 0; } @@ -436,7 +472,8 @@ get_card_decal() const { */ INLINE bool TextNode:: is_card_as_margin() const { - nassertr(has_card(), false); + MutexHolder holder(_lock); + nassertr((_flags & F_has_card) != 0, false); return (_flags & F_card_as_margin) != 0; } @@ -448,7 +485,8 @@ is_card_as_margin() const { */ INLINE LVecBase4 TextNode:: get_card_as_set() const { - nassertr(has_card(), LVecBase4(0.0, 0.0, 0.0, 0.0)); + MutexHolder holder(_lock); + nassertr((_flags & F_has_card) != 0, LVecBase4(0.0, 0.0, 0.0, 0.0)); return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]); } @@ -463,18 +501,20 @@ get_card_as_set() const { */ INLINE LVecBase4 TextNode:: get_card_actual() const { - if (!has_card()) { + MutexHolder holder(_lock); + if (_flags & F_has_card) { + if (_flags & F_card_as_margin) { + check_measure(); + return LVecBase4(_text_ul[0] - _card_ul[0], + _text_lr[0] + _card_lr[0], + _text_lr[1] - _card_lr[1], + _text_ul[1] + _card_ul[1]); + } else { + return LVecBase4(_card_ul[0], _card_lr[0], _card_lr[1], _card_ul[1]); + } + } else { check_measure(); return LVecBase4(_text_ul[0], _text_lr[0], _text_lr[1], _text_ul[1]); - - } else if (is_card_as_margin()) { - check_measure(); - return LVecBase4(_text_ul[0] - _card_ul[0], - _text_lr[0] + _card_lr[0], - _text_lr[1] - _card_lr[1], - _text_ul[1] + _card_ul[1]); - } else { - return get_card_as_set(); } } @@ -487,6 +527,8 @@ get_card_actual() const { INLINE LVecBase4 TextNode:: get_card_transformed() const { LVecBase4 card = get_card_actual(); + + MutexHolder holder(_lock); LPoint3 ul = LPoint3(card[0], 0.0, card[3]) * _transform; LPoint3 lr = LPoint3(card[1], 0.0, card[2]) * _transform; @@ -498,6 +540,7 @@ get_card_transformed() const { */ INLINE void TextNode:: set_transform(const LMatrix4 &transform) { + MutexHolder holder(_lock); _transform = transform; invalidate_with_measure(); } @@ -507,6 +550,7 @@ set_transform(const LMatrix4 &transform) { */ INLINE LMatrix4 TextNode:: get_transform() const { + MutexHolder holder(_lock); return _transform; } @@ -515,6 +559,7 @@ get_transform() const { */ INLINE void TextNode:: set_coordinate_system(CoordinateSystem coordinate_system) { + MutexHolder holder(_lock); _coordinate_system = coordinate_system; invalidate_with_measure(); } @@ -524,6 +569,7 @@ set_coordinate_system(CoordinateSystem coordinate_system) { */ INLINE CoordinateSystem TextNode:: get_coordinate_system() const { + MutexHolder holder(_lock); return _coordinate_system; } @@ -535,6 +581,7 @@ get_coordinate_system() const { */ INLINE void TextNode:: set_usage_hint(Geom::UsageHint usage_hint) { + MutexHolder holder(_lock); _usage_hint = usage_hint; invalidate_no_measure(); } @@ -545,6 +592,7 @@ set_usage_hint(Geom::UsageHint usage_hint) { */ INLINE Geom::UsageHint TextNode:: get_usage_hint() const { + MutexHolder holder(_lock); return _usage_hint; } @@ -585,6 +633,7 @@ get_usage_hint() const { */ INLINE void TextNode:: set_flatten_flags(int flatten_flags) { + MutexHolder holder(_lock); _flatten_flags = flatten_flags; } @@ -593,6 +642,7 @@ set_flatten_flags(int flatten_flags) { */ INLINE int TextNode:: get_flatten_flags() const { + MutexHolder holder(_lock); return _flatten_flags; } @@ -602,6 +652,7 @@ get_flatten_flags() const { */ INLINE void TextNode:: set_font(TextFont *font) { + MutexHolder holder(_lock); TextProperties::set_font(font); invalidate_with_measure(); } @@ -611,6 +662,7 @@ set_font(TextFont *font) { */ INLINE void TextNode:: clear_font() { + MutexHolder holder(_lock); TextProperties::clear_font(); invalidate_with_measure(); } @@ -631,6 +683,7 @@ clear_font() { */ INLINE void TextNode:: set_small_caps(bool small_caps) { + MutexHolder holder(_lock); TextProperties::set_small_caps(small_caps); invalidate_with_measure(); } @@ -640,6 +693,7 @@ set_small_caps(bool small_caps) { */ INLINE void TextNode:: clear_small_caps() { + MutexHolder holder(_lock); TextProperties::clear_small_caps(); invalidate_with_measure(); } @@ -651,6 +705,7 @@ clear_small_caps() { */ INLINE void TextNode:: set_small_caps_scale(PN_stdfloat small_caps_scale) { + MutexHolder holder(_lock); TextProperties::set_small_caps_scale(small_caps_scale); invalidate_with_measure(); } @@ -660,6 +715,7 @@ set_small_caps_scale(PN_stdfloat small_caps_scale) { */ INLINE void TextNode:: clear_small_caps_scale() { + MutexHolder holder(_lock); TextProperties::clear_small_caps_scale(); invalidate_with_measure(); } @@ -669,6 +725,7 @@ clear_small_caps_scale() { */ INLINE void TextNode:: set_slant(PN_stdfloat slant) { + MutexHolder holder(_lock); TextProperties::set_slant(slant); invalidate_with_measure(); } @@ -678,6 +735,7 @@ set_slant(PN_stdfloat slant) { */ INLINE void TextNode:: clear_slant() { + MutexHolder holder(_lock); TextProperties::clear_slant(); invalidate_with_measure(); } @@ -687,6 +745,7 @@ clear_slant() { */ INLINE void TextNode:: set_align(TextNode::Alignment align_type) { + MutexHolder holder(_lock); TextProperties::set_align(align_type); invalidate_with_measure(); } @@ -696,6 +755,7 @@ set_align(TextNode::Alignment align_type) { */ INLINE void TextNode:: clear_align() { + MutexHolder holder(_lock); TextProperties::clear_align(); invalidate_with_measure(); } @@ -706,6 +766,7 @@ clear_align() { */ INLINE void TextNode:: set_indent(PN_stdfloat indent) { + MutexHolder holder(_lock); TextProperties::set_indent(indent); invalidate_with_measure(); } @@ -715,6 +776,7 @@ set_indent(PN_stdfloat indent) { */ INLINE void TextNode:: clear_indent() { + MutexHolder holder(_lock); TextProperties::clear_indent(); invalidate_with_measure(); } @@ -725,6 +787,7 @@ clear_indent() { */ INLINE void TextNode:: set_wordwrap(PN_stdfloat wordwrap) { + MutexHolder holder(_lock); TextProperties::set_wordwrap(wordwrap); invalidate_with_measure(); } @@ -735,6 +798,7 @@ set_wordwrap(PN_stdfloat wordwrap) { */ INLINE void TextNode:: clear_wordwrap() { + MutexHolder holder(_lock); TextProperties::clear_wordwrap(); invalidate_with_measure(); } @@ -744,6 +808,7 @@ clear_wordwrap() { */ INLINE void TextNode:: set_text_color(const LColor &text_color) { + MutexHolder holder(_lock); TextProperties::set_text_color(text_color); invalidate_no_measure(); } @@ -762,6 +827,7 @@ set_text_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: clear_text_color() { + MutexHolder holder(_lock); TextProperties::clear_text_color(); invalidate_no_measure(); } @@ -779,6 +845,7 @@ set_shadow_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a) { */ INLINE void TextNode:: set_shadow_color(const LColor &shadow_color) { + MutexHolder holder(_lock); TextProperties::set_shadow_color(shadow_color); invalidate_no_measure(); } @@ -788,6 +855,7 @@ set_shadow_color(const LColor &shadow_color) { */ INLINE void TextNode:: clear_shadow_color() { + MutexHolder holder(_lock); TextProperties::clear_shadow_color(); invalidate_with_measure(); } @@ -807,6 +875,7 @@ set_shadow(PN_stdfloat xoffset, PN_stdfloat yoffset) { */ INLINE void TextNode:: set_shadow(const LVecBase2 &shadow_offset) { + MutexHolder holder(_lock); TextProperties::set_shadow(shadow_offset); invalidate_no_measure(); } @@ -816,6 +885,7 @@ set_shadow(const LVecBase2 &shadow_offset) { */ INLINE void TextNode:: clear_shadow() { + MutexHolder holder(_lock); TextProperties::clear_shadow(); invalidate_no_measure(); } @@ -831,6 +901,7 @@ clear_shadow() { */ INLINE void TextNode:: set_bin(const std::string &bin) { + MutexHolder holder(_lock); TextProperties::set_bin(bin); invalidate_no_measure(); } @@ -841,6 +912,7 @@ set_bin(const std::string &bin) { */ INLINE void TextNode:: clear_bin() { + MutexHolder holder(_lock); TextProperties::clear_bin(); invalidate_no_measure(); } @@ -858,6 +930,7 @@ clear_bin() { */ INLINE int TextNode:: set_draw_order(int draw_order) { + MutexHolder holder(_lock); invalidate_no_measure(); return TextProperties::set_draw_order(draw_order); } @@ -867,6 +940,7 @@ set_draw_order(int draw_order) { */ INLINE void TextNode:: clear_draw_order() { + MutexHolder holder(_lock); TextProperties::clear_draw_order(); invalidate_with_measure(); } @@ -877,6 +951,7 @@ clear_draw_order() { */ INLINE void TextNode:: set_tab_width(PN_stdfloat tab_width) { + MutexHolder holder(_lock); TextProperties::set_tab_width(tab_width); invalidate_with_measure(); } @@ -886,6 +961,7 @@ set_tab_width(PN_stdfloat tab_width) { */ INLINE void TextNode:: clear_tab_width() { + MutexHolder holder(_lock); TextProperties::clear_tab_width(); invalidate_with_measure(); } @@ -897,6 +973,7 @@ clear_tab_width() { */ INLINE void TextNode:: set_glyph_scale(PN_stdfloat glyph_scale) { + MutexHolder holder(_lock); TextProperties::set_glyph_scale(glyph_scale); invalidate_with_measure(); } @@ -906,6 +983,7 @@ set_glyph_scale(PN_stdfloat glyph_scale) { */ INLINE void TextNode:: clear_glyph_scale() { + MutexHolder holder(_lock); TextProperties::clear_glyph_scale(); invalidate_with_measure(); } @@ -917,6 +995,7 @@ clear_glyph_scale() { */ INLINE void TextNode:: set_glyph_shift(PN_stdfloat glyph_shift) { + MutexHolder holder(_lock); TextProperties::set_glyph_shift(glyph_shift); invalidate_with_measure(); } @@ -926,6 +1005,7 @@ set_glyph_shift(PN_stdfloat glyph_shift) { */ INLINE void TextNode:: clear_glyph_shift() { + MutexHolder holder(_lock); TextProperties::clear_glyph_shift(); invalidate_with_measure(); } @@ -936,6 +1016,7 @@ clear_glyph_shift() { */ INLINE void TextNode:: set_text(const std::string &text) { + MutexHolder holder(_lock); TextEncoder::set_text(text); invalidate_with_measure(); } @@ -948,6 +1029,7 @@ set_text(const std::string &text) { */ INLINE void TextNode:: set_text(const std::string &text, TextNode::Encoding encoding) { + MutexHolder holder(_lock); TextEncoder::set_text(text, encoding); invalidate_with_measure(); } @@ -957,6 +1039,7 @@ set_text(const std::string &text, TextNode::Encoding encoding) { */ INLINE void TextNode:: clear_text() { + MutexHolder holder(_lock); TextEncoder::clear_text(); invalidate_with_measure(); } @@ -966,6 +1049,7 @@ clear_text() { */ INLINE void TextNode:: append_text(const std::string &text) { + MutexHolder holder(_lock); TextEncoder::append_text(text); invalidate_with_measure(); } @@ -976,6 +1060,7 @@ append_text(const std::string &text) { */ INLINE void TextNode:: append_unicode_char(wchar_t character) { + MutexHolder holder(_lock); TextEncoder::append_unicode_char(character); invalidate_with_measure(); } @@ -1008,6 +1093,7 @@ calc_width(const std::string &line) const { */ INLINE void TextNode:: set_wtext(const std::wstring &wtext) { + MutexHolder holder(_lock); TextEncoder::set_wtext(wtext); invalidate_with_measure(); } @@ -1017,6 +1103,7 @@ set_wtext(const std::wstring &wtext) { */ INLINE void TextNode:: append_wtext(const std::wstring &wtext) { + MutexHolder holder(_lock); TextEncoder::append_wtext(wtext); invalidate_with_measure(); } @@ -1030,6 +1117,7 @@ append_wtext(const std::wstring &wtext) { */ INLINE std::wstring TextNode:: get_wordwrapped_wtext() const { + MutexHolder holder(_lock); check_measure(); return _wordwrapped_wtext; } @@ -1040,6 +1128,7 @@ get_wordwrapped_wtext() const { */ INLINE PN_stdfloat TextNode:: get_left() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[0]; } @@ -1050,6 +1139,7 @@ get_left() const { */ INLINE PN_stdfloat TextNode:: get_right() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[0]; } @@ -1060,6 +1150,7 @@ get_right() const { */ INLINE PN_stdfloat TextNode:: get_bottom() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[1]; } @@ -1070,6 +1161,7 @@ get_bottom() const { */ INLINE PN_stdfloat TextNode:: get_top() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[1]; } @@ -1079,6 +1171,7 @@ get_top() const { */ INLINE PN_stdfloat TextNode:: get_height() const { + MutexHolder holder(_lock); check_measure(); return _text_ul[1] - _text_lr[1]; } @@ -1088,6 +1181,7 @@ get_height() const { */ INLINE PN_stdfloat TextNode:: get_width() const { + MutexHolder holder(_lock); check_measure(); return _text_lr[0] - _text_ul[0]; } @@ -1098,6 +1192,7 @@ get_width() const { */ INLINE LPoint3 TextNode:: get_upper_left_3d() const { + MutexHolder holder(_lock); check_measure(); return _ul3d; } @@ -1108,6 +1203,7 @@ get_upper_left_3d() const { */ INLINE LPoint3 TextNode:: get_lower_right_3d() const { + MutexHolder holder(_lock); check_measure(); return _lr3d; } @@ -1118,10 +1214,22 @@ get_lower_right_3d() const { */ INLINE int TextNode:: get_num_rows() const { + MutexHolder holder(_lock); check_measure(); return _num_rows; } +/** + * Generates the text, according to the parameters indicated within the + * TextNode, and returns a Node that may be parented within the tree to + * represent it. + */ +PT(PandaNode) TextNode:: +generate() { + MutexHolder holder(_lock); + return do_generate(); +} + /** * Can be called after the TextNode has been fully configured, to force the * node to recompute its text immediately, rather than waiting for it to be @@ -1129,6 +1237,7 @@ get_num_rows() const { */ INLINE void TextNode:: update() { + MutexHolder holder(_lock); check_rebuild(); } @@ -1140,8 +1249,9 @@ update() { */ INLINE void TextNode:: force_update() { - invalidate_with_measure(); - check_rebuild(); + MutexHolder holder(_lock); + mark_internal_bounds_stale(); + do_rebuild(); } /** diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index a68ebba812..1ecfc1ea65 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -74,7 +74,7 @@ TextNode(const string &name) : PandaNode(name) { } if (text_small_caps) { - set_small_caps(true); + TextProperties::set_small_caps(true); } _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f); @@ -277,10 +277,10 @@ void TextNode:: output(std::ostream &out) const { PandaNode::output(out); - check_rebuild(); + PT(PandaNode) internal_geom = do_get_internal_geom(); int geom_count = 0; - if (_internal_geom != nullptr) { - geom_count = count_geoms(_internal_geom); + if (internal_geom != nullptr) { + geom_count = count_geoms(internal_geom); } out << " (" << geom_count << " geoms)"; @@ -291,6 +291,7 @@ output(std::ostream &out) const { */ void TextNode:: write(std::ostream &out, int indent_level) const { + MutexHolder holder(_lock); PandaNode::write(out, indent_level); TextProperties::write(out, indent_level + 2); indent(out, indent_level + 2) @@ -301,13 +302,263 @@ write(std::ostream &out, int indent_level) const { << "text is " << get_text() << "\n"; } +/** + * Returns the actual node that is used internally to render the text, if the + * TextNode is parented within the scene graph. + * + * In general, you should not call this method. Call generate() instead if + * you want to get a handle to geometry that represents the text. This method + * is provided as a debugging aid only. + */ +PT(PandaNode) TextNode:: +get_internal_geom() const { + // Output a nuisance warning to discourage the naive from calling this + // method accidentally. + text_cat.info() + << "TextNode::get_internal_geom() called.\n"; + return do_get_internal_geom(); +} + +/** + * Returns the union of all attributes from SceneGraphReducer::AttribTypes + * that may not safely be applied to the vertices of this node. If this is + * nonzero, these attributes must be dropped at this node as a state change. + * + * This is a generalization of safe_to_transform(). + */ +int TextNode:: +get_unsafe_to_apply_attribs() const { + // We have no way to apply these kinds of attributes to our TextNode, so + // insist they get dropped into the PandaNode's basic state. + return + SceneGraphReducer::TT_tex_matrix | + SceneGraphReducer::TT_other; +} + +/** + * Applies whatever attributes are specified in the AccumulatedAttribs object + * (and by the attrib_types bitmask) to the vertices on this node, if + * appropriate. If this node uses geom arrays like a GeomNode, the supplied + * GeomTransformer may be used to unify shared arrays across multiple + * different nodes. + * + * This is a generalization of xform(). + */ +void TextNode:: +apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, + GeomTransformer &transformer) { + MutexHolder holder(_lock); + if ((attrib_types & SceneGraphReducer::TT_transform) != 0) { + const LMatrix4 &mat = attribs._transform->get_mat(); + _transform *= mat; + + if ((_flags & F_needs_measure) == 0) { + // If we already have a measure, transform it too. We don't need to + // invalidate the 2-d parts, since that's not affected by the transform + // anyway. + _ul3d = _ul3d * mat; + _lr3d = _lr3d * mat; + } + } + if ((attrib_types & SceneGraphReducer::TT_color) != 0) { + if (attribs._color != nullptr) { + const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color); + if (ca->get_color_type() == ColorAttrib::T_flat) { + const LColor &c = ca->get_color(); + TextProperties::set_text_color(c); + TextProperties::set_shadow_color(c); + _frame_color = c; + _card_color = c; + invalidate_no_measure(); + } + } + } + if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) { + if (attribs._color_scale != nullptr) { + const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale); + const LVecBase4 &s = csa->get_scale(); + if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) { + LVecBase4 tc = get_text_color(); + tc.componentwise_mult(s); + TextProperties::set_text_color(tc); + + LVecBase4 sc = get_shadow_color(); + sc.componentwise_mult(s); + TextProperties::set_shadow_color(sc); + + _frame_color.componentwise_mult(s); + _card_color.componentwise_mult(s); + + invalidate_no_measure(); + } + } + } + + // Now propagate the attributes down to our already-generated geometry, if + // we have any. + if ((_flags & F_needs_rebuild) == 0 && + _internal_geom != nullptr) { + SceneGraphReducer gr; + gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer); + } +} + +/** + * This is used to support NodePath::calc_tight_bounds(). It is not intended + * to be called directly, and it has nothing to do with the normal Panda + * bounding-volume computation. + * + * If the node contains any geometry, this updates min_point and max_point to + * enclose its bounding box. found_any is to be set true if the node has any + * geometry at all, or left alone if it has none. This method may be called + * over several nodes, so it may enter with min_point, max_point, and + * found_any already set. + */ +CPT(TransformState) TextNode:: +calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, + const TransformState *transform, Thread *current_thread) const { + CPT(TransformState) next_transform = + PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, + current_thread); + + PT(PandaNode) geom = do_get_internal_geom(); + if (geom != nullptr) { + geom->calc_tight_bounds(min_point, max_point, + found_any, next_transform, current_thread); + } + + return next_transform; +} + +/** + * This function will be called during the cull traversal to perform any + * additional operations that should be performed at cull time. This may + * include additional manipulation of render state or additional + * visible/invisible decisions, or any other arbitrary operation. + * + * Note that this function will *not* be called unless set_cull_callback() is + * called in the constructor of the derived class. It is necessary to call + * set_cull_callback() to indicated that we require cull_callback() to be + * called. + * + * By the time this function is called, the node has already passed the + * bounding-volume test for the viewing frustum, and the node's transform and + * state have already been applied to the indicated CullTraverserData object. + * + * The return value is true if this node should be visible, or false if it + * should be culled. + */ +bool TextNode:: +cull_callback(CullTraverser *trav, CullTraverserData &data) { + + PT(PandaNode) internal_geom = do_get_internal_geom(); + if (internal_geom != nullptr) { + // Render the text with this node. + CullTraverserData next_data(data, internal_geom); + trav->traverse(next_data); + } + + // Now continue to render everything else below this node. + return true; +} + +/** + * Returns true if there is some value to visiting this particular node during + * the cull traversal for any camera, false otherwise. This will be used to + * optimize the result of get_net_draw_show_mask(), so that any subtrees that + * contain only nodes for which is_renderable() is false need not be visited. + */ +bool TextNode:: +is_renderable() const { + return true; +} + +/** + * Called when needed to recompute the node's _internal_bound object. Nodes + * that contain anything of substance should redefine this to do the right + * thing. + */ +void TextNode:: +compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const { + // First, get ourselves a fresh, empty bounding volume. + PT(BoundingVolume) bound = new BoundingSphere; + + GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound); + + // Now enclose the bounding box around the text. We can do this without + // actually generating the text, if we have at least measured it. + LPoint3 vertices[8]; + { + MutexHolder holder(_lock); + check_measure(); + + vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]); + vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]); + vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]); + vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]); + vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]); + vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]); + vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]); + vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]); + } + + gbv->around(vertices, vertices + 8); + + internal_bounds = bound; + internal_vertices = 0; // TODO: estimate this better. +} + +/** + * The recursive implementation of prepare_scene(). Don't call this directly; + * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead. + */ +void TextNode:: +r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, + GeomTransformer &transformer, Thread *current_thread) { + + PT(PandaNode) child = do_get_internal_geom(); + if (child != nullptr) { + CPT(RenderState) child_state = node_state->compose(child->get_state()); + child->r_prepare_scene(gsg, child_state, transformer, current_thread); + } + + PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread); +} + +/** + * Removes any existing children of the TextNode, and adds the newly generated + * text instead. + */ +void TextNode:: +do_rebuild() { + nassertv(_lock.debug_is_locked()); + _flags &= ~(F_needs_rebuild | F_needs_measure); + _internal_geom = do_generate(); +} + + +/** + * Can be called in lieu of do_rebuild() to measure the text and set up the + * bounding boxes properly without actually assembling it. + */ +void TextNode:: +do_measure() { + // We no longer make this a special case. + do_rebuild(); +} + /** * Generates the text, according to the parameters indicated within the * TextNode, and returns a Node that may be parented within the tree to * represent it. */ PT(PandaNode) TextNode:: -generate() { +do_generate() { + nassertr(_lock.debug_is_locked(), nullptr); + PStatTimer timer(_text_generate_pcollector); if (text_cat.is_debug()) { text_cat.debug() @@ -408,20 +659,20 @@ generate() { // Now deal with the decorations. - if (has_card()) { + if (_flags & F_has_card) { PT(PandaNode) card_root; - if (has_card_border()) { + if (_flags & F_has_card_border) { card_root = make_card_with_border(); } else { card_root = make_card(); } card_root->set_transform(transform); - card_root->set_attrib(ColorAttrib::make_flat(get_card_color())); - if (get_card_color()[3] != 1.0f) { + card_root->set_attrib(ColorAttrib::make_flat(_card_color)); + if (_card_color[3] != 1.0f) { card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); } - if (has_card_texture()) { - card_root->set_attrib(TextureAttrib::make(get_card_texture())); + if (_flags & F_has_card_texture) { + card_root->set_attrib(TextureAttrib::make(_card_texture)); } if (has_bin()) { @@ -437,17 +688,17 @@ generate() { card_root->add_child(root); root = card_root; - if (get_card_decal()) { + if (_flags & F_card_decal) { card_root->set_effect(DecalEffect::make()); } } - if (has_frame()) { + if (_flags & F_has_frame) { PT(PandaNode) frame_root = make_frame(); frame_root->set_transform(transform); root->add_child(frame_root, get_draw_order() + 1); - frame_root->set_attrib(ColorAttrib::make_flat(get_frame_color())); - if (get_frame_color()[3] != 1.0f) { + frame_root->set_attrib(ColorAttrib::make_flat(_frame_color)); + if (_frame_color[3] != 1.0f) { frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); } @@ -465,271 +716,35 @@ generate() { /** * Returns the actual node that is used internally to render the text, if the * TextNode is parented within the scene graph. - * - * In general, you should not call this method. Call generate() instead if - * you want to get a handle to geometry that represents the text. This method - * is provided as a debugging aid only. */ -PandaNode *TextNode:: -get_internal_geom() const { - // Output a nuisance warning to discourage the naive from calling this - // method accidentally. - text_cat.info() - << "TextNode::get_internal_geom() called.\n"; +PT(PandaNode) TextNode:: +do_get_internal_geom() const { + MutexHolder holder(_lock); check_rebuild(); return _internal_geom; } -/** - * Returns the union of all attributes from SceneGraphReducer::AttribTypes - * that may not safely be applied to the vertices of this node. If this is - * nonzero, these attributes must be dropped at this node as a state change. - * - * This is a generalization of safe_to_transform(). - */ -int TextNode:: -get_unsafe_to_apply_attribs() const { - // We have no way to apply these kinds of attributes to our TextNode, so - // insist they get dropped into the PandaNode's basic state. - return - SceneGraphReducer::TT_tex_matrix | - SceneGraphReducer::TT_other; -} - -/** - * Applies whatever attributes are specified in the AccumulatedAttribs object - * (and by the attrib_types bitmask) to the vertices on this node, if - * appropriate. If this node uses geom arrays like a GeomNode, the supplied - * GeomTransformer may be used to unify shared arrays across multiple - * different nodes. - * - * This is a generalization of xform(). - */ -void TextNode:: -apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, - GeomTransformer &transformer) { - if ((attrib_types & SceneGraphReducer::TT_transform) != 0) { - const LMatrix4 &mat = attribs._transform->get_mat(); - _transform *= mat; - - if ((_flags & F_needs_measure) == 0) { - // If we already have a measure, transform it too. We don't need to - // invalidate the 2-d parts, since that's not affected by the transform - // anyway. - _ul3d = _ul3d * mat; - _lr3d = _lr3d * mat; - } - } - if ((attrib_types & SceneGraphReducer::TT_color) != 0) { - if (attribs._color != nullptr) { - const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color); - if (ca->get_color_type() == ColorAttrib::T_flat) { - const LColor &c = ca->get_color(); - set_text_color(c); - set_frame_color(c); - set_card_color(c); - set_shadow_color(c); - } - } - } - if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) { - if (attribs._color_scale != nullptr) { - const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale); - const LVecBase4 &s = csa->get_scale(); - if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) { - LVecBase4 tc = get_text_color(); - tc[0] *= s[0]; - tc[1] *= s[1]; - tc[2] *= s[2]; - tc[3] *= s[3]; - set_text_color(tc); - LVecBase4 sc = get_shadow_color(); - sc[0] *= s[0]; - sc[1] *= s[1]; - sc[2] *= s[2]; - sc[3] *= s[3]; - set_shadow_color(sc); - LVecBase4 fc = get_frame_color(); - fc[0] *= s[0]; - fc[1] *= s[1]; - fc[2] *= s[2]; - fc[3] *= s[3]; - set_frame_color(fc); - LVecBase4 cc = get_card_color(); - cc[0] *= s[0]; - cc[1] *= s[1]; - cc[2] *= s[2]; - cc[3] *= s[3]; - set_card_color(cc); - } - } - } - - // Now propagate the attributes down to our already-generated geometry, if - // we have any. - if ((_flags & F_needs_rebuild) == 0 && - _internal_geom != nullptr) { - SceneGraphReducer gr; - gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer); - } -} - -/** - * This is used to support NodePath::calc_tight_bounds(). It is not intended - * to be called directly, and it has nothing to do with the normal Panda - * bounding-volume computation. - * - * If the node contains any geometry, this updates min_point and max_point to - * enclose its bounding box. found_any is to be set true if the node has any - * geometry at all, or left alone if it has none. This method may be called - * over several nodes, so it may enter with min_point, max_point, and - * found_any already set. - */ -CPT(TransformState) TextNode:: -calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, - const TransformState *transform, Thread *current_thread) const { - CPT(TransformState) next_transform = - PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform, - current_thread); - - check_rebuild(); - - if (_internal_geom != nullptr) { - _internal_geom->calc_tight_bounds(min_point, max_point, - found_any, next_transform, current_thread); - } - - return next_transform; -} - -/** - * This function will be called during the cull traversal to perform any - * additional operations that should be performed at cull time. This may - * include additional manipulation of render state or additional - * visible/invisible decisions, or any other arbitrary operation. - * - * Note that this function will *not* be called unless set_cull_callback() is - * called in the constructor of the derived class. It is necessary to call - * set_cull_callback() to indicated that we require cull_callback() to be - * called. - * - * By the time this function is called, the node has already passed the - * bounding-volume test for the viewing frustum, and the node's transform and - * state have already been applied to the indicated CullTraverserData object. - * - * The return value is true if this node should be visible, or false if it - * should be culled. - */ -bool TextNode:: -cull_callback(CullTraverser *trav, CullTraverserData &data) { - check_rebuild(); - if (_internal_geom != nullptr) { - // Render the text with this node. - CullTraverserData next_data(data, _internal_geom); - trav->traverse(next_data); - } - - // Now continue to render everything else below this node. - return true; -} - -/** - * Returns true if there is some value to visiting this particular node during - * the cull traversal for any camera, false otherwise. This will be used to - * optimize the result of get_net_draw_show_mask(), so that any subtrees that - * contain only nodes for which is_renderable() is false need not be visited. - */ -bool TextNode:: -is_renderable() const { - return true; -} - -/** - * Called when needed to recompute the node's _internal_bound object. Nodes - * that contain anything of substance should redefine this to do the right - * thing. - */ -void TextNode:: -compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, - int &internal_vertices, - int pipeline_stage, - Thread *current_thread) const { - // First, get ourselves a fresh, empty bounding volume. - PT(BoundingVolume) bound = new BoundingSphere; - - GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound); - - // Now enclose the bounding box around the text. We can do this without - // actually generating the text, if we have at least measured it. - check_measure(); - - LPoint3 vertices[8]; - vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]); - vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]); - vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]); - vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]); - vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]); - vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]); - vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]); - vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]); - - gbv->around(vertices, vertices + 8); - - internal_bounds = bound; - internal_vertices = 0; // TODO: estimate this better. -} - -/** - * The recursive implementation of prepare_scene(). Don't call this directly; - * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead. - */ -void TextNode:: -r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, - GeomTransformer &transformer, Thread *current_thread) { - check_rebuild(); - - PandaNode *child = _internal_geom; - if (child != nullptr) { - CPT(RenderState) child_state = node_state->compose(child->get_state()); - child->r_prepare_scene(gsg, child_state, transformer, current_thread); - } - - PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread); -} - -/** - * Removes any existing children of the TextNode, and adds the newly generated - * text instead. - */ -void TextNode:: -do_rebuild() { - _flags &= ~(F_needs_rebuild | F_needs_measure); - _internal_geom = generate(); -} - - -/** - * Can be called in lieu of do_rebuild() to measure the text and set up the - * bounding boxes properly without actually assembling it. - */ -void TextNode:: -do_measure() { - // We no longer make this a special case. - do_rebuild(); -} - /** * Creates a frame around the text. */ PT(PandaNode) TextNode:: make_frame() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) frame_node = new GeomNode("frame"); - LVector4 dimensions = get_frame_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _frame_ul[0]; + PN_stdfloat right = _frame_lr[0]; + PN_stdfloat bottom = _frame_lr[1]; + PN_stdfloat top = _frame_ul[1]; + + if (_flags & F_frame_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width); CPT(RenderState) state = RenderState::make(thick); @@ -753,8 +768,8 @@ make_frame() { geom->add_primitive(frame); frame_node->add_geom(geom, state); - if (get_frame_corners()) { - PT(GeomPoints) corners = new GeomPoints(get_usage_hint()); + if (_flags & F_frame_corners) { + PT(GeomPoints) corners = new GeomPoints(_usage_hint); corners->add_consecutive_vertices(0, 4); PT(Geom) geom2 = new Geom(vdata); geom2->add_primitive(corners); @@ -769,13 +784,22 @@ make_frame() { */ PT(PandaNode) TextNode:: make_card() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) card_node = new GeomNode("card"); - LVector4 dimensions = get_card_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _card_ul[0]; + PN_stdfloat right = _card_lr[0]; + PN_stdfloat bottom = _card_lr[1]; + PN_stdfloat top = _card_ul[1]; + + if (_flags & F_card_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } PT(GeomVertexData) vdata = new GeomVertexData ("text", GeomVertexFormat::get_v3t2(), _usage_hint); @@ -793,7 +817,7 @@ make_card() { texcoord.set_data2(1.0f, 1.0f); texcoord.set_data2(1.0f, 0.0f); - PT(GeomTristrips) card = new GeomTristrips(get_usage_hint()); + PT(GeomTristrips) card = new GeomTristrips(_usage_hint); card->add_consecutive_vertices(0, 4); card->close_primitive(); @@ -812,13 +836,22 @@ make_card() { */ PT(PandaNode) TextNode:: make_card_with_border() { + nassertr(_lock.debug_is_locked(), nullptr); + nassertr((_flags & F_needs_measure) == 0, nullptr); + PT(GeomNode) card_node = new GeomNode("card"); - LVector4 dimensions = get_card_actual(); - PN_stdfloat left = dimensions[0]; - PN_stdfloat right = dimensions[1]; - PN_stdfloat bottom = dimensions[2]; - PN_stdfloat top = dimensions[3]; + PN_stdfloat left = _card_ul[0]; + PN_stdfloat right = _card_lr[0]; + PN_stdfloat bottom = _card_lr[1]; + PN_stdfloat top = _card_ul[1]; + + if (_flags & F_card_as_margin) { + left = _text_ul[0] - left; + right = _text_lr[0] + right; + bottom = _text_lr[1] - bottom; + top = _text_ul[1] + top; + } /* * we now create three tri-strips instead of one with vertices arranged as diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index 4f37f806f4..ecf7dcafcf 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -24,6 +24,8 @@ #include "pandaNode.h" #include "luse.h" #include "geom.h" +#include "pmutex.h" +#include "mutexHolder.h" /** * The primary interface to this module. This class does basic text assembly; @@ -225,11 +227,11 @@ PUBLISHED: INLINE int get_num_rows() const; - PT(PandaNode) generate(); + INLINE PT(PandaNode) generate(); INLINE void update(); INLINE void force_update(); - PandaNode *get_internal_geom() const; + PT(PandaNode) get_internal_geom() const; PUBLISHED: MAKE_PROPERTY(max_rows, get_max_rows, set_max_rows); @@ -312,12 +314,16 @@ private: void do_rebuild(); void do_measure(); + PT(PandaNode) do_generate(); + PT(PandaNode) do_get_internal_geom() const; + PT(PandaNode) make_frame(); PT(PandaNode) make_card(); PT(PandaNode) make_card_with_border(); static int count_geoms(PandaNode *node); + Mutex _lock; PT(PandaNode) _internal_geom; PT(Texture) _card_texture; From a6ad608207c42de37017313e0eb0fddde5cd21be Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 11:38:03 +0200 Subject: [PATCH 10/18] tests: add some unit tests for TextNode --- tests/text/test_textnode.py | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/text/test_textnode.py diff --git a/tests/text/test_textnode.py b/tests/text/test_textnode.py new file mode 100644 index 0000000000..1f9a8349b9 --- /dev/null +++ b/tests/text/test_textnode.py @@ -0,0 +1,106 @@ +from panda3d import core + + +def test_textnode_card_as_margin(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_card_as_margin(l, r, b, t) + + assert text.has_card() + assert text.is_card_as_margin() + assert text.get_card_as_set() == (l, r, b, t) + + card_actual = text.get_card_actual() + card_expect = core.LVecBase4( + text.get_left() - l, + text.get_right() + r, + text.get_bottom() - b, + text.get_top() + t) + assert card_actual == card_expect + + +def test_textnode_card_actual(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_card_actual(l, r, b, t) + + assert text.has_card() + assert not text.is_card_as_margin() + assert text.get_card_as_set() == (l, r, b, t) + + card_actual = text.get_card_actual() + card_expect = core.LVecBase4(l, r, b, t) + assert card_actual == card_expect + + +def test_textnode_frame_as_margin(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_frame_as_margin(l, r, b, t) + + assert text.has_frame() + assert text.is_frame_as_margin() + assert text.get_frame_as_set() == (l, r, b, t) + + frame_actual = text.get_frame_actual() + frame_expect = core.LVecBase4( + text.get_left() - l, + text.get_right() + r, + text.get_bottom() - b, + text.get_top() + t) + assert frame_actual == frame_expect + + +def test_textnode_frame_actual(): + text = core.TextNode("test") + text.text = "Test" + + l, r, b, t = 0.1, 0.2, 0.3, 0.4 + text.set_frame_actual(l, r, b, t) + + assert text.has_frame() + assert not text.is_frame_as_margin() + assert text.get_frame_as_set() == (l, r, b, t) + + frame_actual = text.get_frame_actual() + frame_expect = core.LVecBase4(l, r, b, t) + assert frame_actual == frame_expect + + +def test_textnode_flatten_color(): + text = core.TextNode("test") + text.text_color = (0, 0, 0, 1) + path = core.NodePath(text) + + color = core.LColor(1, 0, 0, 1) + path.set_color(color) + path.flatten_strong() + + assert text.text_color == color + assert text.shadow_color == color + assert text.frame_color == color + assert text.card_color == color + + +def test_textnode_flatten_colorscale(): + text = core.TextNode("test") + text.text_color = (1, 0, 0, 0) + text.shadow_color = (0, 1, 0, 0) + text.frame_color = (0, 0, 1, 0) + text.card_color = (0, 0, 0, 1) + path = core.NodePath(text) + + color = core.LColor(.5, .5, .5, .5) + path.set_color_scale(color) + path.flatten_strong() + + assert text.text_color == (.5, 0, 0, 0) + assert text.shadow_color == (0, .5, 0, 0) + assert text.frame_color == (0, 0, .5, 0) + assert text.card_color == (0, 0, 0, .5) From cd033c27e8f458d3c8e0fb19380eb2906a232435 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 21:00:08 +0200 Subject: [PATCH 11/18] grutil: add thread safety to ShaderTerrainMesh --- panda/src/grutil/shaderTerrainMesh.I | 10 ++++++++++ panda/src/grutil/shaderTerrainMesh.cxx | 3 +++ panda/src/grutil/shaderTerrainMesh.h | 3 +++ 3 files changed, 16 insertions(+) diff --git a/panda/src/grutil/shaderTerrainMesh.I b/panda/src/grutil/shaderTerrainMesh.I index f6a66bc7f5..f4a6b4286e 100644 --- a/panda/src/grutil/shaderTerrainMesh.I +++ b/panda/src/grutil/shaderTerrainMesh.I @@ -22,6 +22,7 @@ * @param filename Heightfield texture */ INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) { + MutexHolder holder(_lock); _heightfield_tex = heightfield; } @@ -33,6 +34,7 @@ INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) { * @return Path to the heightfield */ INLINE Texture* ShaderTerrainMesh::get_heightfield() const { + MutexHolder holder(_lock); return _heightfield_tex; } @@ -54,6 +56,7 @@ INLINE Texture* ShaderTerrainMesh::get_heightfield() const { * @param chunk_size Size of the chunks, has to be a power of two */ INLINE void ShaderTerrainMesh::set_chunk_size(size_t chunk_size) { + MutexHolder holder(_lock); _chunk_size = chunk_size; } @@ -63,6 +66,7 @@ INLINE void ShaderTerrainMesh::set_chunk_size(size_t chunk_size) { * @return Chunk size */ INLINE size_t ShaderTerrainMesh::get_chunk_size() const { + MutexHolder holder(_lock); return _chunk_size; } @@ -81,6 +85,7 @@ INLINE size_t ShaderTerrainMesh::get_chunk_size() const { * @param generate_patches [description] */ INLINE void ShaderTerrainMesh::set_generate_patches(bool generate_patches) { + MutexHolder holder(_lock); _generate_patches = generate_patches; } @@ -92,6 +97,7 @@ INLINE void ShaderTerrainMesh::set_generate_patches(bool generate_patches) { * @return Whether to generate patches */ INLINE bool ShaderTerrainMesh::get_generate_patches() const { + MutexHolder holder(_lock); return _generate_patches; } @@ -107,6 +113,7 @@ INLINE bool ShaderTerrainMesh::get_generate_patches() const { * @param target_triangle_width Desired triangle width in pixels */ INLINE void ShaderTerrainMesh::set_target_triangle_width(PN_stdfloat target_triangle_width) { + MutexHolder holder(_lock); _target_triangle_width = target_triangle_width; } @@ -118,6 +125,7 @@ INLINE void ShaderTerrainMesh::set_target_triangle_width(PN_stdfloat target_tria * @return Target triangle width */ INLINE PN_stdfloat ShaderTerrainMesh::get_target_triangle_width() const { + MutexHolder holder(_lock); return _target_triangle_width; } @@ -131,6 +139,7 @@ INLINE PN_stdfloat ShaderTerrainMesh::get_target_triangle_width() const { * @param update_enabled Whether to update the terrain */ INLINE void ShaderTerrainMesh::set_update_enabled(bool update_enabled) { + MutexHolder holder(_lock); _update_enabled = update_enabled; } @@ -142,6 +151,7 @@ INLINE void ShaderTerrainMesh::set_update_enabled(bool update_enabled) { * @return Whether to update the terrain */ INLINE bool ShaderTerrainMesh::get_update_enabled() const { + MutexHolder holder(_lock); return _update_enabled; } diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index 44c8397279..a0f3c34690 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -122,6 +122,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() : * @return true if the terrain was initialized, false if an error occured */ bool ShaderTerrainMesh::generate() { + MutexHolder holder(_lock); if (!do_check_heightfield()) return false; @@ -461,6 +462,7 @@ bool ShaderTerrainMesh::safe_to_combine() const { * @copydoc PandaNode::add_for_draw() */ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &data) { + MutexHolder holder(_lock); // Make sure the terrain was properly initialized, and the geom was created // successfully @@ -711,6 +713,7 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) { * @return World-Space point */ LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const { + MutexHolder holder(_lock); nassertr(_heightfield_tex != nullptr, LPoint3(0)); // Heightfield not set yet nassertr(_heightfield_tex->has_ram_image(), LPoint3(0)); // Heightfield not in memory diff --git a/panda/src/grutil/shaderTerrainMesh.h b/panda/src/grutil/shaderTerrainMesh.h index c57a5d4e74..9c4f8d9a3a 100644 --- a/panda/src/grutil/shaderTerrainMesh.h +++ b/panda/src/grutil/shaderTerrainMesh.h @@ -25,6 +25,8 @@ #include "configVariableInt.h" #include "pStatCollector.h" #include "filename.h" +#include "pmutex.h" +#include "mutexHolder.h" #include extern ConfigVariableBool stm_use_hexagonal_layout; @@ -160,6 +162,7 @@ private: void do_emit_chunk(Chunk* chunk, TraversalData* data); bool do_check_lod_matches(Chunk* chunk, TraversalData* data); + Mutex _lock; Chunk _base_chunk; size_t _size; size_t _chunk_size; From 4c67861a289e3e0816cb1ee166d9761a4d4ebc84 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 21:03:09 +0200 Subject: [PATCH 12/18] samples: set heightfield to clamp mode in shader-terrain sample --- samples/shader-terrain/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/shader-terrain/main.py b/samples/shader-terrain/main.py index 92ff61467a..10bffb3767 100644 --- a/samples/shader-terrain/main.py +++ b/samples/shader-terrain/main.py @@ -34,7 +34,10 @@ class ShaderTerrainDemo(ShowBase): # Set a heightfield, the heightfield should be a 16-bit png and # have a quadratic size of a power of two. - self.terrain_node.heightfield = self.loader.loadTexture("heightfield.png") + heightfield = self.loader.loadTexture("heightfield.png") + heightfield.wrap_u = SamplerState.WM_clamp + heightfield.wrap_v = SamplerState.WM_clamp + self.terrain_node.heightfield = heightfield # Set the target triangle width. For a value of 10.0 for example, # the terrain will attempt to make every triangle 10 pixels wide on screen. From a099c852459be8a59ef1d8044c09ceea869cb3ed Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 25 Sep 2018 21:18:48 +0200 Subject: [PATCH 13/18] ShaderGenerator: fix broken handling of CO_undefined alpha operand Fixes #394 --- panda/src/pgraphnodes/shaderGenerator.cxx | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index d954ddcafc..1b08e921b0 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -53,14 +53,22 @@ TypeHandle ShaderGenerator::_type_handle; #ifdef HAVE_CG -#define PACK_COMBINE(src0, op0, src1, op1, src2, op2) ( \ - ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | \ - ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | \ - ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u)) - #define UNPACK_COMBINE_SRC(from, n) (TextureStage::CombineSource)((from >> ((uint16_t)n * 5u)) & 7u) #define UNPACK_COMBINE_OP(from, n) (TextureStage::CombineOperand)(((from >> (((uint16_t)n * 5u) + 3u)) & 3u) + 1u) +static inline uint16_t +pack_combine(TextureStage::CombineSource src0, TextureStage::CombineOperand op0, + TextureStage::CombineSource src1, TextureStage::CombineOperand op1, + TextureStage::CombineSource src2, TextureStage::CombineOperand op2) { + if (op0 == TextureStage::CO_undefined) op0 = TextureStage::CO_src_alpha; + if (op1 == TextureStage::CO_undefined) op1 = TextureStage::CO_src_alpha; + if (op2 == TextureStage::CO_undefined) op2 = TextureStage::CO_src_alpha; + + return ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) | + ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) | + ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u); +} + static PStatCollector lookup_collector("*:Munge:ShaderGen:Lookup"); static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize"); @@ -399,11 +407,12 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { if (stage->get_alpha_scale() == 4) { info._flags |= ShaderKey::TF_alpha_scale_4; } - info._combine_rgb = PACK_COMBINE( + + info._combine_rgb = pack_combine( stage->get_combine_rgb_source0(), stage->get_combine_rgb_operand0(), stage->get_combine_rgb_source1(), stage->get_combine_rgb_operand1(), stage->get_combine_rgb_source2(), stage->get_combine_rgb_operand2()); - info._combine_alpha = PACK_COMBINE( + info._combine_alpha = pack_combine( stage->get_combine_alpha_source0(), stage->get_combine_alpha_operand0(), stage->get_combine_alpha_source1(), stage->get_combine_alpha_operand1(), stage->get_combine_alpha_source2(), stage->get_combine_alpha_operand2()); From 5ae38a8a924d839cba7a5d0a99112909c533787c Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 27 Sep 2018 22:14:47 +0200 Subject: [PATCH 14/18] Fix crash loading from search path Possible fix for #395 --- dtool/src/prc/configVariableSearchPath.I | 8 ++++++-- dtool/src/prc/configVariableSearchPath.h | 2 +- panda/src/pgraph/loader.cxx | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dtool/src/prc/configVariableSearchPath.I b/dtool/src/prc/configVariableSearchPath.I index 89a9ac7df9..37c4383c02 100644 --- a/dtool/src/prc/configVariableSearchPath.I +++ b/dtool/src/prc/configVariableSearchPath.I @@ -229,9 +229,13 @@ get_num_directories() const { /** * Returns the nth directory on the search list. */ -INLINE const Filename &ConfigVariableSearchPath:: +INLINE Filename ConfigVariableSearchPath:: get_directory(size_t n) const { - return get_value().get_directory(n); + Filename dir; + _lock.lock(); + dir = _cache.get_directory(n); + _lock.unlock(); + return dir; } /** diff --git a/dtool/src/prc/configVariableSearchPath.h b/dtool/src/prc/configVariableSearchPath.h index 12ad1c54c9..ca575c078a 100644 --- a/dtool/src/prc/configVariableSearchPath.h +++ b/dtool/src/prc/configVariableSearchPath.h @@ -66,7 +66,7 @@ PUBLISHED: INLINE bool is_empty() const; INLINE size_t get_num_directories() const; - INLINE const Filename &get_directory(size_t n) const; + INLINE Filename get_directory(size_t n) const; MAKE_SEQ(get_directories, get_num_directories, get_directory); MAKE_SEQ_PROPERTY(directories, get_num_directories, get_directory); diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index 03ae0e0587..3c04578a55 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -207,7 +207,7 @@ load_file(const Filename &filename, const LoaderOptions &options) const { if (search) { // Look for the file along the model path. - const ConfigVariableSearchPath &model_path = get_model_path(); + DSearchPath model_path(get_model_path()); int num_dirs = model_path.get_num_directories(); for (int i = 0; i < num_dirs; ++i) { Filename pathname(model_path.get_directory(i), this_filename); From c43d9b50029c59db212be10c0d31f89653f7b6db Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 27 Sep 2018 22:23:10 +0200 Subject: [PATCH 15/18] tests: fix issue with double-precision TextNode tests --- tests/text/test_textnode.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/text/test_textnode.py b/tests/text/test_textnode.py index 1f9a8349b9..e96c9e5371 100644 --- a/tests/text/test_textnode.py +++ b/tests/text/test_textnode.py @@ -82,10 +82,10 @@ def test_textnode_flatten_color(): path.set_color(color) path.flatten_strong() - assert text.text_color == color - assert text.shadow_color == color - assert text.frame_color == color - assert text.card_color == color + assert text.text_color.almost_equal(color) + assert text.shadow_color.almost_equal(color) + assert text.frame_color.almost_equal(color) + assert text.card_color.almost_equal(color) def test_textnode_flatten_colorscale(): @@ -100,7 +100,7 @@ def test_textnode_flatten_colorscale(): path.set_color_scale(color) path.flatten_strong() - assert text.text_color == (.5, 0, 0, 0) - assert text.shadow_color == (0, .5, 0, 0) - assert text.frame_color == (0, 0, .5, 0) - assert text.card_color == (0, 0, 0, .5) + assert text.text_color.almost_equal((.5, 0, 0, 0)) + assert text.shadow_color.almost_equal((0, .5, 0, 0)) + assert text.frame_color.almost_equal((0, 0, .5, 0)) + assert text.card_color.almost_equal((0, 0, 0, .5)) From fac82e6dcaeda1cb205ecdb1f7fcd0a04c883893 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 28 Sep 2018 13:19:36 +0200 Subject: [PATCH 16/18] pgraph: fix precision issues with Color(Scale)Attrib quantization --- panda/src/pgraph/colorAttrib.cxx | 10 +++++----- panda/src/pgraph/colorScaleAttrib.cxx | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/panda/src/pgraph/colorAttrib.cxx b/panda/src/pgraph/colorAttrib.cxx index 7bfbecee79..129f5a9184 100644 --- a/panda/src/pgraph/colorAttrib.cxx +++ b/panda/src/pgraph/colorAttrib.cxx @@ -133,17 +133,17 @@ get_hash_impl() const { } /** - * Quantizes the color color to the nearest multiple of 1000, just to prevent + * Quantizes the flat color to the nearest multiple of 1024, just to prevent * runaway accumulation of only slightly-different ColorAttribs. */ void ColorAttrib:: quantize_color() { switch (_type) { case T_flat: - _color[0] = cfloor(_color[0] * 1000.0f + 0.5f) * 0.001f; - _color[1] = cfloor(_color[1] * 1000.0f + 0.5f) * 0.001f; - _color[2] = cfloor(_color[2] * 1000.0f + 0.5f) * 0.001f; - _color[3] = cfloor(_color[3] * 1000.0f + 0.5f) * 0.001f; + _color[0] = cfloor(_color[0] * 1024.0f + 0.5f) / 1024.0f; + _color[1] = cfloor(_color[1] * 1024.0f + 0.5f) / 1024.0f; + _color[2] = cfloor(_color[2] * 1024.0f + 0.5f) / 1024.0f; + _color[3] = cfloor(_color[3] * 1024.0f + 0.5f) / 1024.0f; break; case T_off: diff --git a/panda/src/pgraph/colorScaleAttrib.cxx b/panda/src/pgraph/colorScaleAttrib.cxx index 088c3142ef..04001dd0ab 100644 --- a/panda/src/pgraph/colorScaleAttrib.cxx +++ b/panda/src/pgraph/colorScaleAttrib.cxx @@ -230,15 +230,15 @@ invert_compose_impl(const RenderAttrib *other) const { } /** - * Quantizes the color scale to the nearest multiple of 1000, just to prevent + * Quantizes the color scale to the nearest multiple of 1024, just to prevent * runaway accumulation of only slightly-different ColorScaleAttribs. */ void ColorScaleAttrib:: quantize_scale() { - _scale[0] = cfloor(_scale[0] * 1000.0f + 0.5f) * 0.001f; - _scale[1] = cfloor(_scale[1] * 1000.0f + 0.5f) * 0.001f; - _scale[2] = cfloor(_scale[2] * 1000.0f + 0.5f) * 0.001f; - _scale[3] = cfloor(_scale[3] * 1000.0f + 0.5f) * 0.001f; + _scale[0] = cfloor(_scale[0] * 1024.0f + 0.5f) / 1024.0f; + _scale[1] = cfloor(_scale[1] * 1024.0f + 0.5f) / 1024.0f; + _scale[2] = cfloor(_scale[2] * 1024.0f + 0.5f) / 1024.0f; + _scale[3] = cfloor(_scale[3] * 1024.0f + 0.5f) / 1024.0f; } /** From cd9673ae9b10123e9376a52fd29bb93e2d39092f Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 28 Sep 2018 14:19:40 +0200 Subject: [PATCH 17/18] tests: fix pytest deprecation warnings --- tests/interrogate/test_property.py | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/interrogate/test_property.py b/tests/interrogate/test_property.py index e39f45f9dc..4d521d03e5 100755 --- a/tests/interrogate/test_property.py +++ b/tests/interrogate/test_property.py @@ -2,7 +2,11 @@ import sys import pytest from panda3d import core from contextlib import contextmanager -import collections + +if sys.version_info >= (3, 3): + import collections.abc as collections_abc +else: + import _abcoll as collections_abc @contextmanager @@ -52,7 +56,6 @@ def test_property2(): # The next tests are for MAKE_SEQ_PROPERTY. -@pytest.fixture def seq_property(*items): """ Returns a sequence property initialized with the given items. """ @@ -73,11 +76,11 @@ item_c = core.CollisionSphere((0, 0, 0), 3) def test_seq_property_abc(): prop = seq_property() - assert isinstance(prop, collections.Container) - assert isinstance(prop, collections.Sized) - assert isinstance(prop, collections.Iterable) - assert isinstance(prop, collections.MutableSequence) - assert isinstance(prop, collections.Sequence) + assert isinstance(prop, collections_abc.Container) + assert isinstance(prop, collections_abc.Sized) + assert isinstance(prop, collections_abc.Iterable) + assert isinstance(prop, collections_abc.MutableSequence) + assert isinstance(prop, collections_abc.Sequence) def test_seq_property_empty(): @@ -411,7 +414,6 @@ def test_seq_property_extend(): # The next tests are for MAKE_MAP_PROPERTY. -@pytest.fixture def map_property(**items): """ Returns a mapping property initialized with the given values. """ @@ -425,11 +427,11 @@ def map_property(**items): def test_map_property_abc(): prop = map_property() - assert isinstance(prop, collections.Container) - assert isinstance(prop, collections.Sized) - assert isinstance(prop, collections.Iterable) - assert isinstance(prop, collections.MutableMapping) - assert isinstance(prop, collections.Mapping) + assert isinstance(prop, collections_abc.Container) + assert isinstance(prop, collections_abc.Sized) + assert isinstance(prop, collections_abc.Iterable) + assert isinstance(prop, collections_abc.MutableMapping) + assert isinstance(prop, collections_abc.Mapping) def test_map_property_empty(): @@ -607,19 +609,19 @@ def test_map_property_update(): def test_map_property_keys(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.keys(), collections.MappingView) + assert isinstance(prop.keys(), collections_abc.MappingView) assert frozenset(prop.keys()) == frozenset(('key', 'key2')) def test_map_property_values(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.values(), collections.ValuesView) + assert isinstance(prop.values(), collections_abc.ValuesView) assert frozenset(prop.values()) == frozenset(('value', 'value2')) def test_map_property_items(): prop = map_property(key='value', key2='value2') - assert isinstance(prop.items(), collections.MappingView) + assert isinstance(prop.items(), collections_abc.MappingView) assert frozenset(prop.items()) == frozenset((('key', 'value'), ('key2', 'value2'))) From 183d66a5c2620034854a79e2e2e3b6f78f7e59f9 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 28 Sep 2018 14:20:47 +0200 Subject: [PATCH 18/18] pipeline: fix compiler warning due to PAUSE symbol redefined --- panda/src/pipeline/conditionVarSpinlockImpl.cxx | 2 ++ panda/src/pipeline/reMutexSpinlockImpl.cxx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/panda/src/pipeline/conditionVarSpinlockImpl.cxx b/panda/src/pipeline/conditionVarSpinlockImpl.cxx index d35d8cd1f7..2b4be0d4e7 100644 --- a/panda/src/pipeline/conditionVarSpinlockImpl.cxx +++ b/panda/src/pipeline/conditionVarSpinlockImpl.cxx @@ -58,4 +58,6 @@ wait(double timeout) { _mutex.lock(); } +#undef PAUSE + #endif // MUTEX_SPINLOCK diff --git a/panda/src/pipeline/reMutexSpinlockImpl.cxx b/panda/src/pipeline/reMutexSpinlockImpl.cxx index 0de12f986f..de0b2c0355 100644 --- a/panda/src/pipeline/reMutexSpinlockImpl.cxx +++ b/panda/src/pipeline/reMutexSpinlockImpl.cxx @@ -54,4 +54,6 @@ try_lock() { } } +#undef PAUSE + #endif // MUTEX_SPINLOCK