Jelajahi Sumber

Merge branch 'master' into webgl-port

rdb 3 tahun lalu
induk
melakukan
185e439ff6
100 mengubah file dengan 1475 tambahan dan 1153 penghapusan
  1. 4 4
      .github/workflows/ci.yml
  2. 5 29
      CMakeLists.txt
  3. 19 22
      README.md
  4. 12 77
      cmake/macros/BuildMetalib.cmake
  5. 2 15
      cmake/macros/PackageConfig.cmake
  6. 53 33
      cmake/modules/FindLibSquish.cmake
  7. 0 88
      cmake/modules/FindOpenEXR.cmake
  8. 5 5
      contrib/src/rplight/shadowSource.h
  9. 89 66
      direct/src/dist/FreezeTool.py
  10. 5 0
      direct/src/dist/_android.py
  11. 27 18
      direct/src/dist/commands.py
  12. 13 0
      direct/src/distributed/PyDatagramIterator.py
  13. 4 5
      direct/src/distributed/cConnectionRepository.cxx
  14. 15 5
      direct/src/filter/CommonFilters.py
  15. 2 1
      direct/src/filter/filterBloomI.py
  16. 3 2
      direct/src/filter/filterBloomX.py
  17. 2 1
      direct/src/filter/filterBloomY.py
  18. 4 4
      direct/src/filter/filterBlurX.py
  19. 4 4
      direct/src/filter/filterBlurY.py
  20. 3 2
      direct/src/filter/filterCopy.py
  21. 3 2
      direct/src/filter/filterDown4.py
  22. 0 6
      direct/src/gui/DirectGuiBase.py
  23. 2 1
      direct/src/motiontrail/cMotionTrail.h
  24. 4 4
      direct/src/showbase/BufferViewer.py
  25. 5 1
      direct/src/showbase/ShowBase.py
  26. 7 7
      direct/src/stdpy/threading.py
  27. 9 5
      direct/src/task/Task.py
  28. 12 32
      dtool/CompilerFlags.cmake
  29. 33 17
      dtool/Package.cmake
  30. 0 4
      dtool/PandaVersion.cmake
  31. 139 139
      dtool/metalibs/dtoolconfig/pydtool.cxx
  32. 2 2
      dtool/src/cppparser/cppManifest.cxx
  33. 1 1
      dtool/src/cppparser/cppManifest.h
  34. 2 2
      dtool/src/cppparser/cppNamespace.cxx
  35. 2 2
      dtool/src/cppparser/cppStructType.cxx
  36. 7 0
      dtool/src/dtoolbase/CMakeLists.txt
  37. 1 0
      dtool/src/dtoolbase/p3dtoolbase_composite2.cxx
  38. 211 6
      dtool/src/dtoolbase/patomic.I
  39. 168 0
      dtool/src/dtoolbase/patomic.cxx
  40. 85 18
      dtool/src/dtoolbase/patomic.h
  41. 0 1
      dtool/src/dtoolbase/typeHandle_ext.cxx
  42. 39 6
      dtool/src/dtoolutil/executionEnvironment.cxx
  43. 15 1
      dtool/src/dtoolutil/filename.cxx
  44. 2 0
      dtool/src/dtoolutil/pandaSystem.cxx
  45. 10 2
      dtool/src/dtoolutil/panda_getopt_impl.cxx
  46. 3 4
      dtool/src/dtoolutil/pfstreamBuf.cxx
  47. 4 4
      dtool/src/interrogate/interfaceMaker.cxx
  48. 2 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  49. 2 2
      dtool/src/interrogate/interfaceMakerPythonObj.cxx
  50. 2 2
      dtool/src/interrogate/interfaceMakerPythonSimple.cxx
  51. 7 0
      dtool/src/interrogate/interrogate.cxx
  52. 6 0
      dtool/src/prc/CMakeLists.txt
  53. 1 0
      dtool/src/prc/configFlags.h
  54. 2 1
      dtool/src/prc/configVariableBase.h
  55. 2 1
      dtool/src/prc/configVariableCore.h
  56. 47 10
      makepanda/makepackage.py
  57. 37 10
      makepanda/makepanda.py
  58. 33 5
      makepanda/makepandacore.py
  59. 8 2
      panda/src/android/PythonActivity.java
  60. 2 3
      panda/src/audio/audioLoadRequest.I
  61. 5 0
      panda/src/audiotraits/CMakeLists.txt
  62. 3 2
      panda/src/bullet/bulletAllHitsRayResult.h
  63. 2 1
      panda/src/bullet/bulletClosestHitRayResult.h
  64. 2 1
      panda/src/bullet/bulletClosestHitSweepResult.h
  65. 2 2
      panda/src/bullet/bulletConvexPointCloudShape.I
  66. 2 2
      panda/src/bullet/bulletTriangleMeshShape.cxx
  67. 2 2
      panda/src/chan/animControl.cxx
  68. 7 7
      panda/src/char/character.cxx
  69. 4 36
      panda/src/collide/collisionBox.I
  70. 54 81
      panda/src/collide/collisionBox.cxx
  71. 7 10
      panda/src/collide/collisionBox.h
  72. 0 9
      panda/src/collide/collisionHandlerEvent.I
  73. 0 1
      panda/src/collide/collisionHandlerEvent.h
  74. 0 1
      panda/src/collide/collisionHandlerPhysical_ext.cxx
  75. 7 7
      panda/src/collide/collisionLevelStateBase.I
  76. 3 3
      panda/src/collide/collisionTraverser.cxx
  77. 0 1
      panda/src/collide/collisionTraverser_ext.cxx
  78. 4 4
      panda/src/device/evdevInputDevice.cxx
  79. 1 1
      panda/src/device/inputDeviceManager.h
  80. 1 1
      panda/src/device/trackerData.h
  81. 1 1
      panda/src/device/xInputDevice.cxx
  82. 4 4
      panda/src/display/displayRegion.cxx
  83. 86 70
      panda/src/display/graphicsEngine.cxx
  84. 1 5
      panda/src/display/graphicsEngine.h
  85. 2 2
      panda/src/display/graphicsOutput.cxx
  86. 39 173
      panda/src/display/graphicsStateGuardian.cxx
  87. 6 15
      panda/src/display/graphicsStateGuardian.h
  88. 21 0
      panda/src/display/graphicsWindow.cxx
  89. 4 0
      panda/src/display/graphicsWindow.h
  90. 0 1
      panda/src/display/pStatGPUTimer.h
  91. 3 3
      panda/src/display/standardMunger.cxx
  92. 7 0
      panda/src/downloadertools/multify.cxx
  93. 1 1
      panda/src/dxgsg9/dxShaderContext9.cxx
  94. 2 2
      panda/src/dxgsg9/wdxGraphicsBuffer9.cxx
  95. 1 1
      panda/src/dxgsg9/wdxGraphicsWindow9.cxx
  96. 0 12
      panda/src/egg/eggMesherEdge.I
  97. 0 1
      panda/src/egg/eggMesherEdge.h
  98. 2 1
      panda/src/egg/eggTransform.h
  99. 1 1
      panda/src/egg2pg/eggSaver.cxx
  100. 0 2
      panda/src/event/CMakeLists.txt

+ 4 - 4
.github/workflows/ci.yml

@@ -333,7 +333,7 @@ jobs:
     if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
     strategy:
       matrix:
-        os: [ubuntu-18.04, windows-2016, macOS-11]
+        os: [ubuntu-18.04, windows-2019, macOS-11]
     runs-on: ${{ matrix.os }}
     steps:
     - uses: actions/checkout@v1
@@ -365,7 +365,7 @@ jobs:
     - name: Build Python 3.9
       shell: bash
       run: |
-        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
     - name: Test Python 3.9
       shell: bash
       run: |
@@ -378,7 +378,7 @@ jobs:
     - name: Build Python 3.8
       shell: bash
       run: |
-        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
     - name: Test Python 3.8
       shell: bash
       run: |
@@ -391,7 +391,7 @@ jobs:
     - name: Build Python 3.7
       shell: bash
       run: |
-        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10
     - name: Test Python 3.7
       shell: bash
       run: |

+ 5 - 29
CMakeLists.txt

@@ -1,36 +1,14 @@
-cmake_minimum_required(VERSION 3.0.2)
+cmake_minimum_required(VERSION 3.13)
 set(CMAKE_DISABLE_SOURCE_CHANGES ON) # Must go before project() below
 set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Must go before project() below
 
-if(CMAKE_VERSION VERSION_GREATER "3.11" OR POLICY CMP0072)
-  # Prefer GLVND over libGL when available; this will be enabled by default
-  # once the minimum CMake version is at least 3.11.
-  cmake_policy(SET CMP0072 NEW)
-endif()
-
-if(CMAKE_VERSION VERSION_GREATER "3.12" OR POLICY CMP0074)
-  # Needed for THIRDPARTY_DIRECTORY support; this will be enabled by default
-  # once the minimum CMake version is at least 3.12.
-  cmake_policy(SET CMP0074 NEW)
-endif()
-
 if(POLICY CMP0091)
   # Needed for CMake to pass /MD flag properly with non-VC generators.
   cmake_policy(SET CMP0091 NEW)
 endif()
 
 # Determine whether we are using a multi-config generator.
-if(CMAKE_VERSION VERSION_GREATER "3.8")
-  get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-else()
-  message(WARNING "Multi-configuration builds may not work properly when using
-a CMake < 3.9. Making a guess if this is a multi-config generator.")
-  if(DEFINED CMAKE_CONFIGURATION_TYPES)
-    set(IS_MULTICONFIG ON)
-  else()
-    set(IS_MULTICONFIG OFF)
-  endif()
-endif()
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 # Set the default CMAKE_BUILD_TYPE before calling project().
 if(IS_MULTICONFIG)
@@ -89,11 +67,9 @@ string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INT
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros/")
 
-if(CMAKE_VERSION VERSION_GREATER "3.8")
-  # When using the Xcode generator, don't append the platform name to the
-  # intermediate configuration directory.
-  set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF)
-endif()
+# When using the Xcode generator, don't append the platform name to the
+# intermediate configuration directory.
+set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF)
 
 # Include modules builtin to CMake
 include(GNUInstallDirs)     # Defines CMAKE_INSTALL_<dir> variables

+ 19 - 22
README.md

@@ -177,35 +177,32 @@ directory which you can install using `pkg install`.
 Android
 -------
 
-Note: building on Android is very experimental and not guaranteed to work.
+Although it's possible to build Panda3D on an Android device itself using the
+[termux](https://termux.com/) shell, the recommended route is to cross-compile
+.whl files using the SDK and NDK, which can then be used by the `build_apps`
+command to build a Python application into an .apk or .aab bundle.  You will
+need to get the latest thirdparty packages, which can be obtained from the
+artifacts page of the last successful run here:
 
-You can experimentally build the Android Python runner via the [termux](https://termux.com/)
-shell.  You will need to install [Termux](https://play.google.com/store/apps/details?id=com.termux)
-and [Termux API](https://play.google.com/store/apps/details?id=com.termux.api)
-from the Play Store.  Many of the dependencies can be installed by running the
-following command in the Termux shell:
+https://github.com/rdb/panda3d-thirdparty/actions?query=branch%3Amain+is%3Asuccess+event%3Apush
 
-```bash
-pkg install python ndk-sysroot clang bison freetype harfbuzz libpng eigen openal-soft opusfile libvorbis assimp libopus ecj dx patchelf aapt apksigner libcrypt openssl pkg-config
-```
+This does not include Python at the moment, which can be extracted from
+[this archive](https://rdb.name/thirdparty-android.tar.gz) instead.
 
-Then, you can build the .apk using this command:
+These commands show how to compile wheels for the supported Android ABIs:
 
 ```bash
-python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer
+export ANDROID_SDK_ROOT=/home/rdb/local/android
+python3.8 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel
+python3.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel
+python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel
+python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-19 --threads 6 --wheel
 ```
 
-You can install the generated panda3d.apk by browsing to the panda3d folder
-using a file manager.  You may need to copy it to `/sdcard` to be able to
-access it from other apps.
-
-To launch a Python program from Termux, you can use the `run_python.sh` script
-inside the `panda/src/android` directory.  It will launch Python in a separate
-activity, load it with the Python script you passed as argument, and use a
-socket for returning the command-line output to the Termux shell.  Do note
-that this requires the Python application to reside on the SD card and that
-Termux needs to be set up with access to the SD card (using the
-`termux-setup-storage` command).
+It is now possible to use the generated wheels with `build_apps`, as explained
+on this page:
+
+https://discourse.panda3d.org/t/deployment-for-android/28226
 
 Running Tests
 =============

+ 12 - 77
cmake/macros/BuildMetalib.cmake

@@ -5,83 +5,6 @@
 #   instead just an agglomeration of the various component libraries that get
 #   linked into them. A library of libraries - a "metalibrary."
 
-#
-# Function: target_link_libraries(...)
-#
-# Overrides CMake's target_link_libraries() to support "linking" object
-# libraries. This is a partial reimplementation of CMake commit dc38970f83,
-# which is only available in CMake 3.12+
-#
-if(CMAKE_VERSION VERSION_LESS "3.12")
-  function(target_link_libraries target)
-    get_target_property(target_type "${target}" TYPE)
-    if(NOT target_type STREQUAL "OBJECT_LIBRARY")
-      _target_link_libraries("${target}" ${ARGN})
-      return()
-    endif()
-
-    foreach(library ${ARGN})
-      # This is a quick and dirty regex to tell targets apart from other stuff.
-      # It just checks if it's alphanumeric and starts with p3/panda.
-      if(library MATCHES "^(PKG::|p3|panda)[A-Za-z0-9]*$")
-        # We need to add "library"'s include directories to "target"
-        # (and transitively to INTERFACE_INCLUDE_DIRECTORIES so further
-        # dependencies will work)
-        set(include_directories "$<TARGET_PROPERTY:${library},INTERFACE_INCLUDE_DIRECTORIES>")
-        set_property(TARGET "${target}" APPEND PROPERTY INCLUDE_DIRECTORIES "${include_directories}")
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${include_directories}")
-
-        # SYSTEM include directories should still be reported as SYSTEM, so
-        # that warnings from those includes are suppressed
-        set(sys_include_directories
-          "$<TARGET_PROPERTY:${library},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>")
-        target_include_directories("${target}" SYSTEM PUBLIC "${sys_include_directories}")
-
-        # And for INTERFACE_COMPILE_DEFINITIONS as well
-        set(compile_definitions "$<TARGET_PROPERTY:${library},INTERFACE_COMPILE_DEFINITIONS>")
-        set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}")
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS "${compile_definitions}")
-
-        # Build up some generator expressions for determining whether `library`
-        # is a component library or not.
-        if(library MATCHES ".*::.*")
-          # "::" messes up CMake's genex parser; fortunately, a library whose
-          # name contains that is either an interface library or alias, and
-          # definitely not a component
-          set(is_component 0)
-          set(name_of_component "")
-          set(name_of_non_component "${library}")
-
-        else()
-          set(is_component "$<TARGET_PROPERTY:${library},IS_COMPONENT>")
-
-          # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :(
-          set(is_object "$<STREQUAL:$<TARGET_PROPERTY:${library},TYPE>,OBJECT_LIBRARY>")
-          set(is_component "$<BOOL:$<${is_object}:${is_component}>>")
-
-          set(name_of_component "$<${is_component}:$<TARGET_NAME:${library}>>")
-          set(name_of_non_component "$<$<NOT:${is_component}>:$<TARGET_NAME:${library}>>")
-
-        endif()
-
-        # Libraries are only linked transitively if they aren't components.
-        set_property(TARGET "${target}" APPEND PROPERTY
-          INTERFACE_LINK_LIBRARIES "${name_of_non_component}")
-
-      else()
-        # This is a file path to an out-of-tree library - this needs to be
-        # recorded so that the metalib can link them. (They aren't needed at
-        # all for the object libraries themselves, so they don't have to work
-        # transitively.)
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
-
-      endif()
-
-    endforeach(library)
-
-  endfunction(target_link_libraries)
-endif()
-
 #
 # Function: add_component_library(target [SYMBOL building_symbol]
 #                                 [SOURCES] [[NOINIT]/[INIT func [header]]])
@@ -305,6 +228,7 @@ function(add_metalib target_name)
   set(interface_defines)
   set(includes)
   set(libs)
+  set(link_options)
   set(component_init_funcs "")
   foreach(component ${components})
     if(NOT TARGET "${component}")
@@ -392,6 +316,16 @@ function(add_metalib target_name)
         endif()
       endforeach(component_library)
 
+      # All the linker options applied to an individual component get applied
+      # when building the metalib (for things like --exclude-libs).
+      get_target_property(component_link_options "${component}" LINK_OPTIONS)
+      foreach(component_link_option ${component_link_options})
+        if(component_link_option)
+          list(APPEND link_options "${component_link_option}")
+
+        endif()
+      endforeach(component_link_option)
+
       # Consume this component's objects
       list(APPEND sources "$<TARGET_OBJECTS:${component}>")
 
@@ -419,6 +353,7 @@ function(add_metalib target_name)
     PRIVATE ${private_defines}
     INTERFACE ${interface_defines})
   target_link_libraries("${target_name}" ${libs})
+  target_link_options("${target_name}" PRIVATE ${link_options})
   target_include_directories("${target_name}"
     PUBLIC ${includes}
     INTERFACE "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/panda3d>")

+ 2 - 15
cmake/macros/PackageConfig.cmake

@@ -176,18 +176,6 @@ function(package_option name)
   # Create the INTERFACE library used to depend on this package.
   add_library(PKG::${name} INTERFACE IMPORTED GLOBAL)
 
-  # Explicitly record the package's include directories as system include
-  # directories.  CMake does do this automatically for INTERFACE libraries, but
-  # it does it by discovering all transitive links first, then reading
-  # INTERFACE_INCLUDE_DIRECTORIES for those which are INTERFACE libraries.  So,
-  # this would be broken for the metalib system (pre CMake 3.12) which doesn't
-  # "link" the object libraries.
-  if(CMAKE_VERSION VERSION_LESS "3.12")
-    set_target_properties(PKG::${name} PROPERTIES
-      INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
-      "$<TARGET_PROPERTY:PKG::${name},INTERFACE_INCLUDE_DIRECTORIES>")
-  endif()
-
   # If the option actually is enabled, populate the INTERFACE library created above
   if(HAVE_${name})
     set(use_variables ON)
@@ -412,9 +400,8 @@ function(export_packages filename)
             endforeach(config)
           endif()
 
-        elseif(CMAKE_VERSION VERSION_GREATER "3.8")
-          # This is an INTERFACE_LIBRARY, and CMake is new enough to support
-          # IMPORTED_IMPLIB
+        else()
+          # This is an INTERFACE_LIBRARY.
           get_target_property(imported_libname "${head}" IMPORTED_LIBNAME)
           if(imported_libname)
             list(APPEND libraries ${imported_libname})

+ 53 - 33
cmake/modules/FindLibSquish.cmake

@@ -14,42 +14,62 @@
 #   LIBSQUISH_DEBUG_LIBRARY   - the filepath of the libsquish debug library
 #
 
-# Find the libsquish include files
-find_path(LIBSQUISH_INCLUDE_DIR
-  NAMES "squish.h"
-  PATHS "/usr/include"
-        "/usr/local/include"
-        "/sw/include"
-        "/opt/include"
-        "/opt/local/include"
-        "/opt/csw/include"
-  PATH_SUFFIXES "" "cppunit"
-)
+if(LibSquish_ROOT)
+  # Search exclusively under the root
+  find_path(LIBSQUISH_INCLUDE_DIR
+    NAMES "squish.h"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "include"
+  )
 
-# Find the libsquish library built for release
-find_library(LIBSQUISH_RELEASE_LIBRARY
-  NAMES "squish" "libsquish"
-  PATHS "/usr"
-        "/usr/local"
-        "/usr/freeware"
-        "/sw"
-        "/opt"
-        "/opt/csw"
-  PATH_SUFFIXES "lib" "lib32" "lib64"
-)
+  find_library(LIBSQUISH_RELEASE_LIBRARY
+    NAMES "squish" "libsquish"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "lib"
+  )
 
-# Find the libsquish library built for debug
-find_library(LIBSQUISH_DEBUG_LIBRARY
-  NAMES "squishd" "libsquishd"
-  PATHS "/usr"
-        "/usr/local"
-        "/usr/freeware"
-        "/sw"
-        "/opt"
-        "/opt/csw"
-  PATH_SUFFIXES "lib" "lib32" "lib64"
-)
+  find_library(LIBSQUISH_DEBUG_LIBRARY
+    NAMES "squishd" "libsquishd"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "lib"
+  )
+else()
+  # Find the libsquish include files
+  find_path(LIBSQUISH_INCLUDE_DIR
+    NAMES "squish.h"
+    PATHS "/usr/include"
+          "/usr/local/include"
+          "/sw/include"
+          "/opt/include"
+          "/opt/local/include"
+          "/opt/csw/include"
+    PATH_SUFFIXES "" "cppunit"
+  )
 
+  # Find the libsquish library built for release
+  find_library(LIBSQUISH_RELEASE_LIBRARY
+    NAMES "squish" "libsquish"
+    PATHS "/usr"
+          "/usr/local"
+          "/usr/freeware"
+          "/sw"
+          "/opt"
+          "/opt/csw"
+    PATH_SUFFIXES "lib" "lib32" "lib64"
+  )
+
+  # Find the libsquish library built for debug
+  find_library(LIBSQUISH_DEBUG_LIBRARY
+    NAMES "squishd" "libsquishd"
+    PATHS "/usr"
+          "/usr/local"
+          "/usr/freeware"
+          "/sw"
+          "/opt"
+          "/opt/csw"
+    PATH_SUFFIXES "lib" "lib32" "lib64"
+  )
+endif()
 
 mark_as_advanced(LIBSQUISH_INCLUDE_DIR)
 mark_as_advanced(LIBSQUISH_RELEASE_LIBRARY)

+ 0 - 88
cmake/modules/FindOpenEXR.cmake

@@ -1,88 +0,0 @@
-# Filename: FindOpenEXR.cmake
-# Authors: CFSworks (5 Nov, 2018)
-#
-# Usage:
-#   find_package(OpenEXR [REQUIRED] [QUIET])
-#
-# Once done this will define:
-#   OPENEXR_FOUND       - system has OpenEXR
-#   OPENEXR_INCLUDE_DIR - the include directory containing OpenEXR header files
-#   OPENEXR_LIBRARIES   - the path to the OpenEXR libraries
-#
-
-find_path(OPENEXR_INCLUDE_DIR
-  "ImfVersion.h"
-  PATH_SUFFIXES "OpenEXR")
-
-mark_as_advanced(OPENEXR_INCLUDE_DIR)
-
-find_library(OPENEXR_imf_LIBRARY
-  NAMES "IlmImf")
-
-if(OPENEXR_imf_LIBRARY)
-  get_filename_component(_imf_dir "${OPENEXR_imf_LIBRARY}" DIRECTORY)
-  find_library(OPENEXR_imfutil_LIBRARY
-    NAMES "IlmImfUtil"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_ilmthread_LIBRARY
-    NAMES "IlmThread"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_iex_LIBRARY
-    NAMES "Iex"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_iexmath_LIBRARY
-    NAMES "IexMath"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_imath_LIBRARY
-    NAMES "Imath"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_half_LIBRARY
-    NAMES "Half"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  unset(_imf_dir)
-endif()
-
-mark_as_advanced(
-  OPENEXR_imf_LIBRARY
-  OPENEXR_imfutil_LIBRARY
-  OPENEXR_ilmthread_LIBRARY
-  OPENEXR_iex_LIBRARY
-  OPENEXR_iexmath_LIBRARY
-  OPENEXR_imath_LIBRARY
-  OPENEXR_half_LIBRARY
-)
-
-set(OPENEXR_LIBRARIES
-  ${OPENEXR_imf_LIBRARY}
-  ${OPENEXR_imfutil_LIBRARY}
-  ${OPENEXR_ilmthread_LIBRARY}
-  ${OPENEXR_iex_LIBRARY}
-  ${OPENEXR_iexmath_LIBRARY}
-  ${OPENEXR_imath_LIBRARY}
-  ${OPENEXR_half_LIBRARY}
-)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(OpenEXR DEFAULT_MSG
-  OPENEXR_INCLUDE_DIR OPENEXR_LIBRARIES
-
-  OPENEXR_imf_LIBRARY
-  OPENEXR_imfutil_LIBRARY
-  OPENEXR_ilmthread_LIBRARY
-  OPENEXR_iex_LIBRARY
-  OPENEXR_iexmath_LIBRARY
-  OPENEXR_imath_LIBRARY
-  OPENEXR_half_LIBRARY
-)

+ 5 - 5
contrib/src/rplight/shadowSource.h

@@ -30,6 +30,7 @@
 
 #include "pandabase.h"
 #include "luse.h"
+#include "memoryBase.h"
 #include "transformState.h"
 #include "look_at.h"
 #include "compose_matrix.h"
@@ -48,7 +49,7 @@
  *   and a view-projection matrix. The shadow manager regenerates the shadow maps
  *   using the data from the shadow sources.
  */
-class ShadowSource {
+class ShadowSource : public MemoryBase {
 public:
   ShadowSource();
 
@@ -78,14 +79,13 @@ public:
   inline const BoundingSphere& get_bounds() const;
 
 private:
-  int _slot;
-  bool _needs_update;
-  size_t _resolution;
   LMatrix4 _mvp;
   LVecBase4i _region;
   LVecBase4 _region_uv;
-
   BoundingSphere _bounds;
+  int _slot;
+  bool _needs_update;
+  size_t _resolution;
 };
 
 #include "shadowSource.I"

+ 89 - 66
direct/src/dist/FreezeTool.py

@@ -80,6 +80,7 @@ defaultHiddenImports = {
     ],
     'pandas.compat': ['lzma', 'cmath'],
     'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
+    'plyer': ['plyer.platforms'],
 }
 
 
@@ -791,10 +792,6 @@ class Freezer:
         # default object will be created when it is needed.
         self.cenv = None
 
-        # This is the search path to use for Python modules.  Leave it
-        # to the default value of None to use sys.path.
-        self.path = path
-
         # The filename extension to append to the source file before
         # compiling.
         self.sourceExtension = '.c'
@@ -843,32 +840,44 @@ class Freezer:
         # builds.  It can be explicitly included if desired.
         self.modules['doctest'] = self.ModuleDef('doctest', exclude = True)
 
-        self.mf = None
-
         # Actually, make sure we know how to find all of the
         # already-imported modules.  (Some of them might do their own
         # special path mangling.)
         for moduleName, module in list(sys.modules.items()):
             if module and getattr(module, '__path__', None) is not None:
-                path = list(getattr(module, '__path__'))
-                if path:
-                    modulefinder.AddPackagePath(moduleName, path[0])
+                modPath = list(getattr(module, '__path__'))
+                if modPath:
+                    modulefinder.AddPackagePath(moduleName, modPath[0])
 
         # Module with non-obvious dependencies
         self.hiddenImports = defaultHiddenImports.copy()
         if hiddenImports is not None:
             self.hiddenImports.update(hiddenImports)
 
+        # Special hack for plyer, which has platform-specific hidden imports
+        plyer_platform = None
+        if self.platform.startswith('android'):
+            plyer_platform = 'android'
+        elif self.platform.startswith('linux'):
+            plyer_platform = 'linux'
+        elif self.platform.startswith('mac'):
+            plyer_platform = 'macosx'
+        elif self.platform.startswith('win'):
+            plyer_platform = 'win'
+
+        if plyer_platform:
+            self.hiddenImports['plyer'].append(f'plyer.platforms.{plyer_platform}.*')
+
         # Suffix/extension for Python C extension modules
         if self.platform == PandaSystem.getPlatform():
-            self.moduleSuffixes = imp.get_suffixes()
+            suffixes = imp.get_suffixes()
 
             # Set extension for Python files to binary mode
-            for i, suffix in enumerate(self.moduleSuffixes):
+            for i, suffix in enumerate(suffixes):
                 if suffix[2] == imp.PY_SOURCE:
-                    self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
+                    suffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
         else:
-            self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
+            suffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
 
             abi_version = '{0}{1}'.format(*sys.version_info)
             abi_flags = ''
@@ -876,7 +885,7 @@ class Freezer:
                 abi_flags += 'm'
 
             if 'linux' in self.platform:
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
@@ -884,24 +893,26 @@ class Freezer:
                 ]
             elif 'win' in self.platform:
                 # ABI flags are not appended on Windows.
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3),
                     ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3),
                     ('.pyd', 'rb', 3),
                 ]
             elif 'mac' in self.platform:
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
             else: # FreeBSD et al.
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
 
+        self.mf = PandaModuleFinder(excludes=['doctest'], suffixes=suffixes, path=path)
+
     def excludeFrom(self, freezer):
         """ Excludes all modules that have already been processed by
         the indicated FreezeTool.  This is equivalent to passing the
@@ -921,8 +932,6 @@ class Freezer:
         allowChildren is true, the children of the indicated module
         may still be included."""
 
-        assert self.mf is None
-
         self.modules[moduleName] = self.ModuleDef(
             moduleName, exclude = True,
             forbid = forbid, allowChildren = allowChildren,
@@ -947,24 +956,6 @@ class Freezer:
         files can be found.  If the module is a .py file and not a
         directory, returns None. """
 
-        # First, try to import the module directly.  That's the most
-        # reliable answer, if it works.
-        try:
-            module = __import__(moduleName)
-        except:
-            print("couldn't import %s" % (moduleName))
-            module = None
-
-        if module is not None:
-            for symbol in moduleName.split('.')[1:]:
-                module = getattr(module, symbol)
-            if hasattr(module, '__path__'):
-                return module.__path__
-
-        # If it didn't work--maybe the module is unimportable because
-        # it makes certain assumptions about the builtins, or
-        # whatever--then just look for file on disk.  That's usually
-        # good enough.
         path = None
         baseName = moduleName
         if '.' in baseName:
@@ -974,34 +965,20 @@ class Freezer:
                 return None
 
         try:
-            file, pathname, description = imp.find_module(baseName, path)
+            file, pathname, description = self.mf.find_module(baseName, path)
         except ImportError:
             return None
 
-        if not os.path.isdir(pathname):
+        if not self.mf._dir_exists(pathname):
             return None
+
         return [pathname]
 
     def getModuleStar(self, moduleName):
         """ Looks for the indicated directory module and returns the
         __all__ member: the list of symbols within the module. """
 
-        # First, try to import the module directly.  That's the most
-        # reliable answer, if it works.
-        try:
-            module = __import__(moduleName)
-        except:
-            print("couldn't import %s" % (moduleName))
-            module = None
-
-        if module is not None:
-            for symbol in moduleName.split('.')[1:]:
-                module = getattr(module, symbol)
-            if hasattr(module, '__all__'):
-                return module.__all__
-
-        # If it didn't work, just open the directory and scan for *.py
-        # files.
+        # Open the directory and scan for *.py files.
         path = None
         baseName = moduleName
         if '.' in baseName:
@@ -1011,16 +988,16 @@ class Freezer:
                 return None
 
         try:
-            file, pathname, description = imp.find_module(baseName, path)
+            file, pathname, description = self.mf.find_module(baseName, path)
         except ImportError:
             return None
 
-        if not os.path.isdir(pathname):
+        if not self.mf._dir_exists(pathname):
             return None
 
         # Scan the directory, looking for .py files.
         modules = []
-        for basename in sorted(os.listdir(pathname)):
+        for basename in sorted(self.mf._listdir(pathname)):
             if basename.endswith('.py') and basename != '__init__.py':
                 modules.append(basename[:-3])
 
@@ -1054,8 +1031,8 @@ class Freezer:
             modulePath = self.getModulePath(topName)
             if modulePath:
                 for dirname in modulePath:
-                    for basename in sorted(os.listdir(dirname)):
-                        if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
+                    for basename in sorted(self.mf._listdir(dirname)):
+                        if self.mf._file_exists(os.path.join(dirname, basename, '__init__.py')):
                             parentName = '%s.%s' % (topName, basename)
                             newParentName = '%s.%s' % (newTopName, basename)
                             if self.getModulePath(parentName):
@@ -1100,8 +1077,6 @@ class Freezer:
         directories within a particular directory.
         """
 
-        assert self.mf is None
-
         if not newName:
             newName = moduleName
 
@@ -1122,8 +1097,6 @@ class Freezer:
         to done(), you may not add any more modules until you call
         reset(). """
 
-        assert self.mf is None
-
         # If we are building an exe, we also need to implicitly
         # bring in Python's startup modules.
         if addStartupModules:
@@ -1165,7 +1138,9 @@ class Freezer:
             else:
                 includes.append(mdef)
 
-        self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path)
+        # Add the excludes to the ModuleFinder.
+        for exclude in excludeDict:
+            self.mf.excludes.append(exclude)
 
         # Attempt to import the explicit modules into the modulefinder.
 
@@ -2428,6 +2403,17 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
 
         return None
 
+    def _file_exists(self, path):
+        if os.path.exists(path):
+            return os.path.isfile(path)
+
+        fh = self._open_file(path, 'rb')
+        if fh:
+            fh.close()
+            return True
+
+        return False
+
     def _dir_exists(self, path):
         """Returns True if the given directory exists, either on disk or inside
         a wheel."""
@@ -2466,6 +2452,43 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
 
         return False
 
+    def _listdir(self, path):
+        """Lists files in the given directory if it exists."""
+
+        if os.path.isdir(path):
+            return os.listdir(path)
+
+        # Is there a zip file along the path?
+        dir, dirname = os.path.split(path.rstrip(os.path.sep + '/'))
+        fn = dirname
+        while dirname:
+            if os.path.isfile(dir):
+                # Okay, this is actually a file.  Is it a zip file?
+                if dir in self._zip_files:
+                    # Yes, and we've previously opened this.
+                    zip = self._zip_files[dir]
+                elif zipfile.is_zipfile(dir):
+                    zip = zipfile.ZipFile(dir)
+                    self._zip_files[dir] = zip
+                else:
+                    # It's not a directory or zip file.
+                    return []
+
+                # List files whose path start with our directory name.
+                prefix = fn.replace(os.path.sep, '/') + '/'
+                result = []
+                for name in zip.namelist():
+                    if name.startswith(prefix) and '/' not in name[len(prefix):]:
+                        result.append(name[len(prefix):])
+
+                return result
+
+            # Look at the parent directory.
+            dir, dirname = os.path.split(dir)
+            fn = os.path.join(dirname, fn)
+
+        return []
+
     def load_module(self, fqname, fp, pathname, file_info):
         """Copied from ModuleFinder.load_module with fixes to handle sending bytes
         to compile() for PY_SOURCE types. Sending bytes to compile allows it to
@@ -2712,7 +2735,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
         modules = {}
         for dir in m.__path__:
             try:
-                names = os.listdir(dir)
+                names = self._listdir(dir)
             except OSError:
                 self.msg(2, "can't list directory", dir)
                 continue

+ 5 - 0
direct/src/dist/_android.py

@@ -169,9 +169,11 @@ ANDROID_ATTRIBUTES = {
     'alwaysRetainTaskState': bool_resource(0x1010203),
     'clearTaskOnLaunch': bool_resource(0x1010015),
     'debuggable': bool_resource(0x0101000f),
+    'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"),
     'configChanges': flag_resource(0x0101001f, mcc=0x0001, mnc=0x0002, locale=0x0004, touchscreen=0x0008, keyboard=0x0010, keyboardHidden=0x0020, navigation=0x0040, orientation=0x0080, screenLayout=0x0100, uiMode=0x0200, screenSize=0x0400, smallestScreenSize=0x0800, layoutDirection=0x2000, fontScale=0x40000000),
     'enabled': bool_resource(0x101000e),
     'excludeFromRecents': bool_resource(0x1010017),
+    'exported': bool_resource(0x1010010),
     'extractNativeLibs': bool_resource(0x10104ea),
     'finishOnTaskLaunch': bool_resource(0x1010014),
     'fullBackupContent': bool_resource(0x10104eb),
@@ -189,9 +191,12 @@ ANDROID_ATTRIBUTES = {
     'minSdkVersion': int_resource(0x101020c),
     'multiprocess': bool_resource(0x1010013),
     'name': str_resource(0x1010003),
+    'noHistory': bool_resource(0x101022d),
     'pathPattern': str_resource(0x101002c),
+    'resizeableActivity': bool_resource(0x10104f6),
     'required': bool_resource(0x101028e),
     'scheme': str_resource(0x1010027),
+    'screenOrientation': enum_resource(0x101001e, 'landscape', 'portrait', 'user', 'behind', 'sensor', 'nosensor', 'sensorLandscape', 'sensorPortrait', 'reverseLandscape', 'reversePortrait', 'fullSensor', 'userLandscape', 'userPortrait', 'fullUser', 'locked'),
     'stateNotNeeded': bool_resource(0x1010016),
     'supportsRtl': bool_resource(0x010103af),
     'supportsUploading': bool_resource(0x101029b),

+ 27 - 18
direct/src/dist/commands.py

@@ -116,7 +116,7 @@ from _frozen_importlib import _imp, FrozenImporter
 
 sys.frozen = True
 
-if sys.platform == 'win32':
+if sys.platform == 'win32' and sys.version_info < (3, 10):
     # Make sure the preferred encoding is something we actually support.
     import _bootlocale
     enc = _bootlocale.getpreferredencoding().lower()
@@ -155,7 +155,9 @@ from android_log import write as android_log_write
 
 
 sys.frozen = True
-sys.platform = "android"
+
+# Temporary hack for plyer to detect Android, see kivy/plyer#670
+os.environ['ANDROID_ARGUMENT'] = ''
 
 
 # Replace stdout/stderr with something that writes to the Android log.
@@ -465,6 +467,12 @@ class build_apps(setuptools.Command):
         elif not self.android_abis:
             self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86']
 
+        supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
+        unsupported_abis = set(self.android_abis) - set(supported_abis)
+        if unsupported_abis:
+            raise ValueError(f'Unrecognized value(s) for android_abis: {", ".join(unsupported_abis)}\n'
+                             f'Valid ABIs are: {", ".join(supported_abis)}')
+
         self.icon_objects = {}
         for app, iconpaths in self.icons.items():
             if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
@@ -513,7 +521,9 @@ class build_apps(setuptools.Command):
                     else: # e.g. x86, x86_64, mips, mips64
                         suffix = '_' + abi.replace('-', '_')
 
-                    self.build_binaries(lib_dir, platform + suffix)
+                    # We end up copying the data multiple times to the same
+                    # directory, but that's probably fine for now.
+                    self.build_binaries(platform + suffix, lib_dir, data_dir)
 
                 # Write out the icons to the res directory.
                 for appname, icon in self.icon_objects.items():
@@ -532,13 +542,13 @@ class build_apps(setuptools.Command):
                     if icon.getLargestSize() >= 192:
                         icon.writeSize(192, os.path.join(res_dir, 'mipmap-xxxhdpi-v4', basename))
 
-                self.build_data(data_dir, platform)
+                self.build_assets(platform, data_dir)
 
                 # Generate an AndroidManifest.xml
                 self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml'))
             else:
-                self.build_binaries(build_dir, platform)
-                self.build_data(build_dir, platform)
+                self.build_binaries(platform, build_dir, build_dir)
+                self.build_assets(platform, build_dir)
 
             # Bundle into an .app on macOS
             if self.macos_main_app and 'macosx' in platform:
@@ -727,7 +737,7 @@ class build_apps(setuptools.Command):
 
         for appname in self.gui_apps:
             activity = ET.SubElement(application, 'activity')
-            activity.set('android:name', 'org.panda3d.android.PandaActivity')
+            activity.set('android:name', 'org.panda3d.android.PythonActivity')
             activity.set('android:label', appname)
             activity.set('android:theme', '@android:style/Theme.NoTitleBar')
             activity.set('android:configChanges', 'orientation|keyboardHidden')
@@ -750,7 +760,7 @@ class build_apps(setuptools.Command):
         with open(path, 'wb') as fh:
             tree.write(fh, encoding='utf-8', xml_declaration=True)
 
-    def build_binaries(self, binary_dir, platform):
+    def build_binaries(self, platform, binary_dir, data_dir=None):
         """ Builds the binary data for the given platform. """
 
         use_wheels = True
@@ -1015,7 +1025,7 @@ class build_apps(setuptools.Command):
 
             freezer_extras.update(freezer.extras)
             freezer_modules.update(freezer.getAllModuleNames())
-            for suffix in freezer.moduleSuffixes:
+            for suffix in freezer.mf.suffixes:
                 if suffix[2] == imp.C_EXTENSION:
                     ext_suffixes.add(suffix[0])
 
@@ -1030,12 +1040,9 @@ class build_apps(setuptools.Command):
             self.warn("Detected use of tkinter, but tkinter is not specified in requirements.txt!")
 
         # Copy extension modules
-        whl_modules = []
-        whl_modules_ext = ''
+        whl_modules = {}
         if use_wheels:
             # Get the module libs
-            whl_modules = []
-
             for i in p3dwhl.namelist():
                 if not i.startswith('deploy_libs/'):
                     continue
@@ -1050,8 +1057,7 @@ class build_apps(setuptools.Command):
 
                 base = os.path.basename(i)
                 module, _, ext = base.partition('.')
-                whl_modules.append(module)
-                whl_modules_ext = ext
+                whl_modules[module] = i
 
         # Make sure to copy any builtins that have shared objects in the
         # deploy libs, assuming they are not already in freezer_extras.
@@ -1103,7 +1109,7 @@ class build_apps(setuptools.Command):
             else:
                 # Builtin module, but might not be builtin in wheel libs, so double check
                 if module in whl_modules:
-                    source_path = os.path.join(p3dwhlfn, 'deploy_libs/{}.{}'.format(module, whl_modules_ext))#'{0}/deploy_libs/{1}.{2}'.format(p3dwhlfn, module, whl_modules_ext)
+                    source_path = os.path.join(p3dwhlfn, whl_modules[module])
                     basename = os.path.basename(source_path)
                     #XXX should we remove python version string here too?
                 else:
@@ -1125,6 +1131,9 @@ class build_apps(setuptools.Command):
                       os.path.join(binary_dir, '..', '..', 'classes.dex'))
 
         # Extract any other data files from dependency packages.
+        if data_dir is None:
+            return
+
         for module, datadesc in self.package_data_dirs.items():
             if module not in freezer_modules:
                 continue
@@ -1165,11 +1174,11 @@ class build_apps(setuptools.Command):
                             else:
                                 self.copy(source_path, target_path)
 
-    def build_data(self, data_dir, platform):
+    def build_assets(self, platform, data_dir):
         """ Builds the data files for the given platform. """
 
         # Copy Game Files
-        self.announce('Copying game files for platform: {}'.format(platform), distutils.log.INFO)
+        self.announce('Copying assets for platform: {}'.format(platform), distutils.log.INFO)
         ignore_copy_list = [
             '**/__pycache__/**',
             '**/*.pyc',

+ 13 - 0
direct/src/distributed/PyDatagramIterator.py

@@ -30,6 +30,19 @@ class PyDatagramIterator(DatagramIterator):
 
     getChannel = DatagramIterator.getUint64
 
+    def __init__(self, datagram=None, offset=0):
+        if datagram is not None:
+            super().__init__(datagram, offset)
+
+            # Retain a reference to it so that it doesn't get deleted.
+            self.__initialDatagram = datagram
+        else:
+            super().__init__()
+
+    def assign(self, datagram, offset = 0):
+        super().assign(datagram, offset)
+        self.__initialDatagram = datagram
+
     def getArg(self, subatomicType, divisor=1):
         # Import the type numbers
         if divisor == 1:

+ 4 - 5
direct/src/distributed/cConnectionRepository.cxx

@@ -58,19 +58,18 @@ CConnectionRepository(bool has_owner_view, bool threaded_net) :
   _bdc(4096000,4096000,1400),
   _native(false),
 #endif
+  _has_owner_view(has_owner_view),
+  _handle_c_updates(true),
   _client_datagram(true),
   _handle_datagrams_internally(handle_datagrams_internally),
   _simulated_disconnect(false),
   _verbose(distributed_cat.is_spam()),
+  _in_quiet_zone(0),
   _time_warning(0.0),
-// _msg_channels(),
   _msg_sender(0),
   _msg_type(0),
-  _has_owner_view(has_owner_view),
-  _handle_c_updates(true),
   _want_message_bundling(true),
-  _bundling_msgs(0),
-  _in_quiet_zone(0)
+  _bundling_msgs(0)
 {
 #if defined(HAVE_NET) && defined(SIMULATE_NETWORK_DELAY)
   if (min_lag != 0.0 || max_lag != 0.0) {

+ 15 - 5
direct/src/filter/CommonFilters.py

@@ -20,6 +20,7 @@ from panda3d.core import LVecBase4, LPoint2
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import Texture, Shader, ATSNone
 from panda3d.core import FrameBufferProperties
+from panda3d.core import getDefaultCoordinateSystem, CS_zup_right, CS_zup_left
 
 from direct.task.TaskManagerGlobal import taskMgr
 
@@ -52,6 +53,7 @@ o_color = lerp(o_color, k_cartooncolor, cartoon_thresh);
 SSAO_BODY = """//Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoord : TEXCOORD0,
              out float2 l_texcoordD : TEXCOORD1,
@@ -61,9 +63,9 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position = mul(mat_modelproj, vtx_position);
-  l_texcoord = vtx_position.xz;
-  l_texcoordD = (vtx_position.xz * texpad_depth.xy) + texpad_depth.xy;
-  l_texcoordN = (vtx_position.xz * texpad_normal.xy) + texpad_normal.xy;
+  l_texcoord = vtx_texcoord;
+  l_texcoordD = vtx_texcoord * texpad_depth.xy * 2;
+  l_texcoordN = vtx_texcoord * texpad_normal.xy * 2;
 }
 
 float3 sphere[16] = float3[](float3(0.53812504, 0.18565957, -0.43192),float3(0.13790712, 0.24864247, 0.44301823),float3(0.33715037, 0.56794053, -0.005789503),float3(-0.6999805, -0.04511441, -0.0019965635),float3(0.06896307, -0.15983082, -0.85477847),float3(0.056099437, 0.006954967, -0.1843352),float3(-0.014653638, 0.14027752, 0.0762037),float3(0.010019933, -0.1924225, -0.034443386),float3(-0.35775623, -0.5301969, -0.43581226),float3(-0.3169221, 0.106360726, 0.015860917),float3(0.010350345, -0.58698344, 0.0046293875),float3(-0.08972908, -0.49408212, 0.3287904),float3(0.7119986, -0.0154690035, -0.09183723),float3(-0.053382345, 0.059675813, -0.5411899),float3(0.035267662, -0.063188605, 0.54602677),float3(-0.47761092, 0.2847911, -0.0271716));
@@ -292,11 +294,19 @@ class CommonFilters:
             text += "{\n"
             text += "  l_position = mul(mat_modelproj, vtx_position);\n"
 
+            # The card is oriented differently depending on our chosen
+            # coordinate system.  We could just use vtx_texcoord, but this
+            # saves on an additional variable.
+            if getDefaultCoordinateSystem() in (CS_zup_right, CS_zup_left):
+                pos = "vtx_position.xz"
+            else:
+                pos = "vtx_position.xy"
+
             for texcoord, padTex in texcoordPadding.items():
                 if padTex is None:
-                    text += "  %s = vtx_position.xz * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord)
+                    text += "  %s = %s * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord, pos)
                 else:
-                    text += "  %s = (vtx_position.xz * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, padTex, padTex)
+                    text += "  %s = (%s * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, pos, padTex, padTex)
 
                     if "HalfPixelShift" in configuration:
                         text += "  %s += texpix_tx%s.xy * 0.5;\n" % (texcoord, padTex)

+ 2 - 1
direct/src/filter/filterBloomI.py

@@ -32,6 +32,7 @@ BLOOM_I = """
 
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoordNW : TEXCOORD0,
              out float2 l_texcoordNE : TEXCOORD1,
@@ -42,7 +43,7 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float4 offs = texpix_src * 0.5;
   l_texcoordNW = c + float2( offs.x, -offs.y);
   l_texcoordNE = c + float2( offs.x,  offs.y);

+ 3 - 2
direct/src/filter/filterBloomX.py

@@ -2,6 +2,7 @@ BLOOM_X = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_texcoord0 : TEXCOORD0,
              out float4 l_texcoord1 : TEXCOORD1,
@@ -10,8 +11,8 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4 texpix_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float offset = texpix_src.x;
   float pad = texpad_src.x * 2;
   l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y);

+ 2 - 1
direct/src/filter/filterBloomY.py

@@ -2,6 +2,7 @@ BLOOM_Y = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_texcoord0 : TEXCOORD0,
              out float4 l_texcoord1 : TEXCOORD1,
@@ -11,7 +12,7 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float offset = texpix_src.y;
   float pad = texpad_src.y * 2;
   l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x);

+ 4 - 4
direct/src/filter/filterBlurX.py

@@ -4,14 +4,14 @@ BLUR_X = """
 //Cg profile arbvp1 arbfp1
 
 void vshader(float4 vtx_position : POSITION,
-             float2 vtx_texcoord0 : TEXCOORD0,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
-      	     out float2 l_texcoord0 : TEXCOORD0,
+             out float2 l_texcoord0 : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord0 = vtx_texcoord * texpad_src.xy * 2;
 }
 
 

+ 4 - 4
direct/src/filter/filterBlurY.py

@@ -4,14 +4,14 @@ BLUR_Y = """
 //Cg profile arbvp1 arbfp1
 
 void vshader(float4 vtx_position : POSITION,
-             float2 vtx_texcoord0 : TEXCOORD0,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
-      	     out float2 l_texcoord0 : TEXCOORD0,
+             out float2 l_texcoord0 : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord0 = vtx_texcoord * texpad_src.xy * 2;
 }
 
 

+ 3 - 2
direct/src/filter/filterCopy.py

@@ -3,13 +3,14 @@ COPY = """
 
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoord : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord = vtx_texcoord * texpad_src.xy * 2;
 }
 
 void fshader(float2 l_texcoord : TEXCOORD0,

+ 3 - 2
direct/src/filter/filterDown4.py

@@ -2,6 +2,7 @@ DOWN_4 = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoordNW : TEXCOORD0,
              out float2 l_texcoordNE : TEXCOORD1,
@@ -11,8 +12,8 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4 texpix_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   l_texcoordNW = c + float2( texpix_src.x, -texpix_src.y);
   l_texcoordNE = c + float2( texpix_src.x,  texpix_src.y);
   l_texcoordSW = c + float2(-texpix_src.x, -texpix_src.y);

+ 0 - 6
direct/src/gui/DirectGuiBase.py

@@ -100,8 +100,6 @@ from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 
-guiObjectCollector = PStatCollector("Client::GuiObjects")
-
 _track_gui_items = ConfigVariableBool('track-gui-items', False)
 
 
@@ -732,8 +730,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         self.guiId = self.guiItem.getId()
 
         if ShowBaseGlobal.__dev__:
-            guiObjectCollector.addLevel(1)
-            guiObjectCollector.flushLevel()
             # track gui items by guiId for tracking down leaks
             if _track_gui_items:
                 if not hasattr(ShowBase, 'guiItems'):
@@ -1033,8 +1029,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     def destroy(self):
         if hasattr(self, "frameStyle"):
             if ShowBaseGlobal.__dev__:
-                guiObjectCollector.subLevel(1)
-                guiObjectCollector.flushLevel()
                 if hasattr(ShowBase, 'guiItems'):
                     ShowBase.guiItems.pop(self.guiId, None)
 

+ 2 - 1
direct/src/motiontrail/cMotionTrail.h

@@ -21,11 +21,12 @@
 #include "geomVertexWriter.h"
 #include "geomTriangles.h"
 #include "luse.h"
+#include "memoryBase.h"
 #include "nurbsCurveEvaluator.h"
 #include "plist.h"
 #include "epvector.h"
 
-class CMotionTrailVertex {
+class CMotionTrailVertex : public MemoryBase {
 public:
   LPoint4 _vertex;
   LVecBase4 _start_color;

+ 4 - 4
direct/src/showbase/BufferViewer.py

@@ -240,10 +240,10 @@ class BufferViewer(DirectObject):
             offsetx = (ringoffset[ring]*2.0) / float(sizex)
             offsety = (ringoffset[ring]*2.0) / float(sizey)
             bright = ringbright[ring]
-            vwriter.addData3f(-1-offsetx, 0, -1-offsety)
-            vwriter.addData3f(1+offsetx, 0, -1-offsety)
-            vwriter.addData3f(1+offsetx, 0,  1+offsety)
-            vwriter.addData3f(-1-offsetx, 0,  1+offsety)
+            vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, -1 - offsety))
+            vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, -1 - offsety))
+            vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0,  1 + offsety))
+            vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0,  1 + offsety))
             cwriter.addData3f(bright, bright, bright)
             cwriter.addData3f(bright, bright, bright)
             cwriter.addData3f(bright, bright, bright)

+ 5 - 1
direct/src/showbase/ShowBase.py

@@ -1930,7 +1930,11 @@ class ShowBase(DirectObject.DirectObject):
         if port is None:
             port = -1
         PStatClient.connect(hostname, port)
-        return PStatClient.isConnected()
+        if PStatClient.isConnected():
+            PStatClient.mainTick()
+            return True
+        else:
+            return False
 
     def addSfxManager(self, extraSfxManager):
         """

+ 7 - 7
direct/src/stdpy/threading.py

@@ -216,8 +216,8 @@ class RLock(core.ReMutex):
         core.ReMutex.__init__(self, name)
 
 
-class Condition(core.ConditionVarFull):
-    """ This class provides a wrapper around Panda's ConditionVarFull
+class Condition(core.ConditionVar):
+    """ This class provides a wrapper around Panda's ConditionVar
     object.  The wrapper is designed to emulate Python's own
     threading.Condition object. """
 
@@ -230,7 +230,7 @@ class Condition(core.ConditionVarFull):
         assert isinstance(lock, Lock)
 
         self.__lock = lock
-        core.ConditionVarFull.__init__(self, self.__lock)
+        core.ConditionVar.__init__(self, self.__lock)
 
     def acquire(self, *args, **kw):
         return self.__lock.acquire(*args, **kw)
@@ -240,12 +240,12 @@ class Condition(core.ConditionVarFull):
 
     def wait(self, timeout = None):
         if timeout is None:
-            core.ConditionVarFull.wait(self)
+            core.ConditionVar.wait(self)
         else:
-            core.ConditionVarFull.wait(self, timeout)
+            core.ConditionVar.wait(self, timeout)
 
     def notifyAll(self):
-        core.ConditionVarFull.notifyAll(self)
+        core.ConditionVar.notifyAll(self)
 
     notify_all = notifyAll
 
@@ -295,7 +295,7 @@ class Event:
 
     def __init__(self):
         self.__lock = core.Mutex("Python Event")
-        self.__cvar = core.ConditionVarFull(self.__lock)
+        self.__cvar = core.ConditionVar(self.__lock)
         self.__flag = False
 
     def is_set(self):

+ 9 - 5
direct/src/task/Task.py

@@ -16,11 +16,17 @@ from direct.showbase.MessengerGlobal import messenger
 import types
 import random
 import importlib
+import sys
 
-try:
-    import _signal as signal
-except ImportError:
+# On Android, there's no use handling SIGINT, and in fact we can't, since we
+# run the application in a separate thread from the main thread.
+if hasattr(sys, 'getandroidapilevel'):
     signal = None
+else:
+    try:
+        import _signal as signal
+    except ImportError:
+        signal = None
 
 from panda3d.core import *
 from direct.extensions_native import HTTPChannel_extensions
@@ -31,7 +37,6 @@ def print_exc_plus():
     Print the usual traceback information, followed by a listing of all the
     local variables in each frame.
     """
-    import sys
     import traceback
 
     tb = sys.exc_info()[2]
@@ -1271,7 +1276,6 @@ class TaskManager:
 
 if __debug__:
     def checkLeak():
-        import sys
         import gc
         gc.enable()
         from direct.showbase.DirectObject import DirectObject

+ 12 - 32
dtool/CompilerFlags.cmake

@@ -48,21 +48,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
 
 endif()
 
-# Panda3D is now a C++11 project. Newer versions of CMake support this out of
-# the box; for older versions we take a shot in the dark:
-if(CMAKE_VERSION VERSION_LESS "3.1")
-  check_cxx_compiler_flag("-std=gnu++11" COMPILER_SUPPORTS_CXX11)
-  if(COMPILER_SUPPORTS_CXX11)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
-  else()
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
-  endif()
-
-else()
-  set(CMAKE_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-endif()
+# Panda3D is now a C++11 project.
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 # Set certain CMake flags we expect
 set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON)
@@ -110,21 +98,6 @@ if(APPLE)
   set(CMAKE_SHARED_MODULE_SUFFIX ".dylib")
 endif()
 
-# We want the output structured like build/CONFIG/bin, not build/bin/CONFIG per
-# the default for multi-configuration generators. In CMake 3.4+, it switches
-# automatically if the *_OUTPUT_DIRECTORY property contains a generator
-# expresssion, but as of this writing we support as early as CMake 3.0.2.
-#
-# So, let's just do this:
-if(CMAKE_VERSION VERSION_LESS "3.4")
-  foreach(_type RUNTIME ARCHIVE LIBRARY)
-    foreach(_config ${CMAKE_CONFIGURATION_TYPES})
-      string(TOUPPER "${_config}" _config)
-      set(CMAKE_${_type}_OUTPUT_DIRECTORY_${_config} "${CMAKE_${_type}_OUTPUT_DIRECTORY}")
-    endforeach(_config)
-  endforeach(_type)
-endif()
-
 # Set warning levels
 if(MSVC)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
@@ -138,7 +111,7 @@ endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
   set(global_flags
-    "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing")
+    "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing -Werror=return-type")
   set(release_flags "-Wno-unused-variable")
 
   if(NOT MSVC)
@@ -154,7 +127,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
   endif()
 
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${global_flags}")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags} -Wno-reorder")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags}")
   set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${release_flags}")
   set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${release_flags}")
   set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${release_flags}")
@@ -245,3 +218,10 @@ if(NOT MSVC)
     add_compile_options("-fvisibility=hidden")
   endif()
 endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  check_cxx_compiler_flag("-fno-semantic-interposition" COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION)
+  if(COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION)
+    add_compile_options("-fno-semantic-interposition")
+  endif()
+endif()

+ 33 - 17
dtool/Package.cmake

@@ -1,5 +1,5 @@
 set(_thirdparty_dir_default "${PROJECT_SOURCE_DIR}/thirdparty")
-if(NOT (APPLE OR WIN32) OR NOT IS_DIRECTORY "${_thirdparty_dir_default}")
+if(NOT IS_DIRECTORY "${_thirdparty_dir_default}")
   set(_thirdparty_dir_default "")
 endif()
 
@@ -47,6 +47,27 @@ if(THIRDPARTY_DIRECTORY)
     set(BISON_ROOT "${THIRDPARTY_DIRECTORY}/win-util")
     set(FLEX_ROOT "${THIRDPARTY_DIRECTORY}/win-util")
 
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-arm64)
+    elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-x64)
+    else()
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-a)
+    endif()
+
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-arm64)
+    elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-x64)
+    else()
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-a)
+    endif()
+
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
+    set(_package_dir ${THIRDPARTY_DIRECTORY}/android-libs-${CMAKE_ANDROID_ARCH})
+
   else()
     message(FATAL_ERROR
       "You can't use THIRDPARTY_DIRECTORY on this platform. Unset it to continue.")
@@ -190,19 +211,6 @@ if(Python_FOUND)
   set(PYTHON_INCLUDE_DIRS ${Python_INCLUDE_DIRS})
   set(PYTHON_LIBRARY_DIRS ${Python_LIBRARY_DIRS})
   set(PYTHON_VERSION_STRING ${Python_VERSION})
-
-elseif(CMAKE_VERSION VERSION_LESS "3.12")
-  find_package(PythonInterp ${WANT_PYTHON_VERSION} QUIET)
-  find_package(PythonLibs ${PYTHON_VERSION_STRING} QUIET)
-
-  if(PYTHONLIBS_FOUND)
-    set(PYTHON_FOUND ON)
-
-    if(NOT PYTHON_VERSION_STRING)
-      set(PYTHON_VERSION_STRING ${PYTHONLIBS_VERSION_STRING})
-    endif()
-  endif()
-
 endif()
 
 if(CMAKE_VERSION VERSION_LESS "3.15")
@@ -369,14 +377,22 @@ package_option(TIFF "Enable support for loading .tif images.")
 package_status(TIFF "libtiff")
 
 # OpenEXR
-find_package(OpenEXR QUIET MODULE)
+find_package(OpenEXR QUIET)
 
-package_option(OpenEXR "Enable support for loading .exr images.")
+if (TARGET OpenEXR::IlmImf AND NOT TARGET OpenEXR::OpenEXR)
+  package_option(OpenEXR
+    "Enable support for loading .exr images."
+    IMPORTED_AS OpenEXR::IlmImf)
+else()
+  package_option(OpenEXR
+    "Enable support for loading .exr images."
+    IMPORTED_AS OpenEXR::OpenEXR)
+endif()
 
 package_status(OpenEXR "OpenEXR")
 
 # libsquish
-find_package(LibSquish QUIET)
+find_package(LibSquish QUIET MODULE)
 
 package_option(SQUISH
   "Enables support for automatic compression of DXT textures."

+ 0 - 4
dtool/PandaVersion.cmake

@@ -46,10 +46,6 @@ math(EXPR PANDA_NUMERIC_VERSION "${PROJECT_VERSION_MAJOR}*1000000 + ${PROJECT_VE
 
 # If SOURCE_DATE_EPOCH is set, it affects PandaSystem::get_build_date()
 if(DEFINED ENV{SOURCE_DATE_EPOCH})
-  if(CMAKE_VERSION VERSION_LESS "3.8")
-    message(FATAL_ERROR "CMake 3.8 is required to support SOURCE_DATE_EPOCH properly.")
-  endif()
-
   string(TIMESTAMP _build_date "%b %d %Y %H:%M:%S" UTC)
 
   # CMake doesn't support %e, replace leading zero in day with space

+ 139 - 139
dtool/metalibs/dtoolconfig/pydtool.cxx

@@ -2449,145 +2449,145 @@ _inP07ytw_15(PyObject *, PyObject *args) {
 
 
 static PyMethodDef python_simple_funcs[] = {
-  { "interrogate_add_search_directory", &_inP07yttbRf, METH_VARARGS },
-  { "interrogate_add_search_path", &_inP07ytda_g, METH_VARARGS },
-  { "interrogate_error_flag", &_inP07yt4RgX, METH_VARARGS },
-  { "interrogate_number_of_manifests", &_inP07yt3Gip, METH_VARARGS },
-  { "interrogate_get_manifest", &_inP07ytRKDz, METH_VARARGS },
-  { "interrogate_get_manifest_by_name", &_inP07ytgZ9N, METH_VARARGS },
-  { "interrogate_manifest_name", &_inP07ytFnRZ, METH_VARARGS },
-  { "interrogate_manifest_definition", &_inP07ytg0Qv, METH_VARARGS },
-  { "interrogate_manifest_has_type", &_inP07yttrqw, METH_VARARGS },
-  { "interrogate_manifest_get_type", &_inP07ytdmpW, METH_VARARGS },
-  { "interrogate_manifest_has_getter", &_inP07ytUYgQ, METH_VARARGS },
-  { "interrogate_manifest_getter", &_inP07yt0k7F, METH_VARARGS },
-  { "interrogate_manifest_has_int_value", &_inP07ytfIsr, METH_VARARGS },
-  { "interrogate_manifest_get_int_value", &_inP07ytvysR, METH_VARARGS },
-  { "interrogate_element_name", &_inP07ytYQ_2, METH_VARARGS },
-  { "interrogate_element_scoped_name", &_inP07yt3kdv, METH_VARARGS },
-  { "interrogate_element_has_comment", &_inP07ytew01, METH_VARARGS },
-  { "interrogate_element_comment", &_inP07ytQna7, METH_VARARGS },
-  { "interrogate_get_element_by_name", &_inP07ytkg95, METH_VARARGS },
-  { "interrogate_get_element_by_scoped_name", &_inP07ytluRc, METH_VARARGS },
-  { "interrogate_element_type", &_inP07yttHdM, METH_VARARGS },
-  { "interrogate_element_has_getter", &_inP07ytDId0, METH_VARARGS },
-  { "interrogate_element_getter", &_inP07ytHuAm, METH_VARARGS },
-  { "interrogate_element_has_setter", &_inP07yt_xr0, METH_VARARGS },
-  { "interrogate_element_setter", &_inP07ytH5qp, METH_VARARGS },
-  { "interrogate_element_is_sequence", &_inP07ytq45U, METH_VARARGS },
-  { "interrogate_element_is_mapping", &_inP07yt6IPa, METH_VARARGS },
-  { "interrogate_number_of_globals", &_inP07ytU2_B, METH_VARARGS },
-  { "interrogate_get_global", &_inP07ytHFO2, METH_VARARGS },
-  { "interrogate_number_of_global_functions", &_inP07ytcfjm, METH_VARARGS },
-  { "interrogate_get_global_function", &_inP07yt3Sjw, METH_VARARGS },
-  { "interrogate_number_of_functions", &_inP07ytgJcX, METH_VARARGS },
-  { "interrogate_get_function", &_inP07ytYlw6, METH_VARARGS },
-  { "interrogate_function_name", &_inP07ytsmnz, METH_VARARGS },
-  { "interrogate_function_scoped_name", &_inP07ytxQ10, METH_VARARGS },
-  { "interrogate_function_has_comment", &_inP07yt6gPB, METH_VARARGS },
-  { "interrogate_function_comment", &_inP07ytISgV, METH_VARARGS },
-  { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS },
-  { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS },
-  { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS },
-  { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS },
-  { "interrogate_function_module_name", &_inP07ytwpYd, METH_VARARGS },
-  { "interrogate_function_has_library_name", &_inP07ytOfNh, METH_VARARGS },
-  { "interrogate_function_library_name", &_inP07ytf5_U, METH_VARARGS },
-  { "interrogate_function_is_virtual", &_inP07ytL3ZB, METH_VARARGS },
-  { "interrogate_function_number_of_c_wrappers", &_inP07ytXw0I, METH_VARARGS },
-  { "interrogate_function_c_wrapper", &_inP07yt3zru, METH_VARARGS },
-  { "interrogate_function_number_of_python_wrappers", &_inP07ytRrg2, METH_VARARGS },
-  { "interrogate_function_python_wrapper", &_inP07ytEJCx, METH_VARARGS },
-  { "interrogate_wrapper_name", &_inP07ytWAZr, METH_VARARGS },
-  { "interrogate_wrapper_is_callable_by_name", &_inP07ytrD_M, METH_VARARGS },
-  { "interrogate_wrapper_has_comment", &_inP07ytjolz, METH_VARARGS },
-  { "interrogate_wrapper_comment", &_inP07ytt_JD, METH_VARARGS },
-  { "interrogate_wrapper_has_return_value", &_inP07ytwEts, METH_VARARGS },
-  { "interrogate_wrapper_return_type", &_inP07ytrJWs, METH_VARARGS },
-  { "interrogate_wrapper_caller_manages_return_value", &_inP07ytpmFD, METH_VARARGS },
-  { "interrogate_wrapper_return_value_destructor", &_inP07ytyYUX, METH_VARARGS },
-  { "interrogate_wrapper_number_of_parameters", &_inP07yt54dn, METH_VARARGS },
-  { "interrogate_wrapper_parameter_type", &_inP07ytGMpW, METH_VARARGS },
-  { "interrogate_wrapper_parameter_has_name", &_inP07ytNuBV, METH_VARARGS },
-  { "interrogate_wrapper_parameter_name", &_inP07yt9UwA, METH_VARARGS },
-  { "interrogate_wrapper_parameter_is_this", &_inP07yt3FDt, METH_VARARGS },
-  { "interrogate_wrapper_has_pointer", &_inP07ytf513, METH_VARARGS },
-  { "interrogate_wrapper_pointer", &_inP07ytsqGH, METH_VARARGS },
-  { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS },
-  { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS },
-  { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS },
-  { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS },
-  { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS },
-  { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS },
-  { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS },
-  { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS },
-  { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS },
-  { "interrogate_get_global_type", &_inP07ytMT0z, METH_VARARGS },
-  { "interrogate_number_of_types", &_inP07ytiW3v, METH_VARARGS },
-  { "interrogate_get_type", &_inP07yt4Px8, METH_VARARGS },
-  { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS },
-  { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS },
-  { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS },
-  { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS },
-  { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS },
-  { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS },
-  { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS },
-  { "interrogate_type_is_nested", &_inP07ytXGxx, METH_VARARGS },
-  { "interrogate_type_outer_class", &_inP07ytj04Z, METH_VARARGS },
-  { "interrogate_type_has_comment", &_inP07ytEOv4, METH_VARARGS },
-  { "interrogate_type_comment", &_inP07ytpCqJ, METH_VARARGS },
-  { "interrogate_type_has_module_name", &_inP07yt_Pz3, METH_VARARGS },
-  { "interrogate_type_module_name", &_inP07ytt_06, METH_VARARGS },
-  { "interrogate_type_has_library_name", &_inP07ytmuPs, METH_VARARGS },
-  { "interrogate_type_library_name", &_inP07ytvM8B, METH_VARARGS },
-  { "interrogate_type_is_atomic", &_inP07ytap97, METH_VARARGS },
-  { "interrogate_type_atomic_token", &_inP07yt0o8D, METH_VARARGS },
-  { "interrogate_type_is_unsigned", &_inP07ytOoQ2, METH_VARARGS },
-  { "interrogate_type_is_signed", &_inP07ytKuFh, METH_VARARGS },
-  { "interrogate_type_is_long", &_inP07yto5L6, METH_VARARGS },
-  { "interrogate_type_is_longlong", &_inP07ytzgKK, METH_VARARGS },
-  { "interrogate_type_is_short", &_inP07yt0FIF, METH_VARARGS },
-  { "interrogate_type_is_wrapped", &_inP07ytZqvD, METH_VARARGS },
-  { "interrogate_type_is_pointer", &_inP07ytDyRd, METH_VARARGS },
-  { "interrogate_type_is_const", &_inP07ytMnKa, METH_VARARGS },
-  { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS },
-  { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS },
-  { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS },
-  { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS },
-  { "interrogate_type_enum_value_name", &_inP07ytbyPY, METH_VARARGS },
-  { "interrogate_type_enum_value_scoped_name", &_inP07ytAaT6, METH_VARARGS },
-  { "interrogate_type_enum_value_comment", &_inP07ytgL9q, METH_VARARGS },
-  { "interrogate_type_enum_value", &_inP07ytWB97, METH_VARARGS },
-  { "interrogate_type_is_struct", &_inP07ytDUAl, METH_VARARGS },
-  { "interrogate_type_is_class", &_inP07yt1_Kf, METH_VARARGS },
-  { "interrogate_type_is_union", &_inP07yt98lD, METH_VARARGS },
-  { "interrogate_type_is_fully_defined", &_inP07yt9SHr, METH_VARARGS },
-  { "interrogate_type_is_unpublished", &_inP07ytdiZP, METH_VARARGS },
-  { "interrogate_type_number_of_constructors", &_inP07ytTdER, METH_VARARGS },
-  { "interrogate_type_get_constructor", &_inP07ytYO56, METH_VARARGS },
-  { "interrogate_type_has_destructor", &_inP07ytxtCG, METH_VARARGS },
-  { "interrogate_type_destructor_is_inherited", &_inP07yt_EB2, METH_VARARGS },
-  { "interrogate_type_get_destructor", &_inP07ytEG1l, METH_VARARGS },
-  { "interrogate_type_number_of_elements", &_inP07yt7tUq, METH_VARARGS },
-  { "interrogate_type_get_element", &_inP07ytyStU, METH_VARARGS },
-  { "interrogate_type_number_of_methods", &_inP07ytdM85, METH_VARARGS },
-  { "interrogate_type_get_method", &_inP07ytk_GN, METH_VARARGS },
-  { "interrogate_type_number_of_make_seqs", &_inP07yt8QjG, METH_VARARGS },
-  { "interrogate_type_get_make_seq", &_inP07ytyMtj, METH_VARARGS },
-  { "interrogate_type_number_of_casts", &_inP07ytHDtN, METH_VARARGS },
-  { "interrogate_type_get_cast", &_inP07ytHFjA, METH_VARARGS },
-  { "interrogate_type_number_of_derivations", &_inP07yt_NPR, METH_VARARGS },
-  { "interrogate_type_get_derivation", &_inP07ytcTOH, METH_VARARGS },
-  { "interrogate_type_derivation_has_upcast", &_inP07ythdU7, METH_VARARGS },
-  { "interrogate_type_get_upcast", &_inP07ytQPxU, METH_VARARGS },
-  { "interrogate_type_derivation_downcast_is_impossible", &_inP07ytO7Pz, METH_VARARGS },
-  { "interrogate_type_derivation_has_downcast", &_inP07ytvu_E, METH_VARARGS },
-  { "interrogate_type_get_downcast", &_inP07ytxGUt, METH_VARARGS },
-  { "interrogate_type_number_of_nested_types", &_inP07ytzM1P, METH_VARARGS },
-  { "interrogate_type_get_nested_type", &_inP07ytoY5L, METH_VARARGS },
-  { "interrogate_request_database", &_inP07yte_7S, METH_VARARGS },
-  { "interrogate_request_module", &_inP07ytw_15, METH_VARARGS },
-  { nullptr, nullptr }
+  { "interrogate_add_search_directory", &_inP07yttbRf, METH_VARARGS, nullptr },
+  { "interrogate_add_search_path", &_inP07ytda_g, METH_VARARGS, nullptr },
+  { "interrogate_error_flag", &_inP07yt4RgX, METH_VARARGS, nullptr },
+  { "interrogate_number_of_manifests", &_inP07yt3Gip, METH_VARARGS, nullptr },
+  { "interrogate_get_manifest", &_inP07ytRKDz, METH_VARARGS, nullptr },
+  { "interrogate_get_manifest_by_name", &_inP07ytgZ9N, METH_VARARGS, nullptr },
+  { "interrogate_manifest_name", &_inP07ytFnRZ, METH_VARARGS, nullptr },
+  { "interrogate_manifest_definition", &_inP07ytg0Qv, METH_VARARGS, nullptr },
+  { "interrogate_manifest_has_type", &_inP07yttrqw, METH_VARARGS, nullptr },
+  { "interrogate_manifest_get_type", &_inP07ytdmpW, METH_VARARGS, nullptr },
+  { "interrogate_manifest_has_getter", &_inP07ytUYgQ, METH_VARARGS, nullptr },
+  { "interrogate_manifest_getter", &_inP07yt0k7F, METH_VARARGS, nullptr },
+  { "interrogate_manifest_has_int_value", &_inP07ytfIsr, METH_VARARGS, nullptr },
+  { "interrogate_manifest_get_int_value", &_inP07ytvysR, METH_VARARGS, nullptr },
+  { "interrogate_element_name", &_inP07ytYQ_2, METH_VARARGS, nullptr },
+  { "interrogate_element_scoped_name", &_inP07yt3kdv, METH_VARARGS, nullptr },
+  { "interrogate_element_has_comment", &_inP07ytew01, METH_VARARGS, nullptr },
+  { "interrogate_element_comment", &_inP07ytQna7, METH_VARARGS, nullptr },
+  { "interrogate_get_element_by_name", &_inP07ytkg95, METH_VARARGS, nullptr },
+  { "interrogate_get_element_by_scoped_name", &_inP07ytluRc, METH_VARARGS, nullptr },
+  { "interrogate_element_type", &_inP07yttHdM, METH_VARARGS, nullptr },
+  { "interrogate_element_has_getter", &_inP07ytDId0, METH_VARARGS, nullptr },
+  { "interrogate_element_getter", &_inP07ytHuAm, METH_VARARGS, nullptr },
+  { "interrogate_element_has_setter", &_inP07yt_xr0, METH_VARARGS, nullptr },
+  { "interrogate_element_setter", &_inP07ytH5qp, METH_VARARGS, nullptr },
+  { "interrogate_element_is_sequence", &_inP07ytq45U, METH_VARARGS, nullptr },
+  { "interrogate_element_is_mapping", &_inP07yt6IPa, METH_VARARGS, nullptr },
+  { "interrogate_number_of_globals", &_inP07ytU2_B, METH_VARARGS, nullptr },
+  { "interrogate_get_global", &_inP07ytHFO2, METH_VARARGS, nullptr },
+  { "interrogate_number_of_global_functions", &_inP07ytcfjm, METH_VARARGS, nullptr },
+  { "interrogate_get_global_function", &_inP07yt3Sjw, METH_VARARGS, nullptr },
+  { "interrogate_number_of_functions", &_inP07ytgJcX, METH_VARARGS, nullptr },
+  { "interrogate_get_function", &_inP07ytYlw6, METH_VARARGS, nullptr },
+  { "interrogate_function_name", &_inP07ytsmnz, METH_VARARGS, nullptr },
+  { "interrogate_function_scoped_name", &_inP07ytxQ10, METH_VARARGS, nullptr },
+  { "interrogate_function_has_comment", &_inP07yt6gPB, METH_VARARGS, nullptr },
+  { "interrogate_function_comment", &_inP07ytISgV, METH_VARARGS, nullptr },
+  { "interrogate_function_prototype", &_inP07ytH3bx, METH_VARARGS, nullptr },
+  { "interrogate_function_is_method", &_inP07ytzeUk, METH_VARARGS, nullptr },
+  { "interrogate_function_class", &_inP07ytUeI5, METH_VARARGS, nullptr },
+  { "interrogate_function_has_module_name", &_inP07ytuSvx, METH_VARARGS, nullptr },
+  { "interrogate_function_module_name", &_inP07ytwpYd, METH_VARARGS, nullptr },
+  { "interrogate_function_has_library_name", &_inP07ytOfNh, METH_VARARGS, nullptr },
+  { "interrogate_function_library_name", &_inP07ytf5_U, METH_VARARGS, nullptr },
+  { "interrogate_function_is_virtual", &_inP07ytL3ZB, METH_VARARGS, nullptr },
+  { "interrogate_function_number_of_c_wrappers", &_inP07ytXw0I, METH_VARARGS, nullptr },
+  { "interrogate_function_c_wrapper", &_inP07yt3zru, METH_VARARGS, nullptr },
+  { "interrogate_function_number_of_python_wrappers", &_inP07ytRrg2, METH_VARARGS, nullptr },
+  { "interrogate_function_python_wrapper", &_inP07ytEJCx, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_name", &_inP07ytWAZr, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_is_callable_by_name", &_inP07ytrD_M, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_has_comment", &_inP07ytjolz, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_comment", &_inP07ytt_JD, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_has_return_value", &_inP07ytwEts, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_return_type", &_inP07ytrJWs, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_caller_manages_return_value", &_inP07ytpmFD, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_return_value_destructor", &_inP07ytyYUX, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_number_of_parameters", &_inP07yt54dn, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_parameter_type", &_inP07ytGMpW, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_parameter_has_name", &_inP07ytNuBV, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_parameter_name", &_inP07yt9UwA, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_parameter_is_this", &_inP07yt3FDt, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_has_pointer", &_inP07ytf513, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_pointer", &_inP07ytsqGH, METH_VARARGS, nullptr },
+  { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS, nullptr },
+  { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS, nullptr },
+  { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS, nullptr },
+  { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS, nullptr },
+  { "interrogate_get_global_type", &_inP07ytMT0z, METH_VARARGS, nullptr },
+  { "interrogate_number_of_types", &_inP07ytiW3v, METH_VARARGS, nullptr },
+  { "interrogate_get_type", &_inP07yt4Px8, METH_VARARGS, nullptr },
+  { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS, nullptr },
+  { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS, nullptr },
+  { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS, nullptr },
+  { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS, nullptr },
+  { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS, nullptr },
+  { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS, nullptr },
+  { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS, nullptr },
+  { "interrogate_type_is_nested", &_inP07ytXGxx, METH_VARARGS, nullptr },
+  { "interrogate_type_outer_class", &_inP07ytj04Z, METH_VARARGS, nullptr },
+  { "interrogate_type_has_comment", &_inP07ytEOv4, METH_VARARGS, nullptr },
+  { "interrogate_type_comment", &_inP07ytpCqJ, METH_VARARGS, nullptr },
+  { "interrogate_type_has_module_name", &_inP07yt_Pz3, METH_VARARGS, nullptr },
+  { "interrogate_type_module_name", &_inP07ytt_06, METH_VARARGS, nullptr },
+  { "interrogate_type_has_library_name", &_inP07ytmuPs, METH_VARARGS, nullptr },
+  { "interrogate_type_library_name", &_inP07ytvM8B, METH_VARARGS, nullptr },
+  { "interrogate_type_is_atomic", &_inP07ytap97, METH_VARARGS, nullptr },
+  { "interrogate_type_atomic_token", &_inP07yt0o8D, METH_VARARGS, nullptr },
+  { "interrogate_type_is_unsigned", &_inP07ytOoQ2, METH_VARARGS, nullptr },
+  { "interrogate_type_is_signed", &_inP07ytKuFh, METH_VARARGS, nullptr },
+  { "interrogate_type_is_long", &_inP07yto5L6, METH_VARARGS, nullptr },
+  { "interrogate_type_is_longlong", &_inP07ytzgKK, METH_VARARGS, nullptr },
+  { "interrogate_type_is_short", &_inP07yt0FIF, METH_VARARGS, nullptr },
+  { "interrogate_type_is_wrapped", &_inP07ytZqvD, METH_VARARGS, nullptr },
+  { "interrogate_type_is_pointer", &_inP07ytDyRd, METH_VARARGS, nullptr },
+  { "interrogate_type_is_const", &_inP07ytMnKa, METH_VARARGS, nullptr },
+  { "interrogate_type_is_typedef", &_inP07ytRtji, METH_VARARGS, nullptr },
+  { "interrogate_type_wrapped_type", &_inP07ytCnbQ, METH_VARARGS, nullptr },
+  { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_enum_values", &_inP07ytihbt, METH_VARARGS, nullptr },
+  { "interrogate_type_enum_value_name", &_inP07ytbyPY, METH_VARARGS, nullptr },
+  { "interrogate_type_enum_value_scoped_name", &_inP07ytAaT6, METH_VARARGS, nullptr },
+  { "interrogate_type_enum_value_comment", &_inP07ytgL9q, METH_VARARGS, nullptr },
+  { "interrogate_type_enum_value", &_inP07ytWB97, METH_VARARGS, nullptr },
+  { "interrogate_type_is_struct", &_inP07ytDUAl, METH_VARARGS, nullptr },
+  { "interrogate_type_is_class", &_inP07yt1_Kf, METH_VARARGS, nullptr },
+  { "interrogate_type_is_union", &_inP07yt98lD, METH_VARARGS, nullptr },
+  { "interrogate_type_is_fully_defined", &_inP07yt9SHr, METH_VARARGS, nullptr },
+  { "interrogate_type_is_unpublished", &_inP07ytdiZP, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_constructors", &_inP07ytTdER, METH_VARARGS, nullptr },
+  { "interrogate_type_get_constructor", &_inP07ytYO56, METH_VARARGS, nullptr },
+  { "interrogate_type_has_destructor", &_inP07ytxtCG, METH_VARARGS, nullptr },
+  { "interrogate_type_destructor_is_inherited", &_inP07yt_EB2, METH_VARARGS, nullptr },
+  { "interrogate_type_get_destructor", &_inP07ytEG1l, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_elements", &_inP07yt7tUq, METH_VARARGS, nullptr },
+  { "interrogate_type_get_element", &_inP07ytyStU, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_methods", &_inP07ytdM85, METH_VARARGS, nullptr },
+  { "interrogate_type_get_method", &_inP07ytk_GN, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_make_seqs", &_inP07yt8QjG, METH_VARARGS, nullptr },
+  { "interrogate_type_get_make_seq", &_inP07ytyMtj, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_casts", &_inP07ytHDtN, METH_VARARGS, nullptr },
+  { "interrogate_type_get_cast", &_inP07ytHFjA, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_derivations", &_inP07yt_NPR, METH_VARARGS, nullptr },
+  { "interrogate_type_get_derivation", &_inP07ytcTOH, METH_VARARGS, nullptr },
+  { "interrogate_type_derivation_has_upcast", &_inP07ythdU7, METH_VARARGS, nullptr },
+  { "interrogate_type_get_upcast", &_inP07ytQPxU, METH_VARARGS, nullptr },
+  { "interrogate_type_derivation_downcast_is_impossible", &_inP07ytO7Pz, METH_VARARGS, nullptr },
+  { "interrogate_type_derivation_has_downcast", &_inP07ytvu_E, METH_VARARGS, nullptr },
+  { "interrogate_type_get_downcast", &_inP07ytxGUt, METH_VARARGS, nullptr },
+  { "interrogate_type_number_of_nested_types", &_inP07ytzM1P, METH_VARARGS, nullptr },
+  { "interrogate_type_get_nested_type", &_inP07ytoY5L, METH_VARARGS, nullptr },
+  { "interrogate_request_database", &_inP07yte_7S, METH_VARARGS, nullptr },
+  { "interrogate_request_module", &_inP07ytw_15, METH_VARARGS, nullptr },
+  { nullptr, nullptr, 0, nullptr }
 };
 
 #if PY_MAJOR_VERSION >= 3

+ 2 - 2
dtool/src/cppparser/cppManifest.cxx

@@ -226,8 +226,8 @@ output(std::ostream &out) const {
         out << "$1";
       }
 
-      for (int i = 1; i < _num_parameters; ++i) {
-        if (_variadic_param == i) {
+      for (size_t i = 1; i < _num_parameters; ++i) {
+        if (_variadic_param == (int)i) {
           out << ", ...";
         } else {
           out << ", $" << i + 1;

+ 1 - 1
dtool/src/cppparser/cppManifest.h

@@ -43,7 +43,7 @@ public:
 
   std::string _name;
   bool _has_parameters;
-  int _num_parameters;
+  size_t _num_parameters;
   int _variadic_param;
   cppyyltype _loc;
   CPPExpression *_expr;

+ 2 - 2
dtool/src/cppparser/cppNamespace.cxx

@@ -22,9 +22,9 @@
 CPPNamespace::
 CPPNamespace(CPPIdentifier *ident, CPPScope *scope, const CPPFile &file) :
   CPPDeclaration(file),
+  _is_inline(false),
   _ident(ident),
-  _scope(scope),
-  _is_inline(false)
+  _scope(scope)
 {
 }
 

+ 2 - 2
dtool/src/cppparser/cppStructType.cxx

@@ -58,8 +58,8 @@ CPPStructType(const CPPStructType &copy) :
   CPPExtensionType(copy),
   _scope(copy._scope),
   _incomplete(copy._incomplete),
-  _derivation(copy._derivation),
-  _final(copy._final)
+  _final(copy._final),
+  _derivation(copy._derivation)
 {
   _subst_decl_recursive_protect = false;
 }

+ 7 - 0
dtool/src/dtoolbase/CMakeLists.txt

@@ -46,6 +46,7 @@ set(P3DTOOLBASE_HEADERS
   typeRegistryNode.I typeRegistryNode.h
   typedObject.I typedObject.h
   pallocator.T pallocator.h
+  patomic.h patomic.I
   pdeque.h plist.h pmap.h pset.h
   pvector.h epvector.h
   lookup3.h
@@ -70,6 +71,7 @@ set(P3DTOOLBASE_SOURCES
   mutexWin32Impl.cxx
   mutexSpinlockImpl.cxx
   neverFreeMemory.cxx
+  patomic.cxx
   pdtoa.cxx
   pstrtod.cxx
   register_type.cxx
@@ -95,6 +97,11 @@ target_include_directories(p3dtoolbase PUBLIC
 target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS PKG::MIMALLOC)
 target_interrogate(p3dtoolbase ${P3DTOOLBASE_SOURCES} EXTENSIONS ${P3DTOOLBASE_IGATEEXT})
 
+if(HAVE_MIMALLOC AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+  # Do not re-export symbols from these libraries.
+  target_link_options(p3dtoolbase PRIVATE "LINKER:--exclude-libs,libmimalloc.a")
+endif()
+
 if(NOT BUILD_METALIBS)
   install(TARGETS p3dtoolbase
     EXPORT Core COMPONENT Core

+ 1 - 0
dtool/src/dtoolbase/p3dtoolbase_composite2.cxx

@@ -1,3 +1,4 @@
+#include "patomic.cxx"
 #include "mutexPosixImpl.cxx"
 #include "mutexWin32Impl.cxx"
 #include "mutexSpinlockImpl.cxx"

+ 211 - 6
dtool/src/dtoolbase/patomic.I

@@ -11,6 +11,7 @@
  * @date 2022-01-28
  */
 
+#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
 /**
  * Value initializer.
  */
@@ -247,21 +248,225 @@ operator ^=(T arg) noexcept {
   return _value ^= arg;
 }
 
+#endif  // defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
+
+/**
+ * Initializes the variable to zero (according to C++20 semantics, NOT C++11
+ * semantics!)
+ */
+constexpr patomic_unsigned_lock_free::
+patomic_unsigned_lock_free() noexcept :
+  patomic<uint32_t>(0u) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_unsigned_lock_free::
+patomic_unsigned_lock_free(uint32_t desired) noexcept :
+  patomic<uint32_t>(desired) {
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+wait(uint32_t old, std::memory_order order) const noexcept {
+  if (load(order) == old) {
+    patomic_wait((const volatile uint32_t *)this, old);
+  }
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+notify_one() noexcept {
+  patomic_notify_one((volatile uint32_t *)this);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+notify_all() noexcept {
+  patomic_notify_all((volatile uint32_t *)this);
+}
+
+/**
+ * Initializes the variable to zero (according to C++20 semantics, NOT C++11
+ * semantics!)
+ */
+constexpr patomic_signed_lock_free::
+patomic_signed_lock_free() noexcept :
+  patomic<int32_t>(0) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_signed_lock_free::
+patomic_signed_lock_free(int32_t desired) noexcept :
+  patomic<int32_t>(desired) {
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+wait(int32_t old, std::memory_order order) const noexcept {
+  if (load(order) == old) {
+    patomic_wait((const volatile int32_t *)this, old);
+  }
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+notify_one() noexcept {
+  patomic_notify_one((volatile int32_t *)this);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+notify_all() noexcept {
+  patomic_notify_all((volatile int32_t *)this);
+}
+
+/**
+ * Allows assignment from ATOMIC_FLAG_INIT.
+ */
+constexpr patomic_flag::
+patomic_flag(bool desired) noexcept : _value(desired) {
+}
+
+/**
+ * Sets the flag to false.
+ */
+ALWAYS_INLINE void patomic_flag::
+clear(std::memory_order order) noexcept {
+  _value.store(0u, order);
+}
 
 /**
  * Sets the flag to true and returns the previous value.
  */
 ALWAYS_INLINE bool patomic_flag::
 test_and_set(std::memory_order order) noexcept {
-  bool value = __internal_flag;
-  __internal_flag = true;
-  return value;
+  return _value.exchange(1u, order) != 0u;
 }
 
 /**
- * Sets the flag to false.
+ * Returns the current value of the flag.
+ */
+ALWAYS_INLINE bool patomic_flag::
+test(std::memory_order order) const noexcept {
+  return _value.load(order) != 0u;
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
  */
 ALWAYS_INLINE void patomic_flag::
-clear(std::memory_order order) noexcept {
-  __internal_flag = false;
+wait(bool old, std::memory_order order) const noexcept {
+  _value.wait(old, order);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_flag::
+notify_one() noexcept {
+  _value.notify_one();
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_flag::
+notify_all() noexcept {
+  _value.notify_all();
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_wait(const volatile int32_t *value, int32_t old) {
+  patomic_wait((const volatile uint32_t *)value, (uint32_t)old);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_one(volatile int32_t *value) {
+  patomic_notify_one((volatile uint32_t *)value);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_all(volatile int32_t *value) {
+  patomic_notify_all((volatile uint32_t *)value);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_wait(const volatile uint32_t *value, uint32_t old) {
+#ifdef __linux__
+  while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) {
+    syscall(SYS_futex, old, FUTEX_WAIT_PRIVATE, old, 0, 0, 0);
+  }
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  while (*value == old) {
+//    WaitOnAddress((volatile void *)value, &old, sizeof(uint32_t), INFINITE);
+//  }
+#elif defined(_WIN32)
+  while (*value == old) {
+    _patomic_wait_func((volatile void *)value, &old, sizeof(uint32_t), INFINITE);
+  }
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_wait(value, old);
+#else
+  while (*value == old);
+#endif
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_one(volatile uint32_t *value) {
+#ifdef __linux__
+  syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  WakeByAddressSingle((void *)value);
+#elif defined(_WIN32)
+  _patomic_wake_one_func((void *)value);
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_notify_all(value);
+#endif
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_all(volatile uint32_t *value) {
+#ifdef __linux__
+  syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, INT_MAX, 0, 0, 0);
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  WakeByAddressAll((void *)value);
+#elif defined(_WIN32)
+  _patomic_wake_all_func((void *)value);
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_notify_all(value);
+#endif
 }

+ 168 - 0
dtool/src/dtoolbase/patomic.cxx

@@ -0,0 +1,168 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file patomic.cxx
+ * @author rdb
+ * @date 2022-02-23
+ */
+
+#include "patomic.h"
+
+#include <functional>
+
+static_assert(sizeof(patomic_unsigned_lock_free) == sizeof(uint32_t),
+              "expected atomic uint32_t to have same size as uint32_t");
+static_assert(sizeof(patomic_signed_lock_free) == sizeof(int32_t),
+              "expected atomic int32_t to have same size as int32_t");
+static_assert(sizeof(uint32_t) == sizeof(int32_t),
+              "expected int32_t to have same size as uint32_t");
+
+#if !defined(CPPPARSER) && defined(_WIN32)
+
+// On Windows 7, we try to load the Windows 8 functions dynamically, and
+// fall back to a condition variable table if they aren't available.
+static BOOL __stdcall initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout);
+static void __stdcall dummy_wake(PVOID addr) {}
+
+BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD) = &initialize_wait;
+void (__stdcall *_patomic_wake_one_func)(PVOID) = &dummy_wake;
+void (__stdcall *_patomic_wake_all_func)(PVOID) = &dummy_wake;
+
+// Randomly pick an entry into the wait table based on the hash of the address.
+// It's possible to get hash collision, but that's not so bad, it just means
+// that the other thread will get a spurious wakeup.
+struct alignas(64) WaitTableEntry {
+  SRWLOCK _lock = SRWLOCK_INIT;
+  CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT;
+  DWORD _waiters = 0;
+};
+static WaitTableEntry _wait_table[64] = {};
+static const size_t _wait_hash_mask = 63;
+
+/**
+ * Emulates WakeByAddressSingle for Windows Vista and 7.
+ */
+static void __stdcall
+emulated_wake(PVOID addr) {
+  size_t i = std::hash<volatile void *>{}(addr) & (sizeof(_wait_table) / sizeof(WaitTableEntry) - 1);
+  WaitTableEntry &entry = _wait_table[i];
+  AcquireSRWLockExclusive(&entry._lock);
+  DWORD num_waiters = entry._waiters;
+  ReleaseSRWLockExclusive(&entry._lock);
+  if (num_waiters > 0) {
+    // We have to wake up all the threads, even if only one of them is for this
+    // address.  Some of them will get a spurious wakeup, but that's OK.
+    WakeAllConditionVariable(&entry._cvar);
+  }
+}
+
+/**
+ * Emulates WaitOnAddress for Windows Vista and 7.  Only supports aligned
+ * 32-bit values.
+ */
+static BOOL __stdcall
+emulated_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
+  assert(size == sizeof(LONG));
+
+  LONG cmpval = *(LONG *)cmp;
+  if (*(LONG *)addr != cmpval) {
+    return TRUE;
+  }
+
+  size_t i = std::hash<volatile void *>{}(addr) & _wait_hash_mask;
+  WaitTableEntry &entry = _wait_table[i];
+  AcquireSRWLockExclusive(&entry._lock);
+  ++entry._waiters;
+  while (*(LONG *)addr == cmpval) {
+    if (SleepConditionVariableSRW(&entry._cvar, &entry._lock, timeout, 0) != 0) {
+      // Timeout.
+      --entry._waiters;
+      ReleaseSRWLockExclusive(&entry._lock);
+      return FALSE;
+    }
+  }
+  --entry._waiters;
+  ReleaseSRWLockExclusive(&entry._lock);
+  return TRUE;
+}
+
+/**
+ * Initially assigned to the wait function slot to initialize the function
+ * pointers.
+ */
+static BOOL __stdcall
+initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
+  // There's a chance of a race here, with two threads trying to initialize the
+  // functions at the same time.  That's OK, because they should all produce
+  // the same results, and the stores to the function pointers are atomic.
+  HMODULE lib = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll");
+  if (lib) {
+    auto wait_func = (decltype(_patomic_wait_func))GetProcAddress(lib, "WaitOnAddress");
+    auto wake_one_func = (decltype(_patomic_wake_one_func))GetProcAddress(lib, "WakeByAddressSingle");
+    auto wake_all_func = (decltype(_patomic_wake_all_func))GetProcAddress(lib, "WakeByAddressAll");
+    if (wait_func && wake_one_func && wake_all_func) {
+      // Make sure that the wake function is guaranteed to be visible to other
+      // threads by the time we assign the wait function.
+      _patomic_wake_one_func = wake_one_func;
+      _patomic_wake_all_func = wake_all_func;
+      patomic_thread_fence(std::memory_order_release);
+      _patomic_wait_func = wait_func;
+      return wait_func(addr, cmp, size, timeout);
+    }
+  }
+
+  // We don't have Windows 8's functions, use the emulated wait and wake funcs.
+  _patomic_wake_one_func = &emulated_wake;
+  _patomic_wake_all_func = &emulated_wake;
+  patomic_thread_fence(std::memory_order_release);
+  _patomic_wait_func = &emulated_wait;
+
+  return emulated_wait(addr, cmp, size, timeout);
+}
+
+#elif !defined(CPPPARSER) && !defined(__linux__) && defined(HAVE_POSIX_THREADS)
+
+// Same as above, but using pthreads.
+struct alignas(64) WaitTableEntry {
+  pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
+  pthread_cond_t _cvar = PTHREAD_COND_INITIALIZER;
+  unsigned int _waiters = 0;
+};
+static WaitTableEntry _wait_table[64];
+static const size_t _wait_hash_mask = 63;
+
+/**
+ *
+ */
+void
+_patomic_wait(const volatile uint32_t *value, uint32_t old) {
+  WaitTableEntry &entry = _wait_table[std::hash<const volatile void *>{}(value) & _wait_hash_mask];
+  pthread_mutex_lock(&entry._lock);
+  ++entry._waiters;
+  while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) {
+    pthread_cond_wait(&entry._cvar, &entry._lock);
+  }
+  --entry._waiters;
+  pthread_mutex_unlock(&entry._lock);
+}
+
+/**
+ *
+ */
+void
+_patomic_notify_all(volatile uint32_t *value) {
+  WaitTableEntry &entry = _wait_table[std::hash<const volatile void *>{}(value) & _wait_hash_mask];
+  pthread_mutex_lock(&entry._lock);
+  unsigned int num_waiters = entry._waiters;
+  pthread_mutex_unlock(&entry._lock);
+  if (num_waiters > 0) {
+    pthread_cond_broadcast(&entry._cvar);
+  }
+}
+
+#endif

+ 85 - 18
dtool/src/dtoolbase/patomic.h

@@ -19,6 +19,19 @@
 
 #include <atomic>
 
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+#endif
+
+#ifdef __linux__
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif
+
 #if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
 
 /**
@@ -73,36 +86,90 @@ private:
   T _value;
 };
 
+#define patomic_thread_fence(order) (std::atomic_signal_fence((order)))
+
+#else
+
+// We're using real threading, so use the real implementation.
+template<class T>
+using patomic = std::atomic<T>;
+
+#define patomic_thread_fence(order) (std::atomic_thread_fence((order)))
+
+#endif
+
 /**
- * Dummy implementation of std::atomic_flag that does not do any atomic
- * operations.
+ * Implementation of atomic_unsigned_lock_free with C++20 semantics.
  */
-struct EXPCL_DTOOL_DTOOLBASE patomic_flag {
-  constexpr patomic_flag() noexcept = default;
+class EXPCL_DTOOL_DTOOLBASE patomic_unsigned_lock_free : public patomic<uint32_t> {
+public:
+  typedef uint32_t value_type;
 
-  patomic_flag(const patomic_flag &) = delete;
-  patomic_flag &operator=(const patomic_flag &) = delete;
-
-  ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept;
-  ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  constexpr patomic_unsigned_lock_free() noexcept;
+  constexpr patomic_unsigned_lock_free(uint32_t desired) noexcept;
 
-  bool __internal_flag = false;
+  INLINE void wait(uint32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
 };
 
-#define patomic_thread_fence(order) (std::atomic_signal_fence((order)))
+/**
+ * Implementation of atomic_signed_lock_free with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_signed_lock_free : public patomic<int32_t> {
+public:
+  typedef int32_t value_type;
 
-#include "patomic.I"
+  constexpr patomic_signed_lock_free() noexcept;
+  constexpr patomic_signed_lock_free(int32_t desired) noexcept;
 
-#else
+  INLINE void wait(int32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
+};
 
-// We're using real threading, so use the real implementation.
-template<class T>
-using patomic = std::atomic<T>;
+/**
+ * Implementation of atomic_flag with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_flag {
+public:
+  constexpr patomic_flag() noexcept = default;
+  constexpr patomic_flag(bool desired) noexcept;
 
-typedef std::atomic_flag patomic_flag;
+  patomic_flag(const patomic_flag &) = delete;
+  patomic_flag &operator=(const patomic_flag &) = delete;
 
-#define patomic_thread_fence(order) (std::atomic_thread_fence((order)))
+  ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  ALWAYS_INLINE bool test(std::memory_order order = std::memory_order_seq_cst) const noexcept;
+
+  ALWAYS_INLINE void wait(bool old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
 
+private:
+  patomic_unsigned_lock_free _value { 0u };
+};
+
+#ifndef CPPPARSER
+ALWAYS_INLINE void patomic_wait(const volatile int32_t *value, int32_t old);
+ALWAYS_INLINE void patomic_notify_one(volatile int32_t *value);
+ALWAYS_INLINE void patomic_notify_all(volatile int32_t *value);
+
+ALWAYS_INLINE void patomic_wait(const volatile uint32_t *value, uint32_t old);
+ALWAYS_INLINE void patomic_notify_one(volatile uint32_t *value);
+ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value);
+
+#ifdef _WIN32
+EXPCL_DTOOL_DTOOLBASE extern BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD);
+EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_one_func)(PVOID);
+EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_all_func)(PVOID);
+#elif !defined(__linux__) && defined(HAVE_POSIX_THREADS)
+EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old);
+EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value);
 #endif
 
+#include "patomic.I"
+#endif  // CPPPARSER
+
 #endif

+ 0 - 1
dtool/src/dtoolbase/typeHandle_ext.cxx

@@ -38,7 +38,6 @@ make(PyTypeObject *tp) {
 PyObject *Extension<TypeHandle>::
 __reduce__() const {
   extern struct Dtool_PyTypedObject Dtool_TypeHandle;
-  extern struct Dtool_PyTypedObject Dtool_TypeRegistry;
 
   if (!*_this) {
     PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_TypeHandle, "none");

+ 39 - 6
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -259,10 +259,14 @@ ns_has_environment_variable(const string &var) const {
     return true;
   }
 
-#ifndef PREREAD_ENVIRONMENT
-  return getenv(var.c_str()) != nullptr;
-#else
+#ifdef PREREAD_ENVIRONMENT
   return false;
+#elif defined(_MSC_VER)
+  size_t size = 0;
+  getenv_s(&size, nullptr, 0, var.c_str());
+  return size != 0;
+#else
+  return getenv(var.c_str()) != nullptr;
 #endif
 }
 
@@ -301,11 +305,24 @@ ns_get_environment_variable(const string &var) const {
   }
 
 #ifndef PREREAD_ENVIRONMENT
+#ifdef _MSC_VER
+  std::string value(128, '\0');
+  size_t size = value.size();
+  while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) {
+    value.resize(size);
+  }
+  if (size != 0) {
+    // Strip off the trailing null byte.
+    value.resize(size - 1);
+    return value;
+  }
+#else
   const char *def = getenv(var.c_str());
   if (def != nullptr) {
     return def;
   }
 #endif
+#endif
 
 #ifdef _WIN32
   // On Windows only, we also simulate several standard folder names as
@@ -414,14 +431,15 @@ ns_get_environment_variable(const string &var) const {
 void ExecutionEnvironment::
 ns_set_environment_variable(const string &var, const string &value) {
   _variables[var] = value;
+
+#ifdef _MSC_VER
+  _putenv_s(var.c_str(), value.c_str());
+#else
   string putstr = var + "=" + value;
 
   // putenv() requires us to malloc a new C-style string.
   char *put = (char *)malloc(putstr.length() + 1);
   strcpy(put, putstr.c_str());
-#ifdef _MSC_VER
-  _putenv(put);
-#else
   putenv(put);
 #endif
 }
@@ -447,12 +465,27 @@ ns_clear_shadow(const string &var) {
 
 #ifdef PREREAD_ENVIRONMENT
   // Now we have to replace the value in the table.
+#ifdef _MSC_VER
+  std::string value(128, '\0');
+  size_t size = value.size();
+  while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) {
+    value.resize(size);
+  }
+  if (size != 0) {
+    // Strip off the trailing null byte.
+    value.resize(size - 1);
+    (*vi).second = std::move(value);
+  } else {
+    _variables.erase(vi);
+  }
+#else
   const char *def = getenv(var.c_str());
   if (def != nullptr) {
     (*vi).second = def;
   } else {
     _variables.erase(vi);
   }
+#endif
 #endif  // PREREAD_ENVIRONMENT
 }
 

+ 15 - 1
dtool/src/dtoolutil/filename.cxx

@@ -152,10 +152,22 @@ get_panda_root() {
 
   if (panda_root == nullptr) {
     panda_root = new string;
+
+#ifdef _MSC_VER
+    char *envvar = nullptr;
+    size_t size = 0;
+    while (getenv_s(&size, envvar, size, "PANDA_ROOT") == ERANGE) {
+      envvar = (char *)alloca(size);
+    }
+    if (size != 0) {
+      (*panda_root) = front_to_back_slash(envvar);
+    }
+#else
     const char *envvar = getenv("PANDA_ROOT");
     if (envvar != nullptr) {
       (*panda_root) = front_to_back_slash(envvar);
     }
+#endif
 
     // Ensure the string ends in a backslash.  If PANDA_ROOT is empty or
     // undefined, this function must return a single backslash--not an empty
@@ -477,7 +489,8 @@ get_home_directory() {
   if (AtomicAdjust::get_ptr(_home_directory) == nullptr) {
     Filename home_directory;
 
-    // In all environments, check $HOME first.
+    // In all environments except Windows, check $HOME first.
+#ifndef _WIN32
     char *home = getenv("HOME");
     if (home != nullptr) {
       Filename dirname = from_os_specific(home);
@@ -487,6 +500,7 @@ get_home_directory() {
         }
       }
     }
+#endif
 
     if (home_directory.empty()) {
 #ifdef _WIN32

+ 2 - 0
dtool/src/dtoolutil/pandaSystem.cxx

@@ -53,6 +53,8 @@ PandaSystem() :
   set_system_tag("system", "malloc", "dlmalloc");
 #elif defined(USE_MEMORY_PTMALLOC2)
   set_system_tag("system", "malloc", "ptmalloc2");
+#elif defined(USE_MEMORY_MIMALLOC)
+  set_system_tag("system", "malloc", "mimalloc");
 #else
   set_system_tag("system", "malloc", "malloc");
 #endif

+ 10 - 2
dtool/src/dtoolutil/panda_getopt_impl.cxx

@@ -140,6 +140,10 @@ PandaGetopt(int argc, char *const argv[], const char *optstring,
   // _options[0] is used for invalid characters.
   _options.push_back(Option('?', no_argument));
 
+#ifdef _MSC_VER
+  size_t size;
+#endif
+
   if (optstring[0] == '-') {
     // RETURN_IN_ORDER: Non-option arguments (operands) are handled as if they
     // were the argument to an option with the value 1 ('\001').
@@ -154,8 +158,12 @@ PandaGetopt(int argc, char *const argv[], const char *optstring,
     // argument is reached, or when the element of argv is "--".
     ++optstring;
     _require_order = true;
-
-  } else if (getenv("POSIXLY_CORRECT") != nullptr) {
+  }
+#ifdef _MSC_VER
+  else if (getenv_s(&size, nullptr, 0, "POSIXLY_CORRECT") == 0 && size != 0) {
+#else
+  else if (getenv("POSIXLY_CORRECT") != nullptr) {
+#endif
     // REQUIRE_ORDER.
     _require_order = true;
 

+ 3 - 4
dtool/src/dtoolutil/pfstreamBuf.cxx

@@ -333,8 +333,9 @@ open_pipe(const string &cmd) {
 
   // Both WinExec() and CreateProcess() want a non-const char pointer.  Maybe
   // they change it, and maybe they don't.  I'm not taking chances.
-  char *cmdline = new char[cmd.length() + 1];
-  strcpy(cmdline, cmd.c_str());
+  char *cmdline = (char *)alloca(cmd.size() + 1);
+  memcpy(cmdline, cmd.data(), cmd.size());
+  cmdline[cmd.size()] = 0;
 
   // We should be using CreateProcess() instead of WinExec(), but that seems
   // to be likely to crash Win98.  WinExec() seems better behaved, and it's
@@ -347,8 +348,6 @@ open_pipe(const string &cmd) {
     // Don't return yet, since we still need to clean up.
   }
 
-  delete[] cmdline;
-
   // Now restore our own stdout, up here in the parent process.
   if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
 #ifndef NDEBUG

+ 4 - 4
dtool/src/interrogate/interfaceMaker.cxx

@@ -77,8 +77,8 @@ InterfaceMaker::Function::
  */
 InterfaceMaker::MakeSeq::
 MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
-  _name(name),
   _imake_seq(imake_seq),
+  _name(name),
   _length_getter(nullptr),
   _element_getter(nullptr)
 {
@@ -237,7 +237,7 @@ generate_wrappers() {
 
   int num_global_elements = idb->get_num_global_elements();
   for (int gi = 0; gi < num_global_elements; ++gi) {
-    printf(" Global Type = %d", gi);
+    //printf(" Global Type = %d", gi);
     TypeIndex type_index = idb->get_global_element(gi);
     record_object(type_index);
   }
@@ -256,12 +256,12 @@ generate_wrappers() {
       FunctionIndex func_index = iman.get_getter();
       record_function(dummy_type, func_index);
     }
-    printf(" Manifests %d\n", mi);
+    //printf(" Manifests %d\n", mi);
   }
 
   int num_elements = idb->get_num_global_elements();
   for (int ei = 0; ei < num_elements; ei++) {
-    printf(" Element %d\n", ei);
+    //printf(" Element %d\n", ei);
 
     ElementIndex element_index = idb->get_global_element(ei);
     const InterrogateElement &ielement = idb->get_element(element_index);

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2510,11 +2510,11 @@ write_module_class(ostream &out, Object *obj) {
           out << "  if (arg2 != nullptr && arg2 != Py_None) {\n";
           out << "    PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
           write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4,
-                                true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true);
+                                true, true, AT_varargs, return_flags | RF_decref_args, true);
           out << "    Py_DECREF(args);\n";
           out << "  } else {\n";
           write_function_forset(out, one_param_remaps, 1, 1, expected_params, 4,
-                                true, true, AT_single_arg, RF_pyobject | RF_err_null, true);
+                                true, true, AT_single_arg, return_flags, true);
           out << "  }\n\n";
 
           out << "  if (!_PyErr_OCCURRED()) {\n";

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonObj.cxx

@@ -102,10 +102,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
          ++fi) {
       Function *func = (*fi);
       out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name
-          << ", METH_VARARGS },\n";
+          << ", METH_VARARGS, nullptr },\n";
     }
   }
-  out << "  { nullptr, nullptr }\n"
+  out << "  { nullptr, nullptr, 0, nullptr }\n"
       << "};\n\n"
 
       << "#if PY_MAJOR_VERSION >= 3\n"

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonSimple.cxx

@@ -89,10 +89,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
     for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
       FunctionRemap *remap = (*ri);
       out << "  { \"" << remap->_reported_name << "\", &"
-          << remap->_wrapper_name << ", METH_VARARGS },\n";
+          << remap->_wrapper_name << ", METH_VARARGS, nullptr },\n";
     }
   }
-  out << "  { nullptr, nullptr }\n"
+  out << "  { nullptr, nullptr, 0, nullptr }\n"
       << "};\n\n"
 
       << "#if PY_MAJOR_VERSION >= 3\n"

+ 7 - 0
dtool/src/interrogate/interrogate.cxx

@@ -547,8 +547,15 @@ main(int argc, char **argv) {
   // We allow overriding this value by setting SOURCE_DATE_EPOCH to support
   // reproducible builds.
   int file_identifier;
+#ifdef _MSC_VER
+  char source_date_epoch[64];
+  size_t source_date_epoch_size = 0;
+  if (getenv_s(&source_date_epoch_size, source_date_epoch,
+               sizeof(source_date_epoch), "SOURCE_DATE_EPOCH"), source_date_epoch_size > 1) {
+#else
   const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
   if (source_date_epoch != nullptr && source_date_epoch[0] != 0) {
+#endif
     file_identifier = atoi(source_date_epoch);
   } else {
     file_identifier = time(nullptr);

+ 6 - 0
dtool/src/prc/CMakeLists.txt

@@ -99,6 +99,12 @@ if(ANDROID)
   target_link_libraries(p3prc log)
 endif()
 
+if(HAVE_OPENSSL AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+  # Do not re-export symbols from these libraries.
+  target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libssl.a")
+  target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libcrypto.a")
+endif()
+
 install(TARGETS p3prc
   EXPORT Core COMPONENT Core
   DESTINATION ${CMAKE_INSTALL_LIBDIR}

+ 1 - 0
dtool/src/prc/configFlags.h

@@ -17,6 +17,7 @@
 #include "dtoolbase.h"
 #include "numeric_types.h"
 #include "atomicAdjust.h"
+#include "memoryBase.h"
 
 /**
  * This class is the base class of both ConfigVariable and ConfigVariableCore.

+ 2 - 1
dtool/src/prc/configVariableBase.h

@@ -21,6 +21,7 @@
 #include "configVariableManager.h"
 #include "vector_string.h"
 #include "pset.h"
+#include "memoryBase.h"
 
 // Use this macro to wrap around a description passed to a ConfigVariable
 // constructor.  This allows the description to be completely compiled out, so
@@ -42,7 +43,7 @@
  * and/or ConfigDeclaration, more or less duplicating the interface presented
  * there.
  */
-class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags {
+class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags, public MemoryBase {
 protected:
   INLINE ConfigVariableBase(const std::string &name, ValueType type);
   ConfigVariableBase(const std::string &name, ValueType type,

+ 2 - 1
dtool/src/prc/configVariableCore.h

@@ -18,6 +18,7 @@
 #include "configFlags.h"
 #include "configPageManager.h"
 #include "pnotify.h"
+#include "memoryBase.h"
 
 #include <vector>
 
@@ -31,7 +32,7 @@ class ConfigDeclaration;
  * make() method, which may return a shared instance.  Once created, these
  * objects are never destructed.
  */
-class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags {
+class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags, public MemoryBase {
 private:
   ConfigVariableCore(const std::string &name);
   ConfigVariableCore(const ConfigVariableCore &templ, const std::string &name);

+ 47 - 10
makepanda/makepackage.py

@@ -189,31 +189,65 @@ def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwa
     oscmd(cmd)
 
 
-def MakeDebugSymbolArchive(zipname, dirname):
-    outputdir = GetOutputDir()
-
+def MakeDebugSymbolZipArchive(zipname):
     import zipfile
 
-    zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
+    outputdir = GetOutputDir()
+    zip = zipfile.ZipFile(zipname + '.zip', 'w', zipfile.ZIP_DEFLATED)
 
     for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')):
-        zip.write(fn, dirname + '/bin/' + os.path.basename(fn))
+        zip.write(fn, 'bin/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')):
-        zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn))
+        zip.write(fn, 'panda3d/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')):
-        zip.write(fn, dirname + '/plugins/' + os.path.basename(fn))
+        zip.write(fn, 'plugins/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')):
-        zip.write(fn, dirname + '/python/' + os.path.basename(fn))
+        zip.write(fn, 'python/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')):
-        zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn))
+        zip.write(fn, 'python/DLLs/' + os.path.basename(fn))
 
     zip.close()
 
 
+def MakeDebugSymbolSevenZipArchive(zipname, compressor):
+    zipname += '.7z'
+    flags = ['-t7z', '-y']
+
+    if compressor == 'zlib':
+        # This will still build an LZMA2 archive by default,
+        # but will complete significantly faster.
+        flags.extend(['-mx=3'])
+
+    # Remove the old archive before proceeding.
+    if os.path.exists(zipname):
+        os.remove(zipname)
+
+    outputdir = GetOutputDir()
+
+    # We'll be creating the archive inside the output
+    # directory, so we need the relative path to the archive
+    zipname = os.path.relpath(zipname, outputdir)
+
+    # Create a 7-zip archive, including all *.pdb files
+    # that are not in the tmp folder
+    cmd = [GetSevenZip(), 'a']
+    cmd.extend(flags)
+    cmd.extend(['-ir!*.pdb', '-x!' + os.path.join('tmp', '*'), zipname])
+
+    subprocess.call(cmd, stdout=subprocess.DEVNULL, cwd=outputdir)
+
+
+def MakeDebugSymbolArchive(zipname, compressor):
+    if HasSevenZip():
+        MakeDebugSymbolSevenZipArchive(zipname, compressor)
+    else:
+        MakeDebugSymbolZipArchive(zipname)
+
+
 def MakeInstallerLinux(version, debversion=None, rpmversion=None, rpmrelease=1,
                        python_versions=[], **kwargs):
     outputdir = GetOutputDir()
@@ -969,6 +1003,7 @@ def MakeInstallerAndroid(version, **kwargs):
 
 def MakeInstaller(version, **kwargs):
     target = GetTarget()
+
     if target == 'windows':
         dir = kwargs.pop('installdir', None)
         if dir is None:
@@ -991,8 +1026,10 @@ def MakeInstaller(version, **kwargs):
         if GetTargetArch() == 'x64':
             fn += '-x64'
 
+        compressor = kwargs.get('compressor')
+
         MakeInstallerNSIS(version, fn + '.exe', title, dir, **kwargs)
-        MakeDebugSymbolArchive(fn + '-pdb.zip', dir)
+        MakeDebugSymbolArchive(fn + '-pdb', compressor)
     elif target == 'linux':
         MakeInstallerLinux(version, **kwargs)
     elif target == 'darwin':

+ 37 - 10
makepanda/makepanda.py

@@ -860,7 +860,6 @@ if (COMPILER=="GCC"):
     SmartPkgEnable("OPENAL",    "openal",    ("openal"), "AL/al.h", framework = "OpenAL")
     SmartPkgEnable("SQUISH",    "",          ("squish"), "squish.h")
     SmartPkgEnable("TIFF",      "libtiff-4", ("tiff"), "tiff.h")
-    SmartPkgEnable("OPENEXR",   "OpenEXR",   ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h"))
     SmartPkgEnable("VRPN",      "",          ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h"))
     SmartPkgEnable("OPUS",      "opusfile",  ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus"))
     SmartPkgEnable("JPEG",      "",          ("jpeg"), "jpeglib.h")
@@ -914,6 +913,23 @@ if (COMPILER=="GCC"):
                 for ffmpeg_lib in ffmpeg_libs:
                     LibName("FFMPEG", "-Wl,--exclude-libs,%s.a" % (ffmpeg_lib))
 
+    if not PkgSkip("OPENEXR"):
+        # OpenEXR libraries have different names depending on the version.
+        openexr_libdir = os.path.join(GetThirdpartyDir(), "openexr", "lib")
+        openexr_incs = ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h")
+        if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_1.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_1", "IlmThread-3_1", "Imath-3_1", "Iex-3_1"), openexr_incs)
+        if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_0.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_0", "IlmThread-3_0", "Imath-3_0", "Iex-3_0"), openexr_incs)
+        elif os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs)
+        elif os.path.isfile(os.path.join(openexr_libdir, "libIlmImf.a")):
+            SmartPkgEnable("OPENEXR", "", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), openexr_incs)
+        else:
+            # Find it in the system, preferably using pkg-config, otherwise
+            # using the OpenEXR 3 naming scheme.
+            SmartPkgEnable("OPENEXR", "OpenEXR", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs)
+
     if GetTarget() not in ("darwin", "emscripten"):
         for fcollada_lib in fcollada_libs:
             LibName("FCOLLADA", "-Wl,--exclude-libs,lib%s.a" % (fcollada_lib))
@@ -945,6 +961,9 @@ if (COMPILER=="GCC"):
             LibName("OPENEXR", "-Wl,--exclude-libs,libIlmImfUtil.a")
             LibName("OPENEXR", "-Wl,--exclude-libs,libIlmThread.a")
             LibName("OPENEXR", "-Wl,--exclude-libs,libImath.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXR.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRCore.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRUtil.a")
 
         if not PkgSkip("VORBIS"):
             LibName("VORBIS", "-Wl,--exclude-libs,libogg.a")
@@ -1072,8 +1091,6 @@ if (COMPILER=="GCC"):
             LibName("FFMPEG", "-undefined dynamic_lookup")
         if not PkgSkip("ASSIMP"):
             LibName("ASSIMP", "-undefined dynamic_lookup")
-        if not PkgSkip("OPENEXR"):
-            LibName("OPENEXR", "-undefined dynamic_lookup")
         if not PkgSkip("VRPN"):
             LibName("VRPN", "-undefined dynamic_lookup")
 
@@ -1214,7 +1231,7 @@ def CompileCxx(obj,src,opts):
             cmd = "cl "
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
-            cmd += "/wd4996 /wd4275 /wd4273 "
+            cmd += "/wd4996 "
 
             # Set the minimum version to Windows Vista.
             cmd += "/DWINVER=0x600 "
@@ -1246,7 +1263,7 @@ def CompileCxx(obj,src,opts):
             if (building):
                 cmd += " /DBUILDING_" + building
 
-            if ("BIGOBJ" in opts) or GetTargetArch() == 'x64':
+            if ("BIGOBJ" in opts) or GetTargetArch() == 'x64' or not PkgSkip("EIGEN"):
                 cmd += " /bigobj"
 
             cmd += " /Zm300"
@@ -1264,7 +1281,7 @@ def CompileCxx(obj,src,opts):
             cmd = "icl "
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
-            cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 "
+            cmd += "/wd4996 /wd4267 /wd4101 "
             cmd += "/DWINVER=0x600 "
             cmd += "/Fo" + obj + " /c"
             for x in ipath: cmd += " /I" + x
@@ -1377,6 +1394,10 @@ def CompileCxx(obj,src,opts):
                 if 'NOARCH:' + arch.upper() not in opts:
                     cmd += " -arch %s" % arch
 
+        elif 'clang' not in GetCXX().split('/')[-1]:
+            # Enable interprocedural optimizations in GCC.
+            cmd += " -fno-semantic-interposition"
+
         if "SYSROOT" in SDK:
             if GetTarget() != "android":
                 cmd += ' --sysroot=%s' % (SDK["SYSROOT"])
@@ -1477,10 +1498,7 @@ def CompileCxx(obj,src,opts):
         if (optlevel==4): cmd += " -O3 -DNDEBUG"
 
         # Enable more warnings.
-        cmd += " -Wall -Wno-unused-function"
-
-        if not src.endswith(".c"):
-            cmd += " -Wno-reorder"
+        cmd += " -Wall -Wno-unused-function -Werror=return-type"
 
         # Ignore unused variables in NDEBUG builds, often used in asserts.
         if optlevel == 4:
@@ -6137,17 +6155,21 @@ for VER in MAYAVERSIONS:
     TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS)
 
 if MAYA_BUILT:
+    OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', 'NOARCH:ARM64']
+
     TargetAdd('mayaprogs_mayaConversionClient.obj', opts=OPTS, input='mayaConversionClient.cxx')
 
     TargetAdd('maya2egg_mayaToEggClient.obj', opts=OPTS, input='mayaToEggClient.cxx')
     TargetAdd('maya2egg_client.exe', input='mayaprogs_mayaConversionClient.obj')
     TargetAdd('maya2egg_client.exe', input='maya2egg_mayaToEggClient.obj')
     TargetAdd('maya2egg_client.exe', input=COMMON_EGG2X_LIBS)
+    TargetAdd('maya2egg_client.exe', opts=['NOARCH:ARM64'])
 
     TargetAdd('egg2maya_eggToMayaClient.obj', opts=OPTS, input='eggToMayaClient.cxx')
     TargetAdd('egg2maya_client.exe', input='mayaprogs_mayaConversionClient.obj')
     TargetAdd('egg2maya_client.exe', input='egg2maya_eggToMayaClient.obj')
     TargetAdd('egg2maya_client.exe', input=COMMON_EGG2X_LIBS)
+    TargetAdd('egg2maya_client.exe', opts=['NOARCH:ARM64'])
 
 #
 # DIRECTORY: contrib/src/ai/
@@ -6228,6 +6250,9 @@ if PkgSkip("PYTHON") == 0:
         PyTargetAdd('deploy-stubw.exe', input='deploy-stubw.obj')
         PyTargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'DEPLOYSTUB', 'NOICON'])
     elif GetTarget() == 'android':
+        TargetAdd('org/jnius/NativeInvocationHandler.class', opts=OPTS, input='NativeInvocationHandler.java')
+        TargetAdd('classes.dex', input='org/jnius/NativeInvocationHandler.class')
+
         PyTargetAdd('deploy-stubw_android_main.obj', opts=OPTS, input='android_main.cxx')
         PyTargetAdd('deploy-stubw_android_log.obj', opts=OPTS, input='android_log.c')
         PyTargetAdd('libdeploy-stubw.dll', input='android_native_app_glue.obj')
@@ -6356,6 +6381,8 @@ def ParallelMake(tasklist):
             tasklist = extras
         sys.stdout.flush()
         if tasksqueued == 0:
+            if len(tasklist) > 0:
+                continue
             break
         donetask = donequeue.get()
         if donetask == 0:

+ 33 - 5
makepanda/makepandacore.py

@@ -588,6 +588,26 @@ def GetFlexVersion():
         Warn("Unable to detect flex version")
         return (0, 0, 0)
 
+SEVENZIP = None
+def GetSevenZip():
+    global SEVENZIP
+    if SEVENZIP is not None:
+        return SEVENZIP
+
+    win_util = os.path.join(GetThirdpartyBase(), 'win-util')
+    if GetHost() == 'windows' and os.path.isdir(win_util):
+        SEVENZIP = GetThirdpartyBase() + "/win-util/7za.exe"
+    elif LocateBinary('7z'):
+        SEVENZIP = '7z'
+    else:
+        # We don't strictly need it, so don't give an error
+        return None
+
+    return SEVENZIP
+
+def HasSevenZip():
+    return GetSevenZip() is not None
+
 ########################################################################
 ##
 ## LocateBinary
@@ -873,8 +893,11 @@ def JavaGetImports(path):
     imports = []
     try:
         for match in JavaImportRegex.finditer(source, 0):
-            impname = match.group(1)
-            imports.append(impname.strip())
+            impname = match.group(1).strip()
+            if not impname.startswith('java.') and \
+               not impname.startswith('dalvik.') and \
+               not impname.startswith('android.'):
+                imports.append(impname.strip())
     except:
         print("Failed to determine dependencies of \"" + path  +"\".")
         raise
@@ -3375,10 +3398,15 @@ def SetOrigExt(x, v):
     ORIG_EXT[x] = v
 
 def GetExtensionSuffix():
-    if GetTarget() == 'emscripten':
+    target = GetTarget()
+    if target == 'windows':
+        if GetTargetArch() == 'x64':
+            return '.cp%d%d-win_amd64.pyd' % (sys.version_info[:2])
+        else:
+            return '.cp%d%d-win32.pyd' % (sys.version_info[:2])
+    elif target == 'emscripten':
         return '.so'
-
-    if CrossCompiling():
+    elif CrossCompiling():
         return '.{0}.so'.format(GetPythonABI())
     else:
         import _imp

+ 8 - 2
panda/src/android/PythonActivity.java

@@ -16,8 +16,14 @@ package org.panda3d.android;
 import org.panda3d.android.PandaActivity;
 
 /**
- * This is only declared as a separate class from PandaActivity so that we
- * can have two separate activity definitions in ApplicationManifest.xml.
+ * Extends PandaActivity with some things that are useful in a Python
+ * application.
  */
 public class PythonActivity extends PandaActivity {
+    // This is required by plyer.
+    public static PythonActivity mActivity;
+
+    public PythonActivity() {
+        mActivity = this;
+    }
 }

+ 2 - 3
panda/src/audio/audioLoadRequest.I

@@ -59,7 +59,7 @@ get_positional() const {
  */
 INLINE bool AudioLoadRequest::
 is_ready() const {
-  return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+  return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
 }
 
 /**
@@ -70,6 +70,5 @@ is_ready() const {
  */
 INLINE AudioSound *AudioLoadRequest::
 get_sound() const {
-  nassertr_always(done(), nullptr);
-  return (AudioSound *)_result;
+  return (AudioSound *)get_result();
 }

+ 5 - 0
panda/src/audiotraits/CMakeLists.txt

@@ -60,6 +60,11 @@ if(HAVE_OPENAL)
   set_target_properties(p3openal_audio PROPERTIES DEFINE_SYMBOL BUILDING_OPENAL_AUDIO)
   target_link_libraries(p3openal_audio panda PKG::OPENAL)
 
+  if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+    # When statically linking OpenAL, keep its symbols private to this module.
+    target_link_options(p3openal_audio PRIVATE "LINKER:--exclude-libs,libopenal.a")
+  endif()
+
   install(TARGETS p3openal_audio
     EXPORT OpenAL COMPONENT OpenAL
     DESTINATION ${CMAKE_INSTALL_LIBDIR}

+ 3 - 2
panda/src/bullet/bulletAllHitsRayResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletRayHit {
+struct EXPCL_PANDABULLET BulletRayHit : public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletRayHit empty();
@@ -61,7 +62,7 @@ private:
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback {
+struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletAllHitsRayResult empty();

+ 2 - 1
panda/src/bullet/bulletClosestHitRayResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback {
+struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletClosestHitRayResult empty();

+ 2 - 1
panda/src/bullet/bulletClosestHitSweepResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback {
+struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletClosestHitSweepResult empty();

+ 2 - 2
panda/src/bullet/bulletConvexPointCloudShape.I

@@ -16,8 +16,8 @@
  */
 INLINE BulletConvexPointCloudShape::
 BulletConvexPointCloudShape() :
-  _scale(1),
-  _shape(nullptr) {
+  _shape(nullptr),
+  _scale(1) {
 }
 
 /**

+ 2 - 2
panda/src/bullet/bulletTriangleMeshShape.cxx

@@ -29,9 +29,9 @@ TypeHandle BulletTriangleMeshShape::_type_handle;
  */
 BulletTriangleMeshShape::
 BulletTriangleMeshShape() :
-  _mesh(nullptr),
-  _gimpact_shape(nullptr),
   _bvh_shape(nullptr),
+  _gimpact_shape(nullptr),
+  _mesh(nullptr),
   _dynamic(false),
   _compress(false),
   _bvh(false) {

+ 2 - 2
panda/src/chan/animControl.cxx

@@ -32,8 +32,8 @@ AnimControl(const std::string &name, PartBundle *part,
   Namable(name),
   _pending_lock(name),
   _pending_cvar(_pending_lock),
-  _bound_joints(BitArray::all_on()),
-  _part(part)
+  _part(part),
+  _bound_joints(BitArray::all_on())
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, get_class_type());

+ 7 - 7
panda/src/char/character.cxx

@@ -38,16 +38,16 @@ PStatCollector Character::_animation_pcollector("*:Animation");
 Character::
 Character(const Character &copy, bool copy_bundles) :
   PartBundleNode(copy),
+  _last_auto_update(-1.0),
+  _view_frame(-1),
+  _view_distance2(0.0f),
   _lod_center(copy._lod_center),
   _lod_far_distance(copy._lod_far_distance),
   _lod_near_distance(copy._lod_near_distance),
   _lod_delay_factor(copy._lod_delay_factor),
   _do_lod_animation(copy._do_lod_animation),
   _joints_pcollector(copy._joints_pcollector),
-  _skinning_pcollector(copy._skinning_pcollector),
-  _last_auto_update(-1.0),
-  _view_frame(-1),
-  _view_distance2(0.0f)
+  _skinning_pcollector(copy._skinning_pcollector)
 {
   set_cull_callback();
 
@@ -75,11 +75,11 @@ Character(const Character &copy, bool copy_bundles) :
 Character::
 Character(const std::string &name) :
   PartBundleNode(name, new CharacterJointBundle(name)),
-  _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"),
-  _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices"),
   _last_auto_update(-1.0),
   _view_frame(-1),
-  _view_distance2(0.0f)
+  _view_distance2(0.0f),
+  _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"),
+  _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices")
 {
   set_cull_callback();
   clear_lod_animation();

+ 4 - 36
panda/src/collide/collisionBox.I

@@ -17,11 +17,10 @@
  */
 INLINE CollisionBox::
 CollisionBox(const LPoint3 &center, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) :
-  _center(center), _x(x), _y(y), _z(z)
+  _center(center)
 {
-  _min = LPoint3(_center.get_x() - _x, _center.get_y() - _y, _center.get_z() - _z);
-  _max = LPoint3(_center.get_x() + _x, _center.get_y() + _y, _center.get_z() + _z);
-  _radius = sqrt(_x*_x + _y*_y + _z*_z);
+  _min = LPoint3(_center.get_x() - x, _center.get_y() - y, _center.get_z() - z);
+  _max = LPoint3(_center.get_x() + x, _center.get_y() + y, _center.get_z() + z);
   for(int v = 0; v < 8; v++)
     _vertex[v] = get_point_aabb(v);
   for(int p = 0; p < 6; p++)
@@ -37,10 +36,6 @@ CollisionBox(const LPoint3 &min, const LPoint3 &max) :
   _min(min), _max(max)
 {
   _center = (_min + _max) / 2;
-  _x = _center.get_x() - _min.get_x();
-  _y = _center.get_y() - _min.get_y();
-  _z = _center.get_z() - _min.get_z();
-  _radius = sqrt(_x*_x + _y*_y + _z*_z);
   for(int v = 0; v < 8; v++)
     _vertex[v] = get_point_aabb(v);
   for(int p = 0; p < 6; p++)
@@ -63,11 +58,7 @@ CollisionBox(const CollisionBox &copy) :
   CollisionSolid(copy),
   _center(copy._center),
   _min(copy._min),
-  _max(copy._max),
-  _x(copy._x ),
-  _y(copy._y ),
-  _z(copy._z ),
-  _radius(copy._radius )
+  _max(copy._max)
 {
   for(int v = 0; v < 8; v++)
     _vertex[v] = copy._vertex[v];
@@ -152,7 +143,6 @@ get_point(int n) const {
   return _vertex[n];
 }
 
-
 /**
  * Returns the nth vertex of the Axis Aligned Bounding Box.
  */
@@ -243,20 +233,6 @@ calc_to_3d_mat(LMatrix4 &to_3d_mat,int plane) const {
   to_3d_mat.set_row(3, get_plane(plane).get_point());
 }
 
-/**
- * Fills the indicated matrix with the appropriate rotation transform to move
- * points from the 2-d plane into the 3-d (X, 0, Z) plane.
- *
- * This is essentially similar to calc_to_3d_mat, except that the matrix is
- * rederived from whatever is stored in _to_2d_mat, guaranteeing that it will
- * match whatever algorithm produced that one, even if it was produced on a
- * different machine with different numerical precision.
- */
-INLINE void CollisionBox::
-rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const {
-  to_3d_mat.invert_from(_to_2d_mat[plane]);
-}
-
 /**
  * Extrude the indicated point in the polygon's 2-d definition space back into
  * 3-d coordinates.
@@ -295,11 +271,3 @@ operator = (const CollisionBox::PointDef &copy) {
   _p = copy._p;
   _v = copy._v;
 }
-
-/**
- * returns the points that form the nth plane
- */
-INLINE CollisionBox::Points CollisionBox::
-get_plane_points(int n) {
-  return _points[n];
-}

+ 54 - 81
panda/src/collide/collisionBox.cxx

@@ -66,14 +66,11 @@ make_copy() {
  * Compute parameters for each of the box's sides
  */
 void CollisionBox::
-setup_box(){
-  for(int plane = 0; plane < 6; plane++) {
-    LPoint3 array[4];
-    array[0] = get_point(plane_def[plane][0]);
-    array[1] = get_point(plane_def[plane][1]);
-    array[2] = get_point(plane_def[plane][2]);
-    array[3] = get_point(plane_def[plane][3]);
-    setup_points(array, array+4, plane);
+setup_box() {
+  assert(sizeof(_points) / sizeof(_points[0]) == 6);
+  assert(sizeof(_points[0]) / sizeof(_points[0][0]) == 4);
+  for (int plane = 0; plane < 6; plane++) {
+    setup_points(plane);
   }
 }
 
@@ -81,11 +78,8 @@ setup_box(){
  * Computes the plane and 2d projection of points that make up this side.
  */
 void CollisionBox::
-setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
-  int num_points = end - begin;
-  nassertv(num_points >= 3);
-
-  _points[plane].clear();
+setup_points(int plane) {
+  PointDef *points = _points[plane];
 
   // Construct a matrix that rotates the points from the (X,0,Z) plane into
   // the 3-d plane.
@@ -96,32 +90,15 @@ setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
   _to_2d_mat[plane].invert_from(to_3d_mat);
 
   // Now project all of the points onto the 2-d plane.
-
-  const LPoint3 *pi;
-  for (pi = begin; pi != end; ++pi) {
-    LPoint3 point = (*pi) * _to_2d_mat[plane];
-    _points[plane].push_back(PointDef(point[0], point[2]));
+  for (size_t i = 0; i < 4; ++i) {
+    LPoint3 point = get_point(plane_def[plane][i]) * _to_2d_mat[plane];
+    points[i] = PointDef(point[0], point[2]);
   }
 
-  nassertv(_points[plane].size() >= 3);
-
-#ifndef NDEBUG
-  /*
-  // Now make sure the points define a convex polygon.
-  if (is_concave()) {
-  collide_cat.error() << "Invalid concave CollisionPolygon defined:\n";
-  const LPoint3 *pi;
-  for (pi = begin; pi != end; ++pi) {
-  collide_cat.error(false) << "  " << (*pi) << "\n";
-  }
-  collide_cat.error(false)
-  << "  normal " << normal << " with length " << normal.length() << "\n";
-  _points.clear();
-  }
-  */
-#endif
-
-  compute_vectors(_points[plane]);
+  for (size_t i = 0; i < 4; i++) {
+    points[i]._v = points[(i + 1) % 4]._p - points[i]._p;
+    points[i]._v.normalize();
+  }
 }
 
 /**
@@ -146,10 +123,6 @@ xform(const LMatrix4 &mat) {
   for(int p = 0; p < 6 ; p++) {
     _planes[p] = set_plane(p);
   }
-  _x = _vertex[0].get_x() - _center.get_x();
-  _y = _vertex[0].get_y() - _center.get_y();
-  _z = _vertex[0].get_z() - _center.get_z();
-  _radius = sqrt(_x * _x + _y * _y + _z * _z);
   setup_box();
   mark_viz_stale();
   mark_internal_bounds_stale();
@@ -196,7 +169,11 @@ output(std::ostream &out) const {
  */
 PT(BoundingVolume) CollisionBox::
 compute_internal_bounds() const {
-  return new BoundingSphere(_center, _radius);
+  PN_stdfloat x = _vertex[0].get_x() - _center.get_x();
+  PN_stdfloat y = _vertex[0].get_y() - _center.get_y();
+  PN_stdfloat z = _vertex[0].get_z() - _center.get_z();
+  PN_stdfloat radius = sqrt(x * x + y * y + z * z);
+  return new BoundingSphere(_center, radius);
 }
 
 /**
@@ -233,10 +210,8 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   LVector3 normal;
 
   for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) {
-    plane = get_plane( ip );
-    if (_points[ip].size() < 3) {
-      continue;
-    }
+    plane = get_plane(ip);
+
     if (wrt_prev_space != wrt_space) {
       // If we have a delta between the previous position and the current
       // position, we use that to determine some more properties of the
@@ -322,17 +297,17 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
       Points new_points;
       if (apply_clip_plane(new_points, cpa, entry.get_into_node_path().get_net_transform(),ip)) {
         // All points are behind the clip plane; just do the default test.
-        edge_dist = dist_to_polygon(p, _points[ip]);
+        edge_dist = dist_to_polygon(p, _points[ip], 4);
       } else if (new_points.empty()) {
         // The polygon is completely clipped.
         continue;
       } else {
         // Test against the clipped polygon.
-        edge_dist = dist_to_polygon(p, new_points);
+        edge_dist = dist_to_polygon(p, new_points.data(), new_points.size());
       }
     } else {
       // No clip plane is in effect.  Do the default test.
-      edge_dist = dist_to_polygon(p, _points[ip]);
+      edge_dist = dist_to_polygon(p, _points[ip], 4);
     }
 
     max_dist = from_radius;
@@ -506,7 +481,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const {
   LParabola local_p(parabola->get_parabola());
   local_p.xform(wrt_mat);
 
-  PN_stdfloat t = INT_MAX;
+  PN_stdfloat t = FLT_MAX;
   PN_stdfloat t1, t2;
   int intersecting_face = -1;
   for (int i = 0; i < get_num_planes(); i++) {
@@ -1129,13 +1104,13 @@ apply_clip_plane(CollisionBox::Points &new_points,
       LPlane plane = plane_node->get_plane() * new_transform->get_mat();
       if (first_plane) {
         first_plane = false;
-        if (!clip_polygon(new_points, _points[plane_no], plane, plane_no)) {
+        if (!clip_polygon(new_points, _points[plane_no], 4, plane, plane_no)) {
           all_in = false;
         }
       } else {
         Points last_points;
         last_points.swap(new_points);
-        if (!clip_polygon(new_points, last_points, plane, plane_no)) {
+        if (!clip_polygon(new_points, last_points.data(), last_points.size(), plane, plane_no)) {
           all_in = false;
         }
       }
@@ -1158,10 +1133,10 @@ apply_clip_plane(CollisionBox::Points &new_points,
  */
 bool CollisionBox::
 clip_polygon(CollisionBox::Points &new_points,
-             const CollisionBox::Points &source_points,
+             const PointDef *source_points, size_t num_source_points,
              const LPlane &plane, int plane_no) const {
   new_points.clear();
-  if (source_points.empty()) {
+  if (num_source_points == 0) {
     return true;
   }
 
@@ -1173,7 +1148,7 @@ clip_polygon(CollisionBox::Points &new_points,
     if (plane.dist_to_plane(get_plane(plane_no).get_point()) < 0.0) {
       // A point within the polygon is behind the clipping plane: the polygon
       // is all in.
-      new_points = source_points;
+      new_points.insert(new_points.end(), source_points, source_points + num_source_points);
       return true;
     }
     return false;
@@ -1194,14 +1169,13 @@ clip_polygon(CollisionBox::Points &new_points,
   // We might increase the number of vertices by as many as 1, if the plane
   // clips off exactly one corner.  (We might also decrease the number of
   // vertices, or keep them the same number.)
-  new_points.reserve(source_points.size() + 1);
+  new_points.reserve(num_source_points + 1);
 
-  LPoint2 last_point = source_points.back()._p;
+  LPoint2 last_point = source_points[num_source_points - 1]._p;
   bool last_is_in = !is_right(last_point - from2d, delta2d);
   bool all_in = last_is_in;
-  Points::const_iterator pi;
-  for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
-    const LPoint2 &this_point = (*pi)._p;
+  for (size_t pi = 0; pi < num_source_points; ++pi) {
+    const LPoint2 &this_point = source_points[pi]._p;
     bool this_is_in = !is_right(this_point - from2d, delta2d);
 
     // There appears to be a compiler bug in gcc 4.0: we need to extract this
@@ -1234,15 +1208,13 @@ clip_polygon(CollisionBox::Points &new_points,
   return all_in;
 }
 
-
 /**
  * Returns the linear distance from the 2-d point to the nearest part of the
  * polygon defined by the points vector.  The result is negative if the point
  * is within the polygon.
  */
 PN_stdfloat CollisionBox::
-dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
-
+dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const {
   // We know that that the polygon is convex and is defined with the points in
   // counterclockwise order.  Therefore, we simply compare the signed distance
   // to each line segment; we ignore any negative values, and take the minimum
@@ -1254,10 +1226,9 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
   bool got_dist = false;
   PN_stdfloat best_dist = -1.0f;
 
-  size_t num_points = points.size();
   for (size_t i = 0; i < num_points - 1; ++i) {
     PN_stdfloat d = dist_to_line_segment(p, points[i]._p, points[i + 1]._p,
-                                   points[i]._v);
+                                         points[i]._v);
     if (d >= 0.0f) {
       if (!got_dist || d < best_dist) {
         best_dist = d;
@@ -1267,7 +1238,7 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
   }
 
   PN_stdfloat d = dist_to_line_segment(p, points[num_points - 1]._p, points[0]._p,
-                                 points[num_points - 1]._v);
+                                       points[num_points - 1]._v);
   if (d >= 0.0f) {
     if (!got_dist || d < best_dist) {
       best_dist = d;
@@ -1450,10 +1421,14 @@ write_datagram(BamWriter *manager, Datagram &me) {
   for(int i=0; i < 8; i++) {
     _vertex[i].write_datagram(me);
   }
-  me.add_stdfloat(_radius);
-  me.add_stdfloat(_x);
-  me.add_stdfloat(_y);
-  me.add_stdfloat(_z);
+  PN_stdfloat x = _vertex[0].get_x() - _center.get_x();
+  PN_stdfloat y = _vertex[0].get_y() - _center.get_y();
+  PN_stdfloat z = _vertex[0].get_z() - _center.get_z();
+  PN_stdfloat radius = sqrt(x * x + y * y + z * z);
+  me.add_stdfloat(radius);
+  me.add_stdfloat(x);
+  me.add_stdfloat(y);
+  me.add_stdfloat(z);
   for(int i=0; i < 6; i++) {
     _planes[i].write_datagram(me);
   }
@@ -1461,8 +1436,8 @@ write_datagram(BamWriter *manager, Datagram &me) {
     _to_2d_mat[i].write_datagram(me);
   }
   for(int i=0; i < 6; i++) {
-    me.add_uint16(_points[i].size());
-    for (size_t j = 0; j < _points[i].size(); j++) {
+    me.add_uint16(4);
+    for (size_t j = 0; j < 4; j++) {
       _points[i][j]._p.write_datagram(me);
       _points[i][j]._v.write_datagram(me);
     }
@@ -1497,10 +1472,10 @@ fillin(DatagramIterator& scan, BamReader* manager) {
   for(int i=0; i < 8; i++) {
     _vertex[i].read_datagram(scan);
   }
-  _radius = scan.get_stdfloat();
-  _x = scan.get_stdfloat();
-  _y = scan.get_stdfloat();
-  _z = scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
   for(int i=0; i < 6; i++) {
     _planes[i].read_datagram(scan);
   }
@@ -1509,12 +1484,10 @@ fillin(DatagramIterator& scan, BamReader* manager) {
   }
   for(int i=0; i < 6; i++) {
     size_t size = scan.get_uint16();
+    nassertv(size == 4);
     for (size_t j = 0; j < size; j++) {
-      LPoint2 p;
-      LVector2 v;
-      p.read_datagram(scan);
-      v.read_datagram(scan);
-      _points[i].push_back(PointDef(p, v));
+      _points[i][j]._p.read_datagram(scan);
+      _points[i][j]._v.read_datagram(scan);
     }
   }
 }

+ 7 - 10
panda/src/collide/collisionBox.h

@@ -99,7 +99,6 @@ private:
   LPoint3 _center;
   LPoint3 _min;
   LPoint3 _max;
-  PN_stdfloat _x, _y, _z, _radius;
   LPoint3 _vertex[8]; // Each of the Eight Vertices of the Box
   LPlane _planes[6]; //Points to each of the six sides of the Box
 
@@ -119,6 +118,7 @@ private:
 public:
   class PointDef {
   public:
+    PointDef() = default;
     INLINE PointDef(const LPoint2 &p, const LVector2 &v);
     INLINE PointDef(PN_stdfloat x, PN_stdfloat y);
     INLINE PointDef(const PointDef &copy);
@@ -134,25 +134,22 @@ public:
                     const Points &points) const;
 
   bool point_is_inside(const LPoint2 &p, const Points &points) const;
-  PN_stdfloat dist_to_polygon(const LPoint2 &p, const Points &points) const;
+  PN_stdfloat dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const;
 
-  void setup_points(const LPoint3 *begin, const LPoint3 *end, int plane);
+  void setup_points(int plane);
   INLINE LPoint2 to_2d(const LVecBase3 &point3d, int plane) const;
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
-  INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
-  bool clip_polygon(Points &new_points, const Points &source_points,
-                    const LPlane &plane,int plane_no) const;
+  bool clip_polygon(Points &new_points, const PointDef *source_points,
+                    size_t num_source_points, const LPlane &plane,
+                    int plane_no) const;
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,
                         const TransformState *net_transform, int plane_no) const;
 
 private:
-  Points _points[6]; // one set of points for each of the six planes that make up the box
+  PointDef _points[6][4]; // one set of points for each of the six planes that make up the box
   LMatrix4 _to_2d_mat[6];
 
-public:
-  INLINE Points get_plane_points( int n );
-
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);

+ 0 - 9
panda/src/collide/collisionHandlerEvent.I

@@ -25,15 +25,6 @@ operator () (const PT(CollisionEntry) &a,
   return a->get_into_node_path() < b->get_into_node_path();
 }
 
-/**
- * The assignment operator does absolutely nothing, since this is just a
- * function object class that stores no data.  We define it just to quiet up
- * g++ in -Wall mode.
- */
-INLINE void CollisionHandlerEvent::SortEntries::
-operator = (const CollisionHandlerEvent::SortEntries &) {
-}
-
 /**
  * Removes all of the previously-added in patterns.  See add_in_pattern.
  */

+ 0 - 1
panda/src/collide/collisionHandlerEvent.h

@@ -90,7 +90,6 @@ protected:
     INLINE bool
     operator () (const PT(CollisionEntry) &a,
                  const PT(CollisionEntry) &b) const;
-    INLINE void operator = (const SortEntries &other);
   };
 
   typedef pset<PT(CollisionEntry), SortEntries> Colliding;

+ 0 - 1
panda/src/collide/collisionHandlerPhysical_ext.cxx

@@ -21,7 +21,6 @@
  */
 PyObject *Extension<CollisionHandlerPhysical>::
 __reduce__(PyObject *self) const {
-  extern struct Dtool_PyTypedObject Dtool_Datagram;
   extern struct Dtool_PyTypedObject Dtool_NodePath;
 
   // Create a tuple with all the NodePath pointers.

+ 7 - 7
panda/src/collide/collisionLevelStateBase.I

@@ -31,8 +31,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child)
   _node_path(parent._node_path, child),
   _colliders(parent._colliders),
   _include_mask(parent._include_mask),
-  _local_bounds(parent._local_bounds),
-  _node_gbv(child->get_bounds()->as_geometric_bounding_volume())
+  _node_gbv(child->get_bounds()->as_geometric_bounding_volume()),
+  _local_bounds(parent._local_bounds)
 {
 }
 
@@ -44,8 +44,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, const PandaNode::
   _node_path(parent._node_path, child.get_child()),
   _colliders(parent._colliders),
   _include_mask(parent._include_mask),
-  _local_bounds(parent._local_bounds),
-  _node_gbv(child.get_bounds())
+  _node_gbv(child.get_bounds()),
+  _local_bounds(parent._local_bounds)
 {
 }
 
@@ -57,9 +57,9 @@ CollisionLevelStateBase(const CollisionLevelStateBase &copy) :
   _node_path(copy._node_path),
   _colliders(copy._colliders),
   _include_mask(copy._include_mask),
+  _node_gbv(copy._node_gbv),
   _local_bounds(copy._local_bounds),
-  _parent_bounds(copy._parent_bounds),
-  _node_gbv(copy._node_gbv)
+  _parent_bounds(copy._parent_bounds)
 {
 }
 
@@ -71,9 +71,9 @@ operator = (const CollisionLevelStateBase &copy) {
   _node_path = copy._node_path;
   _colliders = copy._colliders;
   _include_mask = copy._include_mask;
+  _node_gbv = copy._node_gbv;
   _local_bounds = copy._local_bounds;
   _parent_bounds = copy._parent_bounds;
-  _node_gbv = copy._node_gbv;
 }
 
 /**

+ 3 - 3
panda/src/collide/collisionTraverser.cxx

@@ -647,7 +647,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {
@@ -856,7 +856,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {
@@ -1065,7 +1065,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {

+ 0 - 1
panda/src/collide/collisionTraverser_ext.cxx

@@ -21,7 +21,6 @@
 PyObject *Extension<CollisionTraverser>::
 __getstate__() const {
   extern struct Dtool_PyTypedObject Dtool_CollisionHandler;
-  extern struct Dtool_PyTypedObject Dtool_CollisionTraverser;
   extern struct Dtool_PyTypedObject Dtool_NodePath;
 
   const std::string &name = _this->get_name();

+ 4 - 4
panda/src/device/evdevInputDevice.cxx

@@ -126,8 +126,9 @@ TypeHandle EvdevInputDevice::_type_handle;
 EvdevInputDevice::
 EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
   _manager(manager),
-  _index(index),
   _fd(-1),
+  _quirks(0),
+  _index(index),
   _can_write(false),
   _ff_id(-1),
   _ff_playing(false),
@@ -138,9 +139,8 @@ EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
   _dpad_left_button(-1),
   _dpad_up_button(-1),
   _ltrigger_code(-1),
-  _rtrigger_code(-1),
-  _quirks(0) {
-
+  _rtrigger_code(-1)
+{
   char path[64];
   sprintf(path, "/dev/input/event%zd", index);
 

+ 1 - 1
panda/src/device/inputDeviceManager.h

@@ -30,7 +30,7 @@ class WinRawInputDevice;
  *
  * @since 1.10.0
  */
-class EXPCL_PANDA_DEVICE InputDeviceManager {
+class EXPCL_PANDA_DEVICE InputDeviceManager : public MemoryBase {
 protected:
   InputDeviceManager();
   ~InputDeviceManager() = default;

+ 1 - 1
panda/src/device/trackerData.h

@@ -20,7 +20,7 @@
 /**
  * Stores the kinds of data that a tracker might output.
  */
-class EXPCL_PANDA_DEVICE TrackerData {
+class EXPCL_PANDA_DEVICE TrackerData : public MemoryBase {
 public:
   INLINE TrackerData();
   INLINE TrackerData(const TrackerData &copy);

+ 1 - 1
panda/src/device/xInputDevice.cxx

@@ -70,7 +70,7 @@
 
 // With MingW32 this raises the error:
 // Redefinition of '_XINPUT_BATTERY_INFORMATION'
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && _WIN32_WINNT < 0x0602
 typedef struct _XINPUT_BATTERY_INFORMATION {
   BYTE BatteryType;
   BYTE BatteryLevel;

+ 4 - 4
panda/src/display/displayRegion.cxx

@@ -731,6 +731,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
  */
 DisplayRegion::CData::
 CData() :
+  _depth_range(0, 1),
   _lens_index(0),
   _camera_node(nullptr),
   _active(true),
@@ -738,8 +739,7 @@ CData() :
   _stereo_channel(Lens::SC_mono),
   _tex_view_offset(0),
   _target_tex_page(-1),
-  _scissor_enabled(true),
-  _depth_range(0, 1)
+  _scissor_enabled(true)
 {
   _regions.push_back(Region());
 }
@@ -750,6 +750,7 @@ CData() :
 DisplayRegion::CData::
 CData(const DisplayRegion::CData &copy) :
   _regions(copy._regions),
+  _depth_range(copy._depth_range),
   _lens_index(copy._lens_index),
   _camera(copy._camera),
   _camera_node(copy._camera_node),
@@ -758,8 +759,7 @@ CData(const DisplayRegion::CData &copy) :
   _stereo_channel(copy._stereo_channel),
   _tex_view_offset(copy._tex_view_offset),
   _target_tex_page(copy._target_tex_page),
-  _scissor_enabled(copy._scissor_enabled),
-  _depth_range(copy._depth_range)
+  _scissor_enabled(copy._scissor_enabled)
 {
 }
 

+ 86 - 70
panda/src/display/graphicsEngine.cxx

@@ -69,9 +69,8 @@ PT(GraphicsEngine) GraphicsEngine::_global_ptr;
 
 PStatCollector GraphicsEngine::_wait_pcollector("Wait:Thread sync");
 PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle");
-PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General");
+//PStatCollector GraphicsEngine::_app_pcollector("App:Show code:General");
 PStatCollector GraphicsEngine::_render_frame_pcollector("App:render_frame");
-PStatCollector GraphicsEngine::_do_frame_pcollector("*:do_frame");
 PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
 PStatCollector GraphicsEngine::_cull_pcollector("Cull");
 PStatCollector GraphicsEngine::_cull_setup_pcollector("Cull:Setup");
@@ -79,15 +78,12 @@ PStatCollector GraphicsEngine::_cull_sort_pcollector("Cull:Sort");
 PStatCollector GraphicsEngine::_draw_pcollector("Draw");
 PStatCollector GraphicsEngine::_sync_pcollector("Draw:Sync");
 PStatCollector GraphicsEngine::_flip_pcollector("Wait:Flip");
-PStatCollector GraphicsEngine::_flip_begin_pcollector("Wait:Flip:Begin");
-PStatCollector GraphicsEngine::_flip_end_pcollector("Wait:Flip:End");
 PStatCollector GraphicsEngine::_transform_states_pcollector("TransformStates");
 PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformStates:Unused");
 PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates");
 PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused");
 PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers");
-PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers");
-PStatCollector GraphicsEngine::_delete_pcollector("App:Delete");
+PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("PipelineCyclers:Dirty");
 
 
 PStatCollector GraphicsEngine::_sw_sprites_pcollector("SW Sprites");
@@ -182,9 +178,9 @@ GraphicsEngine(Pipeline *pipeline) :
 GraphicsEngine::
 ~GraphicsEngine() {
 #ifdef DO_PSTATS
-  if (_app_pcollector.is_started()) {
-    _app_pcollector.stop();
-  }
+  //if (_app_pcollector.is_started()) {
+  //  _app_pcollector.stop();
+  //}
 #endif
 
   remove_all_windows();
@@ -714,9 +710,9 @@ render_frame() {
   // to be App.
 #ifdef DO_PSTATS
   _render_frame_pcollector.start();
-  if (_app_pcollector.is_started()) {
-    _app_pcollector.stop();
-  }
+  //if (_app_pcollector.is_started()) {
+  //  _app_pcollector.stop();
+  //}
 #endif
 
   // Make sure our buffers and windows are fully realized before we render a
@@ -854,6 +850,7 @@ render_frame() {
     // Reset our pcollectors that track data across the frame.
     CullTraverser::_nodes_pcollector.clear_level();
     CullTraverser::_geom_nodes_pcollector.clear_level();
+    CullTraverser::_pgui_nodes_pcollector.clear_level();
     CullTraverser::_geoms_pcollector.clear_level();
     GeomCacheManager::_geom_cache_active_pcollector.clear_level();
     GeomCacheManager::_geom_cache_record_pcollector.clear_level();
@@ -925,10 +922,14 @@ render_frame() {
     for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
       RenderThread *thread = (*ti).second;
       if (thread->_thread_state == TS_wait) {
+        // Release before notifying, otherwise the other thread will wake up
+        // and get blocked on the mutex straight away.
         thread->_thread_state = TS_do_frame;
+        thread->_cv_mutex.release();
         thread->_cv_start.notify();
+      } else {
+        thread->_cv_mutex.release();
       }
-      thread->_cv_mutex.release();
     }
 
     // Some threads may still be drawing, so indicate that we have to wait for
@@ -950,7 +951,7 @@ render_frame() {
 
   // Anything that happens outside of GraphicsEngine::render_frame() is deemed
   // to be App.
-  _app_pcollector.start();
+  //_app_pcollector.start();
   _render_frame_pcollector.stop();
 }
 
@@ -1036,8 +1037,8 @@ open_windows() {
       }
 
       thread->_thread_state = TS_do_windows;
-      thread->_cv_start.notify();
       thread->_cv_mutex.release();
+      thread->_cv_start.notify();
     }
   }
 
@@ -1154,11 +1155,11 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
     thread->_gsg = gsg;
     thread->_texture = tex;
     thread->_thread_state = TS_do_extract;
-    thread->_cv_start.notify();
     thread->_cv_mutex.release();
+    thread->_cv_start.notify();
     thread->_cv_mutex.acquire();
 
-    //XXX is this necessary, or is acquiring the mutex enough?
+    // Wait for it to finish the extraction.
     while (thread->_thread_state != TS_wait) {
       thread->_cv_done.wait();
     }
@@ -1222,11 +1223,11 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph
     thread->_state = state.p();
     thread->_work_groups = work_groups;
     thread->_thread_state = TS_do_compute;
-    thread->_cv_start.notify();
     thread->_cv_mutex.release();
+    thread->_cv_start.notify();
     thread->_cv_mutex.acquire();
 
-    //XXX is this necessary, or is acquiring the mutex enough?
+    // Wait for it to finish the compute task.
     while (thread->_thread_state != TS_wait) {
       thread->_cv_done.wait();
     }
@@ -1292,11 +1293,11 @@ do_get_screenshot(DisplayRegion *region, GraphicsStateGuardian *gsg) {
   // Now that the draw thread is idle, signal it to do the extraction task.
   thread->_region = region;
   thread->_thread_state = TS_do_screenshot;
-  thread->_cv_start.notify();
   thread->_cv_mutex.release();
+  thread->_cv_start.notify();
   thread->_cv_mutex.acquire();
 
-  //XXX is this necessary, or is acquiring the mutex enough?
+  // Wait for it to finish the extraction.
   while (thread->_thread_state != TS_wait) {
     thread->_cv_done.wait();
   }
@@ -1408,14 +1409,13 @@ cull_and_draw_together(GraphicsEngine::Windows wlist,
     GraphicsOutput *win = wlist[wi];
     if (win->is_active() && win->get_gsg()->is_active()) {
       if (win->flip_ready()) {
-        {
-          PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
-          win->begin_flip();
-        }
-        {
-          PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
-          win->end_flip();
+        if (display_cat.is_spam()) {
+          display_cat.spam()
+            << "Flipping window " << win->get_name() << "\n";
         }
+        PStatTimer timer(_flip_pcollector, current_thread);
+        win->begin_flip();
+        win->end_flip();
       }
 
       if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
@@ -1427,6 +1427,11 @@ cull_and_draw_together(GraphicsEngine::Windows wlist,
           gsg->pop_group_marker();
         }
 
+        if (display_cat.is_spam()) {
+          display_cat.spam()
+            << "Culling and drawing window " << win->get_name() << "\n";
+        }
+
         int num_display_regions = win->get_num_active_display_regions();
         for (int i = 0; i < num_display_regions; i++) {
           PT(DisplayRegion) dr = win->get_active_display_region(i);
@@ -1438,14 +1443,13 @@ cull_and_draw_together(GraphicsEngine::Windows wlist,
 
         if (_auto_flip) {
           if (win->flip_ready()) {
-            {
-              PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
-              win->begin_flip();
-            }
-            {
-              PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
-              win->end_flip();
+            if (display_cat.is_spam()) {
+              display_cat.spam()
+                << "Flipping window " << win->get_name() << "\n";
             }
+            PStatTimer timer(_flip_pcollector, current_thread);
+            win->begin_flip();
+            win->end_flip();
           }
         }
       }
@@ -1539,6 +1543,11 @@ cull_to_bins(GraphicsEngine::Windows wlist, Thread *current_thread) {
   for (size_t wi = 0; wi < wlist_size; ++wi) {
     GraphicsOutput *win = wlist[wi];
     if (win->is_active() && win->get_gsg()->is_active()) {
+      if (display_cat.is_spam()) {
+        display_cat.spam()
+          << "Culling window " << win->get_name() << "\n";
+      }
+
       GraphicsStateGuardian *gsg = win->get_gsg();
       PStatTimer timer(win->get_cull_window_pcollector(), current_thread);
       int num_display_regions = win->get_num_active_display_regions();
@@ -1683,6 +1692,8 @@ cull_to_bins(GraphicsOutput *win, GraphicsStateGuardian *gsg,
  */
 void GraphicsEngine::
 draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
+  PStatTimer timer(_draw_pcollector, current_thread);
+
   nassertv(wlist.verify_list());
 
   size_t wlist_size = wlist.size();
@@ -1694,16 +1705,14 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
 
       GraphicsOutput *host = win->get_host();
       if (host->flip_ready()) {
-        {
-          // We can't use a PStatGPUTimer before begin_frame, so when using
-          // GPU timing, it is advisable to set auto-flip to #t.
-          PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
-          host->begin_flip();
-        }
-        {
-          PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
-          host->end_flip();
+        if (display_cat.is_spam()) {
+          display_cat.spam()
+            << "Flipping window " << host->get_name()
+            << " before drawing window " << win->get_name() << "\n";
         }
+        PStatTimer timer(_flip_pcollector, current_thread);
+        host->begin_flip();
+        host->end_flip();
       }
 
       if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
@@ -1733,24 +1742,14 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
         win->end_frame(GraphicsOutput::FM_render, current_thread);
 
         if (_auto_flip) {
-#ifdef DO_PSTATS
-          // This is a good time to perform a latency query.
-          if (gsg->get_timer_queries_active()) {
-            gsg->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index());
-          }
-#endif
-
           if (win->flip_ready()) {
-            {
-              // begin_flip doesn't do anything interesting, let's not waste
-              // two timer queries on that.
-              PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
-              win->begin_flip();
-            }
-            {
-              PStatGPUTimer timer(gsg, GraphicsEngine::_flip_end_pcollector, current_thread);
-              win->end_flip();
+            if (display_cat.is_spam()) {
+              display_cat.spam()
+                << "Flipping window " << win->get_name() << "\n";
             }
+            PStatGPUTimer timer(gsg, _flip_pcollector, current_thread);
+            win->begin_flip();
+            win->end_flip();
           }
         }
 
@@ -1812,22 +1811,27 @@ flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
   size_t warray_count = 0;
   GraphicsOutput **warray = (GraphicsOutput **)alloca(warray_size);
 
+  PStatTimer timer(_flip_pcollector, current_thread);
+
   size_t i;
   for (i = 0; i < num_windows; ++i) {
     GraphicsOutput *win = wlist[i];
     if (win->flip_ready()) {
+      if (display_cat.is_spam()) {
+        display_cat.spam()
+          << "Flipping window " << win->get_name() << "\n";
+      }
+
       nassertv(warray_count < num_windows);
       warray[warray_count] = win;
       ++warray_count;
 
-      PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
       win->begin_flip();
     }
   }
 
   for (i = 0; i < warray_count; ++i) {
     GraphicsOutput *win = warray[i];
-    PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
     win->end_flip();
   }
 }
@@ -1843,7 +1847,7 @@ ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread)
   for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
     GraphicsOutput *win = (*wi);
     if (win->flip_ready()) {
-      PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
+      PStatTimer timer(_flip_pcollector, current_thread);
       win->ready_flip();
     }
   }
@@ -1937,8 +1941,8 @@ do_flip_frame(Thread *current_thread) {
       RenderThread *thread = (*ti).second;
       nassertv(thread->_thread_state == TS_wait);
       thread->_thread_state = TS_do_flip;
-      thread->_cv_start.notify();
       thread->_cv_mutex.release();
+      thread->_cv_start.notify();
     }
   }
 
@@ -2245,6 +2249,11 @@ void GraphicsEngine::
 do_resort_windows() {
   _windows_sorted = true;
 
+  if (display_cat.is_spam()) {
+    display_cat.spam()
+      << "Re-sorting window list.\n";
+  }
+
   _app.resort_windows();
   Threads::const_iterator ti;
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
@@ -2365,8 +2374,8 @@ terminate_threads(Thread *current_thread) {
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
     thread->_thread_state = TS_terminate;
-    thread->_cv_start.notify();
     thread->_cv_mutex.release();
+    thread->_cv_start.notify();
   }
 
   // Finally, wait for them all to finish cleaning up.
@@ -2569,13 +2578,20 @@ resort_windows() {
  */
 void GraphicsEngine::WindowRenderer::
 do_frame(GraphicsEngine *engine, Thread *current_thread) {
-  PStatTimer timer(engine->_do_frame_pcollector, current_thread);
   LightReMutexHolder holder(_wl_lock);
 
-  engine->cull_to_bins(_cull, current_thread);
-  engine->cull_and_draw_together(_cdraw, current_thread);
-  engine->draw_bins(_draw, current_thread);
-  engine->process_events(_window, current_thread);
+  if (!_cull.empty()) {
+    engine->cull_to_bins(_cull, current_thread);
+  }
+  if (!_cdraw.empty()) {
+    engine->cull_and_draw_together(_cdraw, current_thread);
+  }
+  if (!_draw.empty()) {
+    engine->draw_bins(_draw, current_thread);
+  }
+  if (!_window.empty()) {
+    engine->process_events(_window, current_thread);
+  }
 
   // If any GSG's on the list have no more outstanding pointers, clean them
   // up.  (We are in the draw thread for all of these GSG's.)

+ 1 - 5
panda/src/display/graphicsEngine.h

@@ -358,9 +358,8 @@ private:
 
   static PStatCollector _wait_pcollector;
   static PStatCollector _cycle_pcollector;
-  static PStatCollector _app_pcollector;
+  //static PStatCollector _app_pcollector;
   static PStatCollector _render_frame_pcollector;
-  static PStatCollector _do_frame_pcollector;
   static PStatCollector _yield_pcollector;
   static PStatCollector _cull_pcollector;
   static PStatCollector _cull_setup_pcollector;
@@ -368,15 +367,12 @@ private:
   static PStatCollector _draw_pcollector;
   static PStatCollector _sync_pcollector;
   static PStatCollector _flip_pcollector;
-  static PStatCollector _flip_begin_pcollector;
-  static PStatCollector _flip_end_pcollector;
   static PStatCollector _transform_states_pcollector;
   static PStatCollector _transform_states_unused_pcollector;
   static PStatCollector _render_states_pcollector;
   static PStatCollector _render_states_unused_pcollector;
   static PStatCollector _cyclers_pcollector;
   static PStatCollector _dirty_cyclers_pcollector;
-  static PStatCollector _delete_pcollector;
 
   static PStatCollector _sw_sprites_pcollector;
   static PStatCollector _vertex_data_small_pcollector;

+ 2 - 2
panda/src/display/graphicsOutput.cxx

@@ -75,10 +75,10 @@ GraphicsOutput(GraphicsEngine *engine, GraphicsPipe *pipe,
                GraphicsOutput *host,
                bool default_stereo_flags) :
   _lock("GraphicsOutput"),
+  _size(0, 0),
   _cull_window_pcollector(_cull_pcollector, name),
   _draw_window_pcollector(_draw_pcollector, name),
-  _clear_window_pcollector(_draw_window_pcollector, "Clear"),
-  _size(0, 0)
+  _clear_window_pcollector(_draw_window_pcollector, "Clear")
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, this);

+ 39 - 173
panda/src/display/graphicsStateGuardian.cxx

@@ -63,9 +63,9 @@
 
 using std::string;
 
-PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex");
-PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index");
-PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader");
+//PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex");
+//PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index");
+//PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader");
 PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer");
 PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer");
@@ -98,7 +98,6 @@ PStatCollector GraphicsStateGuardian::_compute_dispatch_pcollector("Draw:Compute
 PStatCollector GraphicsStateGuardian::_wait_occlusion_pcollector("Wait:Occlusion");
 PStatCollector GraphicsStateGuardian::_wait_timer_pcollector("Wait:Timer Queries");
 PStatCollector GraphicsStateGuardian::_timer_queries_pcollector("Timer queries");
-PStatCollector GraphicsStateGuardian::_command_latency_pcollector("Command latency");
 
 PStatCollector GraphicsStateGuardian::_prepare_pcollector("Draw:Prepare");
 PStatCollector GraphicsStateGuardian::_prepare_texture_pcollector("Draw:Prepare:Texture");
@@ -222,10 +221,6 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
 
 #ifdef DO_PSTATS
   _timer_queries_active = false;
-  _last_query_frame = 0;
-  _last_num_queried = 0;
-  // _timer_delta = 0.0;
-
   _pstats_gpu_thread = -1;
 #endif
 
@@ -778,9 +773,17 @@ end_occlusion_query() {
  * Adds a timer query to the command stream, associated with the given PStats
  * collector index.
  */
-PT(TimerQueryContext) GraphicsStateGuardian::
+void GraphicsStateGuardian::
 issue_timer_query(int pstats_index) {
-  return nullptr;
+}
+
+/**
+ * A latency query is a special type of timer query that measures the
+ * difference between CPU time and GPU time, ie. how far the GPU is behind in
+ * processing the commands being generated by the CPU right now.
+ */
+void GraphicsStateGuardian::
+issue_latency_query(int pstats_index) {
 }
 
 /**
@@ -2032,7 +2035,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2055,7 +2058,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2084,7 +2087,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2114,7 +2117,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2137,7 +2140,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2167,7 +2170,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2188,7 +2191,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
     {
       const TextureAttrib *texattrib;
       if (_target_rs->get_attrib(texattrib)) {
-        size_t si = 0;
+        int si = 0;
         for (int i = 0; i < texattrib->get_num_on_stages(); ++i) {
           TextureStage *stage = texattrib->get_on_stage(i);
           TextureStage::Mode mode = stage->get_mode();
@@ -2360,7 +2363,10 @@ calc_projection_mat(const Lens *lens) {
  */
 bool GraphicsStateGuardian::
 begin_frame(Thread *current_thread) {
-  _prepared_objects->begin_frame(this, current_thread);
+  {
+    PStatTimer timer(_prepare_pcollector);
+    _prepared_objects->begin_frame(this, current_thread);
+  }
 
   // We should reset the state to the default at the beginning of every frame.
   // Although this will incur additional overhead, particularly in a simple
@@ -2370,28 +2376,6 @@ begin_frame(Thread *current_thread) {
   _state_rs = RenderState::make_empty();
   _state_mask.clear();
 
-#ifdef DO_PSTATS
-  // We have to do this here instead of in GraphicsEngine because we need a
-  // current context to issue timer queries.
-  int frame = ClockObject::get_global_clock()->get_frame_count();
-  if (_last_query_frame < frame) {
-    _last_query_frame = frame;
-    _timer_queries_pcollector.clear_level();
-
-    // Now is a good time to flush previous frame's queries.  We may not
-    // actually have all of the previous frame's results in yet, but that's
-    // okay; the GPU data is allowed to lag a few frames behind.
-    flush_timer_queries();
-
-    if (_timer_queries_active) {
-      // Issue a stop and start event for collector 0, marking the beginning
-      // of the new frame.
-      issue_timer_query(0x8000);
-      issue_timer_query(0x0000);
-    }
-  }
-#endif
-
   return !_needs_reset;
 }
 
@@ -2477,133 +2461,6 @@ end_frame(Thread *current_thread) {
   _prepared_objects->_graphics_memory_lru.begin_epoch();
 }
 
-/**
- * Called by the graphics engine on the draw thread to check the status of the
- * running timer queries and submit their results to the PStats server.
- */
-void GraphicsStateGuardian::
-flush_timer_queries() {
-#ifdef DO_PSTATS
-  // This uses the lower-level PStats interfaces for now because of all the
-  // unnecessary overhead that would otherwise be incurred when adding such a
-  // large amount of data at once.
-
-  PStatClient *client = PStatClient::get_global_pstats();
-
-  if (!client->client_is_connected()) {
-    _timer_queries_active = false;
-    return;
-  }
-
-  if (!_timer_queries_active) {
-    if (pstats_gpu_timing && _supports_timer_query) {
-      // Check if timer queries should be enabled.
-      _timer_queries_active = true;
-    } else {
-      return;
-    }
-  }
-
-  // Currently, we use one thread per GSG, for convenience.  In the future, we
-  // may want to try and use one thread per graphics card.
-  if (_pstats_gpu_thread == -1) {
-    _pstats_gpu_thread = client->make_gpu_thread(get_driver_renderer()).get_index();
-  }
-  PStatThread gpu_thread(client, _pstats_gpu_thread);
-
-  // Get the results of all the timer queries.
-  int first = 0;
-  if (!_pending_timer_queries.empty()) {
-    int count = _pending_timer_queries.size();
-    if (count == 0) {
-      return;
-    }
-
-    PStatGPUTimer timer(this, _wait_timer_pcollector);
-
-    if (_last_num_queried > 0) {
-      // We know how many queries were available last frame, and this usually
-      // stays fairly constant, so use this as a starting point.
-      int i = std::min(_last_num_queried, count) - 1;
-
-      if (_pending_timer_queries[i]->is_answer_ready()) {
-        first = count;
-        while (i < count - 1) {
-          if (!_pending_timer_queries[++i]->is_answer_ready()) {
-            first = i;
-            break;
-          }
-        }
-      } else {
-        first = 0;
-        while (i > 0) {
-          if (_pending_timer_queries[--i]->is_answer_ready()) {
-            first = i + 1;
-            break;
-          }
-        }
-      }
-    } else {
-      // We figure out which tasks the GPU has already finished by doing a
-      // binary search for the first query that does not have an answer ready.
-      // We know then that everything before that must be ready.
-      while (count > 0) {
-        int step = count / 2;
-        int i = first + step;
-        if (_pending_timer_queries[i]->is_answer_ready()) {
-          first += step + 1;
-          count -= step + 1;
-        } else {
-          count = step;
-        }
-      }
-    }
-
-    if (first <= 0) {
-      return;
-    }
-
-    _last_num_queried = first;
-
-    for (int i = 0; i < first; ++i) {
-      CPT(TimerQueryContext) query = _pending_timer_queries[i];
-
-      double time_data = query->get_timestamp(); //  + _timer_delta;
-
-      if (query->_pstats_index == _command_latency_pcollector.get_index()) {
-        // Special case for the latency pcollector.
-        PStatCollectorDef *cdef;
-        cdef = client->get_collector_ptr(query->_pstats_index)->get_def(client, query->_pstats_index);
-        _pstats_gpu_data.add_level(query->_pstats_index, time_data * cdef->_factor);
-
-      } else if (query->_pstats_index & 0x8000) {
-        _pstats_gpu_data.add_stop(query->_pstats_index & 0x7fff, time_data);
-
-      } else {
-        _pstats_gpu_data.add_start(query->_pstats_index & 0x7fff, time_data);
-      }
-
-      // We found an end-frame marker (a stop event for collector 0). This
-      // means that the GPU actually caught up with that frame, and we can
-      // flush the GPU thread's frame data to the pstats server.
-      if (query->_pstats_index == 0x8000) {
-        gpu_thread.add_frame(_pstats_gpu_data);
-        _pstats_gpu_data.clear();
-      }
-    }
-  }
-
-  if (first > 0) {
-    // Do this out of the scope of _wait_timer_pcollector.
-    _pending_timer_queries.erase(
-      _pending_timer_queries.begin(),
-      _pending_timer_queries.begin() + first
-    );
-    _timer_queries_pcollector.add_level_now(first);
-  }
-#endif
-}
-
 /**
  * Returns true if this GSG can implement decals using a DepthOffsetAttrib, or
  * false if that is unreliable and the three-step rendering process should be
@@ -3223,8 +3080,9 @@ void GraphicsStateGuardian::
 init_frame_pstats() {
   if (PStatClient::is_connected()) {
     _data_transferred_pcollector.clear_level();
-    _vertex_buffer_switch_pcollector.clear_level();
-    _index_buffer_switch_pcollector.clear_level();
+    //_vertex_buffer_switch_pcollector.clear_level();
+    //_index_buffer_switch_pcollector.clear_level();
+    //_shader_buffer_switch_pcollector.clear_level();
 
     _primitive_batches_pcollector.clear_level();
     _primitive_batches_tristrip_pcollector.clear_level();
@@ -3243,8 +3101,19 @@ init_frame_pstats() {
     _texture_state_pcollector.clear_level();
   }
 }
-#endif  // DO_PSTATS
 
+/**
+ * Returns a PStatThread used to represent this GL context.
+ */
+PStatThread GraphicsStateGuardian::
+get_pstats_thread() {
+  PStatClient *client = PStatClient::get_global_pstats();
+  if (_pstats_gpu_thread == -1) {
+    _pstats_gpu_thread = client->make_gpu_thread("GPU").get_index();
+  }
+  return PStatThread(client, _pstats_gpu_thread);
+}
+#endif  // DO_PSTATS
 
 /**
  * Create a gamma table.
@@ -3464,9 +3333,6 @@ close_gsg() {
 
   // Make sure that all the contexts belonging to the GSG are deleted.
   _prepared_objects.clear();
-#ifdef DO_PSTATS
-  _pending_timer_queries.clear();
-#endif
 
   free_pointers();
 }

+ 6 - 15
panda/src/display/graphicsStateGuardian.h

@@ -44,7 +44,6 @@
 #include "bitMask.h"
 #include "texture.h"
 #include "occlusionQueryContext.h"
-#include "timerQueryContext.h"
 #include "loader.h"
 #include "shaderAttrib.h"
 #include "texGenAttrib.h"
@@ -320,7 +319,8 @@ public:
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
-  virtual PT(TimerQueryContext) issue_timer_query(int pstats_index);
+  virtual void issue_timer_query(int pstats_index);
+  virtual void issue_latency_query(int pstats_index);
 
   virtual void dispatch_compute(int size_x, int size_y, int size_z);
 
@@ -363,8 +363,6 @@ PUBLISHED:
 public:
   virtual void end_frame(Thread *current_thread);
 
-  void flush_timer_queries();
-
   void set_current_properties(const FrameBufferProperties *properties);
 
   virtual bool depth_offset_decals();
@@ -445,6 +443,7 @@ public:
 
 #ifdef DO_PSTATS
   static void init_frame_pstats();
+  PStatThread get_pstats_thread();
 #endif
 
 protected:
@@ -602,13 +601,6 @@ protected:
 #ifdef DO_PSTATS
   int _pstats_gpu_thread;
   bool _timer_queries_active;
-  PStatFrameData _pstats_gpu_data;
-
-  int _last_query_frame;
-  int _last_num_queried;
-  // double _timer_delta;
-  typedef pdeque<PT(TimerQueryContext)> TimerQueryQueue;
-  TimerQueryQueue _pending_timer_queries;
 #endif
 
   bool _copy_texture_inverted;
@@ -664,9 +656,9 @@ protected:
 
 public:
   // Statistics
-  static PStatCollector _vertex_buffer_switch_pcollector;
-  static PStatCollector _index_buffer_switch_pcollector;
-  static PStatCollector _shader_buffer_switch_pcollector;
+  //static PStatCollector _vertex_buffer_switch_pcollector;
+  //static PStatCollector _index_buffer_switch_pcollector;
+  //static PStatCollector _shader_buffer_switch_pcollector;
   static PStatCollector _load_vertex_buffer_pcollector;
   static PStatCollector _load_index_buffer_pcollector;
   static PStatCollector _load_shader_buffer_pcollector;
@@ -699,7 +691,6 @@ public:
   static PStatCollector _wait_occlusion_pcollector;
   static PStatCollector _wait_timer_pcollector;
   static PStatCollector _timer_queries_pcollector;
-  static PStatCollector _command_latency_pcollector;
 
   static PStatCollector _prepare_pcollector;
   static PStatCollector _prepare_texture_pcollector;

+ 21 - 0
panda/src/display/graphicsWindow.cxx

@@ -39,6 +39,7 @@ GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
                GraphicsOutput *host) :
   GraphicsOutput(engine, pipe, name, fb_prop, win_prop, flags, gsg, host, true),
   _input_lock("GraphicsWindow::_input_lock"),
+  _latency_pcollector(name + " latency"),
   _properties_lock("GraphicsWindow::_properties_lock")
 {
 #ifdef DO_MEMORY_USAGE
@@ -610,6 +611,26 @@ close_window() {
   _is_valid = false;
 }
 
+/**
+ * This function will be called within the draw thread after end_frame() has
+ * been called on all windows, to initiate the exchange of the front and back
+ * buffers.
+ *
+ * This should instruct the window to prepare for the flip at the next video
+ * sync, but it should not wait.
+ *
+ * We have the two separate functions, begin_flip() and end_flip(), to make it
+ * easier to flip all of the windows at the same time.
+ */
+void GraphicsWindow::
+begin_flip() {
+#ifdef DO_PSTATS
+  if (_gsg->get_timer_queries_active()) {
+    _gsg->issue_latency_query(_latency_pcollector.get_index());
+  }
+#endif
+}
+
 /**
  * Opens the window right now.  Called from the window thread.  Returns true
  * if the window is successfully opened, or false if there was a problem.

+ 4 - 0
panda/src/display/graphicsWindow.h

@@ -126,6 +126,8 @@ public:
   virtual void process_events();
   virtual void set_properties_now(WindowProperties &properties);
 
+  virtual void begin_flip();
+
 protected:
   virtual void close_window();
   virtual bool open_window();
@@ -152,6 +154,8 @@ protected:
 
   bool _got_expose_event;
 
+  PStatCollector _latency_pcollector;
+
 private:
   LightReMutex _properties_lock;
   // protects _requested_properties, _rejected_properties, and _window_event.

+ 0 - 1
panda/src/display/pStatGPUTimer.h

@@ -18,7 +18,6 @@
 #include "pStatTimer.h"
 #include "pStatCollector.h"
 #include "config_pstatclient.h"
-#include "timerQueryContext.h"
 
 class Thread;
 class GraphicsStateGuardian;

+ 3 - 3
panda/src/display/standardMunger.cxx

@@ -37,11 +37,11 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
   _num_components(num_components),
   _numeric_type(numeric_type),
   _contents(contents),
-  _munge_color(false),
-  _munge_color_scale(false),
   _auto_shader(false),
   _shader_skinning(false),
-  _remove_material(false)
+  _remove_material(false),
+  _munge_color(false),
+  _munge_color_scale(false)
 {
   const ShaderAttrib *shader_attrib;
   state->get_attrib_def(shader_attrib);

+ 7 - 0
panda/src/downloadertools/multify.cxx

@@ -792,8 +792,15 @@ main(int argc, char **argv) {
     }
   }
 
+#ifdef _MSC_VER
+  char source_date_epoch_str[64];
+  size_t source_date_epoch_size = 0;
+  if (getenv_s(&source_date_epoch_size, source_date_epoch_str,
+               sizeof(source_date_epoch_str), "SOURCE_DATE_EPOCH"), source_date_epoch_size > 1) {
+#else
   const char *source_date_epoch_str = getenv("SOURCE_DATE_EPOCH");
   if (source_date_epoch_str != nullptr && source_date_epoch_str[0] != 0) {
+#endif
     source_date_epoch = (time_t)strtoll(source_date_epoch_str, nullptr, 10);
   }
 

+ 1 - 1
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -693,7 +693,7 @@ update_shader_texture_bindings(DXShaderContext9 *prev, GSG *gsg) {
         continue;
       }
 
-      if (spec._suffix != 0) {
+      if (spec._suffix != nullptr) {
         // The suffix feature is inefficient.  It is a temporary hack.
         tex = tex->load_related(spec._suffix);
       }

+ 2 - 2
panda/src/dxgsg9/wdxGraphicsBuffer9.cxx

@@ -266,7 +266,7 @@ rebuild_bitplanes() {
 
   // Decide how big the bitplanes should be.
 
-  if ((_host != 0)&&(_creation_flags & GraphicsPipe::BF_size_track_host)) {
+  if (_host != nullptr && (_creation_flags & GraphicsPipe::BF_size_track_host) != 0) {
     if (_host->get_size() != _size) {
       set_size_and_recalc(_host->get_x_size(),
                           _host->get_y_size());
@@ -739,7 +739,7 @@ bool wdxGraphicsBuffer9::
 open_buffer() {
 
   // GSG creationinitialization.
-  if (_gsg == 0) {
+  if (_gsg == nullptr) {
     // The code below doesn't support creating a GSG on the fly.  Just error
     // out for now.  _dxgsg = new DXGraphicsStateGuardian9(_engine, _pipe);
     // _gsg = _dxgsg;

+ 1 - 1
panda/src/dxgsg9/wdxGraphicsWindow9.cxx

@@ -261,7 +261,7 @@ open_window() {
   static ConfigVariableBool always_discard_device("always-discard-device", true);
   bool discard_device = always_discard_device;
 
-  if (_gsg == 0) {
+  if (_gsg == nullptr) {
     _dxgsg = new DXGraphicsStateGuardian9(_engine, _pipe);
     _gsg = _dxgsg;
   } else {

+ 0 - 12
panda/src/egg/eggMesherEdge.I

@@ -20,18 +20,6 @@ EggMesherEdge(int vi_a, int vi_b) : _vi_a(vi_a), _vi_b(vi_b) {
   _opposite = nullptr;
 }
 
-/**
- *
- */
-INLINE EggMesherEdge::
-EggMesherEdge(const EggMesherEdge &copy) :
-  _vi_a(copy._vi_a),
-  _vi_b(copy._vi_b),
-  _strips(copy._strips),
-  _opposite(copy._opposite)
-{
-}
-
 /**
  * Returns true if the edge contains the indicated vertex index, false
  * otherwise.

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

@@ -29,7 +29,6 @@ class EggMesherStrip;
 class EXPCL_PANDA_EGG EggMesherEdge {
 public:
   INLINE EggMesherEdge(int vi_a, int vi_b);
-  INLINE EggMesherEdge(const EggMesherEdge &copy);
 
   void remove(EggMesherStrip *strip);
   void change_strip(EggMesherStrip *from, EggMesherStrip *to);

+ 2 - 1
panda/src/egg/eggTransform.h

@@ -16,6 +16,7 @@
 
 #include "pandabase.h"
 #include "luse.h"
+#include "memoryBase.h"
 #include "eggObject.h"
 
 /**
@@ -26,7 +27,7 @@
  * This may be either a 3-d transform, and therefore described by a 4x4
  * matrix, or a 2-d transform, described by a 3x3 matrix.
  */
-class EXPCL_PANDA_EGG EggTransform {
+class EXPCL_PANDA_EGG EggTransform : public MemoryBase {
 PUBLISHED:
   EggTransform();
   EggTransform(const EggTransform &copy);

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

@@ -803,7 +803,7 @@ convert_primitive(const GeomVertexData *vertex_data,
     const TexMatrixAttrib *tma = nullptr;
     net_state->get_attrib(tma);
 
-    for (size_t i = 0; i < ta->get_num_on_stages(); ++i) {
+    for (size_t i = 0; i < (size_t)ta->get_num_on_stages(); ++i) {
       TextureStage *tex_stage = ta->get_on_stage(i);
 
       EggTexture *egg_tex = get_egg_texture(ta->get_on_texture(tex_stage));

+ 0 - 2
panda/src/event/CMakeLists.txt

@@ -9,7 +9,6 @@ set(P3EVENT_HEADERS
   config_event.h
   buttonEvent.I buttonEvent.h
   buttonEventList.I buttonEventList.h
-  functionAsyncTask.h functionAsyncTask.I
   genericAsyncTask.h genericAsyncTask.I
   pointerEvent.I pointerEvent.h
   pointerEventList.I pointerEventList.h
@@ -29,7 +28,6 @@ set(P3EVENT_SOURCES
   asyncTaskSequence.cxx
   buttonEvent.cxx
   buttonEventList.cxx
-  functionAsyncTask.cxx
   genericAsyncTask.cxx
   pointerEvent.cxx
   pointerEventList.cxx

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini