diff --git a/direct/src/gui/DirectFrame.py b/direct/src/gui/DirectFrame.py index e078b28021..684fda005c 100644 --- a/direct/src/gui/DirectFrame.py +++ b/direct/src/gui/DirectFrame.py @@ -61,7 +61,14 @@ class DirectFrame(DirectGuiWidget): def destroy(self): DirectGuiWidget.destroy(self) - def setText(self): + def clearText(self): + self['text'] = None + self.setText() + + def setText(self, text=None): + if text is not None: + self['text'] = text + # Determine if user passed in single string or a sequence if self['text'] == None: textList = (None,) * self['numStates'] @@ -100,7 +107,14 @@ class DirectFrame(DirectGuiWidget): sort = DGG.TEXT_SORT_INDEX, ) - def setGeom(self): + def clearGeom(self): + self['geom'] = None + self.setGeom() + + def setGeom(self, geom=None): + if geom is not None: + self['geom'] = geom + # Determine argument type geom = self['geom'] @@ -142,7 +156,14 @@ class DirectFrame(DirectGuiWidget): geom = geom, scale = 1, sort = DGG.GEOM_SORT_INDEX) - def setImage(self): + def clearImage(self): + self['image'] = None + self.setImage() + + def setImage(self, image=None): + if image is not None: + self['image'] = image + # Determine argument type arg = self['image'] if arg == None: diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 17c9cea25e..924b396616 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -934,8 +934,8 @@ class Loader(DirectObject): just as in loadModel(); otherwise, the loading happens before loadSound() returns.""" - if not isinstance(soundPath, (MovieAudio, tuple, list, set)): - # We were given a single sound pathname. + if not isinstance(soundPath, (tuple, list, set)): + # We were given a single sound pathname or a MovieAudio instance. soundList = [soundPath] gotList = False else: diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h index ff52c6291a..36da6b2b0b 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -49,6 +49,8 @@ // interrogate pass (CPPPARSER isn't defined), this maps to public. #define PUBLISHED __published +#define PHAVE_ATOMIC 1 + typedef int ios_openmode; typedef int ios_fmtflags; typedef int ios_iostate; @@ -93,6 +95,23 @@ typedef std::ios::iostate ios_iostate; typedef std::ios::seekdir ios_seekdir; #endif +#ifdef _MSC_VER +#define ALWAYS_INLINE __forceinline +#elif defined(__GNUC__) +#define ALWAYS_INLINE __attribute__((always_inline)) inline +#else +#define ALWAYS_INLINE inline +#endif + +#ifdef FORCE_INLINING +// If FORCE_INLINING is defined, we use the keyword __forceinline, which tells +// MS VC++ to override its internal benefit heuristic and inline the fn if it +// is technically possible to do so. +#define INLINE ALWAYS_INLINE +#else +#define INLINE inline +#endif + // Apple has an outdated libstdc++. Not all is lost, though, as we can fill // in some important missing functions. #if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 @@ -115,24 +134,38 @@ namespace std { } template struct owner_less; + + typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, + } memory_order; + + #define ATOMIC_FLAG_INIT { 0 } + class atomic_flag { + bool _flag; + + public: + atomic_flag() noexcept = default; + ALWAYS_INLINE constexpr atomic_flag(bool flag) noexcept : _flag(flag) {} + atomic_flag(const atomic_flag &) = delete; + ~atomic_flag() noexcept = default; + atomic_flag &operator = (const atomic_flag&) = delete; + + ALWAYS_INLINE bool test_and_set(memory_order order = memory_order_seq_cst) noexcept { + return __atomic_test_and_set(&_flag, order); + } + ALWAYS_INLINE void clear(memory_order order = memory_order_seq_cst) noexcept { + __atomic_clear(&_flag, order); + } + }; }; -#endif - -#ifdef _MSC_VER -#define ALWAYS_INLINE __forceinline -#elif defined(__GNUC__) -#define ALWAYS_INLINE __attribute__((always_inline)) inline #else -#define ALWAYS_INLINE inline -#endif - -#ifdef FORCE_INLINING -// If FORCE_INLINING is defined, we use the keyword __forceinline, which tells -// MS VC++ to override its internal benefit heuristic and inline the fn if it -// is technically possible to do so. -#define INLINE ALWAYS_INLINE -#else -#define INLINE inline +// Expect that we have access to the header. +#define PHAVE_ATOMIC 1 #endif // Determine the availability of C++11 features. diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.I b/dtool/src/dtoolbase/mutexSpinlockImpl.I index b3eb084181..53ce89445f 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.I +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.I @@ -11,13 +11,6 @@ * @date 2006-04-11 */ -/** - * - */ -constexpr MutexSpinlockImpl:: -MutexSpinlockImpl() : _lock(0) { -} - /** * */ @@ -33,7 +26,7 @@ lock() { */ INLINE bool MutexSpinlockImpl:: try_lock() { - return (AtomicAdjust::compare_and_exchange(_lock, 0, 1) == 0); + return !_flag.test_and_set(std::memory_order_acquire); } /** @@ -41,5 +34,5 @@ try_lock() { */ INLINE void MutexSpinlockImpl:: unlock() { - AtomicAdjust::set(_lock, 0); + _flag.clear(std::memory_order_release); } diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.cxx b/dtool/src/dtoolbase/mutexSpinlockImpl.cxx index 32a84fa28f..73f2683ff2 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.cxx +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.cxx @@ -17,12 +17,21 @@ #include "mutexSpinlockImpl.h" +#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64) +#include +#define PAUSE() _mm_pause() +#else +#define PAUSE() +#endif + /** * */ void MutexSpinlockImpl:: do_lock() { - while (AtomicAdjust::compare_and_exchange(_lock, 0, 1) != 0) { + // Loop until we changed the flag from 0 to 1 (and it wasn't already 1). + while (_flag.test_and_set(std::memory_order_acquire)) { + PAUSE(); } } diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.h b/dtool/src/dtoolbase/mutexSpinlockImpl.h index c7dfb72cf2..cd858f5551 100644 --- a/dtool/src/dtoolbase/mutexSpinlockImpl.h +++ b/dtool/src/dtoolbase/mutexSpinlockImpl.h @@ -19,7 +19,9 @@ #ifdef MUTEX_SPINLOCK -#include "atomicAdjust.h" +#ifdef PHAVE_ATOMIC +#include +#endif /** * Uses a simple user-space spinlock to implement a mutex. It is usually not @@ -29,7 +31,7 @@ */ class EXPCL_DTOOL_DTOOLBASE MutexSpinlockImpl { public: - constexpr MutexSpinlockImpl(); + constexpr MutexSpinlockImpl() noexcept = default; MutexSpinlockImpl(const MutexSpinlockImpl ©) = delete; MutexSpinlockImpl &operator = (const MutexSpinlockImpl ©) = delete; @@ -42,7 +44,7 @@ public: private: void do_lock(); - TVOLATILE AtomicAdjust::Integer _lock; + std::atomic_flag _flag = ATOMIC_FLAG_INIT; }; #include "mutexSpinlockImpl.I" diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index e7a2f828d9..9836a23bfc 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -254,14 +254,13 @@ call_function(ostream &out, int indent_level, bool convert_result, &parser); out << " = " << call << ";\n"; - // MOVE() expands to std::move() when we are compiling with a compiler - // that supports rvalue references. It basically turns an lvalue into + // Use of the C++11 std::move function basically turns an lvalue into // an rvalue, allowing a move constructor to be called instead of a // copy constructor (since we won't be using the return value any // more), which is usually more efficient if it exists. If it // doesn't, it shouldn't do any harm. string new_str = - _return_type->prepare_return_expr(out, indent_level, "MOVE(result)"); + _return_type->prepare_return_expr(out, indent_level, "std::move(result)"); return_expr = _return_type->get_return_expr(new_str); } else { diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 5752fe126b..8d2b0f2288 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -5491,7 +5491,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, // Use move constructor when available for functions that take an // actual PointerTo. This eliminates an unref()ref() pair. - pexpr_string = "MOVE(" + param_name + "_this)"; + pexpr_string = "std::move(" + param_name + "_this)"; } else { // This is a move-assignable type, such as TypeHandle or LVecBase4. @@ -6156,7 +6156,7 @@ write_function_instance(ostream &out, FunctionRemap *remap, indent(out, indent_level) << "return true;\n"; } else if (TypeManager::is_reference_count(remap->_cpptype)) { - indent(out, indent_level) << "coerced = MOVE(" << return_expr << ");\n"; + indent(out, indent_level) << "coerced = std::move(" << return_expr << ");\n"; indent(out, indent_level) << "return true;\n"; } else { diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx index d11bd22ff6..964d38020c 100644 --- a/dtool/src/interrogate/interrogate.cxx +++ b/dtool/src/interrogate/interrogate.cxx @@ -516,7 +516,7 @@ main(int argc, char **argv) { cerr << "Error parsing file: '" << argv[i] << "'\n"; exit(1); } - builder.add_source_file(filename); + builder.add_source_file(filename.to_os_generic()); } // Now that we've parsed all the source code, change the way things are diff --git a/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx index f5e7c35b39..9f217076cb 100644 --- a/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx +++ b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx @@ -40,7 +40,7 @@ pass_parameter(std::ostream &out, const std::string &variable_name) { if (variable_name.size() > 1 && variable_name[0] == '&') { // Prevent generating something like *¶m Also, if this is really some // local type, we can presumably just move it? - out << "MOVE(" << variable_name.substr(1) << ")"; + out << "std::move(" << variable_name.substr(1) << ")"; } else { out << "*" << variable_name; } diff --git a/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx index 6c8e8b05a9..4e8d1fad90 100644 --- a/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx +++ b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx @@ -42,7 +42,7 @@ pass_parameter(std::ostream &out, const std::string &variable_name) { // this parameter is an rvalue reference, but CPPParser can't know that, // and it might have an overload that takes an rvalue reference. It // shouldn't hurt either way. - out << "MOVE(" << variable_name.substr(1) << ")"; + out << "std::move(" << variable_name.substr(1) << ")"; } else { out << "*" << variable_name; } diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 00dd2c3cd3..e371fbd3e8 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2271,6 +2271,7 @@ DTOOL_CONFIG=[ ("OS_SIMPLE_THREADS", '1', '1'), ("DEBUG_THREADS", 'UNDEF', 'UNDEF'), ("HAVE_POSIX_THREADS", 'UNDEF', '1'), + ("MUTEX_SPINLOCK", 'UNDEF', 'UNDEF'), ("HAVE_AUDIO", '1', '1'), ("NOTIFY_DEBUG", 'UNDEF', 'UNDEF'), ("DO_PSTATS", 'UNDEF', 'UNDEF'), diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index e490f1c05e..74ee15d7f1 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -28,7 +28,7 @@ default_platform = get_platform() if default_platform.startswith("linux-"): # Is this manylinux1? - if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"): + if (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"): default_platform = default_platform.replace("linux", "manylinux1") 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" diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 0e5687d490..0921350629 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -3463,7 +3463,7 @@ do_issue_material() { cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data()); cur_material.Power = material->get_shininess(); - if (material->has_diffuse()) { + if (material->has_diffuse() || material->has_base_color()) { // If the material specifies an diffuse color, use it. set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL); } else { @@ -3476,7 +3476,7 @@ do_issue_material() { set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1); } } - if (material->has_ambient()) { + if (material->has_ambient() || material->has_base_color()) { // If the material specifies an ambient color, use it. set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL); } else { @@ -3490,7 +3490,7 @@ do_issue_material() { } } - if (material->has_specular()) { + if (material->has_specular() || material->has_base_color()) { set_render_state(D3DRS_SPECULARENABLE, TRUE); } else { set_render_state(D3DRS_SPECULARENABLE, FALSE); 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/egg/eggVertexPool.cxx b/panda/src/egg/eggVertexPool.cxx index 8fa3b5e637..6f2768599e 100644 --- a/panda/src/egg/eggVertexPool.cxx +++ b/panda/src/egg/eggVertexPool.cxx @@ -677,7 +677,15 @@ transform(const LMatrix4d &mat) { typedef pvector Verts; Verts verts; verts.reserve(size()); + + // Work around MSVC 2017 compiler bug, see GitHub issue #379 +#ifdef _MSC_VER + for (const IndexVertices::value_type &v : _index_vertices) { + verts.push_back(v.second); + } +#else std::copy(begin(), end(), std::back_inserter(verts)); +#endif Verts::const_iterator vi; for (vi = verts.begin(); vi != verts.end(); ++vi) { 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); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index fb22fdb644..48b9112387 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -7627,7 +7627,7 @@ do_issue_material() { call_glMaterialfv(face, GL_EMISSION, material->get_emission()); glMaterialf(face, GL_SHININESS, max(min(material->get_shininess(), (PN_stdfloat)128), (PN_stdfloat)0)); - if (material->has_ambient() && material->has_diffuse()) { + if ((material->has_ambient() && material->has_diffuse()) || material->has_base_color()) { // The material has both an ambient and diffuse specified. This means we // do not need glMaterialColor(). glDisable(GL_COLOR_MATERIAL); diff --git a/panda/src/gobj/material.cxx b/panda/src/gobj/material.cxx index 24bc0fb590..091c7eff2f 100644 --- a/panda/src/gobj/material.cxx +++ b/panda/src/gobj/material.cxx @@ -422,7 +422,7 @@ void Material:: write(std::ostream &out, int indent_level) const { indent(out, indent_level) << "Material " << get_name() << "\n"; if (has_base_color()) { - indent(out, indent_level + 2) << "base_color = " << get_ambient() << "\n"; + indent(out, indent_level + 2) << "base_color = " << get_base_color() << "\n"; } if (has_ambient()) { indent(out, indent_level + 2) << "ambient = " << get_ambient() << "\n"; diff --git a/panda/src/movies/movieAudio.h b/panda/src/movies/movieAudio.h index cce8eb3465..819f501e35 100644 --- a/panda/src/movies/movieAudio.h +++ b/panda/src/movies/movieAudio.h @@ -43,7 +43,7 @@ class MovieAudioCursor; */ class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable { PUBLISHED: - MovieAudio(const std::string &name = "Blank Audio"); + explicit MovieAudio(const std::string &name = "Blank Audio"); virtual ~MovieAudio(); virtual PT(MovieAudioCursor) open(); static PT(MovieAudio) get(const Filename &name); diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index f52d0f4579..d954ddcafc 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -258,6 +258,11 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { // states to be rehashed. mat->mark_used_by_auto_shader(); key._material_flags = mat->get_flags(); + + if ((key._material_flags & Material::F_base_color) != 0) { + key._material_flags |= (Material::F_diffuse | Material::F_specular | Material::F_ambient); + key._material_flags &= ~Material::F_base_color; + } } // Break out the lights by type. diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index a266547ced..aeff5bff25 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -147,8 +147,8 @@ void PGItem:: transform_changed() { LightReMutexHolder holder(_lock); PandaNode::transform_changed(); - if (has_notify()) { - get_notify()->item_transform_changed(this); + if (_notify != nullptr) { + _notify->item_transform_changed(this); } } @@ -161,8 +161,8 @@ void PGItem:: draw_mask_changed() { LightReMutexHolder holder(_lock); PandaNode::draw_mask_changed(); - if (has_notify()) { - get_notify()->item_draw_mask_changed(this); + if (_notify != nullptr) { + _notify->item_draw_mask_changed(this); } } @@ -530,8 +530,8 @@ enter_region(const MouseWatcherParameter ¶m) { play_sound(event); throw_event(event, EventParameter(ep)); - if (has_notify()) { - get_notify()->item_enter(this, param); + if (_notify != nullptr) { + _notify->item_enter(this, param); } } @@ -554,8 +554,8 @@ exit_region(const MouseWatcherParameter ¶m) { play_sound(event); throw_event(event, EventParameter(ep)); - if (has_notify()) { - get_notify()->item_exit(this, param); + if (_notify != nullptr) { + _notify->item_exit(this, param); } // pgui_cat.info() << get_name() << "::exit()" << endl; @@ -580,8 +580,8 @@ within_region(const MouseWatcherParameter ¶m) { play_sound(event); throw_event(event, EventParameter(ep)); - if (has_notify()) { - get_notify()->item_within(this, param); + if (_notify != nullptr) { + _notify->item_within(this, param); } } @@ -602,8 +602,8 @@ without_region(const MouseWatcherParameter ¶m) { play_sound(event); throw_event(event, EventParameter(ep)); - if (has_notify()) { - get_notify()->item_without(this, param); + if (_notify != nullptr) { + _notify->item_without(this, param); } } @@ -623,8 +623,8 @@ focus_in() { play_sound(event); throw_event(event); - if (has_notify()) { - get_notify()->item_focus_in(this); + if (_notify != nullptr) { + _notify->item_focus_in(this); } } @@ -644,8 +644,8 @@ focus_out() { play_sound(event); throw_event(event); - if (has_notify()) { - get_notify()->item_focus_out(this); + if (_notify != nullptr) { + _notify->item_focus_out(this); } } @@ -673,8 +673,8 @@ press(const MouseWatcherParameter ¶m, bool background) { throw_event(event, EventParameter(ep)); } - if (has_notify()) { - get_notify()->item_press(this, param); + if (_notify != nullptr) { + _notify->item_press(this, param); } } @@ -697,8 +697,8 @@ release(const MouseWatcherParameter ¶m, bool background) { throw_event(event, EventParameter(ep)); } - if (has_notify()) { - get_notify()->item_release(this, param); + if (_notify != nullptr) { + _notify->item_release(this, param); } } @@ -757,8 +757,8 @@ move(const MouseWatcherParameter ¶m) { << *this << "::move(" << param << ")\n"; } - if (has_notify()) { - get_notify()->item_move(this, param); + if (_notify != nullptr) { + _notify->item_move(this, param); } } @@ -1169,9 +1169,10 @@ mouse_to_local(const LPoint2 &mouse_point) const { */ void PGItem:: frame_changed() { + LightReMutexHolder holder(_lock); mark_frames_stale(); - if (has_notify()) { - get_notify()->item_frame_changed(this); + if (_notify != nullptr) { + _notify->item_frame_changed(this); } } diff --git a/panda/src/pgui/pgScrollFrame.cxx b/panda/src/pgui/pgScrollFrame.cxx index a41916d3c9..9631f66685 100644 --- a/panda/src/pgui/pgScrollFrame.cxx +++ b/panda/src/pgui/pgScrollFrame.cxx @@ -19,19 +19,18 @@ TypeHandle PGScrollFrame::_type_handle; * */ PGScrollFrame:: -PGScrollFrame(const std::string &name) : PGVirtualFrame(name) +PGScrollFrame(const std::string &name) : + PGVirtualFrame(name), + _needs_remanage(false), + _needs_recompute_clip(false), + _has_virtual_frame(false), + _virtual_frame(0.0f, 0.0f, 0.0f, 0.0f), + _manage_pieces(false), + _auto_hide(false) { - set_cull_callback(); + _canvas_computed.test_and_set(); - _needs_remanage = false; - _needs_recompute_canvas = false; - _needs_recompute_clip = false; - _has_virtual_frame = false; - _virtual_frame.set(0.0f, 0.0f, 0.0f, 0.0f); - _manage_pieces = false; - _auto_hide = false; - _horizontal_slider = nullptr; - _vertical_slider = nullptr; + set_cull_callback(); } /** @@ -55,8 +54,8 @@ PGScrollFrame(const PGScrollFrame ©) : _auto_hide(copy._auto_hide) { _needs_remanage = false; - _needs_recompute_canvas = true; _needs_recompute_clip = true; + _canvas_computed.clear(); } /** @@ -97,7 +96,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { if (_needs_recompute_clip) { recompute_clip(); } - if (_needs_recompute_canvas) { + if (!_canvas_computed.test_and_set()) { recompute_canvas(); } return PGVirtualFrame::cull_callback(trav, data); @@ -257,7 +256,7 @@ remanage() { // Showing or hiding one of the scroll bars might have set this flag again // indirectly; we clear it again to avoid a feedback loop. _needs_remanage = false; -} + } // Are either or both of the scroll bars hidden? if (got_horizontal && _horizontal_slider->is_overall_hidden()) { @@ -329,19 +328,20 @@ item_draw_mask_changed(PGItem *) { */ void PGScrollFrame:: slider_bar_adjust(PGSliderBar *) { - LightReMutexHolder holder(_lock); - _needs_recompute_canvas = true; + // Indicate that recompute_canvas() needs to be called. + _canvas_computed.clear(); } /** * Recomputes the clipping window of the PGScrollFrame, based on the position * of the slider bars. + * + * Assumes the lock is held. */ void PGScrollFrame:: recompute_clip() { - LightReMutexHolder holder(_lock); _needs_recompute_clip = false; - _needs_recompute_canvas = true; + _canvas_computed.clear(); // Figure out how to remove the scroll bars from the clip region. LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame()); @@ -361,34 +361,40 @@ recompute_clip() { /** * Recomputes the portion of the virtual canvas that is visible within the * PGScrollFrame, based on the values of the slider bars. + * + * Assumes the lock is held. */ void PGScrollFrame:: recompute_canvas() { - LightReMutexHolder holder(_lock); - _needs_recompute_canvas = false; + const LVecBase4 &clip = _has_clip_frame ? _clip_frame : get_frame(); - const LVecBase4 &clip = get_clip_frame(); + // Set this to true before we sample the slider bar ratios. + // If slider_bar_adjust happens to get called while we do this, no big deal, + // this method will just be called again next frame. + _canvas_computed.test_and_set(); - PN_stdfloat x = interpolate_canvas(clip[0], clip[1], - _virtual_frame[0], _virtual_frame[1], - _horizontal_slider); + PN_stdfloat cx, cy; + cx = interpolate_canvas(clip[0], clip[1], + _virtual_frame[0], _virtual_frame[1], + _horizontal_slider); - PN_stdfloat y = interpolate_canvas(clip[3], clip[2], - _virtual_frame[3], _virtual_frame[2], - _vertical_slider); + cy = interpolate_canvas(clip[3], clip[2], + _virtual_frame[3], _virtual_frame[2], + _vertical_slider); - get_canvas_node()->set_transform(TransformState::make_pos(LVector3::rfu(x, 0, y))); + _canvas_node->set_transform(TransformState::make_pos(LVector3::rfu(cx, 0, cy))); } /** * Computes the linear translation that should be applied to the virtual * canvas node, based on the corresponding slider bar's position. + * + * Assumes the lock is held. */ PN_stdfloat PGScrollFrame:: interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max, PN_stdfloat canvas_min, PN_stdfloat canvas_max, PGSliderBar *slider_bar) { - LightReMutexHolder holder(_lock); PN_stdfloat t = 0.0f; if (slider_bar != nullptr) { t = slider_bar->get_ratio(); diff --git a/panda/src/pgui/pgScrollFrame.h b/panda/src/pgui/pgScrollFrame.h index de01954e0d..bb8b3a3af3 100644 --- a/panda/src/pgui/pgScrollFrame.h +++ b/panda/src/pgui/pgScrollFrame.h @@ -20,6 +20,10 @@ #include "pgSliderBarNotify.h" #include "pgSliderBar.h" +#ifdef PHAVE_ATOMIC +#include +#endif + /** * This is a special kind of frame that pretends to be much larger than it * actually is. You can scroll through the frame, as if you're looking @@ -92,7 +96,7 @@ private: private: bool _needs_remanage; bool _needs_recompute_clip; - bool _needs_recompute_canvas; + std::atomic_flag _canvas_computed; bool _has_virtual_frame; LVecBase4 _virtual_frame; diff --git a/panda/src/pgui/pgVirtualFrame.h b/panda/src/pgui/pgVirtualFrame.h index c8aff4fca6..d284b08ab0 100644 --- a/panda/src/pgui/pgVirtualFrame.h +++ b/panda/src/pgui/pgVirtualFrame.h @@ -72,7 +72,7 @@ protected: private: void setup_child_nodes(); -private: +protected: bool _has_clip_frame; LVecBase4 _clip_frame; diff --git a/panda/src/pipeline/conditionVarSpinlockImpl.cxx b/panda/src/pipeline/conditionVarSpinlockImpl.cxx index f36bc83125..d35d8cd1f7 100644 --- a/panda/src/pipeline/conditionVarSpinlockImpl.cxx +++ b/panda/src/pipeline/conditionVarSpinlockImpl.cxx @@ -16,6 +16,14 @@ #ifdef MUTEX_SPINLOCK #include "conditionVarSpinlockImpl.h" +#include "trueClock.h" + +#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64) +#include +#define PAUSE() _mm_pause() +#else +#define PAUSE() +#endif /** * @@ -23,12 +31,31 @@ void ConditionVarSpinlockImpl:: wait() { AtomicAdjust::Integer current = _event; - _mutex.release(); + _mutex.unlock(); while (AtomicAdjust::get(_event) == current) { + PAUSE(); } - _mutex.acquire(); + _mutex.lock(); +} + +/** + * + */ +void ConditionVarSpinlockImpl:: +wait(double timeout) { + TrueClock *clock = TrueClock::get_global_ptr(); + double end_time = clock->get_short_time() + timeout; + + AtomicAdjust::Integer current = _event; + _mutex.unlock(); + + while (AtomicAdjust::get(_event) == current && clock->get_short_time() < end_time) { + PAUSE(); + } + + _mutex.lock(); } #endif // MUTEX_SPINLOCK diff --git a/panda/src/pipeline/conditionVarSpinlockImpl.h b/panda/src/pipeline/conditionVarSpinlockImpl.h index 5d8da7bbf7..34b61645f8 100644 --- a/panda/src/pipeline/conditionVarSpinlockImpl.h +++ b/panda/src/pipeline/conditionVarSpinlockImpl.h @@ -37,6 +37,7 @@ public: INLINE ~ConditionVarSpinlockImpl(); void wait(); + void wait(double timeout); INLINE void notify(); INLINE void notify_all(); diff --git a/panda/src/pipeline/lightReMutexDirect.I b/panda/src/pipeline/lightReMutexDirect.I index 08bb0daa67..8484092ecc 100644 --- a/panda/src/pipeline/lightReMutexDirect.I +++ b/panda/src/pipeline/lightReMutexDirect.I @@ -11,21 +11,6 @@ * @date 2008-10-08 */ -/** - * - */ -INLINE LightReMutexDirect:: -LightReMutexDirect() -#ifndef HAVE_REMUTEXIMPL - : _cvar_impl(_lock_impl) -#endif -{ -#ifndef HAVE_REMUTEXIMPL - _locking_thread = nullptr; - _lock_count = 0; -#endif -} - /** * Alias for acquire() to match C++11 semantics. * @see acquire() diff --git a/panda/src/pipeline/lightReMutexDirect.h b/panda/src/pipeline/lightReMutexDirect.h index 56371cc443..d1bc022740 100644 --- a/panda/src/pipeline/lightReMutexDirect.h +++ b/panda/src/pipeline/lightReMutexDirect.h @@ -29,7 +29,7 @@ class Thread; */ class EXPCL_PANDA_PIPELINE LightReMutexDirect { protected: - INLINE LightReMutexDirect(); + LightReMutexDirect() = default; LightReMutexDirect(const LightReMutexDirect ©) = delete; ~LightReMutexDirect() = default; @@ -57,7 +57,7 @@ PUBLISHED: private: #ifdef HAVE_REMUTEXTRUEIMPL - mutable ReMutexImpl _impl; + mutable ReMutexTrueImpl _impl; #else // If we don't have a reentrant mutex, use the one we hand-rolled in diff --git a/panda/src/pipeline/mutexTrueImpl.h b/panda/src/pipeline/mutexTrueImpl.h index 2366159a38..5a4850a4a0 100644 --- a/panda/src/pipeline/mutexTrueImpl.h +++ b/panda/src/pipeline/mutexTrueImpl.h @@ -43,6 +43,13 @@ typedef MutexImpl MutexTrueImpl; #if HAVE_REMUTEXIMPL typedef ReMutexImpl ReMutexTrueImpl; #define HAVE_REMUTEXTRUEIMPL 1 + +#elif MUTEX_SPINLOCK +// This is defined here because it needs code from pipeline. +#include "reMutexSpinlockImpl.h" +typedef ReMutexSpinlockImpl ReMutexTrueImpl; +#define HAVE_REMUTEXTRUEIMPL 1 + #else #undef HAVE_REMUTEXTRUEIMPL #endif // HAVE_REMUTEXIMPL diff --git a/panda/src/pipeline/p3pipeline_composite2.cxx b/panda/src/pipeline/p3pipeline_composite2.cxx index e6607a6ebf..e9e928ee4c 100644 --- a/panda/src/pipeline/p3pipeline_composite2.cxx +++ b/panda/src/pipeline/p3pipeline_composite2.cxx @@ -13,6 +13,7 @@ #include "reMutex.cxx" #include "reMutexDirect.cxx" #include "reMutexHolder.cxx" +#include "reMutexSpinlockImpl.cxx" #include "thread.cxx" #include "threadDummyImpl.cxx" #include "threadPosixImpl.cxx" diff --git a/panda/src/pipeline/reMutexDirect.I b/panda/src/pipeline/reMutexDirect.I index 0785473e7c..e7fa3a6fce 100644 --- a/panda/src/pipeline/reMutexDirect.I +++ b/panda/src/pipeline/reMutexDirect.I @@ -33,7 +33,11 @@ ReMutexDirect() INLINE void ReMutexDirect:: lock() { TAU_PROFILE("void ReMutexDirect::acquire()", " ", TAU_USER); +#ifdef HAVE_REMUTEXTRUEIMPL _impl.lock(); +#else + ((ReMutexDirect *)this)->do_lock(); +#endif // HAVE_REMUTEXTRUEIMPL } /** @@ -43,7 +47,11 @@ lock() { INLINE bool ReMutexDirect:: try_lock() { TAU_PROFILE("void ReMutexDirect::try_acquire()", " ", TAU_USER); +#ifdef HAVE_REMUTEXTRUEIMPL return _impl.try_lock(); +#else + return ((ReMutexDirect *)this)->do_try_lock(); +#endif // HAVE_REMUTEXTRUEIMPL } /** @@ -53,7 +61,11 @@ try_lock() { INLINE void ReMutexDirect:: unlock() { TAU_PROFILE("void ReMutexDirect::unlock()", " ", TAU_USER); +#ifdef HAVE_REMUTEXTRUEIMPL _impl.unlock(); +#else + ((ReMutexDirect *)this)->do_unlock(); +#endif // HAVE_REMUTEXTRUEIMPL } /** diff --git a/panda/src/pipeline/reMutexDirect.cxx b/panda/src/pipeline/reMutexDirect.cxx index 7bfdcb8f8d..f6cd59e4de 100644 --- a/panda/src/pipeline/reMutexDirect.cxx +++ b/panda/src/pipeline/reMutexDirect.cxx @@ -141,11 +141,11 @@ do_elevate_lock() { * mutex). */ void ReMutexDirect:: -do_unlock() { +do_unlock(Thread *current_thread) { _lock_impl.lock(); #ifdef _DEBUG - if (_locking_thread != Thread::get_current_thread()) { + if (_locking_thread != current_thread) { std::ostringstream ostr; ostr << *_locking_thread << " attempted to release " << *this << " which it does not own"; diff --git a/panda/src/pipeline/reMutexDirect.h b/panda/src/pipeline/reMutexDirect.h index b6f5215319..faf46d0fb0 100644 --- a/panda/src/pipeline/reMutexDirect.h +++ b/panda/src/pipeline/reMutexDirect.h @@ -59,7 +59,7 @@ PUBLISHED: private: #ifdef HAVE_REMUTEXTRUEIMPL - mutable ReMutexImpl _impl; + mutable ReMutexTrueImpl _impl; #else // If we don't have a reentrant mutex, we have to hand-roll one. @@ -68,7 +68,7 @@ private: INLINE bool do_try_lock(); bool do_try_lock(Thread *current_thread); void do_elevate_lock(); - void do_unlock(); + void do_unlock(Thread *current_thread = Thread::get_current_thread()); Thread *_locking_thread; int _lock_count; diff --git a/panda/src/pipeline/reMutexSpinlockImpl.I b/panda/src/pipeline/reMutexSpinlockImpl.I new file mode 100644 index 0000000000..5f5e2d89c3 --- /dev/null +++ b/panda/src/pipeline/reMutexSpinlockImpl.I @@ -0,0 +1,23 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file reMutexSpinlockImpl.I + * @author rdb + * @date 2018-09-03 + */ + +/** + * + */ +INLINE void ReMutexSpinlockImpl:: +unlock() { + assert(_counter > 0); + if (!--_counter) { + AtomicAdjust::set_ptr(_locking_thread, nullptr); + } +} diff --git a/panda/src/pipeline/reMutexSpinlockImpl.cxx b/panda/src/pipeline/reMutexSpinlockImpl.cxx new file mode 100644 index 0000000000..0de12f986f --- /dev/null +++ b/panda/src/pipeline/reMutexSpinlockImpl.cxx @@ -0,0 +1,57 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file reMutexSpinlockImpl.cxx + * @author rdb + * @date 2018-09-03 + */ + +#include "selectThreadImpl.h" + +#ifdef MUTEX_SPINLOCK + +#include "reMutexSpinlockImpl.h" +#include "thread.h" + +#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64) +#include +#define PAUSE() _mm_pause() +#else +#define PAUSE() +#endif + +/** + * + */ +void ReMutexSpinlockImpl:: +lock() { + Thread *current_thread = Thread::get_current_thread(); + Thread *locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread); + while (locking_thread != nullptr && locking_thread != current_thread) { + PAUSE(); + locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread); + } + ++_counter; +} + +/** + * + */ +bool ReMutexSpinlockImpl:: +try_lock() { + Thread *current_thread = Thread::get_current_thread(); + Thread *locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread); + if (locking_thread == nullptr || locking_thread == current_thread) { + ++_counter; + return true; + } else { + return false; + } +} + +#endif // MUTEX_SPINLOCK diff --git a/panda/src/pipeline/reMutexSpinlockImpl.h b/panda/src/pipeline/reMutexSpinlockImpl.h new file mode 100644 index 0000000000..666ed832e3 --- /dev/null +++ b/panda/src/pipeline/reMutexSpinlockImpl.h @@ -0,0 +1,54 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file reMutexSpinlockImpl.h + * @author rdb + * @date 2018-09-03 + */ + +#ifndef REMUTEXSPINLOCKIMPL_H +#define REMUTEXSPINLOCKIMPL_H + +#include "dtoolbase.h" +#include "selectThreadImpl.h" + +#ifdef MUTEX_SPINLOCK + +#include "atomicAdjust.h" + +class Thread; + +/** + * Uses a simple user-space spinlock to implement a mutex. It is usually not + * a good idea to use this implementation, unless you are building Panda for a + * specific application on a specific SMP machine, and you are confident that + * you have at least as many CPU's as you have threads. + */ +class EXPCL_PANDA_PIPELINE ReMutexSpinlockImpl { +public: + constexpr ReMutexSpinlockImpl() noexcept = default; + ReMutexSpinlockImpl(const ReMutexSpinlockImpl ©) = delete; + + ReMutexSpinlockImpl &operator = (const ReMutexSpinlockImpl ©) = delete; + +public: + void lock(); + bool try_lock(); + INLINE void unlock(); + +private: + AtomicAdjust::Pointer _locking_thread = nullptr; + unsigned int _counter = 0; +}; + + +#include "reMutexSpinlockImpl.I" + +#endif // MUTEX_SPINLOCK + +#endif diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index c69e1ea032..a0ddb2ae38 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -1056,7 +1056,9 @@ add_collector(PStatClient::Collector *collector) { // the lock. int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2; CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size]; - memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer)); + if (_collectors != nullptr) { + memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer)); + } AtomicAdjust::set_ptr(_collectors, new_collectors); AtomicAdjust::set(_collectors_size, new_collectors_size); @@ -1091,7 +1093,9 @@ add_thread(PStatClient::InternalThread *thread) { // the lock. int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2; ThreadPointer *new_threads = new ThreadPointer[new_threads_size]; - memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer)); + if (_threads != nullptr) { + memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer)); + } // We assume that assignment to a pointer and to an int are each atomic. AtomicAdjust::set_ptr(_threads, new_threads); AtomicAdjust::set(_threads_size, new_threads_size); diff --git a/panda/src/putil/buttonHandle.I b/panda/src/putil/buttonHandle.I index c45145dd40..55edc7f61a 100644 --- a/panda/src/putil/buttonHandle.I +++ b/panda/src/putil/buttonHandle.I @@ -137,15 +137,6 @@ output(std::ostream &out) const { out << get_name(); } -/** - * Returns a special zero-valued ButtonHandle that is used to indicate no - * button. - */ -INLINE ButtonHandle ButtonHandle:: -none() { - return _none; -} - /** * ButtonHandle::none() evaluates to false, everything else evaluates to true. */ diff --git a/panda/src/putil/buttonHandle.cxx b/panda/src/putil/buttonHandle.cxx index 831a3daddd..4ce3cd5a91 100644 --- a/panda/src/putil/buttonHandle.cxx +++ b/panda/src/putil/buttonHandle.cxx @@ -14,9 +14,6 @@ #include "buttonHandle.h" #include "buttonRegistry.h" -// This is initialized to zero by static initialization. -ButtonHandle ButtonHandle::_none; - TypeHandle ButtonHandle::_type_handle; /** diff --git a/panda/src/putil/buttonHandle.h b/panda/src/putil/buttonHandle.h index ad43ad2a86..6e9dd9a40f 100644 --- a/panda/src/putil/buttonHandle.h +++ b/panda/src/putil/buttonHandle.h @@ -53,7 +53,7 @@ PUBLISHED: constexpr int get_index() const; INLINE void output(std::ostream &out) const; - INLINE static ButtonHandle none(); + constexpr static ButtonHandle none() { return ButtonHandle(0); } INLINE operator bool () const; @@ -65,7 +65,6 @@ PUBLISHED: private: int _index; - static ButtonHandle _none; public: static TypeHandle get_class_type() { diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 8f49433ebd..fd3c44c917 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -2951,7 +2951,7 @@ setup_material(GLMaterial *gl_material, const Material *material) { _color_material_flags = CMF_ambient | CMF_diffuse; - if (material->has_ambient()) { + if (material->has_ambient() || material->has_base_color()) { const LColor &ambient = material->get_ambient(); gl_material->ambient.v[0] = ambient[0]; gl_material->ambient.v[1] = ambient[1]; @@ -2961,7 +2961,7 @@ setup_material(GLMaterial *gl_material, const Material *material) { _color_material_flags &= ~CMF_ambient; } - if (material->has_diffuse()) { + if (material->has_diffuse() || material->has_base_color()) { const LColor &diffuse = material->get_diffuse(); gl_material->diffuse.v[0] = diffuse[0]; gl_material->diffuse.v[1] = diffuse[1]; diff --git a/tests/pipeline/test_mutex.py b/tests/pipeline/test_mutex.py new file mode 100644 index 0000000000..2073c09efa --- /dev/null +++ b/tests/pipeline/test_mutex.py @@ -0,0 +1,60 @@ +from panda3d.core import Mutex, ReMutex + + +def test_mutex_acquire_release(): + m = Mutex() + m.acquire() + + # Assert that the lock is truly held now + assert m.debug_is_locked() + + # Release the lock + m.release() + + # Make sure the lock is properly released + assert m.try_acquire() + + # Clean up + m.release() + + +def test_mutex_try_acquire(): + m = Mutex() + + # Trying to acquire the lock should succeed + assert m.try_acquire() + + # Assert that the lock is truly held now + assert m.debug_is_locked() + + # Clean up + m.release() + + +def test_remutex_acquire_release(): + m = ReMutex() + m.acquire() + m.acquire() + m.release() + m.release() + + +def test_remutex_try_acquire(): + m = ReMutex() + + # Trying to acquire the lock should succeed + assert m.try_acquire() + + # Should report being locked + assert m.debug_is_locked() + + # Trying a second time should succeed + assert m.try_acquire() + + # Should still report being locked + assert m.debug_is_locked() + + # Clean up + m.release() + m.release() +