diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 546a463136..3862904d48 100644
--- a/.github/workflows/ci.yml
+++ b/.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: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d8e251090c..5249ac8b57 100644
--- a/CMakeLists.txt
+++ b/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_
variables
diff --git a/README.md b/README.md
index 0213db141d..dc7e4fdb54 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Installing Panda3D
==================
The latest Panda3D SDK can be downloaded from
-[this page](https://www.panda3d.org/download/sdk-1-10-11/).
+[this page](https://www.panda3d.org/download/sdk-1-10-12/).
If you are familiar with installing Python packages, you can use
the following command:
@@ -58,14 +58,14 @@ You will also need to install the [Windows SDK](https://developer.microsoft.com/
and if you intend to target Windows Vista, you will also need the
[Windows 8.1 SDK](https://go.microsoft.com/fwlink/p/?LinkId=323507).
-You will also need to have the third-party dependency libraries available for
+You will also need the thirdparty dependency libraries available for
the build scripts to use. These are available from one of these two URLs,
depending on whether you are on a 32-bit or 64-bit system, or you can
[click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
building them from source.
-- https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win64.zip
-- https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win32.zip
+- https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-win64.zip
+- https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-win32.zip
After acquiring these dependencies, you can build Panda3D from the command
prompt using the following command. Change the `--msvc-version` option based
@@ -136,7 +136,7 @@ macOS
-----
On macOS, you will need to download a set of precompiled thirdparty packages in order to
-compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.12/panda3d-1.10.12-tools-mac.tar.gz).
After placing the thirdparty directory inside the panda3d source directory,
you may build Panda3D using a command like the following:
@@ -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 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
+
+This does not include Python at the moment, which can be extracted from
+[this archive](https://rdb.name/thirdparty-android.tar.gz) instead.
+
+These commands show how to compile wheels for the supported Android ABIs:
```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
+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
```
-Then, you can build the .apk using this command:
+It is now possible to use the generated wheels with `build_apps`, as explained
+on this page:
-```bash
-python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer
-```
-
-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).
+https://discourse.panda3d.org/t/deployment-for-android/28226
Running Tests
=============
diff --git a/cmake/macros/BuildMetalib.cmake b/cmake/macros/BuildMetalib.cmake
index 079b794b85..b13c68b204 100644
--- a/cmake/macros/BuildMetalib.cmake
+++ b/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 "$")
- 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_include_directories("${target}" SYSTEM PUBLIC "${sys_include_directories}")
-
- # And for INTERFACE_COMPILE_DEFINITIONS as well
- set(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 "$")
-
- # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :(
- set(is_object "$,OBJECT_LIBRARY>")
- set(is_component "$>")
-
- set(name_of_component "$<${is_component}:$>")
- set(name_of_non_component "$<$:$>")
-
- 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 "$")
@@ -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 "$/${CMAKE_INSTALL_INCLUDEDIR}/panda3d>")
diff --git a/cmake/macros/PackageConfig.cmake b/cmake/macros/PackageConfig.cmake
index ac020f7866..873c93d098 100644
--- a/cmake/macros/PackageConfig.cmake
+++ b/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
- "$")
- 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})
diff --git a/cmake/modules/FindGTK3.cmake b/cmake/modules/FindGTK3.cmake
new file mode 100644
index 0000000000..213d9c5745
--- /dev/null
+++ b/cmake/modules/FindGTK3.cmake
@@ -0,0 +1,12 @@
+find_package(PkgConfig QUIET)
+
+set(__gtk3_required_version "${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION}")
+if(__gtk3_required_version)
+ set(__gtk3_required_version " >= ${__gtk3_required_version}")
+endif()
+pkg_check_modules(GTK3 QUIET "gtk+-3.0${__gtk3_required_version}" IMPORTED_TARGET)
+
+if (NOT TARGET PkgConfig::GTK3)
+ set(GTK3_FOUND 0)
+endif()
+unset(__gtk3_required_version)
diff --git a/cmake/modules/FindLibSquish.cmake b/cmake/modules/FindLibSquish.cmake
index 3399d81ae7..2ff949abd0 100644
--- a/cmake/modules/FindLibSquish.cmake
+++ b/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)
diff --git a/cmake/modules/FindOpenEXR.cmake b/cmake/modules/FindOpenEXR.cmake
deleted file mode 100644
index 365ceec733..0000000000
--- a/cmake/modules/FindOpenEXR.cmake
+++ /dev/null
@@ -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
-)
diff --git a/contrib/src/rplight/shadowSource.h b/contrib/src/rplight/shadowSource.h
index 84b00a197b..3f3d15fe15 100644
--- a/contrib/src/rplight/shadowSource.h
+++ b/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"
diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py
index db22e9bf02..37583e9e12 100644
--- a/direct/src/dist/FreezeTool.py
+++ b/direct/src/dist/FreezeTool.py
@@ -80,6 +80,13 @@ defaultHiddenImports = {
],
'pandas.compat': ['lzma', 'cmath'],
'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
+ 'plyer': ['plyer.platforms'],
+ 'scipy.linalg': ['scipy.linalg.cython_blas', 'scipy.linalg.cython_lapack'],
+ 'scipy.sparse.csgraph': ['scipy.sparse.csgraph._validation'],
+ 'scipy.spatial._qhull': ['scipy._lib.messagestream'],
+ 'scipy.spatial.transform._rotation': ['scipy.spatial.transform._rotation_groups'],
+ 'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
+ 'scipy.stats._stats': ['scipy.special.cython_special'],
}
@@ -791,10 +798,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 +846,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 +891,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 +899,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 +938,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 +962,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 +971,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 +994,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 +1037,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 +1083,6 @@ class Freezer:
directories within a particular directory.
"""
- assert self.mf is None
-
if not newName:
newName = moduleName
@@ -1122,8 +1103,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 +1144,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.
@@ -1212,12 +1193,33 @@ class Freezer:
else:
self.__loadModule(self.ModuleDef(modname, implicit = True))
+ # Special case for sysconfig, which depends on a platform-specific
+ # sysconfigdata module on POSIX systems.
+ missing = []
+ if 'sysconfig' in self.mf.modules and \
+ ('linux' in self.platform or 'mac' in self.platform):
+ modname = '_sysconfigdata'
+ if sys.version_info >= (3, 6):
+ modname += '_'
+ if sys.version_info < (3, 8):
+ modname += 'm'
+
+ if 'linux' in self.platform:
+ arch = self.platform.split('_', 1)[1]
+ modname += '_linux_' + arch + '-linux-gnu'
+ elif 'mac' in self.platform:
+ modname += '_darwin_darwin'
+
+ try:
+ self.__loadModule(self.ModuleDef(modname, implicit=True))
+ except:
+ missing.append(modname)
+
# Now, any new modules we found get added to the export list.
for origName in list(self.mf.modules.keys()):
if origName not in origToNewName:
self.modules[origName] = self.ModuleDef(origName, implicit = True)
- missing = []
for origName in self.mf.any_missing_maybe()[0]:
if origName in startupModules:
continue
@@ -2428,6 +2430,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 +2479,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 +2762,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
diff --git a/direct/src/dist/_android.py b/direct/src/dist/_android.py
index 6214626a57..3ea7d8ea76 100644
--- a/direct/src/dist/_android.py
+++ b/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),
diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py
index 4a628d292d..1d60a7a8b2 100644
--- a/direct/src/dist/commands.py
+++ b/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',
diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py
index be50ebd96a..0a492b9b40 100644
--- a/direct/src/distributed/ConnectionRepository.py
+++ b/direct/src/distributed/ConnectionRepository.py
@@ -9,7 +9,6 @@ from direct.showbase import GarbageReport
from direct.showbase.MessengerGlobal import messenger
from .PyDatagramIterator import PyDatagramIterator
-import inspect
import gc
__all__ = ["ConnectionRepository", "GCTrigger"]
@@ -311,6 +310,8 @@ class ConnectionRepository(
# Now get the class definition for the classes named in the DC
# file.
+ import inspect
+
for i in range(dcFile.getNumClasses()):
dclass = dcFile.getClass(i)
number = dclass.getNumber()
diff --git a/direct/src/distributed/PyDatagramIterator.py b/direct/src/distributed/PyDatagramIterator.py
index bd27469c41..591734c0dc 100755
--- a/direct/src/distributed/PyDatagramIterator.py
+++ b/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:
diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx
index 1730f71591..65627140ac 100644
--- a/direct/src/distributed/cConnectionRepository.cxx
+++ b/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) {
diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py
index 6634b504f2..9242290929 100644
--- a/direct/src/filter/CommonFilters.py
+++ b/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)
diff --git a/direct/src/filter/FilterManager.py b/direct/src/filter/FilterManager.py
index 40a3f08b5a..fee9a02365 100644
--- a/direct/src/filter/FilterManager.py
+++ b/direct/src/filter/FilterManager.py
@@ -323,7 +323,7 @@ class FilterManager(DirectObject):
props.setAuxRgba(1)
if auxtex1 is not None:
props.setAuxRgba(2)
- buffer=base.graphicsEngine.makeOutput(
+ buffer=self.engine.makeOutput(
self.win.getPipe(), name, -1,
props, winprops, GraphicsPipe.BFRefuseWindow | GraphicsPipe.BFResizeable,
self.win.getGsg(), self.win)
diff --git a/direct/src/filter/filterBloomI.py b/direct/src/filter/filterBloomI.py
index 6c44fb3ecb..ddb44c4ad0 100644
--- a/direct/src/filter/filterBloomI.py
+++ b/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);
diff --git a/direct/src/filter/filterBloomX.py b/direct/src/filter/filterBloomX.py
index 220cd776b7..c7e40ab326 100644
--- a/direct/src/filter/filterBloomX.py
+++ b/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);
diff --git a/direct/src/filter/filterBloomY.py b/direct/src/filter/filterBloomY.py
index 18e2885b1e..0b6cc9d5f8 100644
--- a/direct/src/filter/filterBloomY.py
+++ b/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);
diff --git a/direct/src/filter/filterBlurX.py b/direct/src/filter/filterBlurX.py
index b472a5d7a7..426c1dc9a7 100644
--- a/direct/src/filter/filterBlurX.py
+++ b/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;
}
diff --git a/direct/src/filter/filterBlurY.py b/direct/src/filter/filterBlurY.py
index 173c7a73b5..00d0980b30 100644
--- a/direct/src/filter/filterBlurY.py
+++ b/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;
}
diff --git a/direct/src/filter/filterCopy.py b/direct/src/filter/filterCopy.py
index d9770fb04b..dee3dadace 100644
--- a/direct/src/filter/filterCopy.py
+++ b/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,
diff --git a/direct/src/filter/filterDown4.py b/direct/src/filter/filterDown4.py
index 14595dbbd7..d0c7ab4d2c 100644
--- a/direct/src/filter/filterDown4.py
+++ b/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);
diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py
index 51f08e19e9..159c2f2063 100644
--- a/direct/src/gui/DirectGuiBase.py
+++ b/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)
diff --git a/direct/src/motiontrail/cMotionTrail.h b/direct/src/motiontrail/cMotionTrail.h
index fdb5ba1f56..bb42001ffc 100644
--- a/direct/src/motiontrail/cMotionTrail.h
+++ b/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;
diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py
index 8c6c2c72e9..f54edca7f9 100644
--- a/direct/src/showbase/BufferViewer.py
+++ b/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(Vec3F.rfu(-1 - offsetx, 0, -1 - offsety))
+ vwriter.addData3f(Vec3F.rfu( 1 + offsetx, 0, -1 - offsety))
+ vwriter.addData3f(Vec3F.rfu( 1 + offsetx, 0, 1 + offsety))
+ vwriter.addData3f(Vec3F.rfu(-1 - offsetx, 0, 1 + offsety))
cwriter.addData3f(bright, bright, bright)
cwriter.addData3f(bright, bright, bright)
cwriter.addData3f(bright, bright, bright)
diff --git a/direct/src/showbase/DirectObject.py b/direct/src/showbase/DirectObject.py
index f511e8f49d..f97eb76d3c 100644
--- a/direct/src/showbase/DirectObject.py
+++ b/direct/src/showbase/DirectObject.py
@@ -44,14 +44,14 @@ class DirectObject:
def addTask(self, *args, **kwargs):
if not hasattr(self, "_taskList"):
self._taskList = {}
- kwargs['owner']=self
+ kwargs['owner'] = self
task = taskMgr.add(*args, **kwargs)
return task
def doMethodLater(self, *args, **kwargs):
if not hasattr(self, "_taskList"):
self._taskList = {}
- kwargs['owner']=self
+ kwargs['owner'] = self
task = taskMgr.doMethodLater(*args, **kwargs)
return task
diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py
index 178cfa6bca..b38f0a234d 100644
--- a/direct/src/showbase/PythonUtil.py
+++ b/direct/src/showbase/PythonUtil.py
@@ -11,7 +11,7 @@ __all__ = [
'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
'findPythonModule', 'mostDerivedLast', 'clampScalar', 'weightedChoice',
'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32',
- 'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
+ 'SerialNumGen', 'serialNum', 'uniqueName', 'Singleton',
'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr',
'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString',
'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall',
@@ -1223,111 +1223,6 @@ def uniqueName(name):
global _serialGen
return '%s-%s' % (name, _serialGen.next())
-class EnumIter:
- def __init__(self, enum):
- self._values = tuple(enum._stringTable.keys())
- self._index = 0
- def __iter__(self):
- return self
- def __next__(self):
- if self._index >= len(self._values):
- raise StopIteration
- self._index += 1
- return self._values[self._index-1]
-
-class Enum:
- """Pass in list of strings or string of comma-separated strings.
- Items are accessible as instance.item, and are assigned unique,
- increasing integer values. Pass in integer for 'start' to override
- starting value.
-
- Example:
-
- >>> colors = Enum('red, green, blue')
- >>> colors.red
- 0
- >>> colors.green
- 1
- >>> colors.blue
- 2
- >>> colors.getString(colors.red)
- 'red'
- """
-
- if __debug__:
- # chars that cannot appear within an item string.
- def _checkValidIdentifier(item):
- import string
- invalidChars = string.whitespace + string.punctuation
- invalidChars = invalidChars.replace('_', '')
- invalidFirstChars = invalidChars+string.digits
- if item[0] in invalidFirstChars:
- raise SyntaxError("Enum '%s' contains invalid first char" %
- item)
- if not disjoint(item, invalidChars):
- for char in item:
- if char in invalidChars:
- raise SyntaxError(
- "Enum\n'%s'\ncontains illegal char '%s'" %
- (item, char))
- return 1
- _checkValidIdentifier = staticmethod(_checkValidIdentifier)
-
- def __init__(self, items, start=0):
- if isinstance(items, str):
- items = items.split(',')
-
- self._stringTable = {}
-
- # make sure we don't overwrite an existing element of the class
- assert self._checkExistingMembers(items)
- assert uniqueElements(items)
-
- i = start
- for item in items:
- # remove leading/trailing whitespace
- item = item.strip()
- # is there anything left?
- if len(item) == 0:
- continue
- # make sure there are no invalid characters
- assert Enum._checkValidIdentifier(item)
- self.__dict__[item] = i
- self._stringTable[i] = item
- i += 1
-
- def __iter__(self):
- return EnumIter(self)
-
- def hasString(self, string):
- return string in set(self._stringTable.values())
-
- def fromString(self, string):
- if self.hasString(string):
- return self.__dict__[string]
- # throw an error
- {}[string]
-
- def getString(self, value):
- return self._stringTable[value]
-
- def __contains__(self, value):
- return value in self._stringTable
-
- def __len__(self):
- return len(self._stringTable)
-
- def copyTo(self, obj):
- # copies all members onto obj
- for name, value in self._stringTable:
- setattr(obj, name, value)
-
- if __debug__:
- def _checkExistingMembers(self, items):
- for item in items:
- if hasattr(self, item):
- return 0
- return 1
############################################################
# class: Singleton
@@ -2635,7 +2530,6 @@ class PriorityCallbacks:
builtins.Functor = Functor
builtins.Stack = Stack
builtins.Queue = Queue
-builtins.Enum = Enum
builtins.SerialNumGen = SerialNumGen
builtins.SerialMaskedGen = SerialMaskedGen
builtins.ScratchPad = ScratchPad
diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py
index 5002ddd824..eae8383387 100644
--- a/direct/src/showbase/ShowBase.py
+++ b/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):
"""
diff --git a/direct/src/stdpy/threading.py b/direct/src/stdpy/threading.py
index 3b83e7c691..c4062b48a9 100644
--- a/direct/src/stdpy/threading.py
+++ b/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):
diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py
index f946ca9122..a6363e3644 100644
--- a/direct/src/task/Task.py
+++ b/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
diff --git a/doc/INSTALL b/doc/INSTALL
index 2deff94b14..6deaa5394a 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -186,7 +186,7 @@ it will show you the available command-line options:
--use-opencv --no-opencv (enable/disable use of OPENCV)
--use-directcam --no-directcam (enable/disable use of DIRECTCAM)
--use-vision --no-vision (enable/disable use of VISION)
- --use-gtk2 --no-gtk2 (enable/disable use of GTK2)
+ --use-gtk3 --no-gtk3 (enable/disable use of GTK3)
--use-npapi --no-npapi (enable/disable use of NPAPI)
--use-mfc --no-mfc (enable/disable use of MFC)
--use-wx --no-wx (enable/disable use of WX)
diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes
index a2499ce00d..53aa89a4bb 100644
--- a/doc/ReleaseNotes
+++ b/doc/ReleaseNotes
@@ -1,3 +1,59 @@
+----------------------- RELEASE 1.10.12 -----------------------
+
+Recommended maintenance release containing primarily bug fixes.
+
+Windowing
+* Windows: Fix origin not respected when switching to windowed mode
+* macOS: Fix origin not being updated when resizing window
+* macOS: Fix off-by-one errors with mouse cursor position
+* macOS: Fix issues with confined mouse mode
+* macOS: Fix events being suppressed when moving the mouse pointer
+* macOS: Invert horizontal scroll, set `cocoa-invert-wheel-x true` to revert
+
+Rendering
+* Add `shadow-cube-map-filter` setting to enable cube map shadow filtering
+* Support floating-point FBOs in OpenGL ES 2+
+* Fix texture format selection in OpenGL with T_half_float component type
+* Added `egl-device-index` config var to select EGL device
+* Offscreen windows in tinydisplay renderer are now resizeable
+* CommonFilters now supports alternative coordinate systems
+* Fix BufferViewer frame when using a different coordinate system
+
+Deployment
+* Fix _bootlocale error in deployed application on Windows with Python 3.10
+* Include _sysconfigdata module properly when using sysconfig module
+* Fix building deploy-stub on platforms that use DT_RUNPATH instead of DT_RPATH
+* `sys.flags.optimize` is now set to 2 in Python 3.2 and above
+* Fix import errors when using scipy
+
+Miscellaneous
+* `Texture::get_ram_image_as()` fixed for 3D textures
+* Fix PStats GPU timing not working with newer NVIDIA drivers
+* Fix false negative in collision test when sphere is fully inside box
+* Resolve segmentation fault when statically linking ffmpeg module
+* Fix issue with failed mmap when using WebcamVideo on Linux
+* macOS: Keyboard/mouse devices are no longer enumerated by default
+* Fix repr of LPlane class
+* Remove dependency on ShowBase in FilterManager
+* Many new functions added to interrogatedb module to query additional info
+* Interrogate no longer writes wrappers with rvalue references to interrogatedb
+* PStats on Linux: Fix mouse motion detected outside strip chart graph area
+* Fix assertion when reading bam file with Bullet convex hull shape
+* Fix memory leak when specifying owner of a task
+* Add additional helpful debug/spam prints to display code
+
+Build
+* Support building with Python 3.11
+* Support building with OpenSSL 1.1.1 on Windows
+* Support building with OpenEXR 3.0 or 3.1 on Windows
+* Fix errors when compiling Panda headers with MinGW
+* Allow compiling Panda headers on Windows without NOMINMAX set
+* Fix wheel platform tag on manylinux aarch64
+* Experimentally allow building with mimalloc on Windows
+* Makepanda records cache timestamps as integers instead of floats
+* Makepanda can now also build tinydisplay on Linux without X11
+* Fix naming of built wheels when building for macOS 12
+
----------------------- RELEASE 1.10.11 -----------------------
Maintenance release containing assorted bug fixes and minor improvements.
diff --git a/dtool/CompilerFlags.cmake b/dtool/CompilerFlags.cmake
index a947d50927..0b6945878e 100644
--- a/dtool/CompilerFlags.cmake
+++ b/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()
diff --git a/dtool/Config.cmake b/dtool/Config.cmake
index 3a19fb90ad..3d8ca9272f 100644
--- a/dtool/Config.cmake
+++ b/dtool/Config.cmake
@@ -288,6 +288,26 @@ mark_as_advanced(SIMULATE_NETWORK_DELAY DO_MEMORY_USAGE DO_DCAST)
# The following options have to do with the memory allocation system.
#
+find_package(MIMALLOC 1.0 QUIET)
+
+package_option(MIMALLOC
+ "The mimalloc allocator. See also USE_MEMORY_MIMALLOC, which
+you will need to use to activate it by default. If you do not set
+USE_MEMORY_MIMALLOC, Panda will decide whether to use it."
+ IMPORTED_AS mimalloc-static)
+
+if (WIN32 AND HAVE_MIMALLOC)
+ set(_prefer_mimalloc ON)
+else()
+ set(_prefer_mimalloc OFF)
+endif()
+
+option(USE_MEMORY_MIMALLOC
+ "This is an optional memory allocator with good multi-threading
+support. It is recommended on Windows, where it gives much better
+performance than the built-in malloc. However, it does not appear
+to be significantly faster on glibc-based systems." ${_prefer_mimalloc})
+
option(USE_MEMORY_DLMALLOC
"This is an optional alternative memory-allocation scheme
available within Panda. You can experiment with it to see
@@ -307,16 +327,33 @@ if 16-byte alignment must be performed on top of it, wasting up to
is required and not provided by the system malloc library, then an
alternative malloc system (above) will be used instead." OFF)
-option(USE_DELETED_CHAIN
- "Define this true to use the DELETED_CHAIN macros, which support
+if (WIN32 AND NOT HAVE_MIMALLOC)
+ option(USE_DELETED_CHAIN
+ "Define this true to use the DELETED_CHAIN macros, which support
fast re-use of existing allocated blocks, minimizing the low-level
calls to malloc() and free() for frequently-created and -deleted
-objects. There's usually no reason to set this false, unless you
-suspect a bug in Panda's memory management code." ON)
+objects. This is significantly better than built-in malloc on Windows
+but suffers with multiple threads, where mimalloc performs better, so
+it is preferred to get mimalloc instead and turn this OFF." ON)
+else()
+ option(USE_DELETED_CHAIN
+ "Define this true to use the DELETED_CHAIN macros, which support
+fast re-use of existing allocated blocks, minimizing the low-level
+calls to malloc() and free() for frequently-created and -deleted
+objects. However, modern memory allocators generally perform as good,
+especially with threading, so best leave this OFF." OFF)
+endif()
mark_as_advanced(USE_MEMORY_DLMALLOC USE_MEMORY_PTMALLOC2
- MEMORY_HOOK_DO_ALIGN USE_DELETED_CHAIN)
+ USE_MEMORY_MIMALLOC MEMORY_HOOK_DO_ALIGN USE_DELETED_CHAIN)
+if(USE_MEMORY_MIMALLOC)
+ package_status(MIMALLOC "mimalloc memory allocator")
+else()
+ package_status(MIMALLOC "mimalloc memory allocator (not used)")
+endif()
+
+unset(_prefer_mimalloc)
#
# This section relates to mobile-device/phone support and options
diff --git a/dtool/Package.cmake b/dtool/Package.cmake
index 4c41cfb7d2..10e653f6d4 100644
--- a/dtool/Package.cmake
+++ b/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."
@@ -556,16 +572,17 @@ package_option(HarfBuzz
package_status(HarfBuzz "HarfBuzz")
-# GTK2
+# GTK3
-set(Freetype_FIND_QUIETLY TRUE) # Fix for builtin FindGTK2
-set(GTK2_GTK_FIND_QUIETLY TRUE) # Fix for builtin FindGTK2
-find_package(GTK2 QUIET COMPONENTS gtk)
+if(NOT WIN32)
+ find_package(GTK3 QUIET)
+endif()
-package_option(GTK2)
-
-package_status(GTK2 "gtk+-2")
+package_option(GTK3
+ "This is necessary to build the PStats performance analysis tool on platforms
+ other than Windows.")
+package_status(GTK3 "gtk+-3")
#
# ------------ Physics engines ------------
diff --git a/dtool/PandaVersion.cmake b/dtool/PandaVersion.cmake
index e27bcbd95d..3813d4ccde 100644
--- a/dtool/PandaVersion.cmake
+++ b/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
diff --git a/dtool/dtool_config.h.in b/dtool/dtool_config.h.in
index 2a0a91946c..f115e022c3 100644
--- a/dtool/dtool_config.h.in
+++ b/dtool/dtool_config.h.in
@@ -130,8 +130,12 @@
/* Define if we want to support fixed-function OpenGL rendering. */
#cmakedefine SUPPORT_FIXED_FUNCTION
-/* Define for either of the alternative malloc schemes. */
+/* Define if we have mimalloc available. */
+#cmakedefine HAVE_MIMALLOC
+
+/* Define for one of the alternative malloc schemes. */
#cmakedefine USE_MEMORY_DLMALLOC
+#cmakedefine USE_MEMORY_MIMALLOC
#cmakedefine USE_MEMORY_PTMALLOC2
/* Define if we want to compile in support for pipelining. */
diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx
index 39719b385d..0293df157f 100644
--- a/dtool/metalibs/dtoolconfig/pydtool.cxx
+++ b/dtool/metalibs/dtoolconfig/pydtool.cxx
@@ -60,6 +60,10 @@ static PyObject *_inP07ytISgV(PyObject *self, PyObject *args);
static PyObject *_inP07ytH3bx(PyObject *self, PyObject *args);
static PyObject *_inP07ytzeUk(PyObject *self, PyObject *args);
static PyObject *_inP07ytUeI5(PyObject *self, PyObject *args);
+static PyObject *_inP07ytbmxJ(PyObject *self, PyObject *args);
+static PyObject *_inP07ytY8Lc(PyObject *self, PyObject *args);
+static PyObject *_inP07ytJAAI(PyObject *self, PyObject *args);
+static PyObject *_inP07yt0UXw(PyObject *self, PyObject *args);
static PyObject *_inP07ytuSvx(PyObject *self, PyObject *args);
static PyObject *_inP07ytwpYd(PyObject *self, PyObject *args);
static PyObject *_inP07ytOfNh(PyObject *self, PyObject *args);
@@ -82,6 +86,7 @@ static PyObject *_inP07ytGMpW(PyObject *self, PyObject *args);
static PyObject *_inP07ytNuBV(PyObject *self, PyObject *args);
static PyObject *_inP07yt9UwA(PyObject *self, PyObject *args);
static PyObject *_inP07yt3FDt(PyObject *self, PyObject *args);
+static PyObject *_inP07ytDgOY(PyObject *self, PyObject *args);
static PyObject *_inP07ytf513(PyObject *self, PyObject *args);
static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args);
static PyObject *_inP07yt7shV(PyObject *self, PyObject *args);
@@ -92,6 +97,8 @@ static PyObject *_inP07ytiytI(PyObject *self, PyObject *args);
static PyObject *_inP07ytZc07(PyObject *self, PyObject *args);
static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args);
static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args);
+static PyObject *_inP07ytrppS(PyObject *self, PyObject *args);
+static PyObject *_inP07ytO50x(PyObject *self, PyObject *args);
static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args);
static PyObject *_inP07ytMT0z(PyObject *self, PyObject *args);
static PyObject *_inP07ytiW3v(PyObject *self, PyObject *args);
@@ -123,7 +130,10 @@ static PyObject *_inP07ytDyRd(PyObject *self, PyObject *args);
static PyObject *_inP07ytMnKa(PyObject *self, PyObject *args);
static PyObject *_inP07ytRtji(PyObject *self, PyObject *args);
static PyObject *_inP07ytCnbQ(PyObject *self, PyObject *args);
+static PyObject *_inP07ytoxqc(PyObject *self, PyObject *args);
+static PyObject *_inP07ytZQIS(PyObject *self, PyObject *args);
static PyObject *_inP07ytdUVN(PyObject *self, PyObject *args);
+static PyObject *_inP07ytZtNk(PyObject *self, PyObject *args);
static PyObject *_inP07ytihbt(PyObject *self, PyObject *args);
static PyObject *_inP07ytbyPY(PyObject *self, PyObject *args);
static PyObject *_inP07ytAaT6(PyObject *self, PyObject *args);
@@ -149,6 +159,7 @@ static PyObject *_inP07ytHDtN(PyObject *self, PyObject *args);
static PyObject *_inP07ytHFjA(PyObject *self, PyObject *args);
static PyObject *_inP07yt_NPR(PyObject *self, PyObject *args);
static PyObject *_inP07ytcTOH(PyObject *self, PyObject *args);
+static PyObject *_inP07ytC5Uk(PyObject *self, PyObject *args);
static PyObject *_inP07ythdU7(PyObject *self, PyObject *args);
static PyObject *_inP07ytQPxU(PyObject *self, PyObject *args);
static PyObject *_inP07ytO7Pz(PyObject *self, PyObject *args);
@@ -168,7 +179,7 @@ static PyObject *
_inP07yttbRf(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ::interrogate_add_search_directory((char const *)param0);
+ (::interrogate_add_search_directory)((char const *)param0);
return Py_BuildValue("");
}
return nullptr;
@@ -182,7 +193,7 @@ static PyObject *
_inP07ytda_g(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ::interrogate_add_search_path((char const *)param0);
+ (::interrogate_add_search_path)((char const *)param0);
return Py_BuildValue("");
}
return nullptr;
@@ -195,7 +206,7 @@ _inP07ytda_g(PyObject *, PyObject *args) {
static PyObject *
_inP07yt4RgX(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- bool return_value = ::interrogate_error_flag();
+ bool return_value = (::interrogate_error_flag)();
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -208,7 +219,7 @@ _inP07yt4RgX(PyObject *, PyObject *args) {
static PyObject *
_inP07yt3Gip(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_manifests();
+ int return_value = (::interrogate_number_of_manifests)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -226,7 +237,7 @@ static PyObject *
_inP07ytRKDz(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- ManifestIndex return_value = ::interrogate_get_manifest((int)param0);
+ ManifestIndex return_value = (::interrogate_get_manifest)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -244,7 +255,7 @@ static PyObject *
_inP07ytgZ9N(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ManifestIndex return_value = ::interrogate_get_manifest_by_name((char const *)param0);
+ ManifestIndex return_value = (::interrogate_get_manifest_by_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -262,7 +273,7 @@ static PyObject *
_inP07ytFnRZ(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_manifest_name((ManifestIndex)param0);
+ char const *return_value = (::interrogate_manifest_name)((ManifestIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -280,7 +291,7 @@ static PyObject *
_inP07ytg0Qv(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_manifest_definition((ManifestIndex)param0);
+ char const *return_value = (::interrogate_manifest_definition)((ManifestIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -298,7 +309,7 @@ static PyObject *
_inP07yttrqw(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_manifest_has_type((ManifestIndex)param0);
+ bool return_value = (::interrogate_manifest_has_type)((ManifestIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -312,7 +323,7 @@ static PyObject *
_inP07ytdmpW(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_manifest_get_type((ManifestIndex)param0);
+ TypeIndex return_value = (::interrogate_manifest_get_type)((ManifestIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -330,7 +341,7 @@ static PyObject *
_inP07ytUYgQ(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_manifest_has_getter((ManifestIndex)param0);
+ bool return_value = (::interrogate_manifest_has_getter)((ManifestIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -344,7 +355,7 @@ static PyObject *
_inP07yt0k7F(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_manifest_getter((ManifestIndex)param0);
+ FunctionIndex return_value = (::interrogate_manifest_getter)((ManifestIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -362,7 +373,7 @@ static PyObject *
_inP07ytfIsr(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_manifest_has_int_value((ManifestIndex)param0);
+ bool return_value = (::interrogate_manifest_has_int_value)((ManifestIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -376,7 +387,7 @@ static PyObject *
_inP07ytvysR(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_manifest_get_int_value((ManifestIndex)param0);
+ int return_value = (::interrogate_manifest_get_int_value)((ManifestIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -394,7 +405,7 @@ static PyObject *
_inP07ytYQ_2(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_element_name((ElementIndex)param0);
+ char const *return_value = (::interrogate_element_name)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -412,7 +423,7 @@ static PyObject *
_inP07yt3kdv(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_element_scoped_name((ElementIndex)param0);
+ char const *return_value = (::interrogate_element_scoped_name)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -430,7 +441,7 @@ static PyObject *
_inP07ytew01(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_element_has_comment((ElementIndex)param0);
+ bool return_value = (::interrogate_element_has_comment)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -444,7 +455,7 @@ static PyObject *
_inP07ytQna7(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_element_comment((ElementIndex)param0);
+ char const *return_value = (::interrogate_element_comment)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -462,7 +473,7 @@ static PyObject *
_inP07ytkg95(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ElementIndex return_value = ::interrogate_get_element_by_name((char const *)param0);
+ ElementIndex return_value = (::interrogate_get_element_by_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -480,7 +491,7 @@ static PyObject *
_inP07ytluRc(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ElementIndex return_value = ::interrogate_get_element_by_scoped_name((char const *)param0);
+ ElementIndex return_value = (::interrogate_get_element_by_scoped_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -498,7 +509,7 @@ static PyObject *
_inP07yttHdM(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_element_type((ElementIndex)param0);
+ TypeIndex return_value = (::interrogate_element_type)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -516,7 +527,7 @@ static PyObject *
_inP07ytDId0(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_element_has_getter((ElementIndex)param0);
+ bool return_value = (::interrogate_element_has_getter)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -530,7 +541,7 @@ static PyObject *
_inP07ytHuAm(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_element_getter((ElementIndex)param0);
+ FunctionIndex return_value = (::interrogate_element_getter)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -548,7 +559,7 @@ static PyObject *
_inP07yt_xr0(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_element_has_setter((ElementIndex)param0);
+ bool return_value = (::interrogate_element_has_setter)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -562,7 +573,7 @@ static PyObject *
_inP07ytH5qp(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_element_setter((ElementIndex)param0);
+ FunctionIndex return_value = (::interrogate_element_setter)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -580,7 +591,7 @@ static PyObject *
_inP07ytq45U(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_element_is_sequence((ElementIndex)param0);
+ bool return_value = (::interrogate_element_is_sequence)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -594,7 +605,7 @@ static PyObject *
_inP07yt6IPa(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_element_is_mapping((ElementIndex)param0);
+ bool return_value = (::interrogate_element_is_mapping)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -607,7 +618,7 @@ _inP07yt6IPa(PyObject *, PyObject *args) {
static PyObject *
_inP07ytU2_B(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_globals();
+ int return_value = (::interrogate_number_of_globals)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -625,7 +636,7 @@ static PyObject *
_inP07ytHFO2(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- ElementIndex return_value = ::interrogate_get_global((int)param0);
+ ElementIndex return_value = (::interrogate_get_global)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -642,7 +653,7 @@ _inP07ytHFO2(PyObject *, PyObject *args) {
static PyObject *
_inP07ytcfjm(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_global_functions();
+ int return_value = (::interrogate_number_of_global_functions)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -660,7 +671,7 @@ static PyObject *
_inP07yt3Sjw(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_get_global_function((int)param0);
+ FunctionIndex return_value = (::interrogate_get_global_function)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -677,7 +688,7 @@ _inP07yt3Sjw(PyObject *, PyObject *args) {
static PyObject *
_inP07ytgJcX(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_functions();
+ int return_value = (::interrogate_number_of_functions)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -695,7 +706,7 @@ static PyObject *
_inP07ytYlw6(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_get_function((int)param0);
+ FunctionIndex return_value = (::interrogate_get_function)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -713,7 +724,7 @@ static PyObject *
_inP07ytsmnz(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_name((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_name)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -731,7 +742,7 @@ static PyObject *
_inP07ytxQ10(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_scoped_name((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_scoped_name)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -749,7 +760,7 @@ static PyObject *
_inP07yt6gPB(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_function_has_comment((FunctionIndex)param0);
+ bool return_value = (::interrogate_function_has_comment)((FunctionIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -763,7 +774,7 @@ static PyObject *
_inP07ytISgV(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_comment((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_comment)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -781,7 +792,7 @@ static PyObject *
_inP07ytH3bx(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_prototype((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_prototype)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -799,7 +810,7 @@ static PyObject *
_inP07ytzeUk(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_function_is_method((FunctionIndex)param0);
+ bool return_value = (::interrogate_function_is_method)((FunctionIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -813,7 +824,7 @@ static PyObject *
_inP07ytUeI5(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_function_class((FunctionIndex)param0);
+ TypeIndex return_value = (::interrogate_function_class)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -823,6 +834,62 @@ _inP07ytUeI5(PyObject *, PyObject *args) {
return nullptr;
}
+/*
+ * Python simple wrapper for
+ * bool interrogate_function_is_unary_op(FunctionIndex function)
+ */
+static PyObject *
+_inP07ytbmxJ(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_function_is_unary_op)((FunctionIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_function_is_operator_typecast(FunctionIndex function)
+ */
+static PyObject *
+_inP07ytY8Lc(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_function_is_operator_typecast)((FunctionIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_function_is_constructor(FunctionIndex function)
+ */
+static PyObject *
+_inP07ytJAAI(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_function_is_constructor)((FunctionIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_function_is_destructor(FunctionIndex function)
+ */
+static PyObject *
+_inP07yt0UXw(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_function_is_destructor)((FunctionIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
/*
* Python simple wrapper for
* bool interrogate_function_has_module_name(FunctionIndex function)
@@ -831,7 +898,7 @@ static PyObject *
_inP07ytuSvx(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_function_has_module_name((FunctionIndex)param0);
+ bool return_value = (::interrogate_function_has_module_name)((FunctionIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -845,7 +912,7 @@ static PyObject *
_inP07ytwpYd(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_module_name((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_module_name)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -863,7 +930,7 @@ static PyObject *
_inP07ytOfNh(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_function_has_library_name((FunctionIndex)param0);
+ bool return_value = (::interrogate_function_has_library_name)((FunctionIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -877,7 +944,7 @@ static PyObject *
_inP07ytf5_U(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_function_library_name((FunctionIndex)param0);
+ char const *return_value = (::interrogate_function_library_name)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -895,7 +962,7 @@ static PyObject *
_inP07ytL3ZB(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_function_is_virtual((FunctionIndex)param0);
+ bool return_value = (::interrogate_function_is_virtual)((FunctionIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -909,7 +976,7 @@ static PyObject *
_inP07ytXw0I(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_function_number_of_c_wrappers((FunctionIndex)param0);
+ int return_value = (::interrogate_function_number_of_c_wrappers)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -928,7 +995,7 @@ _inP07yt3zru(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionWrapperIndex return_value = ::interrogate_function_c_wrapper((FunctionIndex)param0, (int)param1);
+ FunctionWrapperIndex return_value = (::interrogate_function_c_wrapper)((FunctionIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -946,7 +1013,7 @@ static PyObject *
_inP07ytRrg2(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_function_number_of_python_wrappers((FunctionIndex)param0);
+ int return_value = (::interrogate_function_number_of_python_wrappers)((FunctionIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -965,7 +1032,7 @@ _inP07ytEJCx(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionWrapperIndex return_value = ::interrogate_function_python_wrapper((FunctionIndex)param0, (int)param1);
+ FunctionWrapperIndex return_value = (::interrogate_function_python_wrapper)((FunctionIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -983,7 +1050,7 @@ static PyObject *
_inP07ytWAZr(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_wrapper_name((FunctionWrapperIndex)param0);
+ char const *return_value = (::interrogate_wrapper_name)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1001,7 +1068,7 @@ static PyObject *
_inP07ytrD_M(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_wrapper_is_callable_by_name((FunctionWrapperIndex)param0);
+ bool return_value = (::interrogate_wrapper_is_callable_by_name)((FunctionWrapperIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1015,7 +1082,7 @@ static PyObject *
_inP07ytjolz(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_wrapper_has_comment((FunctionWrapperIndex)param0);
+ bool return_value = (::interrogate_wrapper_has_comment)((FunctionWrapperIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1029,7 +1096,7 @@ static PyObject *
_inP07ytt_JD(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_wrapper_comment((FunctionWrapperIndex)param0);
+ char const *return_value = (::interrogate_wrapper_comment)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1047,7 +1114,7 @@ static PyObject *
_inP07ytwEts(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_wrapper_has_return_value((FunctionWrapperIndex)param0);
+ bool return_value = (::interrogate_wrapper_has_return_value)((FunctionWrapperIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1061,7 +1128,7 @@ static PyObject *
_inP07ytrJWs(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_wrapper_return_type((FunctionWrapperIndex)param0);
+ TypeIndex return_value = (::interrogate_wrapper_return_type)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1079,7 +1146,7 @@ static PyObject *
_inP07ytpmFD(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_wrapper_caller_manages_return_value((FunctionWrapperIndex)param0);
+ bool return_value = (::interrogate_wrapper_caller_manages_return_value)((FunctionWrapperIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1093,7 +1160,7 @@ static PyObject *
_inP07ytyYUX(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_wrapper_return_value_destructor((FunctionWrapperIndex)param0);
+ FunctionIndex return_value = (::interrogate_wrapper_return_value_destructor)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1111,7 +1178,7 @@ static PyObject *
_inP07yt54dn(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_wrapper_number_of_parameters((FunctionWrapperIndex)param0);
+ int return_value = (::interrogate_wrapper_number_of_parameters)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1130,7 +1197,7 @@ _inP07ytGMpW(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- TypeIndex return_value = ::interrogate_wrapper_parameter_type((FunctionWrapperIndex)param0, (int)param1);
+ TypeIndex return_value = (::interrogate_wrapper_parameter_type)((FunctionWrapperIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1149,7 +1216,7 @@ _inP07ytNuBV(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- bool return_value = ::interrogate_wrapper_parameter_has_name((FunctionWrapperIndex)param0, (int)param1);
+ bool return_value = (::interrogate_wrapper_parameter_has_name)((FunctionWrapperIndex)param0, (int)param1);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1164,7 +1231,7 @@ _inP07yt9UwA(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- char const *return_value = ::interrogate_wrapper_parameter_name((FunctionWrapperIndex)param0, (int)param1);
+ char const *return_value = (::interrogate_wrapper_parameter_name)((FunctionWrapperIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1183,7 +1250,22 @@ _inP07yt3FDt(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- bool return_value = ::interrogate_wrapper_parameter_is_this((FunctionWrapperIndex)param0, (int)param1);
+ bool return_value = (::interrogate_wrapper_parameter_is_this)((FunctionWrapperIndex)param0, (int)param1);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n)
+ */
+static PyObject *
+_inP07ytDgOY(PyObject *, PyObject *args) {
+ int param0;
+ int param1;
+ if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
+ bool return_value = (::interrogate_wrapper_parameter_is_optional)((FunctionWrapperIndex)param0, (int)param1);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1197,7 +1279,7 @@ static PyObject *
_inP07ytf513(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_wrapper_has_pointer((FunctionWrapperIndex)param0);
+ bool return_value = (::interrogate_wrapper_has_pointer)((FunctionWrapperIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1211,7 +1293,7 @@ static PyObject *
_inP07ytsqGH(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- ::interrogate_wrapper_pointer((FunctionWrapperIndex)param0);
+ (::interrogate_wrapper_pointer)((FunctionWrapperIndex)param0);
return Py_BuildValue("");
}
return nullptr;
@@ -1225,7 +1307,7 @@ static PyObject *
_inP07yt7shV(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_wrapper_unique_name((FunctionWrapperIndex)param0);
+ char const *return_value = (::interrogate_wrapper_unique_name)((FunctionWrapperIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1243,7 +1325,7 @@ static PyObject *
_inP07ytA1eF(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- FunctionWrapperIndex return_value = ::interrogate_get_wrapper_by_unique_name((char const *)param0);
+ FunctionWrapperIndex return_value = (::interrogate_get_wrapper_by_unique_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1261,7 +1343,7 @@ static PyObject *
_inP07yt776V(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_make_seq_seq_name((MakeSeqIndex)param0);
+ char const *return_value = (::interrogate_make_seq_seq_name)((MakeSeqIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1279,7 +1361,7 @@ static PyObject *
_inP07ytryup(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_make_seq_scoped_name((MakeSeqIndex)param0);
+ char const *return_value = (::interrogate_make_seq_scoped_name)((MakeSeqIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1297,7 +1379,7 @@ static PyObject *
_inP07ytiytI(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_make_seq_has_comment((ElementIndex)param0);
+ bool return_value = (::interrogate_make_seq_has_comment)((ElementIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1311,7 +1393,7 @@ static PyObject *
_inP07ytZc07(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_make_seq_comment((ElementIndex)param0);
+ char const *return_value = (::interrogate_make_seq_comment)((ElementIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1329,7 +1411,7 @@ static PyObject *
_inP07ytfaH0(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_make_seq_num_name((MakeSeqIndex)param0);
+ char const *return_value = (::interrogate_make_seq_num_name)((MakeSeqIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1347,7 +1429,7 @@ static PyObject *
_inP07ytGB9D(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_make_seq_element_name((MakeSeqIndex)param0);
+ char const *return_value = (::interrogate_make_seq_element_name)((MakeSeqIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1357,6 +1439,42 @@ _inP07ytGB9D(PyObject *, PyObject *args) {
return nullptr;
}
+/*
+ * Python simple wrapper for
+ * FunctionIndex interrogate_make_seq_num_getter(MakeSeqIndex make_seq)
+ */
+static PyObject *
+_inP07ytrppS(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ FunctionIndex return_value = (::interrogate_make_seq_num_getter)((MakeSeqIndex)param0);
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong(return_value);
+#else
+ return PyInt_FromLong(return_value);
+#endif
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * FunctionIndex interrogate_make_seq_element_getter(MakeSeqIndex make_seq)
+ */
+static PyObject *
+_inP07ytO50x(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ FunctionIndex return_value = (::interrogate_make_seq_element_getter)((MakeSeqIndex)param0);
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong(return_value);
+#else
+ return PyInt_FromLong(return_value);
+#endif
+ }
+ return nullptr;
+}
+
/*
* Python simple wrapper for
* int interrogate_number_of_global_types(void)
@@ -1364,7 +1482,7 @@ _inP07ytGB9D(PyObject *, PyObject *args) {
static PyObject *
_inP07ytsxxs(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_global_types();
+ int return_value = (::interrogate_number_of_global_types)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1382,7 +1500,7 @@ static PyObject *
_inP07ytMT0z(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_get_global_type((int)param0);
+ TypeIndex return_value = (::interrogate_get_global_type)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1399,7 +1517,7 @@ _inP07ytMT0z(PyObject *, PyObject *args) {
static PyObject *
_inP07ytiW3v(PyObject *, PyObject *args) {
if (PyArg_ParseTuple(args, "")) {
- int return_value = ::interrogate_number_of_types();
+ int return_value = (::interrogate_number_of_types)();
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1417,7 +1535,7 @@ static PyObject *
_inP07yt4Px8(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_get_type((int)param0);
+ TypeIndex return_value = (::interrogate_get_type)((int)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1435,7 +1553,7 @@ static PyObject *
_inP07ytNHcs(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- TypeIndex return_value = ::interrogate_get_type_by_name((char const *)param0);
+ TypeIndex return_value = (::interrogate_get_type_by_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1453,7 +1571,7 @@ static PyObject *
_inP07ytqHrb(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- TypeIndex return_value = ::interrogate_get_type_by_scoped_name((char const *)param0);
+ TypeIndex return_value = (::interrogate_get_type_by_scoped_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1471,7 +1589,7 @@ static PyObject *
_inP07ytaOqq(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- TypeIndex return_value = ::interrogate_get_type_by_true_name((char const *)param0);
+ TypeIndex return_value = (::interrogate_get_type_by_true_name)((char const *)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1489,7 +1607,7 @@ static PyObject *
_inP07ytpTBb(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_global((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_global)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1503,7 +1621,7 @@ static PyObject *
_inP07ytqWOw(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_name((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_name)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1521,7 +1639,7 @@ static PyObject *
_inP07ytHu7x(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_scoped_name((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_scoped_name)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1539,7 +1657,7 @@ static PyObject *
_inP07ytwGnA(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_true_name((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_true_name)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1557,7 +1675,7 @@ static PyObject *
_inP07ytXGxx(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_nested((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_nested)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1571,7 +1689,7 @@ static PyObject *
_inP07ytj04Z(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_type_outer_class((TypeIndex)param0);
+ TypeIndex return_value = (::interrogate_type_outer_class)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1589,7 +1707,7 @@ static PyObject *
_inP07ytEOv4(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_has_comment((TypeIndex)param0);
+ bool return_value = (::interrogate_type_has_comment)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1603,7 +1721,7 @@ static PyObject *
_inP07ytpCqJ(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_comment((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_comment)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1621,7 +1739,7 @@ static PyObject *
_inP07yt_Pz3(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_has_module_name((TypeIndex)param0);
+ bool return_value = (::interrogate_type_has_module_name)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1635,7 +1753,7 @@ static PyObject *
_inP07ytt_06(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_module_name((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_module_name)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1653,7 +1771,7 @@ static PyObject *
_inP07ytmuPs(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_has_library_name((TypeIndex)param0);
+ bool return_value = (::interrogate_type_has_library_name)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1667,7 +1785,7 @@ static PyObject *
_inP07ytvM8B(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- char const *return_value = ::interrogate_type_library_name((TypeIndex)param0);
+ char const *return_value = (::interrogate_type_library_name)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1685,7 +1803,7 @@ static PyObject *
_inP07ytap97(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_atomic((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_atomic)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1699,7 +1817,7 @@ static PyObject *
_inP07yt0o8D(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- AtomicToken return_value = ::interrogate_type_atomic_token((TypeIndex)param0);
+ AtomicToken return_value = (::interrogate_type_atomic_token)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1717,7 +1835,7 @@ static PyObject *
_inP07ytOoQ2(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_unsigned((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_unsigned)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1731,7 +1849,7 @@ static PyObject *
_inP07ytKuFh(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_signed((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_signed)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1745,7 +1863,7 @@ static PyObject *
_inP07yto5L6(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_long((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_long)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1759,7 +1877,7 @@ static PyObject *
_inP07ytzgKK(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_longlong((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_longlong)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1773,7 +1891,7 @@ static PyObject *
_inP07yt0FIF(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_short((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_short)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1787,7 +1905,7 @@ static PyObject *
_inP07ytZqvD(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_wrapped((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_wrapped)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1801,7 +1919,7 @@ static PyObject *
_inP07ytDyRd(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_pointer((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_pointer)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1815,7 +1933,7 @@ static PyObject *
_inP07ytMnKa(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_const((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_const)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1829,7 +1947,7 @@ static PyObject *
_inP07ytRtji(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_typedef((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_typedef)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1843,7 +1961,39 @@ static PyObject *
_inP07ytCnbQ(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- TypeIndex return_value = ::interrogate_type_wrapped_type((TypeIndex)param0);
+ TypeIndex return_value = (::interrogate_type_wrapped_type)((TypeIndex)param0);
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong(return_value);
+#else
+ return PyInt_FromLong(return_value);
+#endif
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_type_is_array(TypeIndex type)
+ */
+static PyObject *
+_inP07ytoxqc(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_type_is_array)((TypeIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * int interrogate_type_array_size(TypeIndex type)
+ */
+static PyObject *
+_inP07ytZQIS(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ int return_value = (::interrogate_type_array_size)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1861,7 +2011,21 @@ static PyObject *
_inP07ytdUVN(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_enum((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_enum)((TypeIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_type_is_scoped_enum(TypeIndex type)
+ */
+static PyObject *
+_inP07ytZtNk(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_type_is_scoped_enum)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1875,7 +2039,7 @@ static PyObject *
_inP07ytihbt(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_enum_values((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_enum_values)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1894,7 +2058,7 @@ _inP07ytbyPY(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- char const *return_value = ::interrogate_type_enum_value_name((TypeIndex)param0, (int)param1);
+ char const *return_value = (::interrogate_type_enum_value_name)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1913,7 +2077,7 @@ _inP07ytAaT6(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- char const *return_value = ::interrogate_type_enum_value_scoped_name((TypeIndex)param0, (int)param1);
+ char const *return_value = (::interrogate_type_enum_value_scoped_name)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1932,7 +2096,7 @@ _inP07ytgL9q(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- char const *return_value = ::interrogate_type_enum_value_comment((TypeIndex)param0, (int)param1);
+ char const *return_value = (::interrogate_type_enum_value_comment)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromString(return_value);
#else
@@ -1951,7 +2115,7 @@ _inP07ytWB97(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- int return_value = ::interrogate_type_enum_value((TypeIndex)param0, (int)param1);
+ int return_value = (::interrogate_type_enum_value)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -1969,7 +2133,7 @@ static PyObject *
_inP07ytDUAl(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_struct((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_struct)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1983,7 +2147,7 @@ static PyObject *
_inP07yt1_Kf(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_class((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_class)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -1997,7 +2161,7 @@ static PyObject *
_inP07yt98lD(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_union((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_union)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2011,7 +2175,7 @@ static PyObject *
_inP07yt9SHr(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_fully_defined((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_fully_defined)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2025,7 +2189,7 @@ static PyObject *
_inP07ytdiZP(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_is_unpublished((TypeIndex)param0);
+ bool return_value = (::interrogate_type_is_unpublished)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2039,7 +2203,7 @@ static PyObject *
_inP07ytTdER(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_constructors((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_constructors)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2058,7 +2222,7 @@ _inP07ytYO56(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionIndex return_value = ::interrogate_type_get_constructor((TypeIndex)param0, (int)param1);
+ FunctionIndex return_value = (::interrogate_type_get_constructor)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2076,7 +2240,7 @@ static PyObject *
_inP07ytxtCG(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_has_destructor((TypeIndex)param0);
+ bool return_value = (::interrogate_type_has_destructor)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2090,7 +2254,7 @@ static PyObject *
_inP07yt_EB2(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- bool return_value = ::interrogate_type_destructor_is_inherited((TypeIndex)param0);
+ bool return_value = (::interrogate_type_destructor_is_inherited)((TypeIndex)param0);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2104,7 +2268,7 @@ static PyObject *
_inP07ytEG1l(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- FunctionIndex return_value = ::interrogate_type_get_destructor((TypeIndex)param0);
+ FunctionIndex return_value = (::interrogate_type_get_destructor)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2122,7 +2286,7 @@ static PyObject *
_inP07yt7tUq(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_elements((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_elements)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2141,7 +2305,7 @@ _inP07ytyStU(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- ElementIndex return_value = ::interrogate_type_get_element((TypeIndex)param0, (int)param1);
+ ElementIndex return_value = (::interrogate_type_get_element)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2159,7 +2323,7 @@ static PyObject *
_inP07ytdM85(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_methods((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_methods)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2178,7 +2342,7 @@ _inP07ytk_GN(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionIndex return_value = ::interrogate_type_get_method((TypeIndex)param0, (int)param1);
+ FunctionIndex return_value = (::interrogate_type_get_method)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2196,7 +2360,7 @@ static PyObject *
_inP07yt8QjG(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_make_seqs((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_make_seqs)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2215,7 +2379,7 @@ _inP07ytyMtj(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- MakeSeqIndex return_value = ::interrogate_type_get_make_seq((TypeIndex)param0, (int)param1);
+ MakeSeqIndex return_value = (::interrogate_type_get_make_seq)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2233,7 +2397,7 @@ static PyObject *
_inP07ytHDtN(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_casts((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_casts)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2252,7 +2416,7 @@ _inP07ytHFjA(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionIndex return_value = ::interrogate_type_get_cast((TypeIndex)param0, (int)param1);
+ FunctionIndex return_value = (::interrogate_type_get_cast)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2270,7 +2434,7 @@ static PyObject *
_inP07yt_NPR(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_derivations((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_derivations)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2289,7 +2453,7 @@ _inP07ytcTOH(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- TypeIndex return_value = ::interrogate_type_get_derivation((TypeIndex)param0, (int)param1);
+ TypeIndex return_value = (::interrogate_type_get_derivation)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2299,6 +2463,20 @@ _inP07ytcTOH(PyObject *, PyObject *args) {
return nullptr;
}
+/*
+ * Python simple wrapper for
+ * bool interrogate_type_is_final(TypeIndex type)
+ */
+static PyObject *
+_inP07ytC5Uk(PyObject *, PyObject *args) {
+ int param0;
+ if (PyArg_ParseTuple(args, "i", ¶m0)) {
+ bool return_value = (::interrogate_type_is_final)((TypeIndex)param0);
+ return PyBool_FromLong(return_value);
+ }
+ return nullptr;
+}
+
/*
* Python simple wrapper for
* bool interrogate_type_derivation_has_upcast(TypeIndex type, int n)
@@ -2308,7 +2486,7 @@ _inP07ythdU7(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- bool return_value = ::interrogate_type_derivation_has_upcast((TypeIndex)param0, (int)param1);
+ bool return_value = (::interrogate_type_derivation_has_upcast)((TypeIndex)param0, (int)param1);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2323,7 +2501,7 @@ _inP07ytQPxU(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionIndex return_value = ::interrogate_type_get_upcast((TypeIndex)param0, (int)param1);
+ FunctionIndex return_value = (::interrogate_type_get_upcast)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2342,7 +2520,7 @@ _inP07ytO7Pz(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- bool return_value = ::interrogate_type_derivation_downcast_is_impossible((TypeIndex)param0, (int)param1);
+ bool return_value = (::interrogate_type_derivation_downcast_is_impossible)((TypeIndex)param0, (int)param1);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2357,7 +2535,7 @@ _inP07ytvu_E(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- bool return_value = ::interrogate_type_derivation_has_downcast((TypeIndex)param0, (int)param1);
+ bool return_value = (::interrogate_type_derivation_has_downcast)((TypeIndex)param0, (int)param1);
return PyBool_FromLong(return_value);
}
return nullptr;
@@ -2372,7 +2550,7 @@ _inP07ytxGUt(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- FunctionIndex return_value = ::interrogate_type_get_downcast((TypeIndex)param0, (int)param1);
+ FunctionIndex return_value = (::interrogate_type_get_downcast)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2390,7 +2568,7 @@ static PyObject *
_inP07ytzM1P(PyObject *, PyObject *args) {
int param0;
if (PyArg_ParseTuple(args, "i", ¶m0)) {
- int return_value = ::interrogate_type_number_of_nested_types((TypeIndex)param0);
+ int return_value = (::interrogate_type_number_of_nested_types)((TypeIndex)param0);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2409,7 +2587,7 @@ _inP07ytoY5L(PyObject *, PyObject *args) {
int param0;
int param1;
if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) {
- TypeIndex return_value = ::interrogate_type_get_nested_type((TypeIndex)param0, (int)param1);
+ TypeIndex return_value = (::interrogate_type_get_nested_type)((TypeIndex)param0, (int)param1);
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(return_value);
#else
@@ -2427,7 +2605,7 @@ static PyObject *
_inP07yte_7S(PyObject *, PyObject *args) {
char *param0;
if (PyArg_ParseTuple(args, "s", ¶m0)) {
- ::interrogate_request_database((char const *)param0);
+ (::interrogate_request_database)((char const *)param0);
return Py_BuildValue("");
}
return nullptr;
@@ -2441,7 +2619,7 @@ static PyObject *
_inP07ytw_15(PyObject *, PyObject *args) {
Py_ssize_t param0;
if (PyArg_ParseTuple(args, "n", ¶m0)) {
- ::interrogate_request_module((InterrogateModuleDef *)param0);
+ (::interrogate_request_module)((InterrogateModuleDef *)param0);
return Py_BuildValue("");
}
return nullptr;
@@ -2449,145 +2627,156 @@ _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_is_unary_op", &_inP07ytbmxJ, METH_VARARGS, nullptr },
+ { "interrogate_function_is_operator_typecast", &_inP07ytY8Lc, METH_VARARGS, nullptr },
+ { "interrogate_function_is_constructor", &_inP07ytJAAI, METH_VARARGS, nullptr },
+ { "interrogate_function_is_destructor", &_inP07yt0UXw, 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_parameter_is_optional", &_inP07ytDgOY, 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_make_seq_num_getter", &_inP07ytrppS, METH_VARARGS, nullptr },
+ { "interrogate_make_seq_element_getter", &_inP07ytO50x, 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_array", &_inP07ytoxqc, METH_VARARGS, nullptr },
+ { "interrogate_type_array_size", &_inP07ytZQIS, METH_VARARGS, nullptr },
+ { "interrogate_type_is_enum", &_inP07ytdUVN, METH_VARARGS, nullptr },
+ { "interrogate_type_is_scoped_enum", &_inP07ytZtNk, 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_is_final", &_inP07ytC5Uk, 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
diff --git a/dtool/src/cppparser/cppManifest.cxx b/dtool/src/cppparser/cppManifest.cxx
index ae8784cdf9..197213df36 100644
--- a/dtool/src/cppparser/cppManifest.cxx
+++ b/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;
diff --git a/dtool/src/cppparser/cppManifest.h b/dtool/src/cppparser/cppManifest.h
index 486b287728..3fe0f1b4a1 100644
--- a/dtool/src/cppparser/cppManifest.h
+++ b/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;
diff --git a/dtool/src/cppparser/cppNamespace.cxx b/dtool/src/cppparser/cppNamespace.cxx
index 9a6f608f49..a72f4b093d 100644
--- a/dtool/src/cppparser/cppNamespace.cxx
+++ b/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)
{
}
diff --git a/dtool/src/cppparser/cppStructType.cxx b/dtool/src/cppparser/cppStructType.cxx
index 745abab586..e08d4a4a4e 100644
--- a/dtool/src/cppparser/cppStructType.cxx
+++ b/dtool/src/cppparser/cppStructType.cxx
@@ -58,8 +58,8 @@ CPPStructType(const CPPStructType ©) :
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;
}
diff --git a/dtool/src/dtoolbase/CMakeLists.txt b/dtool/src/dtoolbase/CMakeLists.txt
index aea0a1196f..72d69b7e07 100644
--- a/dtool/src/dtoolbase/CMakeLists.txt
+++ b/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
@@ -92,9 +94,14 @@ add_component_library(p3dtoolbase NOINIT SYMBOL BUILDING_DTOOL_DTOOLBASE
target_include_directories(p3dtoolbase PUBLIC
$
$)
-target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS)
+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
diff --git a/dtool/src/dtoolbase/deletedBufferChain.I b/dtool/src/dtoolbase/deletedBufferChain.I
index 9eb17bd4fb..ac5243de76 100644
--- a/dtool/src/dtoolbase/deletedBufferChain.I
+++ b/dtool/src/dtoolbase/deletedBufferChain.I
@@ -27,7 +27,7 @@ validate(void *ptr) {
#if defined(USE_DELETEDCHAINFLAG) && defined(USE_DELETED_CHAIN)
const ObjectNode *obj = buffer_to_node(ptr);
- return AtomicAdjust::get(obj->_flag) == DCF_alive;
+ return obj->_flag.load(std::memory_order_relaxed) == DCF_alive;
#else
return true;
#endif // USE_DELETEDCHAINFLAG
diff --git a/dtool/src/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx
index d009510ad3..d38b833ea1 100644
--- a/dtool/src/dtoolbase/deletedBufferChain.cxx
+++ b/dtool/src/dtoolbase/deletedBufferChain.cxx
@@ -50,8 +50,8 @@ allocate(size_t size, TypeHandle type_handle) {
_lock.unlock();
#ifdef USE_DELETEDCHAINFLAG
- assert(obj->_flag == (AtomicAdjust::Integer)DCF_deleted);
- obj->_flag = DCF_alive;
+ DeletedChainFlag orig_flag = obj->_flag.exchange(DCF_alive, std::memory_order_relaxed);
+ assert(orig_flag == DCF_deleted);
#endif // USE_DELETEDCHAINFLAG
void *ptr = node_to_buffer(obj);
@@ -75,7 +75,7 @@ allocate(size_t size, TypeHandle type_handle) {
obj = (ObjectNode *)(aligned - flag_reserved_bytes);
#ifdef USE_DELETEDCHAINFLAG
- obj->_flag = DCF_alive;
+ obj->_flag.store(DCF_alive, std::memory_order_relaxed);
#endif // USE_DELETEDCHAINFLAG
void *ptr = node_to_buffer(obj);
@@ -116,14 +116,16 @@ deallocate(void *ptr, TypeHandle type_handle) {
ObjectNode *obj = buffer_to_node(ptr);
#ifdef USE_DELETEDCHAINFLAG
- AtomicAdjust::Integer orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_alive, DCF_deleted);
+ DeletedChainFlag orig_flag = DCF_alive;
+ if (UNLIKELY(!obj->_flag.compare_exchange_strong(orig_flag, DCF_deleted,
+ std::memory_order_relaxed))) {
+ // If this assertion is triggered, you double-deleted an object.
+ assert(orig_flag != DCF_deleted);
- // If this assertion is triggered, you double-deleted an object.
- assert(orig_flag != (AtomicAdjust::Integer)DCF_deleted);
-
- // If this assertion is triggered, you tried to delete an object that was
- // never allocated, or you have heap corruption.
- assert(orig_flag == (AtomicAdjust::Integer)DCF_alive);
+ // If this assertion is triggered, you tried to delete an object that was
+ // never allocated, or you have heap corruption.
+ assert(orig_flag == DCF_alive);
+ }
#endif // USE_DELETEDCHAINFLAG
_lock.lock();
diff --git a/dtool/src/dtoolbase/deletedBufferChain.h b/dtool/src/dtoolbase/deletedBufferChain.h
index 10ac5847fd..3bfd8cc071 100644
--- a/dtool/src/dtoolbase/deletedBufferChain.h
+++ b/dtool/src/dtoolbase/deletedBufferChain.h
@@ -20,6 +20,7 @@
#include "atomicAdjust.h"
#include "numeric_types.h"
#include "typeHandle.h"
+#include "patomic.h"
#include
// Though it's tempting, it doesn't seem to be possible to implement
@@ -37,7 +38,7 @@
#endif // NDEBUG
#ifdef USE_DELETEDCHAINFLAG
-enum DeletedChainFlag {
+enum DeletedChainFlag : unsigned int {
DCF_deleted = 0xfeedba0f,
DCF_alive = 0x12487654,
};
@@ -73,7 +74,7 @@ private:
// In development mode, we piggyback this extra data. This is maintained
// out-of-band from the actual pointer returned, so we can safely use this
// flag to indicate the difference between allocated and freed pointers.
- TVOLATILE AtomicAdjust::Integer _flag;
+ patomic _flag;
#endif
// This pointer sits within the buffer, in the same space referenced by
diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h
index 67dee423da..738bc55482 100644
--- a/dtool/src/dtoolbase/dtoolbase.h
+++ b/dtool/src/dtoolbase/dtoolbase.h
@@ -375,6 +375,10 @@ typedef struct _object PyObject;
// This specialized malloc implementation can perform the required alignment.
#undef MEMORY_HOOK_DO_ALIGN
+#elif defined(USE_MEMORY_MIMALLOC)
+// This one does, too.
+#undef MEMORY_HOOK_DO_ALIGN
+
#elif defined(USE_MEMORY_PTMALLOC2)
// But not this one. For some reason it crashes when we try to build it with
// alignment 16. So if we're using ptmalloc2, we need to enforce alignment
@@ -385,6 +389,12 @@ typedef struct _object PyObject;
// The OS-provided malloc implementation will do the required alignment.
#undef MEMORY_HOOK_DO_ALIGN
+#elif defined(HAVE_MIMALLOC) && defined(_WIN32)
+// Prefer mimalloc on Windows, if we have it. It is significantly faster than
+// standard malloc, supports multi-threading well and does the alignment too.
+#undef MEMORY_HOOK_DO_ALIGN
+#define USE_MEMORY_MIMALLOC 1
+
#elif defined(MEMORY_HOOK_DO_ALIGN)
// We need memory alignment, and we're willing to provide it ourselves.
@@ -426,7 +436,7 @@ typedef struct _object PyObject;
#endif
/* Determine our memory-allocation requirements. */
-#if defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN)
+#if defined(USE_MEMORY_MIMALLOC) || defined(USE_MEMORY_PTMALLOC2) || defined(USE_MEMORY_DLMALLOC) || defined(DO_MEMORY_USAGE) || defined(MEMORY_HOOK_DO_ALIGN)
/* In this case we have some custom memory management requirements. */
#else
/* Otherwise, if we have no custom memory management needs at all, we
diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h
index de8baeedab..d19db7f5ef 100644
--- a/dtool/src/dtoolbase/dtoolbase_cc.h
+++ b/dtool/src/dtoolbase/dtoolbase_cc.h
@@ -49,8 +49,6 @@
// interrogate pass (CPPPARSER isn't defined), this maps to public.
#define PUBLISHED __published
-#define PHAVE_ATOMIC 1
-
typedef int ios_openmode;
typedef int ios_fmtflags;
typedef int ios_iostate;
@@ -112,9 +110,6 @@ typedef std::ios::seekdir ios_seekdir;
#define INLINE inline
#endif
-// Expect that we have access to the header.
-#define PHAVE_ATOMIC 1
-
// Determine the availability of C++11 features.
#if defined(_MSC_VER) && _MSC_VER < 1900 // Visual Studio 2015
#error Microsoft Visual C++ 2015 or later is required to compile Panda3D.
diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx
index 4abc0661d8..8712099ab7 100644
--- a/dtool/src/dtoolbase/memoryHook.cxx
+++ b/dtool/src/dtoolbase/memoryHook.cxx
@@ -51,6 +51,18 @@ static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0,
#if defined(CPPPARSER)
+#elif defined(USE_MEMORY_MIMALLOC)
+
+// mimalloc is a modern memory manager by Microsoft that is very fast as well
+// as thread-safe.
+
+#include "mimalloc.h"
+
+#define call_malloc mi_malloc
+#define call_realloc mi_realloc
+#define call_free mi_free
+#undef MEMORY_HOOK_MALLOC_LOCK
+
#elif defined(USE_MEMORY_DLMALLOC)
// Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very
diff --git a/dtool/src/dtoolbase/mutexSpinlockImpl.h b/dtool/src/dtoolbase/mutexSpinlockImpl.h
index cd858f5551..ca84f58ea9 100644
--- a/dtool/src/dtoolbase/mutexSpinlockImpl.h
+++ b/dtool/src/dtoolbase/mutexSpinlockImpl.h
@@ -19,9 +19,7 @@
#ifdef MUTEX_SPINLOCK
-#ifdef PHAVE_ATOMIC
#include
-#endif
/**
* Uses a simple user-space spinlock to implement a mutex. It is usually not
diff --git a/dtool/src/dtoolbase/mutexWin32Impl.cxx b/dtool/src/dtoolbase/mutexWin32Impl.cxx
index 04d3ac0ec9..bb2a1e33d8 100644
--- a/dtool/src/dtoolbase/mutexWin32Impl.cxx
+++ b/dtool/src/dtoolbase/mutexWin32Impl.cxx
@@ -13,7 +13,7 @@
#include "selectThreadImpl.h"
-#if defined(_WIN32) && !defined(CPPPARSER)
+#ifdef _WIN32
#include "mutexWin32Impl.h"
diff --git a/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx b/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx
index 8cd964b4fd..1840bc7e25 100644
--- a/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx
+++ b/dtool/src/dtoolbase/p3dtoolbase_composite2.cxx
@@ -1,3 +1,4 @@
+#include "patomic.cxx"
#include "mutexPosixImpl.cxx"
#include "mutexWin32Impl.cxx"
#include "mutexSpinlockImpl.cxx"
diff --git a/dtool/src/dtoolbase/patomic.I b/dtool/src/dtoolbase/patomic.I
new file mode 100644
index 0000000000..80ebcd4de3
--- /dev/null
+++ b/dtool/src/dtoolbase/patomic.I
@@ -0,0 +1,472 @@
+/**
+ * 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.I
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
+/**
+ * Value initializer.
+ */
+template
+constexpr patomic::
+patomic(T desired) noexcept : _value(desired) {
+}
+
+/**
+ * Returns true if this is a lock free type (which it always is).
+ */
+template
+ALWAYS_INLINE bool patomic::
+is_lock_free() const noexcept {
+ return true;
+}
+
+/**
+ * Returns the stored value.
+ */
+template
+ALWAYS_INLINE T patomic::
+load(std::memory_order order) const noexcept {
+ return _value;
+}
+
+/**
+ * Returns the stored value.
+ */
+template
+ALWAYS_INLINE patomic::
+operator T() const noexcept {
+ return _value;
+}
+
+/**
+ * Changes the stored value.
+ */
+template
+ALWAYS_INLINE void patomic::
+store(T desired, std::memory_order order) noexcept {
+ _value = desired;
+}
+
+/**
+ * Changes the stored value.
+ */
+template
+ALWAYS_INLINE T patomic::
+operator=(T desired) noexcept {
+ _value = desired;
+}
+
+/**
+ * Changes the stored value, returning the previous value.
+ */
+template
+ALWAYS_INLINE T patomic::
+exchange(T desired, std::memory_order) noexcept {
+ T current = _value;
+ _value = desired;
+ return current;
+}
+
+/**
+ * Sets the desired value if the current value is as the first argument.
+ * If it is not, the current value is written to expected.
+ */
+template
+ALWAYS_INLINE bool patomic::
+compare_exchange_weak(T &expected, T desired,
+ std::memory_order, std::memory_order) noexcept {
+ T current = _value;
+ if (_value == expected) {
+ _value = desired;
+ return true;
+ } else {
+ expected = current;
+ return false;
+ }
+}
+
+/**
+ * Sets the desired value if the current value is as the first argument.
+ * If it is not, the current value is written to expected.
+ */
+template
+ALWAYS_INLINE bool patomic::
+compare_exchange_strong(T &expected, T desired,
+ std::memory_order, std::memory_order) noexcept {
+ T current = _value;
+ if (_value == expected) {
+ _value = desired;
+ return true;
+ } else {
+ expected = current;
+ return false;
+ }
+}
+
+/**
+ * Adds to the stored value, returns the old value.
+ */
+template
+ALWAYS_INLINE T patomic::
+fetch_add(T arg, std::memory_order) noexcept {
+ T old = _value;
+ _value += arg;
+ return old;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+fetch_sub(T arg, std::memory_order) noexcept {
+ T old = _value;
+ _value -= arg;
+ return old;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+fetch_and(T arg, std::memory_order) noexcept {
+ T old = _value;
+ _value &= arg;
+ return old;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+fetch_or(T arg, std::memory_order) noexcept {
+ T old = _value;
+ _value |= arg;
+ return old;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+fetch_xor(T arg, std::memory_order) noexcept {
+ T old = _value;
+ _value ^= arg;
+ return old;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator ++(int) noexcept {
+ return _value++;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator --(int) noexcept {
+ return _value--;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator ++() noexcept {
+ return ++_value;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator --() noexcept {
+ return --_value;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator +=(T arg) noexcept {
+ return _value += arg;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator -=(T arg) noexcept {
+ return _value -= arg;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator &=(T arg) noexcept {
+ return _value &= arg;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+operator |=(T arg) noexcept {
+ return _value |= arg;
+}
+
+/**
+ *
+ */
+template
+ALWAYS_INLINE T patomic::
+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(0u) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_unsigned_lock_free::
+patomic_unsigned_lock_free(uint32_t desired) noexcept :
+ patomic(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(0) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_signed_lock_free::
+patomic_signed_lock_free(int32_t desired) noexcept :
+ patomic(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 {
+ return _value.exchange(1u, order) != 0u;
+}
+
+/**
+ * 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::
+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
+}
diff --git a/dtool/src/dtoolbase/patomic.cxx b/dtool/src/dtoolbase/patomic.cxx
new file mode 100644
index 0000000000..58d52a7e42
--- /dev/null
+++ b/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
+
+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{}(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{}(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{}(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{}(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
diff --git a/dtool/src/dtoolbase/patomic.h b/dtool/src/dtoolbase/patomic.h
new file mode 100644
index 0000000000..604d028392
--- /dev/null
+++ b/dtool/src/dtoolbase/patomic.h
@@ -0,0 +1,175 @@
+/**
+ * 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.h
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#ifndef PATOMIC_H
+#define PATOMIC_H
+
+#include "dtoolbase.h"
+#include "selectThreadImpl.h"
+
+#include
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include
+#endif
+
+#ifdef __linux__
+#include
+#include
+#include
+#endif
+
+#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
+
+/**
+ * Dummy implementation of std::atomic that does not do any atomic operations,
+ * used when compiling without HAVE_THREADS or with SIMPLE_THREADS.
+ */
+template
+struct patomic {
+ using value_type = T;
+
+ constexpr patomic() noexcept = default;
+ constexpr patomic(T desired) noexcept;
+
+ ALWAYS_INLINE patomic(const patomic &) = delete;
+ ALWAYS_INLINE patomic &operator=(const patomic &) = delete;
+
+ static constexpr bool is_always_lock_free = true;
+ ALWAYS_INLINE bool is_lock_free() const noexcept;
+
+ ALWAYS_INLINE T load(std::memory_order order = std::memory_order_seq_cst) const noexcept;
+ ALWAYS_INLINE operator T() const noexcept;
+
+ ALWAYS_INLINE void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE T operator=(T desired) noexcept;
+
+ ALWAYS_INLINE T exchange(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;
+
+ ALWAYS_INLINE bool compare_exchange_weak(T &expected, T desired,
+ std::memory_order success = std::memory_order_seq_cst,
+ std::memory_order failure = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE bool compare_exchange_strong(T &expected, T desired,
+ std::memory_order success = std::memory_order_seq_cst,
+ std::memory_order failure = std::memory_order_seq_cst) noexcept;
+
+ ALWAYS_INLINE T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE T fetch_and(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE T fetch_or(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept;
+ ALWAYS_INLINE T fetch_xor(T arg, std::memory_order order = std::memory_order_seq_cst) noexcept;
+
+ ALWAYS_INLINE T operator ++(int) noexcept;
+ ALWAYS_INLINE T operator --(int) noexcept;
+ ALWAYS_INLINE T operator ++() noexcept;
+ ALWAYS_INLINE T operator --() noexcept;
+ ALWAYS_INLINE T operator +=(T arg) noexcept;
+ ALWAYS_INLINE T operator -=(T arg) noexcept;
+ ALWAYS_INLINE T operator &=(T arg) noexcept;
+ ALWAYS_INLINE T operator |=(T arg) noexcept;
+ ALWAYS_INLINE T operator ^=(T arg) noexcept;
+
+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
+using patomic = std::atomic;
+
+#define patomic_thread_fence(order) (std::atomic_thread_fence((order)))
+
+#endif
+
+/**
+ * Implementation of atomic_unsigned_lock_free with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_unsigned_lock_free : public patomic {
+public:
+ typedef uint32_t value_type;
+
+ constexpr patomic_unsigned_lock_free() noexcept;
+ constexpr patomic_unsigned_lock_free(uint32_t desired) noexcept;
+
+ 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;
+};
+
+/**
+ * Implementation of atomic_signed_lock_free with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_signed_lock_free : public patomic {
+public:
+ typedef int32_t value_type;
+
+ constexpr patomic_signed_lock_free() noexcept;
+ constexpr patomic_signed_lock_free(int32_t desired) noexcept;
+
+ 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;
+};
+
+/**
+ * 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;
+
+ patomic_flag(const patomic_flag &) = delete;
+ patomic_flag &operator=(const patomic_flag &) = delete;
+
+ 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
diff --git a/dtool/src/dtoolbase/typeHandle.cxx b/dtool/src/dtoolbase/typeHandle.cxx
index c72c3f9d99..6b7ee5aaf7 100644
--- a/dtool/src/dtoolbase/typeHandle.cxx
+++ b/dtool/src/dtoolbase/typeHandle.cxx
@@ -13,7 +13,6 @@
#include "typeHandle.h"
#include "typeRegistryNode.h"
-#include "atomicAdjust.h"
/**
* Returns the total allocated memory used by objects of this type, for the
@@ -29,7 +28,7 @@ get_memory_usage(MemoryClass memory_class) const {
} else {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- return (size_t)AtomicAdjust::get(rnode->_memory_usage[memory_class]);
+ return rnode->_memory_usage[memory_class].load(std::memory_order_relaxed);
}
#endif // DO_MEMORY_USAGE
return 0;
@@ -48,10 +47,8 @@ inc_memory_usage(MemoryClass memory_class, size_t size) {
if ((*this) != TypeHandle::none()) {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- AtomicAdjust::add(rnode->_memory_usage[memory_class], (AtomicAdjust::Integer)size);
- // cerr << *this << ".inc(" << memory_class << ", " << size << ") -> " <<
- // rnode->_memory_usage[memory_class] << "\n";
- if (rnode->_memory_usage[memory_class] < 0) {
+ size_t prev = rnode->_memory_usage[memory_class].fetch_add(size, std::memory_order_relaxed);
+ if (prev + size < prev) {
std::cerr << "Memory usage overflow for type " << rnode->_name << ".\n";
abort();
}
@@ -72,10 +69,8 @@ dec_memory_usage(MemoryClass memory_class, size_t size) {
if ((*this) != TypeHandle::none()) {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- AtomicAdjust::add(rnode->_memory_usage[memory_class], -(AtomicAdjust::Integer)size);
- // cerr << *this << ".dec(" << memory_class << ", " << size << ") -> " <<
- // rnode->_memory_usage[memory_class] << "\n";
- assert(rnode->_memory_usage[memory_class] >= 0);
+ size_t prev = rnode->_memory_usage[memory_class].fetch_sub(size, std::memory_order_relaxed);
+ assert(prev - size <= prev);
}
#endif // DO_MEMORY_USAGE
}
@@ -97,8 +92,8 @@ allocate_array(size_t size) {
#endif
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)alloc_size);
- if (rnode->_memory_usage[MC_array] < 0) {
+ size_t prev = rnode->_memory_usage[MC_array].fetch_add(alloc_size, std::memory_order_relaxed);
+ if (prev + size < prev) {
std::cerr << "Memory usage overflow for type " << rnode->_name << ".\n";
abort();
}
@@ -124,8 +119,11 @@ reallocate_array(void *old_ptr, size_t size) {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- AtomicAdjust::add(rnode->_memory_usage[MC_array], (AtomicAdjust::Integer)new_size - (AtomicAdjust::Integer)old_size);
- assert(rnode->_memory_usage[MC_array] >= 0);
+ if (new_size > old_size) {
+ rnode->_memory_usage[MC_array].fetch_add(new_size - old_size, std::memory_order_relaxed);
+ } else {
+ rnode->_memory_usage[MC_array].fetch_sub(old_size - new_size, std::memory_order_relaxed);
+ }
}
#else
void *new_ptr = PANDA_REALLOC_ARRAY(old_ptr, size);
@@ -146,8 +144,8 @@ deallocate_array(void *ptr) {
if ((*this) != TypeHandle::none()) {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
assert(rnode != nullptr);
- AtomicAdjust::add(rnode->_memory_usage[MC_array], -(AtomicAdjust::Integer)alloc_size);
- assert(rnode->_memory_usage[MC_array] >= 0);
+ size_t prev = rnode->_memory_usage[MC_array].fetch_sub(alloc_size, std::memory_order_relaxed);
+ assert(prev - alloc_size <= prev);
}
#endif // DO_MEMORY_USAGE
PANDA_FREE_ARRAY(ptr);
diff --git a/dtool/src/dtoolbase/typeHandle_ext.cxx b/dtool/src/dtoolbase/typeHandle_ext.cxx
index be4b1e654c..cefe62e154 100644
--- a/dtool/src/dtoolbase/typeHandle_ext.cxx
+++ b/dtool/src/dtoolbase/typeHandle_ext.cxx
@@ -38,7 +38,6 @@ make(PyTypeObject *tp) {
PyObject *Extension::
__reduce__() const {
extern struct Dtool_PyTypedObject Dtool_TypeHandle;
- extern struct Dtool_PyTypedObject Dtool_TypeRegistry;
if (!*_this) {
PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_TypeHandle, "none");
diff --git a/dtool/src/dtoolbase/typeRegistryNode.cxx b/dtool/src/dtoolbase/typeRegistryNode.cxx
index 19b4629236..291a6ec95a 100644
--- a/dtool/src/dtoolbase/typeRegistryNode.cxx
+++ b/dtool/src/dtoolbase/typeRegistryNode.cxx
@@ -23,10 +23,9 @@ bool TypeRegistryNode::_paranoid_inheritance = false;
*/
TypeRegistryNode::
TypeRegistryNode(TypeHandle handle, const std::string &name, TypeHandle &ref) :
- _handle(handle), _name(name), _ref(ref)
+ _handle(handle), _name(name), _ref(ref), _memory_usage{}
{
clear_subtree();
- memset(_memory_usage, 0, sizeof(_memory_usage));
}
/**
diff --git a/dtool/src/dtoolbase/typeRegistryNode.h b/dtool/src/dtoolbase/typeRegistryNode.h
index 7dd7f387cc..6ff2275103 100644
--- a/dtool/src/dtoolbase/typeRegistryNode.h
+++ b/dtool/src/dtoolbase/typeRegistryNode.h
@@ -18,6 +18,7 @@
#include "typeHandle.h"
#include "numeric_types.h"
+#include "patomic.h"
#include
#include
@@ -50,7 +51,7 @@ public:
Classes _child_classes;
PyObject *_python_type = nullptr;
- AtomicAdjust::Integer _memory_usage[TypeHandle::MC_limit];
+ patomic _memory_usage[TypeHandle::MC_limit];
static bool _paranoid_inheritance;
diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx
index 4214505faf..d39ae70a4e 100644
--- a/dtool/src/dtoolutil/executionEnvironment.cxx
+++ b/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
}
diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx
index b3b84a84d7..f676220158 100644
--- a/dtool/src/dtoolutil/filename.cxx
+++ b/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
diff --git a/dtool/src/dtoolutil/pandaSystem.cxx b/dtool/src/dtoolutil/pandaSystem.cxx
index 1d54cdd151..96491c5457 100644
--- a/dtool/src/dtoolutil/pandaSystem.cxx
+++ b/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
diff --git a/dtool/src/dtoolutil/panda_getopt_impl.cxx b/dtool/src/dtoolutil/panda_getopt_impl.cxx
index 2779a7906a..f2ca4ed65c 100644
--- a/dtool/src/dtoolutil/panda_getopt_impl.cxx
+++ b/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;
diff --git a/dtool/src/dtoolutil/pfstreamBuf.cxx b/dtool/src/dtoolutil/pfstreamBuf.cxx
index a5585ee3cd..b2fcf2838e 100644
--- a/dtool/src/dtoolutil/pfstreamBuf.cxx
+++ b/dtool/src/dtoolutil/pfstreamBuf.cxx
@@ -331,8 +331,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
@@ -345,8 +346,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
diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx
index a3272b2c70..2f777f84eb 100644
--- a/dtool/src/interrogate/functionRemap.cxx
+++ b/dtool/src/interrogate/functionRemap.cxx
@@ -327,6 +327,9 @@ make_wrapper_entry(FunctionIndex function_index) {
if ((*pi)._has_name) {
param._parameter_flags |= InterrogateFunctionWrapper::PF_has_name;
}
+ if ((*pi)._remap->has_default_value()) {
+ param._parameter_flags |= InterrogateFunctionWrapper::PF_is_optional;
+ }
iwrapper._parameters.push_back(param);
}
diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx
index 0d4d56dfc1..3c5d7f6b57 100644
--- a/dtool/src/interrogate/interfaceMaker.cxx
+++ b/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);
diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx
index 45ecf1b0fc..a617ba4e76 100644
--- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx
+++ b/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";
diff --git a/dtool/src/interrogate/interfaceMakerPythonObj.cxx b/dtool/src/interrogate/interfaceMakerPythonObj.cxx
index fa0e35eaea..4182b0c9a1 100644
--- a/dtool/src/interrogate/interfaceMakerPythonObj.cxx
+++ b/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"
diff --git a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx b/dtool/src/interrogate/interfaceMakerPythonSimple.cxx
index 8a160216e8..cda042efd7 100644
--- a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx
+++ b/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"
diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx
index 8a9d9fe113..0c15cb4a3a 100644
--- a/dtool/src/interrogate/interrogate.cxx
+++ b/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);
diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx
index e94d4bda39..9a2f2a161d 100644
--- a/dtool/src/interrogate/interrogateBuilder.cxx
+++ b/dtool/src/interrogate/interrogateBuilder.cxx
@@ -1071,6 +1071,10 @@ scan_function(CPPInstance *function) {
return;
}
+ if (TypeManager::involves_rvalue_reference(ftype)) {
+ return;
+ }
+
get_function(function, "",
nullptr, scope,
InterrogateFunction::F_global);
@@ -1770,6 +1774,16 @@ get_function(CPPInstance *function, string description,
ifunction->_flags |= InterrogateFunction::F_operator_typecast;
}
+ if (ftype->_flags & CPPFunctionType::F_constructor) {
+ // This is a constructor.
+ ifunction->_flags |= InterrogateFunction::F_constructor;
+ }
+
+ if (ftype->_flags & CPPFunctionType::F_destructor) {
+ // This is a destructor.
+ ifunction->_flags |= InterrogateFunction::F_destructor;
+ }
+
if (function->_storage_class & CPPInstance::SC_virtual) {
// This is a virtual function.
ifunction->_flags |= InterrogateFunction::F_virtual;
@@ -2747,7 +2761,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted;
function->_vis = V_published;
- FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0);
+ FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(),
+ InterrogateFunction::F_constructor);
if (find(itype._constructors.begin(), itype._constructors.end(),
index) == itype._constructors.end()) {
itype._constructors.push_back(index);
@@ -2773,7 +2788,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted;
function->_vis = V_published;
- FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0);
+ FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(),
+ InterrogateFunction::F_constructor);
if (find(itype._constructors.begin(), itype._constructors.end(),
index) == itype._constructors.end()) {
itype._constructors.push_back(index);
@@ -2814,7 +2830,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
itype._destructor = get_function(function, "",
cpptype, cpptype->get_scope(),
- 0);
+ InterrogateFunction::F_destructor);
itype._flags |= InterrogateType::F_implicit_destructor;
}
}
@@ -2988,6 +3004,9 @@ define_method(CPPInstance *function, InterrogateType &itype,
// If it isn't, we should publish this method anyway.
}
+ if (TypeManager::involves_rvalue_reference(ftype)) {
+ return;
+ }
FunctionIndex index = get_function(function, "", struct_type, scope, 0);
if (index != 0) {
diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx
index 44154c65ea..dc5d6679de 100644
--- a/dtool/src/interrogate/typeManager.cxx
+++ b/dtool/src/interrogate/typeManager.cxx
@@ -2017,6 +2017,46 @@ involves_protected(CPPType *type) {
}
}
+/**
+ * Returns true if the type involves an rvalue reference.
+ */
+bool TypeManager::
+involves_rvalue_reference(CPPType *type) {
+ switch (type->get_subtype()) {
+ case CPPDeclaration::ST_const:
+ return involves_rvalue_reference(type->as_const_type()->_wrapped_around);
+
+ case CPPDeclaration::ST_reference:
+ return type->as_reference_type()->_value_category == CPPReferenceType::VC_rvalue;
+
+ case CPPDeclaration::ST_pointer:
+ return involves_rvalue_reference(type->as_pointer_type()->_pointing_at);
+
+ case CPPDeclaration::ST_function:
+ {
+ CPPFunctionType *ftype = type->as_function_type();
+ if (involves_rvalue_reference(ftype->_return_type)) {
+ return true;
+ }
+ const CPPParameterList::Parameters ¶ms =
+ ftype->_parameters->_parameters;
+ CPPParameterList::Parameters::const_iterator pi;
+ for (pi = params.begin(); pi != params.end(); ++pi) {
+ if (involves_rvalue_reference((*pi)->_type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ case CPPDeclaration::ST_typedef:
+ return involves_rvalue_reference(type->as_typedef_type()->_type);
+
+ default:
+ return false;
+ }
+}
+
/**
* Returns the type this pointer type points to.
*/
diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h
index f1aa2908b1..e9161dbd31 100644
--- a/dtool/src/interrogate/typeManager.h
+++ b/dtool/src/interrogate/typeManager.h
@@ -118,6 +118,7 @@ public:
static bool is_handle(CPPType *type);
static bool involves_unpublished(CPPType *type);
static bool involves_protected(CPPType *type);
+ static bool involves_rvalue_reference(CPPType *type);
static bool is_ostream(CPPType *type);
static bool is_pointer_to_ostream(CPPType *type);
diff --git a/dtool/src/interrogatedb/interrogateDatabase.cxx b/dtool/src/interrogatedb/interrogateDatabase.cxx
index a8e7b37a6c..afdb7a11f7 100644
--- a/dtool/src/interrogatedb/interrogateDatabase.cxx
+++ b/dtool/src/interrogatedb/interrogateDatabase.cxx
@@ -968,6 +968,17 @@ read_new(std::istream &in, InterrogateModuleDef *def) {
add_type(index, type);
num_types--;
+
+ // Older versions of interrogate were not setting these flags.
+ const InterrogateType &itype = get_type(index);
+ FunctionIndex dtor = itype.get_destructor();
+ if (dtor != 0) {
+ update_function(dtor)._flags |= InterrogateFunction::F_destructor;
+ }
+ for (int i = 0; i < itype.number_of_constructors(); ++i) {
+ FunctionIndex ctor = itype.get_constructor(i);
+ update_function(ctor)._flags |= InterrogateFunction::F_constructor;
+ }
}
}
diff --git a/dtool/src/interrogatedb/interrogateFunction.I b/dtool/src/interrogatedb/interrogateFunction.I
index 3834e60782..6c36fec5d3 100644
--- a/dtool/src/interrogatedb/interrogateFunction.I
+++ b/dtool/src/interrogatedb/interrogateFunction.I
@@ -54,6 +54,22 @@ is_operator_typecast() const {
return (_flags & F_operator_typecast) != 0;
}
+/**
+ * Returns true if the function is a constructor.
+ */
+INLINE bool InterrogateFunction::
+is_constructor() const {
+ return (_flags & F_constructor) != 0;
+}
+
+/**
+ * Returns true if the function is a destructor.
+ */
+INLINE bool InterrogateFunction::
+is_destructor() const {
+ return (_flags & F_destructor) != 0;
+}
+
/**
* Return the class that owns the method, if is_method() returns true.
*/
diff --git a/dtool/src/interrogatedb/interrogateFunction.h b/dtool/src/interrogatedb/interrogateFunction.h
index e89e76b80c..76f4f20ccd 100644
--- a/dtool/src/interrogatedb/interrogateFunction.h
+++ b/dtool/src/interrogatedb/interrogateFunction.h
@@ -38,6 +38,8 @@ public:
INLINE bool is_method() const;
INLINE bool is_unary_op() const;
INLINE bool is_operator_typecast() const;
+ INLINE bool is_constructor() const;
+ INLINE bool is_destructor() const;
INLINE TypeIndex get_class() const;
INLINE bool has_scoped_name() const;
@@ -70,7 +72,9 @@ private:
F_setter = 0x0020,
F_unary_op = 0x0040,
F_operator_typecast = 0x0080,
- F_item_assignment = 0x0100,
+ F_constructor = 0x0100,
+ F_destructor = 0x0200,
+ F_item_assignment = 0x0400,
};
int _flags;
@@ -98,6 +102,7 @@ public:
std::string _expression;
friend class InterrogateBuilder;
+ friend class InterrogateDatabase;
friend class InterfaceMakerC;
friend class InterfaceMakerPythonSimple;
friend class InterfaceMakerPythonNative;
diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.I b/dtool/src/interrogatedb/interrogateFunctionWrapper.I
index d636e32ebf..b0ec398623 100644
--- a/dtool/src/interrogatedb/interrogateFunctionWrapper.I
+++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.I
@@ -148,6 +148,17 @@ parameter_is_this(int n) const {
return false;
}
+/**
+ *
+ */
+INLINE bool InterrogateFunctionWrapper::
+parameter_is_optional(int n) const {
+ if (n >= 0 && n < (int)_parameters.size()) {
+ return (_parameters[n]._parameter_flags & PF_is_optional) != 0;
+ }
+ return false;
+}
+
/**
*
*/
diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.h b/dtool/src/interrogatedb/interrogateFunctionWrapper.h
index 43b01a9a79..29e59ed259 100644
--- a/dtool/src/interrogatedb/interrogateFunctionWrapper.h
+++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.h
@@ -45,6 +45,7 @@ public:
INLINE bool parameter_has_name(int n) const;
INLINE const std::string ¶meter_get_name(int n) const;
INLINE bool parameter_is_this(int n) const;
+ INLINE bool parameter_is_optional(int n) const;
INLINE const std::string &get_unique_name() const;
@@ -66,6 +67,7 @@ private:
enum ParameterFlags {
PF_has_name = 0x0001,
PF_is_this = 0x0002,
+ PF_is_optional = 0x0004,
};
int _flags;
diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx
index 3fc2053855..d7f4ce58dd 100644
--- a/dtool/src/interrogatedb/interrogate_interface.cxx
+++ b/dtool/src/interrogatedb/interrogate_interface.cxx
@@ -266,6 +266,31 @@ interrogate_function_class(FunctionIndex function) {
return InterrogateDatabase::get_ptr()->get_function(function).get_class();
}
+bool
+interrogate_function_is_unary_op(FunctionIndex function) {
+ // cerr << "interrogate_function_is_unary_op(" << function << ")\n";
+ return InterrogateDatabase::get_ptr()->get_function(function).is_unary_op();
+}
+
+bool
+interrogate_function_is_operator_typecast(FunctionIndex function) {
+ // cerr << "interrogate_function_is_operator_typecast(" << function <<
+ // ")\n";
+ return InterrogateDatabase::get_ptr()->get_function(function).is_operator_typecast();
+}
+
+bool
+interrogate_function_is_constructor(FunctionIndex function) {
+ // cerr << "interrogate_function_is_constructor(" << function << ")\n";
+ return InterrogateDatabase::get_ptr()->get_function(function).is_constructor();
+}
+
+bool
+interrogate_function_is_destructor(FunctionIndex function) {
+ // cerr << "interrogate_function_is_destructor(" << function << ")\n";
+ return InterrogateDatabase::get_ptr()->get_function(function).is_destructor();
+}
+
bool
interrogate_function_has_module_name(FunctionIndex function) {
// cerr << "interrogate_function_has_module_name(" << function << ")\n";
@@ -411,6 +436,12 @@ interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n) {
return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_this(n);
}
+bool
+interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n) {
+ // cerr << "interrogate_wrapper_is_optional(" << wrapper << ", " << n << ")\n";
+ return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_optional(n);
+}
+
bool
interrogate_wrapper_has_pointer(FunctionWrapperIndex wrapper) {
// cerr << "interrogate_wrapper_has_pointer(" << wrapper << ")\n";
@@ -476,11 +507,22 @@ interrogate_make_seq_num_name(MakeSeqIndex make_seq) {
const char *
interrogate_make_seq_element_name(MakeSeqIndex make_seq) {
// cerr << "interrogate_make_seq_element_name(" << make_seq << ")\n";
- static string result;
FunctionIndex function = InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter();
return interrogate_function_name(function);
}
+FunctionIndex
+interrogate_make_seq_num_getter(MakeSeqIndex make_seq) {
+ // cerr << "interrogate_make_seq_num_getter(" << make_seq << ")\n";
+ return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_length_getter();
+}
+
+FunctionIndex
+interrogate_make_seq_element_getter(MakeSeqIndex make_seq) {
+ // cerr << "interrogate_make_seq_element_getter(" << make_seq << ")\n";
+ return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter();
+}
+
int
interrogate_number_of_global_types() {
// cerr << "interrogate_number_of_global_types()\n";
@@ -668,12 +710,30 @@ interrogate_type_wrapped_type(TypeIndex type) {
return InterrogateDatabase::get_ptr()->get_type(type).get_wrapped_type();
}
+bool
+interrogate_type_is_array(TypeIndex type) {
+ // cerr << "interrogate_type_is_array(" << type << ")\n";
+ return InterrogateDatabase::get_ptr()->get_type(type).is_array();
+}
+
+int
+interrogate_type_array_size(TypeIndex type) {
+ // cerr << "interrogate_type_array_size(" << type << ")\n";
+ return InterrogateDatabase::get_ptr()->get_type(type).get_array_size();
+}
+
bool
interrogate_type_is_enum(TypeIndex type) {
// cerr << "interrogate_type_is_enum(" << type << ")\n";
return InterrogateDatabase::get_ptr()->get_type(type).is_enum();
}
+bool
+interrogate_type_is_scoped_enum(TypeIndex type) {
+ // cerr << "interrogate_type_is_scoped_enum(" << type << ")\n";
+ return InterrogateDatabase::get_ptr()->get_type(type).is_scoped_enum();
+}
+
int
interrogate_type_number_of_enum_values(TypeIndex type) {
// cerr << "interrogate_type_number_of_enum_values(" << type << ")\n";
@@ -828,6 +888,12 @@ interrogate_type_get_derivation(TypeIndex type, int n) {
return InterrogateDatabase::get_ptr()->get_type(type).get_derivation(n);
}
+bool
+interrogate_type_is_final(TypeIndex type) {
+ // cerr << "interrogate_type_is_final(" << type << ")\n";
+ return InterrogateDatabase::get_ptr()->get_type(type).is_final();
+}
+
bool
interrogate_type_derivation_has_upcast(TypeIndex type, int n) {
// cerr << "interrogate_type_derivation_has_upcast(" << type << ", " << n <<
diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h
index cc88af041d..a44f9fef92 100644
--- a/dtool/src/interrogatedb/interrogate_interface.h
+++ b/dtool/src/interrogatedb/interrogate_interface.h
@@ -210,6 +210,10 @@ EXPCL_INTERROGATEDB const char *interrogate_function_prototype(FunctionIndex fun
// if the function is a class method.
EXPCL_INTERROGATEDB bool interrogate_function_is_method(FunctionIndex function);
EXPCL_INTERROGATEDB TypeIndex interrogate_function_class(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_unary_op(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_operator_typecast(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_constructor(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_destructor(FunctionIndex function);
// This returns the module name reported for the function, if available.
EXPCL_INTERROGATEDB bool interrogate_function_has_module_name(FunctionIndex function);
@@ -299,6 +303,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_wrapper_parameter_type(FunctionWrapper
EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_has_name(FunctionWrapperIndex wrapper, int n);
EXPCL_INTERROGATEDB const char *interrogate_wrapper_parameter_name(FunctionWrapperIndex wrapper, int n);
EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n);
+EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n);
// This returns a pointer to a function that may be called to invoke the
// function, if the -fptrs option to return function pointers was specified to
@@ -345,7 +350,8 @@ EXPCL_INTERROGATEDB const char *interrogate_make_seq_comment(ElementIndex elemen
EXPCL_INTERROGATEDB const char *interrogate_make_seq_num_name(MakeSeqIndex make_seq);
// The name of the real method that returns the nth element, e.g. "get_thing"
EXPCL_INTERROGATEDB const char *interrogate_make_seq_element_name(MakeSeqIndex make_seq);
-
+EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_num_getter(MakeSeqIndex make_seq);
+EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_element_getter(MakeSeqIndex make_seq);
// Types
@@ -420,9 +426,14 @@ EXPCL_INTERROGATEDB bool interrogate_type_is_const(TypeIndex type);
EXPCL_INTERROGATEDB bool interrogate_type_is_typedef(TypeIndex type);
EXPCL_INTERROGATEDB TypeIndex interrogate_type_wrapped_type(TypeIndex type);
+// If interrogate_type_is_array() returns true, this is an array type.
+EXPCL_INTERROGATEDB bool interrogate_type_is_array(TypeIndex type);
+EXPCL_INTERROGATEDB int interrogate_type_array_size(TypeIndex type);
+
// If interrogate_type_is_enum() returns true, this is an enumerated type,
// which means it may take any one of a number of named integer values.
EXPCL_INTERROGATEDB bool interrogate_type_is_enum(TypeIndex type);
+EXPCL_INTERROGATEDB bool interrogate_type_is_scoped_enum(TypeIndex type);
EXPCL_INTERROGATEDB int interrogate_type_number_of_enum_values(TypeIndex type);
EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_name(TypeIndex type, int n);
EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_scoped_name(TypeIndex type, int n);
@@ -499,6 +510,7 @@ EXPCL_INTERROGATEDB FunctionIndex interrogate_type_get_cast(TypeIndex type, int
// list of base classes for this particular type.
EXPCL_INTERROGATEDB int interrogate_type_number_of_derivations(TypeIndex type);
EXPCL_INTERROGATEDB TypeIndex interrogate_type_get_derivation(TypeIndex type, int n);
+EXPCL_INTERROGATEDB bool interrogate_type_is_final(TypeIndex type);
// For each base class, we might need to define an explicit upcast or downcast
// operation to convert the pointer to the derived class to an appropriate
diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx
index 35465a1090..ae6483f5b1 100644
--- a/dtool/src/interrogatedb/py_panda.cxx
+++ b/dtool/src/interrogatedb/py_panda.cxx
@@ -744,7 +744,7 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
if (callable == nullptr) {
return nullptr;
}
- PyObject *result = _PyObject_CallNoArg(callable);
+ PyObject *result = PyObject_CallNoArgs(callable);
Py_DECREF(callable);
return result;
}
@@ -768,7 +768,7 @@ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
if (callable == nullptr) {
return nullptr;
}
- PyObject *result = _PyObject_CallNoArg(callable);
+ PyObject *result = PyObject_CallNoArgs(callable);
Py_DECREF(callable);
return result;
}
diff --git a/dtool/src/prc/CMakeLists.txt b/dtool/src/prc/CMakeLists.txt
index 2a97c4665a..f5731e9e0f 100644
--- a/dtool/src/prc/CMakeLists.txt
+++ b/dtool/src/prc/CMakeLists.txt
@@ -94,6 +94,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}
diff --git a/dtool/src/prc/configFlags.h b/dtool/src/prc/configFlags.h
index f4b95757d6..c034760667 100644
--- a/dtool/src/prc/configFlags.h
+++ b/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.
diff --git a/dtool/src/prc/configVariableBase.h b/dtool/src/prc/configVariableBase.h
index ac256f1e8d..56ff035e5c 100644
--- a/dtool/src/prc/configVariableBase.h
+++ b/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,
diff --git a/dtool/src/prc/configVariableCore.h b/dtool/src/prc/configVariableCore.h
index 8849cc401b..d9dcb7658d 100644
--- a/dtool/src/prc/configVariableCore.h
+++ b/dtool/src/prc/configVariableCore.h
@@ -18,6 +18,7 @@
#include "configFlags.h"
#include "configPageManager.h"
#include "pnotify.h"
+#include "memoryBase.h"
#include
@@ -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);
diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx
index 2c1b737ddb..2a82f2926d 100644
--- a/dtool/src/prc/notify.cxx
+++ b/dtool/src/prc/notify.cxx
@@ -18,13 +18,10 @@
#include "configVariableBool.h"
#include "filename.h"
#include "config_prc.h"
+#include "patomic.h"
#include
-#ifdef PHAVE_ATOMIC
-#include
-#endif
-
#ifdef BUILD_IPHONE
#include
#endif
@@ -439,7 +436,7 @@ config_initialized() {
"The filename to which to write all the output of notify");
// We use this to ensure that only one thread can initialize the output.
- static std::atomic_flag initialized = ATOMIC_FLAG_INIT;
+ static patomic_flag initialized = ATOMIC_FLAG_INIT;
std::string value = notify_output.get_value();
if (!value.empty() && !initialized.test_and_set()) {
diff --git a/dtool/src/prc/notifyCategory.I b/dtool/src/prc/notifyCategory.I
index 74c1965778..4f513722f4 100644
--- a/dtool/src/prc/notifyCategory.I
+++ b/dtool/src/prc/notifyCategory.I
@@ -49,7 +49,7 @@ set_severity(NotifySeverity severity) {
_severity = severity;
#else
// enforce the no-debug, no-spam rule.
- _severity = std::max(severity, NS_info);
+ _severity = (std::max)(severity, NS_info);
#endif
invalidate_cache();
}
diff --git a/makepanda/makepackage.py b/makepanda/makepackage.py
index 81a64abd44..19b91381da 100755
--- a/makepanda/makepackage.py
+++ b/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':
diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py
index cd30216c93..aa252881e7 100755
--- a/makepanda/makepanda.py
+++ b/makepanda/makepanda.py
@@ -93,7 +93,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support
"VRPN", "OPENSSL", # Transport
"FFTW", # Algorithm helpers
"ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality
- "GTK2", # GTK2 is used for PStats on Unix
+ "GTK3", # GTK3 is used for PStats on Unix
"MFC", "WX", "FLTK", # Used for web plug-in only
"COCOA", # macOS toolkits
"X11", # Unix platform support
@@ -103,6 +103,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support
"PANDAPARTICLESYSTEM", # Built in particle system
"CONTRIB", # Experimental
"SSE2", "NEON", # Compiler features
+ "MIMALLOC", # Memory allocators
"NAMETAG", "MOVEMENT", "NAVIGATION", # libotp
"DNA", "SUIT", "PETS", # libtoontown
])
@@ -638,13 +639,19 @@ if (COMPILER == "MSVC"):
if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "quartz.lib")
if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbc32.lib")
if (PkgSkip("DIRECTCAM")==0): LibName("DIRECTCAM", "odbccp32.lib")
+ if (PkgSkip("MIMALLOC")==0): LibName("MIMALLOC", GetThirdpartyDir() + "mimalloc/lib/mimalloc-static.lib")
if (PkgSkip("OPENSSL")==0):
if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"):
LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
- else:
+ elif os.path.isfile(GetThirdpartyDir() + "openssl/lib/ssleay32.lib"):
LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libeay32.lib")
LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/ssleay32.lib")
+ else:
+ LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libssl.lib")
+ LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libcrypto.lib")
+ LibName("OPENSSL", "crypt32.lib")
+ LibName("OPENSSL", "ws2_32.lib")
if (PkgSkip("PNG")==0):
if os.path.isfile(GetThirdpartyDir() + "png/lib/libpng16_static.lib"):
LibName("PNG", GetThirdpartyDir() + "png/lib/libpng16_static.lib")
@@ -656,23 +663,39 @@ if (COMPILER == "MSVC"):
else:
LibName("TIFF", GetThirdpartyDir() + "tiff/lib/tiff.lib")
if (PkgSkip("OPENEXR")==0):
- suffix = ""
- if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"):
- suffix = "-2_2"
- elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"):
- suffix = "-2_3"
- elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"):
- suffix = "-2_4"
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib")
- if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"):
- suffix += "_s" # _s suffix observed for OpenEXR 2.3 only so far
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib")
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib")
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib")
- if suffix == "-2_2":
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib")
+ if os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXRCore-3_1.lib"):
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_1.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_1.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_1.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_1.lib")
+ elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib"):
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_0.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_0.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_0.lib")
+ elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR.lib"):
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath.lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex.lib")
else:
- LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib")
+ suffix = ""
+ if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"):
+ suffix = "-2_2"
+ elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"):
+ suffix = "-2_3"
+ elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"):
+ suffix = "-2_4"
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib")
+ if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"):
+ suffix += "_s" # _s suffix observed for OpenEXR 2.3 only so far
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib")
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib")
+ if suffix == "-2_2":
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib")
+ else:
+ LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib")
IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/OpenEXR")
IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/Imath")
if (PkgSkip("JPEG")==0): LibName("JPEG", GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib")
@@ -849,13 +872,13 @@ 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("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h"))
SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h"))
SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus"))
SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h")
SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config")
+ SmartPkgEnable("MIMALLOC", "", ("mimalloc"), "mimalloc.h")
# Copy freetype libraries to be specified after harfbuzz libraries as well,
# because there's a circular dependency between the two libraries.
@@ -874,6 +897,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() != "darwin":
for fcollada_lib in fcollada_libs:
LibName("FCOLLADA", "-Wl,--exclude-libs,lib%s.a" % (fcollada_lib))
@@ -905,6 +945,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")
@@ -925,6 +968,9 @@ if (COMPILER=="GCC"):
LibName("ARTOOLKIT", "-Wl,--exclude-libs,libAR.a")
LibName("ARTOOLKIT", "-Wl,--exclude-libs,libARMulti.a")
+ if not PkgSkip("MIMALLOC"):
+ LibName("MIMALLOC", "-Wl,--exclude-libs,libmimalloc.a")
+
if PkgSkip("FFMPEG") or GetTarget() == "darwin":
cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV")
if cv_lib == "opencv_core":
@@ -965,7 +1011,7 @@ if (COMPILER=="GCC"):
SmartPkgEnable("OPENSSL", "openssl", ("ssl", "crypto"), ("openssl/ssl.h", "openssl/crypto.h"))
SmartPkgEnable("ZLIB", "zlib", ("z"), "zlib.h")
- SmartPkgEnable("GTK2", "gtk+-2.0")
+ SmartPkgEnable("GTK3", "gtk+-3.0")
if not PkgSkip("OPENSSL") and GetTarget() != "darwin":
LibName("OPENSSL", "-Wl,--exclude-libs,libssl.a")
@@ -983,11 +1029,6 @@ if (COMPILER=="GCC"):
if GetHost() != "darwin":
# Workaround for an issue where pkg-config does not include this path
if GetTargetArch() in ("x86_64", "amd64"):
- if (os.path.isdir("/usr/lib64/glib-2.0/include")):
- IncDirectory("GTK2", "/usr/lib64/glib-2.0/include")
- if (os.path.isdir("/usr/lib64/gtk-2.0/include")):
- IncDirectory("GTK2", "/usr/lib64/gtk-2.0/include")
-
if not PkgSkip("X11"):
if (os.path.isdir("/usr/X11R6/lib64")):
LibDirectory("ALWAYS", "/usr/X11R6/lib64")
@@ -1033,8 +1074,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")
@@ -1175,7 +1214,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 "
@@ -1207,7 +1246,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"
@@ -1225,7 +1264,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
@@ -1336,6 +1375,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"])
@@ -1422,10 +1465,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:
@@ -2298,6 +2338,7 @@ DTOOL_CONFIG=[
("REPORT_OPENSSL_ERRORS", '1', '1'),
("USE_PANDAFILESTREAM", '1', '1'),
("USE_DELETED_CHAIN", '1', '1'),
+ ("HAVE_MIMALLOC", 'UNDEF', 'UNDEF'),
("HAVE_WGL", '1', 'UNDEF'),
("HAVE_DX9", 'UNDEF', 'UNDEF'),
("HAVE_THREADS", '1', '1'),
@@ -2443,6 +2484,20 @@ def WriteConfigSettings():
dtool_config["HAVE_NET"] = '1'
+ if GetTarget() == 'windows':
+ if not PkgSkip("MIMALLOC"):
+ # This is faster than both DeletedBufferChain and malloc,
+ # especially in the multi-threaded case.
+ dtool_config["USE_MEMORY_MIMALLOC"] = '1'
+ dtool_config["USE_DELETED_CHAIN"] = 'UNDEF'
+ else:
+ # If we don't have mimalloc, use DeletedBufferChain as fallback,
+ # which is still more efficient than malloc.
+ dtool_config["USE_DELETED_CHAIN"] = '1'
+ else:
+ # On other systems, the default malloc seems to be fine.
+ dtool_config["USE_DELETED_CHAIN"] = 'UNDEF'
+
if (PkgSkip("NVIDIACG")==0):
dtool_config["HAVE_CG"] = '1'
dtool_config["HAVE_CGGL"] = '1'
@@ -3374,7 +3429,7 @@ if GetTarget() == 'windows':
# DIRECTORY: dtool/src/dtoolbase/
#
-OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL']
+OPTS=['DIR:dtool/src/dtoolbase', 'BUILDING:DTOOL', 'MIMALLOC']
TargetAdd('p3dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx')
TargetAdd('p3dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx')
TargetAdd('p3dtoolbase_lookup3.obj', opts=OPTS, input='lookup3.c')
@@ -3405,7 +3460,7 @@ TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite1.obj')
TargetAdd('libp3dtool.dll', input='p3dtoolbase_composite2.obj')
TargetAdd('libp3dtool.dll', input='p3dtoolbase_indent.obj')
TargetAdd('libp3dtool.dll', input='p3dtoolbase_lookup3.obj')
-TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL'])
+TargetAdd('libp3dtool.dll', opts=['ADVAPI','WINSHELL','WINKERNEL','MIMALLOC'])
#
# DIRECTORY: dtool/src/cppparser/
@@ -4997,8 +5052,10 @@ if GetTarget() == 'android' and not PkgSkip("EGL") and not PkgSkip("GLES2"):
# DIRECTORY: panda/src/tinydisplay/
#
-if (GetTarget() in ('windows', 'darwin') or not PkgSkip("X11")) and not PkgSkip("TINYDISPLAY"):
+if not PkgSkip("TINYDISPLAY"):
OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY', 'X11']
+ if not PkgSkip("X11"):
+ OPTS += ['X11']
TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx')
TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx')
TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx')
@@ -5009,7 +5066,7 @@ if (GetTarget() in ('windows', 'darwin') or not PkgSkip("X11")) and not PkgSkip(
if GetTarget() == 'windows':
TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll')
TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
- elif GetTarget() != 'darwin':
+ elif GetTarget() != 'darwin' and not PkgSkip("X11"):
TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
TargetAdd('libp3tinydisplay.dll', opts=['X11'])
TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
@@ -6032,19 +6089,19 @@ if not PkgSkip("PANDATOOL"):
# DIRECTORY: pandatool/src/gtk-stats/
#
-if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK2")):
+if not PkgSkip("PANDATOOL") and (GetTarget() == 'windows' or not PkgSkip("GTK3")):
if GetTarget() == 'windows':
OPTS=['DIR:pandatool/src/win-stats']
TargetAdd('pstats_composite1.obj', opts=OPTS, input='winstats_composite1.cxx')
else:
- OPTS=['DIR:pandatool/src/gtk-stats', 'GTK2']
+ OPTS=['DIR:pandatool/src/gtk-stats', 'GTK3']
TargetAdd('pstats_composite1.obj', opts=OPTS, input='gtkstats_composite1.cxx')
TargetAdd('pstats.exe', input='pstats_composite1.obj')
TargetAdd('pstats.exe', input='libp3pstatserver.lib')
TargetAdd('pstats.exe', input='libp3progbase.lib')
TargetAdd('pstats.exe', input='libp3pandatoolbase.lib')
TargetAdd('pstats.exe', input=COMMON_PANDA_LIBS)
- TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK2'])
+ TargetAdd('pstats.exe', opts=['SUBSYSTEM:WINDOWS', 'WINCOMCTL', 'WINSOCK', 'WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM', 'GTK3'])
#
# DIRECTORY: pandatool/src/xfileprogs/
@@ -6192,17 +6249,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/
@@ -6260,7 +6321,7 @@ if PkgSkip("PYTHON") == 0:
if GetTarget() == 'linux' or GetTarget() == 'freebsd':
# Setup rpath so libs can be found in the same directory as the deployed game
- LibName('DEPLOYSTUB', "-Wl,-rpath,\\$ORIGIN")
+ LibName('DEPLOYSTUB', "-Wl,--disable-new-dtags,-rpath,\\$ORIGIN")
LibName('DEPLOYSTUB', "-Wl,-z,origin")
LibName('DEPLOYSTUB', "-rdynamic")
@@ -6280,6 +6341,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')
@@ -6408,6 +6472,8 @@ def ParallelMake(tasklist):
tasklist = extras
sys.stdout.flush()
if tasksqueued == 0:
+ if len(tasklist) > 0:
+ continue
break
donetask = donequeue.get()
if donetask == 0:
diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py
index c278253402..44d836e82b 100644
--- a/makepanda/makepandacore.py
+++ b/makepanda/makepandacore.py
@@ -572,6 +572,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
@@ -635,6 +655,9 @@ def oscmd(cmd, ignoreError = False, cwd=None):
res = os.spawnl(os.P_WAIT, exe_path, cmd)
+ if res == -1073741510: # 0xc000013a
+ exit("keyboard interrupt")
+
if cwd is not None:
os.chdir(pwd)
else:
@@ -717,7 +740,7 @@ def GetTimestamp(path):
if path in TIMESTAMPCACHE:
return TIMESTAMPCACHE[path]
try:
- date = os.path.getmtime(path)
+ date = int(os.path.getmtime(path))
except:
date = 0
TIMESTAMPCACHE[path] = date
@@ -854,8 +877,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
@@ -871,7 +897,7 @@ def JavaGetImports(path):
##
########################################################################
-DCACHE_VERSION = 2
+DCACHE_VERSION = 3
DCACHE_BACKED_UP = False
def SaveDependencyCache():
@@ -3347,7 +3373,12 @@ def SetOrigExt(x, v):
ORIG_EXT[x] = v
def GetExtensionSuffix():
- if CrossCompiling():
+ if GetTarget() == '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 CrossCompiling():
return '.{0}.so'.format(GetPythonABI())
else:
import _imp
diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py
index 2ebfd8efe7..b4049a06df 100644
--- a/makepanda/makewheel.py
+++ b/makepanda/makewheel.py
@@ -800,6 +800,21 @@ if __debug__:
whl.write_file(target_path, source_path)
+ # Include the special sysconfigdata module.
+ if os.name == 'posix':
+ import sysconfig
+
+ if hasattr(sysconfig, '_get_sysconfigdata_name'):
+ modname = sysconfig._get_sysconfigdata_name() + '.py'
+ else:
+ modname = '_sysconfigdata.py'
+
+ for entry in sys.path:
+ source_path = os.path.join(entry, modname)
+ if os.path.isfile(source_path):
+ whl.write_file('deploy_libs/' + modname, source_path)
+ break
+
# Add plug-ins.
for lib in PLUGIN_LIBS:
plugin_name = 'lib' + lib
diff --git a/panda/src/android/PythonActivity.java b/panda/src/android/PythonActivity.java
index 0d282d84a5..731ed4d54c 100644
--- a/panda/src/android/PythonActivity.java
+++ b/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;
+ }
}
diff --git a/panda/src/audio/audioLoadRequest.I b/panda/src/audio/audioLoadRequest.I
index 019501aa77..f5d9b8f5f8 100644
--- a/panda/src/audio/audioLoadRequest.I
+++ b/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();
}
diff --git a/panda/src/audiotraits/CMakeLists.txt b/panda/src/audiotraits/CMakeLists.txt
index c90b9dcbae..419bc66a88 100644
--- a/panda/src/audiotraits/CMakeLists.txt
+++ b/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}
diff --git a/panda/src/bullet/bulletAllHitsRayResult.h b/panda/src/bullet/bulletAllHitsRayResult.h
index 0153e424a8..88ebdfcdc0 100644
--- a/panda/src/bullet/bulletAllHitsRayResult.h
+++ b/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();
diff --git a/panda/src/bullet/bulletClosestHitRayResult.h b/panda/src/bullet/bulletClosestHitRayResult.h
index fd351d3aac..8cd043a22e 100644
--- a/panda/src/bullet/bulletClosestHitRayResult.h
+++ b/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();
diff --git a/panda/src/bullet/bulletClosestHitSweepResult.h b/panda/src/bullet/bulletClosestHitSweepResult.h
index 2581cd34ff..4cdb09da20 100644
--- a/panda/src/bullet/bulletClosestHitSweepResult.h
+++ b/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();
diff --git a/panda/src/bullet/bulletConvexHullShape.cxx b/panda/src/bullet/bulletConvexHullShape.cxx
index b61722e773..89ec5b2034 100644
--- a/panda/src/bullet/bulletConvexHullShape.cxx
+++ b/panda/src/bullet/bulletConvexHullShape.cxx
@@ -194,7 +194,7 @@ make_from_bam(const FactoryParams ¶ms) {
void BulletConvexHullShape::
fillin(DatagramIterator &scan, BamReader *manager) {
BulletShape::fillin(scan, manager);
- nassertv(_shape == nullptr);
+ nassertv(_shape);
_shape->setMargin(scan.get_stdfloat());
unsigned int num_points = scan.get_uint32();
diff --git a/panda/src/bullet/bulletConvexPointCloudShape.I b/panda/src/bullet/bulletConvexPointCloudShape.I
index 47e0b62704..a3e7d00e18 100644
--- a/panda/src/bullet/bulletConvexPointCloudShape.I
+++ b/panda/src/bullet/bulletConvexPointCloudShape.I
@@ -16,8 +16,8 @@
*/
INLINE BulletConvexPointCloudShape::
BulletConvexPointCloudShape() :
- _scale(1),
- _shape(nullptr) {
+ _shape(nullptr),
+ _scale(1) {
}
/**
diff --git a/panda/src/bullet/bulletTriangleMeshShape.cxx b/panda/src/bullet/bulletTriangleMeshShape.cxx
index 688bccd478..77fe9ddcbb 100644
--- a/panda/src/bullet/bulletTriangleMeshShape.cxx
+++ b/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) {
diff --git a/panda/src/chan/animControl.cxx b/panda/src/chan/animControl.cxx
index df7fb26498..dd7cb78c5d 100644
--- a/panda/src/chan/animControl.cxx
+++ b/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());
diff --git a/panda/src/char/character.cxx b/panda/src/char/character.cxx
index bf16135484..eef46d4713 100644
--- a/panda/src/char/character.cxx
+++ b/panda/src/char/character.cxx
@@ -38,16 +38,16 @@ PStatCollector Character::_animation_pcollector("*:Animation");
Character::
Character(const Character ©, 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 ©, 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();
diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm
index 29b0eaef5c..e931b59f44 100644
--- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm
+++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm
@@ -137,15 +137,17 @@ move_pointer(int device, int x, int y) {
if (device == 0) {
CGPoint point;
if (_properties.get_fullscreen()) {
- point = CGPointMake(x, y + 1);
+ point = CGPointMake(x, y);
} else {
point = CGPointMake(x + _properties.get_x_origin(),
- y + _properties.get_y_origin() + 1);
+ y + _properties.get_y_origin());
}
- // I don't know what the difference between these two methods is. if
- // (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
- if (CGDisplayMoveCursorToPoint(_display, point) == kCGErrorSuccess) {
+ if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
+ //After moving (or warping) the mouse position, CG starts an event
+ // suppression interval during which no more mouse events can occur
+ // This interval can be interupted by the following call :
+ CGAssociateMouseAndMouseCursorPosition(YES);
// Generate a mouse event.
NSPoint pos = [_window mouseLocationOutsideOfEventStream];
NSPoint loc = [_view convertPoint:pos fromView:nil];
@@ -1861,9 +1863,8 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
}
}
- // Strangely enough, in Cocoa, mouse Y coordinates are 1-based.
nx = x;
- ny = y - 1;
+ ny = y;
} else {
// We received deltas, so add it to the current mouse position.
@@ -1880,13 +1881,17 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
ny = std::max(0., std::min((double) get_y_size() - 1, ny));
if (_properties.get_fullscreen()) {
- point = CGPointMake(nx, ny + 1);
+ point = CGPointMake(nx, ny);
} else {
point = CGPointMake(nx + _properties.get_x_origin(),
- ny + _properties.get_y_origin() + 1);
+ ny + _properties.get_y_origin());
}
if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
+ //After moving (or warping) the mouse position, CG starts an event
+ // suppression interval during which no more mouse events can occur
+ // This interval can be interupted by the following call :
+ CGAssociateMouseAndMouseCursorPosition(YES);
in_window = true;
} else {
cocoadisplay_cat.warning() << "Failed to return mouse pointer to window\n";
@@ -1929,11 +1934,14 @@ handle_wheel_event(double x, double y) {
_input->button_up(MouseButton::wheel_down());
}
- // TODO: check if this is correct, I don't own a MacBook
- if (x > 0.0) {
+ if (x != 0 && cocoa_invert_wheel_x) {
+ x = -x;
+ }
+
+ if (x < 0.0) {
_input->button_down(MouseButton::wheel_right());
_input->button_up(MouseButton::wheel_right());
- } else if (x < 0.0) {
+ } else if (x > 0.0) {
_input->button_down(MouseButton::wheel_left());
_input->button_up(MouseButton::wheel_left());
}
diff --git a/panda/src/cocoadisplay/cocoaPandaView.mm b/panda/src/cocoadisplay/cocoaPandaView.mm
index 454c4a032f..f501592aa2 100644
--- a/panda/src/cocoadisplay/cocoaPandaView.mm
+++ b/panda/src/cocoadisplay/cocoaPandaView.mm
@@ -121,9 +121,7 @@
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
BOOL inside = [self mouse:loc inRect:[self bounds]];
- // the correlation between mouse deltas and location are "debounced"
- // apparently, so send deltas for both relative and confined modes
- if (_graphicsWindow->get_properties().get_mouse_mode() != WindowProperties::M_absolute) {
+ if (_graphicsWindow->get_properties().get_mouse_mode() == WindowProperties::M_relative) {
_graphicsWindow->handle_mouse_moved_event(inside, [event deltaX], [event deltaY], false);
} else {
_graphicsWindow->handle_mouse_moved_event(inside, loc.x, loc.y, true);
diff --git a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm
index 7e39748bd1..17fae241f3 100644
--- a/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm
+++ b/panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm
@@ -30,6 +30,7 @@
- (void) windowDidResize:(NSNotification *)notification {
// Forcing a move event is unfortunately necessary because Cocoa does not
// call windowDidMove in case of window zooms.
+ _graphicsWindow->handle_move_event();
_graphicsWindow->handle_resize_event();
}
diff --git a/panda/src/cocoadisplay/config_cocoadisplay.h b/panda/src/cocoadisplay/config_cocoadisplay.h
index 43a0e057c3..43051a67c5 100644
--- a/panda/src/cocoadisplay/config_cocoadisplay.h
+++ b/panda/src/cocoadisplay/config_cocoadisplay.h
@@ -20,6 +20,8 @@
NotifyCategoryDecl(cocoadisplay, EXPCL_PANDA_COCOADISPLAY, EXPTP_PANDA_COCOADISPLAY);
+extern ConfigVariableBool cocoa_invert_wheel_x;
+
extern EXPCL_PANDA_COCOADISPLAY void init_libcocoadisplay();
#endif
diff --git a/panda/src/cocoadisplay/config_cocoadisplay.mm b/panda/src/cocoadisplay/config_cocoadisplay.mm
index 96662d6c9c..a79144769b 100644
--- a/panda/src/cocoadisplay/config_cocoadisplay.mm
+++ b/panda/src/cocoadisplay/config_cocoadisplay.mm
@@ -31,6 +31,11 @@ ConfigureFn(config_cocoadisplay) {
init_libcocoadisplay();
}
+ConfigVariableBool cocoa_invert_wheel_x
+("cocoa-invert-wheel-x", false,
+ PRC_DESC("Set this to true to swap the wheel_left and wheel_right mouse "
+ "button events, to restore to the pre-1.10.12 behavior."));
+
/**
* Initializes the library. This must be called at least once before any of
* the functions or classes in this library can be used. Normally it will be
diff --git a/panda/src/collide/collisionBox.I b/panda/src/collide/collisionBox.I
index 62c58cbceb..86473473cf 100644
--- a/panda/src/collide/collisionBox.I
+++ b/panda/src/collide/collisionBox.I
@@ -17,11 +17,10 @@
*/
INLINE CollisionBox::
CollisionBox(const LPoint3 ¢er, 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 ©) :
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 ©) {
_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];
-}
diff --git a/panda/src/collide/collisionBox.cxx b/panda/src/collide/collisionBox.cxx
index e44f8834bf..ab5cb21717 100644
--- a/panda/src/collide/collisionBox.cxx
+++ b/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";
+ for (size_t i = 0; i < 4; i++) {
+ points[i]._v = points[(i + 1) % 4]._p - points[i]._p;
+ points[i]._v.normalize();
}
- collide_cat.error(false)
- << " normal " << normal << " with length " << normal.length() << "\n";
- _points.clear();
- }
- */
-#endif
-
- compute_vectors(_points[plane]);
}
/**
@@ -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);
}
/**
@@ -231,12 +208,11 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
bool intersect;
LPlane plane;
LVector3 normal;
+ bool fully_inside = true;
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
@@ -249,6 +225,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
// moving in the same direction as the plane's normal.
PN_stdfloat dot = delta.dot(plane.get_normal());
if (dot > 0.1f) {
+ fully_inside = false;
continue; // no intersection
}
@@ -305,13 +282,19 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
if (!plane.intersects_line(dist, from_center, -(plane.get_normal()))) {
// No intersection with plane? This means the plane's effective normal
// was within the plane itself. A useless polygon.
+ fully_inside = false;
continue;
}
- if (dist > from_radius || dist < -from_radius) {
- // No intersection with the plane.
+ if (dist > from_radius) {
+ // Fully outside this plane, there can not be an intersection.
+ return nullptr;
+ }
+ if (dist < -from_radius) {
+ // Fully inside this plane.
continue;
}
+ fully_inside = false;
LPoint2 p = to_2d(from_center - dist * plane.get_normal(), ip);
PN_stdfloat edge_dist = 0.0f;
@@ -322,17 +305,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;
@@ -367,8 +350,9 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
}
intersect = true;
}
- if( !intersect )
+ if (!fully_inside && !intersect) {
return nullptr;
+ }
if (collide_cat.is_debug()) {
collide_cat.debug()
@@ -506,7 +490,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 +1113,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 +1142,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 +1157,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 +1178,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 +1217,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 +1235,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 +1247,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 +1430,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 +1445,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 +1481,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 +1493,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);
}
}
}
diff --git a/panda/src/collide/collisionBox.h b/panda/src/collide/collisionBox.h
index 4bf96f0212..69a0e13889 100644
--- a/panda/src/collide/collisionBox.h
+++ b/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 ©);
@@ -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);
diff --git a/panda/src/collide/collisionHandlerEvent.I b/panda/src/collide/collisionHandlerEvent.I
index 13303de921..217978e117 100644
--- a/panda/src/collide/collisionHandlerEvent.I
+++ b/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.
*/
diff --git a/panda/src/collide/collisionHandlerEvent.h b/panda/src/collide/collisionHandlerEvent.h
index e0f8e30997..115ad520b6 100644
--- a/panda/src/collide/collisionHandlerEvent.h
+++ b/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 Colliding;
diff --git a/panda/src/collide/collisionHandlerPhysical_ext.cxx b/panda/src/collide/collisionHandlerPhysical_ext.cxx
index 7539f3f847..023d7ba718 100644
--- a/panda/src/collide/collisionHandlerPhysical_ext.cxx
+++ b/panda/src/collide/collisionHandlerPhysical_ext.cxx
@@ -21,7 +21,6 @@
*/
PyObject *Extension::
__reduce__(PyObject *self) const {
- extern struct Dtool_PyTypedObject Dtool_Datagram;
extern struct Dtool_PyTypedObject Dtool_NodePath;
// Create a tuple with all the NodePath pointers.
diff --git a/panda/src/collide/collisionLevelStateBase.I b/panda/src/collide/collisionLevelStateBase.I
index 5b649838f7..0410a4f229 100644
--- a/panda/src/collide/collisionLevelStateBase.I
+++ b/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 ©) :
_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 ©) {
_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;
}
/**
diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx
index 23459d88f3..50f187ad77 100644
--- a/panda/src/collide/collisionTraverser.cxx
+++ b/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()) {
diff --git a/panda/src/collide/collisionTraverser_ext.cxx b/panda/src/collide/collisionTraverser_ext.cxx
index a982f41029..f297b289a8 100644
--- a/panda/src/collide/collisionTraverser_ext.cxx
+++ b/panda/src/collide/collisionTraverser_ext.cxx
@@ -21,7 +21,6 @@
PyObject *Extension::
__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();
diff --git a/panda/src/device/evdevInputDevice.cxx b/panda/src/device/evdevInputDevice.cxx
index 05507086b1..f43fc3c03d 100644
--- a/panda/src/device/evdevInputDevice.cxx
+++ b/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);
diff --git a/panda/src/device/inputDevice.I b/panda/src/device/inputDevice.I
index 532e77f796..3bf6aa9001 100644
--- a/panda/src/device/inputDevice.I
+++ b/panda/src/device/inputDevice.I
@@ -344,7 +344,7 @@ is_axis_known(size_t index) const {
INLINE void InputDevice::
set_vibration(double strong, double weak) {
LightMutexHolder holder(_lock);
- do_set_vibration(std::max(std::min(strong, 1.0), 0.0), std::max(std::min(weak, 1.0), 0.0));
+ do_set_vibration((std::max)((std::min)(strong, 1.0), 0.0), (std::max)((std::min)(weak, 1.0), 0.0));
}
/**
diff --git a/panda/src/device/inputDeviceManager.h b/panda/src/device/inputDeviceManager.h
index 66381c79e0..f82167c7bb 100644
--- a/panda/src/device/inputDeviceManager.h
+++ b/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;
diff --git a/panda/src/device/ioKitInputDeviceManager.cxx b/panda/src/device/ioKitInputDeviceManager.cxx
index 3f1c807469..aa8d67301c 100644
--- a/panda/src/device/ioKitInputDeviceManager.cxx
+++ b/panda/src/device/ioKitInputDeviceManager.cxx
@@ -16,6 +16,18 @@
#if defined(__APPLE__) && !defined(CPPPARSER)
+static ConfigVariableBool iokit_scan_mouse_devices
+("iokit-scan-mouse-devices", false,
+ PRC_DESC("Set this to true to enable capturing raw mouse data via IOKit on "
+ "macOS. This is disabled by default because newer macOS versions "
+ "will prompt the user explicitly for permissions when this is on."));
+
+static ConfigVariableBool iokit_scan_keyboard_devices
+("iokit-scan-keyboard-devices", false,
+ PRC_DESC("Set this to true to enable capturing raw keyboard data via IOKit on "
+ "macOS. This is disabled by default because newer macOS versions "
+ "will prompt the user explicitly for permissions when this is on."));
+
/**
* Initializes the input device manager by scanning which devices are currently
* connected and setting up any platform-dependent structures necessary for
@@ -34,15 +46,22 @@ IOKitInputDeviceManager() {
int page = kHIDPage_GenericDesktop;
int usages[] = {kHIDUsage_GD_GamePad,
kHIDUsage_GD_Joystick,
- kHIDUsage_GD_Mouse,
- kHIDUsage_GD_Keyboard,
- kHIDUsage_GD_MultiAxisController, 0};
- int *usage = usages;
+ kHIDUsage_GD_MultiAxisController,
+ 0, 0, 0};
+
+ int num_usages = 3;
+ if (iokit_scan_mouse_devices) {
+ usages[num_usages++] = kHIDUsage_GD_Mouse;
+ }
+ if (iokit_scan_keyboard_devices) {
+ usages[num_usages++] = kHIDUsage_GD_Keyboard;
+ }
// This giant mess is necessary to create an array of match dictionaries
// that will match the devices we're interested in.
CFMutableArrayRef match = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
nassertv(match);
+ int *usage = usages;
while (*usage) {
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFNumberRef page_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
diff --git a/panda/src/device/trackerData.h b/panda/src/device/trackerData.h
index 7fdd756bb6..aba9fb6420 100644
--- a/panda/src/device/trackerData.h
+++ b/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 ©);
diff --git a/panda/src/device/xInputDevice.cxx b/panda/src/device/xInputDevice.cxx
index bcc44cd3b1..70b7d5bf3f 100644
--- a/panda/src/device/xInputDevice.cxx
+++ b/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;
diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx
index 093069a2b5..1a5f975215 100644
--- a/panda/src/display/config_display.cxx
+++ b/panda/src/display/config_display.cxx
@@ -475,6 +475,12 @@ ConfigVariableInt shadow_depth_bits
PRC_DESC("The minimum number of depth buffer bits requested when rendering "
"shadow maps. Set this to 32 for more depth resolution in shadow "
"maps."));
+ConfigVariableBool shadow_cube_map_filter
+("shadow-cube-map-filter", false,
+ PRC_DESC("If true, Panda enables hardware depth map comparison mode for "
+ "point lights, if supported. If false, does not. Keep this set to "
+ "false if you want the shader generator to work correctly for point "
+ "light shadows."));
ConfigVariableColor background_color
("background-color", "0.41 0.41 0.41 0.0",
diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h
index 8560251f1b..82cf8ea2e7 100644
--- a/panda/src/display/config_display.h
+++ b/panda/src/display/config_display.h
@@ -105,6 +105,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableInt accum_bits;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt multisamples;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt back_buffers;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt shadow_depth_bits;
+extern EXPCL_PANDA_DISPLAY ConfigVariableBool shadow_cube_map_filter;
extern EXPCL_PANDA_DISPLAY ConfigVariableDouble pixel_zoom;
diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx
index b29d490d03..84c852d472 100644
--- a/panda/src/display/displayRegion.cxx
+++ b/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 ©) :
_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 ©) :
_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)
{
}
diff --git a/panda/src/display/drawableRegion.I b/panda/src/display/drawableRegion.I
index 71fc1658cc..dcaa4ba276 100644
--- a/panda/src/display/drawableRegion.I
+++ b/panda/src/display/drawableRegion.I
@@ -242,7 +242,7 @@ INLINE void DrawableRegion::
update_pixel_factor() {
PN_stdfloat new_pixel_factor;
if (supports_pixel_zoom()) {
- new_pixel_factor = (PN_stdfloat)1 / sqrt(std::max(_pixel_zoom, (PN_stdfloat)1.0));
+ new_pixel_factor = (PN_stdfloat)1 / sqrt((std::max)(_pixel_zoom, (PN_stdfloat)1.0));
} else {
new_pixel_factor = 1;
}
diff --git a/panda/src/display/frameBufferProperties.I b/panda/src/display/frameBufferProperties.I
index 756a98c0be..af69f86fee 100644
--- a/panda/src/display/frameBufferProperties.I
+++ b/panda/src/display/frameBufferProperties.I
@@ -57,10 +57,11 @@ get_depth_bits() const {
*/
INLINE int FrameBufferProperties::
get_color_bits() const {
- return std::max(_property[FBP_color_bits],
- _property[FBP_red_bits] +
- _property[FBP_green_bits] +
- _property[FBP_blue_bits]);
+ return (std::max)(
+ _property[FBP_color_bits],
+ _property[FBP_red_bits] +
+ _property[FBP_green_bits] +
+ _property[FBP_blue_bits]);
}
/**
diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx
index b15474fc3f..af95350f82 100644
--- a/panda/src/display/graphicsEngine.cxx
+++ b/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.)
diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h
index e3e7df66c8..aef9676b27 100644
--- a/panda/src/display/graphicsEngine.h
+++ b/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;
diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I
index 992f634dd9..1f4e2d87a4 100644
--- a/panda/src/display/graphicsOutput.I
+++ b/panda/src/display/graphicsOutput.I
@@ -167,8 +167,8 @@ get_y_size() const {
*/
INLINE LVecBase2i GraphicsOutput::
get_fb_size() const {
- return LVecBase2i(std::max(int(_size.get_x() * get_pixel_factor()), 1),
- std::max(int(_size.get_y() * get_pixel_factor()), 1));
+ return LVecBase2i((std::max)(int(_size.get_x() * get_pixel_factor()), 1),
+ (std::max)(int(_size.get_y() * get_pixel_factor()), 1));
}
/**
@@ -178,7 +178,7 @@ get_fb_size() const {
*/
INLINE int GraphicsOutput::
get_fb_x_size() const {
- return std::max(int(_size.get_x() * get_pixel_factor()), 1);
+ return (std::max)(int(_size.get_x() * get_pixel_factor()), 1);
}
/**
@@ -188,7 +188,7 @@ get_fb_x_size() const {
*/
INLINE int GraphicsOutput::
get_fb_y_size() const {
- return std::max(int(_size.get_y() * get_pixel_factor()), 1);
+ return (std::max)(int(_size.get_y() * get_pixel_factor()), 1);
}
/**
@@ -200,8 +200,8 @@ INLINE LVecBase2i GraphicsOutput::
get_sbs_left_size() const {
PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
- return LVecBase2i(std::max(int(_size.get_x() * left_w), 1),
- std::max(int(_size.get_y() * left_h), 1));
+ return LVecBase2i((std::max)(int(_size.get_x() * left_w), 1),
+ (std::max)(int(_size.get_y() * left_h), 1));
}
/**
@@ -212,7 +212,7 @@ get_sbs_left_size() const {
INLINE int GraphicsOutput::
get_sbs_left_x_size() const {
PN_stdfloat left_w = _sbs_left_dimensions[1] - _sbs_left_dimensions[0];
- return std::max(int(_size.get_x() * left_w), 1);
+ return (std::max)(int(_size.get_x() * left_w), 1);
}
/**
@@ -223,7 +223,7 @@ get_sbs_left_x_size() const {
INLINE int GraphicsOutput::
get_sbs_left_y_size() const {
PN_stdfloat left_h = _sbs_left_dimensions[3] - _sbs_left_dimensions[2];
- return std::max(int(_size.get_y() * left_h), 1);
+ return (std::max)(int(_size.get_y() * left_h), 1);
}
/**
@@ -235,8 +235,8 @@ INLINE LVecBase2i GraphicsOutput::
get_sbs_right_size() const {
PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
- return LVecBase2i(std::max(int(_size.get_x() * right_w), 1),
- std::max(int(_size.get_y() * right_h), 1));
+ return LVecBase2i((std::max)(int(_size.get_x() * right_w), 1),
+ (std::max)(int(_size.get_y() * right_h), 1));
}
/**
@@ -247,7 +247,7 @@ get_sbs_right_size() const {
INLINE int GraphicsOutput::
get_sbs_right_x_size() const {
PN_stdfloat right_w = _sbs_right_dimensions[1] - _sbs_right_dimensions[0];
- return std::max(int(_size.get_x() * right_w), 1);
+ return (std::max)(int(_size.get_x() * right_w), 1);
}
/**
@@ -258,7 +258,7 @@ get_sbs_right_x_size() const {
INLINE int GraphicsOutput::
get_sbs_right_y_size() const {
PN_stdfloat right_h = _sbs_right_dimensions[3] - _sbs_right_dimensions[2];
- return std::max(int(_size.get_y() * right_h), 1);
+ return (std::max)(int(_size.get_y() * right_h), 1);
}
/**
diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx
index 58d4099a45..e6b27bee36 100644
--- a/panda/src/display/graphicsOutput.cxx
+++ b/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);
diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I
index f14a0fb087..09dd8c60b4 100644
--- a/panda/src/display/graphicsStateGuardian.I
+++ b/panda/src/display/graphicsStateGuardian.I
@@ -258,7 +258,7 @@ get_max_vertices_per_primitive() const {
INLINE int GraphicsStateGuardian::
get_max_texture_stages() const {
if (max_texture_stages > 0) {
- return std::min(_max_texture_stages, (int)max_texture_stages);
+ return (std::min)(_max_texture_stages, (int)max_texture_stages);
}
return _max_texture_stages;
}
@@ -695,7 +695,7 @@ get_timer_queries_active() const {
INLINE int GraphicsStateGuardian::
get_max_color_targets() const {
if (max_color_targets > 0) {
- return std::min(_max_color_targets, (int)max_color_targets);
+ return (std::min)(_max_color_targets, (int)max_color_targets);
}
return _max_color_targets;
}
diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx
index 797dba0fcd..6f23741a15 100644
--- a/panda/src/display/graphicsStateGuardian.cxx
+++ b/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();
}
@@ -3683,8 +3549,15 @@ get_dummy_shadow_map(Texture::TextureType texture_type) const {
dummy_cube->setup_cube_map(1, Texture::T_unsigned_byte, Texture::F_depth_component);
dummy_cube->set_clear_color(1);
// Note: cube map shadow filtering doesn't seem to work in Cg.
- dummy_cube->set_minfilter(SamplerState::FT_linear);
- dummy_cube->set_magfilter(SamplerState::FT_linear);
+ // That is why it is currently disabled by default, but it can be
+ // overridden in Config.prc for apps that have custom GLSL shaders.
+ if (shadow_cube_map_filter && get_supports_shadow_filter()) {
+ dummy_cube->set_minfilter(SamplerState::FT_shadow);
+ dummy_cube->set_magfilter(SamplerState::FT_shadow);
+ } else {
+ dummy_cube->set_minfilter(SamplerState::FT_linear);
+ dummy_cube->set_magfilter(SamplerState::FT_linear);
+ }
}
return dummy_cube;
}
diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h
index b0f9a323dc..6d0b26b6e1 100644
--- a/panda/src/display/graphicsStateGuardian.h
+++ b/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 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;
diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx
index 7abda2bac7..e81a1a944d 100644
--- a/panda/src/display/graphicsWindow.cxx
+++ b/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.
diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h
index 12047ceea6..45b1438906 100644
--- a/panda/src/display/graphicsWindow.h
+++ b/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.
diff --git a/panda/src/display/pStatGPUTimer.h b/panda/src/display/pStatGPUTimer.h
index 1e0accdc04..4b54e08b57 100644
--- a/panda/src/display/pStatGPUTimer.h
+++ b/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;
diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx
index 10b66762a9..fd0ff83fcf 100644
--- a/panda/src/display/standardMunger.cxx
+++ b/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);
diff --git a/panda/src/display/windowProperties.I b/panda/src/display/windowProperties.I
index 71a5132224..51f398c666 100644
--- a/panda/src/display/windowProperties.I
+++ b/panda/src/display/windowProperties.I
@@ -679,19 +679,18 @@ clear_z_order() {
* mouse can move outside the window and the mouse coordinates are relative to
* its position in the window.
*
- * M_relative (OSX or Unix/X11 only): a mode where only relative movements are
- * reported; particularly useful for FPS-style mouse movements where you have
- * hidden the mouse pointer and are are more interested in how fast the mouse
- * is moving, rather than precisely where the pointer is hovering.
- *
- * This has no effect on Windows. On Unix/X11, this requires the Xxf86dga
- * extension to be available.
- *
* M_confined: this mode reports absolute mouse positions, but confines the
- * mouse pointer to the window boundary. It can portably replace M_relative
- * for an FPS, but you need to periodically move the pointer to the center of
- * the window and track movement deltas.
+ * mouse pointer to the window boundary. The reported mouse positions will
+ * never be outside of the window boundary.
*
+ * M_relative: a mode where only relative movements are reported; particularly
+ * useful for FPS-style mouse movements where you have hidden the mouse
+ * pointer and are are more interested in how fast the mouse is moving, rather
+ * than precisely where the pointer is hovering. The reported positions still
+ * appear to be absolute, but they can go toward negative or positive infinity
+ * without being constrained by the window (or screen) dimensions. Since the
+ * position of the mouse cursor becomes meaningless in this mode, it is
+ * recommended to combine this with the cursor_hidden flag.
*/
INLINE void WindowProperties::
set_mouse_mode(MouseMode mode) {
diff --git a/panda/src/downloadertools/multify.cxx b/panda/src/downloadertools/multify.cxx
index d649992b40..da3789d3cb 100644
--- a/panda/src/downloadertools/multify.cxx
+++ b/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);
}
diff --git a/panda/src/dxgsg9/dxShaderContext9.cxx b/panda/src/dxgsg9/dxShaderContext9.cxx
index 6c7a15d313..dd9b8f8233 100644
--- a/panda/src/dxgsg9/dxShaderContext9.cxx
+++ b/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);
}
diff --git a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx b/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx
index 81fce0c90b..86a8c9ee41 100644
--- a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx
+++ b/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;
diff --git a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx b/panda/src/dxgsg9/wdxGraphicsWindow9.cxx
index ef70d24879..b1694b5613 100644
--- a/panda/src/dxgsg9/wdxGraphicsWindow9.cxx
+++ b/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 {
diff --git a/panda/src/egg/eggMesherEdge.I b/panda/src/egg/eggMesherEdge.I
index dbd37f0b70..d72d8172cd 100644
--- a/panda/src/egg/eggMesherEdge.I
+++ b/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 ©) :
- _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.
@@ -58,7 +46,7 @@ matches(const EggMesherEdge &other) const {
*/
INLINE EggMesherEdge *EggMesherEdge::
common_ptr() {
- return std::min(this, _opposite);
+ return (std::min)(this, _opposite);
}
/**
diff --git a/panda/src/egg/eggMesherEdge.h b/panda/src/egg/eggMesherEdge.h
index 17e463b79d..6be14550fe 100644
--- a/panda/src/egg/eggMesherEdge.h
+++ b/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 ©);
void remove(EggMesherStrip *strip);
void change_strip(EggMesherStrip *from, EggMesherStrip *to);
diff --git a/panda/src/egg/eggTransform.h b/panda/src/egg/eggTransform.h
index 671a3b1155..529b84d174 100644
--- a/panda/src/egg/eggTransform.h
+++ b/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 ©);
diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx
index c0c690f122..db431a8eb2 100644
--- a/panda/src/egg2pg/eggSaver.cxx
+++ b/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));
diff --git a/panda/src/egldisplay/eglGraphicsPipe.cxx b/panda/src/egldisplay/eglGraphicsPipe.cxx
index a75aec5170..6d4a58adc5 100644
--- a/panda/src/egldisplay/eglGraphicsPipe.cxx
+++ b/panda/src/egldisplay/eglGraphicsPipe.cxx
@@ -21,6 +21,12 @@
#include
+static ConfigVariableInt egl_device_index
+("egl-device-index", -1,
+ PRC_DESC("Selects which EGL device index is used to create the EGL display in "
+ "a headless configuration. The special value -1 selects the default "
+ "device."));
+
TypeHandle eglGraphicsPipe::_type_handle;
/**
@@ -30,6 +36,8 @@ eglGraphicsPipe::
eglGraphicsPipe() {
// Check for client extensions.
vector_string extensions;
+ bool supports_platform_device = false;
+ bool supports_device_enumeration = false;
const char *ext_ptr = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (ext_ptr != nullptr) {
extract_words(ext_ptr, extensions);
@@ -42,6 +50,13 @@ eglGraphicsPipe() {
out << " " << extension << "\n";
}
}
+
+ if (std::find(extensions.begin(), extensions.end(), "EGL_EXT_platform_device") != extensions.end()) {
+ supports_platform_device = true;
+ }
+ if (std::find(extensions.begin(), extensions.end(), "EGL_EXT_device_enumeration") != extensions.end()) {
+ supports_device_enumeration = true;
+ }
}
else if (egldisplay_cat.is_debug()) {
eglGetError();
@@ -51,23 +66,10 @@ eglGraphicsPipe() {
EGLint major, minor;
- //NB. if the X11 display failed to open, _display will be 0, which is a valid
- // input to eglGetDisplay - it means to open the default display.
-#ifdef USE_X11
- _egl_display = eglGetDisplay((NativeDisplayType) _display);
-#else
- _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-#endif
- if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
- egldisplay_cat.warning()
- << "Couldn't initialize the default EGL display: "
- << get_egl_error_string(eglGetError()) << "\n";
- _egl_display = EGL_NO_DISPLAY;
- }
-
- if (!_egl_display &&
- std::find(extensions.begin(), extensions.end(), "EGL_EXT_platform_device") != extensions.end() &&
- std::find(extensions.begin(), extensions.end(), "EGL_EXT_device_enumeration") != extensions.end()) {
+ int index = egl_device_index.get_value();
+ if (index >= 0 && supports_platform_device && supports_device_enumeration) {
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
(PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
@@ -79,23 +81,74 @@ eglGraphicsPipe() {
EGLDeviceEXT *devices = (EGLDeviceEXT *)alloca(sizeof(EGLDeviceEXT) * num_devices);
eglQueryDevicesEXT(num_devices, devices, &num_devices);
- if (egldisplay_cat.is_debug()) {
- egldisplay_cat.debug()
- << "Found " << num_devices << " EGL devices.\n";
+ if (index >= num_devices) {
+ egldisplay_cat.error()
+ << "Requested EGL device index " << index << " does not exist ("
+ << "there are only " << num_devices << " devices)\n";
+ _is_valid = false;
+ return;
}
- PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
- (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+ if (egldisplay_cat.is_debug()) {
+ egldisplay_cat.debug()
+ << "Found " << num_devices << " EGL devices, using device index "
+ << index << ".\n";
+ }
- if (eglGetPlatformDisplayEXT != nullptr) {
- for (EGLint i = 0; i < num_devices && !_egl_display; ++i) {
- _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr);
+ _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[index], nullptr);
- if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
- egldisplay_cat.warning()
- << "Couldn't initialize EGL platform display " << i << ": "
- << get_egl_error_string(eglGetError()) << "\n";
- _egl_display = EGL_NO_DISPLAY;
+ if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
+ egldisplay_cat.error()
+ << "Couldn't initialize EGL platform display " << index << ": "
+ << get_egl_error_string(eglGetError()) << "\n";
+ _egl_display = EGL_NO_DISPLAY;
+ }
+ }
+ }
+ else {
+ //NB. if the X11 display failed to open, _display will be 0, which is a valid
+ // input to eglGetDisplay - it means to open the default display.
+ #ifdef USE_X11
+ _egl_display = eglGetDisplay((NativeDisplayType) _display);
+ #else
+ _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ #endif
+ if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
+ egldisplay_cat.warning()
+ << "Couldn't initialize the default EGL display: "
+ << get_egl_error_string(eglGetError()) << "\n";
+ _egl_display = EGL_NO_DISPLAY;
+ }
+
+ if (!_egl_display && supports_platform_device && supports_device_enumeration) {
+ PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT =
+ (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
+
+ EGLint num_devices = 0;
+ if (eglQueryDevicesEXT != nullptr &&
+ eglQueryDevicesEXT(0, nullptr, &num_devices) &&
+ num_devices > 0) {
+ EGLDeviceEXT *devices = (EGLDeviceEXT *)alloca(sizeof(EGLDeviceEXT) * num_devices);
+ eglQueryDevicesEXT(num_devices, devices, &num_devices);
+
+ if (egldisplay_cat.is_debug()) {
+ egldisplay_cat.debug()
+ << "Found " << num_devices << " EGL devices.\n";
+ }
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+ if (eglGetPlatformDisplayEXT != nullptr) {
+ for (EGLint i = 0; i < num_devices && !_egl_display; ++i) {
+ _egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr);
+
+ if (_egl_display && !eglInitialize(_egl_display, &major, &minor)) {
+ egldisplay_cat.warning()
+ << "Couldn't initialize EGL platform display " << i << ": "
+ << get_egl_error_string(eglGetError()) << "\n";
+ _egl_display = EGL_NO_DISPLAY;
+ }
}
}
}
diff --git a/panda/src/event/CMakeLists.txt b/panda/src/event/CMakeLists.txt
index 47136d1b5e..b0464ff793 100644
--- a/panda/src/event/CMakeLists.txt
+++ b/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
diff --git a/panda/src/event/asyncFuture.I b/panda/src/event/asyncFuture.I
index c38b04d880..201e103c3e 100644
--- a/panda/src/event/asyncFuture.I
+++ b/panda/src/event/asyncFuture.I
@@ -27,7 +27,9 @@ AsyncFuture() :
*/
INLINE bool AsyncFuture::
done() const {
- return (FutureState)AtomicAdjust::get(_future_state) >= FS_finished;
+ // Not an acquire barrier because the caller may not even care about the
+ // result. Instead, a fence is put in get_result().
+ return _future_state.load(std::memory_order_relaxed) >= FS_finished;
}
/**
@@ -35,7 +37,7 @@ done() const {
*/
INLINE bool AsyncFuture::
cancelled() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_cancelled;
+ return _future_state.load(std::memory_order_relaxed) == FS_cancelled;
}
/**
@@ -65,7 +67,7 @@ INLINE TypedObject *AsyncFuture::
get_result() const {
// This is thread safe, since _result may no longer be modified after the
// state is changed to "done".
- nassertr_always(done(), nullptr);
+ nassertr_always(_future_state.load(std::memory_order_acquire) >= FS_finished, nullptr);
return _result;
}
@@ -77,10 +79,14 @@ INLINE void AsyncFuture::
get_result(TypedObject *&ptr, ReferenceCount *&ref_ptr) const {
// This is thread safe, since _result may no longer be modified after the
// state is changed to "done".
- nassertd(done()) {
+#ifdef NDEBUG
+ patomic_thread_fence(std::memory_order_acquire);
+#else
+ nassertd(_future_state.load(std::memory_order_acquire) >= FS_finished) {
ptr = nullptr;
ref_ptr = nullptr;
}
+#endif
ptr = _result;
ref_ptr = _result_ref.p();
}
@@ -124,7 +130,7 @@ INLINE AsyncFuture *AsyncFuture::
gather(Futures futures) {
if (futures.empty()) {
AsyncFuture *fut = new AsyncFuture;
- fut->_future_state = (AtomicAdjust::Integer)FS_finished;
+ fut->_future_state.store(FS_finished, std::memory_order_relaxed);
return fut;
} else if (futures.size() == 1) {
return futures[0].p();
@@ -167,10 +173,18 @@ try_lock_pending() {
INLINE void AsyncFuture::
unlock(FutureState new_state) {
nassertv(new_state != FS_locked_pending);
- FutureState orig_state = (FutureState)AtomicAdjust::set(_future_state, (AtomicAdjust::Integer)new_state);
+ FutureState orig_state = (FutureState)_future_state.exchange(new_state, std::memory_order_release);
nassertv(orig_state == FS_locked_pending);
}
+/**
+ * Atomically returns the current state.
+ */
+INLINE AsyncFuture::FutureState AsyncFuture::
+get_future_state() const {
+ return (FutureState)_future_state.load(std::memory_order_relaxed);
+}
+
/**
* Atomically changes the future state from pending to another state. Returns
* true if successful, false if the future was already done.
@@ -179,19 +193,18 @@ unlock(FutureState new_state) {
*/
INLINE bool AsyncFuture::
set_future_state(FutureState state) {
- FutureState orig_state = (FutureState)
- AtomicAdjust::compare_and_exchange(
- _future_state,
- (AtomicAdjust::Integer)FS_pending,
- (AtomicAdjust::Integer)state);
+ patomic_unsigned_lock_free::value_type orig_state = FS_pending;
+ if (_future_state.compare_exchange_strong(orig_state, state,
+ std::memory_order_relaxed)) {
+ return true;
+ }
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
while (orig_state == FS_locked_pending) {
- Thread::force_yield();
- orig_state = (FutureState)AtomicAdjust::compare_and_exchange(
- _future_state,
- (AtomicAdjust::Integer)FS_pending,
- (AtomicAdjust::Integer)state);
+ Thread::relax();
+ orig_state = FS_pending;
+ _future_state.compare_exchange_weak(orig_state, state,
+ std::memory_order_relaxed);
}
#else
nassertr(orig_state != FS_locked_pending, false);
diff --git a/panda/src/event/asyncFuture.cxx b/panda/src/event/asyncFuture.cxx
index 696680bb1a..d8b8b91d97 100644
--- a/panda/src/event/asyncFuture.cxx
+++ b/panda/src/event/asyncFuture.cxx
@@ -17,6 +17,7 @@
#include "config_event.h"
#include "pStatTimer.h"
#include "throw_event.h"
+#include "trueClock.h"
TypeHandle AsyncFuture::_type_handle;
TypeHandle AsyncGatheringFuture::_type_handle;
@@ -39,6 +40,7 @@ AsyncFuture::
if (result_ref != nullptr) {
_result_ref.cheat() = nullptr;
if (!result_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete _result;
}
_result = nullptr;
@@ -50,6 +52,11 @@ AsyncFuture::
* future was already done. Either way, done() will return true after this
* call returns.
*
+ * Please note that calling this is not a guarantee that the operation
+ * corresponding this future does not run. It could already be in the process
+ * of running, or perhaps not respond to a cancel signal. All this guarantees
+ * is that the future is marked as done when this call returns.
+ *
* In the case of a task, this is equivalent to remove().
*/
bool AsyncFuture::
@@ -70,8 +77,7 @@ cancel() {
void AsyncFuture::
output(std::ostream &out) const {
out << get_type();
- FutureState state = (FutureState)AtomicAdjust::get(_future_state);
- switch (state) {
+ switch (get_future_state()) {
case FS_pending:
case FS_locked_pending:
out << " (pending)";
@@ -93,47 +99,66 @@ output(std::ostream &out) const {
*/
void AsyncFuture::
wait() {
- if (done()) {
- return;
+ if (!done()) {
+ PStatTimer timer(AsyncTaskChain::_wait_pcollector);
+ if (task_cat.is_debug()) {
+ task_cat.debug()
+ << "Waiting for future " << *this << "\n";
+ }
+
+ FutureState state = get_future_state();
+ while (state < FS_finished) {
+ if (state == FS_locked_pending) {
+ // If it's locked, someone is probably about to finish it, so don't
+ // go to sleep.
+ do {
+ Thread::relax();
+ state = get_future_state();
+ }
+ while (state == FS_locked_pending);
+
+ if (state >= FS_finished) {
+ return;
+ }
+ }
+
+ // Go to sleep.
+ _future_state.wait(state, std::memory_order_relaxed);
+ state = get_future_state();
+ }
}
- PStatTimer timer(AsyncTaskChain::_wait_pcollector);
- if (task_cat.is_debug()) {
- task_cat.debug()
- << "Waiting for future " << *this << "\n";
- }
-
- // Continue to yield while the future isn't done. It may be more efficient
- // to use a condition variable, but let's not add the extra complexity
- // unless we're sure that we need it.
- do {
- Thread::force_yield();
- } while (!done());
+ // Let's make wait() an acquire op, so that people can use a future for
+ // synchronization.
+ patomic_thread_fence(std::memory_order_acquire);
}
/**
- * Waits until the future is done, or until the timeout is reached.
+ * Waits until the future is done, or until the timeout is reached. Note that
+ * this can be considerably less efficient than wait() without a timeout, so
+ * it's generally not a good idea to use this unless you really need to.
*/
void AsyncFuture::
wait(double timeout) {
- if (done()) {
- return;
- }
+ if (!done()) {
+ PStatTimer timer(AsyncTaskChain::_wait_pcollector);
+ if (task_cat.is_debug()) {
+ task_cat.debug()
+ << "Waiting up to " << timeout << " seconds for future " << *this << "\n";
+ }
- PStatTimer timer(AsyncTaskChain::_wait_pcollector);
- if (task_cat.is_debug()) {
- task_cat.debug()
- << "Waiting up to " << timeout << " seconds for future " << *this << "\n";
- }
+ // Continue to yield while the future isn't done. It may be more efficient
+ // to use a condition variable, but let's not add the extra complexity
+ // unless we're sure that we need it.
+ TrueClock *clock = TrueClock::get_global_ptr();
+ double end = clock->get_short_time() + timeout;
+ do {
+ Thread::relax();
+ }
+ while (!done() && clock->get_short_time() < end);
- // Continue to yield while the future isn't done. It may be more efficient
- // to use a condition variable, but let's not add the extra complexity
- // unless we're sure that we need it.
- ClockObject *clock = ClockObject::get_global_clock();
- double end = clock->get_real_time() + timeout;
- do {
- Thread::force_yield();
- } while (!done() && clock->get_real_time() < end);
+ patomic_thread_fence(std::memory_order_acquire);
+ }
}
/**
@@ -143,8 +168,12 @@ wait(double timeout) {
*/
void AsyncFuture::
notify_done(bool clean_exit) {
+ patomic_thread_fence(std::memory_order_acquire);
nassertv(done());
+ // Let any calls to wait() know that we're done.
+ _future_state.notify_all();
+
// This will only be called by the thread that managed to set the
// _future_state away from the "pending" state, so this is thread safe.
@@ -158,7 +187,7 @@ notify_done(bool clean_exit) {
// It's a gathering future. Decrease the pending count on it, and if
// we're the last one, call notify_done() on it.
AsyncGatheringFuture *gather = (AsyncGatheringFuture *)fut;
- if (!AtomicAdjust::dec(gather->_num_pending)) {
+ if (gather->_num_pending.fetch_sub(1, std::memory_order_relaxed) == 1) {
if (gather->set_future_state(FS_finished)) {
gather->notify_done(true);
}
@@ -222,22 +251,22 @@ void AsyncFuture::
set_result(TypedObject *ptr, ReferenceCount *ref_ptr) {
// We don't strictly need to lock the future since only one thread is
// allowed to call set_result(), but we might as well.
- FutureState orig_state = (FutureState)AtomicAdjust::
- compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending,
- (AtomicAdjust::Integer)FS_locked_pending);
-
+ patomic_unsigned_lock_free::value_type orig_state = FS_pending;
+ if (!_future_state.compare_exchange_strong(orig_state, FS_locked_pending,
+ std::memory_order_relaxed)) {
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
- while (orig_state == FS_locked_pending) {
- Thread::force_yield();
- orig_state = (FutureState)AtomicAdjust::
- compare_and_exchange(_future_state, (AtomicAdjust::Integer)FS_pending,
- (AtomicAdjust::Integer)FS_locked_pending);
- }
+ while (orig_state == FS_locked_pending) {
+ Thread::relax();
+ orig_state = FS_pending;
+ _future_state.compare_exchange_weak(orig_state, FS_locked_pending,
+ std::memory_order_relaxed);
+ }
#else
- // We can't lose control between now and calling unlock() if we're using a
- // cooperative threading model.
- nassertv(orig_state != FS_locked_pending);
+ // We can't lose control between now and calling unlock() if we're using a
+ // cooperative threading model.
+ nassertv(false);
#endif
+ }
if (orig_state == FS_pending) {
_result = ptr;
@@ -246,15 +275,15 @@ set_result(TypedObject *ptr, ReferenceCount *ref_ptr) {
// OK, now our thread owns the _waiting vector et al.
notify_done(true);
-
- } else if (orig_state == FS_cancelled) {
+ }
+ else if (orig_state == FS_cancelled) {
// This was originally illegal, but there is a chance that the future was
// cancelled while another thread was setting the result. So, we drop
// this, but we can issue a warning.
task_cat.warning()
<< "Ignoring set_result() called on cancelled " << *this << "\n";
-
- } else {
+ }
+ else {
task_cat.error()
<< "set_result() was called on finished " << *this << "\n";
}
@@ -370,9 +399,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) :
bool any_pending = false;
- AsyncFuture::Futures::const_iterator it;
- for (it = _futures.begin(); it != _futures.end(); ++it) {
- AsyncFuture *fut = *it;
+ for (AsyncFuture *fut : _futures) {
// If this returns true, the future is not yet done and we need to
// register ourselves with it. This creates a circular reference, but it
// is resolved when the future is completed or cancelled.
@@ -381,7 +408,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) :
_manager = fut->_manager;
}
fut->_waiting.push_back((AsyncFuture *)this);
- AtomicAdjust::inc(_num_pending);
+ _num_pending.fetch_add(1, std::memory_order_relaxed);
fut->unlock();
any_pending = true;
}
@@ -390,7 +417,7 @@ AsyncGatheringFuture(AsyncFuture::Futures futures) :
// Start in the done state if all the futures we were passed are done.
// Note that it is only safe to set this member in this manner if indeed
// no other future holds a reference to us.
- _future_state = (AtomicAdjust::Integer)FS_finished;
+ _future_state.store(FS_finished, std::memory_order_relaxed);
}
}
@@ -410,7 +437,7 @@ cancel() {
// Temporarily increase the pending count so that the notify_done()
// callbacks won't end up causing it to be set to "finished".
- AtomicAdjust::inc(_num_pending);
+ _num_pending.fetch_add(1, std::memory_order_relaxed);
bool any_cancelled = false;
for (AsyncFuture *fut : _futures) {
@@ -421,7 +448,7 @@ cancel() {
// If all the futures were cancelled, change state of this future to
// "cancelled" and call the notify_done() callbacks.
- if (!AtomicAdjust::dec(_num_pending)) {
+ if (_num_pending.fetch_sub(1, std::memory_order_relaxed) == 1) {
if (set_future_state(FS_cancelled)) {
notify_done(false);
}
diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h
index 73abe9a3a3..20c42ce4d4 100644
--- a/panda/src/event/asyncFuture.h
+++ b/panda/src/event/asyncFuture.h
@@ -18,7 +18,7 @@
#include "typedReferenceCount.h"
#include "typedWritableReferenceCount.h"
#include "eventParameter.h"
-#include "atomicAdjust.h"
+#include "patomic.h"
class AsyncTaskManager;
class AsyncTask;
@@ -110,7 +110,7 @@ private:
void wake_task(AsyncTask *task);
protected:
- enum FutureState {
+ enum FutureState : patomic_unsigned_lock_free::value_type {
// Pending states
FS_pending,
FS_locked_pending,
@@ -121,12 +121,13 @@ protected:
};
INLINE bool try_lock_pending();
INLINE void unlock(FutureState new_state = FS_pending);
+ INLINE FutureState get_future_state() const;
INLINE bool set_future_state(FutureState state);
AsyncTaskManager *_manager;
TypedObject *_result;
PT(ReferenceCount) _result_ref;
- AtomicAdjust::Integer _future_state;
+ patomic_unsigned_lock_free _future_state;
std::string _done_event;
@@ -176,7 +177,7 @@ public:
private:
const Futures _futures;
- AtomicAdjust::Integer _num_pending;
+ patomic _num_pending;
friend class AsyncFuture;
diff --git a/panda/src/event/asyncTaskManager.I b/panda/src/event/asyncTaskManager.I
index 6be7ef3508..976e445671 100644
--- a/panda/src/event/asyncTaskManager.I
+++ b/panda/src/event/asyncTaskManager.I
@@ -34,14 +34,32 @@ get_clock() {
#ifndef CPPPARSER
/**
- * Adds a new task which calls the indicated function to the task manager.
- * Returns the newly created FunctionAsyncTask object.
+ * Adds a new task to the task manager which calls the indicated callable.
+ * This method is defined as a more convenient alternative to subclassing
+ * AsyncTask.
+ *
+ * This given callable allowed to be any object defining a call operator that
+ * accepts an AsyncTask pointer and returns a DoneStatus.
+ *
+ * Returns the newly created AsyncTask object.
*
* @since 1.11.0
*/
+template
INLINE AsyncTask *AsyncTaskManager::
-add(const std::string &name, FunctionAsyncTask::TaskFunction function) {
- AsyncTask *task = new FunctionAsyncTask(name, std::move(function));
+add(const std::string &name, Callable callable) {
+ class InlineTask final : public AsyncTask {
+ public:
+ InlineTask(Callable callable) : _callable(std::move(callable)) {}
+
+ private:
+ virtual DoneStatus do_task() override final {
+ return _callable(this);
+ }
+
+ Callable _callable;
+ };
+ AsyncTask *task = new InlineTask(std::move(callable));
add(task);
return task;
}
diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h
index 0982e5e2c7..0028054ef7 100644
--- a/panda/src/event/asyncTaskManager.h
+++ b/panda/src/event/asyncTaskManager.h
@@ -30,7 +30,6 @@
#include "clockObject.h"
#include "ordered_vector.h"
#include "indirectCompareNames.h"
-#include "functionAsyncTask.h"
/**
* A class to manage a loose queue of isolated tasks, which can be performed
@@ -66,7 +65,8 @@ PUBLISHED:
void add(AsyncTask *task);
#ifndef CPPPARSER
- INLINE AsyncTask *add(const std::string &name, FunctionAsyncTask::TaskFunction function);
+ template
+ INLINE AsyncTask *add(const std::string &name, Callable callable);
#endif
bool has_task(AsyncTask *task) const;
diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx
index 93dc8d916b..ccae0d95ae 100644
--- a/panda/src/event/config_event.cxx
+++ b/panda/src/event/config_event.cxx
@@ -22,7 +22,6 @@
#include "event.h"
#include "eventHandler.h"
#include "eventParameter.h"
-#include "functionAsyncTask.h"
#include "genericAsyncTask.h"
#include "pointerEventList.h"
@@ -50,7 +49,6 @@ ConfigureFn(config_event) {
EventHandler::init_type();
EventStoreInt::init_type("EventStoreInt");
EventStoreDouble::init_type("EventStoreDouble");
- FunctionAsyncTask::init_type();
GenericAsyncTask::init_type();
ButtonEventList::register_with_read_factory();
diff --git a/panda/src/event/eventHandler.I b/panda/src/event/eventHandler.I
index eda0b022ea..b9458af08e 100644
--- a/panda/src/event/eventHandler.I
+++ b/panda/src/event/eventHandler.I
@@ -19,7 +19,7 @@ INLINE EventHandler *EventHandler::
get_global_event_handler(EventQueue *queue) {
// The event queue parameter is present for now, for backward compatibility,
// but it is ignored.
- if (_global_event_handler == 0) {
+ if (_global_event_handler == nullptr) {
make_global_event_handler();
}
return _global_event_handler;
diff --git a/panda/src/event/functionAsyncTask.I b/panda/src/event/functionAsyncTask.I
deleted file mode 100644
index aa413dacf0..0000000000
--- a/panda/src/event/functionAsyncTask.I
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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 functionAsyncTask.I
- * @author rdb
- * @date 2021-11-29
- */
-
-/**
- *
- */
-INLINE FunctionAsyncTask::
-FunctionAsyncTask(const std::string &name) :
- AsyncTask(name)
-{
-}
-
-/**
- *
- */
-INLINE FunctionAsyncTask::
-FunctionAsyncTask(const std::string &name, FunctionAsyncTask::TaskFunction function) :
- AsyncTask(name),
- _function(std::move(function))
-{
-}
-
-/**
- * Replaces the function that is called when the task runs.
- */
-INLINE void FunctionAsyncTask::
-set_function(TaskFunction function) {
- _function = std::move(function);
-}
-
-/**
- * Returns the function that is called when the task runs.
- */
-INLINE const FunctionAsyncTask::TaskFunction &FunctionAsyncTask::
-get_function() const {
- return _function;
-}
-
-/**
- * Replaces the function that is called when the task begins. This is an
- * optional function.
- */
-INLINE void FunctionAsyncTask::
-set_upon_birth(BirthFunction upon_birth) {
- _upon_birth = std::move(upon_birth);
-}
-
-/**
- * Returns the function that is called when the task begins, or NULL if the
- * function is not defined.
- */
-INLINE const FunctionAsyncTask::BirthFunction &FunctionAsyncTask::
-get_upon_birth() const {
- return _upon_birth;
-}
-
-/**
- * Replaces the function that is called when the task ends. This is an
- * optional function.
- */
-INLINE void FunctionAsyncTask::
-set_upon_death(FunctionAsyncTask::DeathFunction upon_death) {
- _upon_death = upon_death;
-}
-
-/**
- * Returns the function that is called when the task ends, or NULL if the
- * function is not defined.
- */
-INLINE const FunctionAsyncTask::DeathFunction &FunctionAsyncTask::
-get_upon_death() const {
- return _upon_death;
-}
diff --git a/panda/src/event/functionAsyncTask.cxx b/panda/src/event/functionAsyncTask.cxx
deleted file mode 100644
index 54b591918d..0000000000
--- a/panda/src/event/functionAsyncTask.cxx
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * 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 functionAsyncTask.cxx
- * @author rdb
- * @date 2021-11-29
- */
-
-#include "functionAsyncTask.h"
-#include "pnotify.h"
-
-#ifndef CPPPARSER
-
-TypeHandle FunctionAsyncTask::_type_handle;
-
-/**
- * Override this function to return true if the task can be successfully
- * executed, false if it cannot. Mainly intended as a sanity check when
- * attempting to add the task to a task manager.
- *
- * This function is called with the lock held.
- */
-bool FunctionAsyncTask::
-is_runnable() {
- return !!_function;
-}
-
-/**
- * Override this function to do something useful for the task.
- *
- * This function is called with the lock *not* held.
- */
-AsyncTask::DoneStatus FunctionAsyncTask::
-do_task() {
- nassertr(_function, DS_interrupt);
- return _function(this);
-}
-
-/**
- * Override this function to do something useful when the task has been added
- * to the active queue.
- *
- * This function is called with the lock *not* held.
- */
-void FunctionAsyncTask::
-upon_birth(AsyncTaskManager *manager) {
- AsyncTask::upon_birth(manager);
-
- if (_upon_birth) {
- _upon_birth(this);
- }
-}
-
-/**
- * Override this function to do something useful when the task has been
- * removed from the active queue. The parameter clean_exit is true if the
- * task has been removed because it exited normally (returning DS_done), or
- * false if it was removed for some other reason (e.g.
- * AsyncTaskManager::remove()). By the time this method is called, _manager
- * has been cleared, so the parameter manager indicates the original
- * AsyncTaskManager that owned this task.
- *
- * The normal behavior is to throw the done_event only if clean_exit is true.
- *
- * This function is called with the lock *not* held.
- */
-void FunctionAsyncTask::
-upon_death(AsyncTaskManager *manager, bool clean_exit) {
- AsyncTask::upon_death(manager, clean_exit);
-
- if (_upon_death) {
- _upon_death(this, clean_exit);
- }
-}
-
-#endif // CPPPARSER
diff --git a/panda/src/event/functionAsyncTask.h b/panda/src/event/functionAsyncTask.h
deleted file mode 100644
index 297d71c691..0000000000
--- a/panda/src/event/functionAsyncTask.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * 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 functionAsyncTask.h
- * @author rdb
- * @date 2021-11-29
- */
-
-#ifndef FUNCTIONASYNCTASK_H
-#define FUNCTIONASYNCTASK_H
-
-#include "pandabase.h"
-
-#include "asyncTask.h"
-
-#ifndef CPPPARSER
-#include
-
-/**
- * Associates a generic std::function (eg. a lambda) with an AsyncTask object.
- * You can use this when you want to create an AsyncTask without having to
- * subclass.
- *
- * @since 1.11.0
- */
-class EXPCL_PANDA_EVENT FunctionAsyncTask final : public AsyncTask {
-public:
- typedef std::function TaskFunction;
- typedef std::function BirthFunction;
- typedef std::function DeathFunction;
-
- INLINE FunctionAsyncTask(const std::string &name = std::string());
- INLINE FunctionAsyncTask(const std::string &name, TaskFunction function);
- ALLOC_DELETED_CHAIN(FunctionAsyncTask);
-
- INLINE void set_function(TaskFunction function);
- INLINE const TaskFunction &get_function() const;
-
- INLINE void set_upon_birth(BirthFunction function);
- INLINE const BirthFunction &get_upon_birth() const;
-
- INLINE void set_upon_death(DeathFunction function);
- INLINE const DeathFunction &get_upon_death() const;
-
-protected:
- virtual bool is_runnable() override;
- virtual DoneStatus do_task() override;
- virtual void upon_birth(AsyncTaskManager *manager) override;
- virtual void upon_death(AsyncTaskManager *manager, bool clean_exit) override;
-
-private:
- TaskFunction _function;
- BirthFunction _upon_birth;
- DeathFunction _upon_death;
-
-public:
- static TypeHandle get_class_type() {
- return _type_handle;
- }
- static void init_type() {
- AsyncTask::init_type();
- register_type(_type_handle, "FunctionAsyncTask",
- AsyncTask::get_class_type());
- }
- virtual TypeHandle get_type() const override {
- return get_class_type();
- }
- virtual TypeHandle force_init_type() override {init_type(); return get_class_type();}
-
-private:
- static TypeHandle _type_handle;
-};
-
-#include "functionAsyncTask.I"
-
-#endif // CPPPARSER
-
-#endif
diff --git a/panda/src/event/genericAsyncTask.h b/panda/src/event/genericAsyncTask.h
index ce7f635156..f35d780e3b 100644
--- a/panda/src/event/genericAsyncTask.h
+++ b/panda/src/event/genericAsyncTask.h
@@ -22,8 +22,6 @@
* Associates a generic C-style function pointer with an AsyncTask object.
* You can use this when you want to create an AsyncTask without having to
* subclass.
- *
- * @deprecated See FunctionAsyncTask instead, which is more powerful.
*/
class EXPCL_PANDA_EVENT GenericAsyncTask : public AsyncTask {
public:
diff --git a/panda/src/event/p3event_composite1.cxx b/panda/src/event/p3event_composite1.cxx
index 34fcb9a0c9..dc72d2b03f 100644
--- a/panda/src/event/p3event_composite1.cxx
+++ b/panda/src/event/p3event_composite1.cxx
@@ -7,7 +7,6 @@
#include "asyncTaskSequence.cxx"
#include "buttonEvent.cxx"
#include "buttonEventList.cxx"
-#include "functionAsyncTask.cxx"
#include "genericAsyncTask.cxx"
#include "pointerEvent.cxx"
#include "pointerEventList.cxx"
diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx
index d9c28b4611..7578e0de64 100644
--- a/panda/src/event/pythonTask.cxx
+++ b/panda/src/event/pythonTask.cxx
@@ -40,13 +40,13 @@ PythonTask(PyObject *func_or_coro, const std::string &name) :
_args(nullptr),
_upon_death(nullptr),
_owner(nullptr),
- _registered_to_owner(false),
_exception(nullptr),
_exc_value(nullptr),
_exc_traceback(nullptr),
_generator(nullptr),
_fut_waiter(nullptr),
_ignore_return(false),
+ _registered_to_owner(false),
_retrieved_exception(false) {
nassertv(func_or_coro != nullptr);
@@ -208,10 +208,22 @@ set_owner(PyObject *owner) {
#ifndef NDEBUG
if (owner != Py_None) {
PyObject *add = PyObject_GetAttrString(owner, "_addTask");
+ PyErr_Clear();
PyObject *clear = PyObject_GetAttrString(owner, "_clearTask");
+ PyErr_Clear();
- if (add == nullptr || !PyCallable_Check(add) ||
- clear == nullptr || !PyCallable_Check(clear)) {
+ bool valid_add = false;
+ if (add != nullptr) {
+ valid_add = PyCallable_Check(add);
+ Py_DECREF(add);
+ }
+ bool valid_clear = false;
+ if (clear != nullptr) {
+ valid_clear = PyCallable_Check(clear);
+ Py_DECREF(clear);
+ }
+
+ if (!valid_add || !valid_clear) {
Dtool_Raise_TypeError("owner object should have _addTask and _clearTask methods");
return;
}
diff --git a/panda/src/express/CMakeLists.txt b/panda/src/express/CMakeLists.txt
index fa4b8d6e2c..b7e18ef397 100644
--- a/panda/src/express/CMakeLists.txt
+++ b/panda/src/express/CMakeLists.txt
@@ -141,6 +141,8 @@ set(P3EXPRESS_IGATEEXT
virtualFileSystem_ext.h
virtualFile_ext.cxx
virtualFile_ext.h
+ multifile_ext.h
+ multifile_ext.I
)
composite_sources(p3express P3EXPRESS_SOURCES)
diff --git a/panda/src/express/datagramIterator.h b/panda/src/express/datagramIterator.h
index 867c7de58c..5137022c5f 100644
--- a/panda/src/express/datagramIterator.h
+++ b/panda/src/express/datagramIterator.h
@@ -23,6 +23,9 @@
* A class to retrieve the individual data elements previously stored in a
* Datagram. Elements may be retrieved one at a time; it is up to the caller
* to know the correct type and order of each element.
+ *
+ * Note that it is the responsibility of the caller to ensure that the datagram
+ * object is not destructed while this DatagramIterator is in use.
*/
class EXPCL_PANDA_EXPRESS DatagramIterator {
public:
diff --git a/panda/src/express/multifile.I b/panda/src/express/multifile.I
index 21462cc467..d418c77598 100644
--- a/panda/src/express/multifile.I
+++ b/panda/src/express/multifile.I
@@ -431,6 +431,6 @@ is_cert_special() const {
*/
INLINE std::streampos Multifile::Subfile::
get_last_byte_pos() const {
- return std::max(_index_start + (std::streampos)_index_length,
+ return (std::max)(_index_start + (std::streampos)_index_length,
_data_start + (std::streampos)_data_length) - (std::streampos)1;
}
diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h
index 99b5242127..b344eed9fd 100644
--- a/panda/src/express/multifile.h
+++ b/panda/src/express/multifile.h
@@ -69,8 +69,6 @@ PUBLISHED:
INLINE void set_encryption_flag(bool flag);
INLINE bool get_encryption_flag() const;
- INLINE void set_encryption_password(const std::string &encryption_password);
- INLINE const std::string &get_encryption_password() const;
INLINE void set_encryption_algorithm(const std::string &encryption_algorithm);
INLINE const std::string &get_encryption_algorithm() const;
@@ -86,6 +84,9 @@ PUBLISHED:
std::string update_subfile(const std::string &subfile_name, const Filename &filename,
int compression_level);
+ EXTENSION(INLINE PyObject *set_encryption_password(PyObject *encryption_password) const);
+ EXTENSION(INLINE PyObject *get_encryption_password() const);
+
#ifdef HAVE_OPENSSL
bool add_signature(const Filename &certificate,
const Filename &chain,
@@ -143,6 +144,9 @@ PUBLISHED:
INLINE const std::string &get_header_prefix() const;
public:
+ INLINE void set_encryption_password(const std::string &encryption_password);
+ INLINE const std::string &get_encryption_password() const;
+
#ifdef HAVE_OPENSSL
class CertRecord {
public:
diff --git a/panda/src/express/multifile_ext.I b/panda/src/express/multifile_ext.I
new file mode 100644
index 0000000000..68e427af2e
--- /dev/null
+++ b/panda/src/express/multifile_ext.I
@@ -0,0 +1,79 @@
+/**
+ * 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 multifile_ext.I
+ * @author Derzsi Daniel
+ * @date 2022-07-20
+ */
+
+#include
+
+/**
+ * Specifies the password, either as a Python string or a Python bytes object,
+ * that will be used to encrypt subfiles subsequently added to the multifile
+ */
+INLINE PyObject *Extension::
+set_encryption_password(PyObject *encryption_password) const {
+ Py_ssize_t pass_len;
+
+ // Have we been passed a string?
+ if (PyUnicode_Check(encryption_password)) {
+ const char *pass_str = PyUnicode_AsUTF8AndSize(encryption_password, &pass_len);
+ _this->set_encryption_password(std::string(pass_str, pass_len));
+ return Dtool_Return_None();
+ }
+
+ // Have we been passed a bytes object?
+ if (PyBytes_Check(encryption_password)) {
+ char *pass_str;
+
+ if (PyBytes_AsStringAndSize(encryption_password, &pass_str, &pass_len) < 0) {
+ PyErr_SetString(PyExc_TypeError, "A valid bytes object is required.");
+ return NULL;
+ }
+
+ // It is dangerous to use null bytes inside the encryption password.
+ // OpenSSL will cut off the password prematurely at the first null byte
+ // encountered.
+ if (memchr(pass_str, '\0', pass_len) != NULL) {
+ PyErr_SetString(PyExc_ValueError, "The password must not contain null bytes.");
+ return NULL;
+ }
+
+ _this->set_encryption_password(std::string(pass_str, pass_len));
+ return Dtool_Return_None();
+ }
+
+ return Dtool_Raise_BadArgumentsError(
+ "set_encryption_password(const Multifile self, str encryption_password)\n"
+ );
+}
+
+/**
+ * Returns the password that will be used to encrypt subfiles subsequently
+ * added to the multifile, either as a Python string (when possible) or a
+ * Python bytes object.
+ */
+INLINE PyObject *Extension::
+get_encryption_password() const {
+ std::string password = _this->get_encryption_password();
+ const char *pass_str = password.c_str();
+ Py_ssize_t pass_len = password.length();
+
+ // First, attempt to decode it as an UTF-8 string...
+ PyObject *result = PyUnicode_DecodeUTF8(pass_str, pass_len, NULL);
+
+ if (PyErr_Occurred()) {
+ // This password cannot be decoded as an UTF-8 string, so let's
+ // return it as a bytes object.
+ PyErr_Clear();
+ result = PyBytes_FromStringAndSize(pass_str, pass_len);
+ }
+
+ return result;
+}
diff --git a/panda/src/express/multifile_ext.h b/panda/src/express/multifile_ext.h
new file mode 100644
index 0000000000..91a7083bfe
--- /dev/null
+++ b/panda/src/express/multifile_ext.h
@@ -0,0 +1,40 @@
+/**
+ * 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 multifile_ext.h
+ * @author Derzsi Daniel
+ * @date 2022-07-20
+ */
+
+#ifndef MULTIFILE_EXT_H
+#define MULTIFILE_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "multifile.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for Multifile, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension : public ExtensionBase {
+public:
+ INLINE PyObject *set_encryption_password(PyObject *encryption_password) const;
+ INLINE PyObject *get_encryption_password() const;
+};
+
+#include "multifile_ext.I"
+
+#endif // HAVE_PYTHON
+
+#endif // MULTIFILE_EXT_H
diff --git a/panda/src/express/p3express_ext_composite.cxx b/panda/src/express/p3express_ext_composite.cxx
index a8f3311753..90901de263 100644
--- a/panda/src/express/p3express_ext_composite.cxx
+++ b/panda/src/express/p3express_ext_composite.cxx
@@ -3,3 +3,4 @@
#include "stringStream_ext.cxx"
#include "virtualFileSystem_ext.cxx"
#include "virtualFile_ext.cxx"
+#include "multifile_ext.h"
diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I
index a3d5e1d4f7..43d81f4ff2 100644
--- a/panda/src/express/pointerToArray.I
+++ b/panda/src/express/pointerToArray.I
@@ -471,9 +471,9 @@ set_data(const std::string &data) {
template
INLINE std::string PointerToArray::
get_subdata(size_type n, size_type count) const {
- n = std::min(n, size());
- count = std::max(count, n);
- count = std::min(count, size() - n);
+ n = (std::min)(n, size());
+ count = (std::max)(count, n);
+ count = (std::min)(count, size() - n);
return std::string((const char *)(p() + n), sizeof(Element) * count);
}
@@ -965,9 +965,9 @@ get_data() const {
template
INLINE std::string ConstPointerToArray::
get_subdata(size_type n, size_type count) const {
- n = std::min(n, size());
- count = std::max(count, n);
- count = std::min(count, size() - n);
+ n = (std::min)(n, size());
+ count = (std::max)(count, n);
+ count = (std::min)(count, size() - n);
return std::string((const char *)(p() + n), sizeof(Element) * count);
}
diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I
index 69e140c09c..e3b295c934 100644
--- a/panda/src/express/pointerToArray_ext.I
+++ b/panda/src/express/pointerToArray_ext.I
@@ -213,9 +213,9 @@ set_data(PyObject *data) {
template
INLINE PyObject *Extension >::
get_subdata(size_t n, size_t count) const {
- n = std::min(n, this->_this->size());
- count = std::max(count, n);
- count = std::min(count, this->_this->size() - n);
+ n = (std::min)(n, this->_this->size());
+ count = (std::max)(count, n);
+ count = (std::min)(count, this->_this->size() - n);
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
}
@@ -269,9 +269,9 @@ get_data() const {
template
INLINE PyObject *Extension >::
get_subdata(size_t n, size_t count) const {
- n = std::min(n, this->_this->size());
- count = std::max(count, n);
- count = std::min(count, this->_this->size() - n);
+ n = (std::min)(n, this->_this->size());
+ count = (std::max)(count, n);
+ count = (std::min)(count, this->_this->size() - n);
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
}
diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I
index f4d71b1117..dd7e39274c 100644
--- a/panda/src/express/referenceCount.I
+++ b/panda/src/express/referenceCount.I
@@ -27,9 +27,9 @@ TypeHandle RefCountObj::_type_handle;
* inheritance.
*/
INLINE ReferenceCount::
-ReferenceCount() {
- _weak_list = nullptr;
- _ref_count = 0;
+ReferenceCount() :
+ _ref_count(0),
+ _weak_list(nullptr) {
#ifdef DO_MEMORY_USAGE
MemoryUsage::record_pointer(this);
#endif
@@ -44,9 +44,9 @@ ReferenceCount() {
* try.
*/
INLINE ReferenceCount::
-ReferenceCount(const ReferenceCount &) {
- _weak_list = nullptr;
- _ref_count = 0;
+ReferenceCount(const ReferenceCount &) :
+ _ref_count(0),
+ _weak_list(nullptr) {
#ifdef DO_MEMORY_USAGE
MemoryUsage::record_pointer(this);
#endif
@@ -69,7 +69,7 @@ operator = (const ReferenceCount &) {
// to create an automatic (local variable) instance of a class that derives
// from ReferenceCount. Or maybe your headers are out of sync, and you need
// to make clean in direct or some higher tree.
- nassertv(_ref_count != deleted_ref_count);
+ nassertv(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count);
}
/**
@@ -78,23 +78,29 @@ operator = (const ReferenceCount &) {
ReferenceCount::
~ReferenceCount() {
TAU_PROFILE("ReferenceCount::~ReferenceCount()", " ", TAU_USER);
+
+ // We can safely use relaxed ordering for everything in this destructor,
+ // since (1) we already issued an acquire barrier before invoking delete,
+ // and (2) we are the only thread accessing this object at this point.
+ int ref_count = _ref_count.load(std::memory_order_relaxed);
+
// If this assertion fails, we're trying to delete an object that was just
// deleted. Possibly you used a real pointer instead of a PointerTo at some
// point, and the object was deleted when the PointerTo went out of scope.
// Maybe you tried to create an automatic (local variable) instance of a
// class that derives from ReferenceCount. Or maybe your headers are out of
// sync, and you need to make clean in direct or some higher tree.
- nassertv(_ref_count != deleted_ref_count);
+ nassertv(ref_count != deleted_ref_count);
// If this assertion fails, we're trying to delete a static object that
// still has an outstanding reference count. You should make sure that all
// references to your static objects are gone by the time the object itself
// destructs.
- nassertv(_ref_count <= local_ref_count);
+ nassertv(ref_count <= local_ref_count);
// If this assertion fails, the reference counts are all screwed up
// altogether. Maybe some errant code stomped all over memory somewhere.
- nassertv(_ref_count >= 0);
+ nassertv(ref_count >= 0);
// If this assertion fails, someone tried to delete this object while its
// reference count was still positive. Maybe you tried to point a PointerTo
@@ -105,19 +111,20 @@ ReferenceCount::
// Another possibility is you inadvertently omitted a copy constructor for a
// ReferenceCount object, and then bitwise copied a dynamically allocated
// value--reference count and all--onto a locally allocated one.
- nassertv(_ref_count == 0 || _ref_count == local_ref_count);
+ nassertv(ref_count == 0 || ref_count == local_ref_count);
// Tell our weak reference holders that we're going away now.
- if (_weak_list != nullptr) {
+ WeakReferenceList *weak_list = _weak_list.load(std::memory_order_relaxed);
+ if (weak_list != nullptr) {
((WeakReferenceList *)_weak_list)->mark_deleted();
- _weak_list = nullptr;
+ _weak_list.store(nullptr, std::memory_order_release);
}
#ifndef NDEBUG
// Ok, all clear to delete. Now set the reference count to
// deleted_ref_count, so we'll have a better chance of noticing if we happen
// to have a stray pointer to it still out there.
- _ref_count = deleted_ref_count;
+ _ref_count.store(deleted_ref_count, std::memory_order_relaxed);
#endif
#ifdef DO_MEMORY_USAGE
@@ -133,7 +140,7 @@ get_ref_count() const {
#ifdef _DEBUG
test_ref_count_integrity();
#endif
- return (int)AtomicAdjust::get(_ref_count);
+ return _ref_count.load(std::memory_order_acquire);
}
/**
@@ -154,7 +161,7 @@ ref() const {
nassertv(test_ref_count_integrity());
#endif
- AtomicAdjust::inc(_ref_count);
+ _ref_count.fetch_add(1, std::memory_order_relaxed);
}
/**
@@ -184,9 +191,9 @@ unref() const {
// If this assertion fails, you tried to unref an object with a zero
// reference count. Are you using ref() and unref() directly? Are you sure
// you can't use PointerTo's?
- nassertr(_ref_count > 0, 0);
+ nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0);
#endif
- return AtomicAdjust::dec(_ref_count);
+ return _ref_count.fetch_sub(1, std::memory_order_release) != 1;
}
/**
@@ -227,11 +234,11 @@ test_ref_count_nonzero() const {
*/
INLINE void ReferenceCount::
local_object() {
+ int prev_count = _ref_count.exchange(local_ref_count, std::memory_order_relaxed);
+
// If this assertion fails, you didn't call this immediately after creating
// a local object.
- nassertv(_ref_count == 0);
-
- _ref_count = local_ref_count;
+ nassertv(prev_count == 0);
}
/**
@@ -242,7 +249,7 @@ local_object() {
*/
INLINE bool ReferenceCount::
has_weak_list() const {
- return _weak_list != nullptr;
+ return _weak_list.load(std::memory_order_relaxed) != nullptr;
}
/**
@@ -255,10 +262,10 @@ has_weak_list() const {
*/
INLINE WeakReferenceList *ReferenceCount::
get_weak_list() const {
- if (AtomicAdjust::get_ptr(_weak_list) == nullptr) {
+ if (_weak_list.load(std::memory_order_relaxed) == nullptr) {
((ReferenceCount *)this)->create_weak_list();
}
- return (WeakReferenceList *)AtomicAdjust::get_ptr(_weak_list);
+ return _weak_list.load(std::memory_order_consume);
}
/**
@@ -273,7 +280,7 @@ weak_ref() {
#ifdef _DEBUG
nassertr(test_ref_count_integrity(), nullptr);
#else
- nassertr(_ref_count != deleted_ref_count, nullptr);
+ nassertr(_ref_count.load(std::memory_order_relaxed) != deleted_ref_count, nullptr);
#endif
WeakReferenceList *weak_ref = get_weak_list();
weak_ref->ref();
@@ -290,7 +297,7 @@ weak_unref() {
#ifdef _DEBUG
nassertv(test_ref_count_integrity());
#endif
- WeakReferenceList *weak_list = (WeakReferenceList *)_weak_list;
+ WeakReferenceList *weak_list = _weak_list.load(std::memory_order_consume);
nassertv(weak_list != nullptr);
bool nonzero = weak_list->unref();
nassertv(nonzero);
@@ -307,13 +314,16 @@ ref_if_nonzero() const {
#ifdef _DEBUG
test_ref_count_integrity();
#endif
- AtomicAdjust::Integer ref_count;
+ int ref_count = _ref_count.load(std::memory_order_relaxed);
do {
- ref_count = AtomicAdjust::get(_ref_count);
if (ref_count <= 0) {
return false;
}
- } while (ref_count != AtomicAdjust::compare_and_exchange(_ref_count, ref_count, ref_count + 1));
+ }
+ while (!_ref_count.compare_exchange_weak(ref_count, ref_count + 1,
+ std::memory_order_seq_cst,
+ std::memory_order_relaxed));
+
return true;
}
@@ -321,15 +331,21 @@ ref_if_nonzero() const {
* Atomically decreases the reference count of this object if it is one.
* Do not use this. This exists only to implement a special case with the
* state cache.
- * @return false if the reference count was decremented to zero.
+ * @return false on success, ie. if the reference count was decremented to 0.
*/
INLINE bool ReferenceCount::
unref_if_one() const {
#ifdef _DEBUG
nassertr(test_ref_count_integrity(), 0);
- nassertr(_ref_count > 0, 0);
+ nassertr(_ref_count.load(std::memory_order_relaxed) > 0, 0);
#endif
- return (AtomicAdjust::compare_and_exchange(_ref_count, 1, 0) != 1);
+
+ // Presumably if the ref count becomes 0, someone is about to delete the
+ // object or something like that, hence the acquire order on success.
+ int expected = 1;
+ return !_ref_count.compare_exchange_strong(expected, 0,
+ std::memory_order_acquire,
+ std::memory_order_relaxed);
}
/**
@@ -350,6 +366,7 @@ unref_delete(RefCountType *ptr) {
if (!ptr->unref()) {
// If the reference count has gone to zero, delete the object.
+ patomic_thread_fence(std::memory_order_acquire);
delete ptr;
}
}
diff --git a/panda/src/express/referenceCount.cxx b/panda/src/express/referenceCount.cxx
index f446b3be46..0d5f98ea84 100644
--- a/panda/src/express/referenceCount.cxx
+++ b/panda/src/express/referenceCount.cxx
@@ -23,17 +23,19 @@ TypeHandle ReferenceCount::_type_handle;
*/
bool ReferenceCount::
do_test_ref_count_integrity() const {
+ int ref_count = _ref_count.load(std::memory_order_relaxed);
+
// If this assertion fails, we're trying to delete an object that was just
// deleted. Possibly you used a real pointer instead of a PointerTo at some
// point, and the object was deleted when the PointerTo went out of scope.
// Maybe you tried to create an automatic (local variable) instance of a
// class that derives from ReferenceCount. Or maybe your headers are out of
// sync, and you need to make clean in direct or some higher tree.
- nassertr(_ref_count != deleted_ref_count, false);
+ nassertr(ref_count != deleted_ref_count, false);
// If this assertion fails, the reference counts are all screwed up
// altogether. Maybe some errant code stomped all over memory somewhere.
- nassertr(_ref_count >= 0, false);
+ nassertr(ref_count >= 0, false);
return true;
}
@@ -44,7 +46,7 @@ do_test_ref_count_integrity() const {
bool ReferenceCount::
do_test_ref_count_nonzero() const {
nassertr(do_test_ref_count_integrity(), false);
- nassertr(_ref_count > 0, false);
+ nassertr(_ref_count.load(std::memory_order_relaxed) > 0, false);
return true;
}
@@ -54,11 +56,12 @@ do_test_ref_count_nonzero() const {
*/
void ReferenceCount::
create_weak_list() {
- WeakReferenceList *weak_list = new WeakReferenceList;
- void *orig =
- AtomicAdjust::compare_and_exchange_ptr(_weak_list, nullptr, weak_list);
- if (orig != nullptr) {
+ WeakReferenceList *new_list = new WeakReferenceList;
+ WeakReferenceList *old_list = nullptr;
+ if (!_weak_list.compare_exchange_strong(old_list, new_list,
+ std::memory_order_release,
+ std::memory_order_relaxed)) {
// Someone else created it first.
- delete weak_list;
+ delete new_list;
}
}
diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h
index 09ef7b4501..b57b85d4a3 100644
--- a/panda/src/express/referenceCount.h
+++ b/panda/src/express/referenceCount.h
@@ -23,6 +23,7 @@
#include "atomicAdjust.h"
#include "numeric_types.h"
#include "deletedChain.h"
+#include "patomic.h"
#include
@@ -89,8 +90,8 @@ private:
deleted_ref_count = -100,
};
- mutable AtomicAdjust::Integer _ref_count;
- AtomicAdjust::Pointer _weak_list; // WeakReferenceList *
+ mutable patomic _ref_count;
+ patomic _weak_list;
public:
static TypeHandle get_class_type() {
diff --git a/panda/src/express/weakPointerToBase.I b/panda/src/express/weakPointerToBase.I
index 3b27047da1..41206e0994 100644
--- a/panda/src/express/weakPointerToBase.I
+++ b/panda/src/express/weakPointerToBase.I
@@ -33,9 +33,7 @@ WeakPointerToBase(To *ptr) {
template
INLINE WeakPointerToBase::
WeakPointerToBase(const PointerToBase ©) {
- // This double-casting is a bit of a cheat to get around the inheritance
- // issue--it's difficult to declare a template class to be a friend.
- To *ptr = (To *)((const WeakPointerToBase *)©)->_void_ptr;
+ To *ptr = (To *)copy._void_ptr;
_void_ptr = ptr;
if (ptr != nullptr) {
_weak_ref = ptr->weak_ref();
@@ -117,6 +115,7 @@ INLINE WeakPointerToBase::
~WeakPointerToBase() {
WeakReferenceList *old_ref = (WeakReferenceList *)_weak_ref;
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -143,6 +142,7 @@ reassign(To *ptr) {
// Now remove the old reference.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -183,6 +183,7 @@ reassign(const WeakPointerToBase ©) {
// Now remove the old reference.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -205,6 +206,7 @@ reassign(WeakPointerToBase &&from) noexcept {
// Now delete the old pointer.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -233,6 +235,7 @@ reassign(const WeakPointerToBase ©) {
// Now remove the old reference.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -260,6 +263,7 @@ reassign(WeakPointerToBase &&from) noexcept {
// Now delete the old pointer.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
@@ -627,6 +631,7 @@ clear() {
// Now remove the old reference.
if (old_ref != nullptr && !old_ref->unref()) {
+ patomic_thread_fence(std::memory_order_acquire);
delete old_ref;
}
}
diff --git a/panda/src/express/weakReferenceList.I b/panda/src/express/weakReferenceList.I
index eb6e7b9976..c9368cc51b 100644
--- a/panda/src/express/weakReferenceList.I
+++ b/panda/src/express/weakReferenceList.I
@@ -18,7 +18,7 @@
*/
INLINE void WeakReferenceList::
ref() const {
- AtomicAdjust::inc(_count);
+ _count.fetch_add(1, std::memory_order_relaxed);
}
/**
@@ -30,7 +30,7 @@ ref() const {
*/
INLINE bool WeakReferenceList::
unref() const {
- return AtomicAdjust::dec(_count);
+ return _count.fetch_sub(1, std::memory_order_release) != 1;
}
/**
@@ -41,5 +41,5 @@ unref() const {
*/
INLINE bool WeakReferenceList::
was_deleted() const {
- return AtomicAdjust::get(_count) < _alive_offset;
+ return _count.load(std::memory_order_relaxed) < _alive_offset;
}
diff --git a/panda/src/express/weakReferenceList.cxx b/panda/src/express/weakReferenceList.cxx
index ac1363c85a..438a10df1c 100644
--- a/panda/src/express/weakReferenceList.cxx
+++ b/panda/src/express/weakReferenceList.cxx
@@ -27,7 +27,7 @@ WeakReferenceList() : _count(_alive_offset) {
*/
WeakReferenceList::
~WeakReferenceList() {
- nassertv(_count == 0);
+ nassertv(_count.load(std::memory_order_relaxed) == 0);
}
/**
@@ -91,7 +91,7 @@ mark_deleted() {
// Decrement the special offset added to the weak pointer count to indicate
// that it can be deleted when all the weak references have gone.
- AtomicAdjust::Integer result = AtomicAdjust::add(_count, -_alive_offset);
+ int result = _count.fetch_sub(_alive_offset, std::memory_order_relaxed) - _alive_offset;
_lock.unlock();
if (result == 0) {
// There are no weak references remaining either, so delete this.
diff --git a/panda/src/express/weakReferenceList.h b/panda/src/express/weakReferenceList.h
index 30ab68edae..69874741c5 100644
--- a/panda/src/express/weakReferenceList.h
+++ b/panda/src/express/weakReferenceList.h
@@ -17,6 +17,7 @@
#include "pandabase.h"
#include "pmap.h"
#include "mutexImpl.h"
+#include "patomic.h"
class WeakPointerCallback;
@@ -53,8 +54,8 @@ private:
// This has a very large number added to it if the object is still alive.
// It could be 1, but having it be a large number makes it easy to check
// whether the object has been deleted or not.
- static const AtomicAdjust::Integer _alive_offset = (1 << 30);
- mutable AtomicAdjust::Integer _count;
+ static const int _alive_offset = (1 << 30);
+ mutable patomic _count;
friend class ReferenceCount;
};
diff --git a/panda/src/express/zipArchive.cxx b/panda/src/express/zipArchive.cxx
index 64cc2ec497..06bd4f9e28 100644
--- a/panda/src/express/zipArchive.cxx
+++ b/panda/src/express/zipArchive.cxx
@@ -1060,7 +1060,7 @@ close_read_subfile(std::istream *stream) {
// stream pointer does not call the appropriate global delete function;
// instead apparently calling the system delete function. So we call the
// delete function by hand instead.
-#if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
+#if defined(__GNUC__) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
stream->~istream();
(*global_operator_delete)(stream);
#else
@@ -1461,7 +1461,6 @@ read_index() {
uint64_t cdir_entries = 0;
uint64_t cdir_offset = 0;
- uint64_t cdir_size = 0;
uint32_t comment_length = 0;
std::streampos eocd_offset = 0;
bool found = false;
@@ -1482,7 +1481,7 @@ read_index() {
eocd_offset = read->tellg() - (std::streamoff)4;
reader.skip_bytes(6);
cdir_entries = reader.get_uint16();
- cdir_size = reader.get_uint32();
+ /*cdir_size = */reader.get_uint32();
cdir_offset = reader.get_uint32();
if (comment_length > 0) {
_comment = reader.get_fixed_string(comment_length);
@@ -1526,7 +1525,7 @@ read_index() {
if (reader.get_uint32() == 0x06064b50) {
reader.skip_bytes(20);
cdir_entries = reader.get_uint64();
- cdir_size = reader.get_uint64();
+ /*cdir_size = */reader.get_uint64();
cdir_offset = reader.get_uint64();
} else {
express_cat.info()
@@ -1702,9 +1701,9 @@ read_index(std::istream &read) {
return false;
}
- uint16_t version = reader.get_uint8();
+ /*uint16_t version = */reader.get_uint8();
_system = reader.get_uint8();
- uint16_t min_version = reader.get_uint16();
+ /*uint16_t min_version = */reader.get_uint16();
_flags = reader.get_uint16();
_compression_method = (CompressionMethod)reader.get_uint16();
{
@@ -2018,7 +2017,13 @@ write_index(std::ostream &write, streampos &fpos) {
if (_timestamp > dos_epoch) {
// Convert from UNIX timestamp to DOS/FAT timestamp.
+#ifdef _MSC_VER
+ struct tm time_data;
+ struct tm *time = &time_data;
+ localtime_s(time, &_timestamp);
+#else
struct tm *time = localtime(&_timestamp);
+#endif
writer.add_uint16((time->tm_sec >> 1)
| (time->tm_min << 5)
| (time->tm_hour << 11));
@@ -2120,7 +2125,13 @@ write_header(std::ostream &write, std::streampos &fpos) {
if (_timestamp > 315532800) {
// Convert from UNIX timestamp to DOS/FAT timestamp.
+#ifdef _MSC_VER
+ struct tm time_data;
+ struct tm *time = &time_data;
+ localtime_s(time, &_timestamp);
+#else
struct tm *time = localtime(&_timestamp);
+#endif
writer.add_uint16((time->tm_sec >> 1)
| (time->tm_min << 5)
| (time->tm_hour << 11));
diff --git a/panda/src/ffmpeg/CMakeLists.txt b/panda/src/ffmpeg/CMakeLists.txt
index a78a3d2cdc..494289f5a7 100644
--- a/panda/src/ffmpeg/CMakeLists.txt
+++ b/panda/src/ffmpeg/CMakeLists.txt
@@ -26,12 +26,27 @@ set_target_properties(p3ffmpeg PROPERTIES DEFINE_SYMBOL BUILDING_FFMPEG)
target_link_libraries(p3ffmpeg panda
PKG::FFMPEG PKG::SWSCALE PKG::SWRESAMPLE)
+# Do not re-export symbols from these libraries.
+if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavcodec.a")
+ target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavformat.a")
+ target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libavutil.a")
+endif()
+
if(HAVE_SWSCALE)
target_compile_definitions(p3ffmpeg PRIVATE HAVE_SWSCALE)
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libswscale.a")
+ endif()
endif()
if(HAVE_SWRESAMPLE)
target_compile_definitions(p3ffmpeg PRIVATE HAVE_SWRESAMPLE)
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ target_link_options(p3ffmpeg PRIVATE "LINKER:--exclude-libs,libswresample.a")
+ endif()
endif()
install(TARGETS p3ffmpeg
diff --git a/panda/src/ffmpeg/ffmpegAudio.h b/panda/src/ffmpeg/ffmpegAudio.h
index 6559f59ff5..fe22f93c23 100644
--- a/panda/src/ffmpeg/ffmpegAudio.h
+++ b/panda/src/ffmpeg/ffmpegAudio.h
@@ -38,7 +38,7 @@ public:
return _type_handle;
}
static void init_type() {
- TypedWritableReferenceCount::init_type();
+ MovieAudio::init_type();
register_type(_type_handle, "FfmpegAudio",
MovieAudio::get_class_type());
}
diff --git a/panda/src/ffmpeg/ffmpegAudioCursor.cxx b/panda/src/ffmpeg/ffmpegAudioCursor.cxx
index 47a00fbee9..8d2c6d2aec 100644
--- a/panda/src/ffmpeg/ffmpegAudioCursor.cxx
+++ b/panda/src/ffmpeg/ffmpegAudioCursor.cxx
@@ -50,10 +50,10 @@ FfmpegAudioCursor(FfmpegAudio *src) :
_packet_data(nullptr),
_format_ctx(nullptr),
_audio_ctx(nullptr),
- _resample_ctx(nullptr),
+ _frame(nullptr),
_buffer(nullptr),
_buffer_alloc(nullptr),
- _frame(nullptr)
+ _resample_ctx(nullptr)
{
if (!_ffvfile.open_vfs(_filename)) {
cleanup();
diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx
index 5cc3d2cffb..aa47af9163 100644
--- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx
+++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx
@@ -47,6 +47,7 @@ FfmpegVideoCursor::
FfmpegVideoCursor() :
_max_readahead_frames(0),
_thread_priority(ffmpeg_thread_priority),
+ _pixel_format((int)AV_PIX_FMT_NONE),
_lock("FfmpegVideoCursor::_lock"),
_action_cvar(_lock),
_thread_status(TS_stopped),
@@ -55,7 +56,6 @@ FfmpegVideoCursor() :
_format_ctx(nullptr),
_video_ctx(nullptr),
_convert_ctx(nullptr),
- _pixel_format((int)AV_PIX_FMT_NONE),
_video_index(-1),
_frame(nullptr),
_frame_out(nullptr),
@@ -698,7 +698,7 @@ thread_main() {
while (do_poll()) {
// Keep doing stuff as long as there's something to do.
_lock.release();
- PStatClient::thread_tick(_sync_name);
+ PStatClient::thread_tick();
Thread::consider_yield();
_lock.acquire();
}
diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx
index 7d65fdd976..e4c3aedf36 100644
--- a/panda/src/glstuff/glCgShaderContext_src.cxx
+++ b/panda/src/glstuff/glCgShaderContext_src.cxx
@@ -1113,14 +1113,14 @@ update_shader_texture_bindings(ShaderContext *prev) {
continue;
}
- if (spec._suffix != 0) {
+ if (spec._suffix != nullptr) {
// The suffix feature is inefficient. It is a temporary hack.
- if (tex == 0) {
+ if (tex == nullptr) {
continue;
}
tex = tex->load_related(spec._suffix);
}
- if ((tex == 0) || (tex->get_texture_type() != spec._desired_type)) {
+ if (tex == nullptr || tex->get_texture_type() != spec._desired_type) {
continue;
}
diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx
index 289ca4530a..1892cb65fd 100644
--- a/panda/src/glstuff/glGraphicsBuffer_src.cxx
+++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx
@@ -30,12 +30,12 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
GraphicsStateGuardian *gsg,
GraphicsOutput *host) :
GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
- _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"),
- _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"),
- _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples"),
_requested_multisamples(0),
_requested_coverage_samples(0),
- _rb_context(nullptr)
+ _rb_context(nullptr),
+ _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"),
+ _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"),
+ _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples")
{
// A FBO doesn't have a back buffer.
_draw_buffer_type = RenderBuffer::T_front;
@@ -360,7 +360,7 @@ check_fbo() {
void CLP(GraphicsBuffer)::
rebuild_bitplanes() {
check_host_valid();
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
return;
}
@@ -855,10 +855,74 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
gl_format = GL_DEPTH_COMPONENT16;
}
break;
+#ifndef OPENGLES_1
+ case RTP_aux_hrgba_0:
+ case RTP_aux_hrgba_1:
+ case RTP_aux_hrgba_2:
+ case RTP_aux_hrgba_3:
+ case RTP_aux_float_0:
+ case RTP_aux_float_1:
+ case RTP_aux_float_2:
+ case RTP_aux_float_3:
+ if (glgsg->has_extension("GL_EXT_color_buffer_float")) {
+ if (slot >= RTP_aux_float_0 && slot <= RTP_aux_float_3) {
+ gl_format = GL_RGBA32F;
+ } else {
+ gl_format = GL_RGBA16F;
+ }
+ }
+ else if (glgsg->has_extension("GL_EXT_color_buffer_half_float")) {
+ gl_format = GL_RGBA16F_EXT;
+ }
+#endif
// NB: we currently use RTP_stencil to store the right eye for stereo.
// case RTP_stencil: gl_format = GL_STENCIL_INDEX8; break
default:
if (_fb_properties.get_alpha_bits() == 0) {
+#ifndef OPENGLES_1
+ if (_fb_properties.get_float_color() &&
+ glgsg->has_extension("GL_EXT_color_buffer_float")) {
+ // This extension supports the full range of floating-point formats.
+ if (_fb_properties.get_color_bits() > 16 * 3 ||
+ _fb_properties.get_red_bits() > 16 ||
+ _fb_properties.get_green_bits() > 16 ||
+ _fb_properties.get_blue_bits() > 16) {
+ // 32-bit, which is always floating-point.
+ if (_fb_properties.get_blue_bits() > 0 ||
+ _fb_properties.get_color_bits() == 1 ||
+ _fb_properties.get_color_bits() > 32 * 2) {
+ gl_format = GL_RGB32F;
+ } else if (_fb_properties.get_green_bits() > 0 ||
+ _fb_properties.get_color_bits() > 32) {
+ gl_format = GL_RG32F;
+ } else {
+ gl_format = GL_R32F;
+ }
+ } else {
+ // 16-bit floating-point.
+ if (_fb_properties.get_blue_bits() > 10 ||
+ _fb_properties.get_color_bits() == 1 ||
+ _fb_properties.get_color_bits() > 32) {
+ gl_format = GL_RGB16F;
+ } else if (_fb_properties.get_blue_bits() > 0) {
+ if (_fb_properties.get_red_bits() > 11 ||
+ _fb_properties.get_green_bits() > 11) {
+ gl_format = GL_RGB16F;
+ } else {
+ gl_format = GL_R11F_G11F_B10F;
+ }
+ } else if (_fb_properties.get_green_bits() > 0 ||
+ _fb_properties.get_color_bits() > 16) {
+ gl_format = GL_RG16F;
+ } else {
+ gl_format = GL_R16F;
+ }
+ }
+ } else if (_fb_properties.get_float_color() &&
+ glgsg->has_extension("GL_EXT_color_buffer_half_float")) {
+ gl_format = GL_RGB16F_EXT;
+ } else
+#endif
if (_fb_properties.get_color_bits() <= 16) {
gl_format = GL_RGB565_OES;
} else if (_fb_properties.get_color_bits() <= 24) {
@@ -866,6 +930,18 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
} else {
gl_format = GL_RGB10_EXT;
}
+#ifndef OPENGLES_1
+ } else if (_fb_properties.get_float_color() &&
+ glgsg->has_extension("GL_EXT_color_buffer_float")) {
+ if (_fb_properties.get_color_bits() > 16 * 3) {
+ gl_format = GL_RGBA32F;
+ } else {
+ gl_format = GL_RGBA16F;
+ }
+ } else if (_fb_properties.get_float_color() &&
+ glgsg->has_extension("GL_EXT_color_buffer_half_float")) {
+ gl_format = GL_RGBA16F_EXT;
+#endif
} else if (_fb_properties.get_color_bits() == 0) {
gl_format = GL_ALPHA8_EXT;
} else if (_fb_properties.get_color_bits() <= 12
@@ -1661,7 +1737,7 @@ close_buffer() {
check_host_valid();
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
return;
}
@@ -1824,7 +1900,7 @@ unregister_shared_depth_buffer(GraphicsOutput *graphics_output) {
*/
void CLP(GraphicsBuffer)::
report_my_errors(int line, const char *file) {
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
GLenum error_code = glGetError();
if (error_code != GL_NO_ERROR) {
GLCAT.error() << file << ", line " << line << ": GL error " << (int)error_code << "\n";
diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx
index 11d66699c0..c1155efa05 100644
--- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx
+++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx
@@ -4162,12 +4162,21 @@ begin_frame(Thread *current_thread) {
_primitive_batches_display_list_pcollector.clear_level();
#endif
+#if defined(DO_PSTATS) && !defined(OPENGLES)
+ int frame_number = ClockObject::get_global_clock()->get_frame_count(current_thread);
+ if (_current_frame_timing == nullptr ||
+ frame_number != _current_frame_timing->_frame_number) {
+
+ _current_frame_timing = begin_frame_timing(frame_number);
+ }
+#endif
+
#ifndef NDEBUG
_show_texture_usage = false;
if (gl_show_texture_usage) {
// When this is true, then every other second, we show the usage textures
// instead of the real textures.
- double now = ClockObject::get_global_clock()->get_frame_time();
+ double now = ClockObject::get_global_clock()->get_frame_time(current_thread);
int this_second = (int)floor(now);
if (this_second & 1) {
_show_texture_usage = true;
@@ -4190,16 +4199,6 @@ begin_frame(Thread *current_thread) {
}
#endif // NDEBUG
-#ifdef DO_PSTATS
- /*if (_supports_timer_query) {
- // Measure the difference between the OpenGL clock and the PStats clock.
- GLint64 time_ns;
- _glGetInteger64v(GL_TIMESTAMP, &time_ns);
- _timer_delta = time_ns * -0.000000001;
- _timer_delta += PStatClient::get_global_pstats()->get_real_time();
- }*/
-#endif
-
#ifndef OPENGLES
if (_current_properties->get_srgb_color()) {
glEnable(GL_FRAMEBUFFER_SRGB);
@@ -4406,6 +4405,129 @@ end_frame(Thread *current_thread) {
}
}
+/**
+ *
+ */
+CLP(GraphicsStateGuardian)::FrameTiming *CLP(GraphicsStateGuardian)::
+begin_frame_timing(int frame_number) {
+#if defined(DO_PSTATS) && !defined(OPENGLES)
+ if (!_timer_queries_active) {
+ if (pstats_gpu_timing && _supports_timer_query && PStatClient::is_connected()) {
+ _timer_queries_active = true;
+ } else {
+ return nullptr;
+ }
+ }
+
+ PStatClient *client = PStatClient::get_global_pstats();
+ PStatTimer timer(_wait_timer_pcollector);
+
+ _timer_queries_pcollector.clear_level();
+
+ if (_deleted_queries.size() < 128) {
+ // We'll need a lot of timer queries, so allocate a whole bunch up front.
+ size_t alloc_count = 128 - _deleted_queries.size();
+ _deleted_queries.resize(_deleted_queries.size() + alloc_count);
+ _glGenQueries(alloc_count, _deleted_queries.data() + _deleted_queries.size() - alloc_count);
+ }
+
+ // Issue a start query for collector 0, marking the start of this frame.
+ GLuint frame_query = _deleted_queries.back();
+ _deleted_queries.pop_back();
+ _glQueryCounter(frame_query, GL_TIMESTAMP);
+
+ // Synchronize the GL time with the PStats clock. Note that the driver may
+ // arbitrarily decide to wait a long time on this call if the queue is
+ // saturated with work. Experimentally, it seems that it queries the time
+ // *after* the wait, so we take the CPU time after it returns. If we find
+ // that some drivers do it differently, we may have to do multiple calls.
+ GLint64 gl_time;
+ _glGetInteger64v(GL_TIMESTAMP, &gl_time);
+ double cpu_time = client->get_real_time();
+
+ // Check if the results from the previous frame are available. We just need
+ // to check whether the last query for each frame is available.
+ while (!_frame_timings.empty()) {
+ const FrameTiming &frame = _frame_timings.front();
+ GLuint last_query = frame._queries.back().first;
+ GLuint result;
+ _glGetQueryObjectuiv(last_query, GL_QUERY_RESULT_AVAILABLE, &result);
+ if (result == 0) {
+ // Not ready, so subsequent frames won't be, either.
+ break;
+ }
+ // We've got a frame whose timer queries are ready.
+ end_frame_timing(frame);
+ _frame_timings.pop_front();
+ }
+
+ FrameTiming frame;
+ frame._frame_number = frame_number;
+ frame._gpu_sync_time = gl_time;
+ frame._cpu_sync_time = cpu_time;
+ frame._queries.push_back(std::make_pair(frame_query, 0));
+ _frame_timings.push_back(std::move(frame));
+
+ return &_frame_timings.back();
+#else
+ return nullptr;
+#endif
+}
+
+/**
+ * Gets the timer query results for the given frame and sends them to the
+ * PStats server.
+ */
+void CLP(GraphicsStateGuardian)::
+end_frame_timing(const FrameTiming &frame) {
+#if defined(DO_PSTATS) && !defined(OPENGLES)
+ // 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.
+ if (!PStatClient::is_connected()) {
+ _timer_queries_active = false;
+ return;
+ }
+
+ // We represent each GSG as one thread. In the future we may change this to
+ // representing each graphics device as one thread, but OpenGL doesn't really
+ // expose this information to us.
+ PStatThread gpu_thread = get_pstats_thread();
+
+ PStatFrameData frame_data;
+ size_t latency_ref_i = 0;
+
+ for (auto &query : frame._queries) {
+ GLuint64 time_ns;
+ _glGetQueryObjectui64v(query.first, GL_QUERY_RESULT, &time_ns);
+
+ if (query.second & 0x10000) {
+ // Latency query.
+ GLint64 ref = frame._latency_refs[latency_ref_i++];
+ double time = ((GLint64)time_ns - ref) * 0.000001;
+ frame_data.add_level(query.second & 0x7fff, time);
+ }
+ else {
+ // Convert GL time to Panda time.
+ double time = ((GLint64)time_ns - frame._gpu_sync_time) * 0.000000001 + frame._cpu_sync_time;
+ if (query.second & 0x8000) {
+ frame_data.add_stop(query.second & 0x7fff, time);
+ }
+ else {
+ frame_data.add_start(query.second & 0x7fff, time);
+ }
+ }
+ _deleted_queries.push_back(query.first);
+ }
+
+ // The end time of the last collector is implicitly the frame's end time.
+ frame_data.add_stop(0, frame_data.get_end());
+ gpu_thread.add_frame(frame._frame_number, frame_data);
+
+ _timer_queries_pcollector.add_level_now(frame._queries.size());
+#endif
+}
+
/**
* Called before a sequence of draw_primitive() functions are called, this
* should prepare the vertex data for rendering. It returns true if the
@@ -7149,48 +7271,57 @@ end_occlusion_query() {
* Adds a timer query to the command stream, associated with the given PStats
* collector index.
*/
-PT(TimerQueryContext) CLP(GraphicsStateGuardian)::
+void CLP(GraphicsStateGuardian)::
issue_timer_query(int pstats_index) {
#if defined(DO_PSTATS) && !defined(OPENGLES)
- nassertr(_supports_timer_query, nullptr);
-
- PT(CLP(TimerQueryContext)) query;
-
- // Hack
- if (pstats_index == _command_latency_pcollector.get_index()) {
- query = new CLP(LatencyQueryContext)(this, pstats_index);
- } else {
- query = new CLP(TimerQueryContext)(this, pstats_index);
+ FrameTiming *frame = _current_frame_timing;
+ if (frame == nullptr) {
+ return;
}
- if (_deleted_queries.size() >= 1) {
- query->_index = _deleted_queries.back();
- _deleted_queries.pop_back();
- } else {
- _glGenQueries(1, &query->_index);
+ nassertv(_supports_timer_query);
- if (GLCAT.is_spam()) {
- GLCAT.spam() << "Generating query for " << pstats_index
- << ": " << query->_index << "\n";
- }
+ if (_deleted_queries.empty()) {
+ // Allocate some number at a time, since we'll need a lot of these.
+ _deleted_queries.resize(_deleted_queries.size() + 16);
+ _glGenQueries(16, _deleted_queries.data() + _deleted_queries.size() - 16);
}
+ GLuint index = _deleted_queries.back();
+ _deleted_queries.pop_back();
+
// Issue the timestamp query.
- _glQueryCounter(query->_index, GL_TIMESTAMP);
+ _glQueryCounter(index, GL_TIMESTAMP);
- if (_use_object_labels) {
- // Assign a label to it based on the PStatCollector name.
- const PStatClient *client = PStatClient::get_global_pstats();
- string name = client->get_collector_fullname(pstats_index & 0x7fff);
- _glObjectLabel(GL_QUERY, query->_index, name.size(), name.data());
+ //if (_use_object_labels) {
+ // // Assign a label to it based on the PStatCollector name.
+ // const PStatClient *client = PStatClient::get_global_pstats();
+ // string name = client->get_collector_fullname(pstats_index & 0x7fff);
+ // _glObjectLabel(GL_QUERY, index, name.size(), name.data());
+ //}
+
+ frame->_queries.push_back(std::make_pair(index, pstats_index));
+#endif
+}
+
+/**
+ * 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 CLP(GraphicsStateGuardian)::
+issue_latency_query(int pstats_index) {
+#if defined(DO_PSTATS) && !defined(OPENGLES)
+ FrameTiming *frame = _current_frame_timing;
+ if (frame == nullptr) {
+ return;
}
- _pending_timer_queries.push_back((TimerQueryContext *)query);
+ GLint64 time;
+ _glGetInteger64v(GL_TIMESTAMP, &time);
+ issue_timer_query(pstats_index | 0x10000);
- return (TimerQueryContext *)query;
-
-#else
- return nullptr;
+ frame->_latency_refs.push_back(time);
#endif
}
@@ -10560,7 +10691,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
case Texture::F_rgba:
case Texture::F_rgbm:
#ifndef OPENGLES_1
- if (component_type == Texture::T_float) {
+ if (component_type == Texture::T_float ||
+ component_type == Texture::T_half_float) {
return GL_RGBA16F;
} else
#endif
@@ -10626,20 +10758,28 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
#endif // OPENGLES
#ifndef OPENGLES
case Texture::F_rgba16:
- if (component_type == Texture::T_float) {
+ if (component_type == Texture::T_float ||
+ component_type == Texture::T_half_float) {
return GL_RGBA16F;
} else if (Texture::is_unsigned(component_type)) {
return GL_RGBA16;
} else {
return GL_RGBA16_SNORM;
}
+#elif !defined(OPENGLES_1)
+ case Texture::F_rgba16:
+ return GL_RGBA16F;
+#endif // OPENGLES
+
+#ifndef OPENGLES_1
case Texture::F_rgba32:
return GL_RGBA32F;
-#endif // OPENGLES
+#endif
case Texture::F_rgb:
switch (component_type) {
case Texture::T_float: return GL_RGB16F;
+ case Texture::T_half_float: return GL_RGB16F;
#ifndef OPENGLES
case Texture::T_unsigned_short: return GL_RGB16;
case Texture::T_short: return GL_RGB16_SNORM;
@@ -10676,7 +10816,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
case Texture::F_rgb12:
return GL_RGB12;
case Texture::F_rgb16:
- if (component_type == Texture::T_float) {
+ if (component_type == Texture::T_float ||
+ component_type == Texture::T_half_float) {
return GL_RGB16F;
} else if (Texture::is_unsigned(component_type)) {
return GL_RGB16;
@@ -10699,7 +10840,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
return GL_RG16F_EXT;
#elif !defined(OPENGLES_1)
case Texture::F_r16:
- if (component_type == Texture::T_float) {
+ if (component_type == Texture::T_float ||
+ component_type == Texture::T_half_float) {
return GL_R16F;
} else if (Texture::is_unsigned(component_type)) {
return GL_R16;
@@ -10707,7 +10849,8 @@ get_internal_image_format(Texture *tex, bool force_sized) const {
return GL_R16_SNORM;
}
case Texture::F_rg16:
- if (component_type == Texture::T_float) {
+ if (component_type == Texture::T_float ||
+ component_type == Texture::T_half_float) {
return GL_RG16F;
} else if (Texture::is_unsigned(component_type)) {
return GL_RG16;
@@ -11743,7 +11886,7 @@ set_state_and_transform(const RenderState *target,
#endif
_state_pcollector.add_level(1);
- PStatGPUTimer timer1(this, _draw_set_state_pcollector);
+ PStatTimer timer1(_draw_set_state_pcollector);
bool transform_changed = transform != _internal_transform;
if (transform_changed) {
@@ -11934,7 +12077,7 @@ set_state_and_transform(const RenderState *target,
int texture_slot = TextureAttrib::get_class_slot();
if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
!_state_mask.get_bit(texture_slot)) {
- PStatGPUTimer timer(this, _draw_set_state_texture_pcollector);
+ //PStatGPUTimer timer(this, _draw_set_state_texture_pcollector);
determine_target_texture();
do_issue_texture();
diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h
index c0af1f5228..ef8f262da7 100644
--- a/panda/src/glstuff/glGraphicsStateGuardian_src.h
+++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h
@@ -293,6 +293,10 @@ public:
virtual void end_scene();
virtual void end_frame(Thread *current_thread);
+ struct FrameTiming;
+ FrameTiming *begin_frame_timing(int frame_index);
+ void end_frame_timing(const FrameTiming &frame);
+
virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
const GeomVertexDataPipelineReader *data_reader,
size_t num_instances, bool force);
@@ -384,7 +388,8 @@ public:
virtual PT(OcclusionQueryContext) end_occlusion_query();
#endif
- virtual PT(TimerQueryContext) issue_timer_query(int pstats_index);
+ virtual void issue_timer_query(int pstats_index) final;
+ virtual void issue_latency_query(int pstats_index) final;
#ifndef OPENGLES_1
virtual void dispatch_compute(int size_x, int size_y, int size_z);
@@ -1148,6 +1153,20 @@ public:
UsageTextures _usage_textures;
#endif // NDEBUG
+#if defined(DO_PSTATS) && !defined(OPENGLES)
+ struct FrameTiming {
+ int _frame_number;
+ GLint64 _gpu_sync_time;
+ double _cpu_sync_time;
+ pvector > _queries;
+ pvector _latency_refs;
+ };
+ GLint64 _gpu_reference_time = 0;
+ double _cpu_reference_time;
+ pdeque _frame_timings;
+ FrameTiming *_current_frame_timing = nullptr;
+#endif
+
BufferResidencyTracker _renderbuffer_residency;
static PStatCollector _load_display_list_pcollector;
@@ -1188,7 +1207,6 @@ private:
friend class CLP(CgShaderContext);
friend class CLP(GraphicsBuffer);
friend class CLP(OcclusionQueryContext);
- friend class CLP(TimerQueryContext);
};
#include "glGraphicsStateGuardian_src.I"
diff --git a/panda/src/glstuff/glLatencyQueryContext_src.I b/panda/src/glstuff/glLatencyQueryContext_src.I
deleted file mode 100644
index adc45cdf5e..0000000000
--- a/panda/src/glstuff/glLatencyQueryContext_src.I
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * 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 glLatencyQueryContext_src.I
- * @author rdb
- * @date 2014-09-24
- */
diff --git a/panda/src/glstuff/glLatencyQueryContext_src.cxx b/panda/src/glstuff/glLatencyQueryContext_src.cxx
deleted file mode 100644
index a08ab74517..0000000000
--- a/panda/src/glstuff/glLatencyQueryContext_src.cxx
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * 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 glLatencyQueryContext_src.cxx
- * @author rdb
- * @date 2014-09-24
- */
-
-#ifndef OPENGLES // Timer queries not supported by OpenGL ES.
-
-TypeHandle CLP(LatencyQueryContext)::_type_handle;
-
-/**
- *
- */
-CLP(LatencyQueryContext)::
-CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
- int pstats_index) :
- CLP(TimerQueryContext)(glgsg, pstats_index),
- _timestamp(0)
-{
- glgsg->_glGetInteger64v(GL_TIMESTAMP, &_timestamp);
-}
-
-/**
- * Returns the timestamp that is the result of this timer query. There's no
- * guarantee about which clock this uses, the only guarantee is that
- * subtracting a start time from an end time should yield a time in seconds.
- * If is_answer_ready() did not return true, this function may block before it
- * returns.
- *
- * It is only valid to call this from the draw thread.
- */
-double CLP(LatencyQueryContext)::
-get_timestamp() const {
- GLint64 time_ns;
- _glgsg->_glGetQueryObjecti64v(_index, GL_QUERY_RESULT, &time_ns);
-
- return (time_ns - _timestamp) * 0.000000001;
-}
-
-#endif // OPENGLES
diff --git a/panda/src/glstuff/glLatencyQueryContext_src.h b/panda/src/glstuff/glLatencyQueryContext_src.h
deleted file mode 100644
index 7c255cb5df..0000000000
--- a/panda/src/glstuff/glLatencyQueryContext_src.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * 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 glLatencyQueryContext_src.h
- * @author rdb
- * @date 2014-09-24
- */
-
-class GraphicsStateGuardian;
-
-#ifndef OPENGLES // Timer queries not supported by OpenGL ES.
-
-/**
- * This is a special variant of GLTimerQueryContext that measures the command
- * latency, ie. the time it takes for the GPU to actually get to the commands
- * we are issuing right now.
- */
-class EXPCL_GL CLP(LatencyQueryContext) : public CLP(TimerQueryContext) {
-public:
- CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index);
-
- ALLOC_DELETED_CHAIN(CLP(LatencyQueryContext));
-
- virtual double get_timestamp() const;
-
- GLint64 _timestamp;
-
-public:
- static TypeHandle get_class_type() {
- return _type_handle;
- }
- static void init_type() {
- CLP(TimerQueryContext)::init_type();
- register_type(_type_handle, CLASSPREFIX_QUOTED "LatencyQueryContext",
- CLP(TimerQueryContext)::get_class_type());
- }
- virtual TypeHandle get_type() const {
- return get_class_type();
- }
- virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
- static TypeHandle _type_handle;
-};
-
-#include "glLatencyQueryContext_src.I"
-
-#endif // OPENGLES
diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx
index 8260d1430d..520f2478e3 100644
--- a/panda/src/glstuff/glShaderContext_src.cxx
+++ b/panda/src/glstuff/glShaderContext_src.cxx
@@ -2105,7 +2105,7 @@ set_state_and_transform(const RenderState *target_rs,
*/
void CLP(ShaderContext)::
issue_parameters(int altered) {
- PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector);
+ PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector);
if (GLCAT.is_spam()) {
GLCAT.spam()
@@ -2689,7 +2689,9 @@ update_shader_texture_bindings(ShaderContext *prev) {
return;
}
+#ifndef OPENGLES
GLbitfield barriers = 0;
+#endif
// First bind all the 'image units'; a bit of an esoteric OpenGL feature
// right now.
@@ -2738,9 +2740,11 @@ update_shader_texture_bindings(ShaderContext *prev) {
_glgsg->update_texture(gtc, true);
gl_tex = gtc->_index;
+#ifndef OPENGLES
if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
}
+#endif
}
}
input._writable = false;
@@ -2858,6 +2862,12 @@ update_shader_texture_bindings(ShaderContext *prev) {
<< "Sampler type of GLSL shader input p3d_LightSource[" << spec._stage
<< "].shadowMap does not match type of texture " << *tex << ".\n";
break;
+
+ default:
+ GLCAT.error()
+ << "Sampler type of GLSL shader input does not match type of "
+ "texture " << *tex << ".\n";
+ break;
}
// TODO: also check whether shadow sampler textures have shadow filter
// enabled.
diff --git a/panda/src/glstuff/glTimerQueryContext_src.I b/panda/src/glstuff/glTimerQueryContext_src.I
deleted file mode 100644
index c898c30261..0000000000
--- a/panda/src/glstuff/glTimerQueryContext_src.I
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * 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 glTimerQueryContext_src.I
- * @author rdb
- * @date 2014-08-22
- */
-
-/**
- *
- */
-INLINE CLP(TimerQueryContext)::
-CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
- int pstats_index) :
- TimerQueryContext(pstats_index),
- _glgsg(glgsg),
- _index(0)
-{
-}
diff --git a/panda/src/glstuff/glTimerQueryContext_src.cxx b/panda/src/glstuff/glTimerQueryContext_src.cxx
deleted file mode 100644
index a7b4ecb612..0000000000
--- a/panda/src/glstuff/glTimerQueryContext_src.cxx
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * 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 glTimerQueryContext_src.cxx
- * @author rdb
- * @date 2014-08-22
- */
-
-#include "pnotify.h"
-#include "dcast.h"
-#include "lightMutexHolder.h"
-#include "pStatTimer.h"
-
-#ifndef OPENGLES // Timer queries not supported by OpenGL ES.
-
-TypeHandle CLP(TimerQueryContext)::_type_handle;
-
-/**
- *
- */
-CLP(TimerQueryContext)::
-~CLP(TimerQueryContext)() {
- if (_index != 0) {
- // Tell the GSG to recycle this index when it gets around to it. If it
- // has already shut down, though, too bad. This means we never get to
- // free this index, but presumably the app is already shutting down
- // anyway.
- if (auto glgsg = _glgsg.lock()) {
- LightMutexHolder holder(glgsg->_lock);
- glgsg->_deleted_queries.push_back(_index);
- _index = 0;
- }
- }
-}
-
-/**
- * Returns true if the query's answer is ready, false otherwise. If this
- * returns false, the application must continue to poll until it returns true.
- *
- * It is only valid to call this from the draw thread.
- */
-bool CLP(TimerQueryContext)::
-is_answer_ready() const {
- GLuint result;
- _glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT_AVAILABLE, &result);
-
- return (result != 0);
-}
-
-/**
- * Requests the graphics engine to expedite the pending answer--the
- * application is now waiting until the answer is ready.
- *
- * It is only valid to call this from the draw thread.
- */
-void CLP(TimerQueryContext)::
-waiting_for_answer() {
- PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector);
- glFlush();
-}
-
-/**
- * Returns the timestamp that is the result of this timer query. There's no
- * guarantee about which clock this uses, the only guarantee is that
- * subtracting a start time from an end time should yield a time in seconds.
- * If is_answer_ready() did not return true, this function may block before it
- * returns.
- *
- * It is only valid to call this from the draw thread.
- */
-double CLP(TimerQueryContext)::
-get_timestamp() const {
- GLuint64 time_ns;
-
- /*GLuint available;
- _glgsg->_glGetQueryObjectuiv(_index[1], GL_QUERY_RESULT_AVAILABLE, &available);
- if (available) {
- // The answer is ready now.
- do_get_timestamps(begin_ns, end_ns);
- } else {
- // The answer is not ready; this call will block.
- PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector);
- do_get_timestamps(begin_ns, end_ns);
- }*/
-
- _glgsg->_glGetQueryObjectui64v(_index, GL_QUERY_RESULT, &time_ns);
-
- return time_ns * 0.000000001;
-}
-
-#endif // OPENGLES
diff --git a/panda/src/glstuff/glTimerQueryContext_src.h b/panda/src/glstuff/glTimerQueryContext_src.h
deleted file mode 100644
index 07709bc7af..0000000000
--- a/panda/src/glstuff/glTimerQueryContext_src.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * 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 glTimerQueryContext_src.h
- * @author rdb
- * @date 2014-08-22
- */
-
-#include "pandabase.h"
-#include "timerQueryContext.h"
-#include "deletedChain.h"
-#include "clockObject.h"
-
-class GraphicsStateGuardian;
-
-#ifndef OPENGLES // Timer queries not supported by OpenGL ES.
-
-/**
- * This class manages a timer query that can be used by a PStatGPUTimer to
- * measure the time a task takes to execute on the GPU. This records the
- * current timestamp; a pair of these is usually used to get the elapsed time.
- */
-class EXPCL_GL CLP(TimerQueryContext) : public TimerQueryContext {
-public:
- INLINE CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
- int pstats_index);
- virtual ~CLP(TimerQueryContext)();
-
- ALLOC_DELETED_CHAIN(CLP(TimerQueryContext));
-
- virtual bool is_answer_ready() const;
- virtual void waiting_for_answer();
- virtual double get_timestamp() const;
-
- GLuint _index;
- WPT(CLP(GraphicsStateGuardian)) _glgsg;
-
-public:
- static TypeHandle get_class_type() {
- return _type_handle;
- }
- static void init_type() {
- TimerQueryContext::init_type();
- register_type(_type_handle, CLASSPREFIX_QUOTED "TimerQueryContext",
- TimerQueryContext::get_class_type());
- }
- virtual TypeHandle get_type() const {
- return get_class_type();
- }
- virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
- static TypeHandle _type_handle;
-};
-
-#include "glTimerQueryContext_src.I"
-
-#endif // OPENGLES
diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx
index 2c388985f6..2d6db9f654 100644
--- a/panda/src/glstuff/glmisc_src.cxx
+++ b/panda/src/glstuff/glmisc_src.cxx
@@ -354,8 +354,6 @@ void CLP(init_classes)() {
#ifndef OPENGLES
CLP(OcclusionQueryContext)::init_type();
- CLP(TimerQueryContext)::init_type();
- CLP(LatencyQueryContext)::init_type();
#endif
PandaSystem *ps = PandaSystem::get_global_ptr();
diff --git a/panda/src/glstuff/glstuff_src.cxx b/panda/src/glstuff/glstuff_src.cxx
index 06abef89f4..fc281d1749 100644
--- a/panda/src/glstuff/glstuff_src.cxx
+++ b/panda/src/glstuff/glstuff_src.cxx
@@ -23,8 +23,6 @@
#include "glIndexBufferContext_src.cxx"
#include "glBufferContext_src.cxx"
#include "glOcclusionQueryContext_src.cxx"
-#include "glTimerQueryContext_src.cxx"
-#include "glLatencyQueryContext_src.cxx"
#include "glGeomContext_src.cxx"
#include "glGeomMunger_src.cxx"
#include "glShaderContext_src.cxx"
diff --git a/panda/src/glstuff/glstuff_src.h b/panda/src/glstuff/glstuff_src.h
index 02e3208445..21ca8ad8fe 100644
--- a/panda/src/glstuff/glstuff_src.h
+++ b/panda/src/glstuff/glstuff_src.h
@@ -35,8 +35,6 @@
#include "glIndexBufferContext_src.h"
#include "glBufferContext_src.h"
#include "glOcclusionQueryContext_src.h"
-#include "glTimerQueryContext_src.h"
-#include "glLatencyQueryContext_src.h"
#include "glGeomContext_src.h"
#include "glGeomMunger_src.h"
#include "glShaderContext_src.h"
diff --git a/panda/src/glxdisplay/panda_glxext.h b/panda/src/glxdisplay/panda_glxext.h
index 5208302929..05a697c8ec 100644
--- a/panda/src/glxdisplay/panda_glxext.h
+++ b/panda/src/glxdisplay/panda_glxext.h
@@ -1,42 +1,21 @@
-#ifndef panda__glxext_h_
-#define panda__glxext_h_ 1
+#ifndef __glx_glxext_h_
+#define __glx_glxext_h_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
-** Copyright (c) 2013-2014 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: MIT
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
** This header is generated from the Khronos OpenGL / OpenGL ES XML
** API Registry. The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
-** http://www.opengl.org/registry/
-**
-** Khronos $Revision$ on $Date$
+** https://github.com/KhronosGroup/OpenGL-Registry
*/
-#define GLX_GLXEXT_VERSION 20140416
+#define GLX_GLXEXT_VERSION 20211115
/* Generated C header for:
* API: glx
@@ -109,41 +88,41 @@ typedef XID GLXPbuffer;
#define GLX_PBUFFER 0x8023
#define GLX_PBUFFER_HEIGHT 0x8040
#define GLX_PBUFFER_WIDTH 0x8041
-typedef GLXFBConfig *( *PFNGLXGETFBCONFIGSPROC) (X11_Display *dpy, int screen, int *nelements);
-typedef GLXFBConfig *( *PFNGLXCHOOSEFBCONFIGPROC) (X11_Display *dpy, int screen, const int *attrib_list, int *nelements);
-typedef int ( *PFNGLXGETFBCONFIGATTRIBPROC) (X11_Display *dpy, GLXFBConfig config, int attribute, int *value);
-typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGPROC) (X11_Display *dpy, GLXFBConfig config);
-typedef GLXWindow ( *PFNGLXCREATEWINDOWPROC) (X11_Display *dpy, GLXFBConfig config, X11_Window win, const int *attrib_list);
-typedef void ( *PFNGLXDESTROYWINDOWPROC) (X11_Display *dpy, GLXWindow win);
-typedef GLXPixmap ( *PFNGLXCREATEPIXMAPPROC) (X11_Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list);
-typedef void ( *PFNGLXDESTROYPIXMAPPROC) (X11_Display *dpy, GLXPixmap pixmap);
-typedef GLXPbuffer ( *PFNGLXCREATEPBUFFERPROC) (X11_Display *dpy, GLXFBConfig config, const int *attrib_list);
-typedef void ( *PFNGLXDESTROYPBUFFERPROC) (X11_Display *dpy, GLXPbuffer pbuf);
-typedef void ( *PFNGLXQUERYDRAWABLEPROC) (X11_Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
-typedef GLXContext ( *PFNGLXCREATENEWCONTEXTPROC) (X11_Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
-typedef Bool ( *PFNGLXMAKECONTEXTCURRENTPROC) (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+typedef GLXFBConfig *( *PFNGLXGETFBCONFIGSPROC) (Display *dpy, int screen, int *nelements);
+typedef GLXFBConfig *( *PFNGLXCHOOSEFBCONFIGPROC) (Display *dpy, int screen, const int *attrib_list, int *nelements);
+typedef int ( *PFNGLXGETFBCONFIGATTRIBPROC) (Display *dpy, GLXFBConfig config, int attribute, int *value);
+typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGPROC) (Display *dpy, GLXFBConfig config);
+typedef GLXWindow ( *PFNGLXCREATEWINDOWPROC) (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list);
+typedef void ( *PFNGLXDESTROYWINDOWPROC) (Display *dpy, GLXWindow win);
+typedef GLXPixmap ( *PFNGLXCREATEPIXMAPPROC) (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list);
+typedef void ( *PFNGLXDESTROYPIXMAPPROC) (Display *dpy, GLXPixmap pixmap);
+typedef GLXPbuffer ( *PFNGLXCREATEPBUFFERPROC) (Display *dpy, GLXFBConfig config, const int *attrib_list);
+typedef void ( *PFNGLXDESTROYPBUFFERPROC) (Display *dpy, GLXPbuffer pbuf);
+typedef void ( *PFNGLXQUERYDRAWABLEPROC) (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
+typedef GLXContext ( *PFNGLXCREATENEWCONTEXTPROC) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+typedef Bool ( *PFNGLXMAKECONTEXTCURRENTPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLEPROC) (void);
-typedef int ( *PFNGLXQUERYCONTEXTPROC) (X11_Display *dpy, GLXContext ctx, int attribute, int *value);
-typedef void ( *PFNGLXSELECTEVENTPROC) (X11_Display *dpy, GLXDrawable draw, unsigned long event_mask);
-typedef void ( *PFNGLXGETSELECTEDEVENTPROC) (X11_Display *dpy, GLXDrawable draw, unsigned long *event_mask);
+typedef int ( *PFNGLXQUERYCONTEXTPROC) (Display *dpy, GLXContext ctx, int attribute, int *value);
+typedef void ( *PFNGLXSELECTEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long event_mask);
+typedef void ( *PFNGLXGETSELECTEDEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long *event_mask);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXFBConfig *glXGetFBConfigs (X11_Display *dpy, int screen, int *nelements);
-GLXFBConfig *glXChooseFBConfig (X11_Display *dpy, int screen, const int *attrib_list, int *nelements);
-int glXGetFBConfigAttrib (X11_Display *dpy, GLXFBConfig config, int attribute, int *value);
-XVisualInfo *glXGetVisualFromFBConfig (X11_Display *dpy, GLXFBConfig config);
-GLXWindow glXCreateWindow (X11_Display *dpy, GLXFBConfig config, X11_Window win, const int *attrib_list);
-void glXDestroyWindow (X11_Display *dpy, GLXWindow win);
-GLXPixmap glXCreatePixmap (X11_Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list);
-void glXDestroyPixmap (X11_Display *dpy, GLXPixmap pixmap);
-GLXPbuffer glXCreatePbuffer (X11_Display *dpy, GLXFBConfig config, const int *attrib_list);
-void glXDestroyPbuffer (X11_Display *dpy, GLXPbuffer pbuf);
-void glXQueryDrawable (X11_Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
-GLXContext glXCreateNewContext (X11_Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
-Bool glXMakeContextCurrent (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+GLXFBConfig *glXGetFBConfigs (Display *dpy, int screen, int *nelements);
+GLXFBConfig *glXChooseFBConfig (Display *dpy, int screen, const int *attrib_list, int *nelements);
+int glXGetFBConfigAttrib (Display *dpy, GLXFBConfig config, int attribute, int *value);
+XVisualInfo *glXGetVisualFromFBConfig (Display *dpy, GLXFBConfig config);
+GLXWindow glXCreateWindow (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list);
+void glXDestroyWindow (Display *dpy, GLXWindow win);
+GLXPixmap glXCreatePixmap (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list);
+void glXDestroyPixmap (Display *dpy, GLXPixmap pixmap);
+GLXPbuffer glXCreatePbuffer (Display *dpy, GLXFBConfig config, const int *attrib_list);
+void glXDestroyPbuffer (Display *dpy, GLXPbuffer pbuf);
+void glXQueryDrawable (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value);
+GLXContext glXCreateNewContext (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct);
+Bool glXMakeContextCurrent (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
GLXDrawable glXGetCurrentReadDrawable (void);
-int glXQueryContext (X11_Display *dpy, GLXContext ctx, int attribute, int *value);
-void glXSelectEvent (X11_Display *dpy, GLXDrawable draw, unsigned long event_mask);
-void glXGetSelectedEvent (X11_Display *dpy, GLXDrawable draw, unsigned long *event_mask);
+int glXQueryContext (Display *dpy, GLXContext ctx, int attribute, int *value);
+void glXSelectEvent (Display *dpy, GLXDrawable draw, unsigned long event_mask);
+void glXGetSelectedEvent (Display *dpy, GLXDrawable draw, unsigned long *event_mask);
#endif
#endif /* GLX_VERSION_1_3 */
@@ -158,6 +137,13 @@ __GLXextFuncPtr glXGetProcAddress (const GLubyte *procName);
#endif
#endif /* GLX_VERSION_1_4 */
+#ifndef GLX_ARB_context_flush_control
+#define GLX_ARB_context_flush_control 1
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0
+#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
+#endif /* GLX_ARB_context_flush_control */
+
#ifndef GLX_ARB_create_context
#define GLX_ARB_create_context 1
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001
@@ -165,12 +151,17 @@ __GLXextFuncPtr glXGetProcAddress (const GLubyte *procName);
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
#define GLX_CONTEXT_FLAGS_ARB 0x2094
-typedef GLXContext ( *PFNGLXCREATECONTEXTATTRIBSARBPROC) (X11_Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+typedef GLXContext ( *PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
+GLXContext glXCreateContextAttribsARB (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
#endif
#endif /* GLX_ARB_create_context */
+#ifndef GLX_ARB_create_context_no_error
+#define GLX_ARB_create_context_no_error 1
+#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
+#endif /* GLX_ARB_create_context_no_error */
+
#ifndef GLX_ARB_create_context_profile
#define GLX_ARB_create_context_profile 1
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
@@ -199,7 +190,6 @@ GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLX
#ifndef GLX_ARB_get_proc_address
#define GLX_ARB_get_proc_address 1
-typedef void (*__GLXextFuncPtr)(void);
typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName);
#ifdef GLX_GLXEXT_PROTOTYPES
__GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);
@@ -244,6 +234,26 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);
#define GLX_GPU_NUM_SIMD_AMD 0x21A6
#define GLX_GPU_NUM_RB_AMD 0x21A7
#define GLX_GPU_NUM_SPI_AMD 0x21A8
+typedef unsigned int ( *PFNGLXGETGPUIDSAMDPROC) (unsigned int maxCount, unsigned int *ids);
+typedef int ( *PFNGLXGETGPUINFOAMDPROC) (unsigned int id, int property, GLenum dataType, unsigned int size, void *data);
+typedef unsigned int ( *PFNGLXGETCONTEXTGPUIDAMDPROC) (GLXContext ctx);
+typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC) (unsigned int id, GLXContext share_list);
+typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (unsigned int id, GLXContext share_context, const int *attribList);
+typedef Bool ( *PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC) (GLXContext ctx);
+typedef Bool ( *PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (GLXContext ctx);
+typedef GLXContext ( *PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void);
+typedef void ( *PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC) (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+#ifdef GLX_GLXEXT_PROTOTYPES
+unsigned int glXGetGPUIDsAMD (unsigned int maxCount, unsigned int *ids);
+int glXGetGPUInfoAMD (unsigned int id, int property, GLenum dataType, unsigned int size, void *data);
+unsigned int glXGetContextGPUIDAMD (GLXContext ctx);
+GLXContext glXCreateAssociatedContextAMD (unsigned int id, GLXContext share_list);
+GLXContext glXCreateAssociatedContextAttribsAMD (unsigned int id, GLXContext share_context, const int *attribList);
+Bool glXDeleteAssociatedContextAMD (GLXContext ctx);
+Bool glXMakeAssociatedContextCurrentAMD (GLXContext ctx);
+GLXContext glXGetCurrentAssociatedContextAMD (void);
+void glXBlitContextFramebufferAMD (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+#endif
#endif /* GLX_AMD_gpu_association */
#ifndef GLX_EXT_buffer_age
@@ -251,6 +261,14 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);
#define GLX_BACK_BUFFER_AGE_EXT 0x20F4
#endif /* GLX_EXT_buffer_age */
+#ifndef GLX_EXT_context_priority
+#define GLX_EXT_context_priority 1
+#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100
+#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101
+#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102
+#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103
+#endif /* GLX_EXT_context_priority */
+
#ifndef GLX_EXT_create_context_es2_profile
#define GLX_EXT_create_context_es2_profile 1
#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
@@ -272,13 +290,45 @@ __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName);
#define GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20B2
#endif /* GLX_EXT_framebuffer_sRGB */
+#ifndef GLX_EXT_get_drawable_type
+#define GLX_EXT_get_drawable_type 1
+#endif /* GLX_EXT_get_drawable_type */
+
+#ifndef GLX_EXT_import_context
+#define GLX_EXT_import_context 1
+#define GLX_SHARE_CONTEXT_EXT 0x800A
+#define GLX_VISUAL_ID_EXT 0x800B
+#define GLX_SCREEN_EXT 0x800C
+typedef Display *( *PFNGLXGETCURRENTDISPLAYEXTPROC) (void);
+typedef int ( *PFNGLXQUERYCONTEXTINFOEXTPROC) (Display *dpy, GLXContext context, int attribute, int *value);
+typedef GLXContextID ( *PFNGLXGETCONTEXTIDEXTPROC) (const GLXContext context);
+typedef GLXContext ( *PFNGLXIMPORTCONTEXTEXTPROC) (Display *dpy, GLXContextID contextID);
+typedef void ( *PFNGLXFREECONTEXTEXTPROC) (Display *dpy, GLXContext context);
+#ifdef GLX_GLXEXT_PROTOTYPES
+Display *glXGetCurrentDisplayEXT (void);
+int glXQueryContextInfoEXT (Display *dpy, GLXContext context, int attribute, int *value);
+GLXContextID glXGetContextIDEXT (const GLXContext context);
+GLXContext glXImportContextEXT (Display *dpy, GLXContextID contextID);
+void glXFreeContextEXT (Display *dpy, GLXContext context);
+#endif
+#endif /* GLX_EXT_import_context */
+
+#ifndef GLX_EXT_libglvnd
+#define GLX_EXT_libglvnd 1
+#define GLX_VENDOR_NAMES_EXT 0x20F6
+#endif /* GLX_EXT_libglvnd */
+
+#ifndef GLX_EXT_no_config_context
+#define GLX_EXT_no_config_context 1
+#endif /* GLX_EXT_no_config_context */
+
#ifndef GLX_EXT_stereo_tree
#define GLX_EXT_stereo_tree 1
typedef struct {
int type;
unsigned long serial;
Bool send_event;
- X11_Display *display;
+ Display *display;
int extension;
int evtype;
GLXDrawable window;
@@ -293,9 +343,9 @@ typedef struct {
#define GLX_EXT_swap_control 1
#define GLX_SWAP_INTERVAL_EXT 0x20F1
#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2
-typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int interval);
+typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXSwapIntervalEXT (X11_Display *dpy, GLXDrawable drawable, int interval);
+void glXSwapIntervalEXT (Display *dpy, GLXDrawable drawable, int interval);
#endif
#endif /* GLX_EXT_swap_control */
@@ -339,11 +389,11 @@ void glXSwapIntervalEXT (X11_Display *dpy, GLXDrawable drawable, int interval);
#define GLX_AUX7_EXT 0x20E9
#define GLX_AUX8_EXT 0x20EA
#define GLX_AUX9_EXT 0x20EB
-typedef void ( *PFNGLXBINDTEXIMAGEEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list);
-typedef void ( *PFNGLXRELEASETEXIMAGEEXTPROC) (X11_Display *dpy, GLXDrawable drawable, int buffer);
+typedef void ( *PFNGLXBINDTEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list);
+typedef void ( *PFNGLXRELEASETEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXBindTexImageEXT (X11_Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list);
-void glXReleaseTexImageEXT (X11_Display *dpy, GLXDrawable drawable, int buffer);
+void glXBindTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list);
+void glXReleaseTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer);
#endif
#endif /* GLX_EXT_texture_from_pixmap */
@@ -392,17 +442,17 @@ unsigned int glXGetAGPOffsetMESA (const void *pointer);
#ifndef GLX_MESA_copy_sub_buffer
#define GLX_MESA_copy_sub_buffer 1
-typedef void ( *PFNGLXCOPYSUBBUFFERMESAPROC) (X11_Display *dpy, GLXDrawable drawable, int x, int y, int width, int height);
+typedef void ( *PFNGLXCOPYSUBBUFFERMESAPROC) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXCopySubBufferMESA (X11_Display *dpy, GLXDrawable drawable, int x, int y, int width, int height);
+void glXCopySubBufferMESA (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height);
#endif
#endif /* GLX_MESA_copy_sub_buffer */
#ifndef GLX_MESA_pixmap_colormap
#define GLX_MESA_pixmap_colormap 1
-typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPMESAPROC) (X11_Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
+typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPMESAPROC) (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXPixmap glXCreateGLXPixmapMESA (X11_Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
+GLXPixmap glXCreateGLXPixmapMESA (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap);
#endif
#endif /* GLX_MESA_pixmap_colormap */
@@ -419,24 +469,23 @@ GLXPixmap glXCreateGLXPixmapMESA (X11_Display *dpy, XVisualInfo *visual, Pixmap
#define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
#define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
#define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
-#define GLX_RENDERER_ID_MESA 0x818E
typedef Bool ( *PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value);
typedef const char *( *PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC) (int attribute);
-typedef Bool ( *PFNGLXQUERYRENDERERINTEGERMESAPROC) (X11_Display *dpy, int screen, int renderer, int attribute, unsigned int *value);
-typedef const char *( *PFNGLXQUERYRENDERERSTRINGMESAPROC) (X11_Display *dpy, int screen, int renderer, int attribute);
+typedef Bool ( *PFNGLXQUERYRENDERERINTEGERMESAPROC) (Display *dpy, int screen, int renderer, int attribute, unsigned int *value);
+typedef const char *( *PFNGLXQUERYRENDERERSTRINGMESAPROC) (Display *dpy, int screen, int renderer, int attribute);
#ifdef GLX_GLXEXT_PROTOTYPES
Bool glXQueryCurrentRendererIntegerMESA (int attribute, unsigned int *value);
const char *glXQueryCurrentRendererStringMESA (int attribute);
-Bool glXQueryRendererIntegerMESA (X11_Display *dpy, int screen, int renderer, int attribute, unsigned int *value);
-const char *glXQueryRendererStringMESA (X11_Display *dpy, int screen, int renderer, int attribute);
+Bool glXQueryRendererIntegerMESA (Display *dpy, int screen, int renderer, int attribute, unsigned int *value);
+const char *glXQueryRendererStringMESA (Display *dpy, int screen, int renderer, int attribute);
#endif
#endif /* GLX_MESA_query_renderer */
#ifndef GLX_MESA_release_buffers
#define GLX_MESA_release_buffers 1
-typedef Bool ( *PFNGLXRELEASEBUFFERSMESAPROC) (X11_Display *dpy, GLXDrawable drawable);
+typedef Bool ( *PFNGLXRELEASEBUFFERSMESAPROC) (Display *dpy, GLXDrawable drawable);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXReleaseBuffersMESA (X11_Display *dpy, GLXDrawable drawable);
+Bool glXReleaseBuffersMESA (Display *dpy, GLXDrawable drawable);
#endif
#endif /* GLX_MESA_release_buffers */
@@ -444,25 +493,45 @@ Bool glXReleaseBuffersMESA (X11_Display *dpy, GLXDrawable drawable);
#define GLX_MESA_set_3dfx_mode 1
#define GLX_3DFX_WINDOW_MODE_MESA 0x1
#define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2
-typedef Bool ( *PFNGLXSET3DFXMODEMESAPROC) (int mode);
+typedef GLboolean ( *PFNGLXSET3DFXMODEMESAPROC) (GLint mode);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXSet3DfxModeMESA (int mode);
+GLboolean glXSet3DfxModeMESA (GLint mode);
#endif
#endif /* GLX_MESA_set_3dfx_mode */
+#ifndef GLX_MESA_swap_control
+#define GLX_MESA_swap_control 1
+typedef int ( *PFNGLXGETSWAPINTERVALMESAPROC) (void);
+typedef int ( *PFNGLXSWAPINTERVALMESAPROC) (unsigned int interval);
+#ifdef GLX_GLXEXT_PROTOTYPES
+int glXGetSwapIntervalMESA (void);
+int glXSwapIntervalMESA (unsigned int interval);
+#endif
+#endif /* GLX_MESA_swap_control */
+
+#ifndef GLX_NV_copy_buffer
+#define GLX_NV_copy_buffer 1
+typedef void ( *PFNGLXCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+typedef void ( *PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+#ifdef GLX_GLXEXT_PROTOTYPES
+void glXCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+void glXNamedCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+#endif
+#endif /* GLX_NV_copy_buffer */
+
#ifndef GLX_NV_copy_image
#define GLX_NV_copy_image 1
-typedef void ( *PFNGLXCOPYIMAGESUBDATANVPROC) (X11_Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
+typedef void ( *PFNGLXCOPYIMAGESUBDATANVPROC) (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXCopyImageSubDataNV (X11_Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
+void glXCopyImageSubDataNV (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
#endif
#endif /* GLX_NV_copy_image */
#ifndef GLX_NV_delay_before_swap
#define GLX_NV_delay_before_swap 1
-typedef Bool ( *PFNGLXDELAYBEFORESWAPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLfloat seconds);
+typedef Bool ( *PFNGLXDELAYBEFORESWAPNVPROC) (Display *dpy, GLXDrawable drawable, GLfloat seconds);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat seconds);
+Bool glXDelayBeforeSwapNV (Display *dpy, GLXDrawable drawable, GLfloat seconds);
#endif
#endif /* GLX_NV_delay_before_swap */
@@ -471,6 +540,15 @@ Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat secon
#define GLX_FLOAT_COMPONENTS_NV 0x20B0
#endif /* GLX_NV_float_buffer */
+#ifndef GLX_NV_multigpu_context
+#define GLX_NV_multigpu_context 1
+#define GLX_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA
+#define GLX_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB
+#define GLX_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC
+#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD
+#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE
+#endif /* GLX_NV_multigpu_context */
+
#ifndef GLX_NV_multisample_coverage
#define GLX_NV_multisample_coverage 1
#define GLX_COVERAGE_SAMPLES_NV 100001
@@ -480,29 +558,34 @@ Bool glXDelayBeforeSwapNV (X11_Display *dpy, GLXDrawable drawable, GLfloat secon
#ifndef GLX_NV_present_video
#define GLX_NV_present_video 1
#define GLX_NUM_VIDEO_SLOTS_NV 0x20F0
-typedef unsigned int *( *PFNGLXENUMERATEVIDEODEVICESNVPROC) (X11_Display *dpy, int screen, int *nelements);
-typedef int ( *PFNGLXBINDVIDEODEVICENVPROC) (X11_Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list);
+typedef unsigned int *( *PFNGLXENUMERATEVIDEODEVICESNVPROC) (Display *dpy, int screen, int *nelements);
+typedef int ( *PFNGLXBINDVIDEODEVICENVPROC) (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list);
#ifdef GLX_GLXEXT_PROTOTYPES
-unsigned int *glXEnumerateVideoDevicesNV (X11_Display *dpy, int screen, int *nelements);
-int glXBindVideoDeviceNV (X11_Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list);
+unsigned int *glXEnumerateVideoDevicesNV (Display *dpy, int screen, int *nelements);
+int glXBindVideoDeviceNV (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list);
#endif
#endif /* GLX_NV_present_video */
+#ifndef GLX_NV_robustness_video_memory_purge
+#define GLX_NV_robustness_video_memory_purge 1
+#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
+#endif /* GLX_NV_robustness_video_memory_purge */
+
#ifndef GLX_NV_swap_group
#define GLX_NV_swap_group 1
-typedef Bool ( *PFNGLXJOINSWAPGROUPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLuint group);
-typedef Bool ( *PFNGLXBINDSWAPBARRIERNVPROC) (X11_Display *dpy, GLuint group, GLuint barrier);
-typedef Bool ( *PFNGLXQUERYSWAPGROUPNVPROC) (X11_Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier);
-typedef Bool ( *PFNGLXQUERYMAXSWAPGROUPSNVPROC) (X11_Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers);
-typedef Bool ( *PFNGLXQUERYFRAMECOUNTNVPROC) (X11_Display *dpy, int screen, GLuint *count);
-typedef Bool ( *PFNGLXRESETFRAMECOUNTNVPROC) (X11_Display *dpy, int screen);
+typedef Bool ( *PFNGLXJOINSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint group);
+typedef Bool ( *PFNGLXBINDSWAPBARRIERNVPROC) (Display *dpy, GLuint group, GLuint barrier);
+typedef Bool ( *PFNGLXQUERYSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier);
+typedef Bool ( *PFNGLXQUERYMAXSWAPGROUPSNVPROC) (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers);
+typedef Bool ( *PFNGLXQUERYFRAMECOUNTNVPROC) (Display *dpy, int screen, GLuint *count);
+typedef Bool ( *PFNGLXRESETFRAMECOUNTNVPROC) (Display *dpy, int screen);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXJoinSwapGroupNV (X11_Display *dpy, GLXDrawable drawable, GLuint group);
-Bool glXBindSwapBarrierNV (X11_Display *dpy, GLuint group, GLuint barrier);
-Bool glXQuerySwapGroupNV (X11_Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier);
-Bool glXQueryMaxSwapGroupsNV (X11_Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers);
-Bool glXQueryFrameCountNV (X11_Display *dpy, int screen, GLuint *count);
-Bool glXResetFrameCountNV (X11_Display *dpy, int screen);
+Bool glXJoinSwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint group);
+Bool glXBindSwapBarrierNV (Display *dpy, GLuint group, GLuint barrier);
+Bool glXQuerySwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier);
+Bool glXQueryMaxSwapGroupsNV (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers);
+Bool glXQueryFrameCountNV (Display *dpy, int screen, GLuint *count);
+Bool glXResetFrameCountNV (Display *dpy, int screen);
#endif
#endif /* GLX_NV_swap_group */
@@ -512,22 +595,22 @@ typedef XID GLXVideoCaptureDeviceNV;
#define GLX_DEVICE_ID_NV 0x20CD
#define GLX_UNIQUE_ID_NV 0x20CE
#define GLX_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF
-typedef int ( *PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device);
-typedef GLXVideoCaptureDeviceNV *( *PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) (X11_Display *dpy, int screen, int *nelements);
-typedef void ( *PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device);
-typedef int ( *PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value);
-typedef void ( *PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) (X11_Display *dpy, GLXVideoCaptureDeviceNV device);
+typedef int ( *PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device);
+typedef GLXVideoCaptureDeviceNV *( *PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) (Display *dpy, int screen, int *nelements);
+typedef void ( *PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device);
+typedef int ( *PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value);
+typedef void ( *PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device);
#ifdef GLX_GLXEXT_PROTOTYPES
-int glXBindVideoCaptureDeviceNV (X11_Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device);
-GLXVideoCaptureDeviceNV *glXEnumerateVideoCaptureDevicesNV (X11_Display *dpy, int screen, int *nelements);
-void glXLockVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device);
-int glXQueryVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value);
-void glXReleaseVideoCaptureDeviceNV (X11_Display *dpy, GLXVideoCaptureDeviceNV device);
+int glXBindVideoCaptureDeviceNV (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device);
+GLXVideoCaptureDeviceNV *glXEnumerateVideoCaptureDevicesNV (Display *dpy, int screen, int *nelements);
+void glXLockVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device);
+int glXQueryVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value);
+void glXReleaseVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device);
#endif
#endif /* GLX_NV_video_capture */
-#ifndef GLX_NV_video_output
-#define GLX_NV_video_output 1
+#ifndef GLX_NV_video_out
+#define GLX_NV_video_out 1
typedef unsigned int GLXVideoDeviceNV;
#define GLX_VIDEO_OUT_COLOR_NV 0x20C3
#define GLX_VIDEO_OUT_ALPHA_NV 0x20C4
@@ -539,21 +622,21 @@ typedef unsigned int GLXVideoDeviceNV;
#define GLX_VIDEO_OUT_FIELD_2_NV 0x20CA
#define GLX_VIDEO_OUT_STACKED_FIELDS_1_2_NV 0x20CB
#define GLX_VIDEO_OUT_STACKED_FIELDS_2_1_NV 0x20CC
-typedef int ( *PFNGLXGETVIDEODEVICENVPROC) (X11_Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice);
-typedef int ( *PFNGLXRELEASEVIDEODEVICENVPROC) (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice);
-typedef int ( *PFNGLXBINDVIDEOIMAGENVPROC) (X11_Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer);
-typedef int ( *PFNGLXRELEASEVIDEOIMAGENVPROC) (X11_Display *dpy, GLXPbuffer pbuf);
-typedef int ( *PFNGLXSENDPBUFFERTOVIDEONVPROC) (X11_Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock);
-typedef int ( *PFNGLXGETVIDEOINFONVPROC) (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
+typedef int ( *PFNGLXGETVIDEODEVICENVPROC) (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice);
+typedef int ( *PFNGLXRELEASEVIDEODEVICENVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice);
+typedef int ( *PFNGLXBINDVIDEOIMAGENVPROC) (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer);
+typedef int ( *PFNGLXRELEASEVIDEOIMAGENVPROC) (Display *dpy, GLXPbuffer pbuf);
+typedef int ( *PFNGLXSENDPBUFFERTOVIDEONVPROC) (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock);
+typedef int ( *PFNGLXGETVIDEOINFONVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
#ifdef GLX_GLXEXT_PROTOTYPES
-int glXGetVideoDeviceNV (X11_Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice);
-int glXReleaseVideoDeviceNV (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice);
-int glXBindVideoImageNV (X11_Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer);
-int glXReleaseVideoImageNV (X11_Display *dpy, GLXPbuffer pbuf);
-int glXSendPbufferToVideoNV (X11_Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock);
-int glXGetVideoInfoNV (X11_Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
+int glXGetVideoDeviceNV (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice);
+int glXReleaseVideoDeviceNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice);
+int glXBindVideoImageNV (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer);
+int glXReleaseVideoImageNV (Display *dpy, GLXPbuffer pbuf);
+int glXSendPbufferToVideoNV (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock);
+int glXGetVideoInfoNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo);
#endif
-#endif /* GLX_NV_video_output */
+#endif /* GLX_NV_video_out */
#ifndef GLX_OML_swap_method
#define GLX_OML_swap_method 1
@@ -602,17 +685,17 @@ typedef unsigned __int64 uint64_t;
#include
#endif
#endif
-typedef Bool ( *PFNGLXGETSYNCVALUESOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc);
-typedef Bool ( *PFNGLXGETMSCRATEOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator);
-typedef int64_t ( *PFNGLXSWAPBUFFERSMSCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder);
-typedef Bool ( *PFNGLXWAITFORMSCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc);
-typedef Bool ( *PFNGLXWAITFORSBCOMLPROC) (X11_Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc);
+typedef Bool ( *PFNGLXGETSYNCVALUESOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc);
+typedef Bool ( *PFNGLXGETMSCRATEOMLPROC) (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator);
+typedef int64_t ( *PFNGLXSWAPBUFFERSMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder);
+typedef Bool ( *PFNGLXWAITFORMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc);
+typedef Bool ( *PFNGLXWAITFORSBCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXGetSyncValuesOML (X11_Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc);
-Bool glXGetMscRateOML (X11_Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator);
-int64_t glXSwapBuffersMscOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder);
-Bool glXWaitForMscOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc);
-Bool glXWaitForSbcOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc);
+Bool glXGetSyncValuesOML (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc);
+Bool glXGetMscRateOML (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator);
+int64_t glXSwapBuffersMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder);
+Bool glXWaitForMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc);
+Bool glXWaitForSbcOML (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc);
#endif
#endif /* GLX_OML_sync_control */
@@ -638,9 +721,9 @@ Bool glXWaitForSbcOML (X11_Display *dpy, GLXDrawable drawable, int64_t target_sb
typedef XID GLXPbufferSGIX;
#ifdef _DM_BUFFER_H_
#define GLX_DIGITAL_MEDIA_PBUFFER_SGIX 0x8024
-typedef Bool ( *PFNGLXASSOCIATEDMPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer);
+typedef Bool ( *PFNGLXASSOCIATEDMPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXAssociateDMPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer);
+Bool glXAssociateDMPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer);
#endif
#endif /* _DM_BUFFER_H_ */
#endif /* GLX_SGIX_dmbuffer */
@@ -658,19 +741,19 @@ typedef struct __GLXFBConfigRec *GLXFBConfigSGIX;
#define GLX_FBCONFIG_ID_SGIX 0x8013
#define GLX_RGBA_TYPE_SGIX 0x8014
#define GLX_COLOR_INDEX_TYPE_SGIX 0x8015
-typedef int ( *PFNGLXGETFBCONFIGATTRIBSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, int attribute, int *value);
-typedef GLXFBConfigSGIX *( *PFNGLXCHOOSEFBCONFIGSGIXPROC) (X11_Display *dpy, int screen, int *attrib_list, int *nelements);
-typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap);
-typedef GLXContext ( *PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct);
-typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config);
-typedef GLXFBConfigSGIX ( *PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) (X11_Display *dpy, XVisualInfo *vis);
+typedef int ( *PFNGLXGETFBCONFIGATTRIBSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value);
+typedef GLXFBConfigSGIX *( *PFNGLXCHOOSEFBCONFIGSGIXPROC) (Display *dpy, int screen, int *attrib_list, int *nelements);
+typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap);
+typedef GLXContext ( *PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct);
+typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config);
+typedef GLXFBConfigSGIX ( *PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) (Display *dpy, XVisualInfo *vis);
#ifdef GLX_GLXEXT_PROTOTYPES
-int glXGetFBConfigAttribSGIX (X11_Display *dpy, GLXFBConfigSGIX config, int attribute, int *value);
-GLXFBConfigSGIX *glXChooseFBConfigSGIX (X11_Display *dpy, int screen, int *attrib_list, int *nelements);
-GLXPixmap glXCreateGLXPixmapWithConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap);
-GLXContext glXCreateContextWithConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct);
-XVisualInfo *glXGetVisualFromFBConfigSGIX (X11_Display *dpy, GLXFBConfigSGIX config);
-GLXFBConfigSGIX glXGetFBConfigFromVisualSGIX (X11_Display *dpy, XVisualInfo *vis);
+int glXGetFBConfigAttribSGIX (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value);
+GLXFBConfigSGIX *glXChooseFBConfigSGIX (Display *dpy, int screen, int *attrib_list, int *nelements);
+GLXPixmap glXCreateGLXPixmapWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap);
+GLXContext glXCreateContextWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct);
+XVisualInfo *glXGetVisualFromFBConfigSGIX (Display *dpy, GLXFBConfigSGIX config);
+GLXFBConfigSGIX glXGetFBConfigFromVisualSGIX (Display *dpy, XVisualInfo *vis);
#endif
#endif /* GLX_SGIX_fbconfig */
@@ -705,23 +788,23 @@ typedef struct {
#define GLX_HYPERPIPE_STEREO_SGIX 0x00000003
#define GLX_HYPERPIPE_PIXEL_AVERAGE_SGIX 0x00000004
#define GLX_HYPERPIPE_ID_SGIX 0x8030
-typedef GLXHyperpipeNetworkSGIX *( *PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) (X11_Display *dpy, int *npipes);
-typedef int ( *PFNGLXHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId);
-typedef GLXHyperpipeConfigSGIX *( *PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int hpId, int *npipes);
-typedef int ( *PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) (X11_Display *dpy, int hpId);
-typedef int ( *PFNGLXBINDHYPERPIPESGIXPROC) (X11_Display *dpy, int hpId);
-typedef int ( *PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList);
-typedef int ( *PFNGLXHYPERPIPEATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList);
-typedef int ( *PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) (X11_Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList);
+typedef GLXHyperpipeNetworkSGIX *( *PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) (Display *dpy, int *npipes);
+typedef int ( *PFNGLXHYPERPIPECONFIGSGIXPROC) (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId);
+typedef GLXHyperpipeConfigSGIX *( *PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId, int *npipes);
+typedef int ( *PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId);
+typedef int ( *PFNGLXBINDHYPERPIPESGIXPROC) (Display *dpy, int hpId);
+typedef int ( *PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList);
+typedef int ( *PFNGLXHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList);
+typedef int ( *PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXHyperpipeNetworkSGIX *glXQueryHyperpipeNetworkSGIX (X11_Display *dpy, int *npipes);
-int glXHyperpipeConfigSGIX (X11_Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId);
-GLXHyperpipeConfigSGIX *glXQueryHyperpipeConfigSGIX (X11_Display *dpy, int hpId, int *npipes);
-int glXDestroyHyperpipeConfigSGIX (X11_Display *dpy, int hpId);
-int glXBindHyperpipeSGIX (X11_Display *dpy, int hpId);
-int glXQueryHyperpipeBestAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList);
-int glXHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *attribList);
-int glXQueryHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList);
+GLXHyperpipeNetworkSGIX *glXQueryHyperpipeNetworkSGIX (Display *dpy, int *npipes);
+int glXHyperpipeConfigSGIX (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId);
+GLXHyperpipeConfigSGIX *glXQueryHyperpipeConfigSGIX (Display *dpy, int hpId, int *npipes);
+int glXDestroyHyperpipeConfigSGIX (Display *dpy, int hpId);
+int glXBindHyperpipeSGIX (Display *dpy, int hpId);
+int glXQueryHyperpipeBestAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList);
+int glXHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList);
+int glXQueryHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList);
#endif
#endif /* GLX_SGIX_hyperpipe */
@@ -752,35 +835,35 @@ int glXQueryHyperpipeAttribSGIX (X11_Display *dpy, int timeSlice, int attrib, in
#define GLX_SAVED_SGIX 0x8021
#define GLX_WINDOW_SGIX 0x8022
#define GLX_PBUFFER_SGIX 0x8023
-typedef GLXPbufferSGIX ( *PFNGLXCREATEGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
-typedef void ( *PFNGLXDESTROYGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuf);
-typedef int ( *PFNGLXQUERYGLXPBUFFERSGIXPROC) (X11_Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value);
-typedef void ( *PFNGLXSELECTEVENTSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, unsigned long mask);
-typedef void ( *PFNGLXGETSELECTEDEVENTSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, unsigned long *mask);
+typedef GLXPbufferSGIX ( *PFNGLXCREATEGLXPBUFFERSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
+typedef void ( *PFNGLXDESTROYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf);
+typedef void ( *PFNGLXQUERYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value);
+typedef void ( *PFNGLXSELECTEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long mask);
+typedef void ( *PFNGLXGETSELECTEDEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long *mask);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXPbufferSGIX glXCreateGLXPbufferSGIX (X11_Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
-void glXDestroyGLXPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuf);
-int glXQueryGLXPbufferSGIX (X11_Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value);
-void glXSelectEventSGIX (X11_Display *dpy, GLXDrawable drawable, unsigned long mask);
-void glXGetSelectedEventSGIX (X11_Display *dpy, GLXDrawable drawable, unsigned long *mask);
+GLXPbufferSGIX glXCreateGLXPbufferSGIX (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list);
+void glXDestroyGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf);
+void glXQueryGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value);
+void glXSelectEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long mask);
+void glXGetSelectedEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long *mask);
#endif
#endif /* GLX_SGIX_pbuffer */
#ifndef GLX_SGIX_swap_barrier
#define GLX_SGIX_swap_barrier 1
-typedef void ( *PFNGLXBINDSWAPBARRIERSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, int barrier);
-typedef Bool ( *PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) (X11_Display *dpy, int screen, int *max);
+typedef void ( *PFNGLXBINDSWAPBARRIERSGIXPROC) (Display *dpy, GLXDrawable drawable, int barrier);
+typedef Bool ( *PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) (Display *dpy, int screen, int *max);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXBindSwapBarrierSGIX (X11_Display *dpy, GLXDrawable drawable, int barrier);
-Bool glXQueryMaxSwapBarriersSGIX (X11_Display *dpy, int screen, int *max);
+void glXBindSwapBarrierSGIX (Display *dpy, GLXDrawable drawable, int barrier);
+Bool glXQueryMaxSwapBarriersSGIX (Display *dpy, int screen, int *max);
#endif
#endif /* GLX_SGIX_swap_barrier */
#ifndef GLX_SGIX_swap_group
#define GLX_SGIX_swap_group 1
-typedef void ( *PFNGLXJOINSWAPGROUPSGIXPROC) (X11_Display *dpy, GLXDrawable drawable, GLXDrawable member);
+typedef void ( *PFNGLXJOINSWAPGROUPSGIXPROC) (Display *dpy, GLXDrawable drawable, GLXDrawable member);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXJoinSwapGroupSGIX (X11_Display *dpy, GLXDrawable drawable, GLXDrawable member);
+void glXJoinSwapGroupSGIX (Display *dpy, GLXDrawable drawable, GLXDrawable member);
#endif
#endif /* GLX_SGIX_swap_group */
@@ -788,17 +871,17 @@ void glXJoinSwapGroupSGIX (X11_Display *dpy, GLXDrawable drawable, GLXDrawable m
#define GLX_SGIX_video_resize 1
#define GLX_SYNC_FRAME_SGIX 0x00000000
#define GLX_SYNC_SWAP_SGIX 0x00000001
-typedef int ( *PFNGLXBINDCHANNELTOWINDOWSGIXPROC) (X11_Display *display, int screen, int channel, X11_Window window);
-typedef int ( *PFNGLXCHANNELRECTSGIXPROC) (X11_Display *display, int screen, int channel, int x, int y, int w, int h);
-typedef int ( *PFNGLXQUERYCHANNELRECTSGIXPROC) (X11_Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh);
-typedef int ( *PFNGLXQUERYCHANNELDELTASSGIXPROC) (X11_Display *display, int screen, int channel, int *x, int *y, int *w, int *h);
-typedef int ( *PFNGLXCHANNELRECTSYNCSGIXPROC) (X11_Display *display, int screen, int channel, GLenum synctype);
+typedef int ( *PFNGLXBINDCHANNELTOWINDOWSGIXPROC) (Display *display, int screen, int channel, Window window);
+typedef int ( *PFNGLXCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int x, int y, int w, int h);
+typedef int ( *PFNGLXQUERYCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh);
+typedef int ( *PFNGLXQUERYCHANNELDELTASSGIXPROC) (Display *display, int screen, int channel, int *x, int *y, int *w, int *h);
+typedef int ( *PFNGLXCHANNELRECTSYNCSGIXPROC) (Display *display, int screen, int channel, GLenum synctype);
#ifdef GLX_GLXEXT_PROTOTYPES
-int glXBindChannelToWindowSGIX (X11_Display *display, int screen, int channel, X11_Window window);
-int glXChannelRectSGIX (X11_Display *display, int screen, int channel, int x, int y, int w, int h);
-int glXQueryChannelRectSGIX (X11_Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh);
-int glXQueryChannelDeltasSGIX (X11_Display *display, int screen, int channel, int *x, int *y, int *w, int *h);
-int glXChannelRectSyncSGIX (X11_Display *display, int screen, int channel, GLenum synctype);
+int glXBindChannelToWindowSGIX (Display *display, int screen, int channel, Window window);
+int glXChannelRectSGIX (Display *display, int screen, int channel, int x, int y, int w, int h);
+int glXQueryChannelRectSGIX (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh);
+int glXQueryChannelDeltasSGIX (Display *display, int screen, int channel, int *x, int *y, int *w, int *h);
+int glXChannelRectSyncSGIX (Display *display, int screen, int channel, GLenum synctype);
#endif
#endif /* GLX_SGIX_video_resize */
@@ -806,11 +889,11 @@ int glXChannelRectSyncSGIX (X11_Display *display, int screen, int channel, GLenu
#define GLX_SGIX_video_source 1
typedef XID GLXVideoSourceSGIX;
#ifdef _VL_H
-typedef GLXVideoSourceSGIX ( *PFNGLXCREATEGLXVIDEOSOURCESGIXPROC) (X11_Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode);
-typedef void ( *PFNGLXDESTROYGLXVIDEOSOURCESGIXPROC) (X11_Display *dpy, GLXVideoSourceSGIX glxvideosource);
+typedef GLXVideoSourceSGIX ( *PFNGLXCREATEGLXVIDEOSOURCESGIXPROC) (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode);
+typedef void ( *PFNGLXDESTROYGLXVIDEOSOURCESGIXPROC) (Display *dpy, GLXVideoSourceSGIX glxvideosource);
#ifdef GLX_GLXEXT_PROTOTYPES
-GLXVideoSourceSGIX glXCreateGLXVideoSourceSGIX (X11_Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode);
-void glXDestroyGLXVideoSourceSGIX (X11_Display *dpy, GLXVideoSourceSGIX glxvideosource);
+GLXVideoSourceSGIX glXCreateGLXVideoSourceSGIX (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode);
+void glXDestroyGLXVideoSourceSGIX (Display *dpy, GLXVideoSourceSGIX glxvideosource);
#endif
#endif /* _VL_H */
#endif /* GLX_SGIX_video_source */
@@ -822,18 +905,18 @@ void glXDestroyGLXVideoSourceSGIX (X11_Display *dpy, GLXVideoSourceSGIX glxvideo
#ifndef GLX_SGI_cushion
#define GLX_SGI_cushion 1
-typedef void ( *PFNGLXCUSHIONSGIPROC) (X11_Display *dpy, X11_Window window, float cushion);
+typedef void ( *PFNGLXCUSHIONSGIPROC) (Display *dpy, Window window, float cushion);
#ifdef GLX_GLXEXT_PROTOTYPES
-void glXCushionSGI (X11_Display *dpy, X11_Window window, float cushion);
+void glXCushionSGI (Display *dpy, Window window, float cushion);
#endif
#endif /* GLX_SGI_cushion */
#ifndef GLX_SGI_make_current_read
#define GLX_SGI_make_current_read 1
-typedef Bool ( *PFNGLXMAKECURRENTREADSGIPROC) (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+typedef Bool ( *PFNGLXMAKECURRENTREADSGIPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLESGIPROC) (void);
#ifdef GLX_GLXEXT_PROTOTYPES
-Bool glXMakeCurrentReadSGI (X11_Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
+Bool glXMakeCurrentReadSGI (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
GLXDrawable glXGetCurrentReadDrawableSGI (void);
#endif
#endif /* GLX_SGI_make_current_read */
@@ -858,9 +941,9 @@ int glXWaitVideoSyncSGI (int divisor, int remainder, unsigned int *count);
#ifndef GLX_SUN_get_transparent_index
#define GLX_SUN_get_transparent_index 1
-typedef Status ( *PFNGLXGETTRANSPARENTINDEXSUNPROC) (X11_Display *dpy, X11_Window overlay, X11_Window underlay, long *pTransparentIndex);
+typedef Status ( *PFNGLXGETTRANSPARENTINDEXSUNPROC) (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex);
#ifdef GLX_GLXEXT_PROTOTYPES
-Status glXGetTransparentIndexSUN (X11_Display *dpy, X11_Window overlay, X11_Window underlay, long *pTransparentIndex);
+Status glXGetTransparentIndexSUN (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex);
#endif
#endif /* GLX_SUN_get_transparent_index */
diff --git a/panda/src/gobj/CMakeLists.txt b/panda/src/gobj/CMakeLists.txt
index 9565a73b0c..c8cfd64b8f 100644
--- a/panda/src/gobj/CMakeLists.txt
+++ b/panda/src/gobj/CMakeLists.txt
@@ -61,7 +61,6 @@ set(P3GOBJ_HEADERS
textureReloadRequest.I textureReloadRequest.h
textureStage.I textureStage.h
textureStagePool.I textureStagePool.h
- timerQueryContext.I timerQueryContext.h
transformBlend.I transformBlend.h
transformBlendTable.I transformBlendTable.h
transformTable.I transformTable.h
@@ -141,7 +140,6 @@ set(P3GOBJ_SOURCES
textureReloadRequest.cxx
textureStage.cxx
textureStagePool.cxx
- timerQueryContext.cxx
transformBlend.cxx
transformBlendTable.cxx
transformTable.cxx
@@ -182,6 +180,11 @@ target_interrogate(p3gobj ALL EXTENSIONS ${P3GOBJ_IGATEEXT})
if(HAVE_SQUISH)
target_compile_definitions(p3gobj PRIVATE HAVE_SQUISH)
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Keep symbols from libsquish internal.
+ target_link_options(p3gobj PRIVATE "LINKER:--exclude-libs,libsquish.a")
+ endif()
endif()
if(PHAVE_LOCKF)
diff --git a/panda/src/gobj/animateVerticesRequest.I b/panda/src/gobj/animateVerticesRequest.I
index eb6ee589d0..f278f9c6d3 100644
--- a/panda/src/gobj/animateVerticesRequest.I
+++ b/panda/src/gobj/animateVerticesRequest.I
@@ -27,5 +27,5 @@ AnimateVerticesRequest(GeomVertexData *geom_vertex_data) :
*/
INLINE bool AnimateVerticesRequest::
is_ready() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+ return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
}
diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx
index df0990f69f..7ada146307 100644
--- a/panda/src/gobj/config_gobj.cxx
+++ b/panda/src/gobj/config_gobj.cxx
@@ -48,7 +48,6 @@
#include "textureReloadRequest.h"
#include "textureStage.h"
#include "textureContext.h"
-#include "timerQueryContext.h"
#include "samplerContext.h"
#include "samplerState.h"
#include "shader.h"
@@ -626,7 +625,6 @@ ConfigureFn(config_gobj) {
TexturePoolFilter::init_type();
TextureReloadRequest::init_type();
TextureStage::init_type();
- TimerQueryContext::init_type();
TransformBlend::init_type();
TransformBlendTable::init_type();
TransformTable::init_type();
diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx
index 9fedfbaef1..5733c4bee0 100644
--- a/panda/src/gobj/geom.cxx
+++ b/panda/src/gobj/geom.cxx
@@ -62,26 +62,6 @@ Geom(const Geom ©) :
{
}
-/**
- * The copy assignment operator is not pipeline-safe. This will completely
- * obliterate all stages of the pipeline, so don't do it for a Geom that is
- * actively being used for rendering.
- */
-void Geom::
-operator = (const Geom ©) {
- CopyOnWriteObject::operator = (copy);
-
- clear_cache();
-
- _cycler = copy._cycler;
-
- OPEN_ITERATE_ALL_STAGES(_cycler) {
- CDStageWriter cdata(_cycler, pipeline_stage);
- mark_internal_bounds_stale(cdata);
- }
- CLOSE_ITERATE_ALL_STAGES(_cycler);
-}
-
/**
*
*/
diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h
index 425d2b0a0a..073805e7ca 100644
--- a/panda/src/gobj/geom.h
+++ b/panda/src/gobj/geom.h
@@ -62,10 +62,11 @@ protected:
Geom(const Geom ©);
PUBLISHED:
- void operator = (const Geom ©);
virtual ~Geom();
ALLOC_DELETED_CHAIN(Geom);
+ void operator = (const Geom ©) = delete;
+
virtual Geom *make_copy() const;
INLINE PrimitiveType get_primitive_type() const;
diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx
index 97f5306110..8e9b9039c0 100644
--- a/panda/src/gobj/geomPrimitive.cxx
+++ b/panda/src/gobj/geomPrimitive.cxx
@@ -77,17 +77,6 @@ GeomPrimitive(const GeomPrimitive ©) :
{
}
-/**
- * The copy assignment operator is not pipeline-safe. This will completely
- * obliterate all stages of the pipeline, so don't do it for a GeomPrimitive
- * that is actively being used for rendering.
- */
-void GeomPrimitive::
-operator = (const GeomPrimitive ©) {
- CopyOnWriteObject::operator = (copy);
- _cycler = copy._cycler;
-}
-
/**
*
*/
diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h
index 03d8a314aa..dab0a3b830 100644
--- a/panda/src/gobj/geomPrimitive.h
+++ b/panda/src/gobj/geomPrimitive.h
@@ -61,10 +61,11 @@ protected:
PUBLISHED:
explicit GeomPrimitive(UsageHint usage_hint);
GeomPrimitive(const GeomPrimitive ©);
- void operator = (const GeomPrimitive ©);
virtual ~GeomPrimitive();
ALLOC_DELETED_CHAIN(GeomPrimitive);
+ void operator = (const GeomPrimitive ©) = delete;
+
virtual PT(GeomPrimitive) make_copy() const=0;
virtual PrimitiveType get_primitive_type() const=0;
diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I
index 30e5c590e8..8c1bfdc6fd 100644
--- a/panda/src/gobj/geomVertexArrayData.I
+++ b/panda/src/gobj/geomVertexArrayData.I
@@ -535,8 +535,8 @@ get_data() const {
INLINE vector_uchar GeomVertexArrayDataHandle::
get_subdata(size_t start, size_t size) const {
mark_used();
- start = std::min(start, _cdata->_buffer.get_size());
- size = std::min(size, _cdata->_buffer.get_size() - start);
+ start = (std::min)(start, _cdata->_buffer.get_size());
+ size = (std::min)(size, _cdata->_buffer.get_size() - start);
const unsigned char *ptr = _cdata->_buffer.get_read_pointer(true) + start;
return vector_uchar(ptr, ptr + size);
}
diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx
index 75a9a5db20..9b07969e46 100644
--- a/panda/src/gobj/geomVertexArrayData.cxx
+++ b/panda/src/gobj/geomVertexArrayData.cxx
@@ -83,8 +83,8 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
GeomVertexArrayData::UsageHint usage_hint) :
SimpleLruPage(0),
_array_format(array_format),
- _cycler(CData(usage_hint)),
- _contexts(nullptr)
+ _contexts(nullptr),
+ _cycler(CData(usage_hint))
{
set_lru_size(0);
nassertv(_array_format->is_registered());
@@ -98,8 +98,8 @@ GeomVertexArrayData(const GeomVertexArrayData ©) :
CopyOnWriteObject(copy),
SimpleLruPage(copy),
_array_format(copy._array_format),
- _cycler(copy._cycler),
- _contexts(nullptr)
+ _contexts(nullptr),
+ _cycler(copy._cycler)
{
copy.mark_used_lru();
@@ -107,30 +107,6 @@ GeomVertexArrayData(const GeomVertexArrayData ©) :
nassertv(_array_format->is_registered());
}
-/**
- * The copy assignment operator is not pipeline-safe. This will completely
- * obliterate all stages of the pipeline, so don't do it for a
- * GeomVertexArrayData that is actively being used for rendering.
- */
-void GeomVertexArrayData::
-operator = (const GeomVertexArrayData ©) {
- CopyOnWriteObject::operator = (copy);
- SimpleLruPage::operator = (copy);
-
- copy.mark_used_lru();
-
- _array_format = copy._array_format;
- _cycler = copy._cycler;
-
- OPEN_ITERATE_ALL_STAGES(_cycler) {
- CDStageWriter cdata(_cycler, pipeline_stage);
- cdata->_modified = Geom::get_next_modified();
- }
- CLOSE_ITERATE_ALL_STAGES(_cycler);
-
- nassertv(_array_format->is_registered());
-}
-
/**
*
*/
diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h
index 917b27c71a..e7f1fa750d 100644
--- a/panda/src/gobj/geomVertexArrayData.h
+++ b/panda/src/gobj/geomVertexArrayData.h
@@ -66,10 +66,11 @@ PUBLISHED:
explicit GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
UsageHint usage_hint);
GeomVertexArrayData(const GeomVertexArrayData ©);
- void operator = (const GeomVertexArrayData ©);
virtual ~GeomVertexArrayData();
ALLOC_DELETED_CHAIN(GeomVertexArrayData);
+ void operator = (const GeomVertexArrayData ©) = delete;
+
int compare_to(const GeomVertexArrayData &other) const;
INLINE const GeomVertexArrayFormat *get_array_format() const;
diff --git a/panda/src/gobj/geomVertexColumn.I b/panda/src/gobj/geomVertexColumn.I
index b382071eac..042a95ce5a 100644
--- a/panda/src/gobj/geomVertexColumn.I
+++ b/panda/src/gobj/geomVertexColumn.I
@@ -30,11 +30,11 @@ GeomVertexColumn(CPT_InternalName name, int num_components,
int element_stride) :
_name(std::move(name)),
_num_components(num_components),
+ _num_elements(num_elements),
_numeric_type(numeric_type),
_contents(contents),
_start(start),
_column_alignment(column_alignment),
- _num_elements(num_elements),
_element_stride(element_stride),
_packer(nullptr)
{
@@ -48,11 +48,11 @@ INLINE GeomVertexColumn::
GeomVertexColumn(const GeomVertexColumn ©) :
_name(copy._name),
_num_components(copy._num_components),
+ _num_elements(copy._num_elements),
_numeric_type(copy._numeric_type),
_contents(copy._contents),
_start(copy._start),
_column_alignment(copy._column_alignment),
- _num_elements(copy._num_elements),
_element_stride(copy._element_stride),
_packer(nullptr)
{
diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I
index 66f8a44b93..dd84cbb7cb 100644
--- a/panda/src/gobj/geomVertexData.I
+++ b/panda/src/gobj/geomVertexData.I
@@ -605,8 +605,8 @@ CacheEntry(GeomVertexData *source, CacheKey &&key) noexcept :
*/
INLINE GeomVertexData::CData::
CData() :
- _format(nullptr),
- _usage_hint(UH_unspecified)
+ _usage_hint(UH_unspecified),
+ _format(nullptr)
{
}
@@ -615,8 +615,8 @@ CData() :
*/
INLINE GeomVertexData::CData::
CData(const GeomVertexFormat *format, GeomVertexData::UsageHint usage_hint) :
- _format(format),
- _usage_hint(usage_hint)
+ _usage_hint(usage_hint),
+ _format(format)
{
size_t num_arrays = format->get_num_arrays();
for (size_t i = 0; i < num_arrays; ++i) {
diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx
index 65dd962902..2345fbef6f 100644
--- a/panda/src/gobj/geomVertexData.cxx
+++ b/panda/src/gobj/geomVertexData.cxx
@@ -66,11 +66,11 @@ GeomVertexData(const std::string &name,
const GeomVertexFormat *format,
GeomVertexData::UsageHint usage_hint) :
_name(name),
+ _cycler(GeomVertexData::CData(format, usage_hint)),
_char_pcollector(PStatCollector(_animation_pcollector, name)),
_skinning_pcollector(_char_pcollector, "Skinning"),
_morphs_pcollector(_char_pcollector, "Morphs"),
- _blends_pcollector(_char_pcollector, "Calc blends"),
- _cycler(GeomVertexData::CData(format, usage_hint))
+ _blends_pcollector(_char_pcollector, "Calc blends")
{
nassertv(format->is_registered());
}
@@ -136,33 +136,6 @@ GeomVertexData(const GeomVertexData ©,
CLOSE_ITERATE_ALL_STAGES(_cycler);
}
-/**
- * The copy assignment operator is not pipeline-safe. This will completely
- * obliterate all stages of the pipeline, so don't do it for a GeomVertexData
- * that is actively being used for rendering.
- */
-void GeomVertexData::
-operator = (const GeomVertexData ©) {
- CopyOnWriteObject::operator = (copy);
-
- clear_cache();
-
- _name = copy._name;
- _cycler = copy._cycler;
- _char_pcollector = copy._char_pcollector;
- _skinning_pcollector = copy._skinning_pcollector;
- _morphs_pcollector = copy._morphs_pcollector;
- _blends_pcollector = copy._blends_pcollector;
-
- OPEN_ITERATE_ALL_STAGES(_cycler) {
- CDStageWriter cdata(_cycler, pipeline_stage);
- cdata->_modified = Geom::get_next_modified();
- cdata->_animated_vertices = nullptr;
- cdata->_animated_vertices_modified = UpdateSeq();
- }
- CLOSE_ITERATE_ALL_STAGES(_cycler);
-}
-
/**
*
*/
diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h
index 84f2e279aa..63a9024190 100644
--- a/panda/src/gobj/geomVertexData.h
+++ b/panda/src/gobj/geomVertexData.h
@@ -78,10 +78,11 @@ PUBLISHED:
GeomVertexData(const GeomVertexData ©);
explicit GeomVertexData(const GeomVertexData ©,
const GeomVertexFormat *format);
- void operator = (const GeomVertexData ©);
virtual ~GeomVertexData();
ALLOC_DELETED_CHAIN(GeomVertexData);
+ void operator = (const GeomVertexData ©) = delete;
+
int compare_to(const GeomVertexData &other) const;
INLINE const std::string &get_name() const;
diff --git a/panda/src/gobj/geomVertexWriter.I b/panda/src/gobj/geomVertexWriter.I
index 09cd586b74..6b06c03357 100644
--- a/panda/src/gobj/geomVertexWriter.I
+++ b/panda/src/gobj/geomVertexWriter.I
@@ -1383,13 +1383,13 @@ inc_add_pointer() {
_handle = nullptr;
GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread);
writer.check_array_writers();
- writer.set_num_rows(std::max(write_row + 1, writer.get_num_rows()));
+ writer.set_num_rows((std::max)(write_row + 1, writer.get_num_rows()));
_handle = writer.get_array_writer(_array);
} else {
// Otherwise, we can get away with modifying only the one array we're
// using.
- _handle->set_num_rows(std::max(write_row + 1, _handle->get_num_rows()));
+ _handle->set_num_rows((std::max)(write_row + 1, _handle->get_num_rows()));
}
set_pointer(write_row);
diff --git a/panda/src/gobj/material.I b/panda/src/gobj/material.I
index dcaef86980..bd717f835c 100644
--- a/panda/src/gobj/material.I
+++ b/panda/src/gobj/material.I
@@ -58,7 +58,7 @@ INLINE Material::
*/
INLINE Material *Material::
get_default() {
- if (_default == 0) {
+ if (_default == nullptr) {
_default = new Material("default");
}
return _default;
diff --git a/panda/src/gobj/p3gobj_composite2.cxx b/panda/src/gobj/p3gobj_composite2.cxx
index 56704b12b3..e0a6bcbd6c 100644
--- a/panda/src/gobj/p3gobj_composite2.cxx
+++ b/panda/src/gobj/p3gobj_composite2.cxx
@@ -20,7 +20,6 @@
#include "textureReloadRequest.cxx"
#include "textureStage.cxx"
#include "textureStagePool.cxx"
-#include "timerQueryContext.cxx"
#include "transformBlend.cxx"
#include "transformBlendTable.cxx"
#include "transformTable.cxx"
diff --git a/panda/src/gobj/paramTexture.I b/panda/src/gobj/paramTexture.I
index c8235778e8..059a4ed33a 100644
--- a/panda/src/gobj/paramTexture.I
+++ b/panda/src/gobj/paramTexture.I
@@ -55,7 +55,7 @@ INLINE ParamTextureImage::
ParamTextureImage(Texture *tex, bool read, bool write, int z, int n) :
_texture(tex),
_access(0),
- _bind_level(std::min(n, 127)),
+ _bind_level((std::min)(n, 127)),
_bind_layer(z)
{
if (read) {
diff --git a/panda/src/gobj/pythonTexturePoolFilter.cxx b/panda/src/gobj/pythonTexturePoolFilter.cxx
index f758e1a9c2..8111c1c4a3 100644
--- a/panda/src/gobj/pythonTexturePoolFilter.cxx
+++ b/panda/src/gobj/pythonTexturePoolFilter.cxx
@@ -67,6 +67,7 @@ init(PyObject *tex_filter) {
nassertr(_post_load_func == nullptr, false);
_pre_load_func = PyObject_GetAttrString(tex_filter, "pre_load");
+ PyErr_Clear();
_post_load_func = PyObject_GetAttrString(tex_filter, "post_load");
PyErr_Clear();
diff --git a/panda/src/gobj/samplerState.I b/panda/src/gobj/samplerState.I
index ae5f9c9e2c..a3b0dabf73 100644
--- a/panda/src/gobj/samplerState.I
+++ b/panda/src/gobj/samplerState.I
@@ -17,14 +17,14 @@
INLINE SamplerState::
SamplerState() :
_border_color(0, 0, 0, 1),
- _wrap_u(WM_repeat),
- _wrap_v(WM_repeat),
- _wrap_w(WM_repeat),
- _minfilter(FT_default),
- _magfilter(FT_default),
_min_lod(-1000),
_max_lod(1000),
_lod_bias(0),
+ _minfilter(FT_default),
+ _magfilter(FT_default),
+ _wrap_u(WM_repeat),
+ _wrap_v(WM_repeat),
+ _wrap_w(WM_repeat),
_anisotropic_degree(0)
{
}
diff --git a/panda/src/gobj/samplerState.h b/panda/src/gobj/samplerState.h
index bef695cd83..c161d48206 100644
--- a/panda/src/gobj/samplerState.h
+++ b/panda/src/gobj/samplerState.h
@@ -17,8 +17,8 @@
#include "pandabase.h"
#include "typedObject.h"
-#include "namable.h"
#include "luse.h"
+#include "memoryBase.h"
#include "numeric_types.h"
#include "bamReader.h"
#include "config_gobj.h"
@@ -33,7 +33,7 @@ class SamplerContext;
* can be used to sample the same texture using different settings in
* different places.
*/
-class EXPCL_PANDA_GOBJ SamplerState {
+class EXPCL_PANDA_GOBJ SamplerState : public MemoryBase {
PUBLISHED:
enum FilterType {
// Mag Filter and Min Filter
diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx
index 51b7f7b64f..cb7d06f7cb 100644
--- a/panda/src/gobj/texture.cxx
+++ b/panda/src/gobj/texture.cxx
@@ -1065,7 +1065,7 @@ async_ensure_ram_image(bool allow_compression, int priority) {
double delay = async_load_delay;
// This texture has not yet been queued to be reloaded. Queue it up now.
- task = new FunctionAsyncTask(task_name, [=](AsyncTask *task) {
+ task = task_mgr->add(task_name, [=](AsyncTask *task) {
if (delay != 0.0) {
Thread::sleep(delay);
}
@@ -1076,9 +1076,8 @@ async_ensure_ram_image(bool allow_compression, int priority) {
}
return AsyncTask::DS_done;
});
- task->set_task_chain("texture_reload");
task->set_priority(priority);
- task_mgr->add(task);
+ task->set_task_chain("texture_reload");
cdataw->_reload_task = task;
return (AsyncFuture *)task;
}
@@ -1117,7 +1116,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
if (cdata->_component_width == 1) {
if (format == "RGBA" && cdata->_num_components == 4) {
imgsize *= 4;
- for (int p = 0; p < imgsize; p += 4) {
+ for (size_t p = 0; p < imgsize; p += 4) {
newdata[p + 2] = image[p ];
newdata[p + 1] = image[p + 1];
newdata[p ] = image[p + 2];
@@ -1128,7 +1127,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
}
if (format == "RGB" && cdata->_num_components == 3) {
imgsize *= 3;
- for (int p = 0; p < imgsize; p += 3) {
+ for (size_t p = 0; p < imgsize; p += 3) {
newdata[p + 2] = image[p ];
newdata[p + 1] = image[p + 1];
newdata[p ] = image[p + 2];
@@ -1139,13 +1138,13 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
if (format == "A" && cdata->_num_components != 3) {
// We can generally rely on alpha to be the last component.
int component = cdata->_num_components - 1;
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
newdata[component] = image[p];
}
do_set_ram_image(cdata, newdata);
return;
}
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
for (unsigned char s = 0; s < format.size(); ++s) {
signed char component = -1;
if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
@@ -1177,7 +1176,7 @@ set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
do_set_ram_image(cdata, newdata);
return;
}
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
for (unsigned char s = 0; s < format.size(); ++s) {
signed char component = -1;
if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
@@ -7512,7 +7511,8 @@ get_ram_image_as(const string &requested_format) {
gobj_cat.error() << "Couldn't find an uncompressed RAM image!\n";
return CPTA_uchar(get_class_type());
}
- int imgsize = cdata->_x_size * cdata->_y_size;
+ size_t imgsize = (size_t)cdata->_x_size * (size_t)cdata->_y_size *
+ (size_t)cdata->_z_size * (size_t)cdata->_num_views;
nassertr(cdata->_num_components > 0 && cdata->_num_components <= 4, CPTA_uchar(get_class_type()));
nassertr(data.size() == (size_t)(cdata->_component_width * cdata->_num_components * imgsize), CPTA_uchar(get_class_type()));
@@ -7550,7 +7550,7 @@ get_ram_image_as(const string &requested_format) {
const uint32_t *src = (const uint32_t *)data.p();
uint32_t *dst = (uint32_t *)newdata.p();
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
uint32_t v = *src++;
*dst++ = ((v & 0xff00ff00u)) |
((v & 0x00ff0000u) >> 16) |
@@ -7637,14 +7637,14 @@ get_ram_image_as(const string &requested_format) {
}
if (format == "A" && cdata->_num_components != 3) {
// We can generally rely on alpha to be the last component.
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
dst[p] = src[alpha];
src += cdata->_num_components;
}
return newdata;
}
// Fallback case for other 8-bit-per-channel formats.
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
for (size_t i = 0; i < format.size(); ++i) {
if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
*dst++ = src[0];
@@ -7670,7 +7670,7 @@ get_ram_image_as(const string &requested_format) {
}
// The slow and general case.
- for (int p = 0; p < imgsize; ++p) {
+ for (size_t p = 0; p < imgsize; ++p) {
for (size_t i = 0; i < format.size(); ++i) {
int component = 0;
if (format[i] == 'B' || (cdata->_num_components <= 2 && format[i] != 'A')) {
diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I
index 70836cdb5b..c7994a4bbc 100644
--- a/panda/src/gobj/textureContext.I
+++ b/panda/src/gobj/textureContext.I
@@ -122,7 +122,7 @@ mark_loaded() {
// _data_size_bytes = _data->get_texture_size_bytes();
_properties_modified = get_texture()->get_properties_modified();
_image_modified = get_texture()->get_image_modified();
- update_modified(std::max(_properties_modified, _image_modified));
+ update_modified((std::max)(_properties_modified, _image_modified));
// Assume the texture is now resident.
set_resident(true);
diff --git a/panda/src/gobj/textureReloadRequest.I b/panda/src/gobj/textureReloadRequest.I
index 1b958ac6ef..05ac192cfa 100644
--- a/panda/src/gobj/textureReloadRequest.I
+++ b/panda/src/gobj/textureReloadRequest.I
@@ -62,5 +62,5 @@ get_allow_compressed() const {
*/
INLINE bool TextureReloadRequest::
is_ready() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+ return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
}
diff --git a/panda/src/gobj/timerQueryContext.I b/panda/src/gobj/timerQueryContext.I
deleted file mode 100644
index a542e96855..0000000000
--- a/panda/src/gobj/timerQueryContext.I
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * 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 timerQueryContext.I
- * @author rdb
- * @date 2014-08-22
- */
-
-/**
- *
- */
-INLINE TimerQueryContext::
-TimerQueryContext(int pstats_index) :
- _pstats_index(pstats_index),
- _frame_index(ClockObject::get_global_clock()->get_frame_count())
-{
-}
diff --git a/panda/src/gobj/timerQueryContext.cxx b/panda/src/gobj/timerQueryContext.cxx
deleted file mode 100644
index af69030ff4..0000000000
--- a/panda/src/gobj/timerQueryContext.cxx
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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 timerQueryContext.cxx
- * @author rdb
- * @date 2014-08-22
- */
-
-#include "timerQueryContext.h"
-
-TypeHandle TimerQueryContext::_type_handle;
-
-/**
- * Returns the timestamp that is the result of this timer query. There's no
- * guarantee about which clock this uses, the only guarantee is that
- * subtracting a start time from an end time should yield a time in seconds.
- * If is_answer_ready() did not return true, this function may block before it
- * returns.
- *
- * It is only valid to call this from the draw thread.
- */
-double TimerQueryContext::
-get_timestamp() const {
- return 0.0;
-}
diff --git a/panda/src/gobj/timerQueryContext.h b/panda/src/gobj/timerQueryContext.h
deleted file mode 100644
index 26fd32efbd..0000000000
--- a/panda/src/gobj/timerQueryContext.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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 timerQueryContext.h
- * @author rdb
- * @date 2014-08-22
- */
-
-#ifndef TIMERQUERYCONTEXT_H
-#define TIMERQUERYCONTEXT_H
-
-#include "pandabase.h"
-#include "queryContext.h"
-#include "clockObject.h"
-#include "pStatCollector.h"
-
-/**
- *
- */
-class EXPCL_PANDA_GOBJ TimerQueryContext : public QueryContext {
-public:
- INLINE TimerQueryContext(int pstats_index);
-
- ALLOC_DELETED_CHAIN(TimerQueryContext);
-
- virtual double get_timestamp() const=0;
-
- int _frame_index;
- int _pstats_index;
-
-public:
- static TypeHandle get_class_type() {
- return _type_handle;
- }
- static void init_type() {
- QueryContext::init_type();
- register_type(_type_handle, "TimerQueryContext",
- QueryContext::get_class_type());
- }
- virtual TypeHandle get_type() const {
- return get_class_type();
- }
- virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
- static TypeHandle _type_handle;
-
- friend class PreparedGraphicsObjects;
-};
-
-#include "timerQueryContext.I"
-
-#endif
diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx
index 74e58edb58..7f48d39d0a 100644
--- a/panda/src/gobj/vertexDataPage.cxx
+++ b/panda/src/gobj/vertexDataPage.cxx
@@ -934,7 +934,7 @@ thread_main() {
_tlock.acquire();
while (true) {
- PStatClient::thread_tick(get_sync_name());
+ PStatClient::thread_tick();
while (_manager->_pending_reads.empty() &&
_manager->_pending_writes.empty()) {
diff --git a/panda/src/grutil/geoMipTerrain.I b/panda/src/grutil/geoMipTerrain.I
index 20f0f1241a..681cfce24e 100644
--- a/panda/src/grutil/geoMipTerrain.I
+++ b/panda/src/grutil/geoMipTerrain.I
@@ -483,8 +483,8 @@ get_border_stitching() {
*/
INLINE double GeoMipTerrain::
get_pixel_value(int x, int y) {
- x = std::max(std::min(x,int(_xsize-1)),0);
- y = std::max(std::min(y,int(_ysize-1)),0);
+ x = (std::max)((std::min)(x,int(_xsize-1)),0);
+ y = (std::max)((std::min)(y,int(_ysize-1)),0);
if (_heightfield.is_grayscale()) {
return double(_heightfield.get_bright(x, y));
} else {
diff --git a/panda/src/grutil/heightfieldTesselator.cxx b/panda/src/grutil/heightfieldTesselator.cxx
index 58d8324521..5c9089dba8 100644
--- a/panda/src/grutil/heightfieldTesselator.cxx
+++ b/panda/src/grutil/heightfieldTesselator.cxx
@@ -161,10 +161,10 @@ generate() {
PT(PandaNode) result = new PandaNode(get_name());
NodePath root(result);
- int total = 0;
+ //int total = 0;
for (int y=0; y= 0 && X + 1 < _index.size(), make_nan(0.0));
+ nassertr(X >= 0 && (size_t)X + 1 < _index.size(), make_nan(0.0));
int A = _index[X] + Y;
int B = _index[X + 1] + Y;
- nassertr(A >= 0 && A + 1 < _index.size(), make_nan(0.0));
- nassertr(B >= 0 && B + 1 < _index.size(), make_nan(0.0));
+ nassertr(A >= 0 && (size_t)A + 1 < _index.size(), make_nan(0.0));
+ nassertr(B >= 0 && (size_t)B + 1 < _index.size(), make_nan(0.0));
// and add blended results from 4 corners of square.
double result =
diff --git a/panda/src/mathutil/plane_src.cxx b/panda/src/mathutil/plane_src.cxx
index 7ba18701c6..4463d57756 100644
--- a/panda/src/mathutil/plane_src.cxx
+++ b/panda/src/mathutil/plane_src.cxx
@@ -158,3 +158,17 @@ void FLOATNAME(LPlane)::
write(std::ostream &out, int indent_level) const {
indent(out, indent_level) << *this << "\n";
}
+
+/**
+ * Returns a string representation of this LPlane.
+ */
+std::string FLOATNAME(LPlane)::
+__repr__() const {
+ std::ostringstream out;
+ out << "LPlane" << FLOATTOKEN << "("
+ << MAYBE_ZERO(_v(0)) << ", "
+ << MAYBE_ZERO(_v(1)) << ", "
+ << MAYBE_ZERO(_v(2)) << ", "
+ << MAYBE_ZERO(_v(3)) << ")";
+ return out.str();
+}
diff --git a/panda/src/mathutil/plane_src.h b/panda/src/mathutil/plane_src.h
index c1dffc4d5d..c1d7ef0252 100644
--- a/panda/src/mathutil/plane_src.h
+++ b/panda/src/mathutil/plane_src.h
@@ -61,6 +61,7 @@ PUBLISHED:
void output(std::ostream &out) const;
void write(std::ostream &out, int indent_level = 0) const;
+ std::string __repr__() const;
};
INLINE_MATHUTIL std::ostream &
diff --git a/panda/src/mathutil/triangulator.h b/panda/src/mathutil/triangulator.h
index 0514917cfd..f0c7ca4c75 100644
--- a/panda/src/mathutil/triangulator.h
+++ b/panda/src/mathutil/triangulator.h
@@ -16,6 +16,7 @@
#include "pandabase.h"
#include "luse.h"
+#include "memoryBase.h"
#include "vector_int.h"
/**
@@ -29,7 +30,7 @@
*
* It works strictly on 2-d points. See Triangulator3 for 3-d points.
*/
-class EXPCL_PANDA_MATHUTIL Triangulator {
+class EXPCL_PANDA_MATHUTIL Triangulator : public MemoryBase {
PUBLISHED:
Triangulator();
diff --git a/panda/src/movies/CMakeLists.txt b/panda/src/movies/CMakeLists.txt
index 30e01d254a..fc61423d18 100644
--- a/panda/src/movies/CMakeLists.txt
+++ b/panda/src/movies/CMakeLists.txt
@@ -51,6 +51,24 @@ target_link_libraries(p3movies p3pstatclient p3gobj p3pandabase pandaexpress
PKG::VORBIS PKG::OPUS)
target_interrogate(p3movies ALL)
+if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Do not re-export symbols from these libraries.
+ if(HAVE_OPUS OR HAVE_VORBIS)
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libogg.a")
+ endif()
+
+ if(HAVE_VORBIS)
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbis.a")
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbisenc.a")
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libvorbisfile.a")
+ endif()
+
+ if(HAVE_OPUS)
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libopus.a")
+ target_link_options(p3movies PRIVATE "LINKER:--exclude-libs,libopusfile.a")
+ endif()
+endif()
+
if(NOT BUILD_METALIBS)
install(TARGETS p3movies
EXPORT Core COMPONENT Core
diff --git a/panda/src/parametrics/nurbsBasisVector.I b/panda/src/parametrics/nurbsBasisVector.I
index 3628d7564b..8bb907f8e9 100644
--- a/panda/src/parametrics/nurbsBasisVector.I
+++ b/panda/src/parametrics/nurbsBasisVector.I
@@ -110,5 +110,5 @@ scale_t(int segment, PN_stdfloat t) const {
PN_stdfloat from = _segments[segment]._from;
PN_stdfloat to = _segments[segment]._to;
t = (t - from) / (to - from);
- return std::min(std::max(t, (PN_stdfloat)0.0), (PN_stdfloat)1.0);
+ return (std::min)((std::max)(t, (PN_stdfloat)0.0), (PN_stdfloat)1.0);
}
diff --git a/panda/src/parametrics/parametricCurveCollection.I b/panda/src/parametrics/parametricCurveCollection.I
index 711b43b67b..99ebe9e63c 100644
--- a/panda/src/parametrics/parametricCurveCollection.I
+++ b/panda/src/parametrics/parametricCurveCollection.I
@@ -42,7 +42,7 @@ get_curve(int index) const {
*/
INLINE void ParametricCurveCollection::
add_curve(ParametricCurve *curve, int index) {
- insert_curve(std::max(index, 0), curve);
+ insert_curve((std::max)(index, 0), curve);
}
/**
diff --git a/panda/src/particlesystem/baseParticleRenderer.I b/panda/src/particlesystem/baseParticleRenderer.I
index 7da2037edd..29dd3878aa 100644
--- a/panda/src/particlesystem/baseParticleRenderer.I
+++ b/panda/src/particlesystem/baseParticleRenderer.I
@@ -97,7 +97,7 @@ get_cur_alpha(BaseParticle* bp) {
return bp->get_parameterized_age();
case PR_ALPHA_IN_OUT:
- return 2.0 * std::min(bp->get_parameterized_age(),
+ return 2.0 * (std::min)(bp->get_parameterized_age(),
1.0f - bp->get_parameterized_age());
case PR_ALPHA_USER:
diff --git a/panda/src/pgraph/CMakeLists.txt b/panda/src/pgraph/CMakeLists.txt
index 85c041f01b..8319e31863 100644
--- a/panda/src/pgraph/CMakeLists.txt
+++ b/panda/src/pgraph/CMakeLists.txt
@@ -66,7 +66,6 @@ set(P3PGRAPH_HEADERS
occluderEffect.I occluderEffect.h
occluderNode.I occluderNode.h
pandaNode.I pandaNode.h
- pandaNodeChain.I pandaNodeChain.h
planeNode.I planeNode.h
paramNodePath.I paramNodePath.h
polylightEffect.I polylightEffect.h
@@ -169,7 +168,6 @@ set(P3PGRAPH_SOURCES
occluderEffect.cxx
occluderNode.cxx
pandaNode.cxx
- pandaNodeChain.cxx
planeNode.cxx
paramNodePath.cxx
polylightEffect.cxx
diff --git a/panda/src/pgraph/cacheStats.I b/panda/src/pgraph/cacheStats.I
index e84ff83878..9cd08c4b3f 100644
--- a/panda/src/pgraph/cacheStats.I
+++ b/panda/src/pgraph/cacheStats.I
@@ -90,6 +90,6 @@ add_total_size(int count) {
INLINE void CacheStats::
add_num_states(int count) {
#ifndef NDEBUG
- _num_states += count;
+ _num_states.fetch_add(count, std::memory_order_relaxed);
#endif // NDEBUG
}
diff --git a/panda/src/pgraph/cacheStats.cxx b/panda/src/pgraph/cacheStats.cxx
index 4721171e86..118e469c9d 100644
--- a/panda/src/pgraph/cacheStats.cxx
+++ b/panda/src/pgraph/cacheStats.cxx
@@ -51,12 +51,13 @@ reset(double now) {
void CacheStats::
write(std::ostream &out, const char *name) const {
#ifndef NDEBUG
+ int num_states = _num_states.load(std::memory_order_relaxed);
out << name << " cache: " << _cache_hits << " hits, "
<< _cache_misses << " misses\n"
<< _cache_adds + _cache_new_adds << "(" << _cache_new_adds << ") adds(new), "
<< _cache_dels << " dels, "
- << _total_cache_size << " / " << _num_states << " = "
- << (double)_total_cache_size / (double)_num_states
+ << _total_cache_size << " / " << num_states << " = "
+ << (double)_total_cache_size / (double)num_states
<< " average cache size\n";
#endif // NDEBUG
}
diff --git a/panda/src/pgraph/cacheStats.h b/panda/src/pgraph/cacheStats.h
index daeeb5340d..b415843e34 100644
--- a/panda/src/pgraph/cacheStats.h
+++ b/panda/src/pgraph/cacheStats.h
@@ -16,6 +16,7 @@
#include "pandabase.h"
#include "clockObject.h"
+#include "patomic.h"
#include "pnotify.h"
/**
@@ -24,7 +25,7 @@
*/
class EXPCL_PANDA_PGRAPH CacheStats {
public:
- CacheStats() = default;
+ constexpr CacheStats() = default;
void init();
void reset(double now);
void write(std::ostream &out, const char *name) const;
@@ -45,7 +46,7 @@ private:
int _cache_new_adds = 0;
int _cache_dels = 0;
int _total_cache_size = 0;
- int _num_states = 0;
+ patomic _num_states {0};
double _last_reset = 0.0;
bool _cache_report = false;
diff --git a/panda/src/pgraph/camera.cxx b/panda/src/pgraph/camera.cxx
index ddf8006862..4d5f7f6602 100644
--- a/panda/src/pgraph/camera.cxx
+++ b/panda/src/pgraph/camera.cxx
@@ -28,8 +28,8 @@ Camera(const string &name, Lens *lens) :
LensNode(name, lens),
_active(true),
_camera_mask(~PandaNode::get_overall_bit()),
- _initial_state(RenderState::make_empty()),
- _lod_scale(1)
+ _lod_scale(1),
+ _initial_state(RenderState::make_empty())
{
}
@@ -42,8 +42,8 @@ Camera(const Camera ©) :
_active(copy._active),
_scene(copy._scene),
_camera_mask(copy._camera_mask),
- _initial_state(copy._initial_state),
_lod_scale(copy._lod_scale),
+ _initial_state(copy._initial_state),
_tag_state_key(copy._tag_state_key),
_tag_states(copy._tag_states)
{
diff --git a/panda/src/pgraph/cullTraverser.I b/panda/src/pgraph/cullTraverser.I
index 7b243bc7c7..8af8c11c9e 100644
--- a/panda/src/pgraph/cullTraverser.I
+++ b/panda/src/pgraph/cullTraverser.I
@@ -198,6 +198,7 @@ INLINE void CullTraverser::
flush_level() {
_nodes_pcollector.flush_level();
_geom_nodes_pcollector.flush_level();
+ _pgui_nodes_pcollector.flush_level();
_geoms_pcollector.flush_level();
_geoms_occluded_pcollector.flush_level();
}
diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx
index 1183707240..aa1ea8eaa2 100644
--- a/panda/src/pgraph/cullTraverser.cxx
+++ b/panda/src/pgraph/cullTraverser.cxx
@@ -38,6 +38,7 @@
PStatCollector CullTraverser::_nodes_pcollector("Nodes");
PStatCollector CullTraverser::_geom_nodes_pcollector("Nodes:GeomNodes");
+PStatCollector CullTraverser::_pgui_nodes_pcollector("Nodes:GUI");
PStatCollector CullTraverser::_geoms_pcollector("Geoms");
PStatCollector CullTraverser::_geoms_occluded_pcollector("Geoms:Occluded");
diff --git a/panda/src/pgraph/cullTraverser.h b/panda/src/pgraph/cullTraverser.h
index a243a165cc..77856351b8 100644
--- a/panda/src/pgraph/cullTraverser.h
+++ b/panda/src/pgraph/cullTraverser.h
@@ -108,6 +108,7 @@ public:
// Statistics
static PStatCollector _nodes_pcollector;
static PStatCollector _geom_nodes_pcollector;
+ static PStatCollector _pgui_nodes_pcollector;
static PStatCollector _geoms_pcollector;
static PStatCollector _geoms_occluded_pcollector;
diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx
index 575def2e22..07d0dea1c1 100644
--- a/panda/src/pgraph/cullableObject.cxx
+++ b/panda/src/pgraph/cullableObject.cxx
@@ -360,7 +360,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
for (size_t ai = 0; ai < sformat._format->get_num_arrays(); ++ai) {
const GeomVertexArrayFormat *aformat = sformat._format->get_array(ai);
- for (size_t ci = 0; ci < aformat->get_num_columns(); ++ci) {
+ for (size_t ci = 0; ci < (size_t)aformat->get_num_columns(); ++ci) {
const GeomVertexColumn *column = aformat->get_column(ci);
const InternalName *name = column->get_name();
if (name != InternalName::get_vertex() &&
@@ -436,7 +436,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
pvector copies;
const GeomVertexArrayFormat *aformat = new_format->get_array(0);
- for (size_t ci = 0; ci < aformat->get_num_columns(); ++ci) {
+ for (size_t ci = 0; ci < (size_t)aformat->get_num_columns(); ++ci) {
const GeomVertexColumn *column = aformat->get_column(ci);
const InternalName *name = column->get_name();
if (name != InternalName::get_vertex() &&
diff --git a/panda/src/pgraph/modelFlattenRequest.I b/panda/src/pgraph/modelFlattenRequest.I
index 75f177e6a8..d63ede05e4 100644
--- a/panda/src/pgraph/modelFlattenRequest.I
+++ b/panda/src/pgraph/modelFlattenRequest.I
@@ -39,7 +39,7 @@ get_orig() const {
*/
INLINE bool ModelFlattenRequest::
is_ready() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+ return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
}
/**
diff --git a/panda/src/pgraph/modelLoadRequest.I b/panda/src/pgraph/modelLoadRequest.I
index 3fb37a1bdc..0f01d01e08 100644
--- a/panda/src/pgraph/modelLoadRequest.I
+++ b/panda/src/pgraph/modelLoadRequest.I
@@ -46,7 +46,7 @@ get_loader() const {
*/
INLINE bool ModelLoadRequest::
is_ready() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+ return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
}
/**
diff --git a/panda/src/pgraph/modelSaveRequest.I b/panda/src/pgraph/modelSaveRequest.I
index e84b8929af..524f09f956 100644
--- a/panda/src/pgraph/modelSaveRequest.I
+++ b/panda/src/pgraph/modelSaveRequest.I
@@ -54,7 +54,7 @@ get_loader() const {
*/
INLINE bool ModelSaveRequest::
is_ready() const {
- return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+ return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
}
/**
diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I
index 0034b2a8d7..ae0236f427 100644
--- a/panda/src/pgraph/nodePath.I
+++ b/panda/src/pgraph/nodePath.I
@@ -16,8 +16,8 @@
*/
INLINE NodePath::
NodePath() :
- _error_type(ET_ok),
- _backup_key(0)
+ _backup_key(0),
+ _error_type(ET_ok)
{
}
diff --git a/panda/src/pgraph/p3pgraph_composite3.cxx b/panda/src/pgraph/p3pgraph_composite3.cxx
index 18be4cb35e..712ed2f778 100644
--- a/panda/src/pgraph/p3pgraph_composite3.cxx
+++ b/panda/src/pgraph/p3pgraph_composite3.cxx
@@ -21,7 +21,6 @@
#include "occluderEffect.cxx"
#include "occluderNode.cxx"
#include "pandaNode.cxx"
-#include "pandaNodeChain.cxx"
#include "paramNodePath.cxx"
#include "planeNode.cxx"
#include "polylightEffect.cxx"
diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I
index bf961413c5..a79fb75a5c 100644
--- a/panda/src/pgraph/pandaNode.I
+++ b/panda/src/pgraph/pandaNode.I
@@ -326,7 +326,13 @@ clear_transform(Thread *current_thread) {
INLINE CPT(TransformState) PandaNode::
get_prev_transform(Thread *current_thread) const {
CDReader cdata(_cycler, current_thread);
- return cdata->_prev_transform.p();
+ if (_prev_transform_valid == _reset_prev_transform_seq) {
+ return cdata->_prev_transform.p();
+ } else {
+ // If these values are different, someone called reset_prev_transform(),
+ // and we haven't changed our transform since then.
+ return cdata->_transform.p();
+ }
}
/**
@@ -334,10 +340,19 @@ get_prev_transform(Thread *current_thread) const {
* indicates its _prev_transform is different from its _transform value (in
* pipeline stage 0). In this case, the node will be visited by
* reset_prev_transform().
+ *
+ * @deprecated Simply check prev_transform != transform instead.
*/
INLINE bool PandaNode::
has_dirty_prev_transform() const {
- return _dirty_prev_transform;
+ CDStageReader cdata(_cycler, 0);
+ if (_prev_transform_valid == _reset_prev_transform_seq) {
+ return cdata->_prev_transform != cdata->_transform;
+ } else {
+ // If these values are different, someone called reset_prev_transform(),
+ // and we haven't changed our transform since then.
+ return false;
+ }
}
/**
@@ -701,34 +716,6 @@ verify_child_no_cycles(PandaNode *child_node) {
return true;
}
-/**
- * Sets the dirty_prev_transform flag, and adds the node to the
- * _dirty_prev_transforms chain. Assumes _dirty_prev_transforms._lock is
- * already held.
- */
-INLINE void PandaNode::
-do_set_dirty_prev_transform() {
- nassertv(_dirty_prev_transforms._lock.debug_is_locked());
- if (!_dirty_prev_transform) {
- LinkedListNode::insert_before(&_dirty_prev_transforms);
- _dirty_prev_transform = true;
- }
-}
-
-/**
- * Clears the dirty_prev_transform flag, and removes the node from the
- * _dirty_prev_transforms chain. Assumes _dirty_prev_transforms._lock is
- * already held.
- */
-INLINE void PandaNode::
-do_clear_dirty_prev_transform() {
- nassertv(_dirty_prev_transforms._lock.debug_is_locked());
- if (_dirty_prev_transform) {
- LinkedListNode::remove_from_list();
- _dirty_prev_transform = false;
- }
-}
-
/**
*
*/
@@ -1513,7 +1500,11 @@ get_transform() const {
*/
const TransformState *PandaNodePipelineReader::
get_prev_transform() const {
- return _cdata->_prev_transform;
+ if (_node->_prev_transform_valid == PandaNode::_reset_prev_transform_seq) {
+ return _cdata->_prev_transform;
+ } else {
+ return _cdata->_transform;
+ }
}
/**
diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx
index 31dee668bd..00f0d76143 100644
--- a/panda/src/pgraph/pandaNode.cxx
+++ b/panda/src/pgraph/pandaNode.cxx
@@ -42,10 +42,9 @@ TypeHandle PandaNode::BamReaderAuxDataDown::_type_handle;
PandaNode::SceneRootFunc *PandaNode::_scene_root_func;
-PandaNodeChain PandaNode::_dirty_prev_transforms("_dirty_prev_transforms");
+UpdateSeq PandaNode::_reset_prev_transform_seq;
DrawMask PandaNode::_overall_bit = DrawMask::bit(31);
-PStatCollector PandaNode::_reset_prev_pcollector("App:Collisions:Reset");
PStatCollector PandaNode::_update_bounds_pcollector("*:Bounds");
TypeHandle PandaNode::_type_handle;
@@ -78,7 +77,7 @@ PandaNode::
PandaNode(const string &name) :
Namable(name),
_paths_lock("PandaNode::_paths_lock"),
- _dirty_prev_transform(false)
+ _prev_transform_valid(_reset_prev_transform_seq)
{
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
@@ -100,12 +99,6 @@ PandaNode::
<< "Destructing " << (void *)this << ", " << get_name() << "\n";
}
- if (_dirty_prev_transform) {
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- do_clear_dirty_prev_transform();
- }
-
// We shouldn't have any parents left by the time we destruct, or there's a
// refcount fault somewhere.
@@ -133,7 +126,7 @@ PandaNode(const PandaNode ©) :
TypedWritableReferenceCount(copy),
Namable(copy),
_paths_lock("PandaNode::_paths_lock"),
- _dirty_prev_transform(false),
+ _prev_transform_valid(_reset_prev_transform_seq),
_python_tag_data(copy._python_tag_data),
_unexpected_change_flags(0)
{
@@ -145,18 +138,16 @@ PandaNode(const PandaNode ©) :
MemoryUsage::update_type(this, this);
#endif
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
-
// Copy the other node's state.
{
CDReader copy_cdata(copy._cycler);
CDWriter cdata(_cycler, true);
cdata->_state = copy_cdata->_state;
cdata->_transform = copy_cdata->_transform;
- cdata->_prev_transform = copy_cdata->_prev_transform;
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
+ if (copy._prev_transform_valid == _reset_prev_transform_seq) {
+ cdata->_prev_transform = copy_cdata->_prev_transform;
+ } else {
+ cdata->_prev_transform = copy_cdata->_transform;
}
cdata->_effects = copy_cdata->_effects;
@@ -1061,24 +1052,23 @@ void PandaNode::
set_transform(const TransformState *transform, Thread *current_thread) {
nassertv(!transform->is_invalid());
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
-
// Apply this operation to the current stage as well as to all upstream
// stages.
bool any_changed = false;
OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
if (cdata->_transform != transform) {
+ if (pipeline_stage == 0) {
+ // Back up the previous transform.
+ if (_prev_transform_valid != _reset_prev_transform_seq) {
+ cdata->_prev_transform = std::move(cdata->_transform);
+ _prev_transform_valid = _reset_prev_transform_seq;
+ }
+ }
+
cdata->_transform = transform;
cdata->set_fancy_bit(FB_transform, !transform->is_identity());
any_changed = true;
-
- if (pipeline_stage == 0) {
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
- }
- }
}
}
CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
@@ -1099,20 +1089,13 @@ void PandaNode::
set_prev_transform(const TransformState *transform, Thread *current_thread) {
nassertv(!transform->is_invalid());
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
-
// Apply this operation to the current stage as well as to all upstream
// stages.
OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
cdata->_prev_transform = transform;
if (pipeline_stage == 0) {
- if (cdata->_transform != cdata->_prev_transform) {
- do_set_dirty_prev_transform();
- } else {
- do_clear_dirty_prev_transform();
- }
+ _prev_transform_valid = _reset_prev_transform_seq;
}
}
CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
@@ -1126,10 +1109,6 @@ set_prev_transform(const TransformState *transform, Thread *current_thread) {
*/
void PandaNode::
reset_prev_transform(Thread *current_thread) {
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
- do_clear_dirty_prev_transform();
-
// Apply this operation to the current stage as well as to all upstream
// stages.
@@ -1138,41 +1117,22 @@ reset_prev_transform(Thread *current_thread) {
cdata->_prev_transform = cdata->_transform;
}
CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
- mark_bam_modified();
}
/**
- * Visits all nodes in the world with the _dirty_prev_transform flag--that is,
- * all nodes whose _prev_transform is different from the _transform in
- * pipeline stage 0--and resets the _prev_transform to be the same as
- * _transform.
+ * Makes sure that all nodes reset their prev_transform value to be the same as
+ * their transform value. This should be called at the start of each frame.
*/
void PandaNode::
reset_all_prev_transform(Thread *current_thread) {
nassertv(current_thread->get_pipeline_stage() == 0);
- PStatTimer timer(_reset_prev_pcollector, current_thread);
- LightMutexHolder holder(_dirty_prev_transforms._lock);
-
- LinkedListNode *list_node = _dirty_prev_transforms._next;
- while (list_node != &_dirty_prev_transforms) {
- PandaNode *panda_node = (PandaNode *)list_node;
- nassertv(panda_node->_dirty_prev_transform);
- panda_node->_dirty_prev_transform = false;
-
- CDStageWriter cdata(panda_node->_cycler, 0, current_thread);
- cdata->_prev_transform = cdata->_transform;
-
- list_node = panda_node->_next;
-#ifndef NDEBUG
- panda_node->_prev = nullptr;
- panda_node->_next = nullptr;
-#endif // NDEBUG
- panda_node->mark_bam_modified();
- }
-
- _dirty_prev_transforms._prev = &_dirty_prev_transforms;
- _dirty_prev_transforms._next = &_dirty_prev_transforms;
+ // Rather than keeping a linked list of all nodes that have changed their
+ // transform, we simply increment this counter. All the nodes compare this
+ // value to their own _prev_transform_valid value, and if it's different,
+ // they should disregard their _prev_transform field and assume it's the same
+ // as their _transform.
+ ++_reset_prev_transform_seq;
}
/**
@@ -1345,9 +1305,6 @@ copy_all_properties(PandaNode *other) {
return;
}
- // Need to have this held before we grab any other locks.
- LightMutexHolder holder(_dirty_prev_transforms._lock);
-
bool any_transform_changed = false;
bool any_state_changed = false;
bool any_draw_mask_changed = false;
@@ -1368,7 +1325,11 @@ copy_all_properties(PandaNode *other) {
}
cdataw->_transform = cdatar->_transform;
- cdataw->_prev_transform = cdatar->_prev_transform;
+ if (other->_prev_transform_valid == _reset_prev_transform_seq) {
+ cdataw->_prev_transform = cdatar->_prev_transform;
+ } else {
+ cdataw->_prev_transform = cdatar->_transform;
+ }
cdataw->_state = cdatar->_state;
cdataw->_effects = cdatar->_effects;
cdataw->_draw_control_mask = cdatar->_draw_control_mask;
@@ -1390,9 +1351,7 @@ copy_all_properties(PandaNode *other) {
(cdatar->_fancy_bits & change_bits);
if (pipeline_stage == 0) {
- if (cdataw->_transform != cdataw->_prev_transform) {
- do_set_dirty_prev_transform();
- }
+ _prev_transform_valid = _reset_prev_transform_seq;
}
}
CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h
index d078839b7e..7542fae076 100644
--- a/panda/src/pgraph/pandaNode.h
+++ b/panda/src/pgraph/pandaNode.h
@@ -39,7 +39,6 @@
#include "pnotify.h"
#include "updateSeq.h"
#include "deletedChain.h"
-#include "pandaNodeChain.h"
#include "pStatCollector.h"
#include "copyOnWriteObject.h"
#include "copyOnWritePointer.h"
@@ -63,7 +62,7 @@ class GraphicsStateGuardianBase;
* properties.
*/
class EXPCL_PANDA_PGRAPH PandaNode : public TypedWritableReferenceCount,
- public Namable, public LinkedListNode {
+ public Namable {
PUBLISHED:
explicit PandaNode(const std::string &name);
virtual ~PandaNode();
@@ -449,9 +448,6 @@ private:
void fix_path_lengths(int pipeline_stage, Thread *current_thread);
void r_list_descendants(std::ostream &out, int indent_level) const;
- INLINE void do_set_dirty_prev_transform();
- INLINE void do_clear_dirty_prev_transform();
-
public:
// This must be declared public so that VC6 will allow the nested CData
// class to access it.
@@ -540,8 +536,10 @@ private:
Paths _paths;
LightReMutex _paths_lock;
- bool _dirty_prev_transform;
- static PandaNodeChain _dirty_prev_transforms;
+ // This is not part of CData because we only care about modifications to the
+ // transform in the App stage.
+ UpdateSeq _prev_transform_valid;
+ static UpdateSeq _reset_prev_transform_seq;
// This is used to maintain a table of keyed data on each node, for the
// user's purposes.
@@ -713,7 +711,6 @@ private:
static DrawMask _overall_bit;
- static PStatCollector _reset_prev_pcollector;
static PStatCollector _update_bounds_pcollector;
PUBLISHED:
diff --git a/panda/src/pgraph/pandaNodeChain.I b/panda/src/pgraph/pandaNodeChain.I
deleted file mode 100644
index c53beec7e7..0000000000
--- a/panda/src/pgraph/pandaNodeChain.I
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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 pandaNodeChain.I
- * @author drose
- * @date 2006-04-21
- */
-
-/**
- *
- */
-INLINE PandaNodeChain::
-PandaNodeChain(const char *lock_name) :
- LinkedListNode(true), // This object is the root of a list of PandaNodes.
- _lock(lock_name)
-{
-}
-
-/**
- *
- */
-INLINE PandaNodeChain::
-~PandaNodeChain() {
- _next = nullptr;
- _prev = nullptr;
-}
diff --git a/panda/src/pgraph/pandaNodeChain.cxx b/panda/src/pgraph/pandaNodeChain.cxx
deleted file mode 100644
index d52fb2b4f0..0000000000
--- a/panda/src/pgraph/pandaNodeChain.cxx
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * 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 pandaNodeChain.cxx
- * @author drose
- * @date 2006-04-21
- */
-
-#include "pandaNodeChain.h"
diff --git a/panda/src/pgraph/pandaNodeChain.h b/panda/src/pgraph/pandaNodeChain.h
deleted file mode 100644
index 002a30041c..0000000000
--- a/panda/src/pgraph/pandaNodeChain.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * 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 pandaNodeChain.h
- * @author drose
- * @date 2006-04-21
- */
-
-#ifndef PANDANODECHAIN_H
-#define PANDANODECHAIN_H
-
-#include "pandabase.h"
-#include "linkedListNode.h"
-#include "lightMutex.h"
-
-class PandaNode;
-
-/**
- * This class maintains a linked list of PandaNodes. It's used to maintain a
- * list of PandaNodes whose _prev_transform is different from their _transform
- * (in pipeline stage 0).
- */
-class EXPCL_PANDA_PGRAPH PandaNodeChain : private LinkedListNode {
-public:
- INLINE PandaNodeChain(const char *lock_name);
- INLINE ~PandaNodeChain();
-
- LightMutex _lock;
-
- friend class PandaNode;
-};
-
-#include "pandaNodeChain.I"
-
-#endif
diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I
index b722a4cc07..1398332c5e 100644
--- a/panda/src/pgraph/renderState.I
+++ b/panda/src/pgraph/renderState.I
@@ -386,22 +386,6 @@ consider_update_pstats(int old_referenced_bits) const {
#endif // DO_PSTATS
}
-/**
- *
- */
-INLINE RenderState::Composition::
-Composition() {
-}
-
-/**
- *
- */
-INLINE RenderState::Composition::
-Composition(const RenderState::Composition ©) :
- _result(copy._result)
-{
-}
-
/**
*
*/
diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx
index 805892b947..76884ea0bb 100644
--- a/panda/src/pgraph/renderState.cxx
+++ b/panda/src/pgraph/renderState.cxx
@@ -117,8 +117,6 @@ RenderState::
nassertv(!is_destructing());
set_destructing();
- LightReMutexHolder holder(*_states_lock);
-
// unref() should have cleared these.
nassertv(_saved_entry == -1);
nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h
index f096dfee27..340eb5dac1 100644
--- a/panda/src/pgraph/renderState.h
+++ b/panda/src/pgraph/renderState.h
@@ -253,9 +253,6 @@ private:
// two involved RenderState objects.
class Composition {
public:
- INLINE Composition();
- INLINE Composition(const Composition ©);
-
// _result is reference counted if and only if it is not the same pointer
// as this.
const RenderState *_result;
diff --git a/panda/src/pgraph/shaderInput.I b/panda/src/pgraph/shaderInput.I
index 2ace5d614b..1aa2b9e583 100644
--- a/panda/src/pgraph/shaderInput.I
+++ b/panda/src/pgraph/shaderInput.I
@@ -19,8 +19,8 @@
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, int priority) :
_name(std::move(name)),
- _type(M_invalid),
- _priority(priority)
+ _priority(priority),
+ _type(M_invalid)
{
}
@@ -30,9 +30,9 @@ ShaderInput(CPT_InternalName name, int priority) :
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, Texture *tex, int priority) :
_name(std::move(name)),
- _type(M_texture),
+ _value(tex),
_priority(priority),
- _value(tex)
+ _type(M_texture)
{
}
@@ -42,9 +42,9 @@ ShaderInput(CPT_InternalName name, Texture *tex, int priority) :
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) :
_name(std::move(name)),
- _type(M_param),
+ _value(param),
_priority(priority),
- _value(param)
+ _type(M_param)
{
}
@@ -54,9 +54,9 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) :
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) :
_name(std::move(name)),
- _type(M_buffer),
+ _value(buf),
_priority(priority),
- _value(buf)
+ _type(M_buffer)
{
}
@@ -65,10 +65,10 @@ ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -77,10 +77,10 @@ ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -89,10 +89,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -101,10 +101,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase2f &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -113,11 +113,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2f &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase4f &vec, int priority) :
- _name(std::move(name)),
- _type(M_vector),
- _priority(priority),
+ _stored_vector(LCAST(PN_stdfloat, vec)),
_stored_ptr(vec),
- _stored_vector(LCAST(PN_stdfloat, vec))
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_vector)
{
}
@@ -126,11 +126,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4f &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase3f &vec, int priority) :
- _name(std::move(name)),
- _type(M_vector),
- _priority(priority),
+ _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0),
_stored_ptr(vec),
- _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_vector)
{
}
@@ -139,11 +139,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3f &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase2f &vec, int priority) :
- _name(std::move(name)),
- _type(M_vector),
- _priority(priority),
+ _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0),
_stored_ptr(vec),
- _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_vector)
{
}
@@ -152,10 +152,10 @@ ShaderInput(CPT_InternalName name, const LVecBase2f &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LMatrix4f &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -164,10 +164,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix4f &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LMatrix3f &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -176,10 +176,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix3f &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LMatrix4f &mat, int priority) :
+ _stored_ptr(mat),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(mat)
+ _type(M_numeric)
{
}
@@ -188,10 +188,10 @@ ShaderInput(CPT_InternalName name, const LMatrix4f &mat, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LMatrix3f &mat, int priority) :
+ _stored_ptr(mat),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(mat)
+ _type(M_numeric)
{
}
@@ -200,10 +200,10 @@ ShaderInput(CPT_InternalName name, const LMatrix3f &mat, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_double &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -212,10 +212,10 @@ ShaderInput(CPT_InternalName name, const PTA_double &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase4d &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -224,10 +224,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4d &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase3d &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -236,10 +236,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3d &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase2d &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -248,11 +248,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2d &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase4d &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector(LCAST(PN_stdfloat, vec)),
_stored_ptr(vec),
- _stored_vector(LCAST(PN_stdfloat, vec))
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
@@ -261,11 +261,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4d &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase3d &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0),
_stored_ptr(vec),
- _stored_vector(vec.get_x(), vec.get_y(), vec.get_z(), 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
@@ -274,11 +274,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3d &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase2d &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0),
_stored_ptr(vec),
- _stored_vector(vec.get_x(), vec.get_y(), 0.0, 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
@@ -287,10 +287,10 @@ ShaderInput(CPT_InternalName name, const LVecBase2d &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LMatrix4d &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -299,10 +299,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix4d &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LMatrix3d &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -311,10 +311,10 @@ ShaderInput(CPT_InternalName name, const PTA_LMatrix3d &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LMatrix4d &mat, int priority) :
+ _stored_ptr(mat),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(mat)
+ _type(M_numeric)
{
}
@@ -323,10 +323,10 @@ ShaderInput(CPT_InternalName name, const LMatrix4d &mat, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LMatrix3d &mat, int priority) :
+ _stored_ptr(mat),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(mat)
+ _type(M_numeric)
{
}
@@ -335,10 +335,10 @@ ShaderInput(CPT_InternalName name, const LMatrix3d &mat, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_int &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -347,10 +347,10 @@ ShaderInput(CPT_InternalName name, const PTA_int &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase4i &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -359,10 +359,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase4i &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase3i &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -371,10 +371,10 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase3i &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const PTA_LVecBase2i &ptr, int priority) :
+ _stored_ptr(ptr),
_name(std::move(name)),
- _type(M_numeric),
_priority(priority),
- _stored_ptr(ptr)
+ _type(M_numeric)
{
}
@@ -383,11 +383,11 @@ ShaderInput(CPT_InternalName name, const PTA_LVecBase2i &ptr, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase4i &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), (int)vec.get_w()),
_stored_ptr(vec),
- _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), (int)vec.get_w())
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
@@ -396,11 +396,11 @@ ShaderInput(CPT_InternalName name, const LVecBase4i &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), 0.0),
_stored_ptr(vec),
- _stored_vector((int)vec.get_x(), (int)vec.get_y(), (int)vec.get_z(), 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
@@ -409,11 +409,11 @@ ShaderInput(CPT_InternalName name, const LVecBase3i &vec, int priority) :
*/
INLINE ShaderInput::
ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) :
- _name(std::move(name)),
- _type(M_numeric),
- _priority(priority),
+ _stored_vector((int)vec.get_x(), (int)vec.get_y(), 0.0, 0.0),
_stored_ptr(vec),
- _stored_vector((int)vec.get_x(), (int)vec.get_y(), 0.0, 0.0)
+ _name(std::move(name)),
+ _priority(priority),
+ _type(M_numeric)
{
}
diff --git a/panda/src/pgraph/shaderInput.cxx b/panda/src/pgraph/shaderInput.cxx
index ddb390f05d..9730351a3d 100644
--- a/panda/src/pgraph/shaderInput.cxx
+++ b/panda/src/pgraph/shaderInput.cxx
@@ -31,9 +31,9 @@ get_blank() {
ShaderInput::
ShaderInput(CPT_InternalName name, const NodePath &np, int priority) :
_name(std::move(name)),
- _type(M_nodepath),
+ _value(new ParamNodePath(np)),
_priority(priority),
- _value(new ParamNodePath(np))
+ _type(M_nodepath)
{
}
@@ -43,9 +43,9 @@ ShaderInput(CPT_InternalName name, const NodePath &np, int priority) :
ShaderInput::
ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z, int n, int priority) :
_name(std::move(name)),
- _type(M_texture_image),
+ _value(new ParamTextureImage(tex, read, write, z, n)),
_priority(priority),
- _value(new ParamTextureImage(tex, read, write, z, n))
+ _type(M_texture_image)
{
}
@@ -55,9 +55,9 @@ ShaderInput(CPT_InternalName name, Texture *tex, bool read, bool write, int z, i
ShaderInput::
ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, int priority) :
_name(std::move(name)),
- _type(M_texture_sampler),
+ _value(new ParamTextureSampler(tex, sampler)),
_priority(priority),
- _value(new ParamTextureSampler(tex, sampler))
+ _type(M_texture_sampler)
{
}
diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h
index dbbf617ff0..2901ccb60d 100644
--- a/panda/src/pgraph/shaderInput.h
+++ b/panda/src/pgraph/shaderInput.h
@@ -37,7 +37,7 @@
* This is a small container class that can hold any one of the value types
* that can be passed as input to a shader.
*/
-class EXPCL_PANDA_PGRAPH ShaderInput {
+class EXPCL_PANDA_PGRAPH ShaderInput : public MemoryBase {
PUBLISHED:
// Used when binding texture images.
enum AccessFlags {
diff --git a/panda/src/pgraph/textureAttrib.I b/panda/src/pgraph/textureAttrib.I
index c3d40bafbe..3814755033 100644
--- a/panda/src/pgraph/textureAttrib.I
+++ b/panda/src/pgraph/textureAttrib.I
@@ -236,9 +236,9 @@ StageNode(const TextureStage *stage, unsigned int implicit_sort, int override) :
// Yeah, we cast away the constness here. Just too much trouble to deal
// with it properly.
_stage((TextureStage *)stage),
+ _has_sampler(false),
_implicit_sort(implicit_sort),
- _override(override),
- _has_sampler(false)
+ _override(override)
{
}
diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I
index 6097430e91..c05e7cc6c9 100644
--- a/panda/src/pgraph/transformState.I
+++ b/panda/src/pgraph/transformState.I
@@ -1050,22 +1050,6 @@ consider_update_pstats(int old_referenced_bits) const {
#endif // DO_PSTATS
}
-/**
- *
- */
-INLINE TransformState::Composition::
-Composition() {
-}
-
-/**
- *
- */
-INLINE TransformState::Composition::
-Composition(const TransformState::Composition ©) :
- _result(copy._result)
-{
-}
-
/**
*
*/
diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h
index 288d26b315..80bdcf5adf 100644
--- a/panda/src/pgraph/transformState.h
+++ b/panda/src/pgraph/transformState.h
@@ -282,9 +282,6 @@ private:
// object destructs.
class Composition {
public:
- INLINE Composition();
- INLINE Composition(const Composition ©);
-
// _result is reference counted if and only if it is not the same pointer
// as this.
const TransformState *_result;
diff --git a/panda/src/pgraphnodes/fadeLodNode.cxx b/panda/src/pgraphnodes/fadeLodNode.cxx
index 265594279b..3223dcc0f8 100644
--- a/panda/src/pgraphnodes/fadeLodNode.cxx
+++ b/panda/src/pgraphnodes/fadeLodNode.cxx
@@ -168,13 +168,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
if (elapsed < half_fade_time) {
// FIRST HALF OF FADE Fade the new LOD in with z writing off Keep
// drawing the old LOD opaque with z writing on
- if (out_child >= 0 && out_child < children.get_num_children()) {
+ if (out_child >= 0 && (size_t)out_child < children.get_num_children()) {
const PandaNode::DownConnection &child = children.get_child_connection(out_child);
trav->traverse_down(data, child,
data._state->compose(get_fade_1_old_state()));
}
- if (in_child >= 0 && in_child < children.get_num_children()) {
+ if (in_child >= 0 && (size_t)in_child < children.get_num_children()) {
const PandaNode::DownConnection &child = children.get_child_connection(in_child);
PN_stdfloat in_alpha = elapsed / half_fade_time;
trav->traverse_down(data, child,
@@ -184,13 +184,13 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
} else {
// SECOND HALF OF FADE: Fade out the old LOD with z write off and
// draw the opaque new LOD with z write on
- if (in_child >= 0 && in_child < children.get_num_children()) {
+ if (in_child >= 0 && (size_t)in_child < children.get_num_children()) {
const PandaNode::DownConnection &child = children.get_child_connection(in_child);
trav->traverse_down(data, child,
data._state->compose(get_fade_2_new_state()));
}
- if (out_child >= 0 && out_child < children.get_num_children()) {
+ if (out_child >= 0 && (size_t)out_child < children.get_num_children()) {
const PandaNode::DownConnection &child = children.get_child_connection(out_child);
PN_stdfloat out_alpha = 1.0f - (elapsed - half_fade_time) / half_fade_time;
trav->traverse_down(data, child,
@@ -206,7 +206,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
// just drawing one child of the LOD.
int index = ldata->_fade_in;
Children children = 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);
trav->traverse_down(data, child, data._state);
}
diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx
index aaa290e08b..0aabcb07b4 100644
--- a/panda/src/pgraphnodes/lightLensNode.cxx
+++ b/panda/src/pgraphnodes/lightLensNode.cxx
@@ -30,8 +30,8 @@ LightLensNode::
LightLensNode(const std::string &name, Lens *lens) :
Camera(name, lens),
_has_specular_color(false),
- _attrib_count(0),
- _used_by_auto_shader(false)
+ _used_by_auto_shader(false),
+ _attrib_count(0)
{
set_active(false);
_shadow_caster = false;
@@ -63,12 +63,12 @@ LightLensNode::
LightLensNode(const LightLensNode ©) :
Light(copy),
Camera(copy),
- _shadow_caster(copy._shadow_caster),
_sb_size(copy._sb_size),
- _sb_sort(-10),
+ _shadow_caster(copy._shadow_caster),
_has_specular_color(copy._has_specular_color),
- _attrib_count(0),
- _used_by_auto_shader(false)
+ _sb_sort(-10),
+ _used_by_auto_shader(false),
+ _attrib_count(0)
{
if (_shadow_caster) {
setup_shadow_map();
diff --git a/panda/src/pgraphnodes/lodNode.cxx b/panda/src/pgraphnodes/lodNode.cxx
index a5574dcaa8..fe595f5d3e 100644
--- a/panda/src/pgraphnodes/lodNode.cxx
+++ b/panda/src/pgraphnodes/lodNode.cxx
@@ -430,7 +430,7 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
// This switch level is in range. Draw its children in the funny
// wireframe mode.
Children children = get_children();
- if (index < children.get_num_children()) {
+ if ((size_t)index < children.get_num_children()) {
const PandaNode::DownConnection &child = children.get_child_connection(index);
trav->traverse_down(data, child, data._state->compose(sw.get_viz_model_state()));
}
diff --git a/panda/src/pgui/pgEntry.I b/panda/src/pgui/pgEntry.I
index e398b42b54..e758a7ff8c 100644
--- a/panda/src/pgui/pgEntry.I
+++ b/panda/src/pgui/pgEntry.I
@@ -558,7 +558,7 @@ set_wtext(const std::wstring &wtext) {
update_text();
}
#endif
- set_cursor_position(std::min(_cursor_position, _text.get_num_characters()));
+ set_cursor_position((std::min)(_cursor_position, _text.get_num_characters()));
return ret;
}
diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx
index 8229f434dc..7d2e884f82 100644
--- a/panda/src/pgui/pgItem.cxx
+++ b/panda/src/pgui/pgItem.cxx
@@ -62,9 +62,9 @@ PGItem(const string &name) :
_notify(nullptr),
_has_frame(false),
_frame(0, 0, 0, 0),
- _region(new PGMouseWatcherRegion(this)),
_state(0),
- _flags(0)
+ _flags(0),
+ _region(new PGMouseWatcherRegion(this))
{
set_cull_callback();
set_renderable();
@@ -195,6 +195,8 @@ draw_mask_changed() {
*/
bool PGItem::
cull_callback(CullTraverser *trav, CullTraverserData &data) {
+ CullTraverser::_pgui_nodes_pcollector.add_level(1);
+
// We try not to hold the lock for longer than necessary.
PT(PandaNode) state_def_root;
bool has_frame;
diff --git a/panda/src/pgui/pgScrollFrame.h b/panda/src/pgui/pgScrollFrame.h
index bb8b3a3af3..5a7c19386d 100644
--- a/panda/src/pgui/pgScrollFrame.h
+++ b/panda/src/pgui/pgScrollFrame.h
@@ -19,10 +19,7 @@
#include "pgVirtualFrame.h"
#include "pgSliderBarNotify.h"
#include "pgSliderBar.h"
-
-#ifdef PHAVE_ATOMIC
-#include
-#endif
+#include "patomic.h"
/**
* This is a special kind of frame that pretends to be much larger than it
@@ -96,7 +93,7 @@ private:
private:
bool _needs_remanage;
bool _needs_recompute_clip;
- std::atomic_flag _canvas_computed;
+ patomic_flag _canvas_computed;
bool _has_virtual_frame;
LVecBase4 _virtual_frame;
diff --git a/panda/src/pgui/pgSliderBar.I b/panda/src/pgui/pgSliderBar.I
index da5b97c153..8b1ae04685 100644
--- a/panda/src/pgui/pgSliderBar.I
+++ b/panda/src/pgui/pgSliderBar.I
@@ -381,7 +381,7 @@ get_adjust_event() const {
*/
INLINE void PGSliderBar::
internal_set_ratio(PN_stdfloat ratio) {
- _ratio = std::max(std::min(ratio, (PN_stdfloat)1.0), (PN_stdfloat)0.0);
+ _ratio = (std::max)((std::min)(ratio, (PN_stdfloat)1.0), (PN_stdfloat)0.0);
_needs_reposition = true;
adjust();
}
diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx
index d6103101e6..dee169bfe5 100644
--- a/panda/src/physics/physicalNode.cxx
+++ b/panda/src/physics/physicalNode.cxx
@@ -13,13 +13,10 @@
#include "physicalNode.h"
#include "physicsManager.h"
-
-#ifdef PHAVE_ATOMIC
-#include
-#endif
+#include "patomic.h"
// static stuff.
-static std::atomic_flag warned_copy_physical_node = ATOMIC_FLAG_INIT;
+static patomic_flag warned_copy_physical_node = ATOMIC_FLAG_INIT;
TypeHandle PhysicalNode::_type_handle;
diff --git a/panda/src/pipeline/conditionVarDummyImpl.I b/panda/src/pipeline/conditionVarDummyImpl.I
index ddb356bfe6..2ea7c39588 100644
--- a/panda/src/pipeline/conditionVarDummyImpl.I
+++ b/panda/src/pipeline/conditionVarDummyImpl.I
@@ -25,22 +25,6 @@ INLINE ConditionVarDummyImpl::
~ConditionVarDummyImpl() {
}
-/**
- *
- */
-INLINE void ConditionVarDummyImpl::
-wait() {
- Thread::force_yield();
-}
-
-/**
- *
- */
-INLINE void ConditionVarDummyImpl::
-wait(double) {
- Thread::force_yield();
-}
-
/**
*
*/
diff --git a/panda/src/pipeline/conditionVarDummyImpl.cxx b/panda/src/pipeline/conditionVarDummyImpl.cxx
index 36dd47e659..815501d96f 100644
--- a/panda/src/pipeline/conditionVarDummyImpl.cxx
+++ b/panda/src/pipeline/conditionVarDummyImpl.cxx
@@ -13,3 +13,20 @@
#include "selectThreadImpl.h"
#include "conditionVarDummyImpl.h"
+#include "thread.h"
+
+/**
+ *
+ */
+void ConditionVarDummyImpl::
+wait() {
+ Thread::force_yield();
+}
+
+/**
+ *
+ */
+void ConditionVarDummyImpl::
+wait(double) {
+ Thread::force_yield();
+}
diff --git a/panda/src/pipeline/conditionVarDummyImpl.h b/panda/src/pipeline/conditionVarDummyImpl.h
index 150b77f910..c5e2b942c4 100644
--- a/panda/src/pipeline/conditionVarDummyImpl.h
+++ b/panda/src/pipeline/conditionVarDummyImpl.h
@@ -16,7 +16,6 @@
#include "pandabase.h"
#include "selectThreadImpl.h"
-#include "thread.h"
#include "pnotify.h"
@@ -31,8 +30,8 @@ public:
INLINE ConditionVarDummyImpl(MutexDummyImpl &mutex);
INLINE ~ConditionVarDummyImpl();
- INLINE void wait();
- INLINE void wait(double timeout);
+ void wait();
+ void wait(double timeout);
INLINE void notify();
INLINE void notify_all();
};
diff --git a/panda/src/pipeline/conditionVarPosixImpl.I b/panda/src/pipeline/conditionVarPosixImpl.I
index 0b3e533717..2f8e73e5f5 100644
--- a/panda/src/pipeline/conditionVarPosixImpl.I
+++ b/panda/src/pipeline/conditionVarPosixImpl.I
@@ -39,7 +39,11 @@ INLINE ConditionVarPosixImpl::
INLINE void ConditionVarPosixImpl::
wait() {
TAU_PROFILE("ConditionVarPosixImpl::wait()", " ", TAU_USER);
+#ifdef DO_PSTATS
+ int result = _wait_func(&_cvar, &_mutex._lock);
+#else
int result = pthread_cond_wait(&_cvar, &_mutex._lock);
+#endif
#ifndef NDEBUG
if (result != 0) {
pipeline_cat.error()
diff --git a/panda/src/pipeline/conditionVarPosixImpl.cxx b/panda/src/pipeline/conditionVarPosixImpl.cxx
index e2e527c369..0d48b3034b 100644
--- a/panda/src/pipeline/conditionVarPosixImpl.cxx
+++ b/panda/src/pipeline/conditionVarPosixImpl.cxx
@@ -18,6 +18,12 @@
#include "conditionVarPosixImpl.h"
#include
+int (*ConditionVarPosixImpl::_wait_func)(pthread_cond_t *,
+ pthread_mutex_t *) = &pthread_cond_wait;
+int (*ConditionVarPosixImpl::_timedwait_func)(pthread_cond_t *,
+ pthread_mutex_t *,
+ const struct timespec *) = &pthread_cond_timedwait;
+
/**
*
*/
@@ -41,7 +47,11 @@ wait(double timeout) {
++ts.tv_sec;
}
+#ifdef DO_PSTATS
+ int result = _timedwait_func(&_cvar, &_mutex._lock, &ts);
+#else
int result = pthread_cond_timedwait(&_cvar, &_mutex._lock, &ts);
+#endif
#ifndef NDEBUG
if (result != 0 && result != ETIMEDOUT) {
pipeline_cat.error()
diff --git a/panda/src/pipeline/conditionVarPosixImpl.h b/panda/src/pipeline/conditionVarPosixImpl.h
index e39a5a122b..56bc3e9acc 100644
--- a/panda/src/pipeline/conditionVarPosixImpl.h
+++ b/panda/src/pipeline/conditionVarPosixImpl.h
@@ -43,6 +43,11 @@ public:
private:
MutexPosixImpl &_mutex;
pthread_cond_t _cvar;
+
+ static int (*_wait_func)(pthread_cond_t *, pthread_mutex_t *);
+ static int (*_timedwait_func)(pthread_cond_t *, pthread_mutex_t *,
+ const struct timespec *);
+ friend class PStatClientImpl;
};
#include "conditionVarPosixImpl.I"
diff --git a/panda/src/pipeline/conditionVarWin32Impl.I b/panda/src/pipeline/conditionVarWin32Impl.I
index 275acff593..56fb036266 100644
--- a/panda/src/pipeline/conditionVarWin32Impl.I
+++ b/panda/src/pipeline/conditionVarWin32Impl.I
@@ -23,7 +23,11 @@ ConditionVarWin32Impl(MutexWin32Impl &mutex) : _mutex(mutex) {
*/
INLINE void ConditionVarWin32Impl::
wait() {
+#ifdef DO_PSTATS
+ _wait_func(&_cvar, &_mutex._lock, INFINITE, 0);
+#else
SleepConditionVariableSRW(&_cvar, &_mutex._lock, INFINITE, 0);
+#endif
}
/**
@@ -31,7 +35,11 @@ wait() {
*/
INLINE void ConditionVarWin32Impl::
wait(double timeout) {
+#ifdef DO_PSTATS
+ _wait_func(&_cvar, &_mutex._lock, (DWORD)(timeout * 1000.0), 0);
+#else
SleepConditionVariableSRW(&_cvar, &_mutex._lock, (DWORD)(timeout * 1000.0), 0);
+#endif
}
/**
diff --git a/panda/src/pipeline/conditionVarWin32Impl.cxx b/panda/src/pipeline/conditionVarWin32Impl.cxx
index 5ba4165a0c..7c1b05d6e0 100644
--- a/panda/src/pipeline/conditionVarWin32Impl.cxx
+++ b/panda/src/pipeline/conditionVarWin32Impl.cxx
@@ -17,4 +17,7 @@
#include "conditionVarWin32Impl.h"
+// This function gets replaced by PStats to measure the time spent waiting.
+BOOL (__stdcall *ConditionVarWin32Impl::_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG) = &SleepConditionVariableSRW;
+
#endif // _WIN32
diff --git a/panda/src/pipeline/conditionVarWin32Impl.h b/panda/src/pipeline/conditionVarWin32Impl.h
index 5c8d4a91d3..5ec0ab0c08 100644
--- a/panda/src/pipeline/conditionVarWin32Impl.h
+++ b/panda/src/pipeline/conditionVarWin32Impl.h
@@ -40,6 +40,9 @@ public:
private:
MutexWin32Impl &_mutex;
CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT;
+
+ static BOOL (__stdcall *_wait_func)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
+ friend class PStatClientImpl;
};
#include "conditionVarWin32Impl.I"
diff --git a/panda/src/pipeline/pipeline.I b/panda/src/pipeline/pipeline.I
index 2116a030e6..b6c9366e1a 100644
--- a/panda/src/pipeline/pipeline.I
+++ b/panda/src/pipeline/pipeline.I
@@ -27,7 +27,7 @@ get_render_pipeline() {
*/
INLINE void Pipeline::
set_min_stages(int min_stages) {
- set_num_stages(std::max(min_stages, get_num_stages()));
+ set_num_stages((std::max)(min_stages, get_num_stages()));
}
/**
diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx
index 641b2c6e2c..30c1c33b5e 100644
--- a/panda/src/pipeline/pipeline.cxx
+++ b/panda/src/pipeline/pipeline.cxx
@@ -26,9 +26,9 @@ Pipeline(const std::string &name, int num_stages) :
Namable(name),
#ifdef THREADED_PIPELINE
_num_stages(num_stages),
+ _next_cycle_seq(1),
_cycle_lock("Pipeline cycle"),
- _lock("Pipeline"),
- _next_cycle_seq(1)
+ _lock("Pipeline")
#else
_num_stages(1)
#endif
diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I
index dc7001fd06..2b07af370a 100644
--- a/panda/src/pipeline/thread.I
+++ b/panda/src/pipeline/thread.I
@@ -75,7 +75,7 @@ get_pipeline_stage() const {
*/
INLINE void Thread::
set_min_pipeline_stage(int min_pipeline_stage) {
- set_pipeline_stage(std::max(_pipeline_stage, min_pipeline_stage));
+ set_pipeline_stage((std::max)(_pipeline_stage, min_pipeline_stage));
}
/**
@@ -191,7 +191,11 @@ is_simple_threads() {
INLINE void Thread::
sleep(double seconds) {
TAU_PROFILE("void Thread::sleep(double)", " ", TAU_USER);
+#ifdef DO_PSTATS
+ _sleep_func(seconds);
+#else
ThreadImpl::sleep(seconds);
+#endif
}
/**
@@ -200,7 +204,11 @@ sleep(double seconds) {
INLINE void Thread::
force_yield() {
TAU_PROFILE("void Thread::yield()", " ", TAU_USER);
+#ifdef DO_PSTATS
+ _yield_func();
+#else
ThreadImpl::yield();
+#endif
}
/**
@@ -214,6 +222,32 @@ consider_yield() {
ThreadImpl::consider_yield();
}
+/**
+ * Equivalent to the pause instruction on x86 or the yield instruction on ARM,
+ * to be called in spin loops.
+ */
+INLINE void Thread::
+relax() {
+#ifdef _MSC_VER
+ YieldProcessor();
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64))
+ __asm__ __volatile__("pause");
+#elif defined(__arm__) || defined(__aarch64__)
+ __asm__ __volatile__ ("yield" ::: "memory");
+#endif
+}
+
+/**
+ * Returns thread statistics. The first number is the total number of context
+ * switches reported by the OS, and the second number is the number of
+ * involuntary context switches (ie. the thread was scheduled out by the OS),
+ * if known.
+ */
+INLINE bool Thread::
+get_context_switches(size_t &total, size_t &involuntary) {
+ return ThreadImpl::get_context_switches(total, involuntary);
+}
+
/**
* Returns true if the thread has been started, false if it has not, or if
* join() has already been called.
diff --git a/panda/src/pipeline/thread.cxx b/panda/src/pipeline/thread.cxx
index 81152a8095..5f2cafda3a 100644
--- a/panda/src/pipeline/thread.cxx
+++ b/panda/src/pipeline/thread.cxx
@@ -22,6 +22,9 @@ Thread *Thread::_main_thread;
Thread *Thread::_external_thread;
TypeHandle Thread::_type_handle;
+void (*Thread::_sleep_func)(double) = &ThreadImpl::sleep;
+void (*Thread::_yield_func)() = &ThreadImpl::yield;
+
/**
* Creates a new Thread object, but does not immediately start executing it.
* This gives the caller a chance to store it in a PT(Thread) object, if
diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h
index 159b93c9df..ec7ada4971 100644
--- a/panda/src/pipeline/thread.h
+++ b/panda/src/pipeline/thread.h
@@ -42,7 +42,8 @@ class AsyncTask;
* object will automatically be destructed if no other pointers are
* referencing it.
*/
-class EXPCL_PANDA_PIPELINE Thread : public TypedReferenceCount, public Namable {
+// Due to a GCC bug, we can't use alignas() together with an attribute.
+class ALIGN_64BYTE EXPCL_PANDA_PIPELINE Thread : public TypedReferenceCount, public Namable {
protected:
Thread(const std::string &name, const std::string &sync_name);
Thread(const Thread ©) = delete;
@@ -79,6 +80,9 @@ PUBLISHED:
BLOCKING INLINE static void force_yield();
BLOCKING INLINE static void consider_yield();
+ BLOCKING INLINE static void relax();
+
+ INLINE static bool get_context_switches(size_t &total, size_t &involuntary);
virtual void output(std::ostream &out) const;
void output_blocker(std::ostream &out) const;
@@ -161,6 +165,10 @@ private:
static Thread *_main_thread;
static Thread *_external_thread;
+ static void (*_sleep_func)(double);
+ static void (*_yield_func)();
+ friend class PStatClientImpl;
+
public:
static TypeHandle get_class_type() {
return _type_handle;
diff --git a/panda/src/pipeline/threadDummyImpl.I b/panda/src/pipeline/threadDummyImpl.I
index 7bab1ae408..85ab2263a4 100644
--- a/panda/src/pipeline/threadDummyImpl.I
+++ b/panda/src/pipeline/threadDummyImpl.I
@@ -124,3 +124,21 @@ yield() {
INLINE void ThreadDummyImpl::
consider_yield() {
}
+
+/**
+ *
+ */
+INLINE bool ThreadDummyImpl::
+get_context_switches(size_t &, size_t &) {
+ return false;
+}
+
+#ifdef ANDROID
+/**
+ * Returns the JNIEnv object for the current thread.
+ */
+INLINE JNIEnv *ThreadDummyImpl::
+get_jni_env() const {
+ return _jni_env;
+}
+#endif
diff --git a/panda/src/pipeline/threadDummyImpl.cxx b/panda/src/pipeline/threadDummyImpl.cxx
index e5d0d0642c..1e45434302 100644
--- a/panda/src/pipeline/threadDummyImpl.cxx
+++ b/panda/src/pipeline/threadDummyImpl.cxx
@@ -25,6 +25,13 @@
#include
#endif
+#ifdef ANDROID
+#include "config_express.h"
+#include
+
+static JavaVM *java_vm = nullptr;
+#endif
+
/**
*
*/
@@ -48,4 +55,36 @@ get_current_thread() {
return Thread::get_main_thread();
}
+#ifdef ANDROID
+/**
+ * Attaches the thread to the Java virtual machine. If this returns true, a
+ * JNIEnv pointer can be acquired using get_jni_env().
+ */
+bool ThreadDummyImpl::
+attach_java_vm() {
+ assert(java_vm != nullptr);
+ JNIEnv *env;
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_2;
+ args.name = "Main";
+ args.group = nullptr;
+ if (java_vm->AttachCurrentThread(&env, &args) != 0) {
+ thread_cat.error()
+ << "Failed to attach Java VM to thread ";
+ _jni_env = nullptr;
+ return false;
+ }
+ _jni_env = env;
+ return true;
+}
+
+/**
+ * Binds the Panda thread to the current thread, assuming that the current
+ * thread is already a valid attached Java thread. Called by JNI_OnLoad.
+ */
+void ThreadDummyImpl::
+bind_java_thread() {
+}
+#endif // ANDROID
+
#endif // THREAD_DUMMY_IMPL
diff --git a/panda/src/pipeline/threadDummyImpl.h b/panda/src/pipeline/threadDummyImpl.h
index c7cea7a3ca..3e1b7a8ab7 100644
--- a/panda/src/pipeline/threadDummyImpl.h
+++ b/panda/src/pipeline/threadDummyImpl.h
@@ -31,6 +31,11 @@ class Thread;
#include // For Sleep().
#endif
+#ifdef ANDROID
+#include
+typedef struct _JNIEnv _jni_env;
+#endif
+
/**
* A fake thread implementation for single-threaded applications. This simply
* fails whenever you try to start a thread.
@@ -57,6 +62,19 @@ public:
INLINE static void sleep(double seconds);
INLINE static void yield();
INLINE static void consider_yield();
+
+#ifdef ANDROID
+ INLINE JNIEnv *get_jni_env() const;
+ bool attach_java_vm();
+ static void bind_java_thread();
+#endif
+
+ INLINE static bool get_context_switches(size_t &, size_t &);
+
+private:
+#ifdef ANDROID
+ JNIEnv *_jni_env = nullptr;
+#endif
};
#include "threadDummyImpl.I"
diff --git a/panda/src/pipeline/threadPosixImpl.I b/panda/src/pipeline/threadPosixImpl.I
index 22a8c90118..308dc3ede0 100644
--- a/panda/src/pipeline/threadPosixImpl.I
+++ b/panda/src/pipeline/threadPosixImpl.I
@@ -46,26 +46,8 @@ prepare_for_exit() {
INLINE Thread *ThreadPosixImpl::
get_current_thread() {
TAU_PROFILE("Thread *ThreadPosixImpl::get_current_thread()", " ", TAU_USER);
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
- return (Thread *)pthread_getspecific(_pt_ptr_index);
-}
-
-/**
- * Associates the indicated Thread object with the currently-executing thread.
- * You should not call this directly; use Thread::bind_thread() instead.
- */
-INLINE void ThreadPosixImpl::
-bind_thread(Thread *thread) {
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
- int result = pthread_setspecific(_pt_ptr_index, thread);
- nassertv(result == 0);
-#ifdef ANDROID
- bind_java_thread();
-#endif
+ Thread *thread = _current_thread;
+ return (thread != nullptr) ? thread : init_current_thread();
}
/**
diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx
index f791d2be43..371c7464ad 100644
--- a/panda/src/pipeline/threadPosixImpl.cxx
+++ b/panda/src/pipeline/threadPosixImpl.cxx
@@ -21,6 +21,11 @@
#include "config_pipeline.h"
#include
+// Used for getrusage().
+#include
+#include
+#include
+
#ifdef ANDROID
#include "config_express.h"
#include
@@ -28,8 +33,8 @@
static JavaVM *java_vm = nullptr;
#endif
-pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
-bool ThreadPosixImpl::_got_pt_ptr_index = false;
+__thread Thread *ThreadPosixImpl::_current_thread = nullptr;
+static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT;
/**
*
@@ -80,10 +85,6 @@ start(ThreadPriority priority, bool joinable) {
_status = S_start_called;
_detached = false;
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
-
pthread_attr_t attr;
pthread_attr_init(&attr);
@@ -186,6 +187,21 @@ get_unique_id() const {
return strm.str();
}
+/**
+ * Associates the indicated Thread object with the currently-executing thread.
+ * You should not call this directly; use Thread::bind_thread() instead.
+ */
+void ThreadPosixImpl::
+bind_thread(Thread *thread) {
+ if (_current_thread == nullptr && thread == Thread::get_main_thread()) {
+ _main_thread_known.test_and_set(std::memory_order_relaxed);
+ }
+ _current_thread = thread;
+#ifdef ANDROID
+ bind_java_thread();
+#endif
+}
+
#ifdef ANDROID
/**
* Attaches the thread to the Java virtual machine. If this returns true, a
@@ -237,6 +253,26 @@ bind_java_thread() {
}
#endif // ANDROID
+/**
+ * Returns the number of context switches that occurred on the current thread.
+ * The first number is the total number of context switches reported by the OS,
+ * and the second number is the number of involuntary context switches (ie. the
+ * thread was scheduled out by the OS), if known, otherwise zero.
+ * Returns true if context switch information was available, false otherwise.
+ */
+bool ThreadPosixImpl::
+get_context_switches(size_t &total, size_t &involuntary) {
+#ifdef RUSAGE_THREAD
+ struct rusage usage;
+ if (getrusage(RUSAGE_THREAD, &usage) == 0) {
+ total = (size_t)usage.ru_nvcsw;
+ involuntary = (size_t)usage.ru_nivcsw;
+ return true;
+ }
+#endif
+ return false;
+}
+
/**
* The entry point of each thread.
*/
@@ -247,8 +283,7 @@ root_func(void *data) {
// TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
ThreadPosixImpl *self = (ThreadPosixImpl *)data;
- int result = pthread_setspecific(_pt_ptr_index, self->_parent_obj);
- nassertr(result == 0, nullptr);
+ _current_thread = self->_parent_obj;
{
self->_mutex.lock();
@@ -302,27 +337,17 @@ root_func(void *data) {
}
/**
- * Allocate a new index to store the Thread parent pointer as a piece of per-
- * thread private data.
+ * Called by get_current_thread() if the current thread pointer is null; checks
+ * whether it might be the main thread.
*/
-void ThreadPosixImpl::
-init_pt_ptr_index() {
- nassertv(!_got_pt_ptr_index);
-
- int result = pthread_key_create(&_pt_ptr_index, nullptr);
- if (result != 0) {
- thread_cat->error()
- << "Unable to associate Thread pointers with threads.\n";
- return;
+Thread *ThreadPosixImpl::
+init_current_thread() {
+ Thread *thread = _current_thread;
+ if (!_main_thread_known.test_and_set(std::memory_order_relaxed)) {
+ thread = Thread::get_main_thread();
+ _current_thread = thread;
}
-
- _got_pt_ptr_index = true;
-
- // Assume that we must be in the main thread, since this method must be
- // called before the first thread is spawned.
- Thread *main_thread_obj = Thread::get_main_thread();
- result = pthread_setspecific(_pt_ptr_index, main_thread_obj);
- nassertv(result == 0);
+ return thread;
}
#ifdef ANDROID
diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h
index cdfd5eab8a..65d7f951ca 100644
--- a/panda/src/pipeline/threadPosixImpl.h
+++ b/panda/src/pipeline/threadPosixImpl.h
@@ -49,7 +49,7 @@ public:
INLINE static void prepare_for_exit();
INLINE static Thread *get_current_thread();
- INLINE static void bind_thread(Thread *thread);
+ static void bind_thread(Thread *thread);
INLINE static bool is_threading_supported();
INLINE static bool is_true_threads();
INLINE static bool is_simple_threads();
@@ -63,9 +63,11 @@ public:
static void bind_java_thread();
#endif
+ static bool get_context_switches(size_t &total, size_t &involuntary);
+
private:
static void *root_func(void *data);
- static void init_pt_ptr_index();
+ static Thread *init_current_thread();
// There appears to be a name collision with the word "Status".
enum PStatus {
@@ -86,8 +88,7 @@ private:
JNIEnv *_jni_env;
#endif
- static pthread_key_t _pt_ptr_index;
- static bool _got_pt_ptr_index;
+ static __thread Thread *_current_thread;
};
#include "threadPosixImpl.I"
diff --git a/panda/src/pipeline/threadSimpleImpl.cxx b/panda/src/pipeline/threadSimpleImpl.cxx
index 14d6f7561f..aeefd34d7e 100644
--- a/panda/src/pipeline/threadSimpleImpl.cxx
+++ b/panda/src/pipeline/threadSimpleImpl.cxx
@@ -55,6 +55,9 @@ ThreadSimpleImpl(Thread *parent_obj) :
#ifdef WIN32
_win32_system_thread_id = 0;
#endif
+
+ _context_switches = 0;
+ _involuntary_context_switches = 0;
}
/**
@@ -229,6 +232,32 @@ yield_this(bool volunteer) {
}
_manager->enqueue_ready(this, true);
_manager->next_context();
+ if (!volunteer) {
+ // Technically all context switches are voluntary here, but to make this
+ // distinction meaningful, we count it as involuntary on consider_yield().
+ ++_involuntary_context_switches;
+ }
+}
+
+/**
+ * Returns the number of context switches that occurred on the current thread.
+ * The first number is the total number of context switches reported by the OS,
+ * and the second number is the number of involuntary context switches (ie. the
+ * thread was scheduled out by the OS), if known, otherwise zero.
+ * Returns true if context switch information was available, false otherwise.
+ */
+bool ThreadSimpleImpl::
+get_context_switches(size_t &total, size_t &involuntary) {
+ ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
+ if (manager->is_same_system_thread()) {
+ ThreadSimpleImpl *thread = manager->get_current_thread();
+ if (thread != nullptr) {
+ total = thread->_context_switches;
+ involuntary = thread->_involuntary_context_switches;
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/panda/src/pipeline/threadSimpleImpl.h b/panda/src/pipeline/threadSimpleImpl.h
index 5bd6af3fdc..5e747ea917 100644
--- a/panda/src/pipeline/threadSimpleImpl.h
+++ b/panda/src/pipeline/threadSimpleImpl.h
@@ -77,6 +77,8 @@ public:
INLINE static void write_status(std::ostream &out);
+ static bool get_context_switches(size_t &total, size_t &involuntary);
+
private:
static void st_begin_thread(void *data);
void begin_thread();
@@ -142,6 +144,9 @@ private:
DWORD _win32_system_thread_id;
#endif
+ size_t _context_switches;
+ size_t _involuntary_context_switches;
+
friend class ThreadSimpleManager;
};
diff --git a/panda/src/pipeline/threadSimpleManager.cxx b/panda/src/pipeline/threadSimpleManager.cxx
index 861e19cd77..d28a4372f6 100644
--- a/panda/src/pipeline/threadSimpleManager.cxx
+++ b/panda/src/pipeline/threadSimpleManager.cxx
@@ -233,6 +233,8 @@ next_context() {
unref_delete(finished_thread->_parent_obj);
}
+ ++_current_thread->_context_switches;
+
// Mark the current thread's resume point.
#ifdef HAVE_PYTHON
diff --git a/panda/src/pipeline/threadWin32Impl.I b/panda/src/pipeline/threadWin32Impl.I
index a4c2b84a21..ca236b78f0 100644
--- a/panda/src/pipeline/threadWin32Impl.I
+++ b/panda/src/pipeline/threadWin32Impl.I
@@ -17,7 +17,8 @@
INLINE ThreadWin32Impl::
ThreadWin32Impl(Thread *parent_obj) :
_cv(_mutex),
- _parent_obj(parent_obj)
+ _parent_obj(parent_obj),
+ _profiling(0)
{
_thread = 0;
_joinable = false;
@@ -38,30 +39,6 @@ INLINE void ThreadWin32Impl::
prepare_for_exit() {
}
-/**
- *
- */
-INLINE Thread *ThreadWin32Impl::
-get_current_thread() {
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
- return (Thread *)TlsGetValue(_pt_ptr_index);
-}
-
-/**
- * Associates the indicated Thread object with the currently-executing thread.
- * You should not call this directly; use Thread::bind_thread() instead.
- */
-INLINE void ThreadWin32Impl::
-bind_thread(Thread *thread) {
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
- BOOL result = TlsSetValue(_pt_ptr_index, thread);
- nassertv(result);
-}
-
/**
*
*/
@@ -99,7 +76,7 @@ sleep(double seconds) {
*/
INLINE void ThreadWin32Impl::
yield() {
- Sleep(1);
+ Sleep(0);
}
/**
diff --git a/panda/src/pipeline/threadWin32Impl.cxx b/panda/src/pipeline/threadWin32Impl.cxx
index 4671787918..22bf5cab79 100644
--- a/panda/src/pipeline/threadWin32Impl.cxx
+++ b/panda/src/pipeline/threadWin32Impl.cxx
@@ -20,8 +20,53 @@
#include "pointerTo.h"
#include "config_pipeline.h"
-DWORD ThreadWin32Impl::_pt_ptr_index = 0;
-bool ThreadWin32Impl::_got_pt_ptr_index = false;
+#include
+
+static thread_local Thread *_current_thread = nullptr;
+static patomic_flag _main_thread_known = ATOMIC_FLAG_INIT;
+
+#if _WIN32_WINNT < 0x0601
+// Requires Windows 7.
+static DWORD (__stdcall *EnableThreadProfiling)(HANDLE, DWORD, DWORD64, HANDLE *) = nullptr;
+static DWORD (__stdcall *DisableThreadProfiling)(HANDLE) = nullptr;
+static DWORD (__stdcall *ReadThreadProfilingData)(HANDLE, DWORD, PPERFORMANCE_DATA data) = nullptr;
+
+static bool init_thread_profiling() {
+ static bool inited = false;
+ if (!inited) {
+ HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
+ EnableThreadProfiling = (decltype(EnableThreadProfiling))GetProcAddress(kernel32, "EnableThreadProfiling");
+ DisableThreadProfiling = (decltype(DisableThreadProfiling))GetProcAddress(kernel32, "DisableThreadProfiling");
+ ReadThreadProfilingData = (decltype(ReadThreadProfilingData))GetProcAddress(kernel32, "ReadThreadProfilingData");
+ inited = true;
+ }
+ return (EnableThreadProfiling && DisableThreadProfiling && ReadThreadProfilingData);
+}
+#else
+static bool init_thread_profiling() {
+ return true;
+}
+#endif
+
+/**
+ * Called by get_current_thread() if the current thread pointer is null; checks
+ * whether it might be the main thread.
+ * Note that adding noinline speeds up this call *significantly*, don't remove!
+ */
+static __declspec(noinline) Thread *
+init_current_thread() {
+ Thread *thread = _current_thread;
+ if (!_main_thread_known.test_and_set(std::memory_order_relaxed)) {
+ // Assume that we must be in the main thread, since this method must be
+ // called before the first thread is spawned.
+ thread = Thread::get_main_thread();
+ _current_thread = thread;
+ }
+ // If this assertion triggers, you are making Panda calls from a thread
+ // that has not first been registered using Thread::bind_thread().
+ nassertr(thread != nullptr, nullptr);
+ return thread;
+}
/**
*
@@ -62,10 +107,6 @@ start(ThreadPriority priority, bool joinable) {
_joinable = joinable;
_status = S_start_called;
- if (!_got_pt_ptr_index) {
- init_pt_ptr_index();
- }
-
// Increment the parent object's reference count first. The thread will
// eventually decrement it when it terminates.
_parent_obj->ref();
@@ -133,6 +174,56 @@ get_unique_id() const {
return strm.str();
}
+/**
+ *
+ */
+Thread *ThreadWin32Impl::
+get_current_thread() {
+ Thread *thread = _current_thread;
+ return (thread != nullptr) ? thread : init_current_thread();
+}
+
+/**
+ * Associates the indicated Thread object with the currently-executing thread.
+ * You should not call this directly; use Thread::bind_thread() instead.
+ */
+void ThreadWin32Impl::
+bind_thread(Thread *thread) {
+ if (_current_thread == nullptr && thread == Thread::get_main_thread()) {
+ _main_thread_known.test_and_set(std::memory_order_relaxed);
+ }
+ _current_thread = thread;
+}
+
+/**
+ * Returns the number of context switches that occurred on the current thread.
+ * The first number is the total number of context switches reported by the OS,
+ * and the second number is the number of involuntary context switches (ie. the
+ * thread was scheduled out by the OS), if known, otherwise zero.
+ * Returns true if context switch information was available, false otherwise.
+ */
+bool ThreadWin32Impl::
+get_context_switches(size_t &total, size_t &involuntary) {
+ Thread *thread = get_current_thread();
+ ThreadWin32Impl *self = &thread->_impl;
+
+ if (!self->_profiling && init_thread_profiling()) {
+ DWORD result = EnableThreadProfiling(GetCurrentThread(), THREAD_PROFILING_FLAG_DISPATCH, 0, &self->_profiling);
+ if (result != ERROR_SUCCESS) {
+ self->_profiling = 0;
+ return false;
+ }
+ }
+
+ PERFORMANCE_DATA data = {sizeof(PERFORMANCE_DATA), PERFORMANCE_DATA_VERSION};
+ if (ReadThreadProfilingData(self->_profiling, READ_THREAD_PROFILING_FLAG_DISPATCHING, &data) == ERROR_SUCCESS) {
+ total = data.ContextSwitchCount;
+ involuntary = 0;
+ return true;
+ }
+ return false;
+}
+
/**
* The entry point of each thread.
*/
@@ -143,8 +234,7 @@ root_func(LPVOID data) {
// TAU_PROFILE("void ThreadWin32Impl::root_func()", " ", TAU_USER);
ThreadWin32Impl *self = (ThreadWin32Impl *)data;
- BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj);
- nassertr(result, 1);
+ _current_thread = self->_parent_obj;
{
self->_mutex.lock();
@@ -176,6 +266,11 @@ root_func(LPVOID data) {
self->_mutex.unlock();
}
+ if (self->_profiling != 0) {
+ DisableThreadProfiling(self->_profiling);
+ self->_profiling = 0;
+ }
+
// Now drop the parent object reference that we grabbed in start(). This
// might delete the parent object, and in turn, delete the ThreadWin32Impl
// object.
@@ -185,28 +280,4 @@ root_func(LPVOID data) {
return 0;
}
-/**
- * Allocate a new index to store the Thread parent pointer as a piece of per-
- * thread private data.
- */
-void ThreadWin32Impl::
-init_pt_ptr_index() {
- nassertv(!_got_pt_ptr_index);
-
- _pt_ptr_index = TlsAlloc();
- if (_pt_ptr_index == TLS_OUT_OF_INDEXES) {
- thread_cat->error()
- << "Unable to associate Thread pointers with threads.\n";
- return;
- }
-
- _got_pt_ptr_index = true;
-
- // Assume that we must be in the main thread, since this method must be
- // called before the first thread is spawned.
- Thread *main_thread_obj = Thread::get_main_thread();
- BOOL result = TlsSetValue(_pt_ptr_index, main_thread_obj);
- nassertv(result);
-}
-
#endif // THREAD_WIN32_IMPL
diff --git a/panda/src/pipeline/threadWin32Impl.h b/panda/src/pipeline/threadWin32Impl.h
index 69163230c9..3c9cfd275e 100644
--- a/panda/src/pipeline/threadWin32Impl.h
+++ b/panda/src/pipeline/threadWin32Impl.h
@@ -43,8 +43,8 @@ public:
INLINE static void prepare_for_exit();
- INLINE static Thread *get_current_thread();
- INLINE static void bind_thread(Thread *thread);
+ static Thread *get_current_thread();
+ static void bind_thread(Thread *thread);
INLINE static bool is_threading_supported();
INLINE static bool is_true_threads();
INLINE static bool is_simple_threads();
@@ -52,9 +52,10 @@ public:
INLINE static void yield();
INLINE static void consider_yield();
+ static bool get_context_switches(size_t &total, size_t &involuntary);
+
private:
static DWORD WINAPI root_func(LPVOID data);
- static void init_pt_ptr_index();
enum Status {
S_new,
@@ -70,9 +71,7 @@ private:
DWORD _thread_id;
bool _joinable;
Status _status;
-
- static DWORD _pt_ptr_index;
- static bool _got_pt_ptr_index;
+ HANDLE _profiling;
};
#include "threadWin32Impl.I"
diff --git a/panda/src/pnmimage/convert_srgb.I b/panda/src/pnmimage/convert_srgb.I
index 27d6bbf802..da183149f9 100644
--- a/panda/src/pnmimage/convert_srgb.I
+++ b/panda/src/pnmimage/convert_srgb.I
@@ -44,8 +44,8 @@ INLINE unsigned char decode_sRGB_uchar(unsigned char val) {
*/
INLINE unsigned char decode_sRGB_uchar(float val) {
return (val <= 0.04045f)
- ? (unsigned char)(std::max(0.f, val) * (255.f / 12.92f) + 0.5f)
- : (unsigned char)(cpow((std::min(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f);
+ ? (unsigned char)((std::max)(0.f, val) * (255.f / 12.92f) + 0.5f)
+ : (unsigned char)(cpow(((std::min)(val, 1.f) + 0.055f) * (1.f / 1.055f), 2.4f) * 255.f + 0.5f);
}
/**
@@ -99,8 +99,8 @@ encode_sRGB_uchar(float val) {
return encode_sRGB_uchar_sse2(val);
#else
return (val < 0.0031308f)
- ? (unsigned char) (std::max(0.f, val) * 3294.6f + 0.5f)
- : (unsigned char) (269.025f * cpow(std::min(val, 1.f), 0.41666f) - 13.525f);
+ ? (unsigned char) ((std::max)(0.f, val) * 3294.6f + 0.5f)
+ : (unsigned char) (269.025f * cpow((std::min)(val, 1.f), 0.41666f) - 13.525f);
#endif
}
diff --git a/panda/src/pnmimage/pfmFile.I b/panda/src/pnmimage/pfmFile.I
index 0ba12061ee..a3fe7c3004 100644
--- a/panda/src/pnmimage/pfmFile.I
+++ b/panda/src/pnmimage/pfmFile.I
@@ -587,12 +587,12 @@ setup_sub_image(const PfmFile ©, int &xto, int &yto,
yto = 0;
}
- x_size = std::min(x_size, copy.get_x_size() - xfrom);
- y_size = std::min(y_size, copy.get_y_size() - yfrom);
+ x_size = (std::min)(x_size, copy.get_x_size() - xfrom);
+ y_size = (std::min)(y_size, copy.get_y_size() - yfrom);
xmin = xto;
ymin = yto;
- xmax = std::min(xmin + x_size, get_x_size());
- ymax = std::min(ymin + y_size, get_y_size());
+ xmax = (std::min)(xmin + x_size, get_x_size());
+ ymax = (std::min)(ymin + y_size, get_y_size());
}
diff --git a/panda/src/pnmimage/pnmImage.I b/panda/src/pnmimage/pnmImage.I
index 07862ab40a..9b396df1ad 100644
--- a/panda/src/pnmimage/pnmImage.I
+++ b/panda/src/pnmimage/pnmImage.I
@@ -68,7 +68,7 @@ INLINE PNMImage::
*/
INLINE xelval PNMImage::
clamp_val(int input_value) const {
- return (xelval)std::min(std::max(0, input_value), (int)get_maxval());
+ return (xelval)(std::min)((std::max)(0, input_value), (int)get_maxval());
}
/**
@@ -113,9 +113,9 @@ to_val(const LRGBColorf &value) const {
case XE_scRGB_alpha:
{
LRGBColorf scaled = value * 8192.f + 4096.5f;
- col.r = std::min(std::max(0, (int)scaled[0]), 65535);
- col.g = std::min(std::max(0, (int)scaled[1]), 65535);
- col.b = std::min(std::max(0, (int)scaled[2]), 65535);
+ col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535);
+ col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535);
+ col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535);
}
break;
}
@@ -131,7 +131,7 @@ to_val(float input_value) const {
switch (_xel_encoding) {
case XE_generic:
case XE_generic_alpha:
- return (int)(std::min(1.0f, std::max(0.0f, input_value)) * get_maxval() + 0.5f);
+ return (int)((std::min)(1.0f, (std::max)(0.0f, input_value)) * get_maxval() + 0.5f);
case XE_generic_sRGB:
case XE_generic_sRGB_alpha:
@@ -148,7 +148,7 @@ to_val(float input_value) const {
case XE_scRGB:
case XE_scRGB_alpha:
- return std::min(std::max(0, (int)((8192 * input_value) + 4096.5f)), 65535);
+ return (std::min)((std::max)(0, (int)((8192 * input_value) + 4096.5f)), 65535);
default:
return 0;
@@ -210,7 +210,7 @@ from_val(xelval input_value) const {
switch (_xel_encoding) {
case XE_generic:
case XE_generic_alpha:
- return std::min((float)input_value * _inv_maxval, 1.0f);
+ return (std::min)((float)input_value * _inv_maxval, 1.0f);
case XE_generic_sRGB:
case XE_generic_sRGB_alpha:
@@ -735,19 +735,19 @@ set_xel_a(int x, int y, const LColorf &value) {
case XE_scRGB:
{
LColorf scaled = value * 8192.0f + 4096.5f;
- col.r = std::min(std::max(0, (int)scaled[0]), 65535);
- col.g = std::min(std::max(0, (int)scaled[1]), 65535);
- col.b = std::min(std::max(0, (int)scaled[2]), 65535);
+ col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535);
+ col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535);
+ col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535);
}
break;
case XE_scRGB_alpha:
{
LColorf scaled = value * 8192.0f + 4096.5f;
- col.r = std::min(std::max(0, (int)scaled[0]), 65535);
- col.g = std::min(std::max(0, (int)scaled[1]), 65535);
- col.b = std::min(std::max(0, (int)scaled[2]), 65535);
- alpha_row(y)[x] = std::min(std::max(0, (int)(value[3] * 65535 + 0.5f)), 65535);
+ col.r = (std::min)((std::max)(0, (int)scaled[0]), 65535);
+ col.g = (std::min)((std::max)(0, (int)scaled[1]), 65535);
+ col.b = (std::min)((std::max)(0, (int)scaled[2]), 65535);
+ alpha_row(y)[x] = (std::min)((std::max)(0, (int)(value[3] * 65535 + 0.5f)), 65535);
}
break;
}
@@ -1224,14 +1224,14 @@ setup_sub_image(const PNMImage ©, int &xto, int &yto,
yto = 0;
}
- x_size = std::min(x_size, copy.get_x_size() - xfrom);
- y_size = std::min(y_size, copy.get_y_size() - yfrom);
+ x_size = (std::min)(x_size, copy.get_x_size() - xfrom);
+ y_size = (std::min)(y_size, copy.get_y_size() - yfrom);
xmin = xto;
ymin = yto;
- xmax = std::min(xmin + x_size, get_x_size());
- ymax = std::min(ymin + y_size, get_y_size());
+ xmax = (std::min)(xmin + x_size, get_x_size());
+ ymax = (std::min)(ymin + y_size, get_y_size());
}
/**
diff --git a/panda/src/pnmimage/pnmImageHeader.h b/panda/src/pnmimage/pnmImageHeader.h
index 7030e06d2e..6e21ddca98 100644
--- a/panda/src/pnmimage/pnmImageHeader.h
+++ b/panda/src/pnmimage/pnmImageHeader.h
@@ -20,6 +20,7 @@
#include "typedObject.h"
#include "filename.h"
+#include "memoryBase.h"
#include "pnotify.h"
#include "pmap.h"
#include "pvector.h"
@@ -37,7 +38,7 @@ class PNMWriter;
* image except the image data itself. It's the sort of information you
* typically read from the image file's header.
*/
-class EXPCL_PANDA_PNMIMAGE PNMImageHeader {
+class EXPCL_PANDA_PNMIMAGE PNMImageHeader : public MemoryBase {
PUBLISHED:
INLINE PNMImageHeader();
INLINE PNMImageHeader(const PNMImageHeader ©);
diff --git a/panda/src/pnmimagetypes/CMakeLists.txt b/panda/src/pnmimagetypes/CMakeLists.txt
index 84c8ceeab7..2045b7130d 100644
--- a/panda/src/pnmimagetypes/CMakeLists.txt
+++ b/panda/src/pnmimagetypes/CMakeLists.txt
@@ -39,6 +39,35 @@ target_link_libraries(p3pnmimagetypes p3pnmimage
PKG::JPEG PKG::TIFF PKG::PNG PKG::OPENEXR)
set_target_properties(p3pnmimagetypes PROPERTIES CXX_EXCEPTIONS ON)
+if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Do not re-export symbols from these libraries.
+ if(HAVE_JPEG)
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libjpeg.a")
+ endif()
+
+ if(HAVE_TIFF)
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libtiff.a")
+ endif()
+
+ if(HAVE_PNG)
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libpng.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libpng16.a")
+ endif()
+
+ if(HAVE_OPENEXR)
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libHalf.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIex.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIexMath.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmImf.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmImfUtil.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libIlmThread.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libImath.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXR.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXRCore.a")
+ target_link_options(p3pnmimagetypes PRIVATE "LINKER:--exclude-libs,libOpenEXRUtil.a")
+ endif()
+endif()
+
if(NOT BUILD_METALIBS)
install(TARGETS p3pnmimagetypes
EXPORT Core COMPONENT Core
diff --git a/panda/src/pstatclient/config_pstatclient.cxx b/panda/src/pstatclient/config_pstatclient.cxx
index 1335254590..7f9443cdb4 100644
--- a/panda/src/pstatclient/config_pstatclient.cxx
+++ b/panda/src/pstatclient/config_pstatclient.cxx
@@ -12,7 +12,7 @@
*/
#include "config_pstatclient.h"
-
+#include "pStatTimer.h"
#include "dconfig.h"
#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_PANDA_PSTATCLIENT)
@@ -82,6 +82,11 @@ ConfigVariableBool pstats_gpu_timing
"is not usually an accurate reflectino of how long the actual "
"operation takes on the video card."));
+ConfigVariableBool pstats_thread_profiling
+("pstats-thread-profiling", false,
+ PRC_DESC("Set this true to query the system for thread statistics, such as "
+ "the number of context switches and time spent waiting."));
+
// The rest are different in that they directly control the server, not the
// client.
ConfigVariableBool pstats_scroll_mode
diff --git a/panda/src/pstatclient/config_pstatclient.h b/panda/src/pstatclient/config_pstatclient.h
index 6de3c5c120..856031e1f2 100644
--- a/panda/src/pstatclient/config_pstatclient.h
+++ b/panda/src/pstatclient/config_pstatclient.h
@@ -38,6 +38,7 @@ extern EXPCL_PANDA_PSTATCLIENT ConfigVariableString pstats_host;
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableInt pstats_port;
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_target_frame_rate;
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_gpu_timing;
+extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_thread_profiling;
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_scroll_mode;
extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_history;
diff --git a/panda/src/pstatclient/pStatClient.I b/panda/src/pstatclient/pStatClient.I
index 452c165901..e7daf8fa0e 100644
--- a/panda/src/pstatclient/pStatClient.I
+++ b/panda/src/pstatclient/pStatClient.I
@@ -16,8 +16,7 @@
*/
INLINE int PStatClient::
get_num_collectors() const {
- ReMutexHolder holder(_lock);
- return (int)_num_collectors;
+ return _num_collectors.load(std::memory_order_relaxed);
}
/**
@@ -25,7 +24,7 @@ get_num_collectors() const {
*/
INLINE PStatCollectorDef *PStatClient::
get_collector_def(int index) const {
- nassertr(index >= 0 && index < _num_collectors, nullptr);
+ nassertr(index >= 0 && index < get_num_collectors(), nullptr);
return get_collector_ptr(index)->get_def(this, index);
}
@@ -35,8 +34,7 @@ get_collector_def(int index) const {
*/
INLINE int PStatClient::
get_num_threads() const {
- ReMutexHolder holder(_lock);
- return (int)_num_threads;
+ return _num_threads.load(std::memory_order_relaxed);
}
/**
@@ -44,7 +42,7 @@ get_num_threads() const {
*/
INLINE std::string PStatClient::
get_thread_name(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), std::string());
+ nassertr(index >= 0 && index < get_num_threads(), std::string());
return get_thread_ptr(index)->_name;
}
@@ -53,7 +51,7 @@ get_thread_name(int index) const {
*/
INLINE std::string PStatClient::
get_thread_sync_name(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), std::string());
+ nassertr(index >= 0 && index < get_num_threads(), std::string());
return get_thread_ptr(index)->_sync_name;
}
@@ -62,7 +60,7 @@ get_thread_sync_name(int index) const {
*/
INLINE PT(Thread) PStatClient::
get_thread_object(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_threads), nullptr);
+ nassertr(index >= 0 && index < get_num_threads(), nullptr);
InternalThread *thread = get_thread_ptr(index);
return thread->_thread.lock();
}
@@ -144,7 +142,7 @@ get_impl() const {
*/
INLINE PStatClient::Collector *PStatClient::
get_collector_ptr(int collector_index) const {
- CollectorPointer *collectors = (CollectorPointer *)AtomicAdjust::get_ptr(_collectors);
+ CollectorPointer *collectors = _collectors.load(std::memory_order_consume);
return collectors[collector_index];
}
@@ -153,7 +151,7 @@ get_collector_ptr(int collector_index) const {
*/
INLINE PStatClient::InternalThread *PStatClient::
get_thread_ptr(int thread_index) const {
- ThreadPointer *threads = (ThreadPointer *)AtomicAdjust::get_ptr(_threads);
+ ThreadPointer *threads = _threads.load(std::memory_order_consume);
return threads[thread_index];
}
diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx
index dd57612107..906cd87e6b 100644
--- a/panda/src/pstatclient/pStatClient.cxx
+++ b/panda/src/pstatclient/pStatClient.cxx
@@ -77,14 +77,6 @@ PStatClient() :
_lock("PStatClient::_lock"),
_impl(nullptr)
{
- _collectors = nullptr;
- _collectors_size = 0;
- _num_collectors = 0;
-
- _threads = nullptr;
- _threads_size = 0;
- _num_threads = 0;
-
// We always have a collector at index 0 named "Frame". This tracks the
// total frame time and is the root of all other collectors. We have to
// make this one by hand since it's the root.
@@ -152,7 +144,7 @@ get_max_rate() const {
*/
PStatCollector PStatClient::
get_collector(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), PStatCollector());
+ nassertr(index >= 0 && index < get_num_collectors(), PStatCollector());
return PStatCollector((PStatClient *)this, index);
}
@@ -161,7 +153,7 @@ get_collector(int index) const {
*/
string PStatClient::
get_collector_name(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
+ nassertr(index >= 0 && index < get_num_collectors(), string());
return get_collector_ptr(index)->get_name();
}
@@ -173,7 +165,7 @@ get_collector_name(int index) const {
*/
string PStatClient::
get_collector_fullname(int index) const {
- nassertr(index >= 0 && index < AtomicAdjust::get(_num_collectors), string());
+ nassertr(index >= 0 && index < get_num_collectors(), string());
Collector *collector = get_collector_ptr(index);
int parent_index = collector->get_parent_index();
@@ -191,7 +183,7 @@ get_collector_fullname(int index) const {
PStatThread PStatClient::
get_thread(int index) const {
ReMutexHolder holder(_lock);
- nassertr(index >= 0 && index < _num_threads, PStatThread());
+ nassertr(index >= 0 && index < get_num_threads(), PStatThread());
return PStatThread((PStatClient *)this, index);
}
@@ -381,6 +373,14 @@ main_tick() {
get_global_pstats()->client_main_tick();
}
+/**
+ * A convenience function to call new_frame() for the current thread.
+ */
+void PStatClient::
+thread_tick() {
+ get_global_pstats()->client_thread_tick();
+}
+
/**
* A convenience function to call new_frame() on any threads with the
* indicated sync_name
@@ -403,6 +403,8 @@ client_main_tick() {
return;
}
+ ClockObject *clock = ClockObject::get_global_clock();
+
_impl->client_main_tick();
MultiThingsByName::const_iterator ni =
@@ -412,12 +414,26 @@ client_main_tick() {
for (vector_int::const_iterator vi = indices.begin();
vi != indices.end();
++vi) {
- _impl->new_frame(*vi);
+ int frame_number = clock->get_frame_count(get_thread_object(*vi));
+ _impl->new_frame(*vi, frame_number);
}
}
}
}
+/**
+ * A convenience function to call new_frame() on the current thread.
+ */
+void PStatClient::
+client_thread_tick() {
+ ReMutexHolder holder(_lock);
+
+ if (has_impl()) {
+ PStatThread thread = do_get_current_thread();
+ _impl->new_frame(thread.get_index());
+ }
+}
+
/**
* A convenience function to call new_frame() on all of the threads with the
* indicated sync name.
@@ -462,8 +478,9 @@ client_disconnect() {
_impl = nullptr;
}
- ThreadPointer *threads = (ThreadPointer *)_threads;
- for (int ti = 0; ti < _num_threads; ++ti) {
+ // These can be relaxed loads because we hold the lock.
+ ThreadPointer *threads = _threads.load(std::memory_order_relaxed);
+ for (int ti = 0; ti < get_num_threads(); ++ti) {
InternalThread *thread = threads[ti];
thread->_frame_number = 0;
thread->_is_active = false;
@@ -471,14 +488,11 @@ client_disconnect() {
thread->_frame_data.clear();
}
- CollectorPointer *collectors = (CollectorPointer *)_collectors;
- for (int ci = 0; ci < _num_collectors; ++ci) {
+ CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed);
+ for (int ci = 0; ci < get_num_collectors(); ++ci) {
Collector *collector = collectors[ci];
- PerThread::iterator ii;
- for (ii = collector->_per_thread.begin();
- ii != collector->_per_thread.end();
- ++ii) {
- (*ii)._nested_count = 0;
+ for (PerThreadData &per_thread : collector->_per_thread) {
+ per_thread._nested_count = 0;
}
}
}
@@ -576,7 +590,8 @@ PStatCollector PStatClient::
make_collector_with_name(int parent_index, const string &name) {
ReMutexHolder holder(_lock);
- nassertr(parent_index >= 0 && parent_index < _num_collectors,
+ int num_collectors = get_num_collectors();
+ nassertr(parent_index >= 0 && parent_index < num_collectors,
PStatCollector());
Collector *parent = get_collector_ptr(parent_index);
@@ -593,26 +608,25 @@ make_collector_with_name(int parent_index, const string &name) {
if (ni != parent->_children.end()) {
// We already had a collector by this name; return it.
int index = (*ni).second;
- nassertr(index >= 0 && index < _num_collectors, PStatCollector());
+ nassertr(index >= 0 && index < num_collectors, PStatCollector());
return PStatCollector(this, (*ni).second);
}
// Create a new collector for this name.
- int new_index = _num_collectors;
- parent->_children.insert(ThingsByName::value_type(name, new_index));
+ parent->_children.insert(ThingsByName::value_type(name, num_collectors));
Collector *collector = new Collector(parent_index, name);
- // collector->_def = new PStatCollectorDef(new_index, name);
+ // collector->_def = new PStatCollectorDef(num_collectors, name);
// collector->_def->set_parent(*_collectors[parent_index]._def);
// initialize_collector_def(this, collector->_def);
// We need one PerThreadData for each thread.
- while ((int)collector->_per_thread.size() < _num_threads) {
+ while ((int)collector->_per_thread.size() < get_num_threads()) {
collector->_per_thread.push_back(PerThreadData());
}
add_collector(collector);
- return PStatCollector(this, new_index);
+ return PStatCollector(this, num_collectors);
}
/**
@@ -662,8 +676,8 @@ do_make_thread(Thread *thread) {
vi != indices.end();
++vi) {
int index = (*vi);
- nassertr(index >= 0 && index < _num_threads, PStatThread());
- ThreadPointer *threads = (ThreadPointer *)_threads;
+ nassertr(index >= 0 && index < get_num_threads(), PStatThread());
+ ThreadPointer *threads = _threads.load(std::memory_order_relaxed);
if (threads[index]->_thread.was_deleted() &&
threads[index]->_sync_name == thread->get_sync_name()) {
// Yes, re-use this one.
@@ -676,7 +690,7 @@ do_make_thread(Thread *thread) {
}
// Create a new PStatsThread for this thread pointer.
- int new_index = _num_threads;
+ int new_index = get_num_threads();
thread->set_pstats_index(new_index);
thread->set_pstats_callback(this);
@@ -693,7 +707,7 @@ do_make_thread(Thread *thread) {
PStatThread PStatClient::
make_gpu_thread(const string &name) {
ReMutexHolder holder(_lock);
- int new_index = _num_threads;
+ int new_index = get_num_threads();
InternalThread *pthread = new InternalThread(name, "GPU");
add_thread(pthread);
@@ -710,8 +724,8 @@ make_gpu_thread(const string &name) {
*/
bool PStatClient::
is_active(int collector_index, int thread_index) const {
- nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
- nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
+ nassertr(collector_index >= 0 && collector_index < get_num_collectors(), false);
+ nassertr(thread_index >= 0 && thread_index < get_num_threads(), false);
return (client_is_connected() &&
get_collector_ptr(collector_index)->is_active() &&
@@ -727,8 +741,8 @@ is_active(int collector_index, int thread_index) const {
*/
bool PStatClient::
is_started(int collector_index, int thread_index) const {
- nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), false);
- nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), false);
+ nassertr(collector_index >= 0 && collector_index < get_num_collectors(), false);
+ nassertr(thread_index >= 0 && thread_index < get_num_threads(), false);
Collector *collector = get_collector_ptr(collector_index);
InternalThread *thread = get_thread_ptr(thread_index);
@@ -758,8 +772,8 @@ start(int collector_index, int thread_index) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -789,8 +803,8 @@ start(int collector_index, int thread_index, double as_of) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -820,8 +834,8 @@ stop(int collector_index, int thread_index) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -862,8 +876,8 @@ stop(int collector_index, int thread_index, double as_of) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -891,6 +905,38 @@ stop(int collector_index, int thread_index, double as_of) {
}
}
+/**
+ * Adds a pair of start/stop times in the given collector. Used in low-level
+ * code that knows there will not be any other collectors started and stopped
+ * in the meantime, and can be more efficient than a pair of start/stop times.
+ */
+void PStatClient::
+start_stop(int collector_index, int thread_index, double start, double stop) {
+ if (!client_is_connected()) {
+ return;
+ }
+
+#ifdef _DEBUG
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
+#endif
+
+ Collector *collector = get_collector_ptr(collector_index);
+ InternalThread *thread = get_thread_ptr(thread_index);
+
+ if (collector->is_active() && thread->_is_active) {
+ LightMutexHolder holder(thread->_thread_lock);
+ if (collector->_per_thread[thread_index]._nested_count == 0) {
+ // This collector wasn't already started in this thread; record a new
+ // data point.
+ if (thread->_thread_active) {
+ thread->_frame_data.add_start(collector_index, start);
+ thread->_frame_data.add_stop(collector_index, stop);
+ }
+ }
+ }
+}
+
/**
* Removes the level value from the indicated collector. The collector will
* no longer be reported as having any particular level value.
@@ -905,8 +951,8 @@ clear_level(int collector_index, int thread_index) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -930,8 +976,8 @@ set_level(int collector_index, int thread_index, double level) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -963,8 +1009,8 @@ add_level(int collector_index, int thread_index, double increment) {
}
#ifdef _DEBUG
- nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
- nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+ nassertv(collector_index >= 0 && collector_index < get_num_collectors());
+ nassertv(thread_index >= 0 && thread_index < get_num_threads());
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -991,8 +1037,8 @@ get_level(int collector_index, int thread_index) const {
}
#ifdef _DEBUG
- nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), 0.0f);
- nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), 0.0f);
+ nassertr(collector_index >= 0 && collector_index < get_num_collectors(), 0.0f);
+ nassertr(thread_index >= 0 && thread_index < get_num_threads(), 0.0f);
#endif
Collector *collector = get_collector_ptr(collector_index);
@@ -1050,17 +1096,19 @@ stop_clock_wait() {
*/
void PStatClient::
add_collector(PStatClient::Collector *collector) {
- if (_num_collectors >= _collectors_size) {
+ int num_collectors = get_num_collectors();
+ if (num_collectors >= (int)_collectors_size) {
// We need to grow the array. We have to be careful here, because there
// might be clients accessing the array right now who are not protected by
// the lock.
- int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
+ size_t new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
+ CollectorPointer *old_collectors = _collectors.load(std::memory_order_relaxed);
CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size];
- if (_collectors != nullptr) {
- memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer));
+ if (old_collectors != nullptr) {
+ memcpy(new_collectors, old_collectors, num_collectors * sizeof(CollectorPointer));
}
- AtomicAdjust::set_ptr(_collectors, new_collectors);
- AtomicAdjust::set(_collectors_size, new_collectors_size);
+ _collectors_size = new_collectors_size;
+ _collectors.store(new_collectors, std::memory_order_release);
// Now, we still have the old array, which we allow to leak. We should
// delete it, but there might be a thread out there that's still trying to
@@ -1068,14 +1116,14 @@ add_collector(PStatClient::Collector *collector) {
// much, since it's not a big leak. (We will only reallocate the array so
// many times in an application, and then no more.)
- new_collectors[_num_collectors] = collector;
- AtomicAdjust::inc(_num_collectors);
-
- } else {
- CollectorPointer *collectors = (CollectorPointer *)_collectors;
- collectors[_num_collectors] = collector;
- AtomicAdjust::inc(_num_collectors);
+ new_collectors[num_collectors] = collector;
}
+ else {
+ CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed);
+ collectors[num_collectors] = collector;
+ }
+
+ _num_collectors.fetch_add(1, std::memory_order_release);
}
/**
@@ -1084,21 +1132,22 @@ add_collector(PStatClient::Collector *collector) {
*/
void PStatClient::
add_thread(PStatClient::InternalThread *thread) {
- _threads_by_name[thread->_name].push_back(_num_threads);
- _threads_by_sync_name[thread->_sync_name].push_back(_num_threads);
+ int num_threads = get_num_threads();
+ _threads_by_name[thread->_name].push_back(num_threads);
+ _threads_by_sync_name[thread->_sync_name].push_back(num_threads);
- if (_num_threads >= _threads_size) {
+ if (num_threads >= (int)_threads_size) {
// We need to grow the array. We have to be careful here, because there
// might be clients accessing the array right now who are not protected by
// the lock.
- int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
+ size_t new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
+ ThreadPointer *old_threads = _threads.load(std::memory_order_relaxed);
ThreadPointer *new_threads = new ThreadPointer[new_threads_size];
- if (_threads != nullptr) {
- memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer));
+ if (old_threads != nullptr) {
+ memcpy(new_threads, old_threads, num_threads * sizeof(ThreadPointer));
}
- // We assume that assignment to a pointer and to an int are each atomic.
- AtomicAdjust::set_ptr(_threads, new_threads);
- AtomicAdjust::set(_threads_size, new_threads_size);
+ _threads_size = new_threads_size;
+ _threads.store(new_threads, std::memory_order_release);
// Now, we still have the old array, which we allow to leak. We should
// delete it, but there might be a thread out there that's still trying to
@@ -1106,22 +1155,23 @@ add_thread(PStatClient::InternalThread *thread) {
// much, since it's not a big leak. (We will only reallocate the array so
// many times in an application, and then no more.)
- new_threads[_num_threads] = thread;
-
- } else {
- ThreadPointer *threads = (ThreadPointer *)_threads;
- threads[_num_threads] = thread;
+ new_threads[num_threads] = thread;
+ }
+ else {
+ ThreadPointer *threads = _threads.load(std::memory_order_relaxed);
+ threads[num_threads] = thread;
}
- AtomicAdjust::inc(_num_threads);
+ _num_threads.fetch_add(1, std::memory_order_release);
+ ++num_threads;
// We need an additional PerThreadData for this thread in all of the
// collectors.
- CollectorPointer *collectors = (CollectorPointer *)_collectors;
- for (int ci = 0; ci < _num_collectors; ++ci) {
+ CollectorPointer *collectors = _collectors.load(std::memory_order_relaxed);
+ for (int ci = 0; ci < get_num_collectors(); ++ci) {
Collector *collector = collectors[ci];
collector->_per_thread.push_back(PerThreadData());
- nassertv((int)collector->_per_thread.size() == _num_threads);
+ nassertv((int)collector->_per_thread.size() == num_threads);
}
}
@@ -1291,6 +1341,10 @@ void PStatClient::
main_tick() {
}
+void PStatClient::
+thread_tick() {
+}
+
void PStatClient::
thread_tick(const std::string &) {
}
@@ -1299,6 +1353,10 @@ void PStatClient::
client_main_tick() {
}
+void PStatClient::
+client_thread_tick() {
+}
+
void PStatClient::
client_thread_tick(const std::string &sync_name) {
}
diff --git a/panda/src/pstatclient/pStatClient.h b/panda/src/pstatclient/pStatClient.h
index 1e6fe34480..d164cb29b1 100644
--- a/panda/src/pstatclient/pStatClient.h
+++ b/panda/src/pstatclient/pStatClient.h
@@ -26,7 +26,7 @@
#include "thread.h"
#include "weakPointerTo.h"
#include "vector_int.h"
-#include "atomicAdjust.h"
+#include "patomic.h"
#include "numeric_types.h"
#include "bitArray.h"
@@ -95,9 +95,11 @@ PUBLISHED:
INLINE static void resume_after_pause();
static void main_tick();
+ static void thread_tick();
static void thread_tick(const std::string &sync_name);
void client_main_tick();
+ void client_thread_tick();
void client_thread_tick(const std::string &sync_name);
bool client_connect(std::string hostname, int port);
void client_disconnect();
@@ -127,6 +129,7 @@ private:
void start(int collector_index, int thread_index, double as_of);
void stop(int collector_index, int thread_index);
void stop(int collector_index, int thread_index, double as_of);
+ void start_stop(int collector_index, int thread_index, double start, double stop);
void clear_level(int collector_index, int thread_index);
void set_level(int collector_index, int thread_index, double level);
@@ -195,9 +198,9 @@ private:
PerThread _per_thread;
};
typedef Collector *CollectorPointer;
- AtomicAdjust::Pointer _collectors; // CollectorPointer *_collectors;
- AtomicAdjust::Integer _collectors_size; // size of the allocated array
- AtomicAdjust::Integer _num_collectors; // number of in-use elements within the array
+ patomic _collectors {nullptr};
+ size_t _collectors_size {0}; // size of the allocated array
+ patomic _num_collectors {0}; // number of in-use elements within the array
// This defines a single thread, i.e. a separate chain of execution,
// independent of all other threads. Timing and level data are maintained
@@ -214,9 +217,10 @@ private:
bool _is_active;
int _frame_number;
double _next_packet;
+ size_t _context_switches = 0;
+ size_t _involuntary_context_switches = 0;
bool _thread_active;
- BitArray _active_collectors; // no longer used.
// This mutex is used to protect writes to _frame_data for this particular
// thread, as well as writes to the _per_thread data for this particular
@@ -224,9 +228,9 @@ private:
LightMutex _thread_lock;
};
typedef InternalThread *ThreadPointer;
- AtomicAdjust::Pointer _threads; // ThreadPointer *_threads;
- AtomicAdjust::Integer _threads_size; // size of the allocated array
- AtomicAdjust::Integer _num_threads; // number of in-use elements within the array
+ patomic _threads {nullptr};
+ size_t _threads_size {0}; // size of the allocated array
+ patomic _num_threads {0}; // number of in-use elements within the array
mutable PStatClientImpl *_impl;
@@ -292,10 +296,12 @@ PUBLISHED:
INLINE static void resume_after_pause() { }
static void main_tick();
+ static void thread_tick();
static void thread_tick(const std::string &);
public:
void client_main_tick();
+ void client_thread_tick();
void client_thread_tick(const std::string &sync_name);
bool client_connect(std::string hostname, int port);
void client_disconnect();
@@ -318,6 +324,7 @@ private:
void start(int collector_index, int thread_index, double as_of);
void stop(int collector_index, int thread_index);
void stop(int collector_index, int thread_index, double as_of);
+ void start_stop(int collector_index, int thread_index, double start, double stop);
void clear_level(int collector_index, int thread_index);
void set_level(int collector_index, int thread_index, double level);
diff --git a/panda/src/pstatclient/pStatClientControlMessage.cxx b/panda/src/pstatclient/pStatClientControlMessage.cxx
index e7fd765547..5cbac136e4 100644
--- a/panda/src/pstatclient/pStatClientControlMessage.cxx
+++ b/panda/src/pstatclient/pStatClientControlMessage.cxx
@@ -39,6 +39,7 @@ encode(Datagram &datagram) const {
datagram.add_string(_client_progname);
datagram.add_uint16(_major_version);
datagram.add_uint16(_minor_version);
+ datagram.add_uint32(_client_pid);
break;
case T_define_collectors:
@@ -86,6 +87,11 @@ decode(const Datagram &datagram, PStatClientVersion *version) {
_major_version = source.get_uint16();
_minor_version = source.get_uint16();
}
+ if (source.get_remaining_size() >= 4) {
+ _client_pid = source.get_uint32();
+ } else {
+ _client_pid = -1;
+ }
break;
case T_define_collectors:
diff --git a/panda/src/pstatclient/pStatClientControlMessage.h b/panda/src/pstatclient/pStatClientControlMessage.h
index 97e7dede98..32a9f65f70 100644
--- a/panda/src/pstatclient/pStatClientControlMessage.h
+++ b/panda/src/pstatclient/pStatClientControlMessage.h
@@ -47,6 +47,7 @@ public:
// Used for T_hello
std::string _client_hostname;
std::string _client_progname;
+ int _client_pid;
int _major_version;
int _minor_version;
diff --git a/panda/src/pstatclient/pStatClientImpl.cxx b/panda/src/pstatclient/pStatClientImpl.cxx
index f25b863b97..69415569fb 100644
--- a/panda/src/pstatclient/pStatClientImpl.cxx
+++ b/panda/src/pstatclient/pStatClientImpl.cxx
@@ -24,6 +24,8 @@
#include "config_pstatclient.h"
#include "pStatProperties.h"
#include "cmath.h"
+#include "conditionVarWin32Impl.h"
+#include "conditionVarPosixImpl.h"
#include
@@ -32,6 +34,15 @@
#include
#endif
+static PStatCollector _cswitch_pcollector("Context Switches");
+static PStatCollector _cswitch_sleep_pcollector("Context Switches:Sleep");
+static PStatCollector _cswitch_yield_pcollector("Context Switches:Yields");
+static PStatCollector _cswitch_cvar_pcollector("Context Switches:Condition Variable");
+static PStatCollector _cswitch_involuntary_pcollector("Context Switches:Involuntary");
+static PStatCollector _wait_sleep_pcollector("Wait:Sleep");
+static PStatCollector _wait_yield_pcollector("Wait:Yield");
+static PStatCollector _wait_cvar_pcollector("Wait:Condition Variable");
+
/**
*
*/
@@ -124,6 +135,111 @@ client_connect(std::string hostname, int port) {
MutexDebug::increment_pstats();
#endif // DEBUG_THREADS
+ if (pstats_thread_profiling) {
+ // Replace the condition variable wait function with one that does PStats
+ // statistics.
+ _thread_profiling = true;
+
+ Thread::_sleep_func = [] (double seconds) {
+ Thread *current_thread = Thread::get_current_thread();
+ int thread_index = current_thread->get_pstats_index();
+ if (thread_index >= 0) {
+ PStatClient *client = PStatClient::get_global_pstats();
+ double start = client->get_real_time();
+ ThreadImpl::sleep(seconds);
+ double stop = client->get_real_time();
+ client->start_stop(_wait_sleep_pcollector.get_index(), thread_index, start, stop);
+ client->add_level(_cswitch_sleep_pcollector.get_index(), thread_index, 1);
+ }
+ else {
+ ThreadImpl::sleep(seconds);
+ }
+ };
+
+ Thread::_yield_func = [] () {
+ Thread *current_thread = Thread::get_current_thread();
+ int thread_index = current_thread->get_pstats_index();
+ if (thread_index >= 0) {
+ PStatClient *client = PStatClient::get_global_pstats();
+ double start = client->get_real_time();
+ ThreadImpl::yield();
+ double stop = client->get_real_time();
+ client->start_stop(_wait_yield_pcollector.get_index(), thread_index, start, stop);
+ client->add_level(_cswitch_yield_pcollector.get_index(), thread_index, 1);
+ }
+ else {
+ ThreadImpl::yield();
+ }
+ };
+
+#ifdef _WIN32
+ ConditionVarWin32Impl::_wait_func =
+ [] (PCONDITION_VARIABLE cvar, PSRWLOCK lock, DWORD time, ULONG flags) {
+ Thread *current_thread = Thread::get_current_thread();
+ int thread_index = current_thread->get_pstats_index();
+ BOOL result;
+ if (thread_index >= 0) {
+ PStatClient *client = PStatClient::get_global_pstats();
+ double start = client->get_real_time();
+ result = SleepConditionVariableSRW(cvar, lock, time, flags);
+ double stop = client->get_real_time();
+ client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop);
+ client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1);
+ }
+ else {
+ result = SleepConditionVariableSRW(cvar, lock, time, flags);
+ }
+ return result;
+ };
+#endif
+
+#ifdef HAVE_POSIX_THREADS
+ ConditionVarPosixImpl::_wait_func =
+ [] (pthread_cond_t *cvar, pthread_mutex_t *lock) {
+ Thread *current_thread = Thread::get_current_thread();
+ int thread_index = current_thread->get_pstats_index();
+ int result;
+ if (thread_index >= 0) {
+ PStatClient *client = PStatClient::get_global_pstats();
+ double start = client->get_real_time();
+ result = pthread_cond_wait(cvar, lock);
+ double stop = client->get_real_time();
+ client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop);
+ client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1);
+ }
+ else {
+ result = pthread_cond_wait(cvar, lock);
+ }
+ return result;
+ };
+
+ ConditionVarPosixImpl::_timedwait_func =
+ [] (pthread_cond_t *cvar, pthread_mutex_t *lock,
+ const struct timespec *ts) {
+ Thread *current_thread = Thread::get_current_thread();
+ int thread_index = current_thread->get_pstats_index();
+ int result;
+ if (thread_index >= 0) {
+ PStatClient *client = PStatClient::get_global_pstats();
+ double start = client->get_real_time();
+ result = pthread_cond_timedwait(cvar, lock, ts);
+ double stop = client->get_real_time();
+ client->start_stop(_wait_cvar_pcollector.get_index(), thread_index, start, stop);
+ client->add_level(_cswitch_cvar_pcollector.get_index(), thread_index, 1);
+ }
+ else {
+ result = pthread_cond_timedwait(cvar, lock, ts);
+ }
+ return result;
+ };
+#endif
+ }
+
+ // Wait for the server hello.
+ while (!_got_udp_port) {
+ transmit_control_data();
+ }
+
return _is_connected;
}
@@ -132,6 +248,23 @@ client_connect(std::string hostname, int port) {
*/
void PStatClientImpl::
client_disconnect() {
+ if (_thread_profiling) {
+ // Switch the functions back to what they were.
+ Thread::_sleep_func = &ThreadImpl::sleep;
+ Thread::_yield_func = &ThreadImpl::yield;
+
+#ifdef _WIN32
+ ConditionVarWin32Impl::_wait_func = &SleepConditionVariableSRW;
+#endif
+
+#ifdef HAVE_POSIX_THREADS
+ ConditionVarPosixImpl::_wait_func = &pthread_cond_wait;
+ ConditionVarPosixImpl::_timedwait_func = &pthread_cond_timedwait;
+#endif
+
+ _thread_profiling = false;
+ }
+
if (_is_connected) {
#ifdef DEBUG_THREADS
MutexDebug::decrement_pstats();
@@ -157,7 +290,9 @@ client_disconnect() {
* data for the previous frame.
*/
void PStatClientImpl::
-new_frame(int thread_index) {
+new_frame(int thread_index, int frame_number) {
+ double frame_start = get_real_time();
+
nassertv(thread_index >= 0 && thread_index < _client->_num_threads);
PStatClient::InternalThread *pthread = _client->get_thread_ptr(thread_index);
@@ -178,8 +313,6 @@ new_frame(int thread_index) {
return;
}
- double frame_start = get_real_time();
- int frame_number = -1;
PStatFrameData frame_data;
if (!pthread->_frame_data.is_empty()) {
@@ -199,11 +332,44 @@ new_frame(int thread_index) {
}
}
pthread->_frame_data.swap(frame_data);
- frame_number = pthread->_frame_number;
+ if (frame_number == -1) {
+ frame_number = pthread->_frame_number;
+ }
+
+ // Record the number of context switches on this thread.
+ if (_thread_profiling) {
+ size_t total, involuntary;
+ PT(Thread) thread = pthread->_thread.lock();
+ if (thread != nullptr && thread->get_context_switches(total, involuntary)) {
+ size_t total_this_frame = total - pthread->_context_switches;
+ pthread->_context_switches = total;
+ frame_data.add_level(_cswitch_pcollector.get_index(), total_this_frame);
+
+ if (involuntary != 0) {
+ size_t involuntary_this_frame = involuntary - pthread->_involuntary_context_switches;
+ pthread->_involuntary_context_switches = involuntary;
+ frame_data.add_level(_cswitch_involuntary_pcollector.get_index(), involuntary_this_frame);
+ }
+ }
+ _client->clear_level(_cswitch_sleep_pcollector.get_index(), thread_index);
+ _client->clear_level(_cswitch_yield_pcollector.get_index(), thread_index);
+ _client->clear_level(_cswitch_cvar_pcollector.get_index(), thread_index);
+ }
+ }
+ else if (_thread_profiling) {
+ // Record the initial number of context switches.
+ PT(Thread) thread = pthread->_thread.lock();
+ if (thread != nullptr) {
+ thread->get_context_switches(pthread->_context_switches,
+ pthread->_involuntary_context_switches);
+ }
+ _client->clear_level(_cswitch_sleep_pcollector.get_index(), thread_index);
+ _client->clear_level(_cswitch_yield_pcollector.get_index(), thread_index);
+ _client->clear_level(_cswitch_cvar_pcollector.get_index(), thread_index);
}
pthread->_frame_data.clear();
- pthread->_frame_number++;
+ pthread->_frame_number = frame_number + 1;
_client->start(0, thread_index, frame_start);
// Also record the time for the PStats operation itself.
@@ -211,7 +377,7 @@ new_frame(int thread_index) {
int pstats_index = PStatClient::_pstats_pcollector.get_index();
_client->start(pstats_index, current_thread_index, frame_start);
- if (frame_number != -1) {
+ if (!frame_data.is_empty()) {
transmit_frame_data(thread_index, frame_number, frame_data);
}
_client->stop(pstats_index, current_thread_index, get_real_time());
@@ -222,7 +388,7 @@ new_frame(int thread_index) {
* data.
*/
void PStatClientImpl::
-add_frame(int thread_index, const PStatFrameData &frame_data) {
+add_frame(int thread_index, int frame_number, const PStatFrameData &frame_data) {
nassertv(thread_index >= 0 && thread_index < _client->_num_threads);
PStatClient::InternalThread *pthread = _client->get_thread_ptr(thread_index);
@@ -243,16 +409,12 @@ add_frame(int thread_index, const PStatFrameData &frame_data) {
return;
}
- int frame_number = pthread->_frame_number++;
-
// Also record the time for the PStats operation itself.
int current_thread_index = Thread::get_current_thread()->get_pstats_index();
int pstats_index = PStatClient::_pstats_pcollector.get_index();
_client->start(pstats_index, current_thread_index);
- if (frame_number != -1) {
- transmit_frame_data(thread_index, frame_number, frame_data);
- }
+ transmit_frame_data(thread_index, frame_number, frame_data);
_client->stop(pstats_index, current_thread_index);
}
@@ -396,6 +558,11 @@ send_hello() {
message._type = PStatClientControlMessage::T_hello;
message._client_hostname = get_hostname();
message._client_progname = _client_name;
+#ifdef _WIN32
+ message._client_pid = GetCurrentProcessId();
+#else
+ message._client_pid = getpid();
+#endif
message._major_version = get_current_pstat_major_version();
message._minor_version = get_current_pstat_minor_version();
diff --git a/panda/src/pstatclient/pStatClientImpl.h b/panda/src/pstatclient/pStatClientImpl.h
index 598a2e63e7..f758d4b262 100644
--- a/panda/src/pstatclient/pStatClientImpl.h
+++ b/panda/src/pstatclient/pStatClientImpl.h
@@ -65,8 +65,8 @@ public:
INLINE void client_resume_after_pause();
- void new_frame(int thread_index);
- void add_frame(int thread_index, const PStatFrameData &frame_data);
+ void new_frame(int thread_index, int frame_number = -1);
+ void add_frame(int thread_index, int frame_number, const PStatFrameData &frame_data);
private:
void transmit_frame_data(int thread_index, int frame_number,
@@ -111,6 +111,8 @@ private:
double _udp_count_factor;
unsigned int _tcp_count;
unsigned int _udp_count;
+
+ bool _thread_profiling = false;
};
#include "pStatClientImpl.I"
diff --git a/panda/src/pstatclient/pStatFrameData.I b/panda/src/pstatclient/pStatFrameData.I
index 47f9a99243..f64054643a 100644
--- a/panda/src/pstatclient/pStatFrameData.I
+++ b/panda/src/pstatclient/pStatFrameData.I
@@ -105,7 +105,7 @@ add_level(int index, double level) {
*/
INLINE double PStatFrameData::
get_start() const {
- if (is_empty()) {
+ if (is_time_empty()) {
return 0.0;
}
@@ -118,7 +118,7 @@ get_start() const {
*/
INLINE double PStatFrameData::
get_end() const {
- nassertr(!is_empty(), 0.0);
+ nassertr(!is_time_empty(), 0.0);
return _time_data.back()._value;
}
@@ -128,7 +128,7 @@ get_end() const {
*/
INLINE double PStatFrameData::
get_net_time() const {
- nassertr(!is_empty(), 0.0);
+ nassertr(!is_time_empty(), 0.0);
return _time_data.back()._value - _time_data.front()._value;
}
diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx
index cce31e693f..03a93d7b59 100644
--- a/panda/src/pstatclient/pStatProperties.cxx
+++ b/panda/src/pstatclient/pStatProperties.cxx
@@ -109,6 +109,7 @@ static TimeCollectorProperties time_properties[] = {
{ 1, "App:Collisions:Reset", { 0.0, 0.0, 0.5 } },
{ 0, "App:Data graph", { 0.5, 0.8, 0.4 } },
{ 1, "App:Show code", { 0.8, 0.2, 1.0 } },
+ //{ 0, "App:Show code:General", { 0.4, 0.3, 0.9 } },
{ 0, "App:Show code:Nametags", { 0.8, 0.8, 1.0 } },
{ 0, "App:Show code:Nametags:2d", { 0.0, 0.0, 0.5 } },
{ 0, "App:Show code:Nametags:2d:Contents", { 0.0, 0.5, 0.0 } },
@@ -216,10 +217,10 @@ static LevelCollectorProperties level_properties[] = {
{ 1, "RenderStates:Cached", { 1.0, 0.0, 0.2 } },
{ 1, "RenderStates:Unused", { 0.2, 0.2, 0.2 } },
{ 1, "PipelineCyclers", { 0.5, 0.5, 1.0 }, "", 50000 },
- { 1, "Dirty PipelineCyclers", { 0.2, 0.2, 0.2 }, "", 5000 },
+ { 1, "PipelineCyclers:Dirty", { 0.2, 0.2, 0.2 }, "", 5000 },
{ 1, "Collision Volumes", { 1.0, 0.8, 0.5 }, "", 500 },
{ 1, "Collision Tests", { 0.5, 0.8, 1.0 }, "", 100 },
- { 1, "Command latency", { 0.8, 0.2, 0.0 }, "ms", 10, 1.0 / 1000.0 },
+ { 1, "window1 latency", { 0.8, 0.2, 0.0 }, "ms", 10, 1.0 / 1000.0 },
{ 0, nullptr }
};
diff --git a/panda/src/pstatclient/pStatThread.cxx b/panda/src/pstatclient/pStatThread.cxx
index e263ac089a..0315b5591f 100644
--- a/panda/src/pstatclient/pStatThread.cxx
+++ b/panda/src/pstatclient/pStatThread.cxx
@@ -24,9 +24,9 @@
* threads with the indicated sync name.
*/
void PStatThread::
-new_frame() {
+new_frame(int frame_number) {
#ifdef DO_PSTATS
- _client->get_impl()->new_frame(_index);
+ _client->get_impl()->new_frame(_index, frame_number);
#endif
}
@@ -35,9 +35,9 @@ new_frame() {
* data to send for this frame.
*/
void PStatThread::
-add_frame(const PStatFrameData &frame_data) {
+add_frame(int frame_number, const PStatFrameData &frame_data) {
#ifdef DO_PSTATS
- _client->get_impl()->add_frame(_index, frame_data);
+ _client->get_impl()->add_frame(_index, frame_number, frame_data);
#endif
}
diff --git a/panda/src/pstatclient/pStatThread.h b/panda/src/pstatclient/pStatThread.h
index 74defa2a15..e3f4acb7e5 100644
--- a/panda/src/pstatclient/pStatThread.h
+++ b/panda/src/pstatclient/pStatThread.h
@@ -36,8 +36,8 @@ PUBLISHED:
INLINE PStatThread(const PStatThread ©);
INLINE void operator = (const PStatThread ©);
- void new_frame();
- void add_frame(const PStatFrameData &frame_data);
+ void new_frame(int frame_number = -1);
+ void add_frame(int frame_number, const PStatFrameData &frame_data);
Thread *get_thread() const;
INLINE int get_index() const;
diff --git a/panda/src/pstatclient/pStatTimer.h b/panda/src/pstatclient/pStatTimer.h
index 4cfca5a7d2..b7a32f27e2 100644
--- a/panda/src/pstatclient/pStatTimer.h
+++ b/panda/src/pstatclient/pStatTimer.h
@@ -27,7 +27,7 @@ class Thread;
* and when the PStatTimer variable goes out of scope (for instance, at the
* end of the function), it will automatically stop the Collector.
*/
-class PStatTimer {
+class EXPCL_PANDA_PSTATCLIENT PStatTimer {
public:
#ifdef DO_PSTATS
INLINE PStatTimer(PStatCollector &collector);
diff --git a/panda/src/putil/clockObject.I b/panda/src/putil/clockObject.I
index bf355cf736..404aa82aca 100644
--- a/panda/src/putil/clockObject.I
+++ b/panda/src/putil/clockObject.I
@@ -110,7 +110,7 @@ INLINE double ClockObject::
get_dt(Thread *current_thread) const {
CDReader cdata(_cycler, current_thread);
if (_max_dt > 0.0) {
- return std::min(_max_dt, cdata->_dt);
+ return (std::min)(_max_dt, cdata->_dt);
}
return cdata->_dt;
}
diff --git a/panda/src/putil/clockObject.cxx b/panda/src/putil/clockObject.cxx
index 0eb72647e4..d275390258 100644
--- a/panda/src/putil/clockObject.cxx
+++ b/panda/src/putil/clockObject.cxx
@@ -32,7 +32,7 @@ TypeHandle ClockObject::_type_handle;
*
*/
ClockObject::
-ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) {
+ClockObject(Mode mode) : _mode(mode), _ticks(get_class_type()) {
_true_clock = TrueClock::get_global_ptr();
_start_short_time = _true_clock->get_short_time();
diff --git a/panda/src/text/geomTextGlyph.cxx b/panda/src/text/geomTextGlyph.cxx
index ecd1a2173c..87751532f9 100644
--- a/panda/src/text/geomTextGlyph.cxx
+++ b/panda/src/text/geomTextGlyph.cxx
@@ -67,15 +67,6 @@ GeomTextGlyph(const Geom ©, const TextGlyph *glyph) :
}
}
-/**
- *
- */
-void GeomTextGlyph::
-operator = (const GeomTextGlyph ©) {
- Geom::operator = (copy);
- _glyphs = copy._glyphs;
-}
-
/**
*
*/
diff --git a/panda/src/text/geomTextGlyph.h b/panda/src/text/geomTextGlyph.h
index 7e697c1405..e9fa1245ae 100644
--- a/panda/src/text/geomTextGlyph.h
+++ b/panda/src/text/geomTextGlyph.h
@@ -30,10 +30,11 @@ public:
GeomTextGlyph(const GeomVertexData *data);
GeomTextGlyph(const GeomTextGlyph ©);
GeomTextGlyph(const Geom ©, const TextGlyph *glyph);
- void operator = (const GeomTextGlyph ©);
virtual ~GeomTextGlyph();
ALLOC_DELETED_CHAIN(GeomTextGlyph);
+ void operator = (const GeomTextGlyph ©) = delete;
+
virtual Geom *make_copy() const;
virtual bool copy_primitives_from(const Geom *other);
void count_geom(const Geom *other);
diff --git a/panda/src/text/textGraphic.h b/panda/src/text/textGraphic.h
index d8a3e1a476..9d6115ef5e 100644
--- a/panda/src/text/textGraphic.h
+++ b/panda/src/text/textGraphic.h
@@ -17,6 +17,7 @@
#include "pandabase.h"
#include "config_text.h"
+#include "memoryBase.h"
#include "nodePath.h"
/**
@@ -34,7 +35,7 @@
* within this rectangle, but if it does not, it may visually overlap with
* nearby text.
*/
-class EXPCL_PANDA_TEXT TextGraphic {
+class EXPCL_PANDA_TEXT TextGraphic : public MemoryBase {
PUBLISHED:
INLINE TextGraphic();
INLINE explicit TextGraphic(const NodePath &model, const LVecBase4 &frame);
diff --git a/panda/src/text/textProperties.h b/panda/src/text/textProperties.h
index aca592e30b..cc6c0b4992 100644
--- a/panda/src/text/textProperties.h
+++ b/panda/src/text/textProperties.h
@@ -18,6 +18,7 @@
#include "config_text.h"
#include "luse.h"
+#include "memoryBase.h"
#include "textFont.h"
#include "pointerTo.h"
#include "renderState.h"
@@ -38,7 +39,7 @@
* the string; each nested TextProperties structure modifies the appearance of
* subsequent text within the block.
*/
-class EXPCL_PANDA_TEXT TextProperties {
+class EXPCL_PANDA_TEXT TextProperties : public MemoryBase {
PUBLISHED:
enum Alignment {
A_left,
diff --git a/panda/src/tinydisplay/tinyGraphicsBuffer.cxx b/panda/src/tinydisplay/tinyGraphicsBuffer.cxx
index ea8e8241a6..47c0327f1b 100644
--- a/panda/src/tinydisplay/tinyGraphicsBuffer.cxx
+++ b/panda/src/tinydisplay/tinyGraphicsBuffer.cxx
@@ -89,6 +89,15 @@ end_frame(FrameMode mode, Thread *current_thread) {
}
}
+/**
+ *
+ */
+void TinyGraphicsBuffer::
+set_size(int x, int y) {
+ GraphicsBuffer::set_size(x, y);
+ create_frame_buffer();
+}
+
/**
* Closes the buffer right now. Called from the buffer thread.
*/
diff --git a/panda/src/tinydisplay/tinyGraphicsBuffer.h b/panda/src/tinydisplay/tinyGraphicsBuffer.h
index 79c6e8c5e5..e1fe665959 100644
--- a/panda/src/tinydisplay/tinyGraphicsBuffer.h
+++ b/panda/src/tinydisplay/tinyGraphicsBuffer.h
@@ -35,6 +35,8 @@ public:
virtual bool begin_frame(FrameMode mode, Thread *current_thread);
virtual void end_frame(FrameMode mode, Thread *current_thread);
+ virtual void set_size(int x, int y);
+
INLINE ZBuffer *get_frame_buffer();
protected:
diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
index 8442cae415..0e2af03bdd 100644
--- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
+++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
@@ -3040,6 +3040,15 @@ get_color_blend_op(ColorBlendAttrib::Operand operand) {
case ColorBlendAttrib::O_incoming_color_saturate:
return 1;
+ case ColorBlendAttrib::O_incoming1_color:
+ return 1;
+ case ColorBlendAttrib::O_one_minus_incoming1_color:
+ return 0;
+ case ColorBlendAttrib::O_incoming1_alpha:
+ return 1;
+ case ColorBlendAttrib::O_one_minus_incoming1_alpha:
+ return 0;
+
case ColorBlendAttrib::O_color_scale:
return 10;
case ColorBlendAttrib::O_one_minus_color_scale:
diff --git a/panda/src/tinydisplay/ztriangle.h b/panda/src/tinydisplay/ztriangle.h
index 72f3c1ae11..891074f68a 100644
--- a/panda/src/tinydisplay/ztriangle.h
+++ b/panda/src/tinydisplay/ztriangle.h
@@ -351,7 +351,7 @@
unsigned int z,zz;
#endif
#ifdef INTERP_RGB
- unsigned int or1,og1,ob1,oa1;
+ UNUSED unsigned int or1,og1,ob1,oa1;
#endif
#ifdef INTERP_ST
unsigned int s,t;
diff --git a/panda/src/vision/CMakeLists.txt b/panda/src/vision/CMakeLists.txt
index 284e764807..c1505ad950 100644
--- a/panda/src/vision/CMakeLists.txt
+++ b/panda/src/vision/CMakeLists.txt
@@ -43,6 +43,12 @@ if(HAVE_FFMPEG)
target_compile_definitions(p3vision PRIVATE HAVE_FFMPEG)
endif()
+if(HAVE_ARTOOLKIT AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Do not re-export symbols from these libraries.
+ target_link_options(p3vision PRIVATE "LINKER:--exclude-libs,libAR.a")
+ target_link_options(p3vision PRIVATE "LINKER:--exclude-libs,libARMulti.a")
+endif()
+
install(TARGETS p3vision
EXPORT Vision COMPONENT Vision
DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/panda/src/vision/webcamVideoCursorV4L.cxx b/panda/src/vision/webcamVideoCursorV4L.cxx
index 197bda3228..1939b16912 100644
--- a/panda/src/vision/webcamVideoCursorV4L.cxx
+++ b/panda/src/vision/webcamVideoCursorV4L.cxx
@@ -217,7 +217,7 @@ WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) {
int mode = O_RDWR;
if (!v4l_blocking) {
- mode = O_NONBLOCK;
+ mode |= O_NONBLOCK;
}
_fd = open(src->_device.c_str(), mode);
diff --git a/panda/src/vrpn/CMakeLists.txt b/panda/src/vrpn/CMakeLists.txt
index 11e5a5fc61..47010a9d79 100644
--- a/panda/src/vrpn/CMakeLists.txt
+++ b/panda/src/vrpn/CMakeLists.txt
@@ -37,6 +37,12 @@ target_interrogate(p3vrpn ALL)
set_target_properties(p3vrpn PROPERTIES CXX_EXCEPTIONS ON)
+if(HAVE_VRPN AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Do not re-export symbols from these libraries.
+ target_link_options(p3vrpn PRIVATE "LINKER:--exclude-libs,libvrpn.a")
+ target_link_options(p3vrpn PRIVATE "LINKER:--exclude-libs,libquat.a")
+endif()
+
install(TARGETS p3vrpn
EXPORT VRPN COMPONENT VRPN
DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/panda/src/wgldisplay/wglGraphicsBuffer.cxx b/panda/src/wgldisplay/wglGraphicsBuffer.cxx
index aacedf0ae3..207fa7eeaf 100644
--- a/panda/src/wgldisplay/wglGraphicsBuffer.cxx
+++ b/panda/src/wgldisplay/wglGraphicsBuffer.cxx
@@ -157,7 +157,7 @@ bind_texture_to_pbuffer() {
if (tex_index >= 0) {
const RenderTexture &rt = cdata->_textures[tex_index];
Texture *tex = rt._texture;
- if ((_pbuffer_bound != 0)&&(_pbuffer_bound != tex)) {
+ if (_pbuffer_bound != nullptr && _pbuffer_bound != tex) {
_pbuffer_bound->release(wglgsg->get_prepared_objects());
_pbuffer_bound = 0;
}
@@ -188,7 +188,7 @@ bind_texture_to_pbuffer() {
}
_pbuffer_bound = tex;
} else {
- if (_pbuffer_bound != 0) {
+ if (_pbuffer_bound != nullptr) {
_pbuffer_bound->release(wglgsg->get_prepared_objects());
_pbuffer_bound = 0;
}
@@ -292,7 +292,7 @@ open_buffer() {
// GSG creationinitialization.
wglGraphicsStateGuardian *wglgsg;
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
// There is no old gsg. Create a new one.
wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr);
wglgsg->choose_pixel_format(_fb_properties, true);
@@ -355,16 +355,16 @@ open_buffer() {
*/
void wglGraphicsBuffer::
release_pbuffer() {
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
return;
}
wglGraphicsStateGuardian *wglgsg;
DCAST_INTO_V(wglgsg, _gsg);
- if (_pbuffer_bound != 0) {
+ if (_pbuffer_bound != nullptr) {
_pbuffer_bound->release(wglgsg->get_prepared_objects());
- _pbuffer_bound = 0;
+ _pbuffer_bound.clear();
}
wglGraphicsPipe::wgl_make_current(0, 0, nullptr);
if (_pbuffer_dc) {
@@ -420,7 +420,7 @@ rebuild_bitplanes() {
// Determine what pbuffer attributes are needed for currently-applicable
// textures.
- 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());
diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx
index 1c4fbe9773..c79ea4eb9f 100644
--- a/panda/src/wgldisplay/wglGraphicsWindow.cxx
+++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx
@@ -122,21 +122,6 @@ end_frame(FrameMode mode, Thread *current_thread) {
}
}
-/**
- * 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 wglGraphicsWindow::
-begin_flip() {
-}
-
/**
* 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
@@ -215,7 +200,7 @@ open_window() {
// GSG creationinitialization.
wglGraphicsStateGuardian *wglgsg;
- if (_gsg == 0) {
+ if (_gsg == nullptr) {
// There is no old gsg. Create a new one.
wglgsg = new wglGraphicsStateGuardian(_engine, _pipe, nullptr);
wglgsg->choose_pixel_format(_fb_properties, false);
diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h
index 06a74aa501..66dc2d5878 100644
--- a/panda/src/wgldisplay/wglGraphicsWindow.h
+++ b/panda/src/wgldisplay/wglGraphicsWindow.h
@@ -34,7 +34,6 @@ public:
virtual bool begin_frame(FrameMode mode, Thread *current_thread);
virtual void end_frame(FrameMode mode, Thread *current_thread);
- virtual void begin_flip();
virtual void ready_flip();
virtual void end_flip();
diff --git a/panda/src/windisplay/winGraphicsPipe.cxx b/panda/src/windisplay/winGraphicsPipe.cxx
index 4815262a77..28787c43b3 100644
--- a/panda/src/windisplay/winGraphicsPipe.cxx
+++ b/panda/src/windisplay/winGraphicsPipe.cxx
@@ -247,6 +247,71 @@ WinGraphicsPipe() {
}
}
+ if (windisplay_cat.is_debug()) {
+ windisplay_cat.debug()
+ << "Detected display devices:\n";
+
+ DISPLAY_DEVICEA device;
+ device.cb = sizeof(device);
+ for (DWORD devnum = 0; EnumDisplayDevicesA(nullptr, devnum, &device, 0); ++devnum) {
+ std::ostream &out = windisplay_cat.debug();
+ out << " " << device.DeviceName << " [" << device.DeviceString << "]";
+ if (device.StateFlags & DISPLAY_DEVICE_ACTIVE) {
+ out << " (active)";
+ }
+ if (device.StateFlags & DISPLAY_DEVICE_MULTI_DRIVER) {
+ out << " (multi-driver)";
+ }
+ if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
+ out << " (primary)";
+ }
+ if (device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) {
+ out << " (mirroring)";
+ }
+ if (device.StateFlags & DISPLAY_DEVICE_REMOVABLE) {
+ out << " (removable)";
+ }
+ out << "\n";
+ }
+
+ int nmonitor = GetSystemMetrics(SM_CMONITORS);
+ windisplay_cat.debug()
+ << "Detected " << nmonitor << " monitors, "
+ << (GetSystemMetrics(SM_SAMEDISPLAYFORMAT) != 0 ? "" : "NOT ")
+ << "sharing same display format:\n";
+
+ EnumDisplayMonitors(
+ nullptr,
+ nullptr,
+ [](HMONITOR monitor, HDC dc, LPRECT rect, LPARAM param) -> BOOL {
+ MONITORINFOEXA info;
+ info.cbSize = sizeof(info);
+ if (GetMonitorInfoA(monitor, &info)) {
+ std::ostream &out = windisplay_cat.debug() << " ";
+
+ DISPLAY_DEVICEA device;
+ device.cb = sizeof(device);
+ device.StateFlags = 0;
+ if (EnumDisplayDevicesA(info.szDevice, 0, &device, 0)) {
+ out << device.DeviceName << " [" << device.DeviceString << "]";
+ }
+ else {
+ out << info.szDevice << " (device enum failed)";
+ }
+
+ if (info.dwFlags & MONITORINFOF_PRIMARY) {
+ out << " (primary)";
+ }
+ if (info.rcWork.left != 0 || info.rcWork.top != 0) {
+ out << " (at " << info.rcWork.left << "x" << info.rcWork.top << ")";
+ }
+ out << "\n";
+ }
+ return TRUE;
+ },
+ 0);
+ }
+
#ifdef HAVE_DX9
// Use D3D to get display info. This is disabled by default as it is slow.
if (request_dxdisplay_information) {
diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx
index ceef979b75..93b4ba989a 100644
--- a/panda/src/windisplay/winGraphicsWindow.cxx
+++ b/panda/src/windisplay/winGraphicsWindow.cxx
@@ -218,21 +218,6 @@ close_ime() {
return;
}
-/**
- * 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 WinGraphicsWindow::
-begin_flip() {
-}
-
/**
* Do whatever processing is necessary to ensure that the window responds to
* user events. Also, honor any requests recently made via
diff --git a/panda/src/windisplay/winGraphicsWindow.h b/panda/src/windisplay/winGraphicsWindow.h
index ca33615219..d262249349 100644
--- a/panda/src/windisplay/winGraphicsWindow.h
+++ b/panda/src/windisplay/winGraphicsWindow.h
@@ -77,8 +77,6 @@ public:
virtual void close_ime();
- virtual void begin_flip();
-
virtual void process_events();
virtual void set_properties_now(WindowProperties &properties);
void receive_windows_message(unsigned int msg, int wparam, int lparam);
diff --git a/panda/src/x11display/x11GraphicsPipe.cxx b/panda/src/x11display/x11GraphicsPipe.cxx
index 6d42d399df..fc76161018 100644
--- a/panda/src/x11display/x11GraphicsPipe.cxx
+++ b/panda/src/x11display/x11GraphicsPipe.cxx
@@ -35,8 +35,8 @@ LightReMutex x11GraphicsPipe::_x_mutex;
*/
x11GraphicsPipe::
x11GraphicsPipe(const std::string &display) :
- _have_xrandr(false),
_xcursor_size(-1),
+ _have_xrandr(false),
_XF86DGADirectVideo(nullptr) {
std::string display_spec = display;
diff --git a/pandatool/src/assimp/CMakeLists.txt b/pandatool/src/assimp/CMakeLists.txt
index 536d15ec39..5b88e0eff6 100644
--- a/pandatool/src/assimp/CMakeLists.txt
+++ b/pandatool/src/assimp/CMakeLists.txt
@@ -26,6 +26,12 @@ set_target_properties(p3assimp PROPERTIES DEFINE_SYMBOL BUILDING_ASSIMP)
target_link_libraries(p3assimp PRIVATE p3pandatoolbase)
target_link_libraries(p3assimp PUBLIC PKG::ASSIMP)
+if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+ # Do not re-export symbols from these libraries.
+ target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libassimp.a")
+ target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libIrrXML.a")
+endif()
+
if(BUILD_SHARED_LIBS)
# We can't install this if we're doing a static build, because it depends on
# a static library that isn't installed.
diff --git a/pandatool/src/deploy-stub/CMakeLists.txt b/pandatool/src/deploy-stub/CMakeLists.txt
index 851bc9ac4c..1c7a37017f 100644
--- a/pandatool/src/deploy-stub/CMakeLists.txt
+++ b/pandatool/src/deploy-stub/CMakeLists.txt
@@ -17,7 +17,7 @@ elseif(IS_LINUX OR IS_FREEBSD)
set_target_properties(deploy-stub PROPERTIES
INSTALL_RPATH "$ORIGIN"
BUILD_WITH_INSTALL_RPATH ON)
- target_link_options(deploy-stub PRIVATE -Wl,-z,origin -rdynamic)
+ target_link_options(deploy-stub PRIVATE -Wl,--disable-new-dtags -Wl,-z,origin -rdynamic)
endif()
target_link_libraries(deploy-stub Python::Python)
diff --git a/pandatool/src/deploy-stub/NativeInvocationHandler.java b/pandatool/src/deploy-stub/NativeInvocationHandler.java
new file mode 100644
index 0000000000..2a10837328
--- /dev/null
+++ b/pandatool/src/deploy-stub/NativeInvocationHandler.java
@@ -0,0 +1,25 @@
+package org.jnius;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * Special support for pyjnius.
+ */
+public class NativeInvocationHandler implements InvocationHandler {
+ private long _ptr;
+
+ public NativeInvocationHandler(long ptr) {
+ _ptr = ptr;
+ }
+
+ public long getPythonObjectPointer() {
+ return _ptr;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ return invoke0(proxy, method, args);
+ }
+
+ native Object invoke0(Object proxy, Method method, Object[] args);
+}
diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c
index f1b5e10852..3d3ba9c9c1 100644
--- a/pandatool/src/deploy-stub/deploy-stub.c
+++ b/pandatool/src/deploy-stub/deploy-stub.c
@@ -388,6 +388,10 @@ int Py_FrozenMain(int argc, char **argv)
Py_NoSiteFlag = 0;
Py_NoUserSiteDirectory = 1;
+#if PY_VERSION_HEX >= 0x03020000
+ Py_OptimizeFlag = 2;
+#endif
+
#ifndef NDEBUG
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
inspect = 1;
diff --git a/pandatool/src/egg-qtess/isoPlacer.cxx b/pandatool/src/egg-qtess/isoPlacer.cxx
index f37588f463..0f7a6a3fc1 100644
--- a/pandatool/src/egg-qtess/isoPlacer.cxx
+++ b/pandatool/src/egg-qtess/isoPlacer.cxx
@@ -106,8 +106,6 @@ get_scores(int subdiv, int across, double ratio,
*/
void IsoPlacer::
place(int count, pvector &iso_points) {
- int i;
-
// Count up the average curvature.
/*
double avg_curve = 0.0;
diff --git a/pandatool/src/gtk-stats/CMakeLists.txt b/pandatool/src/gtk-stats/CMakeLists.txt
index b78ebf2e80..68a10d392f 100644
--- a/pandatool/src/gtk-stats/CMakeLists.txt
+++ b/pandatool/src/gtk-stats/CMakeLists.txt
@@ -1,9 +1,10 @@
-if(NOT HAVE_GTK2 OR NOT HAVE_NET)
+if(NOT HAVE_GTK3 OR NOT HAVE_NET)
return()
endif()
set(GTKSTATS_HEADERS
gtkStatsChartMenu.h
+ gtkStatsFlameGraph.h
gtkStatsGraph.h
gtkStatsLabel.h
gtkStatsLabelStack.h
@@ -17,6 +18,7 @@ set(GTKSTATS_HEADERS
set(GTKSTATS_SOURCES
gtkStats.cxx
gtkStatsChartMenu.cxx
+ gtkStatsFlameGraph.cxx
gtkStatsGraph.cxx
gtkStatsLabel.cxx
gtkStatsLabelStack.cxx
@@ -24,11 +26,12 @@ set(GTKSTATS_SOURCES
gtkStatsPianoRoll.cxx
gtkStatsServer.cxx
gtkStatsStripChart.cxx
+ gtkStatsTimeline.cxx
)
composite_sources(gtk-stats GTKSTATS_SOURCES)
add_executable(gtk-stats ${GTKSTATS_HEADERS} ${GTKSTATS_SOURCES})
-target_link_libraries(gtk-stats p3progbase p3pstatserver PKG::GTK2)
+target_link_libraries(gtk-stats p3progbase p3pstatserver PKG::GTK3)
# This program is NOT actually called gtk-stats. It's pstats-gtk on Win32 and
# pstats everywhere else (as the Win32 GUI is not built).
diff --git a/pandatool/src/gtk-stats/gtkStats.cxx b/pandatool/src/gtk-stats/gtkStats.cxx
index e6889ff23b..959b83a6a4 100644
--- a/pandatool/src/gtk-stats/gtkStats.cxx
+++ b/pandatool/src/gtk-stats/gtkStats.cxx
@@ -44,7 +44,7 @@ timer(gpointer data) {
// are getting starved and falling behind, so that the user still gets a
// chance to see *something* happen onscreen, even if it's just
// increasingly old data.
- gdk_window_process_all_updates();
+ //gdk_window_process_all_updates();
}
return TRUE;
diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx
index c7f8b3f4be..e610eb710f 100644
--- a/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.cxx
@@ -88,7 +88,25 @@ do_update() {
// Now rebuild the menu with the new set of entries.
- // The menu item(s) for the thread's frame time goes first.
+ if (_thread_index == 0) {
+ // Timeline goes first.
+ GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_timeline, false);
+ const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Timeline");
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
+
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+
+ GtkWidget *sep = gtk_separator_menu_item_new();
+ gtk_widget_show(sep);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
+ }
+
+ // The menu item(s) for the thread's frame time goes second.
add_view(_menu, view.get_top_level(), false);
bool needs_separator = true;
@@ -104,9 +122,9 @@ do_update() {
// We put a separator between the above frame collector and the first
// level collector.
if (needs_separator) {
- GtkWidget *sep = gtk_separator_menu_item_new();
- gtk_widget_show(sep);
- gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
+ GtkWidget *sep = gtk_separator_menu_item_new();
+ gtk_widget_show(sep);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
needs_separator = false;
}
@@ -116,20 +134,23 @@ do_update() {
}
}
- // Also a menu item for a piano roll (following a separator).
+ // Also menu item for piano roll (following a separator).
GtkWidget *sep = gtk_separator_menu_item_new();
gtk_widget_show(sep);
gtk_menu_shell_append(GTK_MENU_SHELL(_menu), sep);
- GtkStatsMonitor::MenuDef smd(_thread_index, -1, false);
- const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
+ {
+ GtkStatsMonitor::MenuDef smd(_thread_index, -1, GtkStatsMonitor::CT_piano_roll, false);
+ const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
- GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll");
- gtk_widget_show(menu_item);
- gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Piano Roll");
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu), menu_item);
- g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
- G_CALLBACK(handle_menu), (void *)(const void *)menu_def);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
}
/**
@@ -138,66 +159,89 @@ do_update() {
*/
void GtkStatsChartMenu::
add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
- bool show_level) {
+ bool show_level) {
int collector = view_level->get_collector();
const PStatClientData *client_data = _monitor->get_client_data();
std::string collector_name = client_data->get_collector_name(collector);
- GtkStatsMonitor::MenuDef smd(_thread_index, collector, show_level);
- const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
-
- GtkWidget *menu_item = gtk_menu_item_new_with_label(collector_name.c_str());
- gtk_widget_show(menu_item);
- gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), menu_item);
-
- g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
- G_CALLBACK(handle_menu), (void *)(const void *)menu_def);
-
int num_children = view_level->get_num_children();
- if (num_children > 1) {
- // If the collector has more than one child, add a menu entry to go
- // directly to each of its children.
- std::string submenu_name = collector_name + " components";
+ if (show_level && num_children == 0) {
+ // For a level collector without children, no point in making a submenu.
+ GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level);
+ const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
- GtkWidget *submenu_item = gtk_menu_item_new_with_label(submenu_name.c_str());
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(collector_name.c_str());
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), menu_item);
+
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ return;
+ }
+
+ GtkWidget *menu;
+ if (!show_level && collector == 0 && num_children == 0) {
+ // Root collector without children, just add the options directly to the
+ // parent menu.
+ menu = parent_menu;
+ }
+ else {
+ // Create a submenu.
+ GtkWidget *submenu_item = gtk_menu_item_new_with_label(collector_name.c_str());
gtk_widget_show(submenu_item);
gtk_menu_shell_append(GTK_MENU_SHELL(parent_menu), submenu_item);
- GtkWidget *submenu = gtk_menu_new();
- gtk_widget_show(submenu);
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_item), submenu);
+ menu = gtk_menu_new();
+ gtk_widget_show(menu);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_item), menu);
+ }
+
+ {
+ GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level);
+ const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ if (!show_level) {
+ if (collector == 0 && num_children == 0) {
+ collector = -1;
+ }
+
+ GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_flame_graph, show_level);
+ const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
+ gtk_widget_show(menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ if (num_children > 0) {
+ GtkWidget *sep = gtk_separator_menu_item_new();
+ gtk_widget_show(sep);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
// Reverse the order since the menus are listed from the top down; we want
// to be visually consistent with the graphs, which list these labels from
// the bottom up.
for (int c = num_children - 1; c >= 0; c--) {
- add_view(submenu, view_level->get_child(c), show_level);
+ add_view(menu, view_level->get_child(c), show_level);
}
}
}
-/**
- * Callback when a menu item is selected.
- */
-void GtkStatsChartMenu::
-handle_menu(gpointer data) {
- const GtkStatsMonitor::MenuDef *menu_def = (GtkStatsMonitor::MenuDef *)data;
- GtkStatsMonitor *monitor = menu_def->_monitor;
-
- if (monitor == nullptr) {
- return;
- }
-
- if (menu_def->_collector_index < 0) {
- monitor->open_piano_roll(menu_def->_thread_index);
- } else {
- monitor->open_strip_chart(menu_def->_thread_index,
- menu_def->_collector_index,
- menu_def->_show_level);
- }
-}
-
/**
* Removes a previous menu child from the menu.
*/
diff --git a/pandatool/src/gtk-stats/gtkStatsChartMenu.h b/pandatool/src/gtk-stats/gtkStatsChartMenu.h
index 6206d951ec..41e21aa3ee 100644
--- a/pandatool/src/gtk-stats/gtkStatsChartMenu.h
+++ b/pandatool/src/gtk-stats/gtkStatsChartMenu.h
@@ -40,7 +40,6 @@ private:
void add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
bool show_level);
- static void handle_menu(gpointer data);
static void remove_menu_child(GtkWidget *widget, gpointer data);
GtkStatsMonitor *_monitor;
diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx
new file mode 100644
index 0000000000..d5ecdb0d29
--- /dev/null
+++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.cxx
@@ -0,0 +1,656 @@
+/**
+ * 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 gtkStatsFlameGraph.cxx
+ * @author rdb
+ * @date 2022-02-02
+ */
+
+#include "gtkStatsFlameGraph.h"
+#include "gtkStatsLabel.h"
+#include "gtkStatsMonitor.h"
+#include "pStatCollectorDef.h"
+
+static const int default_flame_graph_width = 800;
+static const int default_flame_graph_height = 150;
+
+/**
+ *
+ */
+GtkStatsFlameGraph::
+GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
+ int collector_index) :
+ PStatFlameGraph(monitor, thread_index, collector_index, 0, 0),
+ GtkStatsGraph(monitor, false)
+{
+ // Let's show the units on the guide bar labels. There's room.
+ set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
+
+ // Put some stuff on top of the graph.
+ _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox,
+ FALSE, FALSE, 0);
+
+ _average_check_box = gtk_check_button_new_with_label("Average");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_average_check_box), TRUE);
+ g_signal_connect(G_OBJECT(_average_check_box), "toggled",
+ G_CALLBACK(toggled_callback), this);
+
+ // Add a DrawingArea widget on top of the graph, to display all of the scale
+ // units.
+ _scale_area = gtk_drawing_area_new();
+ g_signal_connect(G_OBJECT(_scale_area), "draw", G_CALLBACK(draw_callback), this);
+
+ _total_label = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(_top_hbox), _average_check_box, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0);
+
+ gtk_widget_set_size_request(_graph_window,
+ default_flame_graph_width * monitor->get_resolution() / 96,
+ default_flame_graph_height * monitor->get_resolution() / 96);
+
+ gtk_widget_show_all(_window);
+ gtk_widget_show(_window);
+
+ // Allow the window to be resized as small as the user likes. We have to do
+ // this after the window has been shown; otherwise, it will affect the
+ // window's initial size.
+ gtk_widget_set_size_request(_window, 0, 0);
+
+ clear_region();
+
+ if (get_average_mode()) {
+ start_animation();
+ }
+}
+
+/**
+ *
+ */
+GtkStatsFlameGraph::
+~GtkStatsFlameGraph() {
+}
+
+/**
+ * Called whenever a new Collector definition is received from the client.
+ */
+void GtkStatsFlameGraph::
+new_collector(int collector_index) {
+ GtkStatsGraph::new_collector(collector_index);
+}
+
+/**
+ * Called as each frame's data is made available. There is no guarantee the
+ * frames will arrive in order, or that all of them will arrive at all. The
+ * monitor should be prepared to accept frames received out-of-order or
+ * missing.
+ */
+void GtkStatsFlameGraph::
+new_data(int thread_index, int frame_number) {
+ if (is_title_unknown()) {
+ std::string window_title = get_title_text();
+ if (!is_title_unknown()) {
+ gtk_window_set_title(GTK_WINDOW(_window), window_title.c_str());
+ }
+ }
+
+ if (!_pause) {
+ update();
+
+ std::string text = format_number(get_horizontal_scale(), get_guide_bar_units(), get_guide_bar_unit_name());
+ if (_net_value_text != text) {
+ _net_value_text = text;
+ gtk_label_set_text(GTK_LABEL(_total_label), _net_value_text.c_str());
+ }
+ }
+}
+
+/**
+ * Called when it is necessary to redraw the entire graph.
+ */
+void GtkStatsFlameGraph::
+force_redraw() {
+ if (_cr) {
+ PStatFlameGraph::force_redraw();
+ }
+}
+
+/**
+ * Called when the user has resized the window, forcing a resize of the graph.
+ */
+void GtkStatsFlameGraph::
+changed_graph_size(int graph_xsize, int graph_ysize) {
+ PStatFlameGraph::changed_size(graph_xsize, graph_ysize);
+}
+
+/**
+ * Called when the user selects a new time units from the monitor pulldown
+ * menu, this should adjust the units for the graph to the indicated mask if
+ * it is a time-based graph.
+ */
+void GtkStatsFlameGraph::
+set_time_units(int unit_mask) {
+ int old_unit_mask = get_guide_bar_units();
+ if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) {
+ unit_mask = unit_mask & (GBU_hz | GBU_ms);
+ unit_mask |= (old_unit_mask & GBU_show_units);
+ set_guide_bar_units(unit_mask);
+
+ gtk_widget_queue_draw(_scale_area);
+ }
+}
+
+/**
+ * Called when the user single-clicks on a label.
+ */
+void GtkStatsFlameGraph::
+on_click_label(int collector_index) {
+ set_collector_index(collector_index);
+}
+
+/**
+ * Called when the user hovers the mouse over a label.
+ */
+void GtkStatsFlameGraph::
+on_enter_label(int collector_index) {
+ if (collector_index != _highlighted_index) {
+ _highlighted_index = collector_index;
+
+ if (!get_average_mode()) {
+ PStatFlameGraph::force_redraw();
+ }
+ }
+}
+
+/**
+ * Called when the user's mouse cursor leaves a label.
+ */
+void GtkStatsFlameGraph::
+on_leave_label(int collector_index) {
+ if (collector_index == _highlighted_index && collector_index != -1) {
+ _highlighted_index = -1;
+
+ if (!get_average_mode()) {
+ PStatFlameGraph::force_redraw();
+ }
+ }
+}
+
+/**
+ * Calls update_guide_bars with parameters suitable to this kind of graph.
+ */
+void GtkStatsFlameGraph::
+normal_guide_bars() {
+ // We want vaguely 100 pixels between guide bars.
+ int num_bars = get_xsize() / (_pixel_scale * 25);
+
+ _guide_bars.clear();
+
+ double dist = get_horizontal_scale() / num_bars;
+
+ for (int i = 1; i < num_bars; ++i) {
+ _guide_bars.push_back(make_guide_bar(i * dist));
+ }
+
+ _guide_bars_changed = true;
+}
+
+/**
+ * Erases the chart area.
+ */
+void GtkStatsFlameGraph::
+clear_region() {
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_paint(_cr);
+}
+
+/**
+ * Erases the chart area in preparation for drawing a bunch of bars.
+ */
+void GtkStatsFlameGraph::
+begin_draw() {
+ clear_region();
+
+ // Draw in the guide bars.
+ int num_guide_bars = get_num_guide_bars();
+ for (int i = 0; i < num_guide_bars; i++) {
+ draw_guide_bar(_cr, get_guide_bar(i));
+ }
+}
+
+/**
+ * Should be overridden by the user class. Should draw a single bar at the
+ * indicated location.
+ */
+void GtkStatsFlameGraph::
+draw_bar(int depth, int from_x, int to_x, int collector_index) {
+ double bottom = get_ysize() - depth * _pixel_scale * 5;
+ double top = bottom - _pixel_scale * 5;
+
+ bool is_highlighted = collector_index == _highlighted_index;
+ cairo_set_source(_cr, get_collector_pattern(collector_index, is_highlighted));
+
+ if (to_x < from_x + 3) {
+ // It's just a tiny sliver. This is a more reliable way to draw it.
+ cairo_rectangle(_cr, from_x, top, to_x - from_x, bottom - top);
+ cairo_fill(_cr);
+ }
+ else {
+ double radius = std::min((double)_pixel_scale, (to_x - from_x) / 2.0);
+ cairo_new_sub_path(_cr);
+ cairo_arc(_cr, to_x - radius, top + radius, radius, -0.5 * M_PI, 0.0);
+ cairo_arc(_cr, to_x - radius, bottom - radius, radius, 0.0, 0.5 * M_PI);
+ cairo_arc(_cr, from_x + radius, bottom - radius, radius, 0.5 * M_PI, M_PI);
+ cairo_arc(_cr, from_x + radius, top + radius, radius, M_PI, 1.5 * M_PI);
+ cairo_close_path(_cr);
+ cairo_fill(_cr);
+
+ if ((to_x - from_x) >= _pixel_scale * 4) {
+ // Only bother drawing the text if we've got some space to draw on.
+ int left = std::max(from_x, 0) + _pixel_scale / 2;
+ int right = std::min(to_x, get_xsize()) - _pixel_scale / 2;
+
+ const PStatClientData *client_data = GtkStatsGraph::_monitor->get_client_data();
+ const std::string &name = client_data->get_collector_name(collector_index);
+
+ // Choose a suitable foreground color.
+ LRGBColor fg = get_collector_text_color(collector_index, is_highlighted);
+ cairo_set_source_rgb(_cr, fg[0], fg[1], fg[2]);
+
+ PangoLayout *layout = gtk_widget_create_pango_layout(_graph_window, name.c_str());
+ pango_layout_set_attributes(layout, _pango_attrs);
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ pango_layout_set_width(layout, (right - left) * PANGO_SCALE);
+ pango_layout_set_height(layout, -1);
+
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+
+ // Center the text vertically in the bar.
+ cairo_move_to(_cr, left, top + (bottom - top - height) / 2);
+ pango_cairo_show_layout(_cr, layout);
+
+ g_object_unref(layout);
+ }
+ }
+}
+
+/**
+ * Called after all the bars have been drawn, this triggers a refresh event to
+ * draw it to the window.
+ */
+void GtkStatsFlameGraph::
+end_draw() {
+ gtk_widget_queue_draw(_graph_window);
+}
+
+/**
+ * Called at the end of the draw cycle.
+ */
+void GtkStatsFlameGraph::
+idle() {
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool GtkStatsFlameGraph::
+animate(double time, double dt) {
+ return PStatFlameGraph::animate(time, dt);
+}
+
+/**
+ * This is called during the servicing of the draw event; it gives a derived
+ * class opportunity to do some further painting into the graph window.
+ */
+void GtkStatsFlameGraph::
+additional_graph_window_paint(cairo_t *cr) {
+ int num_user_guide_bars = get_num_user_guide_bars();
+ for (int i = 0; i < num_user_guide_bars; i++) {
+ draw_guide_bar(cr, get_user_guide_bar(i));
+ }
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsFlameGraph::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return get_bar_tooltip(pixel_to_depth(mouse_y), mouse_x);
+}
+
+/**
+ * Based on the mouse position within the window's client area, look for
+ * draggable things the mouse might be hovering over and return the
+ * apprioprate DragMode enum or DM_none if nothing is indicated.
+ */
+GtkStatsGraph::DragMode GtkStatsFlameGraph::
+consider_drag_start(int graph_x, int graph_y) {
+ if (graph_y >= 0 && graph_y < get_ysize()) {
+ if (graph_x >= 0 && graph_x < get_xsize()) {
+ // See if the mouse is over a user-defined guide bar.
+ int x = graph_x;
+ double from_height = pixel_to_height(x - 2);
+ double to_height = pixel_to_height(x + 2);
+ _drag_guide_bar = find_user_guide_bar(from_height, to_height);
+ if (_drag_guide_bar >= 0) {
+ return DM_guide_bar;
+ }
+
+ } else {
+ // The mouse is left or right of the graph; maybe create a new guide
+ // bar.
+ return DM_new_guide_bar;
+ }
+ }
+
+ return DM_none;
+}
+
+/**
+ * Called when the mouse button is depressed within the graph window.
+ */
+gboolean GtkStatsFlameGraph::
+handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
+ if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ int depth = pixel_to_depth(graph_y);
+ int collector_index = get_bar_collector(depth, graph_x);
+ if (button == 3) {
+ if (collector_index >= 0) {
+ GtkWidget *menu = gtk_menu_new();
+ _popup_index = collector_index;
+
+ std::string label = get_bar_tooltip(depth, graph_x);
+ if (!label.empty()) {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ }
+
+ {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Set as Focus");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+ if (collector_index == 0 && get_collector_index() == 0) {
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ } else {
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(+[] (GtkWidget *widget, gpointer data) {
+ GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
+ self->set_collector_index(self->_popup_index);
+ }),
+ this);
+ }
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ get_thread_index(), collector_index,
+ GtkStatsMonitor::CT_strip_chart, false,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ get_thread_index(), collector_index,
+ GtkStatsMonitor::CT_flame_graph,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ gtk_widget_show_all(menu);
+ gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if (double_click && button == 1) {
+ // Double-clicking on a color bar in the graph will zoom the graph into
+ // that collector.
+ set_collector_index(collector_index);
+ return TRUE;
+ }
+ }
+
+ if (_potential_drag_mode == DM_none) {
+ set_drag_mode(DM_scale);
+ _drag_scale_start = pixel_to_height(graph_x);
+ // SetCapture(_graph_window);
+ return TRUE;
+
+ } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
+ set_drag_mode(DM_guide_bar);
+ _drag_start_x = graph_x;
+ // SetCapture(_graph_window);
+ return TRUE;
+ }
+
+ return GtkStatsGraph::handle_button_press(graph_x, graph_y,
+ double_click, button);
+}
+
+/**
+ * Called when the mouse button is released within the graph window.
+ */
+gboolean GtkStatsFlameGraph::
+handle_button_release(int graph_x, int graph_y) {
+ if (_drag_mode == DM_scale) {
+ set_drag_mode(DM_none);
+ // ReleaseCapture();
+ return handle_motion(graph_x, graph_y);
+ }
+ else if (_drag_mode == DM_guide_bar) {
+ if (graph_x < 0 || graph_x >= get_xsize()) {
+ remove_user_guide_bar(_drag_guide_bar);
+ } else {
+ move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x));
+ }
+ set_drag_mode(DM_none);
+ // ReleaseCapture();
+ return handle_motion(graph_x, graph_y);
+ }
+
+ return GtkStatsGraph::handle_button_release(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse is moved within the graph window.
+ */
+gboolean GtkStatsFlameGraph::
+handle_motion(int graph_x, int graph_y) {
+ if (_drag_mode == DM_none && _potential_drag_mode == DM_none &&
+ graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ // When the mouse is over a color bar, highlight it.
+ int depth = pixel_to_depth(graph_y);
+ int collector_index = get_bar_collector(depth, graph_x);
+ on_enter_label(collector_index);
+ }
+ else {
+ // If the mouse is in some drag mode, stop highlighting.
+ _label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
+ }
+
+ if (_drag_mode == DM_new_guide_bar) {
+ // We haven't created the new guide bar yet; we won't until the mouse
+ // comes within the graph's region.
+ if (graph_x >= 0 && graph_x < get_xsize()) {
+ set_drag_mode(DM_guide_bar);
+ _drag_guide_bar = add_user_guide_bar(pixel_to_height(graph_x));
+ return TRUE;
+ }
+ }
+ else if (_drag_mode == DM_guide_bar) {
+ move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x));
+ return TRUE;
+ }
+
+ return GtkStatsGraph::handle_motion(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse has left the graph window.
+ */
+gboolean GtkStatsFlameGraph::
+handle_leave() {
+ _label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
+ return TRUE;
+}
+
+/**
+ * Converts a pixel to a depth index.
+ */
+int GtkStatsFlameGraph::
+pixel_to_depth(int y) const {
+ return (get_ysize() - 1 - y) / (_pixel_scale * 5);
+}
+
+/**
+ * Draws the line for the indicated guide bar on the graph.
+ */
+void GtkStatsFlameGraph::
+draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) {
+ int x = height_to_pixel(bar._height);
+
+ if (x > 0 && x < get_xsize() - 1) {
+ // Only draw it if it's not too close to the top.
+ switch (bar._style) {
+ case GBS_target:
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
+ break;
+
+ case GBS_user:
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
+ break;
+
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
+ break;
+ }
+ cairo_move_to(cr, x, 0);
+ cairo_line_to(cr, x, get_ysize());
+ cairo_stroke(cr);
+ }
+}
+
+/**
+ * This is called during the servicing of the draw event.
+ */
+void GtkStatsFlameGraph::
+draw_guide_labels(cairo_t *cr) {
+ int i;
+ int num_guide_bars = get_num_guide_bars();
+ for (i = 0; i < num_guide_bars; i++) {
+ draw_guide_label(cr, get_guide_bar(i));
+ }
+
+ int num_user_guide_bars = get_num_user_guide_bars();
+ for (i = 0; i < num_user_guide_bars; i++) {
+ draw_guide_label(cr, get_user_guide_bar(i));
+ }
+}
+
+/**
+ * Draws the text for the indicated guide bar label at the top of the graph.
+ */
+void GtkStatsFlameGraph::
+draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) {
+ switch (bar._style) {
+ case GBS_target:
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
+ break;
+
+ case GBS_user:
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
+ break;
+
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
+ break;
+ }
+
+ int x = height_to_pixel(bar._height);
+ const std::string &label = bar._label;
+
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str());
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+
+ if (bar._style != GBS_user) {
+ double from_height = pixel_to_height(x - width * _cr_scale);
+ double to_height = pixel_to_height(x + width * _cr_scale);
+ if (find_user_guide_bar(from_height, to_height) >= 0) {
+ // Omit the label: there's a user-defined guide bar in the same space.
+ g_object_unref(layout);
+ return;
+ }
+ }
+
+ if (x >= 0 && x < get_xsize()) {
+ // Now convert our x to a coordinate within our drawing area.
+ int junk_y;
+
+ x /= _cr_scale;
+
+ // The x coordinate comes from the graph_window.
+ gtk_widget_translate_coordinates(_graph_window, _scale_area,
+ x, 0,
+ &x, &junk_y);
+
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(_scale_area, &allocation);
+
+ int this_x = x - width / 2;
+ if (this_x >= 0 && this_x + width < allocation.width) {
+ cairo_move_to(cr, this_x, allocation.height - height);
+ pango_cairo_show_layout(cr, layout);
+ }
+ }
+
+ g_object_unref(layout);
+}
+
+/**
+ * Called when the average check box is toggled.
+ */
+void GtkStatsFlameGraph::
+toggled_callback(GtkToggleButton *button, gpointer data) {
+ GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
+
+ bool active = gtk_toggle_button_get_active(button);
+ self->set_average_mode(active);
+ if (active) {
+ self->start_animation();
+ }
+}
+
+/**
+ * Draws in the scale labels.
+ */
+gboolean GtkStatsFlameGraph::
+draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
+ GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
+ self->draw_guide_labels(cr);
+
+ return TRUE;
+}
diff --git a/pandatool/src/gtk-stats/gtkStatsFlameGraph.h b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h
new file mode 100644
index 0000000000..ad550d16ac
--- /dev/null
+++ b/pandatool/src/gtk-stats/gtkStatsFlameGraph.h
@@ -0,0 +1,84 @@
+/**
+ * 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 gtkStatsFlameGraph.h
+ * @author rdb
+ * @date 2022-02-02
+ */
+
+#ifndef GTKSTATSFLAMEGRAPH_H
+#define GTKSTATSFLAMEGRAPH_H
+
+#include "pandatoolbase.h"
+
+#include "gtkStatsGraph.h"
+#include "pStatFlameGraph.h"
+
+class GtkStatsLabel;
+
+/**
+ * A window that draws a flame chart, which shows the collectors explicitly
+ * stopping and starting, one frame at a time.
+ */
+class GtkStatsFlameGraph : public PStatFlameGraph, public GtkStatsGraph {
+public:
+ GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
+ int collector_index=-1);
+ virtual ~GtkStatsFlameGraph();
+
+ virtual void new_collector(int collector_index);
+ virtual void new_data(int thread_index, int frame_number);
+ virtual void force_redraw();
+ virtual void changed_graph_size(int graph_xsize, int graph_ysize);
+
+ virtual void set_time_units(int unit_mask);
+ virtual void on_click_label(int collector_index);
+ virtual void on_enter_label(int collector_index);
+ virtual void on_leave_label(int collector_index);
+
+protected:
+ virtual void normal_guide_bars();
+
+ void clear_region();
+ virtual void begin_draw();
+ virtual void draw_bar(int depth, int from_x, int to_x, int collector_index);
+ virtual void end_draw();
+ virtual void idle();
+
+ virtual bool animate(double time, double dt);
+
+ virtual void additional_graph_window_paint(cairo_t *cr);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
+ virtual DragMode consider_drag_start(int graph_x, int graph_y);
+
+ virtual gboolean handle_button_press(int graph_x, int graph_y,
+ bool double_click, int button);
+ virtual gboolean handle_button_release(int graph_x, int graph_y);
+ virtual gboolean handle_motion(int graph_x, int graph_y);
+ virtual gboolean handle_leave();
+
+private:
+ int pixel_to_depth(int y) const;
+ void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar);
+ void draw_guide_labels(cairo_t *cr);
+ void draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar);
+
+ static void toggled_callback(GtkToggleButton *button, gpointer data);
+ static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
+
+private:
+ std::string _net_value_text;
+
+ GtkWidget *_top_hbox;
+ GtkWidget *_average_check_box;
+ GtkWidget *_total_label;
+
+ int _popup_index = -1;
+};
+
+#endif
diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.cxx b/pandatool/src/gtk-stats/gtkStatsGraph.cxx
index e56e262d23..269b8ec47f 100644
--- a/pandatool/src/gtk-stats/gtkStatsGraph.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsGraph.cxx
@@ -14,28 +14,23 @@
#include "gtkStatsGraph.h"
#include "gtkStatsMonitor.h"
#include "gtkStatsLabelStack.h"
+#include "convert_srgb.h"
-const GdkColor GtkStatsGraph::rgb_white = {
- 0, 0xffff, 0xffff, 0xffff
+const double GtkStatsGraph::rgb_light_gray[3] = {
+ 0x9a / (double)0xff, 0x9a / (double)0xff, 0x9a / (double)0xff,
};
-const GdkColor GtkStatsGraph::rgb_light_gray = {
- 0, 0x9a9a, 0x9a9a, 0x9a9a,
+const double GtkStatsGraph::rgb_dark_gray[3] = {
+ 0x33 / (double)0xff, 0x33 / (double)0xff, 0x33 / (double)0xff,
};
-const GdkColor GtkStatsGraph::rgb_dark_gray = {
- 0, 0x3333, 0x3333, 0x3333,
-};
-const GdkColor GtkStatsGraph::rgb_black = {
- 0, 0x0000, 0x0000, 0x0000
-};
-const GdkColor GtkStatsGraph::rgb_user_guide_bar = {
- 0, 0x8282, 0x9696, 0xffff
+const double GtkStatsGraph::rgb_user_guide_bar[3] = {
+ 0x82 / (double)0xff, 0x96 / (double)0xff, 0xff / (double)0xff,
};
/**
*
*/
GtkStatsGraph::
-GtkStatsGraph(GtkStatsMonitor *monitor) :
+GtkStatsGraph(GtkStatsMonitor *monitor, bool has_label_stack) :
_monitor(monitor)
{
_parent_window = nullptr;
@@ -45,21 +40,25 @@ GtkStatsGraph(GtkStatsMonitor *monitor) :
GtkWidget *parent_window = monitor->get_window();
- GdkDisplay *display = gdk_drawable_get_display(parent_window->window);
+ GdkWindow *window = gtk_widget_get_window(parent_window);
+ GdkDisplay *display = gdk_window_get_display(window);
_hand_cursor = gdk_cursor_new_for_display(display, GDK_HAND2);
- _pixmap = nullptr;
- _pixmap_gc = nullptr;
+ int scale = gdk_window_get_scale_factor(window);
+ _pixel_scale = scale * monitor->get_resolution() * 4 / 96;
- _pixmap_xsize = 0;
- _pixmap_ysize = 0;
+ _cr_surface = nullptr;
+ _cr = nullptr;
+ _cr_scale = scale;
+ _pango_attrs = nullptr;
+
+ _surface_xsize = 0;
+ _surface_ysize = 0;
_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
- // These calls were intended to kind of emulate the Windows MDI behavior,
- // but it's just weird. gtk_window_set_transient_for(GTK_WINDOW(_window),
- // GTK_WINDOW(parent_window));
- // gtk_window_set_destroy_with_parent(GTK_WINDOW(_window), TRUE);
+ gtk_window_set_type_hint(GTK_WINDOW(_window), GDK_WINDOW_TYPE_HINT_UTILITY);
+ //gtk_window_set_transient_for(GTK_WINDOW(_window), GTK_WINDOW(parent_window));
+ //gtk_window_set_position(GTK_WINDOW(_window), GTK_WIN_POS_CENTER_ON_PARENT);
gtk_widget_add_events(_window,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
@@ -68,19 +67,19 @@ GtkStatsGraph(GtkStatsMonitor *monitor) :
G_CALLBACK(window_delete_event), this);
g_signal_connect(G_OBJECT(_window), "destroy",
G_CALLBACK(window_destroy), this);
- g_signal_connect(G_OBJECT(_window), "button_press_event",
- G_CALLBACK(button_press_event_callback), this);
- g_signal_connect(G_OBJECT(_window), "button_release_event",
- G_CALLBACK(button_release_event_callback), this);
- g_signal_connect(G_OBJECT(_window), "motion_notify_event",
- G_CALLBACK(motion_notify_event_callback), this);
+ //g_signal_connect(G_OBJECT(_window), "button_press_event",
+ // G_CALLBACK(button_press_event_callback), this);
+ //g_signal_connect(G_OBJECT(_window), "button_release_event",
+ // G_CALLBACK(button_release_event_callback), this);
+ //g_signal_connect(G_OBJECT(_window), "motion_notify_event",
+ // G_CALLBACK(motion_notify_event_callback), this);
_graph_window = gtk_drawing_area_new();
gtk_widget_add_events(_graph_window,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_POINTER_MOTION_MASK);
- g_signal_connect(G_OBJECT(_graph_window), "expose_event",
- G_CALLBACK(graph_expose_callback), this);
+ GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
+ g_signal_connect(G_OBJECT(_graph_window), "draw",
+ G_CALLBACK(graph_draw_callback), this);
g_signal_connect(G_OBJECT(_graph_window), "configure_event",
G_CALLBACK(configure_graph_callback), this);
g_signal_connect(G_OBJECT(_graph_window), "button_press_event",
@@ -89,31 +88,41 @@ GtkStatsGraph(GtkStatsMonitor *monitor) :
G_CALLBACK(button_release_event_callback), this);
g_signal_connect(G_OBJECT(_graph_window), "motion_notify_event",
G_CALLBACK(motion_notify_event_callback), this);
+ g_signal_connect(G_OBJECT(_graph_window), "leave_notify_event",
+ G_CALLBACK(leave_notify_event_callback), this);
+ g_signal_connect(G_OBJECT(_graph_window), "query-tooltip",
+ G_CALLBACK(query_tooltip_callback), this);
+
+ gtk_widget_set_has_tooltip(_graph_window, TRUE);
// A Frame to hold the graph.
- GtkWidget *graph_frame = gtk_frame_new(nullptr);
- gtk_frame_set_shadow_type(GTK_FRAME(graph_frame), GTK_SHADOW_IN);
- gtk_container_add(GTK_CONTAINER(graph_frame), _graph_window);
+ _graph_frame = gtk_frame_new(nullptr);
+ gtk_frame_set_shadow_type(GTK_FRAME(_graph_frame), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(_graph_frame), _graph_window);
// A VBox to hold the graph's frame, and any numbers (scale legend? total?)
// above it.
- _graph_vbox = gtk_vbox_new(FALSE, 0);
- gtk_box_pack_end(GTK_BOX(_graph_vbox), graph_frame,
- TRUE, TRUE, 0);
+ _graph_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_end(GTK_BOX(_graph_vbox), _graph_frame, TRUE, TRUE, 0);
// An HBox to hold the graph's frame, and the scale legend to the right of
// it.
- _graph_hbox = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox,
- TRUE, TRUE, 0);
+ _graph_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(_graph_hbox), _graph_vbox, TRUE, TRUE, 0);
// An HPaned to hold the label stack and the graph hbox.
- _hpaned = gtk_hpaned_new();
- gtk_container_add(GTK_CONTAINER(_window), _hpaned);
- gtk_container_set_border_width(GTK_CONTAINER(_window), 8);
+ if (has_label_stack) {
+ _hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_paned_set_wide_handle(GTK_PANED(_hpaned), TRUE);
+ gtk_container_add(GTK_CONTAINER(_window), _hpaned);
+ gtk_container_set_border_width(GTK_CONTAINER(_window), 8);
- gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), TRUE, TRUE);
- gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE);
+ gtk_paned_pack1(GTK_PANED(_hpaned), _label_stack.get_widget(), FALSE, FALSE);
+ gtk_paned_pack2(GTK_PANED(_hpaned), _graph_hbox, TRUE, TRUE);
+ }
+ else {
+ gtk_container_add(GTK_CONTAINER(_window), _graph_hbox);
+ }
_drag_mode = DM_none;
_potential_drag_mode = DM_none;
@@ -127,17 +136,28 @@ GtkStatsGraph(GtkStatsMonitor *monitor) :
*/
GtkStatsGraph::
~GtkStatsGraph() {
- _monitor = nullptr;
- release_pixmap();
-
- Brushes::iterator bi;
- for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) {
- GdkGC *gc = (*bi).second;
- g_object_unref(gc);
+ if (_timer_id != 0) {
+ gtk_widget_remove_tick_callback(_graph_window, _timer_id);
+ _timer_id = 0;
}
+ _monitor = nullptr;
+ release_surface();
+
+ for (auto &item : _brushes) {
+ cairo_pattern_destroy(item.second.first);
+ cairo_pattern_destroy(item.second.second);
+ }
+ _brushes.clear();
+ _text_colors.clear();
+
_label_stack.clear_labels();
+ if (_pango_attrs != nullptr) {
+ pango_attr_list_unref(_pango_attrs);
+ _pango_attrs = nullptr;
+ }
+
if (_window != nullptr) {
GtkWidget *window = _window;
_window = nullptr;
@@ -159,13 +179,6 @@ void GtkStatsGraph::
new_data(int thread_index, int frame_number) {
}
-/**
- * Called when it is necessary to redraw the entire graph.
- */
-void GtkStatsGraph::
-force_redraw() {
-}
-
/**
* Called when the user has resized the window, forcing a resize of the graph.
*/
@@ -214,7 +227,45 @@ user_guide_bars_changed() {
* Called when the user single-clicks on a label.
*/
void GtkStatsGraph::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
+}
+
+/**
+ * Called when a pop-up menu should be shown for the label.
+ */
+void GtkStatsGraph::
+on_popup_label(int collector_index) {
+}
+
+/**
+ * Called when the user hovers the mouse over a label.
+ */
+void GtkStatsGraph::
+on_enter_label(int collector_index) {
+ if (collector_index != _highlighted_index) {
+ _highlighted_index = collector_index;
+ force_redraw();
+ }
+}
+
+/**
+ * Called when the user's mouse cursor leaves a label.
+ */
+void GtkStatsGraph::
+on_leave_label(int collector_index) {
+ if (collector_index == _highlighted_index && collector_index != -1) {
+ _highlighted_index = -1;
+ force_redraw();
+ }
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsGraph::
+get_label_tooltip(int collector_index) const {
+ return std::string();
}
/**
@@ -236,37 +287,92 @@ close() {
}
/**
- * Returns a GC suitable for drawing in the indicated collector's color.
+ * Turns on the animation timer, if it hasn't already been turned on.
*/
-GdkGC *GtkStatsGraph::
-get_collector_gc(int collector_index) {
+void GtkStatsGraph::
+start_animation() {
+ if (_timer_id != 0) {
+ return;
+ }
+
+ _time = 0;
+ _timer_id = gtk_widget_add_tick_callback(_graph_window, tick_callback,
+ this, nullptr);
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool GtkStatsGraph::
+animate(double time, double dt) {
+ return false;
+}
+
+/**
+ * Returns a pattern suitable for drawing in the indicated collector's color.
+ */
+cairo_pattern_t *GtkStatsGraph::
+get_collector_pattern(int collector_index, bool highlight) {
Brushes::iterator bi;
bi = _brushes.find(collector_index);
if (bi != _brushes.end()) {
- return (*bi).second;
+ return highlight ? (*bi).second.second : (*bi).second.first;
}
// Ask the monitor what color this guy should be.
LRGBColor rgb = _monitor->get_collector_color(collector_index);
+ cairo_pattern_t *pattern = cairo_pattern_create_rgb(
+ encode_sRGB_float((float)rgb[0]),
+ encode_sRGB_float((float)rgb[1]),
+ encode_sRGB_float((float)rgb[2]));
+ cairo_pattern_t *hpattern = cairo_pattern_create_rgb(
+ encode_sRGB_float((float)rgb[0] * 0.75f),
+ encode_sRGB_float((float)rgb[1] * 0.75f),
+ encode_sRGB_float((float)rgb[2] * 0.75f));
- GdkColor c;
- c.red = (int)(rgb[0] * 65535.0f);
- c.green = (int)(rgb[1] * 65535.0f);
- c.blue = (int)(rgb[2] * 65535.0f);
- GdkGC *gc = gdk_gc_new(_pixmap);
- // g_object_ref(gc); Should this be ref_sink?
- gdk_gc_set_rgb_fg_color(gc, &c);
-
- _brushes[collector_index] = gc;
- return gc;
+ _brushes[collector_index] = std::make_pair(pattern, hpattern);
+ return highlight ? hpattern : pattern;
}
/**
- * This is called during the servicing of expose_event; it gives a derived
+ * Returns a text color suitable for the given collector.
+ */
+LRGBColor GtkStatsGraph::
+get_collector_text_color(int collector_index, bool highlight) {
+ TextColors::iterator tci;
+ tci = _text_colors.find(collector_index);
+ if (tci != _text_colors.end()) {
+ return highlight ? (*tci).second.second : (*tci).second.first;
+ }
+
+ LRGBColor rgb = _monitor->get_collector_color(collector_index);
+ double bright =
+ rgb[0] * 0.2126 +
+ rgb[1] * 0.7152 +
+ rgb[2] * 0.0722;
+ LRGBColor color = bright >= 0.5 ? LRGBColor(0) : LRGBColor(1);
+ LRGBColor hcolor = bright * 0.75 >= 0.5 ? LRGBColor(0) : LRGBColor(1);
+
+ _text_colors[collector_index] = std::make_pair(color, hcolor);
+ return highlight ? hcolor : color;
+}
+
+/**
+ * This is called during the servicing of the draw event; it gives a derived
* class opportunity to do some further painting into the graph window.
*/
void GtkStatsGraph::
-additional_graph_window_paint() {
+additional_graph_window_paint(cairo_t *cr) {
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsGraph::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return std::string();
}
/**
@@ -293,9 +399,8 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) {
* window.
*/
gboolean GtkStatsGraph::
-handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click) {
- if (_potential_drag_mode != DM_none) {
+handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
+ if (_potential_drag_mode != DM_none && button == 1) {
set_drag_mode(_potential_drag_mode);
_drag_start_x = graph_x;
_drag_start_y = graph_y;
@@ -309,59 +414,83 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
* window.
*/
gboolean GtkStatsGraph::
-handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
+handle_button_release(int graph_x, int graph_y) {
set_drag_mode(DM_none);
// ReleaseCapture();
- return handle_motion(widget, graph_x, graph_y);
+ return handle_motion(graph_x, graph_y);
}
/**
* Called when the mouse is moved within the window, or any nested window.
*/
gboolean GtkStatsGraph::
-handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
+handle_motion(int graph_x, int graph_y) {
_potential_drag_mode = consider_drag_start(graph_x, graph_y);
+ GdkWindow *window = gtk_widget_get_window(_window);
+
if (_potential_drag_mode == DM_guide_bar ||
_drag_mode == DM_guide_bar) {
- gdk_window_set_cursor(_window->window, _hand_cursor);
-
- } else {
- gdk_window_set_cursor(_window->window, nullptr);
+ gdk_window_set_cursor(window, _hand_cursor);
+ }
+ else {
+ gdk_window_set_cursor(window, nullptr);
}
return TRUE;
}
/**
- * Sets up a backing-store bitmap of the indicated size.
+ * Called when the mouse has left the graph window.
*/
-void GtkStatsGraph::
-setup_pixmap(int xsize, int ysize) {
- release_pixmap();
-
- _pixmap_xsize = std::max(xsize, 0);
- _pixmap_ysize = std::max(ysize, 0);
-
- _pixmap = gdk_pixmap_new(_graph_window->window, _pixmap_xsize, _pixmap_ysize, -1);
- // g_object_ref(_pixmap); Should this be ref_sink?
- _pixmap_gc = gdk_gc_new(_pixmap);
- // g_object_ref(_pixmap_gc); Should this be ref_sink?
-
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
- gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0,
- _pixmap_xsize, _pixmap_ysize);
+gboolean GtkStatsGraph::
+handle_leave() {
+ return FALSE;
}
/**
- * Frees the backing-store bitmap created by setup_pixmap().
+ * Sets up a backing-store bitmap of the indicated size.
*/
void GtkStatsGraph::
-release_pixmap() {
- if (_pixmap != nullptr) {
- g_object_unref(_pixmap);
- g_object_unref(_pixmap_gc);
+setup_surface(int xsize, int ysize, int scale) {
+ release_surface();
+
+ _surface_xsize = xsize;
+ _surface_ysize = ysize;
+ _pixel_scale = scale * _monitor->get_resolution() * 4 / 96;
+
+ GdkWindow *window = gtk_widget_get_window(_graph_window);
+ _cr_surface = gdk_window_create_similar_image_surface(window, CAIRO_FORMAT_RGB24, _surface_xsize, _surface_ysize, 1);
+ _cr = cairo_create(_cr_surface);
+ _cr_scale = scale;
+
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_paint(_cr);
+
+ // Cache the font scale attribute.
+ _pango_attrs = pango_attr_list_new();
+ PangoAttribute *attr = pango_attr_scale_new(scale * 0.9);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert(_pango_attrs, attr);
+}
+
+/**
+ * Frees the backing-store bitmap created by setup_surface().
+ */
+void GtkStatsGraph::
+release_surface() {
+ if (_cr_surface != nullptr) {
+ cairo_surface_destroy(_cr_surface);
+ cairo_destroy(_cr);
+ _cr_surface = nullptr;
+ _cr = nullptr;
+ }
+
+ if (_pango_attrs != nullptr) {
+ pango_attr_list_unref(_pango_attrs);
+ _pango_attrs = nullptr;
}
}
@@ -388,17 +517,17 @@ window_destroy(GtkWidget *widget, gpointer data) {
* Fills in the graph window.
*/
gboolean GtkStatsGraph::
-graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
+graph_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
GtkStatsGraph *self = (GtkStatsGraph *)data;
- if (self->_pixmap != nullptr) {
- gdk_draw_drawable(self->_graph_window->window,
- self->_graph_window->style->fg_gc[0],
- self->_pixmap, 0, 0, 0, 0,
- self->_pixmap_xsize, self->_pixmap_ysize);
+ if (self->_cr_surface != nullptr) {
+ double scale = 1.0 / self->_cr_scale;
+ cairo_scale(cr, scale, scale);
+ cairo_set_source_surface(cr, self->_cr_surface, 0, 0);
+ cairo_paint(cr);
}
- self->additional_graph_window_paint();
+ self->additional_graph_window_paint(cr);
return TRUE;
}
@@ -408,12 +537,21 @@ graph_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
*/
gboolean GtkStatsGraph::
configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event,
- gpointer data) {
+ gpointer data) {
GtkStatsGraph *self = (GtkStatsGraph *)data;
- self->changed_graph_size(event->width, event->height);
- self->setup_pixmap(event->width, event->height);
- self->force_redraw();
+ GdkWindow *window = gtk_widget_get_window(widget);
+ int scale = gdk_window_get_scale_factor(window);
+ int scaled_xsize = std::max(event->width * scale, 0);
+ int scaled_ysize = std::max(event->height * scale, 0);
+
+ if (self->_cr == nullptr ||
+ self->_cr_scale != scale ||
+ self->_surface_xsize != scaled_xsize ||
+ self->_surface_ysize != scaled_ysize) {
+ self->setup_surface(scaled_xsize, scaled_ysize, scale);
+ self->changed_graph_size(scaled_xsize, scaled_ysize);
+ }
return TRUE;
}
@@ -424,16 +562,19 @@ configure_graph_callback(GtkWidget *widget, GdkEventConfigure *event,
*/
gboolean GtkStatsGraph::
button_press_event_callback(GtkWidget *widget, GdkEventButton *event,
- gpointer data) {
+ gpointer data) {
GtkStatsGraph *self = (GtkStatsGraph *)data;
int graph_x, graph_y;
gtk_widget_translate_coordinates(widget, self->_graph_window,
- (int)event->x, (int)event->y,
- &graph_x, &graph_y);
+ (int)event->x, (int)event->y,
+ &graph_x, &graph_y);
+ graph_x *= self->_cr_scale;
+ graph_y *= self->_cr_scale;
bool double_click = (event->type == GDK_2BUTTON_PRESS);
- return self->handle_button_press(widget, graph_x, graph_y, double_click);
+ return self->handle_button_press(graph_x, graph_y,
+ double_click, event->button);
}
/**
@@ -442,14 +583,16 @@ button_press_event_callback(GtkWidget *widget, GdkEventButton *event,
*/
gboolean GtkStatsGraph::
button_release_event_callback(GtkWidget *widget, GdkEventButton *event,
- gpointer data) {
+ gpointer data) {
GtkStatsGraph *self = (GtkStatsGraph *)data;
int graph_x, graph_y;
gtk_widget_translate_coordinates(widget, self->_graph_window,
- (int)event->x, (int)event->y,
- &graph_x, &graph_y);
+ (int)event->x, (int)event->y,
+ &graph_x, &graph_y);
+ graph_x *= self->_cr_scale;
+ graph_y *= self->_cr_scale;
- return self->handle_button_release(widget, graph_x, graph_y);
+ return self->handle_button_release(graph_x, graph_y);
}
/**
@@ -457,12 +600,65 @@ button_release_event_callback(GtkWidget *widget, GdkEventButton *event,
*/
gboolean GtkStatsGraph::
motion_notify_event_callback(GtkWidget *widget, GdkEventMotion *event,
- gpointer data) {
+ gpointer data) {
GtkStatsGraph *self = (GtkStatsGraph *)data;
int graph_x, graph_y;
gtk_widget_translate_coordinates(widget, self->_graph_window,
- (int)event->x, (int)event->y,
- &graph_x, &graph_y);
+ (int)event->x, (int)event->y,
+ &graph_x, &graph_y);
+ graph_x *= self->_cr_scale;
+ graph_y *= self->_cr_scale;
- return self->handle_motion(widget, graph_x, graph_y);
+ return self->handle_motion(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse has left the graph window.
+ */
+gboolean GtkStatsGraph::
+leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event,
+ gpointer data) {
+ GtkStatsGraph *self = (GtkStatsGraph *)data;
+ return self->handle_leave();
+}
+
+/**
+ * Called when a tooltip should be displayed.
+ */
+gboolean GtkStatsGraph::
+query_tooltip_callback(GtkWidget *widget, gint x, gint y,
+ gboolean keyboard_tip, GtkTooltip *tooltip,
+ gpointer data) {
+ GtkStatsGraph *self = (GtkStatsGraph *)data;
+ x *= self->_cr_scale;
+ y *= self->_cr_scale;
+
+ std::string text = self->get_graph_tooltip(x, y);
+ gtk_tooltip_set_text(tooltip, text.c_str());
+ return !text.empty();
+}
+
+/**
+ * Called to update the animations.
+ */
+gboolean GtkStatsGraph::
+tick_callback(GtkWidget *widget, GdkFrameClock *clock, gpointer data) {
+ GtkStatsGraph *graph = (GtkStatsGraph *)data;
+ gint64 new_time = gdk_frame_clock_get_frame_time(clock);
+ if (graph->_time == 0) {
+ // First frame, so we don't have a dt yet.
+ graph->_time = new_time;
+ return TRUE;
+ }
+ gint64 delta = new_time - graph->_time;
+ if (delta == 0) {
+ return TRUE;
+ }
+ if (graph->animate(new_time / 1000000.0, delta / 1000000.0)) {
+ graph->_time = new_time;
+ return TRUE;
+ } else {
+ graph->_timer_id = 0;
+ return FALSE;
+ }
}
diff --git a/pandatool/src/gtk-stats/gtkStatsGraph.h b/pandatool/src/gtk-stats/gtkStatsGraph.h
index 94efa0cb06..2ff48cecca 100644
--- a/pandatool/src/gtk-stats/gtkStatsGraph.h
+++ b/pandatool/src/gtk-stats/gtkStatsGraph.h
@@ -17,8 +17,10 @@
#include "pandatoolbase.h"
#include "gtkStatsLabelStack.h"
#include "pmap.h"
+#include "luse.h"
#include
+#include
class GtkStatsMonitor;
@@ -35,15 +37,16 @@ public:
DM_guide_bar,
DM_new_guide_bar,
DM_sizing,
+ DM_pan,
};
public:
- GtkStatsGraph(GtkStatsMonitor *monitor);
+ GtkStatsGraph(GtkStatsMonitor *monitor, bool has_label_stack);
virtual ~GtkStatsGraph();
virtual void new_collector(int collector_index);
virtual void new_data(int thread_index, int frame_number);
- virtual void force_redraw();
+ virtual void force_redraw()=0;
virtual void changed_graph_size(int graph_xsize, int graph_ysize);
virtual void set_time_units(int unit_mask);
@@ -51,29 +54,44 @@ public:
void set_pause(bool pause);
void user_guide_bars_changed();
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual void on_enter_label(int collector_index);
+ virtual void on_leave_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
protected:
void close();
- GdkGC *get_collector_gc(int collector_index);
- virtual void additional_graph_window_paint();
+ void start_animation();
+ virtual bool animate(double time, double dt);
+
+ cairo_pattern_t *get_collector_pattern(int collector_index, bool highlight = false);
+ LRGBColor get_collector_text_color(int collector_index, bool highlight = false);
+
+ virtual void additional_graph_window_paint(cairo_t *cr);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int graph_x, int graph_y);
virtual void set_drag_mode(DragMode drag_mode);
- virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click);
- virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y);
- virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y);
+ virtual gboolean handle_button_press(int graph_x, int graph_y,
+ bool double_click, int button);
+ virtual gboolean handle_button_release(int graph_x, int graph_y);
+ virtual gboolean handle_motion(int graph_x, int graph_y);
+ virtual gboolean handle_leave();
protected:
- // Table of GC's for our various collectors.
- typedef pmap Brushes;
+ // Table of patterns for our various collectors.
+ typedef pmap > Brushes;
Brushes _brushes;
+ typedef pmap > TextColors;
+ TextColors _text_colors;
+
GtkStatsMonitor *_monitor;
GtkWidget *_parent_window;
GtkWidget *_window;
+ GtkWidget *_graph_frame;
GtkWidget *_graph_window;
GtkWidget *_graph_hbox;
GtkWidget *_graph_vbox;
@@ -83,18 +101,12 @@ protected:
GdkCursor *_hand_cursor;
- GdkPixmap *_pixmap;
- GdkGC *_pixmap_gc;
- int _pixmap_xsize, _pixmap_ysize;
-
- /*
- COLORREF _dark_color;
- COLORREF _light_color;
- COLORREF _user_guide_bar_color;
- HPEN _dark_pen;
- HPEN _light_pen;
- HPEN _user_guide_bar_pen;
- */
+ cairo_surface_t *_cr_surface;
+ cairo_t *_cr;
+ int _surface_xsize, _surface_ysize;
+ PangoAttrList *_pango_attrs;
+ int _cr_scale;
+ int _pixel_scale;
DragMode _drag_mode;
DragMode _potential_drag_mode;
@@ -102,36 +114,50 @@ protected:
double _drag_scale_start;
int _drag_guide_bar;
+ int _highlighted_index = -1;
+
bool _pause;
- static const GdkColor rgb_white;
- static const GdkColor rgb_light_gray;
- static const GdkColor rgb_dark_gray;
- static const GdkColor rgb_black;
- static const GdkColor rgb_user_guide_bar;
+ guint _timer_id = 0;
+ gint64 _time = 0;
+
+ static const double rgb_white[3];
+ static const double rgb_light_gray[3];
+ static const double rgb_dark_gray[3];
+ static const double rgb_black[3];
+ static const double rgb_user_guide_bar[3];
private:
- void setup_pixmap(int xsize, int ysize);
- void release_pixmap();
+ void setup_surface(int xsize, int ysize, int scale);
+ void release_surface();
static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event,
- gpointer data);
+ gpointer data);
static void window_destroy(GtkWidget *widget, gpointer data);
- static gboolean graph_expose_callback(GtkWidget *widget,
- GdkEventExpose *event, gpointer data);
+ static gboolean graph_draw_callback(GtkWidget *widget,
+ cairo_t *cr, gpointer data);
static gboolean configure_graph_callback(GtkWidget *widget,
- GdkEventConfigure *event, gpointer data);
+ GdkEventConfigure *event,
+ gpointer data);
protected:
static gboolean button_press_event_callback(GtkWidget *widget,
- GdkEventButton *event,
- gpointer data);
+ GdkEventButton *event,
+ gpointer data);
static gboolean button_release_event_callback(GtkWidget *widget,
- GdkEventButton *event,
- gpointer data);
+ GdkEventButton *event,
+ gpointer data);
static gboolean motion_notify_event_callback(GtkWidget *widget,
- GdkEventMotion *event,
- gpointer data);
+ GdkEventMotion *event,
+ gpointer data);
+ static gboolean leave_notify_event_callback(GtkWidget *widget,
+ GdkEventCrossing *event,
+ gpointer data);
+ static gboolean query_tooltip_callback(GtkWidget *widget, gint x, gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip, gpointer data);
+ static gboolean tick_callback(GtkWidget *widget, GdkFrameClock *clock,
+ gpointer data);
};
#endif
diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.cxx b/pandatool/src/gtk-stats/gtkStatsLabel.cxx
index cac7be3859..7dae836b99 100644
--- a/pandatool/src/gtk-stats/gtkStatsLabel.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsLabel.cxx
@@ -14,6 +14,7 @@
#include "gtkStatsLabel.h"
#include "gtkStatsMonitor.h"
#include "gtkStatsGraph.h"
+#include "convert_srgb.h"
int GtkStatsLabel::_left_margin = 2;
int GtkStatsLabel::_right_margin = 2;
@@ -25,64 +26,61 @@ int GtkStatsLabel::_bottom_margin = 2;
*/
GtkStatsLabel::
GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph,
- int thread_index, int collector_index, bool use_fullname) :
+ int thread_index, int collector_index, bool use_fullname,
+ bool align_right) :
_monitor(monitor),
_graph(graph),
_thread_index(thread_index),
- _collector_index(collector_index)
+ _collector_index(collector_index),
+ _align_right(align_right)
{
- _widget = nullptr;
- if (use_fullname) {
- _text = _monitor->get_client_data()->get_collector_fullname(_collector_index);
- } else {
- _text = _monitor->get_client_data()->get_collector_name(_collector_index);
- }
-
_widget = gtk_drawing_area_new();
gtk_widget_add_events(_widget,
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_BUTTON_PRESS_MASK);
- g_signal_connect(G_OBJECT(_widget), "expose_event",
- G_CALLBACK(expose_event_callback), this);
+ g_signal_connect(G_OBJECT(_widget), "draw",
+ G_CALLBACK(draw_callback), this);
g_signal_connect(G_OBJECT(_widget), "enter_notify_event",
G_CALLBACK(enter_notify_event_callback), this);
g_signal_connect(G_OBJECT(_widget), "leave_notify_event",
G_CALLBACK(leave_notify_event_callback), this);
g_signal_connect(G_OBJECT(_widget), "button_press_event",
G_CALLBACK(button_press_event_callback), this);
+ g_signal_connect(G_OBJECT(_widget), "query-tooltip",
+ G_CALLBACK(query_tooltip_callback), this);
- gtk_widget_show(_widget);
-
- // Make up a PangoLayout to represent the text.
- _layout = gtk_widget_create_pango_layout(_widget, _text.c_str());
+ gtk_widget_set_has_tooltip(_widget, TRUE);
// Set the fg and bg colors on the label.
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
- _bg_color.red = (int)(rgb[0] * 65535.0f);
- _bg_color.green = (int)(rgb[1] * 65535.0f);
- _bg_color.blue = (int)(rgb[2] * 65535.0f);
+ _bg_color = LRGBColor(
+ encode_sRGB_float((float)rgb[0]),
+ encode_sRGB_float((float)rgb[1]),
+ encode_sRGB_float((float)rgb[2]));
+
+ _highlight_bg_color = LRGBColor(
+ encode_sRGB_float((float)rgb[0] * 0.75f),
+ encode_sRGB_float((float)rgb[1] * 0.75f),
+ encode_sRGB_float((float)rgb[2] * 0.75f));
// Should our foreground be black or white?
- double bright =
- rgb[0] * 0.299 +
- rgb[1] * 0.587 +
- rgb[2] * 0.114;
-
+ PN_stdfloat bright = _bg_color.dot(LRGBColor(0.2126, 0.7152, 0.0722));
if (bright >= 0.5) {
- _fg_color.red = _fg_color.green = _fg_color.blue = 0;
+ _fg_color = LRGBColor(0);
} else {
- _fg_color.red = _fg_color.green = _fg_color.blue = 0xffff;
+ _fg_color = LRGBColor(1);
+ }
+ if (bright * 0.75 >= 0.5) {
+ _highlight_fg_color = LRGBColor(0);
+ } else {
+ _highlight_fg_color = LRGBColor(1);
}
-
- // What are the extents of the text? This determines the minimum size of
- // our widget.
- int width, height;
- pango_layout_get_pixel_size(_layout, &width, &height);
- gtk_widget_set_size_request(_widget, width + 8, height);
_highlight = false;
_mouse_within = false;
- _height = height;
+
+ update_text(use_fullname);
+ gtk_widget_show_all(_widget);
}
/**
@@ -90,7 +88,10 @@ GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph,
*/
GtkStatsLabel::
~GtkStatsLabel() {
- // DeleteObject(_bg_brush);
+ if (_layout) {
+ g_object_unref(_layout);
+ _layout = nullptr;
+ }
}
/**
@@ -144,6 +145,33 @@ get_highlight() const {
return _highlight;
}
+/**
+ * Set to true if the full name of the collector should be shown.
+ */
+void GtkStatsLabel::
+update_text(bool use_fullname) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (use_fullname) {
+ _text = client_data->get_collector_fullname(_collector_index);
+ } else {
+ _text = client_data->get_collector_name(_collector_index);
+ }
+
+ // Make up a PangoLayout to represent the text.
+ if (_layout) {
+ g_object_unref(_layout);
+ }
+ _layout = gtk_widget_create_pango_layout(_widget, _text.c_str());
+
+ // What are the extents of the text? This determines the minimum size of
+ // our widget.
+ int width, height;
+ pango_layout_get_pixel_size(_layout, &width, &height);
+ gtk_widget_set_size_request(_widget, width + 8, height);
+ _ideal_width = width;
+ _height = height;
+}
+
/**
* Used internally to indicate whether the mouse is within the label's widget.
*/
@@ -159,31 +187,37 @@ set_mouse_within(bool mouse_within) {
* Draws the background color of the label.
*/
gboolean GtkStatsLabel::
-expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
+draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
GtkStatsLabel *self = (GtkStatsLabel *)data;
- GdkGC *gc = gdk_gc_new(widget->window);
- gdk_gc_set_rgb_fg_color(gc, &self->_bg_color);
+ LRGBColor bg, fg;
+ if (self->_highlight || self->_mouse_within) {
+ bg = self->_highlight_bg_color;
+ fg = self->_highlight_fg_color;
+ } else {
+ bg = self->_bg_color;
+ fg = self->_fg_color;
- gdk_draw_rectangle(widget->window, gc, TRUE, 0, 0,
- widget->allocation.width, widget->allocation.height);
+ }
+ cairo_set_source_rgb(cr, bg[0], bg[1], bg[2]);
+
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(widget, &allocation);
+
+ cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill(cr);
- // Center the text within the rectangle.
int width, height;
pango_layout_get_pixel_size(self->_layout, &width, &height);
- gdk_gc_set_rgb_fg_color(gc, &self->_fg_color);
- gdk_draw_layout(widget->window, gc,
- (widget->allocation.width - width) / 2, 0,
- self->_layout);
-
- // Now draw the highlight rectangle, if any.
- if (self->_highlight || self->_mouse_within) {
- gdk_draw_rectangle(widget->window, gc, FALSE, 0, 0,
- widget->allocation.width - 1, widget->allocation.height - 1);
+ cairo_set_source_rgb(cr, fg[0], fg[1], fg[2]);
+ if (self->_align_right) {
+ cairo_move_to(cr, allocation.width - width, 0);
+ } else {
+ cairo_move_to(cr, 0, 0);
}
+ pango_cairo_show_layout(cr, self->_layout);
- g_object_unref(gc);
return TRUE;
}
@@ -203,7 +237,7 @@ enter_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event,
*/
gboolean GtkStatsLabel::
leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event,
- gpointer data) {
+ gpointer data) {
GtkStatsLabel *self = (GtkStatsLabel *)data;
self->set_mouse_within(false);
return TRUE;
@@ -214,11 +248,27 @@ leave_notify_event_callback(GtkWidget *widget, GdkEventCrossing *event,
*/
gboolean GtkStatsLabel::
button_press_event_callback(GtkWidget *widget, GdkEventButton *event,
- gpointer data) {
+ gpointer data) {
GtkStatsLabel *self = (GtkStatsLabel *)data;
- bool double_click = (event->type == GDK_2BUTTON_PRESS);
- if (double_click) {
- self->_graph->clicked_label(self->_collector_index);
+ if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ self->_graph->on_click_label(self->_collector_index);
+ }
+ else if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ self->_graph->on_popup_label(self->_collector_index);
}
return TRUE;
}
+
+/**
+ * Called when a tooltip should be displayed.
+ */
+gboolean GtkStatsLabel::
+query_tooltip_callback(GtkWidget *widget, gint x, gint y,
+ gboolean keyboard_tip, GtkTooltip *tooltip,
+ gpointer data) {
+ GtkStatsLabel *self = (GtkStatsLabel *)data;
+
+ std::string text = self->_graph->get_label_tooltip(self->_collector_index);
+ gtk_tooltip_set_text(tooltip, text.c_str());
+ return !text.empty();
+}
diff --git a/pandatool/src/gtk-stats/gtkStatsLabel.h b/pandatool/src/gtk-stats/gtkStatsLabel.h
index 624e4193f9..185b29a605 100644
--- a/pandatool/src/gtk-stats/gtkStatsLabel.h
+++ b/pandatool/src/gtk-stats/gtkStatsLabel.h
@@ -17,6 +17,7 @@
#include "pandatoolbase.h"
#include
+#include
class GtkStatsMonitor;
class GtkStatsGraph;
@@ -29,7 +30,8 @@ class GtkStatsGraph;
class GtkStatsLabel {
public:
GtkStatsLabel(GtkStatsMonitor *monitor, GtkStatsGraph *graph,
- int thread_index, int collector_index, bool use_fullname);
+ int thread_index, int collector_index, bool use_fullname,
+ bool align_right = true);
~GtkStatsLabel();
GtkWidget *get_widget() const;
@@ -41,10 +43,12 @@ public:
void set_highlight(bool highlight);
bool get_highlight() const;
+ void update_text(bool use_fullname);
+
private:
void set_mouse_within(bool mouse_within);
- static gboolean expose_event_callback(GtkWidget *widget,
- GdkEventExpose *event, gpointer data);
+ static gboolean draw_callback(GtkWidget *widget,
+ cairo_t *cr, gpointer data);
static gboolean enter_notify_event_callback(GtkWidget *widget,
GdkEventCrossing *event,
gpointer data);
@@ -54,6 +58,9 @@ private:
static gboolean button_press_event_callback(GtkWidget *widget,
GdkEventButton *event,
gpointer data);
+ static gboolean query_tooltip_callback(GtkWidget *widget, gint x, gint y,
+ gboolean keyboard_tip, GtkTooltip *tooltip,
+ gpointer data);
GtkStatsMonitor *_monitor;
GtkStatsGraph *_graph;
@@ -61,20 +68,17 @@ private:
int _collector_index;
std::string _text;
GtkWidget *_widget;
- GdkColor _fg_color;
- GdkColor _bg_color;
- PangoLayout *_layout;
-
- /*
- COLORREF _bg_color;
- COLORREF _fg_color;
- HBRUSH _bg_brush;
- HBRUSH _highlight_brush;
- */
+ LRGBColor _fg_color;
+ LRGBColor _highlight_fg_color;
+ LRGBColor _bg_color;
+ LRGBColor _highlight_bg_color;
+ PangoLayout *_layout = nullptr;
+ int _height;
+ int _ideal_width;
bool _highlight;
bool _mouse_within;
- int _height;
+ bool _align_right;
static int _left_margin, _right_margin;
static int _top_margin, _bottom_margin;
diff --git a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx
index a640fb3217..5a08b2fe38 100644
--- a/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsLabelStack.cxx
@@ -20,7 +20,7 @@
*/
GtkStatsLabelStack::
GtkStatsLabelStack() {
- _widget = gtk_vbox_new(FALSE, 0);
+ _widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
_highlight_label = -1;
}
@@ -48,9 +48,12 @@ int GtkStatsLabelStack::
get_label_y(int label_index, GtkWidget *target_widget) const {
nassertr(label_index >= 0 && label_index < (int)_labels.size(), 0);
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(_widget, &allocation);
+
// Assume all labels have the same height.
int height = _labels[0]->get_height();
- int start_y = _widget->allocation.height - height * label_index;
+ int start_y = allocation.height - height * label_index;
int x, y;
gtk_widget_translate_coordinates(_widget, target_widget,
@@ -81,9 +84,7 @@ get_label_collector_index(int label_index) const {
*/
void GtkStatsLabelStack::
clear_labels(bool delete_widgets) {
- Labels::iterator li;
- for (li = _labels.begin(); li != _labels.end(); ++li) {
- GtkStatsLabel *label = (*li);
+ for (GtkStatsLabel *label : _labels) {
if (delete_widgets) {
gtk_container_remove(GTK_CONTAINER(_widget), label->get_widget());
}
diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.I b/pandatool/src/gtk-stats/gtkStatsMonitor.I
index 65a5bfbf64..b3e8e55b13 100644
--- a/pandatool/src/gtk-stats/gtkStatsMonitor.I
+++ b/pandatool/src/gtk-stats/gtkStatsMonitor.I
@@ -14,10 +14,11 @@
/**
*
*/
-GtkStatsMonitor::MenuDef::
-MenuDef(int thread_index, int collector_index, bool show_level) :
+INLINE GtkStatsMonitor::MenuDef::
+MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) :
_thread_index(thread_index),
_collector_index(collector_index),
+ _chart_type(chart_type),
_show_level(show_level),
_monitor(nullptr)
{
@@ -26,7 +27,7 @@ MenuDef(int thread_index, int collector_index, bool show_level) :
/**
*
*/
-bool GtkStatsMonitor::MenuDef::
+INLINE bool GtkStatsMonitor::MenuDef::
operator < (const MenuDef &other) const {
if (_thread_index != other._thread_index) {
return _thread_index < other._thread_index;
@@ -34,5 +35,8 @@ operator < (const MenuDef &other) const {
if (_collector_index != other._collector_index) {
return _collector_index < other._collector_index;
}
+ if (_chart_type != other._chart_type) {
+ return _chart_type < other._chart_type;
+ }
return (int)_show_level < (int)other._show_level;
}
diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx
index 4f78d88450..9a04cc523a 100644
--- a/pandatool/src/gtk-stats/gtkStatsMonitor.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsMonitor.cxx
@@ -17,29 +17,11 @@
#include "gtkStatsStripChart.h"
#include "gtkStatsChartMenu.h"
#include "gtkStatsPianoRoll.h"
+#include "gtkStatsFlameGraph.h"
+#include "gtkStatsTimeline.h"
#include "gtkStatsMenuId.h"
#include "pStatGraph.h"
#include "pStatCollectorDef.h"
-#include "indent.h"
-
-typedef void vc();
-
-GtkItemFactoryEntry GtkStatsMonitor::menu_entries[] = {
- { (gchar *)"/Options", nullptr, nullptr, 0, (gchar *)"" },
- { (gchar *)"/Options/Units", nullptr, nullptr, 0, (gchar *)"" },
- { (gchar *)"/Options/Units/ms", nullptr, (vc *)&handle_menu_command, MI_time_ms, (gchar *)"" },
- { (gchar *)"/Options/Units/Hz", nullptr, (vc *)&handle_menu_command, MI_time_hz, (gchar *)"/Options/Units/ms" },
- { (gchar *)"/Speed", nullptr, nullptr, 0, (gchar *)"" },
- { (gchar *)"/Speed/1", nullptr, (vc *)&handle_menu_command, MI_speed_1, (gchar *)"" },
- { (gchar *)"/Speed/2", nullptr, (vc *)&handle_menu_command, MI_speed_2, (gchar *)"/Speed/1" },
- { (gchar *)"/Speed/3", nullptr, (vc *)&handle_menu_command, MI_speed_3, (gchar *)"/Speed/1" },
- { (gchar *)"/Speed/6", nullptr, (vc *)&handle_menu_command, MI_speed_6, (gchar *)"/Speed/1" },
- { (gchar *)"/Speed/12", nullptr, (vc *)&handle_menu_command, MI_speed_12, (gchar *)"/Speed/1" },
- { (gchar *)"/Speed/sep", nullptr, nullptr, 0, (gchar *)"" },
- { (gchar *)"/Speed/pause", nullptr, (vc *)&handle_menu_command, MI_pause, (gchar *)"" },
-};
-
-int GtkStatsMonitor::num_menu_entries = sizeof(menu_entries) / sizeof(GtkItemFactoryEntry);
/**
*
@@ -47,12 +29,13 @@ int GtkStatsMonitor::num_menu_entries = sizeof(menu_entries) / sizeof(GtkItemFac
GtkStatsMonitor::
GtkStatsMonitor(GtkStatsServer *server) : PStatMonitor(server) {
_window = nullptr;
- _item_factory = nullptr;
// These will be filled in later when the menu is created.
_time_units = 0;
_scroll_speed = 0.0;
_pause = false;
+
+ _resolution = gdk_screen_get_resolution(gdk_screen_get_default());
}
/**
@@ -160,8 +143,7 @@ new_collector(int collector_index) {
void GtkStatsMonitor::
new_thread(int thread_index) {
GtkStatsChartMenu *chart_menu = new GtkStatsChartMenu(this, thread_index);
- GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, "");
- chart_menu->add_to_menu_bar(menu_bar, _next_chart_index);
+ chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index);
++_next_chart_index;
_chart_menus.push_back(chart_menu);
}
@@ -174,13 +156,14 @@ new_thread(int thread_index) {
*/
void GtkStatsMonitor::
new_data(int thread_index, int frame_number) {
- Graphs::iterator gi;
- for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
- GtkStatsGraph *graph = (*gi);
+ for (GtkStatsGraph *graph : _graphs) {
graph->new_data(thread_index, frame_number);
}
-}
+ if (thread_index == 0) {
+ update_status_bar();
+ }
+}
/**
* Called whenever the connection to the client has been lost. This is a
@@ -214,6 +197,10 @@ idle() {
sprintf(buffer, "%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate);
gtk_label_set_text(GTK_LABEL(_frame_rate_label), buffer);
+
+ if (!_status_bar_labels.empty()) {
+ gtk_label_set_text(GTK_LABEL(_status_bar_labels[0]), buffer);
+ }
}
}
@@ -246,6 +233,14 @@ get_window() const {
return _window;
}
+/**
+ * Returns the screen DPI.
+ */
+double GtkStatsMonitor::
+get_resolution() const {
+ return _resolution;
+}
+
/**
* Opens a new strip chart showing the indicated data.
*/
@@ -273,6 +268,32 @@ open_piano_roll(int thread_index) {
graph->set_pause(_pause);
}
+/**
+ * Opens a new flame graph showing the indicated data.
+ */
+void GtkStatsMonitor::
+open_flame_graph(int thread_index, int collector_index) {
+ GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index);
+ add_graph(graph);
+
+ graph->set_time_units(_time_units);
+ graph->set_scroll_speed(_scroll_speed);
+ graph->set_pause(_pause);
+}
+
+/**
+ * Opens a new timeline.
+ */
+void GtkStatsMonitor::
+open_timeline() {
+ GtkStatsTimeline *graph = new GtkStatsTimeline(this);
+ add_graph(graph);
+
+ graph->set_time_units(_time_units);
+ graph->set_scroll_speed(_scroll_speed);
+ graph->set_pause(_pause);
+}
+
/**
* Adds a new MenuDef to the monitor, or returns an existing one if there is
* already one just like it.
@@ -380,39 +401,39 @@ create_window() {
// Set up the menu.
GtkAccelGroup *accel_group = gtk_accel_group_new();
- _item_factory =
- gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group);
- gtk_item_factory_create_items(_item_factory, num_menu_entries, menu_entries,
- this);
gtk_window_add_accel_group(GTK_WINDOW(_window), accel_group);
- GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, "");
+ _menu_bar = gtk_menu_bar_new();
_next_chart_index = 2;
+ setup_options_menu();
+ setup_speed_menu();
setup_frame_rate_label();
- ChartMenus::iterator mi;
- for (mi = _chart_menus.begin(); mi != _chart_menus.end(); ++mi) {
- (*mi)->add_to_menu_bar(menu_bar, _next_chart_index);
+ for (GtkStatsChartMenu *chart_menu : _chart_menus) {
+ chart_menu->add_to_menu_bar(_menu_bar, _next_chart_index);
++_next_chart_index;
}
// Pack the menu into the window.
- GtkWidget *main_vbox = gtk_vbox_new(FALSE, 1);
+ GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_container_add(GTK_CONTAINER(_window), main_vbox);
- gtk_box_pack_start(GTK_BOX(main_vbox), menu_bar, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(main_vbox), _menu_bar, FALSE, TRUE, 0);
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(_item_factory, "/Speed/3")),
- TRUE);
- set_scroll_speed(3);
+ // Create the status bar.
+ _status_bar = gtk_flow_box_new();
+ gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(_status_bar), FALSE);
+ gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(_status_bar), GTK_SELECTION_NONE);
+ g_signal_connect(G_OBJECT(_status_bar), "button_press_event",
+ G_CALLBACK(status_bar_button_event), this);
+ gtk_box_pack_end(GTK_BOX(main_vbox), _status_bar, FALSE, FALSE, 0);
+ update_status_bar();
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(_item_factory, "/Options/Units/ms")),
- TRUE);
- set_time_units(PStatGraph::GBU_ms);
+ GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_end(GTK_BOX(main_vbox), sep, FALSE, FALSE, 0);
gtk_widget_show_all(_window);
gtk_widget_show(_window);
-
- set_pause(false);
+ gtk_widget_realize(_window);
}
/**
@@ -462,6 +483,126 @@ window_destroy(GtkWidget *widget, gpointer data) {
self->close();
}
+
+/**
+ * Creates the "Options" pulldown menu.
+ */
+void GtkStatsMonitor::
+setup_options_menu() {
+ _options_menu = gtk_menu_new();
+
+ GtkWidget *item = gtk_menu_item_new_with_label("Options");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _options_menu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
+
+ GtkWidget *units_menu = gtk_menu_new();
+ item = gtk_menu_item_new_with_label("Units");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), units_menu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_options_menu), item);
+
+ item = gtk_radio_menu_item_new_with_label(nullptr, "ms");
+ gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_time_units(PStatGraph::GBU_ms);
+ }), this);
+
+ item = gtk_radio_menu_item_new_with_label(
+ gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)), "Hz");
+ gtk_menu_shell_append(GTK_MENU_SHELL(units_menu), item);
+ g_signal_connect(G_OBJECT(item), "activate",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_time_units(PStatGraph::GBU_hz);
+ }), this);
+
+ set_time_units(PStatGraph::GBU_ms);
+}
+
+/**
+ * Creates the "Speed" pulldown menu.
+ */
+void GtkStatsMonitor::
+setup_speed_menu() {
+ _speed_menu = gtk_menu_new();
+
+ GtkWidget *item = gtk_menu_item_new_with_label("Speed");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), _speed_menu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), item);
+
+ GSList *group = nullptr;
+ item = gtk_radio_menu_item_new_with_label(group, "1");
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_scroll_speed(1);
+ }
+ }), this);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+ item = gtk_radio_menu_item_new_with_label(group, "2");
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_scroll_speed(2);
+ }
+ }), this);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+ item = gtk_radio_menu_item_new_with_label(group, "3");
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_scroll_speed(3);
+ }
+ }), this);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+ item = gtk_radio_menu_item_new_with_label(group, "6");
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_scroll_speed(6);
+ }
+ }), this);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+ item = gtk_radio_menu_item_new_with_label(group, "12");
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_scroll_speed(12);
+ }
+ }), this);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+ item = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+
+ item = gtk_check_menu_item_new_with_label("pause");
+ gtk_menu_shell_append(GTK_MENU_SHELL(_speed_menu), item);
+ g_signal_connect(G_OBJECT(item), "toggled",
+ G_CALLBACK(+[](GtkMenuItem *item, gpointer data) {
+ GtkStatsMonitor *self = (GtkStatsMonitor *)data;
+ self->set_pause(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)));
+ }), this);
+
+ set_scroll_speed(3);
+ set_pause(false);
+}
+
/**
* Creates the frame rate label on the right end of the menu bar. This is
* used as a text label to display the main thread's frame rate to the user,
@@ -470,59 +611,210 @@ window_destroy(GtkWidget *widget, gpointer data) {
*/
void GtkStatsMonitor::
setup_frame_rate_label() {
- GtkWidget *menu_bar = gtk_item_factory_get_widget(_item_factory, "");
-
_frame_rate_menu_item = gtk_menu_item_new();
_frame_rate_label = gtk_label_new("");
gtk_container_add(GTK_CONTAINER(_frame_rate_menu_item), _frame_rate_label);
+ gtk_widget_set_sensitive(_frame_rate_menu_item, FALSE);
gtk_widget_show(_frame_rate_menu_item);
gtk_widget_show(_frame_rate_label);
- gtk_menu_item_right_justify(GTK_MENU_ITEM(_frame_rate_menu_item));
+ gtk_menu_item_set_right_justified(GTK_MENU_ITEM(_frame_rate_menu_item), TRUE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), _frame_rate_menu_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(_menu_bar), _frame_rate_menu_item);
}
/**
- *
+ * Updates the status bar.
*/
void GtkStatsMonitor::
-handle_menu_command(gpointer callback_data, guint menu_id, GtkWidget *widget) {
- GtkStatsMonitor *self = (GtkStatsMonitor *)callback_data;
- switch (menu_id) {
- case MI_none:
+update_status_bar() {
+ const PStatClientData *client_data = get_client_data();
+ if (client_data == nullptr) {
+ return;
+ }
+
+ const PStatThreadData *thread_data = get_client_data()->get_thread_data(0);
+ if (thread_data == nullptr || thread_data->is_empty()) {
+ return;
+ }
+ int frame_number = thread_data->get_latest_frame_number();
+ const PStatFrameData &frame_data = thread_data->get_latest_frame();
+
+ pvector collectors;
+
+ // The first label displays the frame rate.
+ size_t li = 1;
+ collectors.push_back(0);
+ if (_status_bar_labels.empty()) {
+ GtkWidget *label = gtk_label_new("");
+ gtk_container_add(GTK_CONTAINER(_status_bar), label);
+ _status_bar_labels.push_back(label);
+ }
+
+ // Gather the top-level collector list.
+ int num_toplevel_collectors = client_data->get_num_toplevel_collectors();
+ for (int tc = 0; tc < num_toplevel_collectors; tc++) {
+ int collector = client_data->get_toplevel_collector(tc);
+ if (client_data->has_collector(collector) &&
+ client_data->get_collector_has_level(collector, 0)) {
+ PStatView &view = get_level_view(collector, 0);
+ view.set_to_frame(frame_data);
+ double value = view.get_net_value();
+ if (value == 0.0) {
+ // Don't include it unless we've included it before.
+ if (std::find(_status_bar_collectors.begin(), _status_bar_collectors.end(), collector) == _status_bar_collectors.end()) {
+ continue;
+ }
+ }
+
+ // Add the value for other threads that have this collector.
+ for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
+ PStatView &view = get_level_view(collector, thread_index);
+ view.set_to_frame(frame_number);
+ value += view.get_net_value();
+ }
+
+ const PStatCollectorDef &def = client_data->get_collector_def(collector);
+ std::string text = def._name;
+ text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
+
+ GtkWidget *label;
+ if (li < _status_bar_labels.size()) {
+ label = _status_bar_labels[li++];
+ gtk_label_set_text(GTK_LABEL(label), text.c_str());
+ }
+ else {
+ label = gtk_label_new(text.c_str());
+ gtk_container_add(GTK_CONTAINER(_status_bar), label);
+ _status_bar_labels.push_back(label);
+ }
+
+ collectors.push_back(collector);
+ }
+ }
+
+ _status_bar_collectors = std::move(collectors);
+
+ gtk_widget_show_all(_status_bar);
+}
+
+/**
+ * Handles clicks on a partion of the status bar.
+ */
+gboolean GtkStatsMonitor::
+status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) {
+ GtkStatsMonitor *monitor = (GtkStatsMonitor *)data;
+
+ GtkFlowBoxChild *child = gtk_flow_box_get_child_at_pos(
+ GTK_FLOW_BOX(monitor->_status_bar), event->x, event->y);
+ if (child == nullptr) {
+ return FALSE;
+ }
+
+ // Which child is this?
+ GList *children = gtk_container_get_children(GTK_CONTAINER(monitor->_status_bar));
+ int index = g_list_index(children, child);
+ g_list_free(children);
+ if (index < 0 || index >= monitor->_status_bar_labels.size()) {
+ return FALSE;
+ }
+
+ const PStatClientData *client_data = monitor->get_client_data();
+ if (client_data == nullptr) {
+ return FALSE;
+ }
+
+ int collector = monitor->_status_bar_collectors[index];
+
+ if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ monitor->open_strip_chart(0, collector, collector != 0);
+
+ // Also open a strip chart for other threads with data for this
+ // collector.
+ if (collector != 0) {
+ for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
+ PStatView &view = monitor->get_level_view(collector, thread_index);
+ if (view.get_net_value() > 0.0) {
+ monitor->open_strip_chart(thread_index, collector, true);
+ }
+ }
+ }
+ return TRUE;
+ }
+ else if (event->type == GDK_BUTTON_PRESS && event->button == 3 && index > 0) {
+ PStatView &level_view = monitor->get_level_view(collector, 0);
+ const PStatViewLevel *view_level = level_view.get_top_level();
+ int num_children = view_level->get_num_children();
+ if (num_children == 0) {
+ return FALSE;
+ }
+
+ GtkWidget *menu = gtk_menu_new();
+
+ // Reverse the order since the menus are listed from the top down; we want
+ // to be visually consistent with the graphs, which list these labels from
+ // the bottom up.
+ for (int c = num_children - 1; c >= 0; c--) {
+ const PStatViewLevel *child_level = view_level->get_child(c);
+
+ int child_collector = child_level->get_collector();
+ const MenuDef *menu_def = monitor->add_menu({0, child_collector, CT_strip_chart, true});
+
+ double value = child_level->get_net_value();
+
+ const PStatCollectorDef &def = client_data->get_collector_def(child_collector);
+ std::string text = def._name;
+ text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(text.c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(menu_activate),
+ (void *)menu_def);
+ }
+
+ gtk_widget_show_all(menu);
+
+ GtkWidget *label = monitor->_status_bar_labels[index];
+ gtk_menu_popup_at_widget(GTK_MENU(menu), label,
+ GDK_GRAVITY_NORTH_WEST,
+ GDK_GRAVITY_SOUTH_WEST, nullptr);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Callback when a menu item is selected.
+ */
+void GtkStatsMonitor::
+menu_activate(GtkWidget *widget, gpointer data) {
+ const MenuDef *menu_def = (const MenuDef *)data;
+ GtkStatsMonitor *monitor = menu_def->_monitor;
+
+ if (monitor == nullptr) {
+ return;
+ }
+
+ switch (menu_def->_chart_type) {
+ case CT_timeline:
+ monitor->open_timeline();
break;
- case MI_time_ms:
- self->set_time_units(PStatGraph::GBU_ms);
+ case CT_strip_chart:
+ monitor->open_strip_chart(menu_def->_thread_index,
+ menu_def->_collector_index,
+ menu_def->_show_level);
break;
- case MI_time_hz:
- self->set_time_units(PStatGraph::GBU_hz);
+ case CT_flame_graph:
+ monitor->open_flame_graph(menu_def->_thread_index,
+ menu_def->_collector_index);
break;
- case MI_speed_1:
- self->set_scroll_speed(1);
- break;
-
- case MI_speed_2:
- self->set_scroll_speed(2);
- break;
-
- case MI_speed_3:
- self->set_scroll_speed(3);
- break;
-
- case MI_speed_6:
- self->set_scroll_speed(6);
- break;
-
- case MI_speed_12:
- self->set_scroll_speed(12);
- break;
-
- case MI_pause:
- self->set_pause(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)));
+ case CT_piano_roll:
+ monitor->open_piano_roll(menu_def->_thread_index);
break;
}
}
diff --git a/pandatool/src/gtk-stats/gtkStatsMonitor.h b/pandatool/src/gtk-stats/gtkStatsMonitor.h
index be1f1c7d42..99f1baecee 100644
--- a/pandatool/src/gtk-stats/gtkStatsMonitor.h
+++ b/pandatool/src/gtk-stats/gtkStatsMonitor.h
@@ -34,13 +34,22 @@ class GtkStatsChartMenu;
*/
class GtkStatsMonitor : public PStatMonitor {
public:
+ enum ChartType {
+ CT_timeline,
+ CT_strip_chart,
+ CT_flame_graph,
+ CT_piano_roll,
+ };
+
class MenuDef {
public:
- INLINE MenuDef(int thread_index, int collector_index, bool show_level);
+ INLINE MenuDef(int thread_index, int collector_index,
+ ChartType chart_type, bool show_level = false);
INLINE bool operator < (const MenuDef &other) const;
int _thread_index;
int _collector_index;
+ ChartType _chart_type;
bool _show_level;
GtkStatsMonitor *_monitor;
};
@@ -64,8 +73,12 @@ public:
virtual void user_guide_bars_changed();
GtkWidget *get_window() const;
+ double get_resolution() const;
+
void open_strip_chart(int thread_index, int collector_index, bool show_level);
void open_piano_roll(int thread_index);
+ void open_flame_graph(int thread_index, int collector_index = -1);
+ void open_timeline();
const MenuDef *add_menu(const MenuDef &menu_def);
@@ -82,10 +95,19 @@ private:
static gboolean window_delete_event(GtkWidget *widget, GdkEvent *event,
gpointer data);
static void window_destroy(GtkWidget *widget, gpointer data);
+ void setup_options_menu();
+ void setup_speed_menu();
void setup_frame_rate_label();
+ void update_status_bar();
+ bool show_popup_menu(int collector);
- static void handle_menu_command(gpointer callback_data, guint menu_id, GtkWidget *widget);
+ static gboolean status_bar_button_event(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data);
+public:
+ static void menu_activate(GtkWidget *widget, gpointer data);
+private:
typedef pset Graphs;
Graphs _graphs;
@@ -96,17 +118,20 @@ private:
Menus _menus;
GtkWidget *_window;
- GtkItemFactory *_item_factory;
+ GtkWidget *_menu_bar;
+ GtkWidget *_options_menu;
+ GtkWidget *_speed_menu;
int _next_chart_index;
GtkWidget *_frame_rate_menu_item;
GtkWidget *_frame_rate_label;
+ GtkWidget *_status_bar;
+ pvector _status_bar_collectors;
+ pvector _status_bar_labels;
std::string _window_title;
int _time_units;
double _scroll_speed;
bool _pause;
-
- static GtkItemFactoryEntry menu_entries[];
- static int num_menu_entries;
+ double _resolution;
friend class GtkStatsGraph;
};
diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx
index 60a3dc7887..4c86269f02 100644
--- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.cxx
@@ -16,18 +16,16 @@
#include "numeric_types.h"
#include "gtkStatsLabelStack.h"
-static const int default_piano_roll_width = 400;
-static const int default_piano_roll_height = 200;
+static const int default_piano_roll_width = 800;
+static const int default_piano_roll_height = 400;
/**
*
*/
GtkStatsPianoRoll::
GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) :
- PStatPianoRoll(monitor, thread_index,
- default_piano_roll_width,
- default_piano_roll_height),
- GtkStatsGraph(monitor)
+ PStatPianoRoll(monitor, thread_index, 0, 0),
+ GtkStatsGraph(monitor, true)
{
// Let's show the units on the guide bar labels. There's room.
set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
@@ -35,15 +33,22 @@ GtkStatsPianoRoll(GtkStatsMonitor *monitor, int thread_index) :
// Add a DrawingArea widget on top of the graph, to display all of the scale
// units.
_scale_area = gtk_drawing_area_new();
- g_signal_connect(G_OBJECT(_scale_area), "expose_event",
- G_CALLBACK(expose_event_callback), this);
- gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area,
- FALSE, FALSE, 0);
- gtk_widget_set_size_request(_scale_area, 0, 20);
+ g_signal_connect(G_OBJECT(_scale_area), "draw",
+ G_CALLBACK(draw_callback), this);
+ gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0);
+ // It should be large enough to display the labels.
+ {
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "0123456789 ms");
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+ gtk_widget_set_size_request(_scale_area, 0, height + 1);
+ g_object_unref(layout);
+ }
- gtk_widget_set_size_request(_graph_window, default_piano_roll_width,
- default_piano_roll_height);
+ gtk_widget_set_size_request(_graph_window,
+ default_piano_roll_width * monitor->get_resolution() / 96,
+ default_piano_roll_height * monitor->get_resolution() / 96);
const PStatClientData *client_data =
GtkStatsGraph::_monitor->get_client_data();
@@ -70,7 +75,7 @@ GtkStatsPianoRoll::
}
/**
- * Called as each frame's data is made available. There is no gurantee the
+ * Called as each frame's data is made available. There is no guarantee the
* frames will arrive in order, or that all of them will arrive at all. The
* monitor should be prepared to accept frames received out-of-order or
* missing.
@@ -87,7 +92,9 @@ new_data(int thread_index, int frame_number) {
*/
void GtkStatsPianoRoll::
force_redraw() {
- PStatPianoRoll::force_redraw();
+ if (_cr) {
+ PStatPianoRoll::force_redraw();
+ }
}
/**
@@ -119,12 +126,63 @@ set_time_units(int unit_mask) {
* Called when the user single-clicks on a label.
*/
void GtkStatsPianoRoll::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
if (collector_index >= 0) {
GtkStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false);
}
}
+/**
+ * Called when the user right-clicks on a label.
+ */
+void GtkStatsPianoRoll::
+on_popup_label(int collector_index) {
+ GtkWidget *menu = gtk_menu_new();
+
+ std::string label = get_label_tooltip(collector_index);
+ if (!label.empty()) {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ _thread_index, collector_index, GtkStatsMonitor::CT_strip_chart, false,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ gtk_widget_show_all(menu);
+ gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsPianoRoll::
+get_label_tooltip(int collector_index) const {
+ return PStatPianoRoll::get_label_tooltip(collector_index);
+}
+
/**
* Changes the amount of time the width of the horizontal axis represents.
* This may force a redraw.
@@ -142,9 +200,8 @@ set_horizontal_scale(double time_width) {
*/
void GtkStatsPianoRoll::
clear_region() {
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
- gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0,
- get_xsize(), get_ysize());
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_paint(_cr);
}
/**
@@ -157,10 +214,22 @@ begin_draw() {
// Draw in the guide bars.
int num_guide_bars = get_num_guide_bars();
for (int i = 0; i < num_guide_bars; i++) {
- draw_guide_bar(_pixmap, get_guide_bar(i));
+ draw_guide_bar(_cr, get_guide_bar(i));
}
}
+/**
+ * Should be overridden by the user class. This hook will be called before
+ * drawing any one row of bars. These bars correspond to the collector whose
+ * index is get_row_collector(row), and in the color get_row_color(row).
+ */
+void GtkStatsPianoRoll::
+begin_row(int row) {
+ int collector_index = get_label_collector(row);
+ cairo_set_source(_cr, get_collector_pattern(collector_index,
+ _highlighted_index == collector_index));
+}
+
/**
* Draws a single bar on the chart.
*/
@@ -170,12 +239,8 @@ draw_bar(int row, int from_x, int to_x) {
int y = _label_stack.get_label_y(row, _graph_window);
int height = _label_stack.get_label_height(row);
- int collector_index = get_label_collector(row);
- GdkGC *gc = get_collector_gc(collector_index);
-
- gdk_draw_rectangle(_pixmap, gc, TRUE,
- from_x, y - height + 2,
- to_x - from_x, height - 4);
+ cairo_rectangle(_cr, from_x, (y - height + 2) * _cr_scale, to_x - from_x, (height - 4) * _cr_scale);
+ cairo_fill(_cr);
}
}
@@ -199,17 +264,30 @@ idle() {
}
/**
- * This is called during the servicing of expose_event; it gives a derived
+ * This is called during the servicing of the draw event; it gives a derived
* class opportunity to do some further painting into the graph window.
*/
void GtkStatsPianoRoll::
-additional_graph_window_paint() {
+additional_graph_window_paint(cairo_t *cr) {
int num_user_guide_bars = get_num_user_guide_bars();
for (int i = 0; i < num_user_guide_bars; i++) {
- draw_guide_bar(_graph_window->window, get_user_guide_bar(i));
+ draw_guide_bar(cr, get_user_guide_bar(i));
}
}
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsPianoRoll::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ int collector_index = get_collector_under_pixel(mouse_x, mouse_y);
+ if (collector_index >= 0) {
+ return get_label_tooltip(collector_index);
+ }
+ return std::string();
+}
+
/**
* Based on the mouse position within the graph window, look for draggable
* things the mouse might be hovering over and return the appropriate DragMode
@@ -242,13 +320,24 @@ consider_drag_start(int graph_x, int graph_y) {
* Called when the mouse button is depressed within the graph window.
*/
gboolean GtkStatsPianoRoll::
-handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click) {
- if (double_click) {
- // Double-clicking on a color bar in the graph is the same as double-
- // clicking on the corresponding label.
- clicked_label(get_collector_under_pixel(graph_x, graph_y));
- return TRUE;
+handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
+ if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ int collector_index = get_collector_under_pixel(graph_x, graph_y);
+ if (button == 3) {
+ // Right-clicking on a color bar in the graph is the same as right-
+ // clicking on the corresponding label.
+ if (collector_index >= 0) {
+ on_popup_label(collector_index);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if (double_click && button == 1) {
+ // Double-clicking on a color bar in the graph is the same as double-
+ // clicking on the corresponding label.
+ on_click_label(get_collector_under_pixel(graph_x, graph_y));
+ return TRUE;
+ }
}
if (_potential_drag_mode == DM_none) {
@@ -264,21 +353,21 @@ handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
return TRUE;
}
- return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y,
- double_click);
+ return GtkStatsGraph::handle_button_press(graph_x, graph_y,
+ double_click, button);
}
/**
* Called when the mouse button is released within the graph window.
*/
gboolean GtkStatsPianoRoll::
-handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
+handle_button_release(int graph_x, int graph_y) {
if (_drag_mode == DM_scale) {
set_drag_mode(DM_none);
// ReleaseCapture();
- return handle_motion(widget, graph_x, graph_y);
-
- } else if (_drag_mode == DM_guide_bar) {
+ return handle_motion(graph_x, graph_y);
+ }
+ else if (_drag_mode == DM_guide_bar) {
if (graph_x < 0 || graph_x >= get_xsize()) {
remove_user_guide_bar(_drag_guide_bar);
} else {
@@ -286,20 +375,22 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
}
set_drag_mode(DM_none);
// ReleaseCapture();
- return handle_motion(widget, graph_x, graph_y);
+ return handle_motion(graph_x, graph_y);
}
- return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y);
+ return GtkStatsGraph::handle_button_release(graph_x, graph_y);
}
/**
* Called when the mouse is moved within the graph window.
*/
gboolean GtkStatsPianoRoll::
-handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
+handle_motion(int graph_x, int graph_y) {
if (_drag_mode == DM_none && _potential_drag_mode == DM_none) {
// When the mouse is over a color bar, highlight it.
- _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y));
+ int collector_index = get_collector_under_pixel(graph_x, graph_y);
+ _label_stack.highlight_label(collector_index);
+ on_enter_label(collector_index);
/*
// Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
@@ -312,10 +403,11 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
};
TrackMouseEvent(&tme);
*/
-
- } else {
+ }
+ else {
// If the mouse is in some drag mode, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
}
if (_drag_mode == DM_scale) {
@@ -324,8 +416,8 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
set_horizontal_scale(_drag_scale_start / ratio);
}
return TRUE;
-
- } else if (_drag_mode == DM_new_guide_bar) {
+ }
+ else if (_drag_mode == DM_new_guide_bar) {
// We haven't created the new guide bar yet; we won't until the mouse
// comes within the graph's region.
if (graph_x >= 0 && graph_x < get_xsize()) {
@@ -339,7 +431,17 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
return TRUE;
}
- return GtkStatsGraph::handle_motion(widget, graph_x, graph_y);
+ return GtkStatsGraph::handle_motion(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse has left the graph window.
+ */
+gboolean GtkStatsPianoRoll::
+handle_leave() {
+ _label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
+ return TRUE;
}
/**
@@ -347,14 +449,14 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
* -1.
*/
int GtkStatsPianoRoll::
-get_collector_under_pixel(int xpoint, int ypoint) {
+get_collector_under_pixel(int xpoint, int ypoint) const {
if (_label_stack.get_num_labels() == 0) {
return -1;
}
// Assume all of the labels are the same height.
int height = _label_stack.get_label_height(0);
- int row = (get_ysize() - ypoint) / height;
+ int row = (get_ysize() - ypoint) / (height * _cr_scale);
if (row >= 0 && row < _label_stack.get_num_labels()) {
return _label_stack.get_label_collector_index(row);
} else {
@@ -380,42 +482,44 @@ update_labels() {
* Draws the line for the indicated guide bar on the graph.
*/
void GtkStatsPianoRoll::
-draw_guide_bar(GdkDrawable *surface, const PStatGraph::GuideBar &bar) {
+draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar) {
int x = height_to_pixel(bar._height);
if (x > 0 && x < get_xsize() - 1) {
// Only draw it if it's not too close to the top.
switch (bar._style) {
case GBS_target:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_light_gray);
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
break;
case GBS_user:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_user_guide_bar);
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
break;
- case GBS_normal:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_dark_gray);
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
break;
}
- gdk_draw_line(surface, _pixmap_gc, x, 0, x, get_ysize());
+ cairo_move_to(cr, x, 0);
+ cairo_line_to(cr, x, get_ysize());
+ cairo_stroke(cr);
}
}
/**
- * This is called during the servicing of expose_event.
+ * This is called during the servicing of the draw event.
*/
void GtkStatsPianoRoll::
-draw_guide_labels() {
+draw_guide_labels(cairo_t *cr) {
int i;
int num_guide_bars = get_num_guide_bars();
for (i = 0; i < num_guide_bars; i++) {
- draw_guide_label(get_guide_bar(i));
+ draw_guide_label(cr, get_guide_bar(i));
}
int num_user_guide_bars = get_num_user_guide_bars();
for (i = 0; i < num_user_guide_bars; i++) {
- draw_guide_label(get_user_guide_bar(i));
+ draw_guide_label(cr, get_user_guide_bar(i));
}
}
@@ -423,37 +527,34 @@ draw_guide_labels() {
* Draws the text for the indicated guide bar label at the top of the graph.
*/
void GtkStatsPianoRoll::
-draw_guide_label(const PStatGraph::GuideBar &bar) {
- GdkGC *gc = gdk_gc_new(_scale_area->window);
-
+draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) {
switch (bar._style) {
case GBS_target:
- gdk_gc_set_rgb_fg_color(gc, &rgb_light_gray);
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
break;
case GBS_user:
- gdk_gc_set_rgb_fg_color(gc, &rgb_user_guide_bar);
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
break;
- case GBS_normal:
- gdk_gc_set_rgb_fg_color(gc, &rgb_dark_gray);
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
break;
}
int x = height_to_pixel(bar._height);
const std::string &label = bar._label;
- PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str());
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str());
int width, height;
pango_layout_get_pixel_size(layout, &width, &height);
if (bar._style != GBS_user) {
- double from_height = pixel_to_height(x - width);
- double to_height = pixel_to_height(x + width);
+ double from_height = pixel_to_height(x - width * _cr_scale);
+ double to_height = pixel_to_height(x + width * _cr_scale);
if (find_user_guide_bar(from_height, to_height) >= 0) {
// Omit the label: there's a user-defined guide bar in the same space.
g_object_unref(layout);
- g_object_unref(gc);
return;
}
}
@@ -462,27 +563,31 @@ draw_guide_label(const PStatGraph::GuideBar &bar) {
// Now convert our x to a coordinate within our drawing area.
int junk_y;
+ x /= _cr_scale;
+
// The x coordinate comes from the graph_window.
gtk_widget_translate_coordinates(_graph_window, _scale_area,
x, 0,
&x, &junk_y);
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(_scale_area, &allocation);
+
int this_x = x - width / 2;
- gdk_draw_layout(_scale_area->window, gc, this_x,
- _scale_area->allocation.height - height, layout);
+ cairo_move_to(cr, this_x, allocation.height - height);
+ pango_cairo_show_layout(cr, layout);
}
g_object_unref(layout);
- g_object_unref(gc);
}
/**
* Draws in the scale labels.
*/
gboolean GtkStatsPianoRoll::
-expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
+draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
GtkStatsPianoRoll *self = (GtkStatsPianoRoll *)data;
- self->draw_guide_labels();
+ self->draw_guide_labels(cr);
return TRUE;
}
diff --git a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h
index cf54e75c66..18a30ee633 100644
--- a/pandatool/src/gtk-stats/gtkStatsPianoRoll.h
+++ b/pandatool/src/gtk-stats/gtkStatsPianoRoll.h
@@ -38,33 +38,37 @@ public:
virtual void changed_graph_size(int graph_xsize, int graph_ysize);
virtual void set_time_units(int unit_mask);
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
void set_horizontal_scale(double time_width);
protected:
void clear_region();
virtual void begin_draw();
+ virtual void begin_row(int row);
virtual void draw_bar(int row, int from_x, int to_x);
virtual void end_draw();
virtual void idle();
- virtual void additional_graph_window_paint();
+ virtual void additional_graph_window_paint(cairo_t *cr);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int graph_x, int graph_y);
- virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click);
- virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y);
- virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y);
+ virtual gboolean handle_button_press(int graph_x, int graph_y,
+ bool double_click, int button);
+ virtual gboolean handle_button_release(int graph_x, int graph_y);
+ virtual gboolean handle_motion(int graph_x, int graph_y);
+ virtual gboolean handle_leave();
private:
- int get_collector_under_pixel(int xpoint, int ypoint);
+ int get_collector_under_pixel(int xpoint, int ypoint) const;
void update_labels();
- void draw_guide_bar(GdkDrawable *surface, const PStatGraph::GuideBar &bar);
- void draw_guide_labels();
- void draw_guide_label(const PStatGraph::GuideBar &bar);
+ void draw_guide_bar(cairo_t *cr, const PStatGraph::GuideBar &bar);
+ void draw_guide_labels(cairo_t *cr);
+ void draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar);
- static gboolean expose_event_callback(GtkWidget *widget,
- GdkEventExpose *event, gpointer data);
+ static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
};
#endif
diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx
index 117ef96576..78c31d6e45 100644
--- a/pandatool/src/gtk-stats/gtkStatsStripChart.cxx
+++ b/pandatool/src/gtk-stats/gtkStatsStripChart.cxx
@@ -26,15 +26,10 @@ GtkStatsStripChart::
GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index,
int collector_index, bool show_level) :
PStatStripChart(monitor,
- show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index),
- thread_index,
- collector_index,
- default_strip_chart_width,
- default_strip_chart_height),
- GtkStatsGraph(monitor)
+ show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index),
+ thread_index, collector_index, 0, 0),
+ GtkStatsGraph(monitor, true)
{
- _brush_origin = 0;
-
if (show_level) {
// If it's a level-type graph, show the appropriate units.
if (_unit_name.empty()) {
@@ -49,32 +44,36 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index,
}
// Put some stuff on top of the graph.
- _top_hbox = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox,
- FALSE, FALSE, 0);
+ _top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox, FALSE, FALSE, 0);
_smooth_check_box = gtk_check_button_new_with_label("Smooth");
g_signal_connect(G_OBJECT(_smooth_check_box), "toggled",
- G_CALLBACK(toggled_callback), this);
+ G_CALLBACK(toggled_callback), this);
_total_label = gtk_label_new("");
- gtk_box_pack_start(GTK_BOX(_top_hbox), _smooth_check_box,
- FALSE, FALSE, 0);
- gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label,
- FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(_top_hbox), _smooth_check_box, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0);
// Add a DrawingArea widget to the right of the graph, to display all of the
// scale units.
_scale_area = gtk_drawing_area_new();
- g_signal_connect(G_OBJECT(_scale_area), "expose_event",
- G_CALLBACK(expose_event_callback), this);
- gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area,
- FALSE, FALSE, 0);
- gtk_widget_set_size_request(_scale_area, 40, 0);
+ g_signal_connect(G_OBJECT(_scale_area), "draw",
+ G_CALLBACK(draw_callback), this);
+ gtk_box_pack_start(GTK_BOX(_graph_hbox), _scale_area, FALSE, FALSE, 0);
+ // Make it wide enough to display a typical label.
+ {
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "99 ms");
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+ gtk_widget_set_size_request(_scale_area, width, 0);
+ g_object_unref(layout);
+ }
- gtk_widget_set_size_request(_graph_window, default_strip_chart_width,
- default_strip_chart_height);
+ gtk_widget_set_size_request(_graph_window,
+ default_strip_chart_width * monitor->get_resolution() / 96,
+ default_strip_chart_height * monitor->get_resolution() / 96);
gtk_widget_show_all(_window);
gtk_widget_show(_window);
@@ -82,7 +81,7 @@ GtkStatsStripChart(GtkStatsMonitor *monitor, int thread_index,
// Allow the window to be resized as small as the user likes. We have to do
// this after the window has been shown; otherwise, it will affect the
// window's initial size.
- gtk_widget_set_size_request(_window, 0, 0);
+ gtk_widget_set_size_request(_graph_window, 0, 0);
clear_region();
}
@@ -103,7 +102,7 @@ new_collector(int collector_index) {
}
/**
- * Called as each frame's data is made available. There is no gurantee the
+ * Called as each frame's data is made available. There is no guarantee the
* frames will arrive in order, or that all of them will arrive at all. The
* monitor should be prepared to accept frames received out-of-order or
* missing.
@@ -133,7 +132,9 @@ new_data(int thread_index, int frame_number) {
*/
void GtkStatsStripChart::
force_redraw() {
- PStatStripChart::force_redraw();
+ if (_cr) {
+ PStatStripChart::force_redraw();
+ }
}
/**
@@ -177,7 +178,7 @@ set_scroll_speed(double scroll_speed) {
* Called when the user single-clicks on a label.
*/
void GtkStatsStripChart::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
if (collector_index < 0) {
// Clicking on whitespace in the graph is the same as clicking on the top
// label.
@@ -204,6 +205,75 @@ clicked_label(int collector_index) {
}
}
+/**
+ * Called when the user right-clicks on a label.
+ */
+void GtkStatsStripChart::
+on_popup_label(int collector_index) {
+ GtkWidget *menu = gtk_menu_new();
+ _popup_index = collector_index;
+
+ std::string label = get_label_tooltip(collector_index);
+ if (!label.empty()) {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ }
+
+ {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Set as Focus");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+
+ if (collector_index == 0 && get_collector_index() == 0) {
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ } else {
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(+[] (GtkWidget *widget, gpointer data) {
+ GtkStatsStripChart *self = (GtkStatsStripChart *)data;
+ self->set_collector_index(self->_popup_index);
+ }),
+ this);
+ }
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ _thread_index, collector_index,
+ GtkStatsMonitor::CT_strip_chart, get_view().get_show_level(),
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ if (!get_view().get_show_level()) {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ _thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ gtk_widget_show_all(menu);
+ gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsStripChart::
+get_label_tooltip(int collector_index) const {
+ return PStatStripChart::get_label_tooltip(collector_index);
+}
+
/**
* Changes the value the height of the vertical axis represents. This may
* force a redraw.
@@ -236,9 +306,8 @@ update_labels() {
*/
void GtkStatsStripChart::
clear_region() {
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
- gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, 0, 0,
- get_xsize(), get_ysize());
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_paint(_cr);
}
/**
@@ -247,18 +316,26 @@ clear_region() {
*/
void GtkStatsStripChart::
copy_region(int start_x, int end_x, int dest_x) {
- gdk_draw_drawable(_pixmap, _pixmap_gc, _pixmap,
- start_x, 0, dest_x, 0,
- end_x - start_x, get_ysize());
+ // We are not allowed to copy a surface onto itself, so we have to create a
+ // temporary surface to copy to.
+ end_x = std::min(end_x, get_xsize());
+ GdkWindow *window = gtk_widget_get_window(_graph_window);
+ cairo_surface_t *temp_surface =
+ gdk_window_create_similar_image_surface(window, CAIRO_FORMAT_RGB24, end_x - start_x, get_ysize(), 1);
+ {
+ cairo_t *temp_cr = cairo_create(temp_surface);
+ cairo_set_source_surface(temp_cr, _cr_surface, -start_x, 0);
+ cairo_paint(temp_cr);
+ cairo_destroy(temp_cr);
+ }
- // Also shift the brush origin over, so we still get proper dithering.
- _brush_origin += (dest_x - start_x);
- // SetBrushOrgEx(_bitmap_dc, _brush_origin, 0, NULL);
+ cairo_set_source_surface(_cr, temp_surface, 0, 0);
+ cairo_rectangle(_cr, dest_x, 0, end_x - start_x, get_ysize());
+ cairo_fill(_cr);
- GdkRectangle rect = {
- dest_x, 0, end_x - start_x, get_ysize()
- };
- gdk_window_invalidate_rect(_graph_window->window, &rect, FALSE);
+ cairo_surface_destroy(temp_surface);
+
+ gdk_window_invalidate_rect(window, nullptr, FALSE);
}
/**
@@ -268,9 +345,9 @@ copy_region(int start_x, int end_x, int dest_x) {
void GtkStatsStripChart::
draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) {
// Start by clearing the band first.
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
- gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, x, 0,
- w + 1, get_ysize());
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_rectangle(_cr, x, 0, w, get_ysize());
+ cairo_fill(_cr);
double overall_time = 0.0;
int y = get_ysize();
@@ -279,18 +356,21 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) {
for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
const ColorData &cd = (*fi);
overall_time += cd._net_value;
- GdkGC *gc = get_collector_gc(cd._collector_index);
+ cairo_set_source(_cr, get_collector_pattern(cd._collector_index,
+ _highlighted_index == cd._collector_index));
if (overall_time > get_vertical_scale()) {
// Off the top. Go ahead and clamp it by hand, in case it's so far off
// the top we'd overflow the 16-bit pixel value.
- gdk_draw_rectangle(_pixmap, gc, TRUE, x, 0, w, y);
+ cairo_rectangle(_cr, x, 0, w, y);
+ cairo_fill(_cr);
// And we can consider ourselves done now.
return;
}
int top_y = height_to_pixel(overall_time);
- gdk_draw_rectangle(_pixmap, gc, TRUE, x, top_y, w, y - top_y);
+ cairo_rectangle(_cr, x, top_y, w, y - top_y);
+ cairo_fill(_cr);
y = top_y;
}
}
@@ -300,9 +380,8 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) {
*/
void GtkStatsStripChart::
draw_empty(int x, int w) {
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_white);
- gdk_draw_rectangle(_pixmap, _pixmap_gc, TRUE, x, 0,
- w + 1, get_ysize());
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_rectangle(_cr, x, 0, w, get_ysize());
}
/**
@@ -310,8 +389,10 @@ draw_empty(int x, int w) {
*/
void GtkStatsStripChart::
draw_cursor(int x) {
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_black);
- gdk_draw_line(_pixmap, _pixmap_gc, x, 0, x, get_ysize());
+ cairo_set_source_rgb(_cr, 0.0, 0.0, 0.0);
+ cairo_move_to(_cr, x, 0);
+ cairo_line_to(_cr, x, get_ysize());
+ cairo_stroke(_cr);
}
/**
@@ -324,27 +405,41 @@ end_draw(int from_x, int to_x) {
// Draw in the guide bars.
int num_guide_bars = get_num_guide_bars();
for (int i = 0; i < num_guide_bars; i++) {
- draw_guide_bar(_pixmap, from_x, to_x, get_guide_bar(i));
+ draw_guide_bar(_cr, from_x, to_x, get_guide_bar(i));
}
+ GdkWindow *window = gtk_widget_get_window(_graph_window);
+ int scale = gdk_window_get_scale_factor(window);
GdkRectangle rect = {
- from_x, 0, to_x - from_x + 1, get_ysize()
+ (from_x * scale) / scale, 0, (to_x - from_x) / scale, get_ysize() / scale
};
- gdk_window_invalidate_rect(_graph_window->window, &rect, FALSE);
+ gdk_window_invalidate_rect(window, &rect, FALSE);
}
/**
- * This is called during the servicing of expose_event; it gives a derived
+ * This is called during the servicing of the draw event; it gives a derived
* class opportunity to do some further painting into the graph window.
*/
void GtkStatsStripChart::
-additional_graph_window_paint() {
+additional_graph_window_paint(cairo_t *cr) {
int num_user_guide_bars = get_num_user_guide_bars();
for (int i = 0; i < num_user_guide_bars; i++) {
- draw_guide_bar(_graph_window->window, 0, get_xsize(), get_user_guide_bar(i));
+ draw_guide_bar(cr, 0, get_xsize(), get_user_guide_bar(i));
}
}
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsStripChart::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ if (_highlighted_index != -1) {
+ return get_label_tooltip(_highlighted_index);
+ }
+ return std::string();
+}
+
/**
* Based on the mouse position within the graph window, look for draggable
* things the mouse might be hovering over and return the appropriate DragMode
@@ -380,19 +475,11 @@ void GtkStatsStripChart::
set_drag_mode(GtkStatsGraph::DragMode drag_mode) {
GtkStatsGraph::set_drag_mode(drag_mode);
- switch (_drag_mode) {
- case DM_scale:
- case DM_sizing:
- // Disable smoothing for these expensive operations.
- set_average_mode(false);
- break;
-
- default:
+ if (_drag_mode == DM_none) {
// Restore smoothing according to the current setting of the check box.
bool active =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(_smooth_check_box));
set_average_mode(active);
- break;
}
}
@@ -400,43 +487,55 @@ set_drag_mode(GtkStatsGraph::DragMode drag_mode) {
* Called when the mouse button is depressed within the graph window.
*/
gboolean GtkStatsStripChart::
-handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click) {
- if (double_click) {
- // Double-clicking on a color bar in the graph is the same as double-
- // clicking on the corresponding label.
- clicked_label(get_collector_under_pixel(graph_x, graph_y));
- return TRUE;
+handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
+ if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ int collector_index = get_collector_under_pixel(graph_x, graph_y);
+ if (button == 3) {
+ // Right-clicking on a color bar in the graph is the same as right-
+ // clicking on the corresponding label.
+ if (collector_index >= 0) {
+ on_popup_label(collector_index);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if (double_click && button == 1) {
+ // Double-clicking on a color bar in the graph is the same as double-
+ // clicking on the corresponding label.
+ on_click_label(get_collector_under_pixel(graph_x, graph_y));
+ return TRUE;
+ }
+
+ if (_potential_drag_mode == DM_none) {
+ set_drag_mode(DM_scale);
+ _drag_scale_start = pixel_to_height(graph_y);
+ // SetCapture(_graph_window);
+ return TRUE;
+ }
}
- if (_potential_drag_mode == DM_none) {
- set_drag_mode(DM_scale);
- _drag_scale_start = pixel_to_height(graph_y);
- // SetCapture(_graph_window);
- return TRUE;
-
- } else if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
+ if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
set_drag_mode(DM_guide_bar);
_drag_start_y = graph_y;
// SetCapture(_graph_window);
return TRUE;
}
- return GtkStatsGraph::handle_button_press(widget, graph_x, graph_y,
- double_click);
+ return GtkStatsGraph::handle_button_press(graph_x, graph_y,
+ double_click, button);
}
/**
* Called when the mouse button is released within the graph window.
*/
gboolean GtkStatsStripChart::
-handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
+handle_button_release(int graph_x, int graph_y) {
if (_drag_mode == DM_scale) {
set_drag_mode(DM_none);
// ReleaseCapture();
- return handle_motion(widget, graph_x, graph_y);
-
- } else if (_drag_mode == DM_guide_bar) {
+ return handle_motion(graph_x, graph_y);
+ }
+ else if (_drag_mode == DM_guide_bar) {
if (graph_y < 0 || graph_y >= get_ysize()) {
remove_user_guide_bar(_drag_guide_bar);
} else {
@@ -444,46 +543,43 @@ handle_button_release(GtkWidget *widget, int graph_x, int graph_y) {
}
set_drag_mode(DM_none);
// ReleaseCapture();
- return handle_motion(widget, graph_x, graph_y);
+ return handle_motion(graph_x, graph_y);
}
- return GtkStatsGraph::handle_button_release(widget, graph_x, graph_y);
+ return GtkStatsGraph::handle_button_release(graph_x, graph_y);
}
/**
* Called when the mouse is moved within the graph window.
*/
gboolean GtkStatsStripChart::
-handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
- if (_drag_mode == DM_none && _potential_drag_mode == DM_none) {
+handle_motion(int graph_x, int graph_y) {
+ if (_drag_mode == DM_none && _potential_drag_mode == DM_none &&
+ graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
// When the mouse is over a color bar, highlight it.
- _label_stack.highlight_label(get_collector_under_pixel(graph_x, graph_y));
-
- /*
- // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
- // window.
- TRACKMOUSEEVENT tme = {
- sizeof(TRACKMOUSEEVENT),
- TME_LEAVE,
- _graph_window,
- 0
- };
- TrackMouseEvent(&tme);
- */
-
- } else {
+ int collector_index = get_collector_under_pixel(graph_x, graph_y);
+ _label_stack.highlight_label(collector_index);
+ on_enter_label(collector_index);
+ }
+ else {
// If the mouse is in some drag mode, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
}
if (_drag_mode == DM_scale) {
- double ratio = 1.0f - ((double)graph_y / (double)get_ysize());
- if (ratio > 0.0f) {
- set_vertical_scale(_drag_scale_start / ratio);
+ double ratio = 1.0 - ((double)graph_y / (double)get_ysize());
+ if (ratio > 0.0) {
+ double new_scale = _drag_scale_start / ratio;
+ if (!IS_NEARLY_EQUAL(get_vertical_scale(), new_scale)) {
+ // Disable smoothing while we do this expensive operation.
+ set_average_mode(false);
+ set_vertical_scale(_drag_scale_start / ratio);
+ }
}
return TRUE;
-
- } else if (_drag_mode == DM_new_guide_bar) {
+ }
+ else if (_drag_mode == DM_new_guide_bar) {
// We haven't created the new guide bar yet; we won't until the mouse
// comes within the graph's region.
if (graph_y >= 0 && graph_y < get_ysize()) {
@@ -491,20 +587,30 @@ handle_motion(GtkWidget *widget, int graph_x, int graph_y) {
_drag_guide_bar = add_user_guide_bar(pixel_to_height(graph_y));
return TRUE;
}
-
- } else if (_drag_mode == DM_guide_bar) {
+ }
+ else if (_drag_mode == DM_guide_bar) {
move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_y));
return TRUE;
}
- return GtkStatsGraph::handle_motion(widget, graph_x, graph_y);
+ return GtkStatsGraph::handle_motion(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse has left the graph window.
+ */
+gboolean GtkStatsStripChart::
+handle_leave() {
+ _label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
+ return TRUE;
}
/**
* Draws the line for the indicated guide bar on the graph.
*/
void GtkStatsStripChart::
-draw_guide_bar(GdkDrawable *surface, int from_x, int to_x,
+draw_guide_bar(cairo_t *cr, int from_x, int to_x,
const PStatGraph::GuideBar &bar) {
int y = height_to_pixel(bar._height);
@@ -512,42 +618,44 @@ draw_guide_bar(GdkDrawable *surface, int from_x, int to_x,
// Only draw it if it's not too close to the top.
switch (bar._style) {
case GBS_target:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_light_gray);
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
break;
case GBS_user:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_user_guide_bar);
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
break;
- case GBS_normal:
- gdk_gc_set_rgb_fg_color(_pixmap_gc, &rgb_dark_gray);
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
break;
}
- gdk_draw_line(surface, _pixmap_gc, from_x, y, to_x, y);
+ cairo_move_to(cr, from_x, y);
+ cairo_line_to(cr, to_x, y);
+ cairo_stroke(cr);
}
}
/**
- * This is called during the servicing of expose_event.
+ * This is called during the servicing of the draw event.
*/
void GtkStatsStripChart::
-draw_guide_labels() {
+draw_guide_labels(cairo_t *cr) {
// Draw in the labels for the guide bars.
int last_y = -100;
int i;
int num_guide_bars = get_num_guide_bars();
for (i = 0; i < num_guide_bars; i++) {
- last_y = draw_guide_label(get_guide_bar(i), last_y);
+ last_y = draw_guide_label(cr, get_guide_bar(i), last_y);
}
GuideBar top_value = make_guide_bar(get_vertical_scale());
- draw_guide_label(top_value, last_y);
+ draw_guide_label(cr, top_value, last_y);
last_y = -100;
int num_user_guide_bars = get_num_user_guide_bars();
for (i = 0; i < num_user_guide_bars; i++) {
- last_y = draw_guide_label(get_user_guide_bar(i), last_y);
+ last_y = draw_guide_label(cr, get_user_guide_bar(i), last_y);
}
}
@@ -557,37 +665,34 @@ draw_guide_labels() {
* value is given. Returns the top pixel value of the new label.
*/
int GtkStatsStripChart::
-draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) {
- GdkGC *gc = gdk_gc_new(_scale_area->window);
-
+draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y) {
switch (bar._style) {
case GBS_target:
- gdk_gc_set_rgb_fg_color(gc, &rgb_light_gray);
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
break;
case GBS_user:
- gdk_gc_set_rgb_fg_color(gc, &rgb_user_guide_bar);
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
break;
- case GBS_normal:
- gdk_gc_set_rgb_fg_color(gc, &rgb_dark_gray);
+ default:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
break;
}
int y = height_to_pixel(bar._height);
const std::string &label = bar._label;
- PangoLayout *layout = gtk_widget_create_pango_layout(_window, label.c_str());
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str());
int width, height;
pango_layout_get_pixel_size(layout, &width, &height);
if (bar._style != GBS_user) {
- double from_height = pixel_to_height(y + height);
- double to_height = pixel_to_height(y - height);
+ double from_height = pixel_to_height(y + height * _cr_scale);
+ double to_height = pixel_to_height(y - height * _cr_scale);
if (find_user_guide_bar(from_height, to_height) >= 0) {
// Omit the label: there's a user-defined guide bar in the same space.
g_object_unref(layout);
- g_object_unref(gc);
return last_y;
}
}
@@ -596,6 +701,8 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) {
// Now convert our y to a coordinate within our drawing area.
int junk_x;
+ y /= _cr_scale;
+
// The y coordinate comes from the graph_window.
gtk_widget_translate_coordinates(_graph_window, _scale_area,
0, y,
@@ -603,13 +710,13 @@ draw_guide_label(const PStatGraph::GuideBar &bar, int last_y) {
int this_y = y - height / 2;
if (last_y < this_y || last_y > this_y + height) {
- gdk_draw_layout(_scale_area->window, gc, 0, this_y, layout);
+ cairo_move_to(cr, 0, this_y);
+ pango_cairo_show_layout(cr, layout);
last_y = this_y;
}
}
g_object_unref(layout);
- g_object_unref(gc);
return last_y;
}
@@ -628,9 +735,9 @@ toggled_callback(GtkToggleButton *button, gpointer data) {
* Draws in the scale labels.
*/
gboolean GtkStatsStripChart::
-expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
+draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
GtkStatsStripChart *self = (GtkStatsStripChart *)data;
- self->draw_guide_labels();
+ self->draw_guide_labels(cr);
return TRUE;
}
diff --git a/pandatool/src/gtk-stats/gtkStatsStripChart.h b/pandatool/src/gtk-stats/gtkStatsStripChart.h
index 484d669c9f..01284e9719 100644
--- a/pandatool/src/gtk-stats/gtkStatsStripChart.h
+++ b/pandatool/src/gtk-stats/gtkStatsStripChart.h
@@ -40,7 +40,9 @@ public:
virtual void set_time_units(int unit_mask);
virtual void set_scroll_speed(double scroll_speed);
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
void set_vertical_scale(double value_height);
protected:
@@ -54,32 +56,34 @@ protected:
virtual void draw_cursor(int x);
virtual void end_draw(int from_x, int to_x);
- virtual void additional_graph_window_paint();
+ virtual void additional_graph_window_paint(cairo_t *cr);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int graph_x, int graph_y);
virtual void set_drag_mode(DragMode drag_mode);
- virtual gboolean handle_button_press(GtkWidget *widget, int graph_x, int graph_y,
- bool double_click);
- virtual gboolean handle_button_release(GtkWidget *widget, int graph_x, int graph_y);
- virtual gboolean handle_motion(GtkWidget *widget, int graph_x, int graph_y);
+ virtual gboolean handle_button_press(int graph_x, int graph_y,
+ bool double_click, int button);
+ virtual gboolean handle_button_release(int graph_x, int graph_y);
+ virtual gboolean handle_motion(int graph_x, int graph_y);
+ virtual gboolean handle_leave();
private:
- void draw_guide_bar(GdkDrawable *surface, int from_x, int to_x,
- const PStatGraph::GuideBar &bar);
- void draw_guide_labels();
- int draw_guide_label(const PStatGraph::GuideBar &bar, int last_y);
+ void draw_guide_bar(cairo_t *cr, int from_x, int to_x,
+ const PStatGraph::GuideBar &bar);
+ void draw_guide_labels(cairo_t *cr);
+ int draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar, int last_y);
static void toggled_callback(GtkToggleButton *button, gpointer data);
- static gboolean expose_event_callback(GtkWidget *widget,
- GdkEventExpose *event, gpointer data);
+ static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
private:
- int _brush_origin;
std::string _net_value_text;
GtkWidget *_top_hbox;
GtkWidget *_smooth_check_box;
GtkWidget *_total_label;
+
+ int _popup_index = -1;
};
#endif
diff --git a/pandatool/src/gtk-stats/gtkStatsTimeline.cxx b/pandatool/src/gtk-stats/gtkStatsTimeline.cxx
new file mode 100644
index 0000000000..fbc45ccc1b
--- /dev/null
+++ b/pandatool/src/gtk-stats/gtkStatsTimeline.cxx
@@ -0,0 +1,812 @@
+/**
+ * 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 gtkStatsTimeline.cxx
+ * @author rdb
+ * @date 2022-02-17
+ */
+
+#include "gtkStatsTimeline.h"
+#include "gtkStatsMonitor.h"
+#include "numeric_types.h"
+#include "gtkStatsLabelStack.h"
+
+static const int default_timeline_width = 1000;
+static const int default_timeline_height = 300;
+
+/**
+ *
+ */
+GtkStatsTimeline::
+GtkStatsTimeline(GtkStatsMonitor *monitor) :
+ PStatTimeline(monitor, 0, 0),
+ GtkStatsGraph(monitor, false)
+{
+ // Let's show the units on the guide bar labels. There's room.
+ set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
+
+ // Add a DrawingArea widget on top of the graph, to display all of the scale
+ // units.
+ _scale_area = gtk_drawing_area_new();
+ g_signal_connect(G_OBJECT(_scale_area), "draw",
+ G_CALLBACK(scale_area_draw_callback), this);
+ gtk_box_pack_start(GTK_BOX(_graph_vbox), _scale_area, FALSE, FALSE, 0);
+
+ // It should be large enough to display the labels.
+ {
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, "0123456789 ms");
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+ gtk_widget_set_size_request(_scale_area, 0, height + _pixel_scale / 2);
+ g_object_unref(layout);
+ }
+
+ // Add a drawing area to the left of the graph to show the thread labels.
+ _thread_area = gtk_drawing_area_new();
+ gtk_box_pack_start(GTK_BOX(_graph_hbox), _thread_area, FALSE, FALSE, 0);
+ gtk_box_reorder_child(GTK_BOX(_graph_hbox), _thread_area, 0);
+ g_signal_connect(G_OBJECT(_thread_area), "draw",
+ G_CALLBACK(thread_area_draw_callback), this);
+
+ // Listen for mouse wheel and keyboard events.
+ gtk_widget_add_events(_graph_window, GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK);
+ gtk_widget_set_can_focus(_graph_window, TRUE);
+ g_signal_connect(G_OBJECT(_graph_window), "scroll_event",
+ G_CALLBACK(scroll_callback), this);
+ g_signal_connect(G_OBJECT(_graph_window), "key_press_event",
+ G_CALLBACK(key_press_callback), this);
+ g_signal_connect(G_OBJECT(_graph_window), "key_release_event",
+ G_CALLBACK(key_release_callback), this);
+
+ int min_height = 0;
+ if (!_threads.empty()) {
+ int num_rows = _threads.back()._row_offset + _threads.back()._rows.size();
+ double height = row_to_pixel(num_rows) + _pixel_scale * 2.5;
+ min_height = height / _cr_scale;
+ }
+
+ gtk_widget_set_size_request(_graph_window,
+ default_timeline_width * monitor->get_resolution() / 96,
+ std::max(min_height, (int)(default_timeline_height * monitor->get_resolution() / 96)));
+
+ gtk_window_set_title(GTK_WINDOW(_window), "Timeline");
+
+ _grid_pattern = cairo_pattern_create_rgb(0xdd / 255.0, 0xdd / 255.0, 0xdd / 255.0);
+
+ gtk_widget_show_all(_window);
+ gtk_widget_show(_window);
+
+ // Allow the window to be resized as small as the user likes. We have to do
+ // this after the window has been shown; otherwise, it will affect the
+ // window's initial size.
+ gtk_widget_set_size_request(_graph_window, 0, min_height);
+
+ clear_region();
+}
+
+/**
+ *
+ */
+GtkStatsTimeline::
+~GtkStatsTimeline() {
+ cairo_pattern_destroy(_grid_pattern);
+}
+
+/**
+ * Called as each frame's data is made available. There is no guarantee the
+ * frames will arrive in order, or that all of them will arrive at all. The
+ * monitor should be prepared to accept frames received out-of-order or
+ * missing.
+ */
+void GtkStatsTimeline::
+new_data(int thread_index, int frame_number) {
+ PStatTimeline::new_data(thread_index, frame_number);
+}
+
+/**
+ * Called when it is necessary to redraw the entire graph.
+ */
+void GtkStatsTimeline::
+force_redraw() {
+ assert(_cr);
+ if (_cr) {
+ PStatTimeline::force_redraw();
+ }
+}
+
+/**
+ * Called when the user has resized the window, forcing a resize of the graph.
+ */
+void GtkStatsTimeline::
+changed_graph_size(int graph_xsize, int graph_ysize) {
+ PStatTimeline::changed_size(graph_xsize, graph_ysize);
+}
+
+/**
+ * Erases the chart area.
+ */
+void GtkStatsTimeline::
+clear_region() {
+ cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
+ cairo_paint(_cr);
+}
+
+/**
+ * Erases the chart area in preparation for drawing a bunch of bars.
+ */
+void GtkStatsTimeline::
+begin_draw() {
+}
+
+/**
+ * Draws a horizontal separator.
+ */
+void GtkStatsTimeline::
+draw_separator(int row) {
+ cairo_set_source(_cr, _grid_pattern);
+ cairo_rectangle(_cr, 0, (row_to_pixel(row) + row_to_pixel(row + 1)) / 2.0,
+ get_xsize(), _pixel_scale * 1 / 3);
+ cairo_fill(_cr);
+}
+
+/**
+ * Draws a vertical guide bar. If the row is -1, draws it in all rows.
+ */
+void GtkStatsTimeline::
+draw_guide_bar(int x, GuideBarStyle style) {
+ double width = _pixel_scale / 3.0;
+ if (style == GBS_frame) {
+ width *= 2;
+ }
+
+ cairo_set_source(_cr, _grid_pattern);
+ cairo_rectangle(_cr, x - width / 2.0, 0, width, get_ysize());
+ cairo_fill(_cr);
+}
+
+/**
+ * Draws a single bar in the chart for the indicated row, in the color for the
+ * given collector, for the indicated horizontal pixel range.
+ */
+void GtkStatsTimeline::
+draw_bar(int row, int from_x, int to_x, int collector_index,
+ const std::string &collector_name) {
+ int top = row_to_pixel(row);
+ int bottom = row_to_pixel(row + 1);
+ int scale = _pixel_scale;
+
+ bool is_highlighted = row == _highlighted_row && _highlighted_x >= from_x && _highlighted_x < to_x;
+ cairo_set_source(_cr, get_collector_pattern(collector_index, is_highlighted));
+
+ if (to_x < from_x + 1) {
+ // Too tiny to draw.
+ }
+ else if (to_x < from_x + scale) {
+ // It's just a tiny sliver. This is a more reliable way to draw it.
+ cairo_rectangle(_cr, from_x, top, to_x - from_x, bottom - top);
+ cairo_fill(_cr);
+ }
+ else {
+ int left = std::max(from_x, -scale - 1);
+ int right = std::min(std::max(to_x, from_x + 1), get_xsize() + scale);
+
+ double radius = std::min((double)scale, (right - left) / 2.0);
+ cairo_new_sub_path(_cr);
+ cairo_arc(_cr, right - radius, top + radius, radius, -0.5 * M_PI, 0.0);
+ cairo_arc(_cr, right - radius, bottom - radius, radius, 0.0, 0.5 * M_PI);
+ cairo_arc(_cr, left + radius, bottom - radius, radius, 0.5 * M_PI, M_PI);
+ cairo_arc(_cr, left + radius, top + radius, radius, M_PI, 1.5 * M_PI);
+ cairo_close_path(_cr);
+ cairo_fill(_cr);
+
+ if ((to_x - from_x) >= scale * 4) {
+ // Only bother drawing the text if we've got some space to draw on.
+ // Choose a suitable foreground color.
+ LRGBColor fg = get_collector_text_color(collector_index, is_highlighted);
+ cairo_set_source_rgb(_cr, fg[0], fg[1], fg[2]);
+
+ // Make sure that the text doesn't run off the chart.
+ int text_width, text_height;
+ PangoLayout *layout = gtk_widget_create_pango_layout(_graph_window, collector_name.c_str());
+ pango_layout_set_attributes(layout, _pango_attrs);
+ pango_layout_set_height(layout, -1);
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+
+ double center = (from_x + to_x) / 2.0;
+ double text_left = std::max(from_x, 0) + scale / 2.0;
+ double text_right = std::min(to_x, get_xsize()) - scale / 2.0;
+ double text_top = top + (bottom - top - text_height) / 2.0;
+
+ if (text_width >= text_right - text_left) {
+ if (text_right - text_left < scale * 6) {
+ // It's a really tiny space. Draw a single letter.
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_text(layout, collector_name.c_str(), 1);
+ } else {
+ // It's going to be tricky to fit it, let pango figure it out.
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ }
+ pango_layout_set_width(layout, (text_right - text_left) * PANGO_SCALE);
+ cairo_move_to(_cr, text_left, text_top);
+ }
+ else if (center - text_width / 2.0 < 0.0) {
+ // Put it against the left-most edge.
+ cairo_move_to(_cr, scale, text_top);
+ }
+ else if (center + text_width / 2.0 >= get_xsize()) {
+ // Put it against the right-most edge.
+ cairo_move_to(_cr, get_xsize() - scale - text_width, text_top);
+ }
+ else {
+ // It fits just fine, center it.
+ cairo_move_to(_cr, center - text_width / 2.0, text_top);
+ }
+
+ pango_cairo_show_layout(_cr, layout);
+ g_object_unref(layout);
+ }
+ }
+}
+
+/**
+ * Called after all the bars have been drawn, this triggers a refresh event to
+ * draw it to the window.
+ */
+void GtkStatsTimeline::
+end_draw() {
+ gtk_widget_queue_draw(_graph_window);
+
+ if (_threads_changed) {
+ // Make sure the window is large enough to fit all of the threads.
+ int num_rows = _threads.back()._row_offset + _threads.back()._rows.size();
+ double height = row_to_pixel(num_rows) + _pixel_scale * 2.5;
+ gtk_widget_set_size_request(_graph_window, 0, height / _cr_scale);
+
+ // Calculate the size of the thread area.
+ PangoLayout *layout = gtk_widget_create_pango_layout(_thread_area, "");
+
+ int max_width = 0;
+ for (const ThreadRow &thread_row : _threads) {
+ pango_layout_set_text(layout, thread_row._label.c_str(), thread_row._label.size());
+
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+
+ if (width > max_width) {
+ max_width = width;
+ }
+ }
+
+ gtk_widget_set_size_request(_thread_area, max_width + _pixel_scale * 2, 0);
+ g_object_unref(layout);
+ gtk_widget_queue_draw(_thread_area);
+ _threads_changed = false;
+ }
+
+ if (_guide_bars_changed) {
+ gtk_widget_queue_draw(_scale_area);
+ _guide_bars_changed = false;
+ }
+}
+
+/**
+ * Called at the end of the draw cycle.
+ */
+void GtkStatsTimeline::
+idle() {
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool GtkStatsTimeline::
+animate(double time, double dt) {
+ return PStatTimeline::animate(time, dt);
+}
+
+/**
+ * This is called during the servicing of the draw event; it gives a derived
+ * class opportunity to do some further painting into the graph window.
+ */
+void GtkStatsTimeline::
+additional_graph_window_paint(cairo_t *cr) {
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string GtkStatsTimeline::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return PStatTimeline::get_bar_tooltip(pixel_to_row(mouse_y), mouse_x);
+}
+
+/**
+ * Based on the mouse position within the graph window, look for draggable
+ * things the mouse might be hovering over and return the appropriate DragMode
+ * enum or DM_none if nothing is indicated.
+ */
+GtkStatsGraph::DragMode GtkStatsTimeline::
+consider_drag_start(int graph_x, int graph_y) {
+ return GtkStatsGraph::consider_drag_start(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse button is depressed within the graph window.
+ */
+gboolean GtkStatsTimeline::
+handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
+ if (graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ if (button == 3) {
+ // Right-clicking a color bar brings up a context menu.
+ int row = pixel_to_row(graph_y);
+
+ ColorBar bar;
+ if (find_bar(row, graph_x, bar)) {
+ GtkWidget *menu = gtk_menu_new();
+ _popup_bar = bar;
+
+ std::string label = get_bar_tooltip(row, graph_x);
+ if (!label.empty()) {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label(label.c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ }
+
+ {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Zoom To");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(+[] (GtkWidget *widget, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+ const ColorBar &bar = self->_popup_bar;
+ double width = bar._end - bar._start;
+ self->zoom_to(width * 1.5, (bar._end + bar._start) / 2.0);
+ self->scroll_to(bar._start - width / 4.0);
+ self->start_animation();
+ }),
+ this);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ bar._thread_index, bar._collector_index,
+ GtkStatsMonitor::CT_strip_chart, false,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ bar._thread_index, bar._collector_index,
+ GtkStatsMonitor::CT_flame_graph,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ {
+ const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
+ bar._thread_index, -1, GtkStatsMonitor::CT_piano_roll,
+ });
+
+ GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Piano Roll");
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ g_signal_connect(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(GtkStatsMonitor::menu_activate),
+ (void *)menu_def);
+ }
+
+ gtk_widget_show_all(menu);
+ gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if (double_click && button == 1) {
+ // Double-clicking on a color bar in the graph will zoom the graph into
+ // that collector.
+ int row = pixel_to_row(graph_y);
+ ColorBar bar;
+ if (find_bar(row, graph_x, bar)) {
+ double width = bar._end - bar._start;
+ zoom_to(width * 1.5, pixel_to_timestamp(graph_x));
+ scroll_to(bar._start - width / 4.0);
+ } else {
+ // Double-clicking the white area zooms out.
+ _zoom_speed -= 100.0;
+ }
+ start_animation();
+ }
+
+ if (_potential_drag_mode == DM_none) {
+ set_drag_mode(DM_pan);
+ _drag_start_x = graph_x;
+ _scroll_speed = 0.0;
+ _zoom_center = pixel_to_timestamp(graph_x);
+ return TRUE;
+ }
+ }
+
+ return GtkStatsGraph::handle_button_press(graph_x, graph_y,
+ double_click, button);
+}
+
+/**
+ * Called when the mouse button is released within the graph window.
+ */
+gboolean GtkStatsTimeline::
+handle_button_release(int graph_x, int graph_y) {
+ if (_drag_mode == DM_scale) {
+ set_drag_mode(DM_none);
+ // ReleaseCapture();
+ return handle_motion(graph_x, graph_y);
+ }
+ else if (_drag_mode == DM_guide_bar) {
+ if (graph_x < 0 || graph_x >= get_xsize()) {
+ remove_user_guide_bar(_drag_guide_bar);
+ } else {
+ move_user_guide_bar(_drag_guide_bar, pixel_to_height(graph_x));
+ }
+ set_drag_mode(DM_none);
+ // ReleaseCapture();
+ return handle_motion(graph_x, graph_y);
+ }
+
+ return GtkStatsGraph::handle_button_release(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse is moved within the graph window.
+ */
+gboolean GtkStatsTimeline::
+handle_motion(int graph_x, int graph_y) {
+ if (_drag_mode == DM_none && _potential_drag_mode == DM_none &&
+ graph_x >= 0 && graph_y >= 0 && graph_x < get_xsize() && graph_y < get_ysize()) {
+ // When the mouse is over a color bar, highlight it.
+ int row = pixel_to_row(graph_y);
+ std::swap(_highlighted_x, graph_x);
+ std::swap(_highlighted_row, row);
+
+ if (row >= 0) {
+ PStatTimeline::force_redraw(row, graph_x, graph_x);
+ }
+ PStatTimeline::force_redraw(_highlighted_row, _highlighted_x, _highlighted_x);
+
+ if ((_keys_held & (F_w | F_s)) != 0) {
+ // Update the zoom center if we move the mouse while zooming with the
+ // keyboard.
+ _zoom_center = pixel_to_timestamp(graph_x);
+ }
+ }
+ else {
+ // If the mouse is in some drag mode, stop highlighting.
+ if (_highlighted_row != -1) {
+ int row = _highlighted_row;
+ _highlighted_row = -1;
+ PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x);
+ }
+ }
+
+ if (_drag_mode == DM_pan) {
+ int delta = _drag_start_x - graph_x;
+ _drag_start_x = graph_x;
+ set_horizontal_scroll(get_horizontal_scroll() + pixel_to_height(delta));
+ return 0;
+ }
+
+ return GtkStatsGraph::handle_motion(graph_x, graph_y);
+}
+
+/**
+ * Called when the mouse has left the graph window.
+ */
+gboolean GtkStatsTimeline::
+handle_leave() {
+ if (_highlighted_row != -1) {
+ int row = _highlighted_row;
+ _highlighted_row = -1;
+ PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x);
+ }
+ return TRUE;
+}
+
+/**
+ *
+ */
+gboolean GtkStatsTimeline::
+handle_scroll(int graph_x, int graph_y, double dx, double dy, bool ctrl_held) {
+ gboolean handled = FALSE;
+
+ if (ctrl_held && dy != 0.0) {
+ handled = TRUE;
+ zoom_by(dy, pixel_to_timestamp(graph_x));
+ start_animation();
+ }
+
+ if (dx != 0.0) {
+ _scroll_speed += dx * 10.0;
+ handled = TRUE;
+ start_animation();
+ }
+
+ return handled;
+}
+
+/**
+ *
+ */
+gboolean GtkStatsTimeline::
+handle_key(bool pressed, guint val, guint16 hw_code) {
+ // Accept WASD based on their position rather than their mapping
+ int flag = 0;
+ switch (hw_code) {
+ case 25:
+ flag = F_w;
+ break;
+ case 38:
+ flag = F_a;
+ break;
+ case 39:
+ flag = F_s;
+ break;
+ case 40:
+ flag = F_d;
+ break;
+ }
+ if (flag == 0) {
+ switch (val) {
+ case GDK_KEY_Left:
+ flag = F_left;
+ break;
+ case GDK_KEY_Right:
+ flag = F_right;
+ break;
+ case GDK_KEY_w:
+ flag = F_w;
+ break;
+ case GDK_KEY_a:
+ flag = F_a;
+ break;
+ case GDK_KEY_s:
+ flag = F_s;
+ break;
+ case GDK_KEY_d:
+ flag = F_d;
+ break;
+ }
+ }
+ if (flag != 0) {
+ if (pressed) {
+ if (flag & (F_w | F_s)) {
+ // Pfoo, GTK sure does make it hard to just get the cursor position.
+ GdkWindow *window = gtk_widget_get_window(_graph_window);
+ GdkDisplay *display = gdk_window_get_display(window);
+ GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
+ GdkDevice *device = gdk_device_manager_get_client_pointer(device_manager);
+ gint x, y;
+ gdk_window_get_device_position(window, device, &x, &y, nullptr);
+ _zoom_center = pixel_to_timestamp(x * _cr_scale);
+ }
+ if (_keys_held == 0) {
+ start_animation();
+ }
+ _keys_held |= flag;
+ }
+ else if (_keys_held != 0) {
+ _keys_held &= ~flag;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * This is called during the servicing of the draw event.
+ */
+void GtkStatsTimeline::
+draw_guide_labels(cairo_t *cr) {
+ int num_guide_bars = get_num_guide_bars();
+ for (int i = 0; i < num_guide_bars; i++) {
+ draw_guide_label(cr, get_guide_bar(i));
+ }
+}
+
+/**
+ * Draws the text for the indicated guide bar label at the top of the graph.
+ */
+void GtkStatsTimeline::
+draw_guide_label(cairo_t *cr, const PStatGraph::GuideBar &bar) {
+ const std::string &label = bar._label;
+ if (label.empty()) {
+ return;
+ }
+
+ switch (bar._style) {
+ case GBS_target:
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
+ break;
+
+ case GBS_user:
+ cairo_set_source_rgb(cr, rgb_user_guide_bar[0], rgb_user_guide_bar[1], rgb_user_guide_bar[2]);
+ break;
+
+ case GBS_normal:
+ cairo_set_source_rgb(cr, rgb_light_gray[0], rgb_light_gray[1], rgb_light_gray[2]);
+ break;
+
+ case GBS_frame:
+ cairo_set_source_rgb(cr, rgb_dark_gray[0], rgb_dark_gray[1], rgb_dark_gray[2]);
+ break;
+ }
+
+ PangoLayout *layout = gtk_widget_create_pango_layout(_scale_area, label.c_str());
+
+ if (bar._style != GBS_frame) {
+ // Make the offsets slightly smaller.
+ PangoAttrList *attrs = pango_attr_list_new();
+ PangoAttribute *attr = pango_attr_scale_new(0.9);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert(attrs, attr);
+ pango_layout_set_attributes(layout, attrs);
+ pango_attr_list_unref(attrs);
+ }
+
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+
+ int x = timestamp_to_pixel(bar._height);
+ if (x >= 0 && x + width * _cr_scale < get_xsize()) {
+ // Now convert our x to a coordinate within our drawing area.
+ int junk_y;
+
+ x /= _cr_scale;
+
+ // The x coordinate comes from the graph_window.
+ gtk_widget_translate_coordinates(_graph_window, _scale_area,
+ x, 0, &x, &junk_y);
+
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(_scale_area, &allocation);
+
+ int this_x = x - width / 2;
+ if (this_x >= 0 && this_x + width < allocation.width) {
+ cairo_move_to(cr, this_x, allocation.height - height);
+ pango_cairo_show_layout(cr, layout);
+ }
+ }
+
+ g_object_unref(layout);
+}
+
+/**
+ * This is called during the servicing of the draw event.
+ */
+void GtkStatsTimeline::
+draw_thread_labels(cairo_t *cr) {
+ for (const ThreadRow &thread_row : _threads) {
+ draw_thread_label(cr, thread_row);
+ }
+}
+
+/**
+ * Draws the text for the indicated thread on the side of the graph.
+ */
+void GtkStatsTimeline::
+draw_thread_label(cairo_t *cr, const ThreadRow &thread_row) {
+ int top = row_to_pixel(thread_row._row_offset);
+ if (top <= get_ysize()) {
+ // Now convert our y to a coordinate within our drawing area.
+ top /= _cr_scale;
+
+ // The y coordinate comes from the graph_window.
+ int junk_x;
+ gtk_widget_translate_coordinates(_graph_window, _thread_area,
+ 0, top, &junk_x, &top);
+
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(_thread_area, &allocation);
+
+ PangoLayout *layout = gtk_widget_create_pango_layout(_thread_area, thread_row._label.c_str());
+ pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ pango_layout_set_width(layout, (allocation.width - _pixel_scale * 2) * PANGO_SCALE);
+
+ cairo_move_to(cr, _pixel_scale, top);
+ pango_cairo_show_layout(cr, layout);
+ g_object_unref(layout);
+ }
+}
+
+/**
+ * Draws in the scale labels.
+ */
+gboolean GtkStatsTimeline::
+scale_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+ self->draw_guide_labels(cr);
+
+ return TRUE;
+}
+
+/**
+ * Draws in the thread labels.
+ */
+gboolean GtkStatsTimeline::
+thread_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+ self->draw_thread_labels(cr);
+
+ return TRUE;
+}
+
+/**
+ *
+ */
+gboolean GtkStatsTimeline::
+scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+
+ bool ctrl_held = (event->state & GDK_CONTROL_MASK) != 0;
+
+ double dx, dy;
+ if (event->direction == GDK_SCROLL_UP) {
+ dx = 0;
+ dy = 1;
+ }
+ else if (event->direction == GDK_SCROLL_DOWN) {
+ dx = 0;
+ dy = -1;
+ }
+ else if (event->direction == GDK_SCROLL_LEFT) {
+ dx = 1;
+ dy = 0;
+ }
+ else if (event->direction == GDK_SCROLL_RIGHT) {
+ dx = -1;
+ dy = 0;
+ }
+ else if (!gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy)) {
+ return FALSE;
+ }
+
+ int graph_x = (int)(event->x * self->_cr_scale);
+ int graph_y = (int)(event->y * self->_cr_scale);
+ return self->handle_scroll(graph_x, graph_y, dx, dy, ctrl_held);
+}
+
+/**
+ *
+ */
+gboolean GtkStatsTimeline::
+key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+ return self->handle_key(true, event->keyval, event->hardware_keycode);
+}
+
+/**
+ *
+ */
+gboolean GtkStatsTimeline::
+key_release_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) {
+ GtkStatsTimeline *self = (GtkStatsTimeline *)data;
+ return self->handle_key(false, event->keyval, event->hardware_keycode);
+}
diff --git a/pandatool/src/gtk-stats/gtkStatsTimeline.h b/pandatool/src/gtk-stats/gtkStatsTimeline.h
new file mode 100644
index 0000000000..4ab8d46505
--- /dev/null
+++ b/pandatool/src/gtk-stats/gtkStatsTimeline.h
@@ -0,0 +1,91 @@
+/**
+ * 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 gtkStatsTimeline.h
+ * @author rdb
+ * @date 2022-02-17
+ */
+
+#ifndef GTKSTATSTIMELINE_H
+#define GTKSTATSTIMELINE_H
+
+#include "pandatoolbase.h"
+
+#include "gtkStatsGraph.h"
+#include "pStatTimeline.h"
+
+class GtkStatsMonitor;
+
+/**
+ * A window that draws all of the start/stop event pairs on each thread on a
+ * horizontal scrolling timeline, with concurrent start/stop pairs stacked
+ * underneath each other.
+ */
+class GtkStatsTimeline : public PStatTimeline, public GtkStatsGraph {
+public:
+ GtkStatsTimeline(GtkStatsMonitor *monitor);
+ virtual ~GtkStatsTimeline();
+
+ virtual void new_data(int thread_index, int frame_number);
+ virtual void force_redraw();
+ virtual void changed_graph_size(int graph_xsize, int graph_ysize);
+
+protected:
+ virtual void clear_region();
+ virtual void begin_draw();
+ virtual void draw_separator(int row);
+ virtual void draw_guide_bar(int x, GuideBarStyle style);
+ virtual void draw_bar(int row, int from_x, int to_x, int collector_index,
+ const std::string &collector_name);
+ virtual void end_draw();
+ virtual void idle();
+
+ virtual bool animate(double time, double dt);
+
+ virtual void additional_graph_window_paint(cairo_t *cr);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
+ virtual DragMode consider_drag_start(int graph_x, int graph_y);
+
+ virtual gboolean handle_button_press(int graph_x, int graph_y,
+ bool double_click, int button);
+ virtual gboolean handle_button_release(int graph_x, int graph_y);
+ virtual gboolean handle_motion(int graph_x, int graph_y);
+ virtual gboolean handle_leave();
+ gboolean handle_scroll(int graph_x, int graph_y,
+ double dx, double dy, bool ctrl_held);
+ gboolean handle_key(bool pressed, guint val, guint16 hw_code);
+
+private:
+ void draw_guide_labels(cairo_t *cr);
+ void draw_guide_label(cairo_t *cr, const GuideBar &bar);
+ void draw_thread_labels(cairo_t *cr);
+ void draw_thread_label(cairo_t *cr, const ThreadRow &thread_row);
+
+ static gboolean scale_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
+ static gboolean thread_area_draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
+ static gboolean scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data);
+ static gboolean key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data);
+ static gboolean key_release_callback(GtkWidget *widget, GdkEventKey *event, gpointer data);
+
+ int row_to_pixel(int y) const {
+ return y * _pixel_scale * 5 + _pixel_scale;
+ }
+ int pixel_to_row(int y) const {
+ return (y - _pixel_scale) / (_pixel_scale * 5);
+ }
+
+ GtkWidget *_thread_area;
+
+ cairo_pattern_t *_grid_pattern;
+
+ int _highlighted_row = -1;
+ int _highlighted_x = 0;
+ ColorBar _popup_bar;
+};
+
+#endif
diff --git a/pandatool/src/gtk-stats/gtkstats_composite1.cxx b/pandatool/src/gtk-stats/gtkstats_composite1.cxx
index 61e1fa7ab7..3ab44d393e 100644
--- a/pandatool/src/gtk-stats/gtkstats_composite1.cxx
+++ b/pandatool/src/gtk-stats/gtkstats_composite1.cxx
@@ -1,5 +1,6 @@
#include "gtkStats.cxx"
#include "gtkStatsChartMenu.cxx"
+#include "gtkStatsFlameGraph.cxx"
#include "gtkStatsGraph.cxx"
#include "gtkStatsLabel.cxx"
#include "gtkStatsLabelStack.cxx"
@@ -7,3 +8,4 @@
#include "gtkStatsPianoRoll.cxx"
#include "gtkStatsServer.cxx"
#include "gtkStatsStripChart.cxx"
+#include "gtkStatsTimeline.cxx"
diff --git a/pandatool/src/mayaprogs/mayaConversionClient.cxx b/pandatool/src/mayaprogs/mayaConversionClient.cxx
index 900b7ca0d4..83735f3186 100644
--- a/pandatool/src/mayaprogs/mayaConversionClient.cxx
+++ b/pandatool/src/mayaprogs/mayaConversionClient.cxx
@@ -19,11 +19,8 @@
* Initializes the Maya conversion client.
*/
MayaConversionClient::
-MayaConversionClient()
+MayaConversionClient() : _reader(&_manager, 0), _writer(&_manager, 0)
{
- _qManager = new QueuedConnectionManager();
- _qReader = new QueuedConnectionReader(_qManager, 0);
- _cWriter = new ConnectionWriter(_qManager, 0);
}
/**
@@ -44,12 +41,12 @@ bool MayaConversionClient::
connect(NetAddress server) {
if (_conn) {
// Remove this connection from the readers list
- _qReader->remove_connection(_conn);
+ _reader.remove_connection(_conn);
_conn = nullptr;
}
// Attempt to open a connection
- _conn = _qManager->open_TCP_client_connection(server, 0);
+ _conn = _manager.open_TCP_client_connection(server, 0);
if (!_conn || _conn.is_null()) {
// This connection could not be opened
@@ -57,7 +54,7 @@ connect(NetAddress server) {
}
// Add this connection to the readers list
- _qReader->add_connection(_conn);
+ _reader.add_connection(_conn);
return true;
}
@@ -94,17 +91,17 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer::
datagram.add_uint8(conversion_type);
// Send the conversion request
- if (!_cWriter->send(datagram, _conn) || !_conn->flush()) {
+ if (!_writer.send(datagram, _conn) || !_conn->flush()) {
nout << "Failed to send workload to server process.\n";
return false;
}
// Wait for a response
- while (_conn->get_socket()->Active() && !_qReader->data_available()) {
- _qReader->poll();
+ while (_conn->get_socket()->Active() && !_reader.data_available()) {
+ _reader.poll();
}
- if (!_qReader->data_available()) {
+ if (!_reader.data_available()) {
// No response has been given by the server.
nout << "No response has been given by the conversion server.\n";
return false;
@@ -113,7 +110,7 @@ queue(Filename working_directory, int argc, char *argv[], MayaConversionServer::
NetDatagram response;
// Let's read the response now!
- if (!_qReader->get_data(response)) {
+ if (!_reader.get_data(response)) {
nout << "The conversion response could not be read.\n";
return false;
}
@@ -147,13 +144,13 @@ close() {
}
while (true) {
- _qReader->data_available();
+ _reader.data_available();
- if (_qManager->reset_connection_available()) {
+ if (_manager.reset_connection_available()) {
PT(Connection) connection;
- if (_qManager->get_reset_connection(connection)) {
- _qManager->close_connection(_conn);
+ if (_manager.get_reset_connection(connection)) {
+ _manager.close_connection(_conn);
_conn = nullptr;
return;
}
diff --git a/pandatool/src/mayaprogs/mayaConversionClient.h b/pandatool/src/mayaprogs/mayaConversionClient.h
index bd524a07eb..2685c7166d 100644
--- a/pandatool/src/mayaprogs/mayaConversionClient.h
+++ b/pandatool/src/mayaprogs/mayaConversionClient.h
@@ -44,9 +44,9 @@ public:
int main(int argc, char *argv[], MayaConversionServer::ConversionType conversion_type);
private:
- QueuedConnectionManager *_qManager;
- QueuedConnectionReader *_qReader;
- ConnectionWriter *_cWriter;
+ QueuedConnectionManager _manager;
+ QueuedConnectionReader _reader;
+ ConnectionWriter _writer;
PT(Connection) _conn;
};
diff --git a/pandatool/src/mayaprogs/mayaConversionServer.cxx b/pandatool/src/mayaprogs/mayaConversionServer.cxx
index 0d6b7e8056..3557082ace 100644
--- a/pandatool/src/mayaprogs/mayaConversionServer.cxx
+++ b/pandatool/src/mayaprogs/mayaConversionServer.cxx
@@ -26,22 +26,8 @@
* Initializes the Maya conversion server.
*/
MayaConversionServer::
-MayaConversionServer() {
- _qManager = new QueuedConnectionManager();
- _qListener = new QueuedConnectionListener(_qManager, 0);
- _qReader = new QueuedConnectionReader(_qManager, 0);
- _cWriter = new ConnectionWriter(_qManager, 0);
-}
-
-/**
- * Cleans up the connection managers for the Maya conversion server.
- */
-MayaConversionServer::
-~MayaConversionServer() {
- delete _qManager;
- delete _qReader;
- delete _qListener;
- delete _cWriter;
+MayaConversionServer() : _listener(&_manager, 0), _reader(&_manager, 0),
+ _writer(&_manager, 0) {
}
/**
@@ -51,39 +37,39 @@ MayaConversionServer::
void MayaConversionServer::
poll() {
// Listen for new connections
- _qListener->poll();
+ _listener.poll();
// If we have a new connection from a client create a new connection pointer
// and add it to the reader list
- if (_qListener->new_connection_available()) {
+ if (_listener.new_connection_available()) {
PT(Connection) rendezvous;
PT(Connection) connection;
NetAddress address;
- if (_qListener->get_new_connection(rendezvous, address, connection)) {
- _qReader->add_connection(connection);
+ if (_listener.get_new_connection(rendezvous, address, connection)) {
+ _reader.add_connection(connection);
_clients.insert(connection);
}
}
// Check for reset clients
- if (_qManager->reset_connection_available()) {
+ if (_manager.reset_connection_available()) {
PT(Connection) connection;
- if (_qManager->get_reset_connection(connection)) {
+ if (_manager.get_reset_connection(connection)) {
_clients.erase(connection);
- _qManager->close_connection(connection);
+ _manager.close_connection(connection);
}
}
// Poll the readers (created above) and if they have data process it
- _qReader->poll();
+ _reader.poll();
- if (_qReader->data_available()) {
+ if (_reader.data_available()) {
// Grab the incoming data and unpack it
NetDatagram datagram;
- if (_qReader->get_data(datagram)) {
+ if (_reader.get_data(datagram)) {
DatagramIterator data(datagram);
// First data should be the "argc" (argument count) from the client
@@ -182,7 +168,7 @@ poll() {
response.add_bool(converted);
// Send the response
- if (!_cWriter->send(response, datagram.get_connection())) {
+ if (!_writer.send(response, datagram.get_connection())) {
// Looks like we couldn't send the response
nout << "Could not send response to the client.\n";
}
@@ -199,13 +185,13 @@ poll() {
}
// Clean up the malloc'd pointer pointer
free(cargv);
- } // qReader->get_data
+ } // qReader.get_data
Clients::iterator ci;
for (ci = _clients.begin(); ci != _clients.end(); ++ci) {
- _qManager->close_connection(*ci);
+ _manager.close_connection(*ci);
}
- } // qReader->data_available
+ } // qReader.data_available
} // poll
/**
@@ -214,7 +200,7 @@ poll() {
void MayaConversionServer::
listen() {
// Open a rendezvous port for receiving new connections from the client
- PT(Connection) rend = _qManager->open_TCP_server_rendezvous(4242, 100);
+ PT(Connection) rend = _manager.open_TCP_server_rendezvous(4242, 100);
if (rend.is_null()) {
nout << "Port opening failed!\n";
@@ -224,7 +210,7 @@ listen() {
nout << "Server opened on port 4242, waiting for requests...\n";
// Add this connection to the listeners list
- _qListener->add_connection(rend);
+ _listener.add_connection(rend);
// Main loop. Keep polling for connections, but don't eat up all the CPU.
while (true) {
diff --git a/pandatool/src/mayaprogs/mayaConversionServer.h b/pandatool/src/mayaprogs/mayaConversionServer.h
index 10212676a8..c5d89910e0 100644
--- a/pandatool/src/mayaprogs/mayaConversionServer.h
+++ b/pandatool/src/mayaprogs/mayaConversionServer.h
@@ -40,7 +40,6 @@ public:
};
MayaConversionServer();
- ~MayaConversionServer();
void listen();
void poll();
@@ -49,10 +48,10 @@ protected:
typedef pset< PT(Connection) > Clients;
Clients _clients;
- QueuedConnectionManager *_qManager;
- QueuedConnectionListener *_qListener;
- QueuedConnectionReader *_qReader;
- ConnectionWriter *_cWriter;
+ QueuedConnectionManager _manager;
+ QueuedConnectionListener _listener;
+ QueuedConnectionReader _reader;
+ ConnectionWriter _writer;
};
#endif
diff --git a/pandatool/src/palettizer/textureProperties.cxx b/pandatool/src/palettizer/textureProperties.cxx
index 30d102f0a0..1268405ce8 100644
--- a/pandatool/src/palettizer/textureProperties.cxx
+++ b/pandatool/src/palettizer/textureProperties.cxx
@@ -292,6 +292,8 @@ fully_define() {
case EggTexture::F_luminance:
case EggTexture::F_luminance_alpha:
case EggTexture::F_luminance_alphamask:
+ case EggTexture::F_srgb:
+ case EggTexture::F_srgb_alpha:
break;
case EggTexture::F_rgba12:
@@ -598,6 +600,12 @@ get_format_string(EggTexture::Format format) {
case EggTexture::F_luminance:
return "l";
+
+ case EggTexture::F_srgb:
+ return "sc";
+
+ case EggTexture::F_srgb_alpha:
+ return "sa";
}
return "x";
diff --git a/pandatool/src/pstatserver/CMakeLists.txt b/pandatool/src/pstatserver/CMakeLists.txt
index 359f529b18..020b57785a 100644
--- a/pandatool/src/pstatserver/CMakeLists.txt
+++ b/pandatool/src/pstatserver/CMakeLists.txt
@@ -4,6 +4,7 @@ endif()
set(P3PSTATSERVER_HEADERS
pStatClientData.h
+ pStatFlameGraph.h pStatFlameGraph.I
pStatGraph.h pStatGraph.I
pStatListener.h
pStatMonitor.h pStatMonitor.I
@@ -12,17 +13,25 @@ set(P3PSTATSERVER_HEADERS
pStatServer.h
pStatStripChart.h pStatStripChart.I
pStatThreadData.h pStatThreadData.I
+ pStatTimeline.h pStatTimeline.I
pStatView.h pStatView.I
pStatViewLevel.h pStatViewLevel.I
)
set(P3PSTATSERVER_SOURCES
- pStatClientData.cxx pStatGraph.cxx
+ pStatClientData.cxx
+ pStatFlameGraph.cxx
+ pStatGraph.cxx
pStatListener.cxx
- pStatMonitor.cxx pStatPianoRoll.cxx
- pStatReader.cxx pStatServer.cxx
- pStatStripChart.cxx pStatThreadData.cxx
- pStatView.cxx pStatViewLevel.cxx
+ pStatMonitor.cxx
+ pStatPianoRoll.cxx
+ pStatReader.cxx
+ pStatServer.cxx
+ pStatStripChart.cxx
+ pStatThreadData.cxx
+ pStatTimeline.cxx
+ pStatView.cxx
+ pStatViewLevel.cxx
)
composite_sources(p3pstatserver P3PSTATSERVER_SOURCES)
diff --git a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx
index aa310d92fc..a3f7c09d34 100644
--- a/pandatool/src/pstatserver/p3pstatserver_composite1.cxx
+++ b/pandatool/src/pstatserver/p3pstatserver_composite1.cxx
@@ -1,4 +1,5 @@
#include "pStatClientData.cxx"
+#include "pStatFlameGraph.cxx"
#include "pStatGraph.cxx"
#include "pStatListener.cxx"
#include "pStatMonitor.cxx"
@@ -7,5 +8,6 @@
#include "pStatServer.cxx"
#include "pStatStripChart.cxx"
#include "pStatThreadData.cxx"
+#include "pStatTimeline.cxx"
#include "pStatView.cxx"
#include "pStatViewLevel.cxx"
diff --git a/pandatool/src/pstatserver/pStatFlameGraph.I b/pandatool/src/pstatserver/pStatFlameGraph.I
new file mode 100644
index 0000000000..2ab06a159c
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatFlameGraph.I
@@ -0,0 +1,112 @@
+/**
+ * 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 pStatFlameGraph.I
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+/**
+ * Returns the particular thread whose data this flame graph reflects.
+ */
+INLINE int PStatFlameGraph::
+get_thread_index() const {
+ return _thread_index;
+}
+
+/**
+ * Returns the particular collector whose data this flame graph reflects.
+ */
+INLINE int PStatFlameGraph::
+get_collector_index() const {
+ return _collector_index;
+}
+
+/**
+ * Returns the amount of total time the width of the horizontal axis
+ * represents.
+ */
+INLINE double PStatFlameGraph::
+get_horizontal_scale() const {
+ return _time_width;
+}
+
+/**
+ * Changes the average_mode flag. When true, the strip chart will average out
+ * the color values over pstats_average_time seconds, which hides spikes and
+ * makes the overall trends easier to read. When false, the strip chart shows
+ * the actual data as it is happening.
+ *
+ * If you set this to true, you need to call animate() periodically so that the
+ * averages are smoothly updated over time.
+ */
+INLINE void PStatFlameGraph::
+set_average_mode(bool average_mode) {
+ if (_average_mode != average_mode) {
+ _average_mode = average_mode;
+ _stack.reset_averages();
+ if (!average_mode) {
+ _time_width = _stack.get_net_value(false);
+ normal_guide_bars();
+ }
+ force_redraw();
+ }
+}
+
+/**
+ * Returns the current state of the average_mode flag. When true, the strip
+ * chart will average out the color values over pstats_average_time seconds,
+ * which hides spikes and makes the overall trends easier to read. When
+ * false, the strip chart shows the actual data as it is happening.
+ */
+INLINE bool PStatFlameGraph::
+get_average_mode() const {
+ return _average_mode;
+}
+
+/**
+ * Converts a value (i.e. a "height" in the strip chart) to a horizontal
+ * pixel offset.
+ */
+INLINE int PStatFlameGraph::
+height_to_pixel(double value) const {
+ return (int)((double)_xsize * value / _time_width);
+}
+
+/**
+ * Converts a horizontal pixel offset to a value (a "height" in the strip
+ * chart).
+ */
+INLINE double PStatFlameGraph::
+pixel_to_height(int x) const {
+ return _time_width * (double)x / (double)_xsize;
+}
+
+/**
+ * Returns true if get_title_text() has never yet returned an answer, false if
+ * it has.
+ */
+INLINE bool PStatFlameGraph::
+is_title_unknown() const {
+ return _title_unknown;
+}
+/**
+ * Returns the net value of this stack level.
+ */
+INLINE double PStatFlameGraph::StackLevel::
+get_net_value(bool average) const {
+ if (_collector_index >= 0) {
+ return average ? _avg_net_value : _net_value;
+ } else {
+ double sum = 0.0;
+ for (auto &item : _children) {
+ sum += item.second.get_net_value(average);
+ }
+ return sum;
+ }
+}
diff --git a/pandatool/src/pstatserver/pStatFlameGraph.cxx b/pandatool/src/pstatserver/pStatFlameGraph.cxx
new file mode 100644
index 0000000000..c146203e63
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatFlameGraph.cxx
@@ -0,0 +1,533 @@
+/**
+ * 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 pStatFlameGraph.cxx
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#include "pStatFlameGraph.h"
+
+#include "pStatFrameData.h"
+#include "pStatCollectorDef.h"
+#include "string_utils.h"
+#include "config_pstatclient.h"
+
+#include
+#include
+
+/**
+ *
+ */
+PStatFlameGraph::
+PStatFlameGraph(PStatMonitor *monitor,
+ int thread_index, int collector_index, int xsize, int ysize) :
+ PStatGraph(monitor, xsize, ysize),
+ _thread_index(thread_index),
+ _collector_index(collector_index),
+ _orig_collector_index(collector_index)
+{
+ _average_mode = true;
+ _average_cursor = 0;
+
+ _time_width = 1.0 / pstats_target_frame_rate;
+ _current_frame = -1;
+
+ _title_unknown = true;
+
+ _guide_bar_units = GBU_ms | GBU_hz | GBU_show_units;
+ normal_guide_bars();
+}
+
+/**
+ *
+ */
+PStatFlameGraph::
+~PStatFlameGraph() {
+}
+
+/**
+ * Updates the chart with the latest data.
+ */
+void PStatFlameGraph::
+update() {
+ const PStatClientData *client_data = _monitor->get_client_data();
+
+ // Don't bother to update the thread data until we know at least something
+ // about the collectors and threads.
+ if (client_data->get_num_collectors() != 0 &&
+ client_data->get_num_threads() != 0) {
+ const PStatThreadData *thread_data =
+ client_data->get_thread_data(_thread_index);
+ if (!thread_data->is_empty()) {
+ int frame_number = thread_data->get_latest_frame_number();
+ if (frame_number != _current_frame) {
+ _current_frame = frame_number;
+
+ update_data();
+ }
+ }
+ }
+
+ idle();
+}
+
+/**
+ * Changes the collector represented by this flame graph. This may force a
+ * redraw.
+ */
+void PStatFlameGraph::
+set_collector_index(int collector_index) {
+ if (collector_index == -1) {
+ // First go back to the collector where we originally opened this graph,
+ // and only then go back to the root.
+ collector_index = _orig_collector_index;
+ if (_collector_index == _orig_collector_index) {
+ collector_index = -1;
+ _orig_collector_index = -1;
+ }
+ }
+ if (_collector_index != collector_index) {
+ _collector_index = collector_index;
+ _title_unknown = true;
+ _stack.clear();
+ update_data();
+ }
+}
+
+/**
+ * Returns the text suitable for the title label on the top line.
+ */
+std::string PStatFlameGraph::
+get_title_text() {
+ std::string text;
+
+ _title_unknown = false;
+
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (_collector_index >= 0) {
+ if (client_data->has_collector(_collector_index)) {
+ text = client_data->get_collector_fullname(_collector_index);
+ text += " flame graph";
+ } else {
+ _title_unknown = true;
+ }
+
+ if (_thread_index != 0) {
+ if (client_data->has_thread(_thread_index)) {
+ text += " (" + client_data->get_thread_name(_thread_index) + " thread)";
+ } else {
+ _title_unknown = true;
+ }
+ }
+ }
+ else if (client_data->has_thread(_thread_index)) {
+ text += client_data->get_thread_name(_thread_index) + " thread flame graph";
+ }
+ else {
+ _title_unknown = true;
+ }
+
+ return text;
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string PStatFlameGraph::
+get_bar_tooltip(int depth, int x) const {
+ const StackLevel *level = _stack.locate(depth, pixel_to_height(x), _average_mode);
+ if (level != nullptr) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (client_data != nullptr && client_data->has_collector(level->_collector_index)) {
+ std::ostringstream text;
+ text << client_data->get_collector_fullname(level->_collector_index);
+ text << " (" << format_number(level->get_net_value(_average_mode), GBU_show_units | GBU_ms) << ")";
+ return text.str();
+ }
+ }
+ return std::string();
+}
+
+/**
+ * Returns the collector index corresponding to the bar at the given location.
+ */
+int PStatFlameGraph::
+get_bar_collector(int depth, int x) const {
+ const StackLevel *level = _stack.locate(depth, pixel_to_height(x), _average_mode);
+ if (level != nullptr) {
+ return level->_collector_index;
+ }
+ return -1;
+}
+
+/**
+ *
+ */
+void PStatFlameGraph::
+update_data() {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (client_data == nullptr) {
+ return;
+ }
+
+ const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
+ if (thread_data == nullptr || thread_data->is_empty()) {
+ return;
+ }
+
+ const PStatFrameData &frame_data = thread_data->get_frame(_current_frame);
+
+ bool first_time = _stack._children.empty();
+
+ StackLevel *top = &_stack;
+ top->reset();
+
+ size_t num_events = frame_data.get_num_events();
+ for (size_t ei = 0; ei < num_events; ++ei) {
+ int collector_index = frame_data.get_time_collector(ei);
+ double time = frame_data.get_time(ei);
+
+ if (frame_data.is_start(ei)) {
+ // If we have a collector index, use it to determine which bottom-level
+ // stack frames we are interested in.
+ if (_collector_index < 0 ||
+ _collector_index == collector_index ||
+ top != &_stack) {
+ top = top->start(collector_index, time);
+ }
+ else {
+ // Check whether one of the parents matches, perhaps.
+ int parent_index = collector_index;
+ do {
+ const PStatCollectorDef &def = client_data->get_collector_def(parent_index);
+ if (parent_index == def._parent_index) {
+ break;
+ }
+ parent_index = def._parent_index;
+ if (parent_index == _collector_index) {
+ // Yes, let it through.
+ top = top->start(collector_index, time);
+ break;
+ }
+ }
+ while (parent_index >= 0 && client_data->has_collector(parent_index));
+ }
+ }
+ else {
+ top = top->stop(collector_index, time);
+ }
+ }
+ top = top->stop_all(frame_data.get_end());
+ nassertv(top == &_stack);
+
+ if (first_time) {
+ _stack.reset_averages();
+ }
+
+ if (!_average_mode) {
+ // Redraw right away, except in average mode, where it's done in animate().
+ _time_width = _stack.get_net_value(false);
+ if (_time_width == 0.0) {
+ _time_width = 1.0 / pstats_target_frame_rate;
+ }
+ normal_guide_bars();
+ force_redraw();
+ }
+}
+
+/**
+ * To be called by the user class when the widget size has changed. This
+ * updates the chart's internal data and causes it to issue redraw commands to
+ * reflect the new size.
+ */
+void PStatFlameGraph::
+changed_size(int xsize, int ysize) {
+ if (xsize != _xsize || ysize != _ysize) {
+ _xsize = xsize;
+ _ysize = ysize;
+
+ normal_guide_bars();
+ force_redraw();
+ }
+}
+
+/**
+ * To be called by the user class when the whole thing needs to be redrawn for
+ * some reason.
+ */
+void PStatFlameGraph::
+force_redraw() {
+ begin_draw();
+ r_draw_level(_stack);
+ end_draw();
+}
+
+/**
+ * Calls update_guide_bars with parameters suitable to this kind of graph.
+ */
+void PStatFlameGraph::
+normal_guide_bars() {
+ // We want vaguely 100 pixels between guide bars.
+ int num_bars = get_xsize() / 100;
+
+ _guide_bars.clear();
+
+ double dist = _time_width / num_bars;
+
+ for (int i = 1; i < num_bars; ++i) {
+ _guide_bars.push_back(make_guide_bar(i * dist));
+ }
+
+ _guide_bars_changed = true;
+}
+
+/**
+ * Should be overridden by the user class. This hook will be called before
+ * drawing any bars in the chart.
+ */
+void PStatFlameGraph::
+begin_draw() {
+}
+
+/**
+ * Should be overridden by the user class. Should draw a single bar at the
+ * indicated location.
+ */
+void PStatFlameGraph::
+draw_bar(int depth, int from_x, int to_x, int collector_index) {
+}
+
+/**
+ * Should be overridden by the user class. This hook will be called after
+ * drawing a series of color bars in the chart.
+ */
+void PStatFlameGraph::
+end_draw() {
+}
+
+/**
+ * Should be overridden by the user class to perform any other updates might
+ * be necessary after the bars have been redrawn.
+ */
+void PStatFlameGraph::
+idle() {
+}
+
+/**
+ * Should be called periodically to update any animated values. Returns false
+ * to indicate that the animation is done and no longer needs to be called.
+ */
+bool PStatFlameGraph::
+animate(double time, double dt) {
+ if (!_average_mode) {
+ return false;
+ }
+
+ if (_stack.update_averages(_average_cursor)) {
+ _time_width = _stack.get_net_value(true);
+ if (_time_width == 0.0) {
+ _time_width = 1.0 / pstats_target_frame_rate;
+ }
+ normal_guide_bars();
+ force_redraw();
+ }
+
+ // Cycle through the ring buffers.
+ _average_cursor = (_average_cursor + 1) % _num_average_frames;
+ return true;
+}
+
+/**
+ * Resets all the nodes by setting their _net_value to 0.0.
+ */
+void PStatFlameGraph::StackLevel::
+reset() {
+ _start_time = 0.0;
+ _net_value = 0.0;
+ _started = false;
+
+ for (auto &item : _children) {
+ item.second.reset();
+ }
+}
+
+/**
+ * Starts the given collector, which starts a new stack frame as a child of
+ * the current one. Returns the new child, which is the new stack top.
+ */
+PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel::
+start(int collector_index, double time) {
+ StackLevel &child = _children[collector_index];
+ child._parent = this;
+ child._collector_index = collector_index;
+ child._start_time = std::max(_start_time, time);
+ child._started = true;
+ return &child;
+}
+
+/**
+ * Stops the given collector, which is assumed to be somewhere up the
+ * hierarchy. Should only be called on the top of the stack, usually.
+ * Returns the new top of the stack.
+ */
+PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel::
+stop(int collector_index, double time) {
+ StackLevel *new_top = r_stop(collector_index, time);
+ if (new_top != nullptr) {
+ return new_top;
+ }
+ // We have a stop event without a preceding start event. Measure the
+ // the time from the start of the current stack frame to the stop time.
+ // Actually, don't do this, because it means that a child may end up with
+ // more time than the parent. Need a better solution for this - or not?
+ //start(collector_index, _start_time)->stop(collector_index, time);
+ return this;
+}
+
+/**
+ * Stops all still started collectors. Returns the bottom of the stack,
+ * which is also the new top of the stack.
+ */
+PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel::
+stop_all(double time) {
+ if (_parent != nullptr) {
+ nassertr(_started, this);
+ _net_value += time - _start_time;
+ return _parent->stop_all(time);
+ } else {
+ return this;
+ }
+}
+
+/**
+ * Resets the average calculator, used when first enabling average mode.
+ */
+void PStatFlameGraph::StackLevel::
+reset_averages() {
+ double net_value = get_net_value(false);
+
+ for (double &value : _values) {
+ value = net_value;
+ }
+ _avg_net_value = net_value;
+
+ for (auto &item : _children) {
+ item.second.reset_averages();
+ }
+}
+
+/**
+ * Recursively calculates the averages. Returns true if any value was changed.
+ */
+bool PStatFlameGraph::StackLevel::
+update_averages(size_t cursor) {
+ _values[cursor] = get_net_value(false);
+
+ bool changed = false;
+
+ double sum = 0;
+ for (double value : _values) {
+ sum += value;
+ }
+ double avg = sum / _num_average_frames;
+ if (avg != _avg_net_value) {
+ _avg_net_value = avg;
+ changed = true;
+ }
+
+ for (auto &item : _children) {
+ if (item.second.update_averages(cursor)) {
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+/**
+ * Locates a stack level at the given depth and the given time offset.
+ */
+const PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel::
+locate(int depth, double time, bool average) const {
+ if (time < 0.0) {
+ return nullptr;
+ }
+ for (const auto &item : _children) {
+ double value = item.second.get_net_value(average);
+ if (time < value) {
+ if (depth == 0) {
+ // This is it.
+ return &item.second;
+ } else {
+ // Recurse.
+ return item.second.locate(depth - 1, time, average);
+ }
+ }
+ time -= value;
+ }
+ return nullptr;
+}
+
+/**
+ * Clears everything.
+ */
+void PStatFlameGraph::StackLevel::
+clear() {
+ _children.clear();
+ _net_value = 0.0;
+}
+
+/**
+ * Recursive helper used by stop().
+ */
+PStatFlameGraph::StackLevel *PStatFlameGraph::StackLevel::
+r_stop(int collector_index, double time) {
+ if (_collector_index == collector_index) {
+ // Found it.
+ nassertr(_started, nullptr);
+ _net_value += time - _start_time;
+ _started = false;
+ nassertr(_parent != nullptr, nullptr);
+ return _parent;
+ }
+ else if (_parent != nullptr) {
+ StackLevel *level = _parent->r_stop(collector_index, time);
+ if (level != nullptr) {
+ nassertr(_started, nullptr);
+ _net_value += time - _start_time;
+ _started = false;
+ return level;
+ }
+ }
+ return nullptr;
+}
+
+/**
+ * Recursively draws a level.
+ */
+void PStatFlameGraph::
+r_draw_level(const StackLevel &level, int depth, double offset) {
+ for (const auto &item : level._children) {
+ const StackLevel &child = item.second;
+
+ double value = child.get_net_value(_average_mode);
+
+ int from_x = height_to_pixel(offset);
+ int to_x = height_to_pixel(offset + value);
+
+ // No need to recurse if the bars have become smaller than a pixel.
+ if (to_x > from_x) {
+ draw_bar(depth, from_x, to_x, child._collector_index);
+ r_draw_level(child, depth + 1, offset);
+ }
+
+ offset += value;
+ }
+}
diff --git a/pandatool/src/pstatserver/pStatFlameGraph.h b/pandatool/src/pstatserver/pStatFlameGraph.h
new file mode 100644
index 0000000000..61a61447e2
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatFlameGraph.h
@@ -0,0 +1,131 @@
+/**
+ * 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 pStatFlameGraph.h
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#ifndef PSTATFLAMEGRAPH_H
+#define PSTATFLAMEGRAPH_H
+
+#include "pandatoolbase.h"
+
+#include "pStatGraph.h"
+#include "pStatMonitor.h"
+#include "pStatClientData.h"
+
+#include "pmap.h"
+#include "pdeque.h"
+
+class PStatFrameData;
+
+/**
+ * This is an abstract class that presents the interface for drawing a flame
+ * chart: it shows the time spent in each of a number of collectors
+ * as a horizontal bar of color, with time as the horizontal axis.
+ *
+ * This class just manages all the flame chart logic; the actual nuts and bolts
+ * of drawing pixels is left to a user-derived class.
+ */
+class PStatFlameGraph : public PStatGraph {
+public:
+ PStatFlameGraph(PStatMonitor *monitor,
+ int thread_index, int collector_index,
+ int xsize, int ysize);
+ virtual ~PStatFlameGraph();
+
+ void update();
+
+ INLINE int get_thread_index() const;
+ INLINE int get_collector_index() const;
+ void set_collector_index(int collector_index);
+
+ INLINE double get_horizontal_scale() const;
+
+ INLINE void set_average_mode(bool average_mode);
+ INLINE bool get_average_mode() const;
+
+ INLINE int height_to_pixel(double value) const;
+ INLINE double pixel_to_height(int y) const;
+
+ INLINE bool is_title_unknown() const;
+ std::string get_title_text();
+
+ std::string get_bar_tooltip(int depth, int x) const;
+ int get_bar_collector(int depth, int x) const;
+
+protected:
+ void update_data();
+ void changed_size(int xsize, int ysize);
+ void force_redraw();
+ virtual void normal_guide_bars();
+
+ virtual void begin_draw();
+ virtual void draw_bar(int depth, int from_x, int to_x, int collector_index);
+ virtual void end_draw();
+ virtual void idle();
+
+ bool animate(double time, double dt);
+
+private:
+ static const size_t _num_average_frames = 150;
+
+ class StackLevel {
+ public:
+ void reset();
+
+ StackLevel *start(int collector_index, double time);
+ StackLevel *stop(int collector_index, double time);
+ StackLevel *stop_all(double time);
+
+ INLINE double get_net_value(bool average) const;
+ void reset_averages();
+ bool update_averages(size_t cursor);
+
+ const StackLevel *locate(int depth, double time, bool average) const;
+
+ void clear();
+
+ private:
+ StackLevel *r_stop(int collector_index, double time);
+
+ double _net_value = 0.0;
+ double _avg_net_value = 0.0;
+
+ // This is updated like a ring buffer, initialized with all the same value
+ // at first, then always at _average_cursor.
+ double _values[_num_average_frames] = {0.0};
+
+ double _start_time = 0.0;
+ bool _started = false;
+
+ int _collector_index = -1;
+ StackLevel *_parent = nullptr;
+ pmap _children;
+
+ friend class PStatFlameGraph;
+ };
+
+ void r_draw_level(const StackLevel &level, int depth = 0, double offset = 0.0);
+
+ StackLevel _stack;
+ int _thread_index;
+ int _collector_index;
+ int _orig_collector_index;
+ bool _average_mode;
+ size_t _average_cursor;
+
+ double _time_width;
+ int _current_frame;
+ bool _title_unknown;
+};
+
+#include "pStatFlameGraph.I"
+
+#endif
diff --git a/pandatool/src/pstatserver/pStatGraph.cxx b/pandatool/src/pstatserver/pStatGraph.cxx
index 88130ff7ab..38cb21a5c8 100644
--- a/pandatool/src/pstatserver/pStatGraph.cxx
+++ b/pandatool/src/pstatserver/pStatGraph.cxx
@@ -178,7 +178,13 @@ format_number(double value, int guide_bar_units, const string &unit_name) {
if ((guide_bar_units & GBU_named) != 0) {
// Units are whatever is specified by unit_name, not a time unit at all.
- label = format_number(value);
+ int int_value = (int)value;
+ if ((double)int_value == value) {
+ // Probably a counter or something, don't display .0 suffix.
+ label = format_string(int_value);
+ } else {
+ label = format_number(value);
+ }
if ((guide_bar_units & GBU_show_units) != 0 && !unit_name.empty()) {
label += " ";
label += unit_name;
@@ -187,10 +193,32 @@ format_number(double value, int guide_bar_units, const string &unit_name) {
} else {
// Units are either milliseconds or hz, or both.
if ((guide_bar_units & GBU_ms) != 0) {
- double ms = value * 1000.0;
- label += format_number(ms);
- if ((guide_bar_units & GBU_show_units) != 0) {
- label += " ms";
+ if ((guide_bar_units & GBU_show_units) != 0 &&
+ value > 0 && value < 0.000001) {
+ double ns = value * 1000000000.0;
+ label += format_number(ns);
+ label += " ns";
+ }
+ else if ((guide_bar_units & GBU_show_units) != 0 &&
+ value > 0 && value < 0.001) {
+ double us = value * 1000000.0;
+ label += format_number(us);
+#ifdef _WIN32
+ label += " \xb5s";
+#else
+ label += " us";
+#endif
+ }
+ else if ((guide_bar_units & GBU_show_units) == 0 || value < 1.0) {
+ double ms = value * 1000.0;
+ label += format_number(ms);
+ if ((guide_bar_units & GBU_show_units) != 0) {
+ label += " ms";
+ }
+ }
+ else {
+ label += format_number(value);
+ label += " s";
}
}
diff --git a/pandatool/src/pstatserver/pStatGraph.h b/pandatool/src/pstatserver/pStatGraph.h
index 5b8a2d664e..69541a86ec 100644
--- a/pandatool/src/pstatserver/pStatGraph.h
+++ b/pandatool/src/pstatserver/pStatGraph.h
@@ -52,6 +52,7 @@ public:
GBS_normal,
GBS_target,
GBS_user,
+ GBS_frame,
};
class GuideBar {
diff --git a/pandatool/src/pstatserver/pStatMonitor.I b/pandatool/src/pstatserver/pStatMonitor.I
index 761b7b9a79..ac9f2e46f9 100644
--- a/pandatool/src/pstatserver/pStatMonitor.I
+++ b/pandatool/src/pstatserver/pStatMonitor.I
@@ -69,3 +69,11 @@ INLINE std::string PStatMonitor::
get_client_progname() const {
return _client_progname;
}
+
+/**
+ * Returns the process id of the client, or -1 if it is not known.
+ */
+INLINE int PStatMonitor::
+get_client_pid() const {
+ return _client_pid;
+}
diff --git a/pandatool/src/pstatserver/pStatMonitor.cxx b/pandatool/src/pstatserver/pStatMonitor.cxx
index 09e22b093a..20444352c1 100644
--- a/pandatool/src/pstatserver/pStatMonitor.cxx
+++ b/pandatool/src/pstatserver/pStatMonitor.cxx
@@ -38,10 +38,11 @@ PStatMonitor::
* indicates the client's reported hostname and program name.
*/
void PStatMonitor::
-hello_from(const string &hostname, const string &progname) {
+hello_from(const string &hostname, const string &progname, int pid) {
_client_known = true;
_client_hostname = hostname;
_client_progname = progname;
+ _client_pid = pid;
got_hello();
}
@@ -52,12 +53,13 @@ hello_from(const string &hostname, const string &progname) {
* effect.
*/
void PStatMonitor::
-bad_version(const string &hostname, const string &progname,
+bad_version(const string &hostname, const string &progname, int pid,
int client_major, int client_minor,
int server_major, int server_minor) {
_client_known = true;
_client_hostname = hostname;
_client_progname = progname;
+ _client_pid = 0;
got_bad_version(client_major, client_minor,
server_major, server_minor);
}
diff --git a/pandatool/src/pstatserver/pStatMonitor.h b/pandatool/src/pstatserver/pStatMonitor.h
index f7e92073c4..8c1259c9f0 100644
--- a/pandatool/src/pstatserver/pStatMonitor.h
+++ b/pandatool/src/pstatserver/pStatMonitor.h
@@ -43,8 +43,10 @@ public:
PStatMonitor(PStatServer *server);
virtual ~PStatMonitor();
- void hello_from(const std::string &hostname, const std::string &progname);
+ void hello_from(const std::string &hostname, const std::string &progname,
+ int pid);
void bad_version(const std::string &hostname, const std::string &progname,
+ int pid,
int client_major, int client_minor,
int server_major, int server_minor);
void set_client_data(PStatClientData *client_data);
@@ -63,6 +65,7 @@ public:
INLINE bool is_client_known() const;
INLINE std::string get_client_hostname() const;
INLINE std::string get_client_progname() const;
+ INLINE int get_client_pid() const;
PStatView &get_view(int thread_index);
PStatView &get_level_view(int collector_index, int thread_index);
@@ -98,6 +101,7 @@ private:
bool _client_known;
std::string _client_hostname;
std::string _client_progname;
+ int _client_pid;
typedef pmap Views;
Views _views;
diff --git a/pandatool/src/pstatserver/pStatPianoRoll.I b/pandatool/src/pstatserver/pStatPianoRoll.I
index f0d3fe15cd..8f5bfcc886 100644
--- a/pandatool/src/pstatserver/pStatPianoRoll.I
+++ b/pandatool/src/pstatserver/pStatPianoRoll.I
@@ -11,6 +11,14 @@
* @date 2000-07-18
*/
+/**
+ * Returns the particular thread whose data this piano roll reflects.
+ */
+INLINE int PStatPianoRoll::
+get_thread_index() const {
+ return _thread_index;
+}
+
/**
* Changes the amount of time the width of the horizontal axis represents.
* This may force a redraw.
diff --git a/pandatool/src/pstatserver/pStatPianoRoll.cxx b/pandatool/src/pstatserver/pStatPianoRoll.cxx
index 923c3991e7..25c2b416cb 100644
--- a/pandatool/src/pstatserver/pStatPianoRoll.cxx
+++ b/pandatool/src/pstatserver/pStatPianoRoll.cxx
@@ -128,6 +128,20 @@ update() {
idle();
}
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string PStatPianoRoll::
+get_label_tooltip(int collector_index) const {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (!client_data->has_collector(collector_index)) {
+ return std::string();
+ }
+
+ return client_data->get_collector_fullname(collector_index);
+}
+
/**
* To be called by the user class when the widget size has changed. This
* updates the chart's internal data and causes it to issue redraw commands to
diff --git a/pandatool/src/pstatserver/pStatPianoRoll.h b/pandatool/src/pstatserver/pStatPianoRoll.h
index e1c597dccf..6c909dc36e 100644
--- a/pandatool/src/pstatserver/pStatPianoRoll.h
+++ b/pandatool/src/pstatserver/pStatPianoRoll.h
@@ -43,6 +43,8 @@ public:
void update();
+ INLINE int get_thread_index() const;
+
INLINE void set_horizontal_scale(double time_width);
INLINE double get_horizontal_scale() const;
@@ -51,6 +53,8 @@ public:
INLINE int height_to_pixel(double value) const;
INLINE double pixel_to_height(int y) const;
+ std::string get_label_tooltip(int collector_index) const;
+
protected:
void changed_size(int xsize, int ysize);
void force_redraw();
diff --git a/pandatool/src/pstatserver/pStatReader.cxx b/pandatool/src/pstatserver/pStatReader.cxx
index 02b01f26a2..a2a1b4b7c9 100644
--- a/pandatool/src/pstatserver/pStatReader.cxx
+++ b/pandatool/src/pstatserver/pStatReader.cxx
@@ -195,11 +195,13 @@ handle_client_control_message(const PStatClientControlMessage &message) {
(message._major_version == server_major_version &&
message._minor_version > server_minor_version)) {
_monitor->bad_version(message._client_hostname, message._client_progname,
+ message._client_pid,
message._major_version, message._minor_version,
server_major_version, server_minor_version);
_monitor->close();
} else {
- _monitor->hello_from(message._client_hostname, message._client_progname);
+ _monitor->hello_from(message._client_hostname, message._client_progname,
+ message._client_pid);
}
}
break;
diff --git a/pandatool/src/pstatserver/pStatStripChart.I b/pandatool/src/pstatserver/pStatStripChart.I
index 791e722009..3e924a1bc8 100644
--- a/pandatool/src/pstatserver/pStatStripChart.I
+++ b/pandatool/src/pstatserver/pStatStripChart.I
@@ -19,6 +19,14 @@ get_view() const {
return _view;
}
+/**
+ * Returns the particular thread whose data this strip chart reflects.
+ */
+INLINE int PStatStripChart::
+get_thread_index() const {
+ return _thread_index;
+}
+
/**
* Returns the particular collector whose data this strip chart reflects.
*/
@@ -156,6 +164,15 @@ pixel_to_height(int x) const {
return _value_height * (double)(get_ysize() - x) / (double)get_ysize();
}
+/**
+ * Returns true if get_title_text() has never yet returned an answer, false if
+ * it has.
+ */
+INLINE bool PStatStripChart::
+is_title_unknown() const {
+ return _title_unknown;
+}
+
/**
* Returns true if the indicated collector appears anywhere on the chart at
* the current time, false otherwise.
diff --git a/pandatool/src/pstatserver/pStatStripChart.cxx b/pandatool/src/pstatserver/pStatStripChart.cxx
index c9ffcb3109..2b454edcc5 100644
--- a/pandatool/src/pstatserver/pStatStripChart.cxx
+++ b/pandatool/src/pstatserver/pStatStripChart.cxx
@@ -56,7 +56,7 @@ PStatStripChart(PStatMonitor *monitor, PStatView &view,
_unit_name = def._level_units;
}
- set_default_vertical_scale();
+ set_auto_vertical_scale();
}
/**
@@ -139,6 +139,7 @@ set_collector_index(int collector_index) {
_title_unknown = true;
_data.clear();
clear_label_usage();
+ set_auto_vertical_scale();
force_redraw();
update_labels();
}
@@ -170,26 +171,44 @@ void PStatStripChart::
set_auto_vertical_scale() {
const PStatThreadData *thread_data = _view.get_thread_data();
- double max_value = 0.0;
+ // Calculate the median value.
+ std::vector values;
- int frame_number = -1;
- for (int x = 0; x <= _xsize; x++) {
- double time = pixel_to_timestamp(x);
- frame_number =
- thread_data->get_frame_number_at_time(time, frame_number);
+ if (thread_data != nullptr && !thread_data->is_empty()) {
+ // Find the oldest visible frame.
+ double start_time = pixel_to_timestamp(0);
+ int oldest_frame = std::max(
+ thread_data->get_frame_number_at_time(start_time),
+ thread_data->get_oldest_frame_number());
+ int latest_frame = thread_data->get_latest_frame_number();
- if (thread_data->has_frame(frame_number)) {
- double net_value = get_net_value(frame_number);
- max_value = max(max_value, net_value);
+ for (int frame_number = oldest_frame; frame_number <= latest_frame; ++frame_number) {
+ if (thread_data->has_frame(frame_number)) {
+ values.push_back(get_net_value(frame_number));
+ }
}
}
- // Ok, now we know what the max value visible in the chart is. Choose a
- // scale that will show all of this sensibly.
- if (max_value == 0.0) {
- set_vertical_scale(1.0);
+ if (values.empty()) {
+ set_default_vertical_scale();
+ return;
+ }
+
+ double median;
+ size_t half = values.size() / 2;
+ if (values.size() % 2 == 0) {
+ std::sort(values.begin(), values.end());
+ median = (values[half] + values[half + 1]) / 2.0;
} else {
- set_vertical_scale(max_value * 1.1);
+ std::nth_element(values.begin(), values.begin() + half, values.end());
+ median = values[half];
+ }
+
+ if (median > 0.0) {
+ // Take 1.5 times the median value as the vertical scale.
+ set_vertical_scale(median * 1.5);
+ } else {
+ set_default_vertical_scale();
}
}
@@ -284,12 +303,71 @@ get_title_text() {
}
/**
- * Returns true if get_title_text() has never yet returned an answer, false if
- * it has.
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
*/
-bool PStatStripChart::
-is_title_unknown() const {
- return _title_unknown;
+std::string PStatStripChart::
+get_label_tooltip(int collector_index) const {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (!client_data->has_collector(collector_index)) {
+ return std::string();
+ }
+
+ std::ostringstream text;
+ text << client_data->get_collector_fullname(collector_index);
+
+ double value;
+ if (collector_index == _collector_index) {
+ value = get_average_net_value();
+ }
+ else {
+ const PStatThreadData *thread_data = _view.get_thread_data();
+ int now_i, then_i;
+ if (!thread_data->get_elapsed_frames(then_i, now_i)) {
+ return text.str();
+ }
+ double now = _time_width + _start_time;
+ double then = now - pstats_average_time;
+
+ double net_value = 0.0f;
+ double net_time = 0.0f;
+
+ // We start with just the portion of frame then_i that actually does fall
+ // within our "then to now" window (usually some portion of it will).
+ const PStatFrameData &frame_data = thread_data->get_frame(then_i);
+ if (frame_data.get_end() > then) {
+ double this_time = (frame_data.get_end() - then);
+ _view.set_to_frame(frame_data);
+
+ const PStatViewLevel *level = _view.get_level(collector_index);
+ if (level != nullptr) {
+ net_value += level->get_net_value() * this_time;
+ net_time += this_time;
+ }
+ }
+ // Then we get all of each of the remaining frames.
+ for (int frame_number = then_i + 1;
+ frame_number <= now_i;
+ frame_number++) {
+ const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
+ double this_time = frame_data.get_net_time();
+ _view.set_to_frame(frame_data);
+
+ const PStatViewLevel *level = _view.get_level(collector_index);
+ if (level != nullptr) {
+ net_value += level->get_net_value() * this_time;
+ net_time += this_time;
+ }
+ }
+
+ if (net_time == 0) {
+ return text.str();
+ }
+ value = net_value / net_time;
+ }
+
+ text << " (" << format_number(value, get_guide_bar_units(), get_guide_bar_unit_name()) << ")";
+ return text.str();
}
/**
diff --git a/pandatool/src/pstatserver/pStatStripChart.h b/pandatool/src/pstatserver/pStatStripChart.h
index 5cba0164e2..5802d0baa2 100644
--- a/pandatool/src/pstatserver/pStatStripChart.h
+++ b/pandatool/src/pstatserver/pStatStripChart.h
@@ -46,6 +46,7 @@ public:
bool first_data() const;
INLINE PStatView &get_view() const;
+ INLINE int get_thread_index() const;
INLINE int get_collector_index() const;
void set_collector_index(int collector_index);
@@ -68,8 +69,9 @@ public:
INLINE int height_to_pixel(double value) const;
INLINE double pixel_to_height(int y) const;
- std::string get_title_text();
bool is_title_unknown() const;
+ std::string get_title_text();
+ std::string get_label_tooltip(int collector_index) const;
protected:
class ColorData {
@@ -89,6 +91,7 @@ protected:
void compute_average_pixel_data(PStatStripChart::FrameData &result,
int &then_i, int &now_i, double now);
double get_net_value(int frame_number) const;
+ double get_net_value(int frame_number, int collector_index) const;
double get_average_net_value() const;
void changed_size(int xsize, int ysize);
diff --git a/pandatool/src/pstatserver/pStatThreadData.cxx b/pandatool/src/pstatserver/pStatThreadData.cxx
index 141f927e8f..06da8a4c3c 100644
--- a/pandatool/src/pstatserver/pStatThreadData.cxx
+++ b/pandatool/src/pstatserver/pStatThreadData.cxx
@@ -267,7 +267,7 @@ record_new_frame(int frame_number, PStatFrameData *frame_data) {
double oldest_allowable_time = time - _history;
while (!_frames.empty() &&
(_frames.front() == nullptr ||
- _frames.front()->is_empty() ||
+ _frames.front()->is_time_empty() ||
_frames.front()->get_start() < oldest_allowable_time)) {
delete _frames.front();
_frames.pop_front();
@@ -288,6 +288,14 @@ record_new_frame(int frame_number, PStatFrameData *frame_data) {
}
int index = frame_number - _first_frame_number;
+
+ // It's possible to receive frames out of order.
+ while (index < 0) {
+ _frames.push_front(nullptr);
+ ++index;
+ --_first_frame_number;
+ }
+
nassertv(index >= 0 && index < (int)_frames.size());
if (_frames[index] != nullptr) {
diff --git a/pandatool/src/pstatserver/pStatTimeline.I b/pandatool/src/pstatserver/pStatTimeline.I
new file mode 100644
index 0000000000..610078b566
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatTimeline.I
@@ -0,0 +1,139 @@
+/**
+ * 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 pStatTimeline.I
+ * @author rdb
+ * @date 2022-02-11
+ */
+
+/**
+ * Changes the amount of time the width of the horizontal axis represents.
+ * This may force a redraw.
+ */
+INLINE void PStatTimeline::
+set_horizontal_scale(double time_width) {
+ double max_time_width = (_highest_end_time - _lowest_start_time) * 2.0;
+ time_width = (std::min)(time_width, max_time_width);
+
+ double scale = time_width / get_xsize();
+ if (_time_scale != scale) {
+ _time_scale = scale;
+ _target_time_scale = scale;
+ _zoom_speed = 0.0;
+ normal_guide_bars();
+ force_redraw();
+ }
+}
+
+/**
+ * Returns the amount of total time the width of the horizontal axis
+ * represents.
+ */
+INLINE double PStatTimeline::
+get_horizontal_scale() const {
+ return _time_scale * get_xsize();
+}
+
+/**
+ * This may force a redraw.
+ */
+INLINE void PStatTimeline::
+set_horizontal_scroll(double start_time) {
+ start_time = (std::max)((std::min)(start_time, _highest_end_time), _lowest_start_time);
+ if (_start_time != start_time) {
+ _start_time = start_time;
+ _target_start_time = start_time;
+ _scroll_speed = 0.0;
+ normal_guide_bars();
+ force_redraw();
+ }
+}
+
+/**
+ * Returns the amount of total time the width of the horizontal axis
+ * represents.
+ */
+INLINE double PStatTimeline::
+get_horizontal_scroll() const {
+ return _start_time;
+}
+
+/**
+ * Smoothly zooms to the given time width, around the given focal point.
+ */
+INLINE void PStatTimeline::
+zoom_to(double time_width, double center) {
+ // Don't allow zooming out to beyond 2x the size of the entire timeline.
+ // There's a limit of zooming beyond 1 ns per bar, there's just no point...
+ double max_time_width = (_highest_end_time - _lowest_start_time) * 2.0;
+ time_width = (std::min)((std::max)(1e-7, time_width), max_time_width);
+ _target_time_scale = time_width / get_xsize();
+ _zoom_center = center;
+
+ double pivot_x = (_zoom_center - _start_time) / _time_scale;
+ scroll_to(_zoom_center - pivot_x * _target_time_scale);
+}
+
+/**
+ * Smoothly zooms by the given amount, where 1.0 is a single "tick" of zooming
+ * in and -1.0 is a single "tick" of zooming out.
+ */
+INLINE void PStatTimeline::
+zoom_by(double amount, double center) {
+ zoom_to(_target_time_scale * pow(0.8, amount) * get_xsize(), center);
+}
+
+/**
+ * Smoothly scrolls to the given time point.
+ */
+INLINE void PStatTimeline::
+scroll_to(double start_time) {
+ _target_start_time = (std::max)((std::min)(start_time, _highest_end_time), _lowest_start_time);
+}
+
+/**
+ * Smoothly scrolls by the given amount.
+ */
+INLINE void PStatTimeline::
+scroll_by(double delta) {
+ scroll_to(_target_start_time + delta);
+}
+
+/**
+ * Converts a timestamp to a horizontal pixel offset.
+ */
+INLINE int PStatTimeline::
+timestamp_to_pixel(double time) const {
+ return (int)((double)_xsize * (time - _start_time) / get_horizontal_scale());
+}
+
+/**
+ * Converts a horizontal pixel offset to a timestamp.
+ */
+INLINE double PStatTimeline::
+pixel_to_timestamp(int x) const {
+ return _time_scale * (double)x + _start_time;
+}
+
+/**
+ * Converts a value (i.e. a "height" in the strip chart) to a horizontal
+ * pixel offset.
+ */
+INLINE int PStatTimeline::
+height_to_pixel(double value) const {
+ return (int)((double)_xsize * value / get_horizontal_scale());
+}
+
+/**
+ * Converts a horizontal pixel offset to a value (a "height" in the strip
+ * chart).
+ */
+INLINE double PStatTimeline::
+pixel_to_height(int x) const {
+ return _time_scale * (double)x;
+}
diff --git a/pandatool/src/pstatserver/pStatTimeline.cxx b/pandatool/src/pstatserver/pStatTimeline.cxx
new file mode 100644
index 0000000000..8c806447c7
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatTimeline.cxx
@@ -0,0 +1,664 @@
+/**
+ * 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 pStatTimeline.cxx
+ * @author rdb
+ * @date 2022-02-11
+ */
+
+#include "pStatTimeline.h"
+
+#include "pStatFrameData.h"
+#include "pStatCollectorDef.h"
+#include "string_utils.h"
+#include "config_pstatclient.h"
+
+#include
+
+/**
+ *
+ */
+PStatTimeline::
+PStatTimeline(PStatMonitor *monitor, int xsize, int ysize) :
+ PStatGraph(monitor, xsize, ysize)
+{
+ // Default to 1 millisecond per 10 pixels.
+ _time_scale = 1 / 10000.0;
+ _target_time_scale = _time_scale;
+
+ _guide_bar_units = GBU_ms | GBU_show_units;
+
+ // Load in the initial data, so that the user can see everything back to the
+ // beginning (or as far as pstats-history goes back to).
+ const PStatClientData *client_data = monitor->get_client_data();
+ if (client_data != nullptr) {
+ size_t row_offset = 0;
+
+ for (int thread_index = 0; thread_index < client_data->get_num_threads(); ++thread_index) {
+ _threads.emplace_back();
+ ThreadRow &thread_row = _threads.back();
+ thread_row._row_offset = row_offset;
+
+ const PStatThreadData *thread_data = client_data->get_thread_data(thread_index);
+ if (thread_data != nullptr) {
+ _threads_changed = true;
+
+ if (!thread_data->is_empty()) {
+ int oldest_frame = thread_data->get_oldest_frame_number();
+ int latest_frame = thread_data->get_latest_frame_number();
+
+ double oldest_start_time = thread_data->get_frame(oldest_frame).get_start();
+ double latest_end_time = thread_data->get_frame(latest_frame).get_end();
+
+ if (!_have_start_time) {
+ _have_start_time = true;
+ _lowest_start_time = oldest_start_time;
+ }
+ else {
+ _lowest_start_time = std::min(_lowest_start_time, oldest_start_time);
+ }
+ _highest_end_time = std::max(_highest_end_time, latest_end_time);
+
+ for (int frame = oldest_frame; frame <= latest_frame; ++frame) {
+ update_bars(thread_index, frame);
+ }
+ }
+ }
+
+ row_offset += thread_row._rows.size() + 1;
+ }
+ }
+
+ _start_time = _lowest_start_time;
+ _target_start_time = _start_time;
+}
+
+/**
+ *
+ */
+PStatTimeline::
+~PStatTimeline() {
+}
+
+/**
+ * Called as each frame's data is made available. There is no guarantee the
+ * frames will arrive in order, or that all of them will arrive at all. The
+ * monitor should be prepared to accept frames received out-of-order or
+ * missing.
+ */
+void PStatTimeline::
+new_data(int thread_index, int frame_number) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+
+ if (client_data != nullptr) {
+ const PStatThreadData *thread_data =
+ client_data->get_thread_data(thread_index);
+
+ if (thread_data != nullptr && !thread_data->is_empty()) {
+ const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
+ double frame_start = frame_data.get_start();
+ double frame_end = frame_data.get_end();
+
+ if (!_have_start_time) {
+ _start_time = frame_start;
+ _have_start_time = true;
+ _lowest_start_time = _start_time;
+ }
+ else if (_start_time < _lowest_start_time) {
+ _lowest_start_time = _start_time;
+ }
+ if (frame_end > _highest_end_time) {
+ _highest_end_time = frame_end;
+ }
+
+ while (thread_index >= _threads.size()) {
+ _threads_changed = true;
+ if (_threads.size() == 0) {
+ _threads.resize(1);
+ } else {
+ _threads.resize(_threads.size() + 1);
+ _threads[_threads.size() - 1]._row_offset =
+ _threads[_threads.size() - 2]._row_offset +
+ _threads[_threads.size() - 2]._rows.size() + 1;
+ }
+ }
+
+ if (update_bars(thread_index, frame_number)) {
+ // The number of rows was changed.
+ // Change the offset of all subsequent ThreadRows.
+ ThreadRow &thread_row = _threads[thread_index];
+ size_t offset = thread_row._row_offset + thread_row._rows.size() + 1;
+ for (size_t ti = (size_t)(thread_index + 1); ti < _threads.size(); ++ti) {
+ _threads[ti]._row_offset = offset;
+ offset += _threads[ti]._rows.size() + 1;
+ }
+ _threads_changed = true;
+ normal_guide_bars();
+ force_redraw();
+ }
+ else if (frame_end >= _start_time || frame_start <= _start_time + get_horizontal_scale()) {
+ normal_guide_bars();
+ begin_draw();
+ draw_thread(thread_index, frame_start, frame_end);
+ end_draw();
+ }
+ }
+ }
+
+ idle();
+}
+
+/**
+ * Called by new_data(). Updates the bars without doing any drawing. Returns
+ * true if the number of rows was changed (forcing a full redraw), false if
+ * only new bars were added on the right side.
+ */
+bool PStatTimeline::
+update_bars(int thread_index, int frame_number) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ const PStatThreadData *thread_data = client_data->get_thread_data(thread_index);
+ const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
+ ThreadRow &thread_row = _threads[thread_index];
+ thread_row._label = client_data->get_thread_name(thread_index);
+ bool changed_num_rows = false;
+
+ // pair
+ pvector > stack;
+
+ size_t num_events = frame_data.get_num_events();
+ for (size_t i = 0; i < num_events; ++i) {
+ int collector_index = frame_data.get_time_collector(i);
+ double time = frame_data.get_time(i);
+
+ if (frame_data.is_start(i)) {
+ stack.push_back(std::make_pair(collector_index, std::max(time, _start_time)));
+ if (stack.size() > thread_row._rows.size()) {
+ thread_row._rows.resize(stack.size());
+ changed_num_rows = true;
+ }
+ }
+ else if (!stack.empty()) {
+ if (stack.back().first == collector_index) {
+ // Most likely case, ending the most recent collector that is still
+ // open.
+ double start_time = stack.back().second;
+ stack.pop_back();
+ thread_row._rows[stack.size()].push_back({
+ start_time, time, collector_index, thread_index, frame_number});
+
+ while (!stack.empty() && stack.back().first < 0) {
+ stack.pop_back();
+ }
+ }
+ else {
+ // Unlikely case: ending a collector before a "child" has ended.
+ // Go back and clear the row where this collector started.
+ // Don't decrement the row index.
+ for (size_t i = 0; i < stack.size(); ++i) {
+ auto &item = stack[stack.size() - 1 - i];
+
+ if (item.first == collector_index) {
+ thread_row._rows[stack.size() - 1 - i].push_back({
+ item.second, time, collector_index, thread_index, frame_number});
+ item.first = -1;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ // Somehow, we got an end event for a collector we didn't start.
+ // This shouldn't really happen, so we just ignore it.
+ }
+ }
+
+ // Add all unclosed bars.
+ while (!stack.empty()) {
+ int collector_index = stack.back().first;
+ if (collector_index >= 0) {
+ double start_time = stack.back().second;
+ thread_row._rows[stack.size() - 1].push_back({
+ start_time, frame_data.get_end(),
+ collector_index, thread_index, frame_number,
+ });
+ }
+ stack.pop_back();
+ }
+
+ if (thread_row._last_frame >= 0 && frame_number < thread_row._last_frame) {
+ // Added a frame out of order. Resort the rows.
+ for (Row &row : thread_row._rows) {
+ std::sort(row.begin(), row.end());
+ }
+ } else {
+ thread_row._last_frame = frame_number;
+ }
+
+ return changed_num_rows;
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string PStatTimeline::
+get_bar_tooltip(int row, int x) const {
+ ColorBar bar;
+ if (find_bar(row, x, bar)) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ if (client_data != nullptr && client_data->has_collector(bar._collector_index)) {
+ std::ostringstream text;
+ text << client_data->get_collector_fullname(bar._collector_index);
+ text << " (" << format_number(bar._end - bar._start, GBU_show_units | GBU_ms) << ")";
+ return text.str();
+ }
+ }
+ return std::string();
+}
+
+/**
+ * To be called by the user class when the widget size has changed. This
+ * updates the chart's internal data and causes it to issue redraw commands to
+ * reflect the new size.
+ */
+void PStatTimeline::
+changed_size(int xsize, int ysize) {
+ if (xsize != _xsize || ysize != _ysize) {
+ _xsize = xsize;
+ _ysize = ysize;
+
+ normal_guide_bars();
+ force_redraw();
+ }
+}
+
+/**
+ * To be called by the user class when the whole thing needs to be redrawn for
+ * some reason.
+ */
+void PStatTimeline::
+force_redraw() {
+ clear_region();
+
+ begin_draw();
+
+ for (const GuideBar &bar : _guide_bars) {
+ int x = timestamp_to_pixel(bar._height);
+ if (x > 0 && x < get_xsize() - 1) {
+ draw_guide_bar(x, bar._style);
+ }
+ }
+
+ double start_time = _start_time;
+ double end_time = start_time + get_horizontal_scale();
+
+ int num_rows = 0;
+
+ for (size_t ti = 0; ti < _threads.size(); ++ti) {
+ ThreadRow &thread_row = _threads[ti];
+ for (size_t ri = 0; ri < thread_row._rows.size(); ++ri) {
+ draw_row((int)ti, (int)ri, start_time, end_time);
+ ++num_rows;
+ }
+ draw_separator(num_rows++);
+ }
+
+ end_draw();
+}
+
+/**
+ * To be called by the user class when the whole thing needs to be redrawn for
+ * some reason.
+ */
+void PStatTimeline::
+force_redraw(int row, int from_x, int to_x) {
+ double start_time = std::max(_start_time, pixel_to_timestamp(from_x));
+ double end_time = std::min(_start_time + get_horizontal_scale(), pixel_to_timestamp(to_x));
+
+ begin_draw();
+
+ for (size_t ti = 0; ti < _threads.size(); ++ti) {
+ ThreadRow &thread_row = _threads[ti];
+ if (thread_row._row_offset > row) {
+ break;
+ }
+
+ int row_index = row - (int)thread_row._row_offset;
+ if (row_index < thread_row._rows.size()) {
+ draw_row((int)ti, row_index, start_time, end_time);
+ }
+ }
+
+ end_draw();
+}
+
+/**
+ * Calls update_guide_bars with parameters suitable to this kind of graph.
+ */
+void PStatTimeline::
+normal_guide_bars() {
+ double start_time = get_horizontal_scroll();
+ double time_width = get_horizontal_scale();
+ double end_time = start_time + time_width;
+
+ // We want vaguely 150 pixels between guide bars.
+ int max_frames = get_xsize() / 100;
+ int l = (int)std::floor(3.0 * log10(pixel_to_height(150)) + 0.5);
+ double interval = pow(10.0, std::ceil(l / 3.0));
+ if ((l + 3000) % 3 == 1) {
+ interval /= 5;
+ }
+ else if ((l + 3000) % 3 == 2) {
+ interval /= 2;
+ }
+
+ _guide_bars.clear();
+
+ // Rather than getting the client data, we look in the color bar data for
+ // the first row, because the client data gets wiped after a while.
+ if (!_threads.empty() && !_threads[0]._rows.empty()) {
+ const Row &row = _threads[0]._rows[0];
+
+ // Look for the last Frame bar with end time lower than our start time.
+ Row::const_iterator it = std::lower_bound(row.begin(), row.end(), ColorBar {0.0, start_time});
+ while (it != row.end() && it->_collector_index != 0) {
+ ++it;
+ }
+
+ int num_frames = 0;
+
+ while (it != row.end() && it->_start <= end_time) {
+ double frame_start = it->_start;
+
+ if (frame_start > start_time) {
+ if (!_guide_bars.empty() && height_to_pixel(frame_start - _guide_bars.back()._height) < 30) {
+ // Get rid of last label, it is in the way.
+ _guide_bars.back()._label.clear();
+ }
+ std::string label = "#";
+ label += format_string(it->_frame_number);
+ _guide_bars.push_back(GuideBar(frame_start, label, GBS_frame));
+
+ if (++num_frames > max_frames) {
+ // Forget it, this is becoming too many lines.
+ _guide_bars.clear();
+ break;
+ }
+ }
+
+ do {
+ ++it;
+ }
+ while (it != row.end() && it->_collector_index != 0);
+
+ double frame_width;
+ if (it != row.end()) {
+ // Only go up to the start of the next frame, limiting to however much
+ // fits in the graph.
+ frame_width = std::min(it->_start - frame_start, end_time - frame_start);
+ } else {
+ // Reached the end; just continue to the end of the graph.
+ frame_width = end_time - frame_start;
+ }
+
+ if (interval > 0.0) {
+ int first_bar = std::max((int)((start_time - frame_start) / interval), 1);
+ int num_bars = (int)std::round(frame_width / interval);
+
+ for (int i = first_bar; i < num_bars; ++i) {
+ double offset = i * interval;
+ std::string label = "+";
+ label += format_number(offset, GBU_show_units | GBU_ms);
+ _guide_bars.push_back(GuideBar(frame_start + offset, label, GBS_normal));
+ }
+ }
+ }
+ }
+
+ if (_guide_bars.empty() && interval > 0.0) {
+ int first_bar = std::max((int)(start_time / interval), 1);
+ int num_bars = (int)std::round(end_time / interval);
+
+ for (int i = first_bar; i < num_bars; ++i) {
+ double time = i * interval;
+ std::string label = format_number(time, GBU_show_units | GBU_ms);
+ _guide_bars.push_back(GuideBar(time, label, GBS_frame));
+ }
+ }
+
+ _guide_bars_changed = true;
+}
+
+/**
+ * Should be overridden by the user class to wipe out the entire strip chart
+ * region.
+ */
+void PStatTimeline::
+clear_region() {
+}
+
+/**
+ * Should be overridden by the user class. This hook will be called before
+ * drawing any bars in the chart.
+ */
+void PStatTimeline::
+begin_draw() {
+}
+
+/**
+ *
+ */
+void PStatTimeline::
+draw_thread(int thread_index, double start_time, double end_time) {
+ if (thread_index < 0 || (size_t)thread_index > _threads.size()) {
+ return;
+ }
+
+ ThreadRow &thread_row = _threads[(size_t)thread_index];
+ for (size_t ri = 0; ri < thread_row._rows.size(); ++ri) {
+ draw_row(thread_index, (int)ri, start_time, end_time);
+ }
+}
+
+/**
+ *
+ */
+void PStatTimeline::
+draw_row(int thread_index, int row_index, double start_time, double end_time) {
+ ThreadRow &thread_row = _threads[thread_index];
+ Row &row = thread_row._rows[row_index];
+
+ const PStatClientData *client_data = _monitor->get_client_data();
+
+ // Find the first element whose end time is larger than our start time.
+ // Then iterate until at least the end of the frame.
+ Row::iterator it = std::lower_bound(row.begin(), row.end(), ColorBar {0.0, start_time});
+ if (it == row.end()) {
+ return;
+ }
+
+ int frame_number = it->_frame_number;
+ do {
+ ColorBar &bar = *it;
+
+ int from_x = timestamp_to_pixel(bar._start);
+ int to_x = timestamp_to_pixel(bar._end);
+
+ if (to_x >= 0 && to_x > from_x && from_x < get_xsize()) {
+ if (bar._collector_index != 0) {
+ draw_bar(thread_row._row_offset + row_index, from_x, to_x,
+ bar._collector_index,
+ client_data->get_collector_name(bar._collector_index));
+ } else {
+ draw_bar(thread_row._row_offset + row_index, from_x, to_x,
+ bar._collector_index,
+ std::string("Frame ") + format_string(bar._frame_number));
+ }
+ }
+
+ ++it;
+ }
+ while (it != row.end() && (it->_start <= end_time || it->_frame_number == frame_number));
+}
+
+/**
+ * Draws a horizontal separator.
+ */
+void PStatTimeline::
+draw_separator(int) {
+}
+
+/**
+ * Draws a vertical guide bar. If the row is -1, draws it in all rows.
+ */
+void PStatTimeline::
+draw_guide_bar(int x, GuideBarStyle style) {
+}
+
+/**
+ * Draws a single bar in the chart for the indicated row, in the color for the
+ * given collector, for the indicated horizontal pixel range.
+ */
+void PStatTimeline::
+draw_bar(int, int, int, int, const std::string &) {
+}
+
+/**
+ * Should be overridden by the user class. This hook will be called after
+ * drawing a series of color bars in the chart.
+ */
+void PStatTimeline::
+end_draw() {
+}
+
+/**
+ * Should be overridden by the user class to perform any other updates might
+ * be necessary after the bars have been redrawn.
+ */
+void PStatTimeline::
+idle() {
+}
+
+/**
+ * Should be called periodically to update any animated values. Returns false
+ * to indicate that the animation is done and no longer needs to be called.
+ */
+bool PStatTimeline::
+animate(double time, double dt) {
+ int hmove = ((_keys_held & (F_right | F_d)) != 0)
+ - ((_keys_held & (F_left | F_a)) != 0);
+ int vmove = ((_keys_held & F_w) != 0)
+ - ((_keys_held & F_s) != 0);
+
+ if (hmove > 0) {
+ if (_scroll_speed < 0) {
+ _scroll_speed = 1.0;
+ }
+ _scroll_speed += 1.0;
+ }
+ else if (hmove < 0) {
+ if (_scroll_speed > 0) {
+ _scroll_speed = -1.0;
+ }
+ _scroll_speed -= 1.0;
+ }
+ else if (_scroll_speed != 0.0) {
+ _scroll_speed *= std::exp(-12.0 * dt);
+ if (std::abs(_scroll_speed) < 0.2) {
+ _scroll_speed = 0.0;
+ }
+ }
+
+ if (vmove > 0) {
+ if (_zoom_speed < 0) {
+ _zoom_speed = 1.0;
+ }
+ _zoom_speed += 1.0;
+ }
+ else if (vmove < 0) {
+ if (_zoom_speed > 0) {
+ _zoom_speed = -1.0;
+ }
+ _zoom_speed -= 1.0;
+ }
+ else if (_zoom_speed != 0.0) {
+ _zoom_speed *= std::exp(-12.0 * dt);
+ if (std::abs(_zoom_speed) < 0.2) {
+ _zoom_speed = 0.0;
+ }
+ }
+
+ if (_zoom_speed != 0.0) {
+ zoom_to(get_horizontal_scale() * pow(0.5, _zoom_speed * dt), _zoom_center);
+ }
+
+ if (_scroll_speed != 0.0) {
+ scroll_by(_scroll_speed * 300 * _time_scale * dt);
+ }
+
+ if (_target_start_time != _start_time) {
+ double dist = _target_start_time - _start_time;
+ // When the difference is less than 2 pixels, snap to target position.
+ if (std::abs(dist) < _time_scale * 2) {
+ _start_time = _target_start_time;
+ } else {
+ dist *= 1.0 - std::exp(-12.0 * dt);
+ _start_time += dist;
+ }
+ }
+
+ if (_target_time_scale != _time_scale) {
+ //double dist = std::log(_target_time_scale) - std::log(_time_scale);
+ double dist = _target_time_scale - _time_scale;
+ if (_target_start_time == _start_time && std::abs(dist) < 0.01) {
+ _time_scale = _target_time_scale;
+ } else {
+ dist *= 1.0 - std::exp(-12.0 * dt);
+ //_time_scale *= std::exp(dist);
+ _time_scale += dist;
+ }
+ }
+
+ normal_guide_bars();
+ force_redraw();
+
+ // Stop the animation when the speed is 0 and no key is still held.
+ return _keys_held != 0
+ || _scroll_speed != 0
+ || _zoom_speed != 0
+ || _target_start_time != _start_time
+ || _target_time_scale != _time_scale;
+}
+
+/**
+ * Return the ColorBar at the indicated position.
+ */
+bool PStatTimeline::
+find_bar(int row, int x, ColorBar &bar) const {
+ double time = pixel_to_timestamp(x);
+
+ for (size_t ti = 0; ti < _threads.size(); ++ti) {
+ const ThreadRow &thread_row = _threads[ti];
+ if (thread_row._row_offset > row) {
+ break;
+ }
+
+ int row_index = row - (int)thread_row._row_offset;
+ if (row_index < thread_row._rows.size()) {
+ // Find the first element whose end time is larger than the given time.
+ const Row &bars = thread_row._rows[row_index];
+ Row::const_iterator it = std::lower_bound(bars.begin(), bars.end(), ColorBar {time, time});
+ if (it != bars.end() && it->_start <= time) {
+ bar = *it;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/pandatool/src/pstatserver/pStatTimeline.h b/pandatool/src/pstatserver/pStatTimeline.h
new file mode 100644
index 0000000000..0a9d0f779f
--- /dev/null
+++ b/pandatool/src/pstatserver/pStatTimeline.h
@@ -0,0 +1,130 @@
+/**
+ * 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 pStatTimeline.h
+ * @author rdb
+ * @date 2022-02-11
+ */
+
+#ifndef PSTATTIMELINE_H
+#define PSTATTIMELINE_H
+
+#include "pandatoolbase.h"
+
+#include "pStatGraph.h"
+#include "pStatMonitor.h"
+#include "pStatClientData.h"
+#include "pdeque.h"
+
+class PStatFrameData;
+
+/**
+ * This is an abstract class that presents the interface for drawing a piano-
+ * roll type chart: it shows the time spent in each of a number of collectors
+ * as a horizontal bar of color, with time as the horizontal axis.
+ *
+ * This class just pnages all the piano-roll logic; the actual nuts and bolts
+ * of drawing pixels is left to a user-derived class.
+ */
+class PStatTimeline : public PStatGraph {
+public:
+ PStatTimeline(PStatMonitor *monitor, int xsize, int ysize);
+ virtual ~PStatTimeline();
+
+ void new_data(int thread_index, int frame_number);
+ bool update_bars(int thread_index, int frame_number);
+
+ INLINE void set_horizontal_scale(double time_width);
+ INLINE double get_horizontal_scale() const;
+ INLINE void set_horizontal_scroll(double time_start);
+ INLINE double get_horizontal_scroll() const;
+
+ INLINE void zoom_to(double time_width, double pivot);
+ INLINE void zoom_by(double amount, double center);
+ INLINE void scroll_to(double time_start);
+ INLINE void scroll_by(double time_start);
+
+ INLINE int timestamp_to_pixel(double time) const;
+ INLINE double pixel_to_timestamp(int x) const;
+ INLINE int height_to_pixel(double value) const;
+ INLINE double pixel_to_height(int y) const;
+
+ std::string get_bar_tooltip(int row, int x) const;
+
+protected:
+ void changed_size(int xsize, int ysize);
+ void force_redraw();
+ void force_redraw(int row, int from_x, int to_x);
+ void normal_guide_bars();
+
+ virtual void clear_region();
+ virtual void begin_draw();
+ void draw_thread(int thread_index, double start_time, double end_time);
+ void draw_row(int thread_index, int row_index, double start_time, double end_time);
+ virtual void draw_separator(int row);
+ virtual void draw_guide_bar(int x, GuideBarStyle style);
+ virtual void draw_bar(int row, int from_x, int to_x, int collector_index,
+ const std::string &collector_name);
+ virtual void end_draw();
+ virtual void idle();
+
+ bool animate(double time, double dt);
+
+ class ColorBar {
+ public:
+ double _start, _end;
+ int _collector_index;
+ int _thread_index;
+ int _frame_number;
+
+ bool operator < (const ColorBar &other) const {
+ return _end < other._end;
+ }
+ };
+ typedef pvector Row;
+ typedef pvector Rows;
+
+ bool find_bar(int row, int x, ColorBar &bar) const;
+
+ class ThreadRow {
+ public:
+ std::string _label;
+ Rows _rows;
+ size_t _row_offset = 0;
+ int _last_frame = -1;
+ };
+ typedef pvector ThreadRows;
+ ThreadRows _threads;
+ bool _threads_changed = true;
+
+ enum KeyFlag {
+ F_left = 1,
+ F_right = 2,
+ F_w = 4,
+ F_a = 8,
+ F_s = 16,
+ F_d = 32,
+ };
+ int _keys_held = 0;
+ double _scroll_speed = 0.0;
+ double _zoom_speed = 0.0;
+ double _zoom_center = 0.0;
+
+private:
+ double _time_scale;
+ double _start_time = 0.0;
+ double _lowest_start_time = 0.0;
+ double _highest_end_time = 0.0;
+ bool _have_start_time = false;
+ double _target_start_time = 0.0;
+ double _target_time_scale;
+};
+
+#include "pStatTimeline.I"
+
+#endif
diff --git a/pandatool/src/pstatserver/pStatView.cxx b/pandatool/src/pstatserver/pStatView.cxx
index e69959a731..7d26455e7c 100644
--- a/pandatool/src/pstatserver/pStatView.cxx
+++ b/pandatool/src/pstatserver/pStatView.cxx
@@ -520,15 +520,16 @@ reset_level(PStatViewLevel *level) {
if (level->_parent == nullptr) {
// This level didn't know its parent before, but now it does.
- PStatViewLevel *parent_level = get_level(parent_index);
- nassertr(parent_level != level, true);
-
- level->_parent = parent_level;
- parent_level->_children.push_back(level);
- parent_level->sort_children(_client_data);
- any_changed = true;
-
- } else if (level->_parent->_collector != parent_index) {
+ if (level->_collector != 0 || parent_index != 0) {
+ PStatViewLevel *parent_level = get_level(parent_index);
+ nassertr(parent_level != level, true);
+ level->_parent = parent_level;
+ parent_level->_children.push_back(level);
+ parent_level->sort_children(_client_data);
+ any_changed = true;
+ }
+ }
+ else if (level->_parent->_collector != parent_index) {
// This level knew about its parent, but now it's something different.
PStatViewLevel *old_parent_level = level->_parent;
nassertr(old_parent_level != level, true);
diff --git a/pandatool/src/text-stats/textMonitor.cxx b/pandatool/src/text-stats/textMonitor.cxx
index 2f4f458387..dd437d6106 100644
--- a/pandatool/src/text-stats/textMonitor.cxx
+++ b/pandatool/src/text-stats/textMonitor.cxx
@@ -22,9 +22,10 @@
*
*/
TextMonitor::
-TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data ) : PStatMonitor(server) {
- _outStream = outStream; //[PECI]
- _show_raw_data = show_raw_data;
+TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json) : PStatMonitor(server) {
+ _outStream = outStream; //[PECI]
+ _show_raw_data = show_raw_data;
+ _json = json;
}
/**
@@ -73,6 +74,30 @@ got_bad_version(int client_major, int client_minor,
<< server_major << "." << server_minor << ".\n";
}
+/**
+ * Called whenever a new Thread definition is received from the client.
+ * Generally, the client will send all of its threads over shortly after
+ * connecting, but there's no guarantee that they will all be received before
+ * the first frames are received. The monitor should be prepared to accept
+ * new Thread definitions midstream.
+ */
+void TextMonitor::
+new_thread(int thread_index) {
+ if (_json) {
+ const PStatClientData *client_data = get_client_data();
+
+ int pid = get_client_pid();
+ if (pid < 0) {
+ pid = _dummy_pid;
+ }
+
+ (*_outStream)
+ << "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":" << pid
+ << ",\"tid\":" << thread_index << ",\"args\":{\"name\":\""
+ << client_data->get_thread_name(thread_index) << "\"}},\n";
+ }
+}
+
/**
* Called as each frame's data is made available. There is no gurantee the
* frames will arrive in order, or that all of them will arrive at all. The
@@ -84,12 +109,29 @@ new_data(int thread_index, int frame_number) {
PStatView &view = get_view(thread_index);
const PStatThreadData *thread_data = view.get_thread_data();
- if (frame_number == thread_data->get_latest_frame_number()) {
- view.set_to_frame(frame_number);
+ view.set_to_frame(frame_number);
- if (view.all_collectors_known()) {
- const PStatClientData *client_data = get_client_data();
+ if (true) {
+ const PStatClientData *client_data = get_client_data();
+ if (_json) {
+ int pid = get_client_pid();
+ if (pid < 0) {
+ pid = _dummy_pid;
+ }
+
+ const PStatFrameData &frame_data = thread_data->get_frame(frame_number);
+ int num_events = frame_data.get_num_events();
+ for (int i = 0; i < num_events; ++i) {
+ int collector_index = frame_data.get_time_collector(i);
+ (*_outStream)
+ << "{\"name\":\"" << client_data->get_collector_fullname(collector_index)
+ << "\",\"ts\":" << (uint64_t)(frame_data.get_time(i) * 1000000)
+ << ",\"ph\":\"" << (frame_data.is_start(i) ? 'B' : 'E') << "\""
+ << ",\"tid\":" << thread_index << ",\"pid\":" << pid << "},\n";
+ }
+ }
+ else {
(*_outStream) << "\rThread "
<< client_data->get_thread_name(thread_index)
<< " frame " << frame_number << ", "
@@ -149,6 +191,7 @@ new_data(int thread_index, int frame_number) {
void TextMonitor::
lost_connection() {
nout << "Lost connection.\n";
+ ++_dummy_pid;
}
/**
diff --git a/pandatool/src/text-stats/textMonitor.h b/pandatool/src/text-stats/textMonitor.h
index 4c88340e46..13cfa860c5 100644
--- a/pandatool/src/text-stats/textMonitor.h
+++ b/pandatool/src/text-stats/textMonitor.h
@@ -29,7 +29,7 @@ class TextStats;
*/
class TextMonitor : public PStatMonitor {
public:
- TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data);
+ TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json = false);
TextStats *get_server();
virtual std::string get_monitor_name();
@@ -37,6 +37,7 @@ public:
virtual void got_hello();
virtual void got_bad_version(int client_major, int client_minor,
int server_major, int server_minor);
+ virtual void new_thread(int thread_index);
virtual void new_data(int thread_index, int frame_number);
virtual void lost_connection();
virtual bool is_thread_safe();
@@ -47,6 +48,8 @@ public:
private:
std::ostream *_outStream; //[PECI]
bool _show_raw_data;
+ bool _json;
+ int _dummy_pid = 0;
};
#include "textMonitor.I"
diff --git a/pandatool/src/text-stats/textStats.cxx b/pandatool/src/text-stats/textStats.cxx
index 9f2929f440..0e677172aa 100644
--- a/pandatool/src/text-stats/textStats.cxx
+++ b/pandatool/src/text-stats/textStats.cxx
@@ -50,6 +50,11 @@ TextStats() {
"time per collector.",
&TextStats::dispatch_none, &_show_raw_data, nullptr);
+ add_option
+ ("j", "", 0,
+ "Output data in JSON format.",
+ &TextStats::dispatch_none, &_json, nullptr);
+
add_option
("o", "filename", 0,
"Filename where to print. If not given then stderr is being used.",
@@ -66,7 +71,7 @@ TextStats() {
PStatMonitor *TextStats::
make_monitor() {
- return new TextMonitor(this, _outFile, _show_raw_data);
+ return new TextMonitor(this, _outFile, _show_raw_data, _json);
}
@@ -87,13 +92,23 @@ run() {
nout << "Listening for connections.\n";
if (_got_outputFileName) {
- _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out);
+ _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out | std::ios::trunc);
} else {
_outFile = &(nout);
}
+ if (_json) {
+ (*_outFile) << "[\n";
+ }
+
main_loop(&user_interrupted);
nout << "Exiting.\n";
+
+ if (_json) {
+ // Remove the last comma.
+ _outFile->seekp(-3, std::ios::cur);
+ (*_outFile) << "\n]\n";
+ }
}
diff --git a/pandatool/src/text-stats/textStats.h b/pandatool/src/text-stats/textStats.h
index b6853ecb46..99725f0e30 100644
--- a/pandatool/src/text-stats/textStats.h
+++ b/pandatool/src/text-stats/textStats.h
@@ -37,6 +37,7 @@ public:
private:
int _port;
bool _show_raw_data;
+ bool _json = false;
// [PECI]
bool _got_outputFileName;
diff --git a/pandatool/src/win-stats/CMakeLists.txt b/pandatool/src/win-stats/CMakeLists.txt
index 0cd7768c3a..99166f6ec9 100644
--- a/pandatool/src/win-stats/CMakeLists.txt
+++ b/pandatool/src/win-stats/CMakeLists.txt
@@ -4,20 +4,23 @@ endif()
set(WINSTATS_HEADERS
winStatsChartMenu.h
+ winStatsFlameGraph.h
winStatsGraph.h
winStats.h
- winStatsLabel.h
+ winStatsLabel.h winStatsLabel.I
winStatsLabelStack.h
winStatsMenuId.h
winStatsMonitor.h winStatsMonitor.I
winStatsPianoRoll.h
winStatsServer.h
winStatsStripChart.h
+ winStatsTimeline.h
)
set(WINSTATS_SOURCES
winStatsChartMenu.cxx
winStats.cxx
+ winStatsFlameGraph.cxx
winStatsGraph.cxx
winStatsLabel.cxx
winStatsLabelStack.cxx
@@ -25,11 +28,12 @@ set(WINSTATS_SOURCES
winStatsPianoRoll.cxx
winStatsServer.cxx
winStatsStripChart.cxx
+ winStatsTimeline.cxx
)
composite_sources(win-stats WINSTATS_SOURCES)
add_executable(win-stats ${WINSTATS_HEADERS} ${WINSTATS_SOURCES})
-target_link_libraries(win-stats p3progbase p3pstatserver)
+target_link_libraries(win-stats p3progbase p3pstatserver comctl32.lib)
# This program is NOT actually called win-stats. It's just pstats.exe
set_target_properties(win-stats PROPERTIES OUTPUT_NAME "pstats")
diff --git a/pandatool/src/win-stats/winStats.cxx b/pandatool/src/win-stats/winStats.cxx
index e40830e189..5dabb7b71a 100644
--- a/pandatool/src/win-stats/winStats.cxx
+++ b/pandatool/src/win-stats/winStats.cxx
@@ -20,6 +20,11 @@
#define WIN32_LEAN_AND_MEAN 1
#endif
#include
+#include
+#include
+
+// Enable common controls version 6, necessary for modern visual styles
+#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
static const char *toplevel_class_name = "pstats";
static WinStatsServer *server = nullptr;
@@ -82,6 +87,15 @@ create_toplevel_window(HINSTANCE application) {
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
+ // Initialize commctl32.dll.
+ INITCOMMONCONTROLSEX icc;
+ icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES;
+ icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCommonControlsEx(&icc);
+
+ // Signal DPI awareness.
+ SetProcessDPIAware();
+
HINSTANCE application = GetModuleHandle(nullptr);
HWND toplevel_window = create_toplevel_window(application);
diff --git a/pandatool/src/win-stats/winStatsChartMenu.cxx b/pandatool/src/win-stats/winStatsChartMenu.cxx
index c042acf8ba..71d30282f5 100644
--- a/pandatool/src/win-stats/winStatsChartMenu.cxx
+++ b/pandatool/src/win-stats/winStatsChartMenu.cxx
@@ -93,15 +93,31 @@ do_update() {
}
// Now rebuild the menu with the new set of entries.
-
- // The menu item(s) for the thread's frame time goes first.
- add_view(_menu, view.get_top_level(), false);
-
- bool needs_separator = true;
MENUITEMINFO mii;
memset(&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
+ if (_thread_index == 0) {
+ // Timeline goes first.
+ WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_timeline, false);
+ int menu_id = _monitor->get_menu_id(menu_def);
+
+ mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = menu_id;
+ mii.dwTypeData = "Timeline";
+ InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
+
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
+ }
+
+ // The menu item(s) for the thread's frame time goes second.
+ add_view(_menu, view.get_top_level(), false);
+
+ bool needs_separator = true;
+
// And then the menu item(s) for each of the level values.
const PStatClientData *client_data = _monitor->get_client_data();
int num_toplevel_collectors = client_data->get_num_toplevel_collectors();
@@ -125,19 +141,21 @@ do_update() {
}
}
- // Also a menu item for a piano roll (following a separator).
+ // Also menu item for piano roll (following a separator).
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
- WinStatsMonitor::MenuDef menu_def(_thread_index, -1, false);
- int menu_id = _monitor->get_menu_id(menu_def);
+ {
+ WinStatsMonitor::MenuDef menu_def(_thread_index, -1, WinStatsMonitor::CT_piano_roll, false);
+ int menu_id = _monitor->get_menu_id(menu_def);
- mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
- mii.fType = MFT_STRING;
- mii.wID = menu_id;
- mii.dwTypeData = "Piano Roll";
- InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
+ mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = menu_id;
+ mii.dwTypeData = "Piano Roll";
+ InsertMenuItem(_menu, GetMenuItemCount(_menu), TRUE, &mii);
+ }
}
/**
@@ -151,37 +169,77 @@ add_view(HMENU parent_menu, const PStatViewLevel *view_level, bool show_level) {
const PStatClientData *client_data = _monitor->get_client_data();
std::string collector_name = client_data->get_collector_name(collector);
- WinStatsMonitor::MenuDef menu_def(_thread_index, collector, show_level);
- int menu_id = _monitor->get_menu_id(menu_def);
-
MENUITEMINFO mii;
memset(&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
- mii.fType = MFT_STRING;
- mii.wID = menu_id;
- mii.dwTypeData = (char *)collector_name.c_str();
- InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii);
-
int num_children = view_level->get_num_children();
- if (num_children > 1) {
- // If the collector has more than one child, add a menu entry to go
- // directly to each of its children.
- HMENU submenu = CreatePopupMenu();
- std::string submenu_name = collector_name + " components";
+ if (show_level && num_children == 0) {
+ // For a level collector without children, no point in making a submenu.
+ WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_strip_chart, show_level);
+ int menu_id = _monitor->get_menu_id(menu_def);
+
+ mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = menu_id;
+ mii.dwTypeData = (char *)collector_name.c_str();
+ InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii);
+ return;
+ }
+
+ HMENU menu;
+ if (!show_level && collector == 0 && num_children == 0) {
+ // Root collector without children, just add the options directly to the
+ // parent menu.
+ menu = parent_menu;
+ }
+ else {
+ // Create a submenu.
+ menu = CreatePopupMenu();
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
mii.fType = MFT_STRING;
- mii.hSubMenu = submenu;
- mii.dwTypeData = (char *)submenu_name.c_str();
+ mii.hSubMenu = menu;
+ mii.dwTypeData = (char *)collector_name.c_str();
InsertMenuItem(parent_menu, GetMenuItemCount(parent_menu), TRUE, &mii);
+ }
+
+ {
+ WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_strip_chart, show_level);
+ int menu_id = _monitor->get_menu_id(menu_def);
+
+ mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = menu_id;
+ mii.dwTypeData = "Open Strip Chart";
+ InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii);
+ }
+
+ if (!show_level) {
+ if (collector == 0 && num_children == 0) {
+ collector = -1;
+ }
+
+ WinStatsMonitor::MenuDef menu_def(_thread_index, collector, WinStatsMonitor::CT_flame_graph);
+ int menu_id = _monitor->get_menu_id(menu_def);
+
+ mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = menu_id;
+ mii.dwTypeData = "Open Flame Graph";
+ InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii);
+ }
+
+ if (num_children > 0) {
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &mii);
// Reverse the order since the menus are listed from the top down; we want
// to be visually consistent with the graphs, which list these labels from
// the bottom up.
for (int c = num_children - 1; c >= 0; c--) {
- add_view(submenu, view_level->get_child(c), show_level);
+ add_view(menu, view_level->get_child(c), show_level);
}
}
}
diff --git a/pandatool/src/win-stats/winStatsFlameGraph.cxx b/pandatool/src/win-stats/winStatsFlameGraph.cxx
new file mode 100644
index 0000000000..80c3e7a2b8
--- /dev/null
+++ b/pandatool/src/win-stats/winStatsFlameGraph.cxx
@@ -0,0 +1,731 @@
+/**
+ * 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 winStatsFlameGraph.cxx
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#include "winStatsFlameGraph.h"
+#include "winStatsLabel.h"
+#include "winStatsMonitor.h"
+#include "pStatCollectorDef.h"
+
+#include
+
+static const int default_flame_graph_width = 1085;
+static const int default_flame_graph_height = 210;
+
+bool WinStatsFlameGraph::_window_class_registered = false;
+const char * const WinStatsFlameGraph::_window_class_name = "flame";
+
+/**
+ *
+ */
+WinStatsFlameGraph::
+WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
+ int collector_index) :
+ PStatFlameGraph(monitor,
+ thread_index, collector_index,
+ monitor->get_pixel_scale() * default_flame_graph_width / 4,
+ monitor->get_pixel_scale() * default_flame_graph_height / 4),
+ WinStatsGraph(monitor)
+{
+ _left_margin = _pixel_scale * 2;
+ _right_margin = _pixel_scale * 2;
+ _top_margin = _pixel_scale * 6;
+ _bottom_margin = _pixel_scale * 2;
+
+ // Let's show the units on the guide bar labels. There's room.
+ set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
+
+ _average_check_box = 0;
+
+ create_window();
+ clear_region();
+}
+
+/**
+ *
+ */
+WinStatsFlameGraph::
+~WinStatsFlameGraph() {
+}
+
+/**
+ * Called as each frame's data is made available. There is no guarantee the
+ * frames will arrive in order, or that all of them will arrive at all. The
+ * monitor should be prepared to accept frames received out-of-order or
+ * missing.
+ */
+void WinStatsFlameGraph::
+new_data(int thread_index, int frame_number) {
+ if (is_title_unknown()) {
+ std::string window_title = get_title_text();
+ if (!is_title_unknown()) {
+ SetWindowText(_window, window_title.c_str());
+ }
+ }
+
+ if (!_pause) {
+ update();
+
+ std::string text = format_number(get_horizontal_scale(), get_guide_bar_units(), get_guide_bar_unit_name());
+ if (_net_value_text != text) {
+ _net_value_text = text;
+ RECT rect;
+ GetClientRect(_window, &rect);
+ rect.bottom = _top_margin;
+ InvalidateRect(_window, &rect, TRUE);
+ }
+ }
+}
+
+/**
+ * Called when it is necessary to redraw the entire graph.
+ */
+void WinStatsFlameGraph::
+force_redraw() {
+ PStatFlameGraph::force_redraw();
+}
+
+/**
+ * Called when the user has resized the window, forcing a resize of the graph.
+ */
+void WinStatsFlameGraph::
+changed_graph_size(int graph_xsize, int graph_ysize) {
+ PStatFlameGraph::changed_size(graph_xsize, graph_ysize);
+}
+
+/**
+ * Called when the user selects a new time units from the monitor pulldown
+ * menu, this should adjust the units for the graph to the indicated mask if
+ * it is a time-based graph.
+ */
+void WinStatsFlameGraph::
+set_time_units(int unit_mask) {
+ int old_unit_mask = get_guide_bar_units();
+ if ((old_unit_mask & (GBU_hz | GBU_ms)) != 0) {
+ unit_mask = unit_mask & (GBU_hz | GBU_ms);
+ unit_mask |= (old_unit_mask & GBU_show_units);
+ set_guide_bar_units(unit_mask);
+
+ RECT rect;
+ GetClientRect(_window, &rect);
+ rect.left = _right_margin;
+ InvalidateRect(_window, &rect, TRUE);
+ }
+}
+
+/**
+ * Called when the user single-clicks on a label.
+ */
+void WinStatsFlameGraph::
+on_click_label(int collector_index) {
+ set_collector_index(collector_index);
+}
+
+/**
+ * Called when the user hovers the mouse over a label.
+ */
+void WinStatsFlameGraph::
+on_enter_label(int collector_index) {
+ if (collector_index != _highlighted_index) {
+ _highlighted_index = collector_index;
+ clear_graph_tooltip();
+
+ if (!get_average_mode()) {
+ PStatFlameGraph::force_redraw();
+ }
+ }
+}
+
+/**
+ * Called when the user's mouse cursor leaves a label.
+ */
+void WinStatsFlameGraph::
+on_leave_label(int collector_index) {
+ if (collector_index == _highlighted_index && collector_index != -1) {
+ _highlighted_index = -1;
+
+ if (!get_average_mode()) {
+ PStatFlameGraph::force_redraw();
+ }
+ }
+}
+
+/**
+ * Calls update_guide_bars with parameters suitable to this kind of graph.
+ */
+void WinStatsFlameGraph::
+normal_guide_bars() {
+ // We want vaguely 100 pixels between guide bars.
+ int num_bars = get_xsize() / (_pixel_scale * 25);
+
+ _guide_bars.clear();
+
+ double dist = get_horizontal_scale() / num_bars;
+
+ for (int i = 1; i < num_bars; ++i) {
+ _guide_bars.push_back(make_guide_bar(i * dist));
+ }
+
+ _guide_bars_changed = true;
+}
+
+/**
+ * Erases the chart area.
+ */
+void WinStatsFlameGraph::
+clear_region() {
+ RECT rect = { 0, 0, get_xsize(), get_ysize() };
+ FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+}
+
+/**
+ * Erases the chart area in preparation for drawing a bunch of bars.
+ */
+void WinStatsFlameGraph::
+begin_draw() {
+ clear_region();
+
+ // Draw in the guide bars.
+ int num_guide_bars = get_num_guide_bars();
+ for (int i = 0; i < num_guide_bars; i++) {
+ draw_guide_bar(_bitmap_dc, get_guide_bar(i));
+ }
+
+ SelectObject(_bitmap_dc, WinStatsGraph::_monitor->get_font());
+ SelectObject(_bitmap_dc, GetStockObject(NULL_PEN));
+ SetBkMode(_bitmap_dc, TRANSPARENT);
+ SetTextAlign(_bitmap_dc, TA_LEFT | TA_TOP | TA_NOUPDATECP);
+}
+
+/**
+ * Should be overridden by the user class. Should draw a single bar at the
+ * indicated location.
+ */
+void WinStatsFlameGraph::
+draw_bar(int depth, int from_x, int to_x, int collector_index) {
+ int bottom = get_ysize() - 1 - depth * _pixel_scale * 5;
+ int top = bottom - _pixel_scale * 5;
+
+ bool is_highlighted = collector_index == _highlighted_index;
+ HBRUSH brush = get_collector_brush(collector_index, is_highlighted);
+
+ if (to_x < from_x + 2) {
+ // It's just a tiny sliver. This is a more reliable way to draw it.
+ RECT rect = {from_x, top + 1, from_x + 1, bottom - 1};
+ FillRect(_bitmap_dc, &rect, brush);
+ }
+ else {
+ SelectObject(_bitmap_dc, brush);
+ RoundRect(_bitmap_dc,
+ std::max(from_x, -_pixel_scale - 1),
+ top,
+ std::min(std::max(to_x, from_x + 1), get_xsize() + _pixel_scale),
+ bottom,
+ _pixel_scale,
+ _pixel_scale);
+
+ int left = std::max(from_x, 0) + _pixel_scale / 2;
+ int right = std::min(to_x, get_xsize()) - _pixel_scale / 2;
+
+ if ((to_x - from_x) >= _pixel_scale * 4) {
+ // Only bother drawing the text if we've got some space to draw on.
+ // Choose a suitable foreground color.
+ SetTextColor(_bitmap_dc, get_collector_text_color(collector_index, is_highlighted));
+
+ const PStatClientData *client_data = WinStatsGraph::_monitor->get_client_data();
+ const std::string &name = client_data->get_collector_name(collector_index);
+
+ RECT rect = {left, top, right, bottom};
+ DrawText(_bitmap_dc, name.data(), name.size(),
+ &rect, DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER);
+ }
+ }
+}
+
+/**
+ * Called after all the bars have been drawn, this triggers a refresh event to
+ * draw it to the window.
+ */
+void WinStatsFlameGraph::
+end_draw() {
+ InvalidateRect(_graph_window, nullptr, FALSE);
+}
+
+/**
+ * Called at the end of the draw cycle.
+ */
+void WinStatsFlameGraph::
+idle() {
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool WinStatsFlameGraph::
+animate(double time, double dt) {
+ return PStatFlameGraph::animate(time, dt);
+}
+
+/**
+ *
+ */
+LONG WinStatsFlameGraph::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ if (_potential_drag_mode == DM_new_guide_bar) {
+ set_drag_mode(DM_new_guide_bar);
+ SetCapture(_graph_window);
+ return 0;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wparam)) {
+ case BN_CLICKED:
+ if ((HWND)lparam == _average_check_box) {
+ int result = SendMessage(_average_check_box, BM_GETCHECK, 0, 0);
+ if (result == BST_CHECKED) {
+ set_average_mode(true);
+ start_animation();
+ } else {
+ set_average_mode(false);
+ }
+ return 0;
+ }
+ break;
+
+ case 101:
+ set_collector_index(_popup_index);
+ return 0;
+
+ case 102:
+ WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index, false);
+ return 0;
+
+ case 103:
+ WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
+}
+
+/**
+ *
+ */
+LONG WinStatsFlameGraph::
+graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
+ set_drag_mode(DM_guide_bar);
+ int16_t x = LOWORD(lparam);
+ _drag_start_x = x;
+ SetCapture(_graph_window);
+ return 0;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (_drag_mode == DM_none && _potential_drag_mode == DM_none) {
+ // When the mouse is over a color bar, highlight it.
+ int x = LOWORD(lparam);
+ int y = HIWORD(lparam);
+
+ int collector_index = get_bar_collector(pixel_to_depth(y), x);
+ on_enter_label(collector_index);
+
+ // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
+ // window.
+ TRACKMOUSEEVENT tme = {
+ sizeof(TRACKMOUSEEVENT),
+ TME_LEAVE,
+ _graph_window,
+ 0
+ };
+ TrackMouseEvent(&tme);
+ }
+ else if (_drag_mode == DM_new_guide_bar) {
+ // We haven't created the new guide bar yet; we won't until the mouse
+ // comes within the graph's region.
+ int16_t x = LOWORD(lparam);
+ if (x >= 0 && x < get_xsize()) {
+ set_drag_mode(DM_guide_bar);
+ _drag_guide_bar = add_user_guide_bar(pixel_to_height(x));
+ return 0;
+ }
+ }
+ else if (_drag_mode == DM_guide_bar) {
+ int16_t x = LOWORD(lparam);
+ move_user_guide_bar(_drag_guide_bar, pixel_to_height(x));
+ return 0;
+ }
+ break;
+
+ case WM_MOUSELEAVE:
+ // When the mouse leaves the graph, stop highlighting.
+ if (_highlighted_index != -1) {
+ on_leave_label(_highlighted_index);
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ if (_drag_mode == DM_guide_bar) {
+ int16_t x = LOWORD(lparam);
+ if (x < 0 || x >= get_xsize()) {
+ remove_user_guide_bar(_drag_guide_bar);
+ } else {
+ move_user_guide_bar(_drag_guide_bar, pixel_to_height(x));
+ }
+ set_drag_mode(DM_none);
+ ReleaseCapture();
+ return 0;
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ {
+ // Double-clicking on a color bar in the graph will zoom the graph into
+ // that collector.
+ int16_t x = LOWORD(lparam);
+ int16_t y = HIWORD(lparam);
+ set_collector_index(get_bar_collector(pixel_to_depth(y), x));
+ return 0;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT point;
+ if (GetCursorPos(&point)) {
+ POINT graph_point = point;
+ if (ScreenToClient(_graph_window, &graph_point)) {
+ int depth = pixel_to_depth(graph_point.y);
+ int collector_index = get_bar_collector(depth, graph_point.x);
+ if (collector_index >= 0) {
+ _popup_index = collector_index;
+ HMENU popup = CreatePopupMenu();
+
+ std::string label = get_bar_tooltip(depth, graph_point.x);
+ if (!label.empty()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str());
+ }
+ if (collector_index == get_collector_index()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 101, "Set as Focus");
+ } else {
+ AppendMenu(popup, MF_STRING, 101, "Set as Focus");
+ }
+ AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
+ AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
+ TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
+ }
+ }
+ }
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam);
+}
+
+/**
+ * This is called during the servicing of WM_PAINT; it gives a derived class
+ * opportunity to do some further painting into the window (the outer window,
+ * not the graph window).
+ */
+void WinStatsFlameGraph::
+additional_window_paint(HDC hdc) {
+ // Draw in the labels for the guide bars.
+ SelectObject(hdc, WinStatsGraph::_monitor->get_font());
+ SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
+ SetBkMode(hdc, TRANSPARENT);
+
+ int y = _top_margin - _pixel_scale / 2;
+
+ int i;
+ int num_guide_bars = get_num_guide_bars();
+ for (i = 0; i < num_guide_bars; i++) {
+ draw_guide_label(hdc, y, get_guide_bar(i));
+ }
+
+ int num_user_guide_bars = get_num_user_guide_bars();
+ for (i = 0; i < num_user_guide_bars; i++) {
+ draw_guide_label(hdc, y, get_user_guide_bar(i));
+ }
+
+ RECT rect;
+ GetClientRect(_window, &rect);
+
+ // Now draw the "net value" label at the top.
+ SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM);
+ SetTextColor(hdc, RGB(0, 0, 0));
+ TextOut(hdc, rect.right - _right_margin, y,
+ _net_value_text.data(), _net_value_text.length());
+}
+
+/**
+ * This is called during the servicing of WM_PAINT; it gives a derived class
+ * opportunity to do some further painting into the window (the outer window,
+ * not the graph window).
+ */
+void WinStatsFlameGraph::
+additional_graph_window_paint(HDC hdc) {
+ int num_user_guide_bars = get_num_user_guide_bars();
+ for (int i = 0; i < num_user_guide_bars; i++) {
+ draw_guide_bar(hdc, get_user_guide_bar(i));
+ }
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsFlameGraph::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return get_bar_tooltip(pixel_to_depth(mouse_y), mouse_x);
+}
+
+/**
+ * Based on the mouse position within the window's client area, look for
+ * draggable things the mouse might be hovering over and return the
+ * apprioprate DragMode enum or DM_none if nothing is indicated.
+ */
+WinStatsGraph::DragMode WinStatsFlameGraph::
+consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
+ if (mouse_y >= _graph_top && mouse_y < _graph_top + get_ysize()) {
+ if (mouse_x >= _graph_left && mouse_x < _graph_left + get_xsize()) {
+ // See if the mouse is over a user-defined guide bar.
+ int x = mouse_x - _graph_left;
+ double from_height = pixel_to_height(x - 2);
+ double to_height = pixel_to_height(x + 2);
+ _drag_guide_bar = find_user_guide_bar(from_height, to_height);
+ if (_drag_guide_bar >= 0) {
+ return DM_guide_bar;
+ }
+
+ } else if (mouse_x < _left_margin - 2 ||
+ mouse_x > width - _right_margin + 2) {
+ // The mouse is left or right of the graph; maybe create a new guide
+ // bar.
+ return DM_new_guide_bar;
+ }
+ }
+
+ // Don't upcall; there's no point resizing the margins.
+ return DM_none;
+}
+
+/**
+ * Repositions the graph child window within the parent window according to
+ * the _margin variables.
+ */
+void WinStatsFlameGraph::
+move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) {
+ WinStatsGraph::move_graph_window(graph_left, graph_top, graph_xsize, graph_ysize);
+ if (_average_check_box != 0) {
+ SIZE size;
+ SendMessage(_average_check_box, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
+
+ SetWindowPos(_average_check_box, 0,
+ _left_margin, _top_margin - size.cy - _pixel_scale / 2,
+ size.cx, size.cy,
+ SWP_NOZORDER | SWP_SHOWWINDOW);
+ InvalidateRect(_average_check_box, nullptr, TRUE);
+ }
+}
+
+/**
+ * Converts a pixel to a depth index.
+ */
+int WinStatsFlameGraph::
+pixel_to_depth(int y) const {
+ return (get_ysize() - 1 - y) / (_pixel_scale * 5);
+}
+
+/**
+ * Draws the line for the indicated guide bar on the graph.
+ */
+void WinStatsFlameGraph::
+draw_guide_bar(HDC hdc, const PStatGraph::GuideBar &bar) {
+ int x = height_to_pixel(bar._height);
+
+ if (x > 0 && x < get_xsize() - 1) {
+ // Only draw it if it's not too close to either edge.
+ switch (bar._style) {
+ case GBS_target:
+ SelectObject(hdc, _light_pen);
+ break;
+
+ case GBS_user:
+ SelectObject(hdc, _user_guide_bar_pen);
+ break;
+
+ case GBS_normal:
+ SelectObject(hdc, _dark_pen);
+ break;
+ }
+ MoveToEx(hdc, x, 0, nullptr);
+ LineTo(hdc, x, get_ysize());
+ }
+}
+
+/**
+ * Draws the text for the indicated guide bar label at the top of the graph.
+ */
+void WinStatsFlameGraph::
+draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) {
+ switch (bar._style) {
+ case GBS_target:
+ SetTextColor(hdc, _light_color);
+ break;
+
+ case GBS_user:
+ SetTextColor(hdc, _user_guide_bar_color);
+ break;
+
+ case GBS_normal:
+ SetTextColor(hdc, _dark_color);
+ break;
+ }
+
+ int x = height_to_pixel(bar._height);
+ const std::string &label = bar._label;
+ SIZE size;
+ GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
+
+ if (bar._style != GBS_user) {
+ double from_height = pixel_to_height(x - size.cx);
+ double to_height = pixel_to_height(x + size.cx);
+ if (find_user_guide_bar(from_height, to_height) >= 0) {
+ // Omit the label: there's a user-defined guide bar in the same space.
+ return;
+ }
+ }
+
+ int this_x = _graph_left + x - size.cx / 2;
+ if (x >= 0 && x < get_xsize()) {
+ TextOut(hdc, this_x, y,
+ label.data(), label.length());
+ }
+}
+
+/**
+ * Creates the window for this strip chart.
+ */
+void WinStatsFlameGraph::
+create_window() {
+ if (_window) {
+ return;
+ }
+
+ HINSTANCE application = GetModuleHandle(nullptr);
+ register_window_class(application);
+
+ std::string window_title = get_title_text();
+ POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos();
+
+ RECT win_rect = {
+ 0, 0,
+ _left_margin + get_xsize() + _right_margin,
+ _top_margin + get_ysize() + _bottom_margin
+ };
+
+ // compute window size based on desired client area size
+ AdjustWindowRect(&win_rect, graph_window_style, FALSE);
+
+ _window =
+ CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name,
+ window_title.c_str(), graph_window_style,
+ window_pos.x, window_pos.y,
+ win_rect.right - win_rect.left,
+ win_rect.bottom - win_rect.top,
+ WinStatsGraph::_monitor->get_window(),
+ nullptr, application, 0);
+ if (!_window) {
+ nout << "Could not create FlameGraph window!\n";
+ exit(1);
+ }
+
+ SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+
+ _average_check_box =
+ CreateWindow(WC_BUTTON, "Average", WS_CHILD | BS_AUTOCHECKBOX,
+ 0, 0, 0, 0,
+ _window, nullptr, application, 0);
+ SendMessage(_average_check_box, WM_SETFONT,
+ (WPARAM)WinStatsGraph::_monitor->get_font(), TRUE);
+
+ if (get_average_mode()) {
+ SendMessage(_average_check_box, BM_SETCHECK, BST_CHECKED, 0);
+ start_animation();
+ }
+
+ // Ensure that the window is on top of the stack.
+ SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+/**
+ * Registers the window class for the FlameGraph window, if it has not already
+ * been registered.
+ */
+void WinStatsFlameGraph::
+register_window_class(HINSTANCE application) {
+ if (_window_class_registered) {
+ return;
+ }
+
+ WNDCLASS wc;
+
+ ZeroMemory(&wc, sizeof(WNDCLASS));
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)static_window_proc;
+ wc.hInstance = application;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = _window_class_name;
+
+ // Reserve space to associate the this pointer with the window.
+ wc.cbWndExtra = sizeof(WinStatsFlameGraph *);
+
+ if (!RegisterClass(&wc)) {
+ nout << "Could not register FlameGraph window class!\n";
+ exit(1);
+ }
+
+ _window_class_registered = true;
+}
+
+/**
+ *
+ */
+LONG WINAPI WinStatsFlameGraph::
+static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ WinStatsFlameGraph *self = (WinStatsFlameGraph *)GetWindowLongPtr(hwnd, 0);
+ if (self != nullptr && self->_window == hwnd) {
+ return self->window_proc(hwnd, msg, wparam, lparam);
+ } else {
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+}
diff --git a/pandatool/src/win-stats/winStatsFlameGraph.h b/pandatool/src/win-stats/winStatsFlameGraph.h
new file mode 100644
index 0000000000..92dd48be36
--- /dev/null
+++ b/pandatool/src/win-stats/winStatsFlameGraph.h
@@ -0,0 +1,81 @@
+/**
+ * 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 winStatsFlameGraph.h
+ * @author rdb
+ * @date 2022-01-28
+ */
+
+#ifndef WINSTATSFLAMEGRAPH_H
+#define WINSTATSFLAMEGRAPH_H
+
+#include "pandatoolbase.h"
+
+#include "winStatsGraph.h"
+#include "pStatFlameGraph.h"
+
+class WinStatsLabel;
+
+/**
+ * A window that draws a flame chart, which shows the collectors explicitly
+ * stopping and starting, one frame at a time.
+ */
+class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph {
+public:
+ WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
+ int collector_index=-1);
+ virtual ~WinStatsFlameGraph();
+
+ virtual void new_data(int thread_index, int frame_number);
+ virtual void force_redraw();
+ virtual void changed_graph_size(int graph_xsize, int graph_ysize);
+
+ virtual void set_time_units(int unit_mask);
+ virtual void on_click_label(int collector_index);
+ virtual void on_enter_label(int collector_index);
+ virtual void on_leave_label(int collector_index);
+
+protected:
+ virtual void normal_guide_bars();
+
+ void clear_region();
+ virtual void begin_draw();
+ virtual void draw_bar(int depth, int from_x, int to_x, int collector_index);
+ virtual void end_draw();
+ virtual void idle();
+
+ virtual bool animate(double time, double dt);
+
+ LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+ virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+ virtual void additional_window_paint(HDC hdc);
+ virtual void additional_graph_window_paint(HDC hdc);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
+ virtual DragMode consider_drag_start(int mouse_x, int mouse_y,
+ int width, int height);
+ virtual void move_graph_window(int graph_left, int graph_top,
+ int graph_xsize, int graph_ysize);
+
+private:
+ int pixel_to_depth(int y) const;
+ void draw_guide_bar(HDC hdc, const GuideBar &bar);
+ void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar);
+ void create_window();
+ static void register_window_class(HINSTANCE application);
+
+ static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+ std::string _net_value_text;
+ HWND _average_check_box;
+ int _popup_index = -1;
+
+ static bool _window_class_registered;
+ static const char * const _window_class_name;
+};
+
+#endif
diff --git a/pandatool/src/win-stats/winStatsGraph.cxx b/pandatool/src/win-stats/winStatsGraph.cxx
index 4946bbc671..f57d7c1959 100644
--- a/pandatool/src/win-stats/winStatsGraph.cxx
+++ b/pandatool/src/win-stats/winStatsGraph.cxx
@@ -14,12 +14,15 @@
#include "winStatsGraph.h"
#include "winStatsMonitor.h"
#include "winStatsLabelStack.h"
+#include "trueClock.h"
+#include "convert_srgb.h"
-bool WinStatsGraph::_graph_window_class_registered = false;
-const char * const WinStatsGraph::_graph_window_class_name = "graph";
+#include
+
+#define IDC_GRAPH 100
DWORD WinStatsGraph::graph_window_style =
-WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
+ WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
/**
*
@@ -30,6 +33,7 @@ WinStatsGraph(WinStatsMonitor *monitor) :
{
_window = 0;
_graph_window = 0;
+ _tooltip_window = 0;
_sizewe_cursor = LoadCursor(nullptr, IDC_SIZEWE);
_hand_cursor = LoadCursor(nullptr, IDC_HAND);
_bitmap = 0;
@@ -40,12 +44,16 @@ WinStatsGraph(WinStatsMonitor *monitor) :
_bitmap_xsize = 0;
_bitmap_ysize = 0;
+ _pixel_scale = monitor->get_pixel_scale();
+
_dark_color = RGB(51, 51, 51);
_light_color = RGB(154, 154, 154);
_user_guide_bar_color = RGB(130, 150, 255);
+ _frame_guide_bar_color = RGB(255, 10, 10);
_dark_pen = CreatePen(PS_SOLID, 1, _dark_color);
_light_pen = CreatePen(PS_SOLID, 1, _light_color);
_user_guide_bar_pen = CreatePen(PS_DASH, 1, _user_guide_bar_color);
+ _frame_guide_bar_pen = CreatePen(PS_DASH, 1, _frame_guide_bar_color);
_drag_mode = DM_none;
_potential_drag_mode = DM_none;
@@ -65,12 +73,14 @@ WinStatsGraph::
DeleteObject(_dark_pen);
DeleteObject(_light_pen);
DeleteObject(_user_guide_bar_pen);
+ DeleteObject(_frame_guide_bar_pen);
- Brushes::iterator bi;
- for (bi = _brushes.begin(); bi != _brushes.end(); ++bi) {
- HBRUSH brush = (*bi).second;
- DeleteObject(brush);
+ for (auto &item : _brushes) {
+ DeleteObject(item.second.first);
+ DeleteObject(item.second.second);
}
+ _brushes.clear();
+ _text_colors.clear();
if (_graph_window) {
DestroyWindow(_graph_window);
@@ -81,6 +91,11 @@ WinStatsGraph::
DestroyWindow(_window);
_window = 0;
}
+
+ if (_tooltip_window) {
+ DestroyWindow(_tooltip_window);
+ _tooltip_window = 0;
+ }
}
/**
@@ -97,13 +112,6 @@ void WinStatsGraph::
new_data(int thread_index, int frame_number) {
}
-/**
- * Called when it is necessary to redraw the entire graph.
- */
-void WinStatsGraph::
-force_redraw() {
-}
-
/**
* Called when the user has resized the window, forcing a resize of the graph.
*/
@@ -150,7 +158,65 @@ user_guide_bars_changed() {
* Called when the user single-clicks on a label.
*/
void WinStatsGraph::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
+}
+
+/**
+ * Called when a pop-up menu should be shown for the label.
+ */
+void WinStatsGraph::
+on_popup_label(int collector_index) {
+}
+
+/**
+ * Called when the user hovers the mouse over a label.
+ */
+void WinStatsGraph::
+on_enter_label(int collector_index) {
+ if (collector_index != _highlighted_index) {
+ _highlighted_index = collector_index;
+ clear_graph_tooltip();
+ force_redraw();
+ }
+}
+
+/**
+ * Called when the user's mouse cursor leaves a label.
+ */
+void WinStatsGraph::
+on_leave_label(int collector_index) {
+ if (collector_index == _highlighted_index && collector_index != -1) {
+ _highlighted_index = -1;
+ clear_graph_tooltip();
+ force_redraw();
+ }
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsGraph::
+get_label_tooltip(int collector_index) const {
+ return std::string();
+}
+
+/**
+ * Hides the graph tooltip.
+ */
+void WinStatsGraph::
+clear_graph_tooltip() {
+ if (_tooltip_window != 0) {
+ SendMessage(_tooltip_window, TTM_POP, 0, 0);
+ }
+}
+
+/**
+ * Returns the window handle of the surrounding window.
+ */
+HWND WinStatsGraph::
+get_window() {
+ return _window;
}
/**
@@ -184,8 +250,8 @@ move_label_stack() {
RECT rect;
GetClientRect(_window, &rect);
- rect.left += 8;
- rect.right = _left_margin - 8;
+ rect.left += _pixel_scale * 2;
+ rect.right = _left_margin - _pixel_scale * 2;
rect.bottom -= _bottom_margin;
_label_stack.set_pos(rect.left, rect.top,
@@ -193,26 +259,76 @@ move_label_stack() {
}
}
+/**
+ * Turns on the animation timer, if it hasn't already been turned on.
+ */
+void WinStatsGraph::
+start_animation() {
+ if (!_timer_running) {
+ TrueClock *clock = TrueClock::get_global_ptr();
+ _time = clock->get_short_time();
+ SetTimer(_window, 0x100, 16, nullptr);
+ _timer_running = true;
+ }
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool WinStatsGraph::
+animate(double time, double dt) {
+ return false;
+}
+
/**
* Returns a brush suitable for drawing in the indicated collector's color.
*/
HBRUSH WinStatsGraph::
-get_collector_brush(int collector_index) {
+get_collector_brush(int collector_index, bool highlight) {
Brushes::iterator bi;
bi = _brushes.find(collector_index);
if (bi != _brushes.end()) {
- return (*bi).second;
+ return highlight ? (*bi).second.second : (*bi).second.first;
}
// Ask the monitor what color this guy should be.
LRGBColor rgb = _monitor->get_collector_color(collector_index);
- int r = (int)(rgb[0] * 255.0f);
- int g = (int)(rgb[1] * 255.0f);
- int b = (int)(rgb[2] * 255.0f);
+ int r = (int)encode_sRGB_uchar((float)rgb[0]);
+ int g = (int)encode_sRGB_uchar((float)rgb[1]);
+ int b = (int)encode_sRGB_uchar((float)rgb[2]);
HBRUSH brush = CreateSolidBrush(RGB(r, g, b));
- _brushes[collector_index] = brush;
- return brush;
+ int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f);
+ int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f);
+ int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f);
+ HBRUSH hbrush = CreateSolidBrush(RGB(hr, hg, hb));
+
+ _brushes[collector_index] = std::make_pair(brush, hbrush);
+ return highlight ? hbrush : brush;
+}
+
+/**
+ * Returns a text color suitable for the given collector.
+ */
+COLORREF WinStatsGraph::
+get_collector_text_color(int collector_index, bool highlight) {
+ TextColors::iterator tci;
+ tci = _text_colors.find(collector_index);
+ if (tci != _text_colors.end()) {
+ return highlight ? (*tci).second.second : (*tci).second.first;
+ }
+
+ LRGBColor rgb = _monitor->get_collector_color(collector_index);
+ double bright =
+ rgb[0] * 0.2126 +
+ rgb[1] * 0.7152 +
+ rgb[2] * 0.0722;
+ COLORREF color = bright >= 0.5 ? RGB(0, 0, 0) : RGB(255, 255, 255);
+ COLORREF hcolor = bright * 0.75 >= 0.5 ? RGB(0, 0, 0) : RGB(255, 255, 255);
+
+ _text_colors[collector_index] = std::make_pair(color, hcolor);
+ return highlight ? hcolor : color;
}
/**
@@ -226,6 +342,20 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
close();
break;
+ case WM_GETMINMAXINFO:
+ {
+ WINDOWINFO winfo;
+ GetWindowInfo(hwnd, &winfo);
+ MINMAXINFO &minmax = *(MINMAXINFO *)lparam;
+ minmax.ptMinTrackSize.x = (winfo.rcClient.left - winfo.rcWindow.left)
+ + (winfo.rcWindow.right - winfo.rcClient.right)
+ + (_right_margin + _left_margin);
+ minmax.ptMinTrackSize.y = (winfo.rcClient.top - winfo.rcWindow.top)
+ + (winfo.rcWindow.bottom - winfo.rcClient.bottom)
+ + (_bottom_margin + _top_margin);
+ return 0;
+ }
+
case WM_SIZE:
move_label_stack();
InvalidateRect(hwnd, nullptr, TRUE);
@@ -284,8 +414,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
case WM_MOUSEMOVE:
if (_drag_mode == DM_left_margin) {
int16_t x = LOWORD(lparam);
- _left_margin += (x - _drag_start_x);
- _drag_start_x = x;
+ int new_left_margin = _left_margin + (x - _drag_start_x);
+ _left_margin = std::max(new_left_margin, _pixel_scale * 2);
+ _drag_start_x = x - (new_left_margin - _left_margin);
InvalidateRect(hwnd, nullptr, TRUE);
move_label_stack();
return 0;
@@ -319,8 +450,6 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
rect.bottom -= _bottom_margin;
if (rect.right > rect.left && rect.bottom > rect.top) {
- DrawEdge(hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
-
int graph_xsize = rect.right - rect.left;
int graph_ysize = rect.bottom - rect.top;
if (_bitmap_dc == 0 ||
@@ -339,6 +468,48 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return 0;
}
+ case WM_DRAWITEM:
+ if (wparam == IDC_GRAPH) {
+ const DRAWITEMSTRUCT &dis = *(DRAWITEMSTRUCT *)lparam;
+
+ // Repaint the graph by copying the backing pixmap in.
+ BitBlt(dis.hDC, 0, 0,
+ _bitmap_xsize, _bitmap_ysize,
+ _bitmap_dc, 0, 0,
+ SRCCOPY);
+
+ additional_graph_window_paint(dis.hDC);
+ return 0;
+ }
+ break;
+
+ case WM_TIMER:
+ {
+ TrueClock *clock = TrueClock::get_global_ptr();
+ double new_time = clock->get_short_time();
+ if (!animate(new_time, new_time - _time)) {
+ KillTimer(hwnd, 0x100);
+ _timer_running = false;
+ }
+ _time = new_time;
+ }
+ return 0;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lparam)->code) {
+ case TTN_GETDISPINFO:
+ {
+ NMTTDISPINFO &info = *(NMTTDISPINFO *)lparam;
+ POINT point;
+ if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) {
+ _tooltip_text = get_graph_tooltip(point.x, point.y);
+ info.lpszText = (char *)_tooltip_text.c_str();
+ }
+ }
+ return 0;
+ }
+ break;
+
default:
break;
}
@@ -357,6 +528,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
force_redraw();
break;
+ case WM_NCHITTEST:
+ // Necessary for mouse events to work; default returns HTTRANSPARENT
+ return HTCLIENT;
+
case WM_LBUTTONDOWN:
// Vector any uncaught WM_LBUTTONDOWN into the main window, so we can drag
// margins, etc.
@@ -372,28 +547,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
ReleaseCapture();
break;
- case WM_PAINT:
- {
- // Repaint the graph by copying the backing pixmap in.
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
-
- BitBlt(hdc, 0, 0,
- _bitmap_xsize, _bitmap_ysize,
- _bitmap_dc, 0, 0,
- SRCCOPY);
-
- additional_graph_window_paint(hdc);
-
- EndPaint(hwnd, &ps);
- return 0;
- }
-
default:
break;
}
- return DefWindowProc(hwnd, msg, wparam, lparam);
+ return DefSubclassProc(hwnd, msg, wparam, lparam);
}
/**
@@ -413,6 +571,15 @@ void WinStatsGraph::
additional_graph_window_paint(HDC hdc) {
}
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsGraph::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return std::string();
+}
+
/**
* Based on the mouse position within the window's client area, look for
* draggable things the mouse might be hovering over and return the
@@ -506,61 +673,48 @@ create_graph_window() {
}
HINSTANCE application = GetModuleHandle(nullptr);
- register_graph_window_class(application);
- std::string window_title = "graph";
- DWORD window_style = WS_CHILD | WS_CLIPSIBLINGS;
+ DWORD window_style = WS_CHILD | WS_CLIPSIBLINGS |
+ SS_SUNKEN | SS_OWNERDRAW;
_graph_window =
- CreateWindow(_graph_window_class_name, window_title.c_str(), window_style,
- 0, 0, 0, 0,
- _window, nullptr, application, 0);
+ CreateWindow(WC_STATIC, "", window_style, 0, 0, 0, 0,
+ _window, (HMENU)IDC_GRAPH, application, 0);
if (!_graph_window) {
nout << "Could not create graph window!\n";
exit(1);
}
- SetWindowLongPtr(_graph_window, 0, (LONG_PTR)this);
-}
+ EnableWindow(_graph_window, TRUE);
-/**
- * Registers the window class for the stripChart window, if it has not already
- * been registered.
- */
-void WinStatsGraph::
-register_graph_window_class(HINSTANCE application) {
- if (_graph_window_class_registered) {
- return;
+ SetWindowSubclass(_graph_window, &static_graph_subclass_proc, 1234, (DWORD_PTR)this);
+
+ // Create the tooltip window. This will cause a TTN_GETDISPINFO message to
+ // be sent to the window to acquire the tooltip text.
+ _tooltip_window = CreateWindow(TOOLTIPS_CLASS, nullptr,
+ WS_POPUP,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ _window, nullptr,
+ application, nullptr);
+
+ if (_tooltip_window != 0) {
+ TOOLINFO info = { 0 };
+ info.cbSize = sizeof(info);
+ info.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ info.hwnd = _window;
+ info.uId = (UINT_PTR)_graph_window;
+ info.lpszText = LPSTR_TEXTCALLBACK;
+ SendMessage(_tooltip_window, TTM_ADDTOOL, 0, (LPARAM)&info);
}
-
- WNDCLASS wc;
-
- ZeroMemory(&wc, sizeof(WNDCLASS));
- wc.style = CS_DBLCLKS;
- wc.lpfnWndProc = (WNDPROC)static_graph_window_proc;
- wc.hInstance = application;
- wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.hbrBackground = nullptr;
- wc.lpszMenuName = nullptr;
- wc.lpszClassName = _graph_window_class_name;
-
- // Reserve space to associate the this pointer with the window.
- wc.cbWndExtra = sizeof(WinStatsGraph *);
-
- if (!RegisterClass(&wc)) {
- nout << "Could not register graph window class!\n";
- exit(1);
- }
-
- _graph_window_class_registered = true;
}
/**
*
*/
-LONG WINAPI WinStatsGraph::
-static_graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
- WinStatsGraph *self = (WinStatsGraph *)GetWindowLongPtr(hwnd, 0);
+LRESULT WINAPI WinStatsGraph::
+static_graph_subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR subclass, DWORD_PTR ref_data) {
+ WinStatsGraph *self = (WinStatsGraph *)ref_data;
if (self != nullptr && self->_graph_window == hwnd) {
return self->graph_window_proc(hwnd, msg, wparam, lparam);
} else {
diff --git a/pandatool/src/win-stats/winStatsGraph.h b/pandatool/src/win-stats/winStatsGraph.h
index 82fb04f71d..1cfc1bcee0 100644
--- a/pandatool/src/win-stats/winStatsGraph.h
+++ b/pandatool/src/win-stats/winStatsGraph.h
@@ -40,6 +40,7 @@ public:
DM_guide_bar,
DM_new_guide_bar,
DM_sizing,
+ DM_pan,
};
public:
@@ -48,7 +49,7 @@ public:
virtual void new_collector(int collector_index);
virtual void new_data(int thread_index, int frame_number);
- virtual void force_redraw();
+ virtual void force_redraw()=0;
virtual void changed_graph_size(int graph_xsize, int graph_ysize);
virtual void set_time_units(int unit_mask);
@@ -56,7 +57,15 @@ public:
void set_pause(bool pause);
void user_guide_bars_changed();
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual void on_enter_label(int collector_index);
+ virtual void on_leave_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
+
+ void clear_graph_tooltip();
+
+ HWND get_window();
protected:
void close();
@@ -64,13 +73,18 @@ protected:
void setup_label_stack();
void move_label_stack();
- HBRUSH get_collector_brush(int collector_index);
+ void start_animation();
+ virtual bool animate(double time, double dt);
+
+ HBRUSH get_collector_brush(int collector_index, bool highlight = false);
+ COLORREF get_collector_text_color(int collector_index, bool highlight = false);
LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual void additional_window_paint(HDC hdc);
virtual void additional_graph_window_paint(HDC hdc);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int mouse_x, int mouse_y,
int width, int height);
virtual void set_drag_mode(DragMode drag_mode);
@@ -80,13 +94,18 @@ protected:
protected:
// Table of brushes for our various collectors.
- typedef pmap Brushes;
+ typedef pmap > Brushes;
Brushes _brushes;
+ typedef pmap > TextColors;
+ TextColors _text_colors;
+
WinStatsMonitor *_monitor;
HWND _window;
HWND _graph_window;
+ HWND _tooltip_window;
WinStatsLabelStack _label_stack;
+ std::string _tooltip_text;
HCURSOR _sizewe_cursor;
HCURSOR _hand_cursor;
@@ -98,13 +117,16 @@ protected:
int _bitmap_xsize, _bitmap_ysize;
int _left_margin, _right_margin;
int _top_margin, _bottom_margin;
+ int _pixel_scale;
COLORREF _dark_color;
COLORREF _light_color;
COLORREF _user_guide_bar_color;
+ COLORREF _frame_guide_bar_color;
HPEN _dark_pen;
HPEN _light_pen;
HPEN _user_guide_bar_pen;
+ HPEN _frame_guide_bar_pen;
DragMode _drag_mode;
DragMode _potential_drag_mode;
@@ -112,18 +134,19 @@ protected:
double _drag_scale_start;
int _drag_guide_bar;
+ int _highlighted_index = -1;
+
bool _pause;
+ bool _timer_running = false;
+ double _time;
+
private:
void setup_bitmap(int xsize, int ysize);
void release_bitmap();
void create_graph_window();
- static void register_graph_window_class(HINSTANCE application);
- static LONG WINAPI static_graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
-
- static bool _graph_window_class_registered;
- static const char * const _graph_window_class_name;
+ static LRESULT WINAPI static_graph_subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR subclass, DWORD_PTR ref_data);
protected:
static DWORD graph_window_style;
diff --git a/pandatool/src/win-stats/winStatsLabel.I b/pandatool/src/win-stats/winStatsLabel.I
new file mode 100644
index 0000000000..a7cabe90b9
--- /dev/null
+++ b/pandatool/src/win-stats/winStatsLabel.I
@@ -0,0 +1,68 @@
+/**
+ * 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 winStatsLabel.I
+ * @author rdb
+ * @date 2022-01-29
+ */
+
+/**
+ * Returns the x position of the label on its parent.
+ */
+INLINE int WinStatsLabel::
+get_x() const {
+ return _x;
+}
+
+/**
+ * Returns the y position of the label on its parent.
+ */
+INLINE int WinStatsLabel::
+get_y() const {
+ return _y;
+}
+
+/**
+ * Returns the width of the label as we requested it.
+ */
+INLINE int WinStatsLabel::
+get_width() const {
+ return _width;
+}
+
+/**
+ * Returns the height of the label as we requested it.
+ */
+INLINE int WinStatsLabel::
+get_height() const {
+ return _height;
+}
+
+/**
+ * Returns the width the label would really prefer to be.
+ */
+INLINE int WinStatsLabel::
+get_ideal_width() const {
+ return _ideal_width;
+}
+
+/**
+ * Returns the collector this label represents.
+ */
+INLINE int WinStatsLabel::
+get_collector_index() const {
+ return _collector_index;
+}
+
+/**
+ * Returns true if the visual highlight for this label is enabled.
+ */
+INLINE bool WinStatsLabel::
+get_highlight() const {
+ return _highlight;
+}
diff --git a/pandatool/src/win-stats/winStatsLabel.cxx b/pandatool/src/win-stats/winStatsLabel.cxx
index 5cd744c501..595d581a80 100644
--- a/pandatool/src/win-stats/winStatsLabel.cxx
+++ b/pandatool/src/win-stats/winStatsLabel.cxx
@@ -14,6 +14,9 @@
#include "winStatsLabel.h"
#include "winStatsMonitor.h"
#include "winStatsGraph.h"
+#include "convert_srgb.h"
+
+#include
int WinStatsLabel::_left_margin = 2;
int WinStatsLabel::_right_margin = 2;
@@ -28,38 +31,45 @@ const char * const WinStatsLabel::_window_class_name = "label";
*/
WinStatsLabel::
WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph,
- int thread_index, int collector_index, bool use_fullname) :
+ int thread_index, int collector_index, bool use_fullname,
+ bool align_right) :
_monitor(monitor),
_graph(graph),
_thread_index(thread_index),
- _collector_index(collector_index)
+ _collector_index(collector_index),
+ _align_right(align_right),
+ _window(0),
+ _tooltip_window(0)
{
- _window = 0;
- if (use_fullname) {
- _text = _monitor->get_client_data()->get_collector_fullname(_collector_index);
- } else {
- _text = _monitor->get_client_data()->get_collector_name(_collector_index);
- }
+ update_text(use_fullname);
LRGBColor rgb = _monitor->get_collector_color(_collector_index);
- int r = (int)(rgb[0] * 255.0f);
- int g = (int)(rgb[1] * 255.0f);
- int b = (int)(rgb[2] * 255.0f);
- _bg_color = RGB(r, g, b);
+ int r = (int)encode_sRGB_uchar((float)rgb[0]);
+ int g = (int)encode_sRGB_uchar((float)rgb[1]);
+ int b = (int)encode_sRGB_uchar((float)rgb[2]);
_bg_brush = CreateSolidBrush(RGB(r, g, b));
+ // Calculate the color when it is highlighted.
+ int hr = (int)encode_sRGB_uchar((float)rgb[0] * 0.75f);
+ int hg = (int)encode_sRGB_uchar((float)rgb[1] * 0.75f);
+ int hb = (int)encode_sRGB_uchar((float)rgb[2] * 0.75f);
+ _highlight_bg_brush = CreateSolidBrush(RGB(hr, hg, hb));
+
// Should our foreground be black or white?
double bright =
- rgb[0] * 0.299 +
- rgb[1] * 0.587 +
- rgb[2] * 0.114;
+ rgb[0] * 0.2126 +
+ rgb[1] * 0.7152 +
+ rgb[2] * 0.0722;
if (bright >= 0.5) {
_fg_color = RGB(0, 0, 0);
- _highlight_brush = (HBRUSH)GetStockObject(BLACK_BRUSH);
} else {
_fg_color = RGB(255, 255, 255);
- _highlight_brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
+ }
+ if (bright * 0.75 >= 0.5) {
+ _highlight_fg_color = RGB(0, 0, 0);
+ } else {
+ _highlight_fg_color = RGB(255, 255, 255);
}
_x = 0;
@@ -77,6 +87,10 @@ WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph,
WinStatsLabel::
~WinStatsLabel() {
if (_window) {
+ if (_tooltip_window) {
+ DestroyWindow(_tooltip_window);
+ _tooltip_window = 0;
+ }
DestroyWindow(_window);
_window = 0;
}
@@ -96,7 +110,7 @@ setup(HWND parent_window) {
create_window(parent_window);
HDC hdc = GetDC(_window);
- HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ HFONT hfnt = _monitor->get_font();
SelectObject(hdc, hfnt);
SIZE size;
@@ -113,59 +127,13 @@ setup(HWND parent_window) {
*/
void WinStatsLabel::
set_pos(int x, int y, int width) {
- _x = x;
- _y = y;
- _width = width;
- SetWindowPos(_window, 0, x, y - _height, _width, _height,
- SWP_NOZORDER | SWP_SHOWWINDOW);
-}
-
-/**
- * Returns the x position of the label on its parent.
- */
-int WinStatsLabel::
-get_x() const {
- return _x;
-}
-
-/**
- * Returns the y position of the label on its parent.
- */
-int WinStatsLabel::
-get_y() const {
- return _y;
-}
-
-/**
- * Returns the width of the label as we requested it.
- */
-int WinStatsLabel::
-get_width() const {
- return _width;
-}
-
-/**
- * Returns the height of the label as we requested it.
- */
-int WinStatsLabel::
-get_height() const {
- return _height;
-}
-
-/**
- * Returns the width the label would really prefer to be.
- */
-int WinStatsLabel::
-get_ideal_width() const {
- return _ideal_width;
-}
-
-/**
- * Returns the collector this label represents.
- */
-int WinStatsLabel::
-get_collector_index() const {
- return _collector_index;
+ if (x != _x || y != _y || width != _width) {
+ _x = x;
+ _y = y;
+ _width = width;
+ SetWindowPos(_window, 0, x, y - _height, _width, _height,
+ SWP_NOZORDER | SWP_SHOWWINDOW);
+ }
}
/**
@@ -180,11 +148,29 @@ set_highlight(bool highlight) {
}
/**
- * Returns true if the visual highlight for this label is enabled.
+ * Set to true if the full name of the collector should be shown.
*/
-bool WinStatsLabel::
-get_highlight() const {
- return _highlight;
+void WinStatsLabel::
+update_text(bool use_fullname) {
+ const PStatClientData *client_data = _monitor->get_client_data();
+ _tooltip_text = client_data->get_collector_fullname(_collector_index);
+ if (use_fullname) {
+ _text = _tooltip_text;
+ } else {
+ _text = client_data->get_collector_name(_collector_index);
+ }
+
+ // Recalculate the dimensions.
+ if (_window) {
+ HDC hdc = GetDC(_window);
+ HFONT hfnt = _monitor->get_font();
+ SelectObject(hdc, hfnt);
+
+ SIZE size;
+ GetTextExtentPoint32(hdc, _text.data(), _text.length(), &size);
+ _height = size.cy + _top_margin + _bottom_margin;
+ _ideal_width = size.cx + _left_margin + _right_margin;
+ }
}
/**
@@ -220,6 +206,25 @@ create_window(HWND parent_window) {
}
SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+
+ // Create the tooltip window. This will cause a TTN_GETDISPINFO message to
+ // be sent to the window to acquire the tooltip text.
+ _tooltip_window = CreateWindow(TOOLTIPS_CLASS, nullptr,
+ WS_POPUP,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ _window, nullptr,
+ application, nullptr);
+
+ if (_tooltip_window != 0) {
+ TOOLINFO info = { 0 };
+ info.cbSize = sizeof(info);
+ info.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ info.hwnd = _window;
+ info.uId = (UINT_PTR)_window;
+ info.lpszText = LPSTR_TEXTCALLBACK;
+ SendMessage(_tooltip_window, TTM_ADDTOOL, 0, (LPARAM)&info);
+ }
}
/**
@@ -274,13 +279,20 @@ LONG WinStatsLabel::
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_LBUTTONDBLCLK:
- _graph->clicked_label(_collector_index);
+ _graph->on_click_label(_collector_index);
+ return 0;
+
+ case WM_CONTEXTMENU:
+ _graph->on_popup_label(_collector_index);
return 0;
case WM_MOUSEMOVE:
{
// When the mouse enters the label area, highlight the label.
- set_mouse_within(true);
+ if (!_mouse_within) {
+ set_mouse_within(true);
+ _graph->on_enter_label(_collector_index);
+ }
// Now we want to get a WM_MOUSELEAVE when the mouse leaves the label.
TRACKMOUSEEVENT tme = {
@@ -294,7 +306,10 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
break;
case WM_MOUSELEAVE:
- set_mouse_within(false);
+ if (_mouse_within) {
+ set_mouse_within(false);
+ _graph->on_leave_label(_collector_index);
+ }
break;
case WM_PAINT:
@@ -303,26 +318,43 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect = { 0, 0, _width, _height };
- FillRect(hdc, &rect, _bg_brush);
+ FillRect(hdc, &rect, (_highlight || _mouse_within) ? _highlight_bg_brush : _bg_brush);
- if (_highlight || _mouse_within) {
- FrameRect(hdc, &rect, _highlight_brush);
+ HFONT hfnt = _monitor->get_font();
+ SelectObject(hdc, hfnt);
+ SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP);
+
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, (_highlight || _mouse_within) ? _highlight_fg_color : _fg_color);
+
+ if (_width > 8) {
+ UINT format = DT_END_ELLIPSIS | DT_SINGLELINE;
+ if (_align_right) {
+ format |= DT_RIGHT;
+ } else {
+ format |= DT_LEFT;
+ }
+
+ RECT margins = { _left_margin, _top_margin, _width - _right_margin, _height - _bottom_margin };
+ DrawText(hdc, _text.data(), _text.length(), &margins, format);
}
- HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT);
- SelectObject(hdc, hfnt);
- SetTextAlign(hdc, TA_RIGHT | TA_TOP);
-
- SetBkColor(hdc, _bg_color);
- SetBkMode(hdc, OPAQUE);
- SetTextColor(hdc, _fg_color);
-
- TextOut(hdc, _width - _right_margin, _top_margin,
- _text.data(), _text.length());
EndPaint(hwnd, &ps);
return 0;
}
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lparam)->code) {
+ case TTN_GETDISPINFO:
+ {
+ NMTTDISPINFO &info = *(NMTTDISPINFO *)lparam;
+ _tooltip_text = _graph->get_label_tooltip(_collector_index);
+ info.lpszText = (char *)_tooltip_text.c_str();
+ }
+ return 0;
+ }
+ break;
+
default:
break;
}
diff --git a/pandatool/src/win-stats/winStatsLabel.h b/pandatool/src/win-stats/winStatsLabel.h
index 5642cdb8d0..a970d3e13e 100644
--- a/pandatool/src/win-stats/winStatsLabel.h
+++ b/pandatool/src/win-stats/winStatsLabel.h
@@ -32,22 +32,25 @@ class WinStatsGraph;
class WinStatsLabel {
public:
WinStatsLabel(WinStatsMonitor *monitor, WinStatsGraph *graph,
- int thread_index, int collector_index, bool use_fullname);
+ int thread_index, int collector_index, bool use_fullname,
+ bool align_right = true);
~WinStatsLabel();
void setup(HWND parent_window);
void set_pos(int x, int y, int width);
- int get_x() const;
- int get_y() const;
- int get_width() const;
- int get_height() const;
- int get_ideal_width() const;
+ INLINE int get_x() const;
+ INLINE int get_y() const;
+ INLINE int get_width() const;
+ INLINE int get_height() const;
+ INLINE int get_ideal_width() const;
- int get_collector_index() const;
+ INLINE int get_collector_index() const;
void set_highlight(bool highlight);
- bool get_highlight() const;
+ INLINE bool get_highlight() const;
+
+ void update_text(bool use_fullname);
private:
void set_mouse_within(bool mouse_within);
@@ -63,11 +66,13 @@ private:
int _thread_index;
int _collector_index;
std::string _text;
+ std::string _tooltip_text;
HWND _window;
- COLORREF _bg_color;
+ HWND _tooltip_window;
COLORREF _fg_color;
+ COLORREF _highlight_fg_color;
HBRUSH _bg_brush;
- HBRUSH _highlight_brush;
+ HBRUSH _highlight_bg_brush;
int _x;
int _y;
@@ -76,6 +81,7 @@ private:
int _ideal_width;
bool _highlight;
bool _mouse_within;
+ bool _align_right;
static int _left_margin, _right_margin;
static int _top_margin, _bottom_margin;
@@ -84,4 +90,6 @@ private:
static const char * const _window_class_name;
};
+#include "winStatsLabel.I"
+
#endif
diff --git a/pandatool/src/win-stats/winStatsLabelStack.cxx b/pandatool/src/win-stats/winStatsLabelStack.cxx
index af3bcccd00..e6633f025b 100644
--- a/pandatool/src/win-stats/winStatsLabelStack.cxx
+++ b/pandatool/src/win-stats/winStatsLabelStack.cxx
@@ -200,6 +200,90 @@ add_label(WinStatsMonitor *monitor, WinStatsGraph *graph,
return label_index;
}
+/**
+ * Replaces the labels with the given collector indices.
+ */
+void WinStatsLabelStack::
+replace_labels(WinStatsMonitor *monitor, WinStatsGraph *graph,
+ int thread_index, const vector_int &collector_indices,
+ bool use_fullname) {
+
+ _ideal_width = 0;
+
+ // First skip the part of the stack that hasn't changed.
+ size_t li = 0;
+ size_t ci = 0;
+ while (ci < collector_indices.size() && li < _labels.size()) {
+ WinStatsLabel *label = _labels[li];
+ if (collector_indices[ci] != label->get_collector_index()) {
+ // Mismatch.
+ break;
+ }
+ _ideal_width = std::max(_ideal_width, label->get_ideal_width());
+ ++ci;
+ ++li;
+ }
+
+ if (ci == collector_indices.size()) {
+ if (ci == _labels.size()) {
+ // Perfect, nothing changed.
+ return;
+ }
+
+ // Simple case, just delete the rest.
+ while (li < _labels.size()) {
+ delete _labels[li++];
+ }
+ _labels.resize(ci);
+ return;
+ }
+
+ int yp = _height;
+ if (li > 0) {
+ WinStatsLabel *label = _labels[li - 1];
+ yp = label->get_y() - label->get_height();
+ }
+
+ // Make a map of remaining labels.
+ std::map label_map;
+ for (size_t li2 = li; li2 < _labels.size(); ++li2) {
+ WinStatsLabel *label = _labels[li2];
+ label_map[label->get_collector_index()] = label;
+ }
+
+ _labels.resize(collector_indices.size());
+
+ while (ci < collector_indices.size()) {
+ int collector_index = collector_indices[ci++];
+
+ WinStatsLabel *label;
+ auto it = label_map.find(collector_index);
+ if (it == label_map.end()) {
+ // It's not in the map. Create a new label.
+ label = new WinStatsLabel(monitor, graph, thread_index, collector_index, use_fullname);
+ if (_window) {
+ label->setup(_window);
+ }
+ } else {
+ // Erase it from the map, so that it's not deleted.
+ label = it->second;
+ label_map.erase(it);
+ }
+ if (_window) {
+ label->set_pos(0, yp, _width);
+ }
+ _ideal_width = std::max(_ideal_width, label->get_ideal_width());
+ yp -= label->get_height();
+
+ _labels[li++] = label;
+ }
+
+ // Anything that's remaining in the label map should be deleted.
+ for (auto it = label_map.begin(); it != label_map.end(); ++it) {
+ delete it->second;
+ }
+}
+
/**
* Returns the number of labels in the stack.
*/
@@ -225,7 +309,6 @@ highlight_label(int collector_index) {
}
}
-
/**
* Creates the window for this stack.
*/
@@ -306,7 +389,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect = { 0, 0, _width, _height };
- FillRect(hdc, &rect, (HBRUSH)COLOR_BACKGROUND);
+ FillRect(hdc, &rect, (HBRUSH)COLOR_WINDOW);
EndPaint(hwnd, &ps);
return 0;
}
diff --git a/pandatool/src/win-stats/winStatsLabelStack.h b/pandatool/src/win-stats/winStatsLabelStack.h
index e7fd1da41f..48b9424754 100644
--- a/pandatool/src/win-stats/winStatsLabelStack.h
+++ b/pandatool/src/win-stats/winStatsLabelStack.h
@@ -16,6 +16,7 @@
#include "pandatoolbase.h"
#include "pvector.h"
+#include "vector_int.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
@@ -51,6 +52,9 @@ public:
void clear_labels();
int add_label(WinStatsMonitor *monitor, WinStatsGraph *graph,
int thread_index, int collector_index, bool use_fullname);
+ void replace_labels(WinStatsMonitor *monitor, WinStatsGraph *graph,
+ int thread_index, const vector_int &collector_indices,
+ bool use_fullname);
int get_num_labels() const;
void highlight_label(int collector_index);
diff --git a/pandatool/src/win-stats/winStatsMonitor.I b/pandatool/src/win-stats/winStatsMonitor.I
index 405f0a39f5..6658a5ab70 100644
--- a/pandatool/src/win-stats/winStatsMonitor.I
+++ b/pandatool/src/win-stats/winStatsMonitor.I
@@ -14,10 +14,11 @@
/**
*
*/
-WinStatsMonitor::MenuDef::
-MenuDef(int thread_index, int collector_index, bool show_level) :
+INLINE WinStatsMonitor::MenuDef::
+MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) :
_thread_index(thread_index),
_collector_index(collector_index),
+ _chart_type(chart_type),
_show_level(show_level)
{
}
@@ -25,7 +26,7 @@ MenuDef(int thread_index, int collector_index, bool show_level) :
/**
*
*/
-bool WinStatsMonitor::MenuDef::
+INLINE bool WinStatsMonitor::MenuDef::
operator < (const MenuDef &other) const {
if (_thread_index != other._thread_index) {
return _thread_index < other._thread_index;
@@ -33,5 +34,8 @@ operator < (const MenuDef &other) const {
if (_collector_index != other._collector_index) {
return _collector_index < other._collector_index;
}
+ if (_chart_type != other._chart_type) {
+ return _chart_type < other._chart_type;
+ }
return (int)_show_level < (int)other._show_level;
}
diff --git a/pandatool/src/win-stats/winStatsMonitor.cxx b/pandatool/src/win-stats/winStatsMonitor.cxx
index 50c66362d1..b8fade17f0 100644
--- a/pandatool/src/win-stats/winStatsMonitor.cxx
+++ b/pandatool/src/win-stats/winStatsMonitor.cxx
@@ -15,11 +15,17 @@
#include "winStatsServer.h"
#include "winStatsStripChart.h"
#include "winStatsPianoRoll.h"
+#include "winStatsFlameGraph.h"
+#include "winStatsTimeline.h"
#include "winStatsChartMenu.h"
#include "winStatsMenuId.h"
+#include "pStatFrameData.h"
#include "pStatGraph.h"
#include "pStatCollectorDef.h"
-#include "indent.h"
+
+#include
+
+#include
bool WinStatsMonitor::_window_class_registered = false;
const char * const WinStatsMonitor::_window_class_name = "monitor";
@@ -37,6 +43,25 @@ WinStatsMonitor(WinStatsServer *server) : PStatMonitor(server) {
_time_units = 0;
_scroll_speed = 0.0;
_pause = false;
+
+ // Create the fonts used for rendering the UI.
+ NONCLIENTMETRICS metrics = {0};
+ metrics.cbSize = sizeof(NONCLIENTMETRICS);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0)) {
+ _font = CreateFontIndirect(&metrics.lfMenuFont);
+ } else {
+ _font = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ }
+
+ HDC dc = GetDC(nullptr);
+ _pixel_scale = 0;
+ if (dc) {
+ _pixel_scale = GetDeviceCaps(dc, LOGPIXELSX) / (96 / 4);
+ }
+ if (_pixel_scale <= 0) {
+ _pixel_scale = 4;
+ }
+ ReleaseDC(nullptr, dc);
}
/**
@@ -171,13 +196,14 @@ new_thread(int thread_index) {
*/
void WinStatsMonitor::
new_data(int thread_index, int frame_number) {
- Graphs::iterator gi;
- for (gi = _graphs.begin(); gi != _graphs.end(); ++gi) {
- WinStatsGraph *graph = (*gi);
+ for (WinStatsGraph *graph : _graphs) {
graph->new_data(thread_index, frame_number);
}
-}
+ if (thread_index == 0) {
+ update_status_bar();
+ }
+}
/**
* Called whenever the connection to the client has been lost. This is a
@@ -210,16 +236,21 @@ idle() {
const PStatThreadData *thread_data = get_client_data()->get_thread_data(0);
double frame_rate = thread_data->get_frame_rate();
if (frame_rate != 0.0f) {
+ // The leading tab centers the text in the status bar.
char buffer[128];
- sprintf(buffer, "%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate);
+ sprintf(buffer, "\t%0.1f ms / %0.1f Hz", 1000.0f / frame_rate, frame_rate);
MENUITEMINFO mii;
memset(&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STRING;
- mii.dwTypeData = buffer;
+ mii.dwTypeData = buffer + 1; // chop off leading tab
SetMenuItemInfo(_menu_bar, MI_frame_rate_label, FALSE, &mii);
DrawMenuBar(_window);
+
+ if (_status_bar) {
+ SendMessage(_status_bar, WM_SETTEXT, 0, (LPARAM)buffer);
+ }
}
}
@@ -252,6 +283,34 @@ get_window() const {
return _window;
}
+/**
+ * Returns the font that should be used for rendering text.
+ */
+HFONT WinStatsMonitor::
+get_font() const {
+ return _font;
+}
+
+/**
+ * Returns the system DPI scaling as a fraction where 4 = no scaling.
+ */
+int WinStatsMonitor::
+get_pixel_scale() const {
+ return _pixel_scale;
+}
+
+/**
+ * Returns an amount by which to offset the next window position.
+ */
+POINT WinStatsMonitor::
+get_new_window_pos() {
+ int offset = _graphs.size() * 10 * _pixel_scale;
+ POINT pt;
+ pt.x = offset + _client_origin.x;
+ pt.y = offset + _client_origin.y;
+ return pt;
+}
+
/**
* Opens a new strip chart showing the indicated data.
*/
@@ -279,13 +338,39 @@ open_piano_roll(int thread_index) {
graph->set_pause(_pause);
}
+/**
+ * Opens a new flame graph showing the indicated data.
+ */
+void WinStatsMonitor::
+open_flame_graph(int thread_index, int collector_index) {
+ WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index);
+ add_graph(graph);
+
+ graph->set_time_units(_time_units);
+ graph->set_scroll_speed(_scroll_speed);
+ graph->set_pause(_pause);
+}
+
+/**
+ * Opens a new timeline.
+ */
+void WinStatsMonitor::
+open_timeline() {
+ WinStatsTimeline *graph = new WinStatsTimeline(this);
+ add_graph(graph);
+
+ graph->set_time_units(_time_units);
+ graph->set_scroll_speed(_scroll_speed);
+ graph->set_pause(_pause);
+}
+
/**
* Returns the MenuDef properties associated with the indicated menu ID. This
* specifies what we expect to do when the given menu has been selected.
*/
const WinStatsMonitor::MenuDef &WinStatsMonitor::
lookup_menu(int menu_id) const {
- static MenuDef invalid(0, 0, false);
+ static MenuDef invalid(0, 0, CT_strip_chart, false);
int menu_index = menu_id - MI_new_chart;
nassertr(menu_index >= 0 && menu_index < (int)_menu_by_id.size(), invalid);
return _menu_by_id[menu_index];
@@ -467,10 +552,16 @@ create_window() {
SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+ create_status_bar(application);
+
// For some reason, SW_SHOWNORMAL doesn't always work, but SW_RESTORE seems
// to.
ShowWindow(_window, SW_RESTORE);
SetForegroundWindow(_window);
+
+ _client_origin.x = 0;
+ _client_origin.y = 0;
+ ClientToScreen(_window, &_client_origin);
}
/**
@@ -587,6 +678,173 @@ setup_frame_rate_label() {
InsertMenuItem(_menu_bar, GetMenuItemCount(_menu_bar), TRUE, &mii);
}
+/**
+ * Sets up a status bar at the bottom of the screen showing assorted level
+ * values.
+ */
+void WinStatsMonitor::
+create_status_bar(HINSTANCE application) {
+ _status_bar = CreateWindow(STATUSCLASSNAME, nullptr,
+ SBARS_SIZEGRIP | WS_CHILD | WS_VISIBLE,
+ 0, 0, 0, 0,
+ _window, (HMENU)0, application, nullptr);
+
+ update_status_bar();
+
+
+ ShowWindow(_status_bar, SW_SHOW);
+ UpdateWindow(_status_bar);
+
+ InvalidateRect(_status_bar, NULL, TRUE);
+}
+
+/**
+ * Updates the status bar.
+ */
+void WinStatsMonitor::
+update_status_bar() {
+ const PStatClientData *client_data = get_client_data();
+ if (client_data == nullptr) {
+ return;
+ }
+
+ const PStatThreadData *thread_data = get_client_data()->get_thread_data(0);
+ if (thread_data == nullptr || thread_data->is_empty()) {
+ return;
+ }
+ int frame_number = thread_data->get_latest_frame_number();
+ const PStatFrameData &frame_data = thread_data->get_latest_frame();
+
+ // Gather the top-level collector list.
+ pvector parts;
+ pvector collectors;
+ size_t total_chars = 0;
+
+ int num_toplevel_collectors = client_data->get_num_toplevel_collectors();
+ for (int tc = 0; tc < num_toplevel_collectors; tc++) {
+ int collector = client_data->get_toplevel_collector(tc);
+ if (client_data->has_collector(collector) &&
+ client_data->get_collector_has_level(collector, 0)) {
+ PStatView &view = get_level_view(collector, 0);
+ view.set_to_frame(frame_data);
+ double value = view.get_net_value();
+ if (value == 0.0) {
+ // Don't include it unless we've included it before.
+ if (std::find(_status_bar_collectors.begin(), _status_bar_collectors.end(), collector) == _status_bar_collectors.end()) {
+ continue;
+ }
+ }
+
+ // Add the value for other threads that have this collector.
+ for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
+ PStatView &view = get_level_view(collector, thread_index);
+ view.set_to_frame(frame_number);
+ value += view.get_net_value();
+ }
+
+ const PStatCollectorDef &def = client_data->get_collector_def(collector);
+ std::string text = "\t" + def._name;
+ text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
+ total_chars += text.size();
+ parts.push_back(text);
+ collectors.push_back(collector);
+ }
+ }
+
+ size_t cur_size = _status_bar_collectors.size();
+ _status_bar_collectors = std::move(collectors);
+
+ // Allocate an array for holding the right edge coordinates.
+ HLOCAL hloc = LocalAlloc(LHND, sizeof(int) * (parts.size() + 1));
+ PINT sizes = (PINT)LocalLock(hloc);
+
+ // Allocate the left-most slot for the framerate indicator.
+ double offset = 28.0 * _pixel_scale;
+ sizes[0] = (int)(offset + 0.5);
+
+ if (!parts.empty()) {
+ // Distribute the sizes roughly based on the number of characters. It's not
+ // as good as measuring the text, but it's good enough.
+ RECT rect;
+ double width_per_char = 0;
+ GetClientRect(_status_bar, &rect);
+ // Leave room for the grip.
+ rect.right -= _pixel_scale * 4;
+ width_per_char = (rect.right - rect.left - offset) / (double)total_chars;
+
+ // If we get below a minimum width, start chopping parts off.
+ while (!parts.empty() && width_per_char < _pixel_scale * 1.5) {
+ total_chars -= parts.back().size();
+ parts.pop_back();
+ width_per_char = (rect.right - rect.left - offset) / (double)total_chars;
+ }
+
+ if (!parts.empty()) {
+ for (size_t i = 0; i < parts.size(); ++i) {
+ offset += (parts[i].size()) * width_per_char;
+ sizes[i + 1] = (int)(offset + 0.5);
+ }
+ } else {
+ // No room for any collectors; the framerate can take up the whole width.
+ sizes[0] = rect.right - rect.left;
+ }
+ }
+
+ SendMessage(_status_bar, SB_SETPARTS, (WPARAM)(parts.size() + 1), (LPARAM)sizes);
+
+ LocalUnlock(hloc);
+ LocalFree(hloc);
+
+ for (size_t i = 0; i < parts.size(); ++i) {
+ SendMessage(_status_bar, SB_SETTEXT, i + 1, (LPARAM)parts[i].c_str());
+ }
+}
+
+/**
+ * Called when someone right-clicks on a part of the status bar.
+ */
+void WinStatsMonitor::
+show_popup_menu(int collector) {
+ POINT point;
+ if (!GetCursorPos(&point)) {
+ return;
+ }
+
+ const PStatClientData *client_data = get_client_data();
+ if (client_data == nullptr) {
+ return;
+ }
+
+ PStatView &level_view = get_level_view(collector, 0);
+ const PStatViewLevel *view_level = level_view.get_top_level();
+ int num_children = view_level->get_num_children();
+ if (num_children == 0) {
+ return;
+ }
+
+ HMENU popup = CreatePopupMenu();
+
+ // Reverse the order since the menus are listed from the top down; we want
+ // to be visually consistent with the graphs, which list these labels from
+ // the bottom up.
+ for (int c = num_children - 1; c >= 0; c--) {
+ const PStatViewLevel *child_level = view_level->get_child(c);
+
+ int child_collector = child_level->get_collector();
+ MenuDef menu_def(0, child_collector, CT_strip_chart, true);
+ int menu_id = get_menu_id(menu_def);
+
+ double value = child_level->get_net_value();
+
+ const PStatCollectorDef &def = client_data->get_collector_def(child_collector);
+ std::string text = def._name;
+ text += ": " + PStatGraph::format_number(value, PStatGraph::GBU_named | PStatGraph::GBU_show_units, def._level_units);
+ AppendMenu(popup, MF_STRING, menu_id, text.c_str());
+ }
+
+ TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
+}
+
/**
* Registers the window class for the monitor window, if it has not already
* been registered.
@@ -604,7 +862,7 @@ register_window_class(HINSTANCE application) {
wc.lpfnWndProc = (WNDPROC)static_window_proc;
wc.hInstance = application;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszMenuName = nullptr;
wc.lpszClassName = _window_class_name;
@@ -642,6 +900,81 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
close();
break;
+ case WM_WINDOWPOSCHANGED:
+ if (!_graphs.empty()) {
+ RECT status_bar_rect;
+ GetWindowRect(_status_bar, &status_bar_rect);
+
+ RECT client_rect;
+ GetClientRect(_window, &client_rect);
+ MapWindowPoints(_window, nullptr, (POINT *)&client_rect, 2);
+
+ int delta_x = client_rect.left - _client_origin.x;
+ int delta_y = client_rect.top - _client_origin.y;
+ _client_origin.x = client_rect.left;
+ _client_origin.y = client_rect.top;
+
+ int iconic_offset = 0;
+
+ for (WinStatsGraph *graph : _graphs) {
+ RECT child_rect;
+ HWND window = graph->get_window();
+ if (GetWindowRect(window, &child_rect)) {
+ if (IsIconic(window)) {
+ // Keep it glued to the bottom-left corner of the parent window.
+ child_rect.left = client_rect.left + iconic_offset;
+ child_rect.top = client_rect.bottom - (child_rect.bottom - child_rect.top) - (status_bar_rect.bottom - status_bar_rect.top);
+ iconic_offset += (child_rect.right - child_rect.left);
+ } else {
+ child_rect.left += delta_x;
+ child_rect.top += delta_y;
+ }
+ SetWindowPos(window, 0, child_rect.left, child_rect.top, 0, 0,
+ SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOACTIVATE);
+ }
+ }
+ }
+ break;
+
+ case WM_SIZE:
+ if (_status_bar) {
+ SendMessage(_status_bar, WM_SIZE, 0, 0);
+ update_status_bar();
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lparam)->code == NM_DBLCLK) {
+ NMMOUSE &mouse = *(NMMOUSE *)lparam;
+ if (mouse.dwItemSpec == 0) {
+ open_strip_chart(0, 0, false);
+ }
+ else if (mouse.dwItemSpec >= 1 && mouse.dwItemSpec <= _status_bar_collectors.size()) {
+ int collector = _status_bar_collectors[mouse.dwItemSpec - 1];
+ open_strip_chart(0, collector, true);
+
+ // Also open a strip chart for other threads with data for this
+ // collector.
+ const PStatClientData *client_data = get_client_data();
+ for (int thread_index = 1; thread_index < client_data->get_num_threads(); ++thread_index) {
+ PStatView &view = get_level_view(collector, thread_index);
+ if (view.get_net_value() > 0.0) {
+ open_strip_chart(thread_index, collector, true);
+ }
+ }
+ }
+ return TRUE;
+ }
+ else if (((LPNMHDR)lparam)->code == NM_RCLICK) {
+ NMMOUSE &mouse = *(NMMOUSE *)lparam;
+ if (mouse.dwItemSpec >= 1 &&
+ mouse.dwItemSpec <= _status_bar_collectors.size()) {
+ int collector = _status_bar_collectors[mouse.dwItemSpec - 1];
+ show_popup_menu(collector);
+ }
+ }
+ break;
+
case WM_COMMAND:
if (HIWORD(wparam) <= 1) {
int menu_id = LOWORD(wparam);
@@ -701,11 +1034,23 @@ handle_menu_command(int menu_id) {
default:
if (menu_id >= MI_new_chart) {
const MenuDef &menu_def = lookup_menu(menu_id);
- if (menu_def._collector_index < 0) {
- open_piano_roll(menu_def._thread_index);
- } else {
+ switch (menu_def._chart_type) {
+ case CT_timeline:
+ open_timeline();
+ break;
+
+ case CT_strip_chart:
open_strip_chart(menu_def._thread_index, menu_def._collector_index,
menu_def._show_level);
+ break;
+
+ case CT_flame_graph:
+ open_flame_graph(menu_def._thread_index, menu_def._collector_index);
+ break;
+
+ case CT_piano_roll:
+ open_piano_roll(menu_def._thread_index);
+ break;
}
}
}
diff --git a/pandatool/src/win-stats/winStatsMonitor.h b/pandatool/src/win-stats/winStatsMonitor.h
index c70a71507e..9fa4ccf57f 100644
--- a/pandatool/src/win-stats/winStatsMonitor.h
+++ b/pandatool/src/win-stats/winStatsMonitor.h
@@ -37,13 +37,22 @@ class WinStatsChartMenu;
*/
class WinStatsMonitor : public PStatMonitor {
public:
+ enum ChartType {
+ CT_timeline,
+ CT_strip_chart,
+ CT_flame_graph,
+ CT_piano_roll,
+ };
+
class MenuDef {
public:
- INLINE MenuDef(int thread_index, int collector_index, bool show_level);
+ INLINE MenuDef(int thread_index, int collector_index,
+ ChartType chart_type, bool show_level = false);
INLINE bool operator < (const MenuDef &other) const;
int _thread_index;
int _collector_index;
+ ChartType _chart_type;
bool _show_level;
};
@@ -66,8 +75,14 @@ public:
virtual void user_guide_bars_changed();
HWND get_window() const;
+ HFONT get_font() const;
+ int get_pixel_scale() const;
+ POINT get_new_window_pos();
+
void open_strip_chart(int thread_index, int collector_index, bool show_level);
void open_piano_roll(int thread_index);
+ void open_flame_graph(int thread_index, int collector_index = -1);
+ void open_timeline();
const MenuDef &lookup_menu(int menu_id) const;
int get_menu_id(const MenuDef &menu_def);
@@ -84,6 +99,9 @@ private:
void setup_options_menu();
void setup_speed_menu();
void setup_frame_rate_label();
+ void create_status_bar(HINSTANCE application);
+ void update_status_bar();
+ void show_popup_menu(int collector);
static void register_window_class(HINSTANCE application);
static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
@@ -105,10 +123,16 @@ private:
HMENU _menu_bar;
HMENU _options_menu;
HMENU _speed_menu;
+ HWND _status_bar;
+ POINT _client_origin;
+ pvector _status_bar_collectors;
std::string _window_title;
int _time_units;
double _scroll_speed;
bool _pause;
+ int _pixel_scale;
+
+ HFONT _font;
static bool _window_class_registered;
static const char * const _window_class_name;
diff --git a/pandatool/src/win-stats/winStatsPianoRoll.cxx b/pandatool/src/win-stats/winStatsPianoRoll.cxx
index 859de11b68..fc70f6f203 100644
--- a/pandatool/src/win-stats/winStatsPianoRoll.cxx
+++ b/pandatool/src/win-stats/winStatsPianoRoll.cxx
@@ -15,8 +15,8 @@
#include "winStatsMonitor.h"
#include "numeric_types.h"
-static const int default_piano_roll_width = 400;
-static const int default_piano_roll_height = 200;
+static const int default_piano_roll_width = 800;
+static const int default_piano_roll_height = 400;
bool WinStatsPianoRoll::_window_class_registered = false;
const char * const WinStatsPianoRoll::_window_class_name = "piano";
@@ -27,14 +27,14 @@ const char * const WinStatsPianoRoll::_window_class_name = "piano";
WinStatsPianoRoll::
WinStatsPianoRoll(WinStatsMonitor *monitor, int thread_index) :
PStatPianoRoll(monitor, thread_index,
- default_piano_roll_width,
- default_piano_roll_height),
+ monitor->get_pixel_scale() * default_piano_roll_width / 4,
+ monitor->get_pixel_scale() * default_piano_roll_height / 4),
WinStatsGraph(monitor)
{
- _left_margin = 128;
- _right_margin = 8;
- _top_margin = 16;
- _bottom_margin = 8;
+ _left_margin = _pixel_scale * 32;
+ _right_margin = _pixel_scale * 2;
+ _top_margin = _pixel_scale * 5;
+ _bottom_margin = _pixel_scale * 2;
// Let's show the units on the guide bar labels. There's room.
set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
@@ -51,7 +51,7 @@ WinStatsPianoRoll::
}
/**
- * Called as each frame's data is made available. There is no gurantee the
+ * Called as each frame's data is made available. There is no guarantee the
* frames will arrive in order, or that all of them will arrive at all. The
* monitor should be prepared to accept frames received out-of-order or
* missing.
@@ -103,12 +103,42 @@ set_time_units(int unit_mask) {
* Called when the user single-clicks on a label.
*/
void WinStatsPianoRoll::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
if (collector_index >= 0) {
WinStatsGraph::_monitor->open_strip_chart(_thread_index, collector_index, false);
}
}
+/**
+ * Called when the user right-clicks on a label.
+ */
+void WinStatsPianoRoll::
+on_popup_label(int collector_index) {
+ POINT point;
+ if (collector_index >= 0 && GetCursorPos(&point)) {
+ _popup_index = collector_index;
+
+ HMENU popup = CreatePopupMenu();
+
+ std::string label = get_label_tooltip(collector_index);
+ if (!label.empty()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str());
+ }
+ AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
+ AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
+ TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
+ }
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsPianoRoll::
+get_label_tooltip(int collector_index) const {
+ return PStatPianoRoll::get_label_tooltip(collector_index);
+}
+
/**
* Changes the amount of time the width of the horizontal axis represents.
* This may force a redraw.
@@ -123,6 +153,15 @@ set_horizontal_scale(double time_width) {
InvalidateRect(_window, &rect, TRUE);
}
+/**
+ * Calls update_guide_bars with parameters suitable to this kind of graph.
+ */
+void WinStatsPianoRoll::
+normal_guide_bars() {
+ // We want vaguely 100 pixels between guide bars.
+ update_guide_bars(get_xsize() / (_pixel_scale * 25), get_horizontal_scale());
+}
+
/**
* Erases the chart area.
*/
@@ -144,6 +183,21 @@ begin_draw() {
for (int i = 0; i < num_guide_bars; i++) {
draw_guide_bar(_bitmap_dc, get_guide_bar(i));
}
+
+ SelectObject(_bitmap_dc, GetStockObject(NULL_PEN));
+}
+
+/**
+ * Should be overridden by the user class. This hook will be called before
+ * drawing any one row of bars. These bars correspond to the collector whose
+ * index is get_row_collector(row), and in the color get_row_color(row).
+ */
+void WinStatsPianoRoll::
+begin_row(int row) {
+ int collector_index = get_label_collector(row);
+ HBRUSH brush = get_collector_brush(collector_index, _highlighted_index == collector_index);
+ SelectObject(_bitmap_dc, brush);
+ SelectObject(_bitmap_dc, GetStockObject(NULL_PEN));
}
/**
@@ -155,13 +209,7 @@ draw_bar(int row, int from_x, int to_x) {
int y = _label_stack.get_label_y(row) - _graph_top;
int height = _label_stack.get_label_height(row);
- RECT rect = {
- from_x, y - height + 2,
- to_x, y - 2,
- };
- int collector_index = get_label_collector(row);
- HBRUSH brush = get_collector_brush(collector_index);
- FillRect(_bitmap_dc, &rect, brush);
+ RoundRect(_bitmap_dc, from_x, y - height + 2, to_x, y - 2, _pixel_scale, _pixel_scale);
}
}
@@ -198,6 +246,18 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
}
break;
+ case WM_COMMAND:
+ switch (LOWORD(wparam)) {
+ case 102:
+ WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index, false);
+ return 0;
+
+ case 103:
+ WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
+ return 0;
+ }
+ break;
+
default:
break;
}
@@ -233,7 +293,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
// When the mouse is over a color bar, highlight it.
int16_t x = LOWORD(lparam);
int16_t y = HIWORD(lparam);
- _label_stack.highlight_label(get_collector_under_pixel(x, y));
+
+ int collector_index = get_collector_under_pixel(x, y);
+ _label_stack.highlight_label(collector_index);
+ on_enter_label(collector_index);
// Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
// window.
@@ -244,10 +307,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
0
};
TrackMouseEvent(&tme);
-
- } else {
+ }
+ else {
// If the mouse is in some drag mode, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
}
if (_drag_mode == DM_scale) {
@@ -278,6 +342,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
case WM_MOUSELEAVE:
// When the mouse leaves the graph, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
break;
case WM_LBUTTONUP:
@@ -305,7 +370,20 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
// clicking on the corresponding label.
int16_t x = LOWORD(lparam);
int16_t y = HIWORD(lparam);
- clicked_label(get_collector_under_pixel(x, y));
+ on_click_label(get_collector_under_pixel(x, y));
+ return 0;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT point;
+ if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) {
+ int collector_index = get_collector_under_pixel(point.x, point.y);
+ if (collector_index >= 0) {
+ on_popup_label(collector_index);
+ }
+ }
return 0;
}
break;
@@ -325,12 +403,11 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
void WinStatsPianoRoll::
additional_window_paint(HDC hdc) {
// Draw in the labels for the guide bars.
- HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT);
- SelectObject(hdc, hfnt);
+ SelectObject(hdc, WinStatsGraph::_monitor->get_font());
SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
SetBkMode(hdc, TRANSPARENT);
- int y = _top_margin;
+ int y = _top_margin - 2;
int i;
int num_guide_bars = get_num_guide_bars();
@@ -357,6 +434,19 @@ additional_graph_window_paint(HDC hdc) {
}
}
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsPianoRoll::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ int collector_index = get_collector_under_pixel(mouse_x, mouse_y);
+ if (collector_index >= 0) {
+ return get_label_tooltip(collector_index);
+ }
+ return std::string();
+}
+
/**
* Based on the mouse position within the window's client area, look for
* draggable things the mouse might be hovering over and return the
@@ -391,7 +481,7 @@ consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
* -1.
*/
int WinStatsPianoRoll::
-get_collector_under_pixel(int xpoint, int ypoint) {
+get_collector_under_pixel(int xpoint, int ypoint) const {
if (_label_stack.get_num_labels() == 0) {
return -1;
}
@@ -411,13 +501,8 @@ get_collector_under_pixel(int xpoint, int ypoint) {
*/
void WinStatsPianoRoll::
update_labels() {
- _label_stack.clear_labels();
- for (int i = 0; i < get_num_labels(); i++) {
- int label_index =
- _label_stack.add_label(WinStatsGraph::_monitor, this,
- _thread_index,
- get_label_collector(i), true);
- }
+ _label_stack.replace_labels(WinStatsGraph::_monitor, this,
+ _thread_index, _labels, true);
_labels_changed = false;
}
@@ -504,7 +589,7 @@ create_window() {
WinStatsGraph::_monitor->get_client_data();
std::string thread_name = client_data->get_thread_name(_thread_index);
std::string window_title = thread_name + " thread piano roll";
-
+ POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos();
RECT win_rect = {
0, 0,
@@ -516,11 +601,13 @@ create_window() {
AdjustWindowRect(&win_rect, graph_window_style, FALSE);
_window =
- CreateWindow(_window_class_name, window_title.c_str(), graph_window_style,
- CW_USEDEFAULT, CW_USEDEFAULT,
- win_rect.right - win_rect.left,
- win_rect.bottom - win_rect.top,
- WinStatsGraph::_monitor->get_window(), nullptr, application, 0);
+ CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name,
+ window_title.c_str(), graph_window_style,
+ window_pos.x, window_pos.y,
+ win_rect.right - win_rect.left,
+ win_rect.bottom - win_rect.top,
+ WinStatsGraph::_monitor->get_window(),
+ nullptr, application, 0);
if (!_window) {
nout << "Could not create PianoRoll window!\n";
exit(1);
@@ -551,7 +638,7 @@ register_window_class(HINSTANCE application) {
wc.lpfnWndProc = (WNDPROC)static_window_proc;
wc.hInstance = application;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszMenuName = nullptr;
wc.lpszClassName = _window_class_name;
diff --git a/pandatool/src/win-stats/winStatsPianoRoll.h b/pandatool/src/win-stats/winStatsPianoRoll.h
index b130c99b18..1fc3ac2443 100644
--- a/pandatool/src/win-stats/winStatsPianoRoll.h
+++ b/pandatool/src/win-stats/winStatsPianoRoll.h
@@ -41,12 +41,16 @@ public:
virtual void changed_graph_size(int graph_xsize, int graph_ysize);
virtual void set_time_units(int unit_mask);
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
void set_horizontal_scale(double time_width);
protected:
+ virtual void normal_guide_bars();
void clear_region();
virtual void begin_draw();
+ virtual void begin_row(int row);
virtual void draw_bar(int row, int from_x, int to_x);
virtual void end_draw();
virtual void idle();
@@ -55,11 +59,12 @@ protected:
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual void additional_window_paint(HDC hdc);
virtual void additional_graph_window_paint(HDC hdc);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int mouse_x, int mouse_y,
int width, int height);
private:
- int get_collector_under_pixel(int xpoint, int ypoint);
+ int get_collector_under_pixel(int xpoint, int ypoint) const;
void update_labels();
void draw_guide_bar(HDC hdc, const GuideBar &bar);
void draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar);
@@ -69,6 +74,8 @@ private:
static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+ int _popup_index = -1;
+
static bool _window_class_registered;
static const char * const _window_class_name;
};
diff --git a/pandatool/src/win-stats/winStatsStripChart.cxx b/pandatool/src/win-stats/winStatsStripChart.cxx
index 879b920673..ccf0707ff4 100644
--- a/pandatool/src/win-stats/winStatsStripChart.cxx
+++ b/pandatool/src/win-stats/winStatsStripChart.cxx
@@ -16,17 +16,11 @@
#include "pStatCollectorDef.h"
#include "numeric_types.h"
-using std::string;
+#include
static const int default_strip_chart_width = 400;
static const int default_strip_chart_height = 100;
-// Surely we aren't expected to hardcode the size of a normal checkbox. But
-// Windows seems to require this data to be passed to CreateWindow(), so what
-// else can I do?
-size_t WinStatsStripChart::_check_box_height = 13;
-size_t WinStatsStripChart::_check_box_width = 13;
-
bool WinStatsStripChart::_window_class_registered = false;
const char * const WinStatsStripChart::_window_class_name = "strip";
@@ -37,19 +31,19 @@ WinStatsStripChart::
WinStatsStripChart(WinStatsMonitor *monitor, int thread_index,
int collector_index, bool show_level) :
PStatStripChart(monitor,
- show_level ? monitor->get_level_view(collector_index, thread_index) : monitor->get_view(thread_index),
+ show_level ? monitor->get_level_view(0, thread_index) : monitor->get_view(thread_index),
thread_index,
collector_index,
- default_strip_chart_width,
- default_strip_chart_height),
+ monitor->get_pixel_scale() * default_strip_chart_width / 4,
+ monitor->get_pixel_scale() * default_strip_chart_height / 4),
WinStatsGraph(monitor)
{
_brush_origin = 0;
- _left_margin = 96;
- _right_margin = 32;
- _top_margin = 16;
- _bottom_margin = 8;
+ _left_margin = _pixel_scale * 24;
+ _right_margin = _pixel_scale * 12;
+ _top_margin = _pixel_scale * 6;
+ _bottom_margin = _pixel_scale * 2;
if (show_level) {
// If it's a level-type graph, show the appropriate units.
@@ -86,7 +80,7 @@ new_collector(int collector_index) {
}
/**
- * Called as each frame's data is made available. There is no gurantee the
+ * Called as each frame's data is made available. There is no guarantee the
* frames will arrive in order, or that all of them will arrive at all. The
* monitor should be prepared to accept frames received out-of-order or
* missing.
@@ -94,7 +88,7 @@ new_collector(int collector_index) {
void WinStatsStripChart::
new_data(int thread_index, int frame_number) {
if (is_title_unknown()) {
- string window_title = get_title_text();
+ std::string window_title = get_title_text();
if (!is_title_unknown()) {
SetWindowText(_window, window_title.c_str());
}
@@ -103,7 +97,7 @@ new_data(int thread_index, int frame_number) {
if (!_pause) {
update();
- string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
+ std::string text = format_number(get_average_net_value(), get_guide_bar_units(), get_guide_bar_unit_name());
if (_net_value_text != text) {
_net_value_text = text;
RECT rect;
@@ -170,7 +164,7 @@ set_scroll_speed(double scroll_speed) {
* Called when the user single-clicks on a label.
*/
void WinStatsStripChart::
-clicked_label(int collector_index) {
+on_click_label(int collector_index) {
if (collector_index < 0) {
// Clicking on whitespace in the graph is the same as clicking on the top
// label.
@@ -197,6 +191,62 @@ clicked_label(int collector_index) {
}
}
+/**
+ * Called when the user right-clicks on a label.
+ */
+void WinStatsStripChart::
+on_popup_label(int collector_index) {
+ POINT point;
+ if (collector_index >= 0 && GetCursorPos(&point)) {
+ _popup_index = collector_index;
+
+ HMENU popup = CreatePopupMenu();
+
+ std::string label = get_label_tooltip(collector_index);
+ if (!label.empty()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str());
+ }
+ if (collector_index == 0 && get_collector_index() == 0) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 101, "Set as Focus");
+ } else {
+ AppendMenu(popup, MF_STRING, 101, "Set as Focus");
+ }
+ AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
+ if (get_view().get_show_level()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 103, "Open Flame Graph");
+ } else {
+ AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
+ }
+ TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _window, nullptr);
+ }
+}
+
+/**
+ * Called when the mouse hovers over a label, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsStripChart::
+get_label_tooltip(int collector_index) const {
+ return PStatStripChart::get_label_tooltip(collector_index);
+}
+
+/**
+ * Changes the collector represented by this strip chart. This may force a
+ * redraw.
+ */
+void WinStatsStripChart::
+set_collector_index(int collector_index) {
+ if (get_collector_index() != collector_index) {
+ PStatStripChart::set_collector_index(collector_index);
+
+ // Redraw the scale labels.
+ RECT rect;
+ GetClientRect(_window, &rect);
+ rect.left = _right_margin;
+ InvalidateRect(_window, &rect, TRUE);
+ }
+}
+
/**
* Changes the value the height of the vertical axis represents. This may
* force a redraw.
@@ -218,11 +268,8 @@ void WinStatsStripChart::
update_labels() {
PStatStripChart::update_labels();
- _label_stack.clear_labels();
- for (int i = 0; i < get_num_labels(); i++) {
- _label_stack.add_label(WinStatsGraph::_monitor, this, _thread_index,
- get_label_collector(i), false);
- }
+ _label_stack.replace_labels(WinStatsGraph::_monitor, this,
+ _thread_index, _labels, false);
_labels_changed = false;
}
@@ -273,7 +320,7 @@ draw_slice(int x, int w, const PStatStripChart::FrameData &fdata) {
for (fi = fdata.begin(); fi != fdata.end(); ++fi) {
const ColorData &cd = (*fi);
overall_time += cd._net_value;
- HBRUSH brush = get_collector_brush(cd._collector_index);
+ HBRUSH brush = get_collector_brush(cd._collector_index, cd._collector_index == _highlighted_index);
if (overall_time > get_vertical_scale()) {
// Off the top. Go ahead and clamp it by hand, in case it's so far off
@@ -353,6 +400,19 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return 0;
}
break;
+
+ case 101:
+ set_collector_index(_popup_index);
+ break;
+
+ case 102:
+ WinStatsGraph::_monitor->open_strip_chart(get_thread_index(), _popup_index,
+ get_view().get_show_level());
+ return 0;
+
+ case 103:
+ WinStatsGraph::_monitor->open_flame_graph(get_thread_index(), _popup_index);
+ return 0;
}
break;
@@ -391,7 +451,10 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
// When the mouse is over a color bar, highlight it.
int16_t x = LOWORD(lparam);
int16_t y = HIWORD(lparam);
- _label_stack.highlight_label(get_collector_under_pixel(x, y));
+
+ int collector_index = get_collector_under_pixel(x, y);
+ _label_stack.highlight_label(collector_index);
+ on_enter_label(collector_index);
// Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
// window.
@@ -402,17 +465,23 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
0
};
TrackMouseEvent(&tme);
-
- } else {
+ }
+ else {
// If the mouse is in some drag mode, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
}
if (_drag_mode == DM_scale) {
int16_t y = HIWORD(lparam);
- double ratio = 1.0f - ((double)y / (double)get_ysize());
- if (ratio > 0.0f) {
- set_vertical_scale(_drag_scale_start / ratio);
+ double ratio = 1.0 - ((double)y / (double)get_ysize());
+ if (ratio > 0.0) {
+ double new_scale = _drag_scale_start / ratio;
+ if (!IS_NEARLY_EQUAL(get_vertical_scale(), new_scale)) {
+ // Disable smoothing while we do this expensive operation.
+ set_average_mode(false);
+ set_vertical_scale(_drag_scale_start / ratio);
+ }
}
return 0;
@@ -436,6 +505,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
case WM_MOUSELEAVE:
// When the mouse leaves the graph, stop highlighting.
_label_stack.highlight_label(-1);
+ on_leave_label(_highlighted_index);
break;
case WM_LBUTTONUP:
@@ -463,7 +533,20 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
// clicking on the corresponding label.
int16_t x = LOWORD(lparam);
int16_t y = HIWORD(lparam);
- clicked_label(get_collector_under_pixel(x, y));
+ on_click_label(get_collector_under_pixel(x, y));
+ return 0;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT point;
+ if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) {
+ int collector_index = get_collector_under_pixel(point.x, point.y);
+ if (collector_index >= 0) {
+ on_popup_label(collector_index);
+ }
+ }
return 0;
}
break;
@@ -483,14 +566,13 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
void WinStatsStripChart::
additional_window_paint(HDC hdc) {
// Draw in the labels for the guide bars.
- HFONT hfnt = (HFONT)GetStockObject(ANSI_VAR_FONT);
- SelectObject(hdc, hfnt);
+ SelectObject(hdc, WinStatsGraph::_monitor->get_font());
SetTextAlign(hdc, TA_LEFT | TA_TOP);
SetBkMode(hdc, TRANSPARENT);
RECT rect;
GetClientRect(_window, &rect);
- int x = rect.right - _right_margin + 2;
+ int x = rect.right - _right_margin + _pixel_scale;
int last_y = -100;
int i;
@@ -511,15 +593,8 @@ additional_window_paint(HDC hdc) {
// Now draw the "net value" label at the top.
SetTextAlign(hdc, TA_RIGHT | TA_BOTTOM);
SetTextColor(hdc, RGB(0, 0, 0));
- TextOut(hdc, rect.right - _right_margin, _top_margin,
+ TextOut(hdc, rect.right - _right_margin - _pixel_scale, _top_margin - _pixel_scale / 2,
_net_value_text.data(), _net_value_text.length());
-
- // Also draw the "Smooth" label on the check box. This isn't part of the
- // check box itself, because doing that doesn't use the right font! Surely
- // this isn't the correct Windows(tm) way to do this sort of thing, but I
- // don't know any better for now.
- SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
- TextOut(hdc, _left_margin + _check_box_width + 2, _top_margin, "Smooth", 6);
}
/**
@@ -535,6 +610,18 @@ additional_graph_window_paint(HDC hdc) {
}
}
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsStripChart::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ if (_highlighted_index != -1) {
+ return get_label_tooltip(_highlighted_index);
+ }
+ return std::string();
+}
+
/**
* Based on the mouse position within the window's client area, look for
* draggable things the mouse might be hovering over and return the
@@ -570,16 +657,7 @@ void WinStatsStripChart::
set_drag_mode(WinStatsGraph::DragMode drag_mode) {
WinStatsGraph::set_drag_mode(drag_mode);
- switch (_drag_mode) {
- case DM_scale:
- case DM_left_margin:
- case DM_right_margin:
- case DM_sizing:
- // Disable smoothing for these expensive operations.
- set_average_mode(false);
- break;
-
- default:
+ if (_drag_mode == DM_none) {
// Restore smoothing according to the current setting of the check box.
int result = SendMessage(_smooth_check_box, BM_GETCHECK, 0, 0);
set_average_mode(result == BST_CHECKED);
@@ -594,10 +672,13 @@ void WinStatsStripChart::
move_graph_window(int graph_left, int graph_top, int graph_xsize, int graph_ysize) {
WinStatsGraph::move_graph_window(graph_left, graph_top, graph_xsize, graph_ysize);
if (_smooth_check_box != 0) {
+ SIZE size;
+ SendMessage(_smooth_check_box, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
+
SetWindowPos(_smooth_check_box, 0,
- _left_margin, _top_margin - _check_box_height - 1,
- 0, 0,
- SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
+ _left_margin, _top_margin - size.cy - _pixel_scale / 2,
+ size.cx, size.cy,
+ SWP_NOZORDER | SWP_SHOWWINDOW);
InvalidateRect(_smooth_check_box, nullptr, TRUE);
}
}
@@ -652,7 +733,7 @@ draw_guide_label(HDC hdc, int x, const PStatGraph::GuideBar &bar, int last_y) {
}
int y = height_to_pixel(bar._height);
- const string &label = bar._label;
+ const std::string &label = bar._label;
SIZE size;
GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
@@ -689,7 +770,8 @@ create_window() {
HINSTANCE application = GetModuleHandle(nullptr);
register_window_class(application);
- string window_title = get_title_text();
+ std::string window_title = get_title_text();
+ POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos();
RECT win_rect = {
0, 0,
@@ -701,11 +783,13 @@ create_window() {
AdjustWindowRect(&win_rect, graph_window_style, FALSE);
_window =
- CreateWindow(_window_class_name, window_title.c_str(), graph_window_style,
- CW_USEDEFAULT, CW_USEDEFAULT,
- win_rect.right - win_rect.left,
- win_rect.bottom - win_rect.top,
- WinStatsGraph::_monitor->get_window(), nullptr, application, 0);
+ CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name,
+ window_title.c_str(), graph_window_style,
+ window_pos.x, window_pos.y,
+ win_rect.right - win_rect.left,
+ win_rect.bottom - win_rect.top,
+ WinStatsGraph::_monitor->get_window(),
+ nullptr, application, 0);
if (!_window) {
nout << "Could not create StripChart window!\n";
exit(1);
@@ -715,10 +799,15 @@ create_window() {
setup_label_stack();
_smooth_check_box =
- CreateWindow("BUTTON", "",
- WS_CHILD | BS_AUTOCHECKBOX,
- 0, 0, _check_box_width, _check_box_height,
+ CreateWindow(WC_BUTTON, "Smooth", WS_CHILD | BS_AUTOCHECKBOX,
+ 0, 0, 0, 0,
_window, nullptr, application, 0);
+ SendMessage(_smooth_check_box, WM_SETFONT,
+ (WPARAM)WinStatsGraph::_monitor->get_font(), TRUE);
+
+ if (get_average_mode()) {
+ SendMessage(_smooth_check_box, BM_SETCHECK, BST_CHECKED, 0);
+ }
// Ensure that the window is on top of the stack.
SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
@@ -742,7 +831,7 @@ register_window_class(HINSTANCE application) {
wc.lpfnWndProc = (WNDPROC)static_window_proc;
wc.hInstance = application;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszMenuName = nullptr;
wc.lpszClassName = _window_class_name;
diff --git a/pandatool/src/win-stats/winStatsStripChart.h b/pandatool/src/win-stats/winStatsStripChart.h
index 9e6f91667f..3874bfe41f 100644
--- a/pandatool/src/win-stats/winStatsStripChart.h
+++ b/pandatool/src/win-stats/winStatsStripChart.h
@@ -43,7 +43,11 @@ public:
virtual void set_time_units(int unit_mask);
virtual void set_scroll_speed(double scroll_speed);
- virtual void clicked_label(int collector_index);
+ virtual void on_click_label(int collector_index);
+ virtual void on_popup_label(int collector_index);
+ virtual std::string get_label_tooltip(int collector_index) const;
+
+ void set_collector_index(int collector_index);
void set_vertical_scale(double value_height);
protected:
@@ -61,6 +65,7 @@ protected:
virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
virtual void additional_window_paint(HDC hdc);
virtual void additional_graph_window_paint(HDC hdc);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int mouse_x, int mouse_y,
int width, int height);
virtual void set_drag_mode(DragMode drag_mode);
@@ -79,7 +84,7 @@ private:
std::string _net_value_text;
HWND _smooth_check_box;
- static size_t _check_box_height, _check_box_width;
+ int _popup_index = -1;
static bool _window_class_registered;
static const char * const _window_class_name;
diff --git a/pandatool/src/win-stats/winStatsTimeline.cxx b/pandatool/src/win-stats/winStatsTimeline.cxx
new file mode 100644
index 0000000000..225def4f89
--- /dev/null
+++ b/pandatool/src/win-stats/winStatsTimeline.cxx
@@ -0,0 +1,752 @@
+/**
+ * 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 winStatsTimeline.cxx
+ * @author rdb
+ * @date 2022-02-11
+ */
+
+#include "winStatsTimeline.h"
+#include "winStatsMonitor.h"
+#include "numeric_types.h"
+
+static const int default_timeline_width = 1000;
+static const int default_timeline_height = 500;
+
+bool WinStatsTimeline::_window_class_registered = false;
+const char * const WinStatsTimeline::_window_class_name = "timeline";
+
+/**
+ *
+ */
+WinStatsTimeline::
+WinStatsTimeline(WinStatsMonitor *monitor) :
+ PStatTimeline(monitor,
+ monitor->get_pixel_scale() * default_timeline_width / 4,
+ monitor->get_pixel_scale() * default_timeline_height / 4),
+ WinStatsGraph(monitor)
+{
+ _left_margin = _pixel_scale * 24;
+ _right_margin = _pixel_scale * 2;
+ _top_margin = _pixel_scale * 5;
+ _bottom_margin = _pixel_scale * 2;
+
+ normal_guide_bars();
+
+ create_window();
+ clear_region();
+
+ _grid_brush = CreateSolidBrush(RGB(0xdd, 0xdd, 0xdd));
+}
+
+/**
+ *
+ */
+WinStatsTimeline::
+~WinStatsTimeline() {
+}
+
+/**
+ * Called as each frame's data is made available. There is no guarantee the
+ * frames will arrive in order, or that all of them will arrive at all. The
+ * monitor should be prepared to accept frames received out-of-order or
+ * missing.
+ */
+void WinStatsTimeline::
+new_data(int thread_index, int frame_number) {
+ PStatTimeline::new_data(thread_index, frame_number);
+}
+
+/**
+ * Called when it is necessary to redraw the entire graph.
+ */
+void WinStatsTimeline::
+force_redraw() {
+ PStatTimeline::force_redraw();
+}
+
+/**
+ * Called when the user has resized the window, forcing a resize of the graph.
+ */
+void WinStatsTimeline::
+changed_graph_size(int graph_xsize, int graph_ysize) {
+ PStatTimeline::changed_size(graph_xsize, graph_ysize);
+}
+
+/**
+ * Erases the chart area.
+ */
+void WinStatsTimeline::
+clear_region() {
+ RECT rect = { 0, 0, get_xsize(), get_ysize() };
+ FillRect(_bitmap_dc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+}
+
+/**
+ * Erases the chart area in preparation for drawing a bunch of bars.
+ */
+void WinStatsTimeline::
+begin_draw() {
+ SelectObject(_bitmap_dc, WinStatsGraph::_monitor->get_font());
+ SelectObject(_bitmap_dc, GetStockObject(NULL_PEN));
+ SetBkMode(_bitmap_dc, TRANSPARENT);
+ SetTextAlign(_bitmap_dc, TA_LEFT | TA_TOP | TA_NOUPDATECP);
+}
+
+/**
+ * Draws a horizontal separator.
+ */
+void WinStatsTimeline::
+draw_separator(int row) {
+ int y = (row_to_pixel(row) + row_to_pixel(row + 1)) / 2;
+ RECT rect = {0, y, get_xsize(), y + _pixel_scale / 3};
+ FillRect(_bitmap_dc, &rect, _grid_brush);
+}
+
+/**
+ * Draws a vertical guide bar. If the row is -1, draws it in all rows.
+ */
+void WinStatsTimeline::
+draw_guide_bar(int x, GuideBarStyle style) {
+ int x1 = x - _pixel_scale / 6;
+ int x2 = x1 + _pixel_scale / 3;
+ if (style == GBS_frame) {
+ ++x2;
+ }
+ RECT rect = {x1, 0, x2, get_ysize()};
+ FillRect(_bitmap_dc, &rect, _grid_brush);
+}
+
+/**
+ * Draws a single bar in the chart for the indicated row, in the color for the
+ * given collector, for the indicated horizontal pixel range.
+ */
+void WinStatsTimeline::
+draw_bar(int row, int from_x, int to_x, int collector_index,
+ const std::string &collector_name) {
+
+ int top = row_to_pixel(row);
+ int bottom = row_to_pixel(row + 1);
+
+ bool is_highlighted = row == _highlighted_row && _highlighted_x >= from_x && _highlighted_x < to_x;
+ HBRUSH brush = get_collector_brush(collector_index, is_highlighted);
+
+ if (to_x < from_x + 2) {
+ // It's just a tiny sliver. This is a more reliable way to draw it.
+ RECT rect = {from_x, top + 1, from_x + 1, bottom - 1};
+ FillRect(_bitmap_dc, &rect, brush);
+
+ //if (to_x <= from_x + 2) {
+ // // Draw an arrow pointing to it, if it's so small.
+ // POINT vertices[] = {{to_x, bottom}, {to_x - _pixel_scale, bottom + _pixel_scale * 2}, {to_x + _pixel_scale, bottom + _pixel_scale * 2}};
+ // Polygon(_bitmap_dc, vertices, 3);
+ //}
+ }
+ else {
+ SelectObject(_bitmap_dc, brush);
+ RoundRect(_bitmap_dc,
+ std::max(from_x, -_pixel_scale - 1),
+ top,
+ std::min(std::max(to_x, from_x + 1), get_xsize() + _pixel_scale),
+ bottom,
+ _pixel_scale,
+ _pixel_scale);
+
+ if ((to_x - from_x) >= _pixel_scale * 4) {
+ // Only bother drawing the text if we've got some space to draw on.
+ // Choose a suitable foreground color.
+ SetTextColor(_bitmap_dc, get_collector_text_color(collector_index, is_highlighted));
+
+ // Make sure that the text doesn't run off the chart.
+ SIZE size;
+ GetTextExtentPoint32(_bitmap_dc, collector_name.data(), collector_name.size(), &size);
+ int center = (from_x + to_x) / 2;
+ int left = std::max(from_x, 0) + _pixel_scale / 2;
+ int right = std::min(to_x, get_xsize()) - _pixel_scale / 2;
+
+ if (size.cx >= right - left) {
+ if (right - left < _pixel_scale * 6) {
+ // It's a really tiny space. Draw a single letter.
+ RECT rect = {left, top, right, bottom};
+ DrawText(_bitmap_dc, collector_name.data(), 1,
+ &rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
+ } else {
+ // It's going to be tricky to fit it, let Windows figure it out via
+ // the more expensive DrawText call.
+ RECT rect = {left, top, right, bottom};
+ DrawText(_bitmap_dc, collector_name.data(), collector_name.size(),
+ &rect, DT_CENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER);
+ }
+ }
+ else {
+ int text_top = top + (bottom - top - size.cy) / 2;
+ if (center - size.cx / 2 < 0) {
+ // Put it against the left-most edge.
+ TextOut(_bitmap_dc, _pixel_scale, text_top,
+ collector_name.data(), collector_name.length());
+ }
+ else if (center + size.cx / 2 >= get_xsize()) {
+ // Put it against the right-most edge.
+ TextOut(_bitmap_dc, get_xsize() - _pixel_scale - size.cx, text_top,
+ collector_name.data(), collector_name.length());
+ }
+ else {
+ // It fits just fine, center it.
+ TextOut(_bitmap_dc, center - size.cx / 2, text_top,
+ collector_name.data(), collector_name.length());
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Called after all the bars have been drawn, this triggers a refresh event to
+ * draw it to the window.
+ */
+void WinStatsTimeline::
+end_draw() {
+ InvalidateRect(_graph_window, nullptr, FALSE);
+
+ if (_threads_changed) {
+ RECT rect;
+ GetClientRect(_window, &rect);
+ rect.top = _top_margin;
+ rect.right = _left_margin;
+ InvalidateRect(_window, &rect, TRUE);
+ _threads_changed = false;
+ }
+
+ if (_guide_bars_changed) {
+ RECT rect;
+ GetClientRect(_window, &rect);
+ rect.bottom = _top_margin;
+ InvalidateRect(_window, &rect, TRUE);
+ _guide_bars_changed = false;
+ }
+}
+
+/**
+ * Called at the end of the draw cycle.
+ */
+void WinStatsTimeline::
+idle() {
+}
+
+/**
+ * Overridden by a derived class to implement an animation. If it returns
+ * false, the animation timer is stopped.
+ */
+bool WinStatsTimeline::
+animate(double time, double dt) {
+ return PStatTimeline::animate(time, dt);
+}
+
+/**
+ *
+ */
+LONG WinStatsTimeline::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch (msg) {
+ case WM_MOUSELEAVE:
+ SetFocus(nullptr);
+ break;
+
+ default:
+ break;
+ }
+
+ return WinStatsGraph::window_proc(hwnd, msg, wparam, lparam);
+}
+
+/**
+ *
+ */
+LONG WinStatsTimeline::
+graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ if (_potential_drag_mode == DM_none) {
+ set_drag_mode(DM_pan);
+ int16_t x = LOWORD(lparam);
+ _drag_start_x = x;
+ _scroll_speed = 0.0;
+ _zoom_center = pixel_to_timestamp(x);
+ SetCapture(_graph_window);
+ return 0;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ // Make sure we can accept keyboard events, except if we're inactive.
+ if (GetActiveWindow() == _window) {
+ SetFocus(hwnd);
+ }
+
+ if (_drag_mode == DM_none && _potential_drag_mode == DM_none) {
+ // When the mouse is over a color bar, highlight it.
+ int x = LOWORD(lparam);
+ int y = HIWORD(lparam);
+ double time = pixel_to_timestamp(x);
+
+ int row = pixel_to_row(y);
+
+ if (row != _highlighted_row) {
+ clear_graph_tooltip();
+ }
+ else if (_highlighted_row >= 0) {
+ // Is the mouse on the same bar? If not, clear the tooltip.
+ ColorBar bar;
+ if (find_bar(row, x, bar)) {
+ double prev_time = pixel_to_timestamp(_highlighted_x);
+ if (prev_time < bar._start || prev_time > bar._end) {
+ clear_graph_tooltip();
+ }
+ } else {
+ clear_graph_tooltip();
+ }
+ }
+
+ std::swap(_highlighted_x, x);
+ std::swap(_highlighted_row, row);
+
+ if (row >= 0) {
+ PStatTimeline::force_redraw(row, x, x);
+ }
+ PStatTimeline::force_redraw(_highlighted_row, _highlighted_x, _highlighted_x);
+
+ if ((_keys_held & (F_w | F_s)) != 0) {
+ // Update the zoom center if we move the mouse while zooming with the
+ // keyboard.
+ _zoom_center = time;
+ }
+
+ // Now we want to get a WM_MOUSELEAVE when the mouse leaves the graph
+ // window.
+ TRACKMOUSEEVENT tme = {
+ sizeof(TRACKMOUSEEVENT),
+ TME_LEAVE,
+ _graph_window,
+ 0
+ };
+ TrackMouseEvent(&tme);
+ }
+ else {
+ // If the mouse is in some drag mode, stop highlighting.
+ if (_highlighted_row != -1) {
+ int row = _highlighted_row;
+ _highlighted_row = -1;
+ PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x);
+ clear_graph_tooltip();
+ }
+ }
+
+ if (_drag_mode == DM_pan) {
+ int16_t x = LOWORD(lparam);
+ int delta = _drag_start_x - x;
+ set_horizontal_scroll(get_horizontal_scroll() + pixel_to_height(delta));
+ _drag_start_x = x;
+ return 0;
+ }
+ break;
+
+ case WM_MOUSELEAVE:
+ // When the mouse leaves the graph, stop highlighting.
+ if (_highlighted_row != -1) {
+ int row = _highlighted_row;
+ _highlighted_row = -1;
+ PStatTimeline::force_redraw(row, _highlighted_x, _highlighted_x);
+ clear_graph_tooltip();
+ }
+ SetFocus(nullptr);
+ break;
+
+ case WM_LBUTTONUP:
+ if (_drag_mode == DM_pan) {
+ set_drag_mode(DM_none);
+ ReleaseCapture();
+ return 0;
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ {
+ // Double-clicking on a color bar in the graph will zoom the graph into
+ // that collector.
+ int16_t x = LOWORD(lparam);
+ int16_t y = HIWORD(lparam);
+ int row = pixel_to_row(y);
+ ColorBar bar;
+ if (find_bar(row, x, bar)) {
+ double width = bar._end - bar._start;
+ zoom_to(width * 1.5, pixel_to_timestamp(x));
+ scroll_to(bar._start - width / 4.0);
+ } else {
+ // Double-clicking the white area zooms out.
+ _zoom_speed -= 100.0;
+ }
+ start_animation();
+ return 0;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ // Right-clicking a color bar brings up a context menu.
+ POINT point;
+ if (GetCursorPos(&point)) {
+ POINT graph_point = point;
+ if (ScreenToClient(_graph_window, &graph_point)) {
+ int row = pixel_to_row(graph_point.y);
+ ColorBar bar;
+ if (find_bar(row, graph_point.x, bar)) {
+ _popup_bar = bar;
+
+ HMENU popup = CreatePopupMenu();
+
+ std::string label = get_bar_tooltip(row, graph_point.x);
+ if (!label.empty()) {
+ AppendMenu(popup, MF_STRING | MF_DISABLED, 0, label.c_str());
+ }
+ AppendMenu(popup, MF_STRING, 101, "Zoom To");
+ AppendMenu(popup, MF_STRING, 102, "Open Strip Chart");
+ AppendMenu(popup, MF_STRING, 103, "Open Flame Graph");
+ AppendMenu(popup, MF_STRING, 104, "Open Piano Roll");
+ TrackPopupMenu(popup, TPM_LEFTBUTTON, point.x, point.y, 0, _graph_window, nullptr);
+ }
+ }
+ }
+ return 0;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wparam)) {
+ case 101:
+ {
+ double width = _popup_bar._end - _popup_bar._start;
+ zoom_to(width * 1.5, (_popup_bar._end + _popup_bar._start) / 2.0);
+ scroll_to(_popup_bar._start - width / 4.0);
+ start_animation();
+ }
+ return 0;
+
+ case 102:
+ WinStatsGraph::_monitor->open_strip_chart(_popup_bar._thread_index, _popup_bar._collector_index, false);
+ return 0;
+
+ case 103:
+ WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index);
+ return 0;
+
+ case 104:
+ WinStatsGraph::_monitor->open_piano_roll(_popup_bar._thread_index);
+ return 0;
+ }
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ if (GET_KEYSTATE_WPARAM(wparam) & MK_CONTROL) {
+ // Zoom in/out around the cursor position.
+ POINT point;
+ if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) {
+ int delta = GET_WHEEL_DELTA_WPARAM(wparam);
+ zoom_by(delta / 120.0, pixel_to_timestamp(point.x));
+ start_animation();
+ }
+ }
+ return 0;
+ }
+ break;
+
+ case WM_MOUSEHWHEEL:
+ {
+ int delta = GET_WHEEL_DELTA_WPARAM(wparam);
+ _scroll_speed += delta / 12.0;
+ start_animation();
+ return 0;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ {
+ int flag = 0;
+ int vsc = (lparam & 0xff0000) >> 16;
+ if ((lparam & 0x1000000) == 0) {
+ // Accept WASD based on their position rather than their mapping
+ switch (vsc) {
+ case 17:
+ flag = F_w;
+ break;
+ case 30:
+ flag = F_a;
+ break;
+ case 31:
+ flag = F_s;
+ break;
+ case 32:
+ flag = F_d;
+ break;
+ }
+ }
+ if (flag == 0) {
+ switch (wparam) {
+ case VK_LEFT:
+ flag = F_left;
+ break;
+ case VK_RIGHT:
+ flag = F_right;
+ break;
+ case 'W':
+ flag = F_w;
+ break;
+ case 'A':
+ flag = F_a;
+ break;
+ case 'S':
+ flag = F_s;
+ break;
+ case 'D':
+ flag = F_d;
+ break;
+ }
+ }
+ if (flag != 0) {
+ if (msg == WM_KEYDOWN) {
+ if (flag & (F_w | F_s)) {
+ POINT point;
+ if (GetCursorPos(&point) && ScreenToClient(_graph_window, &point)) {
+ _zoom_center = pixel_to_timestamp(point.x);
+ } else {
+ _zoom_center = get_horizontal_scroll() + get_horizontal_scale() / 2.0;
+ }
+ }
+ if (_keys_held == 0) {
+ start_animation();
+ }
+ _keys_held |= flag;
+ }
+ else if (_keys_held != 0) {
+ _keys_held &= ~flag;
+ }
+ }
+ }
+ break;
+
+ case WM_KILLFOCUS:
+ _keys_held = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return WinStatsGraph::graph_window_proc(hwnd, msg, wparam, lparam);
+}
+
+/**
+ * This is called during the servicing of WM_PAINT; it gives a derived class
+ * opportunity to do some further painting into the window (the outer window,
+ * not the graph window).
+ */
+void WinStatsTimeline::
+additional_window_paint(HDC hdc) {
+ // Draw in the labels for the guide bars.
+ SelectObject(hdc, WinStatsGraph::_monitor->get_font());
+ SetTextAlign(hdc, TA_LEFT | TA_BOTTOM);
+ SetBkMode(hdc, TRANSPARENT);
+
+ int y = _top_margin - 2;
+
+ int num_guide_bars = get_num_guide_bars();
+ for (int i = 0; i < num_guide_bars; ++i) {
+ draw_guide_label(hdc, y, get_guide_bar(i));
+ }
+
+ SetTextColor(hdc, _dark_color);
+ SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP);
+
+ for (const ThreadRow &thread_row : _threads) {
+ draw_thread_label(hdc, thread_row);
+ }
+}
+
+/**
+ * This is called during the servicing of WM_PAINT; it gives a derived class
+ * opportunity to do some further painting into the window (the outer window,
+ * not the graph window).
+ */
+void WinStatsTimeline::
+additional_graph_window_paint(HDC hdc) {
+}
+
+/**
+ * Called when the mouse hovers over the graph, and should return the text that
+ * should appear on the tooltip.
+ */
+std::string WinStatsTimeline::
+get_graph_tooltip(int mouse_x, int mouse_y) const {
+ return PStatTimeline::get_bar_tooltip(pixel_to_row(mouse_y), mouse_x);
+}
+
+/**
+ * Based on the mouse position within the window's client area, look for
+ * draggable things the mouse might be hovering over and return the
+ * apprioprate DragMode enum or DM_none if nothing is indicated.
+ */
+WinStatsGraph::DragMode WinStatsTimeline::
+consider_drag_start(int mouse_x, int mouse_y, int width, int height) {
+ DragMode mode = WinStatsGraph::consider_drag_start(mouse_x, mouse_y, width, height);
+ if (mode == DM_right_margin) {
+ mode = DM_none;
+ }
+ return mode;
+}
+
+/**
+ * Draws the text for the indicated guide bar label at the top of the graph.
+ */
+void WinStatsTimeline::
+draw_guide_label(HDC hdc, int y, const PStatGraph::GuideBar &bar) {
+ const std::string &label = bar._label;
+ if (label.empty()) {
+ return;
+ }
+
+ switch (bar._style) {
+ case GBS_target:
+ SetTextColor(hdc, _light_color);
+ break;
+
+ case GBS_user:
+ SetTextColor(hdc, _user_guide_bar_color);
+ break;
+
+ case GBS_normal:
+ SetTextColor(hdc, _light_color);
+ break;
+
+ case GBS_frame:
+ SetTextColor(hdc, _dark_color);
+ break;
+ }
+
+ int x = timestamp_to_pixel(bar._height);
+ SIZE size;
+ GetTextExtentPoint32(hdc, label.data(), label.length(), &size);
+
+ int this_x = _graph_left + x - size.cx / 2;
+ if (x >= 0 && x < get_xsize()) {
+ TextOut(hdc, this_x, y,
+ label.data(), label.length());
+ }
+}
+
+/**
+ * Draws the text for the indicated thread on the side of the graph.
+ */
+void WinStatsTimeline::
+draw_thread_label(HDC hdc, const ThreadRow &thread_row) {
+ int top = row_to_pixel(thread_row._row_offset + 1);
+ int bottom = row_to_pixel(thread_row._row_offset + 2);
+
+ RECT rect = {_pixel_scale * 2, top, _left_margin - _pixel_scale * 2, bottom};
+ DrawText(hdc, thread_row._label.data(), thread_row._label.size(),
+ &rect, DT_RIGHT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER);
+}
+
+/**
+ * Creates the window for this strip chart.
+ */
+void WinStatsTimeline::
+create_window() {
+ if (_window) {
+ return;
+ }
+
+ HINSTANCE application = GetModuleHandle(nullptr);
+ register_window_class(application);
+
+ POINT window_pos = WinStatsGraph::_monitor->get_new_window_pos();
+
+ RECT win_rect = {
+ 0, 0,
+ _left_margin + get_xsize() + _right_margin,
+ _top_margin + get_ysize() + _bottom_margin
+ };
+
+ // compute window size based on desired client area size
+ AdjustWindowRect(&win_rect, graph_window_style, FALSE);
+
+ _window =
+ CreateWindowEx(WS_EX_DLGMODALFRAME, _window_class_name,
+ "Timeline", graph_window_style,
+ window_pos.x, window_pos.y,
+ win_rect.right - win_rect.left,
+ win_rect.bottom - win_rect.top,
+ WinStatsGraph::_monitor->get_window(), nullptr, application, 0);
+ if (!_window) {
+ nout << "Could not create timeline window!\n";
+ exit(1);
+ }
+
+ SetWindowLongPtr(_window, 0, (LONG_PTR)this);
+
+ // Ensure that the window is on top of the stack.
+ SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+
+ SetFocus(_window);
+}
+
+/**
+ * Registers the window class for the Timeline window, if it has not already
+ * been registered.
+ */
+void WinStatsTimeline::
+register_window_class(HINSTANCE application) {
+ if (_window_class_registered) {
+ return;
+ }
+
+ WNDCLASS wc;
+
+ ZeroMemory(&wc, sizeof(WNDCLASS));
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)static_window_proc;
+ wc.hInstance = application;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = _window_class_name;
+
+ // Reserve space to associate the this pointer with the window.
+ wc.cbWndExtra = sizeof(WinStatsTimeline *);
+
+ if (!RegisterClass(&wc)) {
+ nout << "Could not register Timeline window class!\n";
+ exit(1);
+ }
+
+ _window_class_registered = true;
+}
+
+/**
+ *
+ */
+LONG WINAPI WinStatsTimeline::
+static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+ WinStatsTimeline *self = (WinStatsTimeline *)GetWindowLongPtr(hwnd, 0);
+ if (self != nullptr && self->_window == hwnd) {
+ return self->window_proc(hwnd, msg, wparam, lparam);
+ } else {
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+}
diff --git a/pandatool/src/win-stats/winStatsTimeline.h b/pandatool/src/win-stats/winStatsTimeline.h
new file mode 100644
index 0000000000..61dc07b943
--- /dev/null
+++ b/pandatool/src/win-stats/winStatsTimeline.h
@@ -0,0 +1,89 @@
+/**
+ * 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 winStatsTimeline.h
+ * @author rdb
+ * @date 2022-02-11
+ */
+
+#ifndef WINSTATSTIMELINE_H
+#define WINSTATSTIMELINE_H
+
+#include "pandatoolbase.h"
+
+#include "winStatsGraph.h"
+#include "pStatTimeline.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include
+
+class WinStatsMonitor;
+
+/**
+ * A window that draws all of the start/stop event pairs on each thread on a
+ * horizontal scrolling timeline, with concurrent start/stop pairs stacked
+ * underneath each other.
+ */
+class WinStatsTimeline : public PStatTimeline, public WinStatsGraph {
+public:
+ WinStatsTimeline(WinStatsMonitor *monitor);
+ virtual ~WinStatsTimeline();
+
+ virtual void new_data(int thread_index, int frame_number);
+ virtual void force_redraw();
+ virtual void changed_graph_size(int graph_xsize, int graph_ysize);
+
+protected:
+ virtual void clear_region();
+ virtual void begin_draw();
+ virtual void draw_separator(int row);
+ virtual void draw_guide_bar(int x, GuideBarStyle style);
+ virtual void draw_bar(int row, int from_x, int to_x, int collector_index,
+ const std::string &collector_name);
+ virtual void end_draw();
+ virtual void idle();
+
+ virtual bool animate(double time, double dt);
+
+ LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+ virtual LONG graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+ virtual void additional_window_paint(HDC hdc);
+ virtual void additional_graph_window_paint(HDC hdc);
+ virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
+ virtual DragMode consider_drag_start(int mouse_x, int mouse_y,
+ int width, int height);
+
+private:
+ void draw_guide_label(HDC hdc, int y, const GuideBar &bar);
+ void draw_thread_label(HDC hdc, const ThreadRow &thread_row);
+
+ void create_window();
+ static void register_window_class(HINSTANCE application);
+
+ static LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+ int row_to_pixel(int y) const {
+ return y * _pixel_scale * 5 + _pixel_scale;
+ }
+ int pixel_to_row(int y) const {
+ return (y - _pixel_scale) / (_pixel_scale * 5);
+ }
+
+ static bool _window_class_registered;
+ static const char * const _window_class_name;
+
+ HBRUSH _grid_brush;
+
+ int _highlighted_row = -1;
+ int _highlighted_x = 0;
+ ColorBar _popup_bar;
+};
+
+#endif
diff --git a/pandatool/src/win-stats/winstats_composite1.cxx b/pandatool/src/win-stats/winstats_composite1.cxx
index 3eac02e787..ef63280222 100644
--- a/pandatool/src/win-stats/winstats_composite1.cxx
+++ b/pandatool/src/win-stats/winstats_composite1.cxx
@@ -1,9 +1,11 @@
#include "winStats.cxx"
#include "winStatsChartMenu.cxx"
+#include "winStatsFlameGraph.cxx"
#include "winStatsGraph.cxx"
#include "winStatsLabel.cxx"
#include "winStatsLabelStack.cxx"
#include "winStatsMonitor.cxx"
#include "winStatsPianoRoll.cxx"
+#include "winStatsTimeline.cxx"
#include "winStatsServer.cxx"
#include "winStatsStripChart.cxx"
diff --git a/setup.cfg b/setup.cfg
index 5fadcd1c53..9c2b79edb3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -19,6 +19,7 @@ classifiers =
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
+ Programming Language :: Python :: 3.11
Programming Language :: Python :: Implementation :: CPython
Topic :: Games/Entertainment
Topic :: Multimedia
diff --git a/tests/bullet/test_bullet_bam.py b/tests/bullet/test_bullet_bam.py
index ae20f2d47a..d26a6c4117 100644
--- a/tests/bullet/test_bullet_bam.py
+++ b/tests/bullet/test_bullet_bam.py
@@ -133,6 +133,26 @@ def test_sphere_shape():
assert shape.radius == shape2.radius
+def test_convex_shape():
+ shape = bullet.BulletConvexHullShape()
+ shape.add_array([
+ (-1.0, -1.0, -1.0),
+ (1.0, -1.0, -1.0),
+ (-1.0, 1.0, -1.0),
+ (-1.0, -1.0, 1.0),
+ (1.0, 1.0, -1.0),
+ (1.0, -1.0, 1.0),
+ (-1.0, 1.0, 1.0),
+ (1.0, 1.0, 1.0),
+ ])
+ shape.margin = 0.5
+
+ shape2 = reconstruct(shape)
+
+ assert type(shape) is type(shape2)
+ assert shape.margin == shape2.margin
+
+
def test_ghost():
node = bullet.BulletGhostNode("some ghost node")
diff --git a/tests/event/test_futures.py b/tests/event/test_futures.py
index db09d8c677..13531db55b 100644
--- a/tests/event/test_futures.py
+++ b/tests/event/test_futures.py
@@ -88,6 +88,30 @@ def test_future_wait():
assert fut.result() is None
+@pytest.mark.skipif(not core.Thread.is_threading_supported(),
+ reason="Threading support disabled")
+def test_future_wait_timeout():
+ threading = pytest.importorskip("direct.stdpy.threading")
+
+ fut = core.AsyncFuture()
+
+ # Launch a thread to set the result value.
+ def thread_main():
+ time.sleep(0.001)
+ fut.set_result(None)
+
+ thread = threading.Thread(target=thread_main)
+
+ assert not fut.done()
+ thread.start()
+
+ assert fut.result(1.0) is None
+
+ assert fut.done()
+ assert not fut.cancelled()
+ assert fut.result() is None
+
+
@pytest.mark.skipif(not core.Thread.is_threading_supported(),
reason="Threading support disabled")
def test_future_wait_cancel():
diff --git a/tests/express/test_multifile.py b/tests/express/test_multifile.py
index 8618688eb2..10e0ae6fc0 100644
--- a/tests/express/test_multifile.py
+++ b/tests/express/test_multifile.py
@@ -10,3 +10,16 @@ def test_multifile_read_empty():
assert m.is_read_valid()
assert m.get_num_subfiles() == 0
m.close()
+
+
+def test_multifile_password():
+ m = Multifile()
+
+ m.set_encryption_password('Panda3D rocks!')
+ assert m.get_encryption_password() == 'Panda3D rocks!'
+
+ m.set_encryption_password(b'Panda3D is awesome!')
+ assert m.get_encryption_password() == 'Panda3D is awesome!'
+
+ m.set_encryption_password(b'\xc4\x97\xa1\x01\x85\xb6')
+ assert m.get_encryption_password() == b'\xc4\x97\xa1\x01\x85\xb6'
diff --git a/tests/pgraph/test_pandanode.py b/tests/pgraph/test_pandanode.py
new file mode 100644
index 0000000000..28e684c04b
--- /dev/null
+++ b/tests/pgraph/test_pandanode.py
@@ -0,0 +1,36 @@
+from panda3d.core import PandaNode, TransformState
+
+
+def test_node_prev_transform():
+ identity = TransformState.make_identity()
+ t1 = TransformState.make_pos((1, 0, 0))
+ t2 = TransformState.make_pos((2, 0, 0))
+ t3 = TransformState.make_pos((3, 0, 0))
+
+ node = PandaNode("node")
+ assert node.transform == identity
+ assert node.prev_transform == identity
+ assert not node.has_dirty_prev_transform()
+
+ node.transform = t1
+ assert node.transform == t1
+ assert node.prev_transform == identity
+ assert node.has_dirty_prev_transform()
+
+ node.transform = t2
+ assert node.transform == t2
+ assert node.prev_transform == identity
+ assert node.has_dirty_prev_transform()
+
+ node.reset_prev_transform()
+ assert node.transform == t2
+ assert node.prev_transform == t2
+ assert not node.has_dirty_prev_transform()
+
+ node.transform = t3
+ assert node.prev_transform == t2
+ assert node.has_dirty_prev_transform()
+ PandaNode.reset_all_prev_transform()
+ assert node.transform == t3
+ assert node.prev_transform == t3
+ assert not node.has_dirty_prev_transform()
diff --git a/tests/pipeline/test_condition_var.py b/tests/pipeline/test_condition_var.py
index 4cfc6c226a..b2021418df 100644
--- a/tests/pipeline/test_condition_var.py
+++ b/tests/pipeline/test_condition_var.py
@@ -1,4 +1,4 @@
-from panda3d.core import Mutex, ConditionVarFull
+from panda3d.core import Mutex, ConditionVar
from panda3d import core
from direct.stdpy import thread
import pytest
@@ -14,7 +14,7 @@ def yield_thread():
def test_cvar_notify():
# Just tests that notifying without waiting does no harm.
m = Mutex()
- cv = ConditionVarFull(m)
+ cv = ConditionVar(m)
cv.notify()
cv.notify_all()
@@ -24,7 +24,7 @@ def test_cvar_notify():
def test_cvar_notify_locked():
# Tests the same thing, but with the lock held.
m = Mutex()
- cv = ConditionVarFull(m)
+ cv = ConditionVar(m)
with m:
cv.notify()
@@ -41,7 +41,7 @@ def test_cvar_notify_locked():
def test_cvar_notify_thread(num_threads):
# Tests notify() with some number of threads waiting.
m = Mutex()
- cv = ConditionVarFull(m)
+ cv = ConditionVar(m)
# We prematurely notify, so that we can test that it's not doing anything.
m.acquire()
@@ -98,7 +98,7 @@ def test_cvar_notify_thread(num_threads):
def test_cvar_notify_all_threads(num_threads):
# Tests notify_all() with some number of threads waiting.
m = Mutex()
- cv = ConditionVarFull(m)
+ cv = ConditionVar(m)
# We prematurely notify, so that we can test that it's not doing anything.
m.acquire()