Quellcode durchsuchen

Merge branch 'master' into cmake

Sam Edwards vor 7 Jahren
Ursprung
Commit
e917c54433
50 geänderte Dateien mit 455 neuen und 155 gelöschten Zeilen
  1. 24 3
      direct/src/gui/DirectFrame.py
  2. 2 2
      direct/src/showbase/Loader.py
  3. 49 16
      dtool/src/dtoolbase/dtoolbase_cc.h
  4. 2 9
      dtool/src/dtoolbase/mutexSpinlockImpl.I
  5. 10 1
      dtool/src/dtoolbase/mutexSpinlockImpl.cxx
  6. 5 3
      dtool/src/dtoolbase/mutexSpinlockImpl.h
  7. 2 3
      dtool/src/interrogate/functionRemap.cxx
  8. 2 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  9. 1 1
      dtool/src/interrogate/interrogate.cxx
  10. 1 1
      dtool/src/interrogate/parameterRemapConcreteToPointer.cxx
  11. 1 1
      dtool/src/interrogate/parameterRemapReferenceToPointer.cxx
  12. 1 0
      makepanda/makepanda.py
  13. 1 1
      makepanda/makewheel.py
  14. 2 2
      panda/src/chan/partBundle.h
  15. 3 3
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  16. 1 1
      panda/src/egg/eggMesher.h
  17. 1 1
      panda/src/egg/eggMesherEdge.h
  18. 1 1
      panda/src/egg/eggMesherFanMaker.h
  19. 1 1
      panda/src/egg/eggMesherStrip.h
  20. 8 0
      panda/src/egg/eggVertexPool.cxx
  21. 1 1
      panda/src/egg2pg/eggBinner.h
  22. 1 1
      panda/src/egg2pg/eggLoader.h
  23. 1 1
      panda/src/egg2pg/eggRenderState.h
  24. 1 1
      panda/src/egg2pg/eggSaver.h
  25. 1 1
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  26. 1 1
      panda/src/gobj/material.cxx
  27. 1 1
      panda/src/movies/movieAudio.h
  28. 5 0
      panda/src/pgraphnodes/shaderGenerator.cxx
  29. 25 24
      panda/src/pgui/pgItem.cxx
  30. 35 29
      panda/src/pgui/pgScrollFrame.cxx
  31. 5 1
      panda/src/pgui/pgScrollFrame.h
  32. 1 1
      panda/src/pgui/pgVirtualFrame.h
  33. 29 2
      panda/src/pipeline/conditionVarSpinlockImpl.cxx
  34. 1 0
      panda/src/pipeline/conditionVarSpinlockImpl.h
  35. 0 15
      panda/src/pipeline/lightReMutexDirect.I
  36. 2 2
      panda/src/pipeline/lightReMutexDirect.h
  37. 7 0
      panda/src/pipeline/mutexTrueImpl.h
  38. 1 0
      panda/src/pipeline/p3pipeline_composite2.cxx
  39. 12 0
      panda/src/pipeline/reMutexDirect.I
  40. 2 2
      panda/src/pipeline/reMutexDirect.cxx
  41. 2 2
      panda/src/pipeline/reMutexDirect.h
  42. 23 0
      panda/src/pipeline/reMutexSpinlockImpl.I
  43. 57 0
      panda/src/pipeline/reMutexSpinlockImpl.cxx
  44. 54 0
      panda/src/pipeline/reMutexSpinlockImpl.h
  45. 6 2
      panda/src/pstatclient/pStatClient.cxx
  46. 0 9
      panda/src/putil/buttonHandle.I
  47. 0 3
      panda/src/putil/buttonHandle.cxx
  48. 1 2
      panda/src/putil/buttonHandle.h
  49. 2 2
      panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
  50. 60 0
      tests/pipeline/test_mutex.py

+ 24 - 3
direct/src/gui/DirectFrame.py

@@ -61,7 +61,14 @@ class DirectFrame(DirectGuiWidget):
     def destroy(self):
     def destroy(self):
         DirectGuiWidget.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
         # Determine if user passed in single string or a sequence
         if self['text'] == None:
         if self['text'] == None:
             textList = (None,) * self['numStates']
             textList = (None,) * self['numStates']
@@ -100,7 +107,14 @@ class DirectFrame(DirectGuiWidget):
                         sort = DGG.TEXT_SORT_INDEX,
                         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
         # Determine argument type
         geom = self['geom']
         geom = self['geom']
 
 
@@ -142,7 +156,14 @@ class DirectFrame(DirectGuiWidget):
                         geom = geom, scale = 1,
                         geom = geom, scale = 1,
                         sort = DGG.GEOM_SORT_INDEX)
                         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
         # Determine argument type
         arg = self['image']
         arg = self['image']
         if arg == None:
         if arg == None:

+ 2 - 2
direct/src/showbase/Loader.py

@@ -934,8 +934,8 @@ class Loader(DirectObject):
         just as in loadModel(); otherwise, the loading happens before
         just as in loadModel(); otherwise, the loading happens before
         loadSound() returns."""
         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]
             soundList = [soundPath]
             gotList = False
             gotList = False
         else:
         else:

+ 49 - 16
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -49,6 +49,8 @@
 // interrogate pass (CPPPARSER isn't defined), this maps to public.
 // interrogate pass (CPPPARSER isn't defined), this maps to public.
 #define PUBLISHED __published
 #define PUBLISHED __published
 
 
+#define PHAVE_ATOMIC 1
+
 typedef int ios_openmode;
 typedef int ios_openmode;
 typedef int ios_fmtflags;
 typedef int ios_fmtflags;
 typedef int ios_iostate;
 typedef int ios_iostate;
@@ -93,6 +95,23 @@ typedef std::ios::iostate ios_iostate;
 typedef std::ios::seekdir ios_seekdir;
 typedef std::ios::seekdir ios_seekdir;
 #endif
 #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
 // Apple has an outdated libstdc++.  Not all is lost, though, as we can fill
 // in some important missing functions.
 // in some important missing functions.
 #if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
 #if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
@@ -115,24 +134,38 @@ namespace std {
   }
   }
 
 
   template<class T> struct owner_less;
   template<class T> struct owner_less;
-};
-#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
+  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);
+    }
+  };
+};
 #else
 #else
-#define INLINE inline
+// Expect that we have access to the <atomic> header.
+#define PHAVE_ATOMIC 1
 #endif
 #endif
 
 
 // Determine the availability of C++11 features.
 // Determine the availability of C++11 features.

+ 2 - 9
dtool/src/dtoolbase/mutexSpinlockImpl.I

@@ -11,13 +11,6 @@
  * @date 2006-04-11
  * @date 2006-04-11
  */
  */
 
 
-/**
- *
- */
-constexpr MutexSpinlockImpl::
-MutexSpinlockImpl() : _lock(0) {
-}
-
 /**
 /**
  *
  *
  */
  */
@@ -33,7 +26,7 @@ lock() {
  */
  */
 INLINE bool MutexSpinlockImpl::
 INLINE bool MutexSpinlockImpl::
 try_lock() {
 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::
 INLINE void MutexSpinlockImpl::
 unlock() {
 unlock() {
-  AtomicAdjust::set(_lock, 0);
+  _flag.clear(std::memory_order_release);
 }
 }

+ 10 - 1
dtool/src/dtoolbase/mutexSpinlockImpl.cxx

@@ -17,12 +17,21 @@
 
 
 #include "mutexSpinlockImpl.h"
 #include "mutexSpinlockImpl.h"
 
 
+#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
+#include <emmintrin.h>
+#define PAUSE() _mm_pause()
+#else
+#define PAUSE()
+#endif
+
 /**
 /**
  *
  *
  */
  */
 void MutexSpinlockImpl::
 void MutexSpinlockImpl::
 do_lock() {
 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();
   }
   }
 }
 }
 
 

+ 5 - 3
dtool/src/dtoolbase/mutexSpinlockImpl.h

@@ -19,7 +19,9 @@
 
 
 #ifdef MUTEX_SPINLOCK
 #ifdef MUTEX_SPINLOCK
 
 
-#include "atomicAdjust.h"
+#ifdef PHAVE_ATOMIC
+#include <atomic>
+#endif
 
 
 /**
 /**
  * Uses a simple user-space spinlock to implement a mutex.  It is usually not
  * Uses a simple user-space spinlock to implement a mutex.  It is usually not
@@ -29,7 +31,7 @@
  */
  */
 class EXPCL_DTOOL_DTOOLBASE MutexSpinlockImpl {
 class EXPCL_DTOOL_DTOOLBASE MutexSpinlockImpl {
 public:
 public:
-  constexpr MutexSpinlockImpl();
+  constexpr MutexSpinlockImpl() noexcept = default;
   MutexSpinlockImpl(const MutexSpinlockImpl &copy) = delete;
   MutexSpinlockImpl(const MutexSpinlockImpl &copy) = delete;
 
 
   MutexSpinlockImpl &operator = (const MutexSpinlockImpl &copy) = delete;
   MutexSpinlockImpl &operator = (const MutexSpinlockImpl &copy) = delete;
@@ -42,7 +44,7 @@ public:
 private:
 private:
   void do_lock();
   void do_lock();
 
 
-  TVOLATILE AtomicAdjust::Integer _lock;
+  std::atomic_flag _flag = ATOMIC_FLAG_INIT;
 };
 };
 
 
 #include "mutexSpinlockImpl.I"
 #include "mutexSpinlockImpl.I"

+ 2 - 3
dtool/src/interrogate/functionRemap.cxx

@@ -254,14 +254,13 @@ call_function(ostream &out, int indent_level, bool convert_result,
                                                            &parser);
                                                            &parser);
         out << " = " << call << ";\n";
         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
         // an rvalue, allowing a move constructor to be called instead of a
         // copy constructor (since we won't be using the return value any
         // copy constructor (since we won't be using the return value any
         // more), which is usually more efficient if it exists.  If it
         // more), which is usually more efficient if it exists.  If it
         // doesn't, it shouldn't do any harm.
         // doesn't, it shouldn't do any harm.
         string new_str =
         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);
         return_expr = _return_type->get_return_expr(new_str);
 
 
       } else {
       } else {

+ 2 - 2
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
           // Use move constructor when available for functions that take an
           // actual PointerTo.  This eliminates an unref()ref() pair.
           // actual PointerTo.  This eliminates an unref()ref() pair.
-          pexpr_string = "MOVE(" + param_name + "_this)";
+          pexpr_string = "std::move(" + param_name + "_this)";
 
 
         } else {
         } else {
           // This is a move-assignable type, such as TypeHandle or LVecBase4.
           // 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";
       indent(out, indent_level) << "return true;\n";
 
 
     } else if (TypeManager::is_reference_count(remap->_cpptype)) {
     } 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";
       indent(out, indent_level) << "return true;\n";
 
 
     } else {
     } else {

+ 1 - 1
dtool/src/interrogate/interrogate.cxx

@@ -516,7 +516,7 @@ main(int argc, char **argv) {
       cerr << "Error parsing file: '" << argv[i] << "'\n";
       cerr << "Error parsing file: '" << argv[i] << "'\n";
       exit(1);
       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
   // Now that we've parsed all the source code, change the way things are

+ 1 - 1
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] == '&') {
   if (variable_name.size() > 1 && variable_name[0] == '&') {
     // Prevent generating something like *&param Also, if this is really some
     // Prevent generating something like *&param Also, if this is really some
     // local type, we can presumably just move it?
     // local type, we can presumably just move it?
-    out << "MOVE(" << variable_name.substr(1) << ")";
+    out << "std::move(" << variable_name.substr(1) << ")";
   } else {
   } else {
     out << "*" << variable_name;
     out << "*" << variable_name;
   }
   }

+ 1 - 1
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,
     // this parameter is an rvalue reference, but CPPParser can't know that,
     // and it might have an overload that takes an rvalue reference.  It
     // and it might have an overload that takes an rvalue reference.  It
     // shouldn't hurt either way.
     // shouldn't hurt either way.
-    out << "MOVE(" << variable_name.substr(1) << ")";
+    out << "std::move(" << variable_name.substr(1) << ")";
   } else {
   } else {
     out << "*" << variable_name;
     out << "*" << variable_name;
   }
   }

+ 1 - 0
makepanda/makepanda.py

@@ -2271,6 +2271,7 @@ DTOOL_CONFIG=[
     ("OS_SIMPLE_THREADS",              '1',                      '1'),
     ("OS_SIMPLE_THREADS",              '1',                      '1'),
     ("DEBUG_THREADS",                  'UNDEF',                  'UNDEF'),
     ("DEBUG_THREADS",                  'UNDEF',                  'UNDEF'),
     ("HAVE_POSIX_THREADS",             'UNDEF',                  '1'),
     ("HAVE_POSIX_THREADS",             'UNDEF',                  '1'),
+    ("MUTEX_SPINLOCK",                 'UNDEF',                  'UNDEF'),
     ("HAVE_AUDIO",                     '1',                      '1'),
     ("HAVE_AUDIO",                     '1',                      '1'),
     ("NOTIFY_DEBUG",                   'UNDEF',                  'UNDEF'),
     ("NOTIFY_DEBUG",                   'UNDEF',                  'UNDEF'),
     ("DO_PSTATS",                      'UNDEF',                  'UNDEF'),
     ("DO_PSTATS",                      'UNDEF',                  'UNDEF'),

+ 1 - 1
makepanda/makewheel.py

@@ -28,7 +28,7 @@ default_platform = get_platform()
 
 
 if default_platform.startswith("linux-"):
 if default_platform.startswith("linux-"):
     # Is this manylinux1?
     # 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")
         default_platform = default_platform.replace("linux", "manylinux1")
 
 
 
 

+ 2 - 2
panda/src/chan/partBundle.h

@@ -248,8 +248,8 @@ inline std::ostream &operator <<(std::ostream &out, const PartBundle &bundle) {
   return out;
   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"
 #include "partBundle.I"
 
 

+ 3 - 3
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -3463,7 +3463,7 @@ do_issue_material() {
   cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data());
   cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data());
   cur_material.Power = material->get_shininess();
   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.
     // If the material specifies an diffuse color, use it.
     set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
     set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
   } else {
   } else {
@@ -3476,7 +3476,7 @@ do_issue_material() {
       set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
       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.
     // If the material specifies an ambient color, use it.
     set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
     set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
   } else {
   } 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);
     set_render_state(D3DRS_SPECULARENABLE, TRUE);
   } else {
   } else {
     set_render_state(D3DRS_SPECULARENABLE, FALSE);
     set_render_state(D3DRS_SPECULARENABLE, FALSE);

+ 1 - 1
panda/src/egg/eggMesher.h

@@ -30,7 +30,7 @@
  * connectivity, and generates a set of EggTriangleStrips that represent the
  * connectivity, and generates a set of EggTriangleStrips that represent the
  * same geometry.
  * same geometry.
  */
  */
-class EggMesher {
+class EXPCL_PANDA_EGG EggMesher {
 public:
 public:
   EggMesher();
   EggMesher();
 
 

+ 1 - 1
panda/src/egg/eggMesherEdge.h

@@ -26,7 +26,7 @@ class EggMesherStrip;
  * connected triangles.  The edge is actually represented as a pair of vertex
  * connected triangles.  The edge is actually represented as a pair of vertex
  * indices into the same vertex pool.
  * indices into the same vertex pool.
  */
  */
-class EggMesherEdge {
+class EXPCL_PANDA_EGG EggMesherEdge {
 public:
 public:
   INLINE EggMesherEdge(int vi_a, int vi_b);
   INLINE EggMesherEdge(int vi_a, int vi_b);
   INLINE EggMesherEdge(const EggMesherEdge &copy);
   INLINE EggMesherEdge(const EggMesherEdge &copy);

+ 1 - 1
panda/src/egg/eggMesherFanMaker.h

@@ -31,7 +31,7 @@ class EggMesher;
  * This class is used by EggMesher::find_fans() to attempt to make an
  * This class is used by EggMesher::find_fans() to attempt to make an
  * EggTriangleFan out of the polygons connected to the indicated vertex.
  * EggTriangleFan out of the polygons connected to the indicated vertex.
  */
  */
-class EggMesherFanMaker {
+class EXPCL_PANDA_EGG EggMesherFanMaker {
 public:
 public:
   typedef plist<const EggMesherEdge *> Edges;
   typedef plist<const EggMesherEdge *> Edges;
   typedef plist<EggMesherStrip *> Strips;
   typedef plist<EggMesherStrip *> Strips;

+ 1 - 1
panda/src/egg/eggMesherStrip.h

@@ -27,7 +27,7 @@ class EggMesherEdge;
  * mesher.  It might also represent a single polygon such as a triangle or
  * mesher.  It might also represent a single polygon such as a triangle or
  * quad, since that's how strips generally start out.
  * quad, since that's how strips generally start out.
  */
  */
-class EggMesherStrip {
+class EXPCL_PANDA_EGG EggMesherStrip {
 public:
 public:
   enum PrimType {
   enum PrimType {
     PT_poly,
     PT_poly,

+ 8 - 0
panda/src/egg/eggVertexPool.cxx

@@ -677,7 +677,15 @@ transform(const LMatrix4d &mat) {
     typedef pvector<EggVertex *> Verts;
     typedef pvector<EggVertex *> Verts;
     Verts verts;
     Verts verts;
     verts.reserve(size());
     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));
     std::copy(begin(), end(), std::back_inserter(verts));
+#endif
 
 
     Verts::const_iterator vi;
     Verts::const_iterator vi;
     for (vi = verts.begin(); vi != verts.end(); ++vi) {
     for (vi = verts.begin(); vi != verts.end(); ++vi) {

+ 1 - 1
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
  * 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.
  * group related LOD children together under a single LOD node.
  */
  */
-class EggBinner : public EggBinMaker {
+class EXPCL_PANDA_EGG2PG EggBinner : public EggBinMaker {
 public:
 public:
   // The BinNumber serves to identify why a particular EggBin was created.
   // The BinNumber serves to identify why a particular EggBin was created.
   enum BinNumber {
   enum BinNumber {

+ 1 - 1
panda/src/egg2pg/eggLoader.h

@@ -64,7 +64,7 @@ class CharacterMaker;
  *
  *
  * This class isn't exported from this package.
  * This class isn't exported from this package.
  */
  */
-class EggLoader {
+class EXPCL_PANDA_EGG2PG EggLoader {
 public:
 public:
   EggLoader();
   EggLoader();
   EggLoader(const EggData *data);
   EggLoader(const EggData *data);

+ 1 - 1
panda/src/egg2pg/eggRenderState.h

@@ -36,7 +36,7 @@ class EggMaterial;
  * should be assigned to each primitive.  It is assigned to EggPrimitive
  * should be assigned to each primitive.  It is assigned to EggPrimitive
  * objects via the EggBinner.
  * objects via the EggBinner.
  */
  */
-class EggRenderState : public EggUserData {
+class EXPCL_PANDA_EGG2PG EggRenderState : public EggUserData {
 public:
 public:
   INLINE EggRenderState(EggLoader &loader);
   INLINE EggRenderState(EggLoader &loader);
   INLINE void add_attrib(const RenderAttrib *attrib);
   INLINE void add_attrib(const RenderAttrib *attrib);

+ 1 - 1
panda/src/egg2pg/eggSaver.h

@@ -50,7 +50,7 @@ class EggVertex;
  * complete (some Panda or egg constructs are not fully supported by this
  * complete (some Panda or egg constructs are not fully supported by this
  * class).
  * class).
  */
  */
-class EggSaver {
+class EXPCL_PANDA_EGG2PG EggSaver {
 PUBLISHED:
 PUBLISHED:
   EggSaver(EggData *data = nullptr);
   EggSaver(EggData *data = nullptr);
 
 

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -7627,7 +7627,7 @@ do_issue_material() {
   call_glMaterialfv(face, GL_EMISSION, material->get_emission());
   call_glMaterialfv(face, GL_EMISSION, material->get_emission());
   glMaterialf(face, GL_SHININESS, max(min(material->get_shininess(), (PN_stdfloat)128), (PN_stdfloat)0));
   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
     // The material has both an ambient and diffuse specified.  This means we
     // do not need glMaterialColor().
     // do not need glMaterialColor().
     glDisable(GL_COLOR_MATERIAL);
     glDisable(GL_COLOR_MATERIAL);

+ 1 - 1
panda/src/gobj/material.cxx

@@ -422,7 +422,7 @@ void Material::
 write(std::ostream &out, int indent_level) const {
 write(std::ostream &out, int indent_level) const {
   indent(out, indent_level) << "Material " << get_name() << "\n";
   indent(out, indent_level) << "Material " << get_name() << "\n";
   if (has_base_color()) {
   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()) {
   if (has_ambient()) {
     indent(out, indent_level + 2) << "ambient = " << get_ambient() << "\n";
     indent(out, indent_level + 2) << "ambient = " << get_ambient() << "\n";

+ 1 - 1
panda/src/movies/movieAudio.h

@@ -43,7 +43,7 @@ class MovieAudioCursor;
  */
  */
 class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
 class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
  PUBLISHED:
  PUBLISHED:
-  MovieAudio(const std::string &name = "Blank Audio");
+  explicit MovieAudio(const std::string &name = "Blank Audio");
   virtual ~MovieAudio();
   virtual ~MovieAudio();
   virtual PT(MovieAudioCursor) open();
   virtual PT(MovieAudioCursor) open();
   static PT(MovieAudio) get(const Filename &name);
   static PT(MovieAudio) get(const Filename &name);

+ 5 - 0
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -258,6 +258,11 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
     // states to be rehashed.
     // states to be rehashed.
     mat->mark_used_by_auto_shader();
     mat->mark_used_by_auto_shader();
     key._material_flags = mat->get_flags();
     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.
   // Break out the lights by type.

+ 25 - 24
panda/src/pgui/pgItem.cxx

@@ -147,8 +147,8 @@ void PGItem::
 transform_changed() {
 transform_changed() {
   LightReMutexHolder holder(_lock);
   LightReMutexHolder holder(_lock);
   PandaNode::transform_changed();
   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() {
 draw_mask_changed() {
   LightReMutexHolder holder(_lock);
   LightReMutexHolder holder(_lock);
   PandaNode::draw_mask_changed();
   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 &param) {
   play_sound(event);
   play_sound(event);
   throw_event(event, EventParameter(ep));
   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 &param) {
   play_sound(event);
   play_sound(event);
   throw_event(event, EventParameter(ep));
   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;
   // pgui_cat.info() << get_name() << "::exit()" << endl;
@@ -580,8 +580,8 @@ within_region(const MouseWatcherParameter &param) {
   play_sound(event);
   play_sound(event);
   throw_event(event, EventParameter(ep));
   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 &param) {
   play_sound(event);
   play_sound(event);
   throw_event(event, EventParameter(ep));
   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);
   play_sound(event);
   throw_event(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);
   play_sound(event);
   throw_event(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 &param, bool background) {
     throw_event(event, EventParameter(ep));
     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 &param, bool background) {
     throw_event(event, EventParameter(ep));
     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 &param) {
       << *this << "::move(" << param << ")\n";
       << *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::
 void PGItem::
 frame_changed() {
 frame_changed() {
+  LightReMutexHolder holder(_lock);
   mark_frames_stale();
   mark_frames_stale();
-  if (has_notify()) {
-    get_notify()->item_frame_changed(this);
+  if (_notify != nullptr) {
+    _notify->item_frame_changed(this);
   }
   }
 }
 }
 
 

+ 35 - 29
panda/src/pgui/pgScrollFrame.cxx

@@ -19,19 +19,18 @@ TypeHandle PGScrollFrame::_type_handle;
  *
  *
  */
  */
 PGScrollFrame::
 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 &copy) :
   _auto_hide(copy._auto_hide)
   _auto_hide(copy._auto_hide)
 {
 {
   _needs_remanage = false;
   _needs_remanage = false;
-  _needs_recompute_canvas = true;
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
+  _canvas_computed.clear();
 }
 }
 
 
 /**
 /**
@@ -97,7 +96,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   if (_needs_recompute_clip) {
   if (_needs_recompute_clip) {
     recompute_clip();
     recompute_clip();
   }
   }
-  if (_needs_recompute_canvas) {
+  if (!_canvas_computed.test_and_set()) {
     recompute_canvas();
     recompute_canvas();
   }
   }
   return PGVirtualFrame::cull_callback(trav, data);
   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
     // Showing or hiding one of the scroll bars might have set this flag again
     // indirectly; we clear it again to avoid a feedback loop.
     // indirectly; we clear it again to avoid a feedback loop.
     _needs_remanage = false;
     _needs_remanage = false;
-}
+  }
 
 
   // Are either or both of the scroll bars hidden?
   // Are either or both of the scroll bars hidden?
   if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
   if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
@@ -329,19 +328,20 @@ item_draw_mask_changed(PGItem *) {
  */
  */
 void PGScrollFrame::
 void PGScrollFrame::
 slider_bar_adjust(PGSliderBar *) {
 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
  * Recomputes the clipping window of the PGScrollFrame, based on the position
  * of the slider bars.
  * of the slider bars.
+ *
+ * Assumes the lock is held.
  */
  */
 void PGScrollFrame::
 void PGScrollFrame::
 recompute_clip() {
 recompute_clip() {
-  LightReMutexHolder holder(_lock);
   _needs_recompute_clip = false;
   _needs_recompute_clip = false;
-  _needs_recompute_canvas = true;
+  _canvas_computed.clear();
 
 
   // Figure out how to remove the scroll bars from the clip region.
   // Figure out how to remove the scroll bars from the clip region.
   LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame());
   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
  * Recomputes the portion of the virtual canvas that is visible within the
  * PGScrollFrame, based on the values of the slider bars.
  * PGScrollFrame, based on the values of the slider bars.
+ *
+ * Assumes the lock is held.
  */
  */
 void PGScrollFrame::
 void PGScrollFrame::
 recompute_canvas() {
 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
  * Computes the linear translation that should be applied to the virtual
  * canvas node, based on the corresponding slider bar's position.
  * canvas node, based on the corresponding slider bar's position.
+ *
+ * Assumes the lock is held.
  */
  */
 PN_stdfloat PGScrollFrame::
 PN_stdfloat PGScrollFrame::
 interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max,
 interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max,
                    PN_stdfloat canvas_min, PN_stdfloat canvas_max,
                    PN_stdfloat canvas_min, PN_stdfloat canvas_max,
                    PGSliderBar *slider_bar) {
                    PGSliderBar *slider_bar) {
-  LightReMutexHolder holder(_lock);
   PN_stdfloat t = 0.0f;
   PN_stdfloat t = 0.0f;
   if (slider_bar != nullptr) {
   if (slider_bar != nullptr) {
     t = slider_bar->get_ratio();
     t = slider_bar->get_ratio();

+ 5 - 1
panda/src/pgui/pgScrollFrame.h

@@ -20,6 +20,10 @@
 #include "pgSliderBarNotify.h"
 #include "pgSliderBarNotify.h"
 #include "pgSliderBar.h"
 #include "pgSliderBar.h"
 
 
+#ifdef PHAVE_ATOMIC
+#include <atomic>
+#endif
+
 /**
 /**
  * This is a special kind of frame that pretends to be much larger than it
  * 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
  * actually is.  You can scroll through the frame, as if you're looking
@@ -92,7 +96,7 @@ private:
 private:
 private:
   bool _needs_remanage;
   bool _needs_remanage;
   bool _needs_recompute_clip;
   bool _needs_recompute_clip;
-  bool _needs_recompute_canvas;
+  std::atomic_flag _canvas_computed;
 
 
   bool _has_virtual_frame;
   bool _has_virtual_frame;
   LVecBase4 _virtual_frame;
   LVecBase4 _virtual_frame;

+ 1 - 1
panda/src/pgui/pgVirtualFrame.h

@@ -72,7 +72,7 @@ protected:
 private:
 private:
   void setup_child_nodes();
   void setup_child_nodes();
 
 
-private:
+protected:
   bool _has_clip_frame;
   bool _has_clip_frame;
   LVecBase4 _clip_frame;
   LVecBase4 _clip_frame;
 
 

+ 29 - 2
panda/src/pipeline/conditionVarSpinlockImpl.cxx

@@ -16,6 +16,14 @@
 #ifdef MUTEX_SPINLOCK
 #ifdef MUTEX_SPINLOCK
 
 
 #include "conditionVarSpinlockImpl.h"
 #include "conditionVarSpinlockImpl.h"
+#include "trueClock.h"
+
+#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
+#include <emmintrin.h>
+#define PAUSE() _mm_pause()
+#else
+#define PAUSE()
+#endif
 
 
 /**
 /**
  *
  *
@@ -23,12 +31,31 @@
 void ConditionVarSpinlockImpl::
 void ConditionVarSpinlockImpl::
 wait() {
 wait() {
   AtomicAdjust::Integer current = _event;
   AtomicAdjust::Integer current = _event;
-  _mutex.release();
+  _mutex.unlock();
 
 
   while (AtomicAdjust::get(_event) == current) {
   while (AtomicAdjust::get(_event) == current) {
+    PAUSE();
+  }
+
+  _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.acquire();
+  _mutex.lock();
 }
 }
 
 
 #endif  // MUTEX_SPINLOCK
 #endif  // MUTEX_SPINLOCK

+ 1 - 0
panda/src/pipeline/conditionVarSpinlockImpl.h

@@ -37,6 +37,7 @@ public:
   INLINE ~ConditionVarSpinlockImpl();
   INLINE ~ConditionVarSpinlockImpl();
 
 
   void wait();
   void wait();
+  void wait(double timeout);
   INLINE void notify();
   INLINE void notify();
   INLINE void notify_all();
   INLINE void notify_all();
 
 

+ 0 - 15
panda/src/pipeline/lightReMutexDirect.I

@@ -11,21 +11,6 @@
  * @date 2008-10-08
  * @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.
  * Alias for acquire() to match C++11 semantics.
  * @see acquire()
  * @see acquire()

+ 2 - 2
panda/src/pipeline/lightReMutexDirect.h

@@ -29,7 +29,7 @@ class Thread;
  */
  */
 class EXPCL_PANDA_PIPELINE LightReMutexDirect {
 class EXPCL_PANDA_PIPELINE LightReMutexDirect {
 protected:
 protected:
-  INLINE LightReMutexDirect();
+  LightReMutexDirect() = default;
   LightReMutexDirect(const LightReMutexDirect &copy) = delete;
   LightReMutexDirect(const LightReMutexDirect &copy) = delete;
   ~LightReMutexDirect() = default;
   ~LightReMutexDirect() = default;
 
 
@@ -57,7 +57,7 @@ PUBLISHED:
 
 
 private:
 private:
 #ifdef HAVE_REMUTEXTRUEIMPL
 #ifdef HAVE_REMUTEXTRUEIMPL
-  mutable ReMutexImpl _impl;
+  mutable ReMutexTrueImpl _impl;
 
 
 #else
 #else
   // If we don't have a reentrant mutex, use the one we hand-rolled in
   // If we don't have a reentrant mutex, use the one we hand-rolled in

+ 7 - 0
panda/src/pipeline/mutexTrueImpl.h

@@ -43,6 +43,13 @@ typedef MutexImpl MutexTrueImpl;
 #if HAVE_REMUTEXIMPL
 #if HAVE_REMUTEXIMPL
 typedef ReMutexImpl ReMutexTrueImpl;
 typedef ReMutexImpl ReMutexTrueImpl;
 #define HAVE_REMUTEXTRUEIMPL 1
 #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
 #else
 #undef HAVE_REMUTEXTRUEIMPL
 #undef HAVE_REMUTEXTRUEIMPL
 #endif // HAVE_REMUTEXIMPL
 #endif // HAVE_REMUTEXIMPL

+ 1 - 0
panda/src/pipeline/p3pipeline_composite2.cxx

@@ -13,6 +13,7 @@
 #include "reMutex.cxx"
 #include "reMutex.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexHolder.cxx"
 #include "reMutexHolder.cxx"
+#include "reMutexSpinlockImpl.cxx"
 #include "thread.cxx"
 #include "thread.cxx"
 #include "threadDummyImpl.cxx"
 #include "threadDummyImpl.cxx"
 #include "threadPosixImpl.cxx"
 #include "threadPosixImpl.cxx"

+ 12 - 0
panda/src/pipeline/reMutexDirect.I

@@ -33,7 +33,11 @@ ReMutexDirect()
 INLINE void ReMutexDirect::
 INLINE void ReMutexDirect::
 lock() {
 lock() {
   TAU_PROFILE("void ReMutexDirect::acquire()", " ", TAU_USER);
   TAU_PROFILE("void ReMutexDirect::acquire()", " ", TAU_USER);
+#ifdef HAVE_REMUTEXTRUEIMPL
   _impl.lock();
   _impl.lock();
+#else
+  ((ReMutexDirect *)this)->do_lock();
+#endif  // HAVE_REMUTEXTRUEIMPL
 }
 }
 
 
 /**
 /**
@@ -43,7 +47,11 @@ lock() {
 INLINE bool ReMutexDirect::
 INLINE bool ReMutexDirect::
 try_lock() {
 try_lock() {
   TAU_PROFILE("void ReMutexDirect::try_acquire()", " ", TAU_USER);
   TAU_PROFILE("void ReMutexDirect::try_acquire()", " ", TAU_USER);
+#ifdef HAVE_REMUTEXTRUEIMPL
   return _impl.try_lock();
   return _impl.try_lock();
+#else
+  return ((ReMutexDirect *)this)->do_try_lock();
+#endif  // HAVE_REMUTEXTRUEIMPL
 }
 }
 
 
 /**
 /**
@@ -53,7 +61,11 @@ try_lock() {
 INLINE void ReMutexDirect::
 INLINE void ReMutexDirect::
 unlock() {
 unlock() {
   TAU_PROFILE("void ReMutexDirect::unlock()", " ", TAU_USER);
   TAU_PROFILE("void ReMutexDirect::unlock()", " ", TAU_USER);
+#ifdef HAVE_REMUTEXTRUEIMPL
   _impl.unlock();
   _impl.unlock();
+#else
+  ((ReMutexDirect *)this)->do_unlock();
+#endif  // HAVE_REMUTEXTRUEIMPL
 }
 }
 
 
 /**
 /**

+ 2 - 2
panda/src/pipeline/reMutexDirect.cxx

@@ -141,11 +141,11 @@ do_elevate_lock() {
  * mutex).
  * mutex).
  */
  */
 void ReMutexDirect::
 void ReMutexDirect::
-do_unlock() {
+do_unlock(Thread *current_thread) {
   _lock_impl.lock();
   _lock_impl.lock();
 
 
 #ifdef _DEBUG
 #ifdef _DEBUG
-  if (_locking_thread != Thread::get_current_thread()) {
+  if (_locking_thread != current_thread) {
     std::ostringstream ostr;
     std::ostringstream ostr;
     ostr << *_locking_thread << " attempted to release "
     ostr << *_locking_thread << " attempted to release "
          << *this << " which it does not own";
          << *this << " which it does not own";

+ 2 - 2
panda/src/pipeline/reMutexDirect.h

@@ -59,7 +59,7 @@ PUBLISHED:
 
 
 private:
 private:
 #ifdef HAVE_REMUTEXTRUEIMPL
 #ifdef HAVE_REMUTEXTRUEIMPL
-  mutable ReMutexImpl _impl;
+  mutable ReMutexTrueImpl _impl;
 
 
 #else
 #else
   // If we don't have a reentrant mutex, we have to hand-roll one.
   // If we don't have a reentrant mutex, we have to hand-roll one.
@@ -68,7 +68,7 @@ private:
   INLINE bool do_try_lock();
   INLINE bool do_try_lock();
   bool do_try_lock(Thread *current_thread);
   bool do_try_lock(Thread *current_thread);
   void do_elevate_lock();
   void do_elevate_lock();
-  void do_unlock();
+  void do_unlock(Thread *current_thread = Thread::get_current_thread());
 
 
   Thread *_locking_thread;
   Thread *_locking_thread;
   int _lock_count;
   int _lock_count;

+ 23 - 0
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);
+  }
+}

+ 57 - 0
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 <emmintrin.h>
+#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

+ 54 - 0
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 &copy) = delete;
+
+  ReMutexSpinlockImpl &operator = (const ReMutexSpinlockImpl &copy) = 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

+ 6 - 2
panda/src/pstatclient/pStatClient.cxx

@@ -1056,7 +1056,9 @@ add_collector(PStatClient::Collector *collector) {
     // the lock.
     // the lock.
     int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
     int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
     CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size];
     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_ptr(_collectors, new_collectors);
     AtomicAdjust::set(_collectors_size, new_collectors_size);
     AtomicAdjust::set(_collectors_size, new_collectors_size);
 
 
@@ -1091,7 +1093,9 @@ add_thread(PStatClient::InternalThread *thread) {
     // the lock.
     // the lock.
     int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
     int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
     ThreadPointer *new_threads = new ThreadPointer[new_threads_size];
     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.
     // We assume that assignment to a pointer and to an int are each atomic.
     AtomicAdjust::set_ptr(_threads, new_threads);
     AtomicAdjust::set_ptr(_threads, new_threads);
     AtomicAdjust::set(_threads_size, new_threads_size);
     AtomicAdjust::set(_threads_size, new_threads_size);

+ 0 - 9
panda/src/putil/buttonHandle.I

@@ -137,15 +137,6 @@ output(std::ostream &out) const {
   out << get_name();
   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.
  * ButtonHandle::none() evaluates to false, everything else evaluates to true.
  */
  */

+ 0 - 3
panda/src/putil/buttonHandle.cxx

@@ -14,9 +14,6 @@
 #include "buttonHandle.h"
 #include "buttonHandle.h"
 #include "buttonRegistry.h"
 #include "buttonRegistry.h"
 
 
-// This is initialized to zero by static initialization.
-ButtonHandle ButtonHandle::_none;
-
 TypeHandle ButtonHandle::_type_handle;
 TypeHandle ButtonHandle::_type_handle;
 
 
 /**
 /**

+ 1 - 2
panda/src/putil/buttonHandle.h

@@ -53,7 +53,7 @@ PUBLISHED:
 
 
   constexpr int get_index() const;
   constexpr int get_index() const;
   INLINE void output(std::ostream &out) const;
   INLINE void output(std::ostream &out) const;
-  INLINE static ButtonHandle none();
+  constexpr static ButtonHandle none() { return ButtonHandle(0); }
 
 
   INLINE operator bool () const;
   INLINE operator bool () const;
 
 
@@ -65,7 +65,6 @@ PUBLISHED:
 
 
 private:
 private:
   int _index;
   int _index;
-  static ButtonHandle _none;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 2 - 2
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -2951,7 +2951,7 @@ setup_material(GLMaterial *gl_material, const Material *material) {
 
 
   _color_material_flags = CMF_ambient | CMF_diffuse;
   _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();
     const LColor &ambient = material->get_ambient();
     gl_material->ambient.v[0] = ambient[0];
     gl_material->ambient.v[0] = ambient[0];
     gl_material->ambient.v[1] = ambient[1];
     gl_material->ambient.v[1] = ambient[1];
@@ -2961,7 +2961,7 @@ setup_material(GLMaterial *gl_material, const Material *material) {
     _color_material_flags &= ~CMF_ambient;
     _color_material_flags &= ~CMF_ambient;
   }
   }
 
 
-  if (material->has_diffuse()) {
+  if (material->has_diffuse() || material->has_base_color()) {
     const LColor &diffuse = material->get_diffuse();
     const LColor &diffuse = material->get_diffuse();
     gl_material->diffuse.v[0] = diffuse[0];
     gl_material->diffuse.v[0] = diffuse[0];
     gl_material->diffuse.v[1] = diffuse[1];
     gl_material->diffuse.v[1] = diffuse[1];

+ 60 - 0
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()
+