From ff356e0fe79c5e1650e3346ff96775b02d88b890 Mon Sep 17 00:00:00 2001 From: Alex Fuller Date: Wed, 5 Mar 2025 17:31:06 +1100 Subject: [PATCH] IECore{Python}|{VDB} : Added boost::python and nanobind conversion. Python bindings renamed from pyopenvdb to openvdb in later versions --- Changes | 14 +++ SConstruct | 27 +++++- include/IECorePython/NanoBindConverter.h | 103 +++++++++++++++++++++ python/IECoreVDB/__init__.py | 5 +- src/IECoreVDB/bindings/IECoreVDBModule.cpp | 16 +++- 5 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 include/IECorePython/NanoBindConverter.h diff --git a/Changes b/Changes index 1afa9a3aac..4f573c4c98 100644 --- a/Changes +++ b/Changes @@ -1,7 +1,21 @@ 10.5.x.x (relative to 10.5.13.0) ======== +Features +-------- + +- NanoBindConverter : Added new class providing interoperability between Boost Python bindings and NanoBind bindings. + +Improvements +------------ +- IEcoreVDB : Support for the renamed python module `openvdb` in more recent versions of OpenVDB. + +Build +----- + +- SConstruct : + - Added `NANOBIND_INCLUDE_PATH` option. 10.5.13.0 (relative to 10.5.12.0) ========= diff --git a/SConstruct b/SConstruct index 93047695a1..a9dc2b8ccd 100644 --- a/SConstruct +++ b/SConstruct @@ -570,6 +570,18 @@ o.Add( "", ) +o.Add( + "NANOBIND_INCLUDE_PATH", + "The path to the nanobind include directory.", + "", +) + +o.Add( + "NANOBIND_LIB_PATH", + "The path to the nanobind lib directory.", + "", +) + # Build options o.Add( @@ -2069,11 +2081,22 @@ vdbEnvPrepends = { "LIBS" : ["openvdb$VDB_LIB_SUFFIX"], "CXXFLAGS" : [ systemIncludeArgument, "$VDB_INCLUDE_PATH", - systemIncludeArgument, "$PYBIND11_INCLUDE_PATH", ] } -vdbEnv.Prepend( **vdbEnvPrepends) +if env[ "PYBIND11_INCLUDE_PATH" ] != "" : + vdbEnvPrepends["CXXFLAGS"].append( [ systemIncludeArgument, "$PYBIND11_INCLUDE_PATH" ] ) + +if env[ "NANOBIND_INCLUDE_PATH" ] != "" : + vdbEnvPrepends["CXXFLAGS"].append( [ systemIncludeArgument, "$NANOBIND_INCLUDE_PATH" ] ) + +vdbEnv.Prepend( **vdbEnvPrepends ) + +if env[ "NANOBIND_LIB_PATH" ] != "" : + vdbEnvPrepends["LIBPATH"].append( "$NANOBIND_LIB_PATH" ) + +if env[ "NANOBIND_INCLUDE_PATH" ] != "" : + vdbEnvPrepends["LIBS"].append( "nanobind-static" ) vdbPythonModuleEnv = corePythonModuleEnv.Clone( **vdbEnvSets ) vdbPythonModuleEnv.Prepend( **vdbEnvPrepends ) diff --git a/include/IECorePython/NanoBindConverter.h b/include/IECorePython/NanoBindConverter.h new file mode 100644 index 0000000000..341eef305c --- /dev/null +++ b/include/IECorePython/NanoBindConverter.h @@ -0,0 +1,103 @@ + +////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2025, Alex Fuller. All rights reserved. +// Copyright (c) 2024, Cinesite VFX Ltd. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided with +// the distribution. +// +// * Neither the name of John Haddon nor the names of +// any other contributors to this software may be used to endorse or +// promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////// + +#ifndef IECOREPYTHON_NANOBINDCONVERTER_H +#define IECOREPYTHON_NANOBINDCONVERTER_H + +#include "boost/python.hpp" + +#include "nanobind/nanobind.h" + +namespace IECorePython +{ + +// Registers `boost::python` converters for types +// wrapped using nanobind. +template +struct NanoBindConverter +{ + + static void registerConverters() + { + boost::python::to_python_converter(); + boost::python::converter::registry::push_back( + &FromNanoBind::convertible, + &FromNanoBind::construct, + boost::python::type_id() + ); + } + + private : + + struct ToNanoBind + { + static PyObject *convert( const T &t ) + { + nanobind::object o = nanobind::cast( t ); + Py_INCREF( o.ptr() ); + return o.ptr(); + } + }; + + struct FromNanoBind + { + + static void *convertible( PyObject *object ) + { + nanobind::handle handle( object ); + return nanobind::cast( handle ) ? object : nullptr; + } + + static void construct( PyObject *object, boost::python::converter::rvalue_from_python_stage1_data *data ) + { + void *storage = ( ( boost::python::converter::rvalue_from_python_storage * ) data )->storage.bytes; + T *t = new( storage ) T; + data->convertible = storage; + + nanobind::handle handle( object ); + *t = nanobind::cast( handle ); + } + + }; + +}; + +} // namespace IECorePython + +#endif // IECOREPYTHON_NANOBINDCONVERTER_H + diff --git a/python/IECoreVDB/__init__.py b/python/IECoreVDB/__init__.py index af2a8048ef..4383fcd44e 100644 --- a/python/IECoreVDB/__init__.py +++ b/python/IECoreVDB/__init__.py @@ -41,7 +41,10 @@ # we use the warnings module to suppress these during the import with warnings.catch_warnings(): warnings.simplefilter("ignore") - import pyopenvdb + try: + import pyopenvdb + except: + import openvdb from ._IECoreVDB import * diff --git a/src/IECoreVDB/bindings/IECoreVDBModule.cpp b/src/IECoreVDB/bindings/IECoreVDBModule.cpp index 6f15505794..a333998a31 100644 --- a/src/IECoreVDB/bindings/IECoreVDBModule.cpp +++ b/src/IECoreVDB/bindings/IECoreVDBModule.cpp @@ -44,15 +44,19 @@ // OpenVDB 10.0 and earlier used `boost::python` for its Python bindings, but // this was switched to PyBind11 in OpenVDB 10.1. We need to take a different // approach to binding VDBObject's grid accessors in each case. -#if OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER > 10 || OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER == 10 && OPENVDB_LIBRARY_MINOR_VERSION_NUMBER >= 1 +// For OpenVDB 12 they yet again changed bindings to nanobind instead. +#if OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER == 11 || OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER == 10 && OPENVDB_LIBRARY_MINOR_VERSION_NUMBER >= 1 #include "IECorePython/PyBindConverter.h" #define IECOREVDB_USE_PYBIND +#elif OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER > 11 +#include "IECorePython/NanoBindConverter.h" +#define IECOREVDB_USE_NANOBIND #endif using namespace boost::python; using namespace IECoreVDB; -#ifndef IECOREVDB_USE_PYBIND +#if !defined( IECOREVDB_USE_PYBIND ) && !defined( IECOREVDB_USE_NANOBIND ) namespace { @@ -148,7 +152,7 @@ void insertGrid( VDBObject::Ptr vdbObject, boost::python::object pyObject ) } // namespace -#endif // #ifndef IECOREVDB_USE_PYBIND +#endif // #if !defined( IECOREVDB_USE_PYBIND ) && !defined( IECOREVDB_USE_NANOBIND ) namespace { @@ -169,8 +173,10 @@ boost::python::list gridNames( VDBObject::Ptr vdbObject ) BOOST_PYTHON_MODULE( _IECoreVDB ) { -#ifdef IECOREVDB_USE_PYBIND +#if defined( IECOREVDB_USE_PYBIND ) IECorePython::PyBindConverter::registerConverters(); +#elif defined( IECOREVDB_USE_NANOBIND ) + IECorePython::NanoBindConverter::registerConverters(); #endif IECorePython::RunTimeTypedClass() @@ -179,7 +185,7 @@ BOOST_PYTHON_MODULE( _IECoreVDB ) .def("gridNames", &::gridNames) .def("metadata", &VDBObject::metadata) .def("removeGrid", &VDBObject::removeGrid) -#ifdef IECOREVDB_USE_PYBIND +#if defined( IECOREVDB_USE_PYBIND ) || defined( IECOREVDB_USE_NANOBIND ) .def( "findGrid", (openvdb::GridBase::Ptr (VDBObject::*)( const std::string &name ))&VDBObject::findGrid ) .def( "insertGrid", &VDBObject::insertGrid ) #else