Release Python GIL when possible

This commit is contained in:
nsubiron 2018-10-07 11:50:08 +02:00
parent 5965c3bdbe
commit d56c17a166
18 changed files with 348 additions and 259 deletions

View File

@ -0,0 +1,92 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/NonCopyable.h"
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
# include <boost/python.hpp>
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
namespace carla {
class PythonUtil {
public:
static bool ThisThreadHasTheGIL() {
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
# ifdef PYTHON3X
return PyGILState_Check();
# else
PyThreadState *tstate = _PyThreadState_Current;
return (tstate != nullptr) && (tstate == PyGILState_GetThisThreadState());
# endif // PYTHON3
#else
return false;
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
}
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
/// Acquires a lock on the Python's Global Interpreter Lock, necessary for
/// calling Python code from a different thread.
class AcquireGIL : private NonCopyable {
public:
AcquireGIL() : _state(PyGILState_Ensure()) {}
~AcquireGIL() {
PyGILState_Release(_state);
}
private:
PyGILState_STATE _state;
};
/// Releases the lock on the Python's Global Interpreter Lock, use it when doing
/// blocking I/O operations.
class ReleaseGIL : private NonCopyable {
public:
ReleaseGIL() : _state(PyEval_SaveThread()) {}
~ReleaseGIL() {
PyEval_RestoreThread(_state);
}
private:
PyThreadState *_state;
};
#else // LIBCARLA_WITH_PYTHON_SUPPORT
class AcquireGIL : private NonCopyable {};
class ReleaseGIL : private NonCopyable {};
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
/// A deleter that can be passed to a shared_ptr to release the GIL before
/// destroying the object.
class ReleaseGILDeleter {
public:
template <typename T>
void operator()(T *ptr) const {
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
if (ptr != nullptr && PythonUtil::ThisThreadHasTheGIL()) {
ReleaseGIL lock;
delete ptr;
} else
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
delete ptr;
}
};
};
} // namespace carla

View File

@ -12,11 +12,11 @@
namespace carla {
namespace client {
geom::Location Actor::GetLocation() {
geom::Location Actor::GetLocation() const {
return GetClientImplementation()->GetActorLocation(*this);
}
geom::Transform Actor::GetTransform() {
geom::Transform Actor::GetTransform() const {
return GetClientImplementation()->GetActorTransform(*this);
}

View File

@ -28,9 +28,9 @@ namespace client {
using Super::GetDisplayId;
using Super::GetWorld;
geom::Location GetLocation();
geom::Location GetLocation() const;
geom::Transform GetTransform();
geom::Transform GetTransform() const;
void SetLocation(const geom::Location &location);

View File

@ -1,16 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "carla/client/Client.h"
namespace carla {
namespace client {
Client::Client(const std::string &host, uint16_t port, size_t worker_threads)
: _client_state(MakeShared<detail::Client>(host, port, worker_threads)) {}
} // namespace client
} // namespace carla

View File

@ -6,6 +6,7 @@
#pragma once
#include "carla/PythonUtil.h"
#include "carla/client/detail/Client.h"
namespace carla {
@ -50,5 +51,13 @@ namespace client {
SharedPtr<detail::Client> _client_state;
};
inline Client::Client(
const std::string &host,
uint16_t port,
size_t worker_threads)
: _client_state(
new detail::Client(host, port, worker_threads),
PythonUtil::ReleaseGILDeleter()) {}
} // namespace client
} // namespace carla

View File

@ -130,11 +130,11 @@ namespace detail {
});
}
geom::Location Client::GetActorLocation(Actor &actor) {
geom::Location Client::GetActorLocation(const Actor &actor) {
return _pimpl->CallAndWait<geom::Location>("get_actor_location", actor.Serialize());
}
geom::Transform Client::GetActorTransform(Actor &actor) {
geom::Transform Client::GetActorTransform(const Actor &actor) {
return _pimpl->CallAndWait<geom::Transform>("get_actor_transform", actor.Serialize());
}

View File

@ -76,9 +76,9 @@ namespace detail {
const streaming::Token &token,
std::function<void(SharedPtr<sensor::SensorData>)> callback);
geom::Location GetActorLocation(Actor &actor);
geom::Location GetActorLocation(const Actor &actor);
geom::Transform GetActorTransform(Actor &actor);
geom::Transform GetActorTransform(const Actor &actor);
void SetActorLocation(Actor &actor, const geom::Location &location);

View File

@ -27,7 +27,8 @@ def get_libcarla_extensions():
os.path.join(pwd, 'dependencies/lib/libboost_filesystem.a'),
os.path.join(pwd, 'dependencies/lib', pylib)]
extra_compile_args = [
'-fPIC', '-std=c++14', '-DBOOST_ERROR_CODE_HEADER_ONLY', '-Wno-missing-braces'
'-fPIC', '-std=c++14', '-Wno-missing-braces',
'-DBOOST_ERROR_CODE_HEADER_ONLY', '-DLIBCARLA_WITH_PYTHON_SUPPORT'
]
if 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true':
print('Travis CI build detected: disabling PNG support.')
@ -44,16 +45,19 @@ def get_libcarla_extensions():
raise NotImplementedError
elif os.name == "nt":
pwd = os.path.dirname(os.path.realpath(__file__))
pylib = "libboost_python%d%d-vc141-mt-x64-1_67.lib" % (sys.version_info.major, sys.version_info.minor)
pylib = "libboost_python%d%d-vc141-mt-x64-1_67.lib" % (
sys.version_info.major,
sys.version_info.minor)
extra_link_args = [
'shlwapi.lib',
os.path.join(pwd, 'dependencies/lib/rpc.lib'),
os.path.join(pwd, 'dependencies/lib', pylib)]
# https://docs.microsoft.com/es-es/cpp/porting/modifying-winver-and-win32-winnt
extra_compile_args = ['/DPYTHON3X', '/DBOOST_ALL_NO_LIB', '/DBOOST_PYTHON_STATIC_LIB',
'/DBOOST_ERROR_CODE_HEADER_ONLY', '/D_WIN32_WINNT=0x0501' ]
extra_compile_args = [
'/DPYTHON3X', '/DBOOST_ALL_NO_LIB', '/DBOOST_PYTHON_STATIC_LIB',
'/DBOOST_ERROR_CODE_HEADER_ONLY', '/D_WIN32_WINNT=0x0501',
'/DLIBCARLA_WITH_PYTHON_SUPPORT']
else:
raise NotImplementedError

View File

@ -7,8 +7,6 @@
#include <carla/client/Actor.h>
#include <carla/client/Vehicle.h>
#include <boost/python.hpp>
#include <ostream>
#include <iostream>
@ -30,23 +28,19 @@ void export_actor() {
class_<cc::Actor, boost::noncopyable, boost::shared_ptr<cc::Actor>>("Actor", no_init)
.add_property("id", &cc::Actor::GetId)
.add_property("type_id", +[](const cc::Actor &self) -> std::string {
return self.GetTypeId();
})
.add_property("type_id", +[](const cc::Actor &self) -> std::string { return self.GetTypeId(); })
.add_property("is_alive", &cc::Actor::IsAlive)
.def("get_world", &cc::Actor::GetWorld)
.def("get_location", &cc::Actor::GetLocation)
.def("get_transform", &cc::Actor::GetTransform)
.def("get_location", CONST_CALL_WITHOUT_GIL(cc::Actor, GetLocation))
.def("get_transform", CONST_CALL_WITHOUT_GIL(cc::Actor, GetTransform))
.def("set_location", &cc::Actor::SetLocation, (arg("location")))
.def("set_transform", &cc::Actor::SetTransform, (arg("transform")))
.def("destroy", &cc::Actor::Destroy)
.def("destroy", CALL_WITHOUT_GIL(cc::Actor, Destroy))
.def(self_ns::str(self_ns::self))
;
class_<cc::Vehicle, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Vehicle>>("Vehicle", no_init)
.add_property("control", +[](const cc::Vehicle &self) -> cr::VehicleControl {
return self.GetControl();
})
.add_property("control", CALL_RETURNING_COPY(cc::Vehicle, GetControl))
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
.def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled")=true))
.def(self_ns::str(self_ns::self))

View File

@ -7,7 +7,6 @@
#include <carla/client/BlueprintLibrary.h>
#include <carla/client/ActorBlueprint.h>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <ostream>
@ -124,13 +123,9 @@ void export_blueprint() {
;
class_<cc::ActorAttribute>("ActorAttribute", no_init)
.add_property("id", +[](const cc::ActorAttribute &self) -> std::string {
return self.GetId();
})
.add_property("id", CALL_RETURNING_COPY(cc::ActorAttribute, GetId))
.add_property("type", &cc::ActorAttribute::GetType)
.add_property("recommended_values", +[](const cc::ActorAttribute &self) -> std::vector<std::string> {
return self.GetRecommendedValues();
})
.add_property("recommended_values", CALL_RETURNING_COPY(cc::ActorAttribute, GetRecommendedValues))
.add_property("is_modifiable", &cc::ActorAttribute::IsModifiable)
.def("as_bool", &cc::ActorAttribute::As<bool>)
.def("as_int", &cc::ActorAttribute::As<int>)
@ -158,16 +153,12 @@ void export_blueprint() {
;
class_<cc::ActorBlueprint>("ActorBlueprint", no_init)
.add_property("id", +[](const cc::ActorBlueprint &self) -> std::string {
return self.GetId();
})
.add_property("id", CALL_RETURNING_COPY(cc::ActorBlueprint, GetId))
.add_property("tags", &cc::ActorBlueprint::GetTags)
.def("contains_tag", &cc::ActorBlueprint::ContainsTag)
.def("match_tags", &cc::ActorBlueprint::MatchTags)
.def("contains_attribute", &cc::ActorBlueprint::ContainsAttribute)
.def("get_attribute", +[](const cc::ActorBlueprint &self, const std::string &id) -> cc::ActorAttribute {
return self.GetAttribute(id);
})
.def("get_attribute", CALL_RETURNING_COPY_1(cc::ActorBlueprint, GetAttribute, const std::string &))
.def("set_attribute", &cc::ActorBlueprint::SetAttribute)
.def("__len__", &cc::ActorBlueprint::size)
.def("__iter__", range(&cc::ActorBlueprint::begin, &cc::ActorBlueprint::end))

View File

@ -4,11 +4,10 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/PythonUtil.h>
#include <carla/client/Client.h>
#include <carla/client/World.h>
#include <boost/python.hpp>
static void SetTimeout(carla::client::Client &client, double seconds) {
size_t ms = static_cast<size_t>(1e3 * seconds);
client.SetTimeout(carla::time_duration::milliseconds(ms));
@ -22,8 +21,8 @@ void export_client() {
init<std::string, uint16_t, size_t>((arg("host"), arg("port"), arg("worker_threads")=0u)))
.def("set_timeout", &::SetTimeout, (arg("seconds")))
.def("get_client_version", &cc::Client::GetClientVersion)
.def("get_server_version", &cc::Client::GetServerVersion)
.def("ping", &cc::Client::Ping)
.def("get_server_version", CONST_CALL_WITHOUT_GIL(cc::Client, GetServerVersion))
.def("ping", CONST_CALL_WITHOUT_GIL(cc::Client, Ping))
.def("get_world", &cc::Client::GetWorld)
;
}

View File

@ -6,8 +6,6 @@
#include <carla/rpc/VehicleControl.h>
#include <boost/python.hpp>
#include <ostream>
namespace carla {

View File

@ -7,8 +7,6 @@
#include <rpc/config.h>
#include <rpc/rpc_error.h>
#include <boost/python.hpp>
#include <sstream>
void translator(const rpc::rpc_error &e) {

View File

@ -4,204 +4,34 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/PythonUtil.h>
#include <carla/client/Sensor.h>
#include <carla/image/ImageConverter.h>
#include <carla/image/ImageIO.h>
#include <carla/image/ImageView.h>
#include <carla/pointcloud/PointCloudIO.h>
#include <carla/sensor/SensorData.h>
#include <carla/sensor/data/Image.h>
#include <carla/sensor/data/LidarMeasurement.h>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <ostream>
#include <iostream>
/// Aquires a lock on the Python's Global Interpreter Lock, necessary for
/// calling Python code from a different thread.
///
/// https://wiki.python.org/moin/GlobalInterpreterLock
class GILLockGuard {
public:
GILLockGuard()
: _state(PyGILState_Ensure()) {}
~GILLockGuard() {
PyGILState_Release(_state);
static void SubscribeToStream(carla::client::Sensor &self, boost::python::object callback) {
// Make sure the callback is actually callable.
if (!PyCallable_Check(callback.ptr())) {
PyErr_SetString(PyExc_TypeError, "callback argument must be callable!");
boost::python::throw_error_already_set();
return;
}
private:
PyGILState_STATE _state;
};
namespace carla {
namespace client {
std::ostream &operator<<(std::ostream &out, const Sensor &sensor) {
out << "Sensor(id=" << sensor.GetId() << ", type=" << sensor.GetTypeId() << ')';
return out;
}
} // namespace client
namespace sensor {
namespace data {
std::ostream &operator<<(std::ostream &out, const Image &image) {
out << "Image(frame=" << image.GetFrameNumber()
<< ", size=" << image.GetWidth() << 'x' << image.GetHeight()
<< ')';
return out;
}
std::ostream &operator<<(std::ostream &out, const LidarMeasurement &meas) {
out << "LidarMeasurement(frame=" << meas.GetFrameNumber()
<< ", number_of_points=" << meas.size()
<< ')';
return out;
}
} // namespace data
} // namespace sensor
} // namespace carla
enum class EColorConverter {
None,
Depth,
LogarithmicDepth,
CityScapesPalette
};
template <typename T>
static auto GetRawDataAsBuffer(T &self) {
auto *data = reinterpret_cast<unsigned char *>(self.data());
auto size = sizeof(typename T::value_type) * self.size();
#if PYTHON3X // NOTE(Andrei): python 3
auto *ptr = PyMemoryView_FromMemory(reinterpret_cast<char *>(data), size, PyBUF_READ);
#else // NOTE(Andrei): python 2
auto *ptr = PyBuffer_FromMemory(data, size);
#endif
return boost::python::object(boost::python::handle<>(ptr));
}
template <typename T>
static void ConvertImage(T &self, EColorConverter cc) {
using namespace carla::image;
auto view = ImageView::MakeView(self);
switch (cc) {
case EColorConverter::Depth:
ImageConverter::ConvertInPlace(view, ColorConverter::Depth());
break;
case EColorConverter::LogarithmicDepth:
ImageConverter::ConvertInPlace(view, ColorConverter::LogarithmicDepth());
break;
case EColorConverter::CityScapesPalette:
ImageConverter::ConvertInPlace(view, ColorConverter::CityScapesPalette());
break;
case EColorConverter::None:
break; // ignore.
default:
throw std::invalid_argument("invalid color converter!");
}
}
template <typename T>
static std::string SaveImageToDisk(T &self, std::string path, EColorConverter cc) {
using namespace carla::image;
auto view = ImageView::MakeView(self);
switch (cc) {
case EColorConverter::None:
return ImageIO::WriteView(
std::move(path),
view);
case EColorConverter::Depth:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::Depth()));
case EColorConverter::LogarithmicDepth:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::LogarithmicDepth()));
case EColorConverter::CityScapesPalette:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::CityScapesPalette()));
default:
throw std::invalid_argument("invalid color converter!");
}
}
template <typename T>
static std::string SavePointCloudToDisk(T &self, std::string path) {
return carla::pointcloud::PointCloudIO::SaveToDisk(std::move(path), self.begin(), self.end());
// Subscribe to the sensor.
self.Listen([callback](auto message) {
carla::PythonUtil::AcquireGIL lock;
try {
boost::python::call<void>(callback.ptr(), boost::python::object(message));
} catch (const boost::python::error_already_set &e) {
PyErr_Print();
}
});
}
void export_sensor() {
using namespace boost::python;
namespace cc = carla::client;
namespace cs = carla::sensor;
namespace csd = carla::sensor::data;
class_<cs::SensorData, boost::noncopyable, boost::shared_ptr<cs::SensorData>>("SensorData", no_init)
.add_property("frame_number", &cs::SensorData::GetFrameNumber)
.add_property("transform", +[](const cs::SensorData &self) -> carla::rpc::Transform {
return self.GetSensorTransform();
})
;
enum_<EColorConverter>("ColorConverter")
.value("None", EColorConverter::None)
.value("Depth", EColorConverter::Depth)
.value("LogarithmicDepth", EColorConverter::LogarithmicDepth)
.value("CityScapesPalette", EColorConverter::CityScapesPalette)
;
class_<csd::Image, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::Image>>("Image", no_init)
.add_property("width", &csd::Image::GetWidth)
.add_property("height", &csd::Image::GetHeight)
.add_property("fov", &csd::Image::GetFOVAngle)
.add_property("raw_data", &GetRawDataAsBuffer<csd::Image>)
.def("convert", &ConvertImage<csd::Image>, (arg("color_converter")))
.def("save_to_disk", &SaveImageToDisk<csd::Image>, (arg("path"), arg("color_converter")=EColorConverter::None))
.def("__len__", &csd::Image::size)
.def("__iter__", iterator<csd::Image>())
.def(self_ns::str(self_ns::self))
;
class_<csd::LidarMeasurement, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::LidarMeasurement>>("LidarMeasurement", no_init)
.add_property("horizontal_angle", &csd::LidarMeasurement::GetHorizontalAngle)
.add_property("channels", &csd::LidarMeasurement::GetChannelCount)
.add_property("raw_data", &GetRawDataAsBuffer<csd::LidarMeasurement>)
.def("get_point_count", &csd::LidarMeasurement::GetPointCount, (arg("channel")))
.def("save_to_disk", &SavePointCloudToDisk<csd::LidarMeasurement>, (arg("path")))
.def("__len__", &csd::LidarMeasurement::size)
.def("__iter__", iterator<csd::LidarMeasurement>())
.def(self_ns::str(self_ns::self))
;
class_<cc::Sensor, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Sensor>>("Sensor", no_init)
.def("listen", +[](cc::Sensor &self, boost::python::object callback) {
// Make sure the callback is actually callable.
if (!PyCallable_Check(callback.ptr())) {
PyErr_SetString(PyExc_TypeError, "callback argument must be callable!");
throw_error_already_set();
return;
}
// Subscribe to the sensor.
self.Listen([callback](auto message) { // Here we retrieve the deserialized object.
GILLockGuard gil_lock;
try {
boost::python::call<void>(callback.ptr(), boost::python::object(message));
} catch (const boost::python::error_already_set &e) {
PyErr_Print();
}
});
}, (arg("callback")))
.def("listen", &SubscribeToStream, (arg("callback")))
.def(self_ns::str(self_ns::self))
;
}

View File

@ -0,0 +1,157 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/PythonUtil.h>
#include <carla/image/ImageConverter.h>
#include <carla/image/ImageIO.h>
#include <carla/image/ImageView.h>
#include <carla/pointcloud/PointCloudIO.h>
#include <carla/sensor/SensorData.h>
#include <carla/sensor/data/Image.h>
#include <carla/sensor/data/LidarMeasurement.h>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <ostream>
#include <iostream>
namespace carla {
namespace sensor {
namespace data {
std::ostream &operator<<(std::ostream &out, const Image &image) {
out << "Image(frame=" << image.GetFrameNumber()
<< ", size=" << image.GetWidth() << 'x' << image.GetHeight()
<< ')';
return out;
}
std::ostream &operator<<(std::ostream &out, const LidarMeasurement &meas) {
out << "LidarMeasurement(frame=" << meas.GetFrameNumber()
<< ", number_of_points=" << meas.size()
<< ')';
return out;
}
} // namespace data
} // namespace sensor
} // namespace carla
enum class EColorConverter {
None,
Depth,
LogarithmicDepth,
CityScapesPalette
};
template <typename T>
static auto GetRawDataAsBuffer(T &self) {
auto *data = reinterpret_cast<unsigned char *>(self.data());
auto size = sizeof(typename T::value_type) * self.size();
#if PYTHON3X // NOTE(Andrei): python 3
auto *ptr = PyMemoryView_FromMemory(reinterpret_cast<char *>(data), size, PyBUF_READ);
#else // NOTE(Andrei): python 2
auto *ptr = PyBuffer_FromMemory(data, size);
#endif
return boost::python::object(boost::python::handle<>(ptr));
}
template <typename T>
static void ConvertImage(T &self, EColorConverter cc) {
carla::PythonUtil::ReleaseGIL unlock;
using namespace carla::image;
auto view = ImageView::MakeView(self);
switch (cc) {
case EColorConverter::Depth:
ImageConverter::ConvertInPlace(view, ColorConverter::Depth());
break;
case EColorConverter::LogarithmicDepth:
ImageConverter::ConvertInPlace(view, ColorConverter::LogarithmicDepth());
break;
case EColorConverter::CityScapesPalette:
ImageConverter::ConvertInPlace(view, ColorConverter::CityScapesPalette());
break;
case EColorConverter::None:
break; // ignore.
default:
throw std::invalid_argument("invalid color converter!");
}
}
template <typename T>
static std::string SaveImageToDisk(T &self, std::string path, EColorConverter cc) {
carla::PythonUtil::ReleaseGIL unlock;
using namespace carla::image;
auto view = ImageView::MakeView(self);
switch (cc) {
case EColorConverter::None:
return ImageIO::WriteView(
std::move(path),
view);
case EColorConverter::Depth:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::Depth()));
case EColorConverter::LogarithmicDepth:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::LogarithmicDepth()));
case EColorConverter::CityScapesPalette:
return ImageIO::WriteView(
std::move(path),
ImageView::MakeColorConvertedView(view, ColorConverter::CityScapesPalette()));
default:
throw std::invalid_argument("invalid color converter!");
}
}
template <typename T>
static std::string SavePointCloudToDisk(T &self, std::string path) {
carla::PythonUtil::ReleaseGIL unlock;
return carla::pointcloud::PointCloudIO::SaveToDisk(std::move(path), self.begin(), self.end());
}
void export_sensor_data() {
using namespace boost::python;
namespace cc = carla::client;
namespace cs = carla::sensor;
namespace csd = carla::sensor::data;
class_<cs::SensorData, boost::noncopyable, boost::shared_ptr<cs::SensorData>>("SensorData", no_init)
.add_property("frame_number", &cs::SensorData::GetFrameNumber)
.add_property("transform", CALL_RETURNING_COPY(cs::SensorData, GetSensorTransform))
;
enum_<EColorConverter>("ColorConverter")
.value("None", EColorConverter::None)
.value("Depth", EColorConverter::Depth)
.value("LogarithmicDepth", EColorConverter::LogarithmicDepth)
.value("CityScapesPalette", EColorConverter::CityScapesPalette)
;
class_<csd::Image, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::Image>>("Image", no_init)
.add_property("width", &csd::Image::GetWidth)
.add_property("height", &csd::Image::GetHeight)
.add_property("fov", &csd::Image::GetFOVAngle)
.add_property("raw_data", &GetRawDataAsBuffer<csd::Image>)
.def("convert", &ConvertImage<csd::Image>, (arg("color_converter")))
.def("save_to_disk", &SaveImageToDisk<csd::Image>, (arg("path"), arg("color_converter")=EColorConverter::None))
.def("__len__", &csd::Image::size)
.def("__iter__", iterator<csd::Image>())
.def(self_ns::str(self_ns::self))
;
class_<csd::LidarMeasurement, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::LidarMeasurement>>("LidarMeasurement", no_init)
.add_property("horizontal_angle", &csd::LidarMeasurement::GetHorizontalAngle)
.add_property("channels", &csd::LidarMeasurement::GetChannelCount)
.add_property("raw_data", &GetRawDataAsBuffer<csd::LidarMeasurement>)
.def("get_point_count", &csd::LidarMeasurement::GetPointCount, (arg("channel")))
.def("save_to_disk", &SavePointCloudToDisk<csd::LidarMeasurement>, (arg("path")))
.def("__len__", &csd::LidarMeasurement::size)
.def("__iter__", iterator<csd::LidarMeasurement>())
.def(self_ns::str(self_ns::self))
;
}

View File

@ -6,8 +6,6 @@
#include <carla/geom/Transform.h>
#include <boost/python.hpp>
#include <ostream>
namespace carla {

View File

@ -4,21 +4,31 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/PythonUtil.h>
#include <carla/client/Actor.h>
#include <carla/client/World.h>
#include <boost/python.hpp>
void export_world() {
using namespace boost::python;
namespace cc = carla::client;
namespace cg = carla::geom;
#define SPAWN_ACTOR_WITHOUT_GIL(fn) +[]( \
cc::World &self, \
const cc::ActorBlueprint &blueprint, \
const cg::Transform &transform, \
cc::Actor *parent) { \
carla::PythonUtil::ReleaseGIL unlock; \
return self.fn(blueprint, transform, parent); \
}, \
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>())
class_<cc::World>("World", no_init)
.def("get_blueprint_library", &cc::World::GetBlueprintLibrary)
.def("get_spectator", &cc::World::GetSpectator)
.def("try_spawn_actor", &cc::World::TrySpawnActor,
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>()))
.def("spawn_actor", &cc::World::SpawnActor,
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>()))
.def("get_blueprint_library", CONST_CALL_WITHOUT_GIL(cc::World, GetBlueprintLibrary))
.def("get_spectator", CONST_CALL_WITHOUT_GIL(cc::World, GetSpectator))
.def("try_spawn_actor", SPAWN_ACTOR_WITHOUT_GIL(TrySpawnActor))
.def("spawn_actor", SPAWN_ACTOR_WITHOUT_GIL(SpawnActor))
;
#undef SPAWN_ACTOR_WITHOUT_GIL
}

View File

@ -4,7 +4,30 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <boost/python.hpp>
#include <carla/PythonUtil.h>
#include <type_traits>
// Convenient for requests without arguments.
#define CALL_WITHOUT_GIL(cls, fn) +[](cls &self) { \
carla::PythonUtil::ReleaseGIL unlock; \
return self.fn(); \
}
// Convenient for const requests without arguments.
#define CONST_CALL_WITHOUT_GIL(cls, fn) CALL_WITHOUT_GIL(const cls, fn)
// Convenient for const requests that need to make a copy of the returned value.
#define CALL_RETURNING_COPY(cls, fn) +[](const cls &self) \
-> std::decay_t<std::result_of_t<decltype(&cls::fn)(cls*)>> { \
return self.fn(); \
}
// Convenient for const requests that need to make a copy of the returned value.
#define CALL_RETURNING_COPY_1(cls, fn, _T1) +[](const cls &self, _T1 t1) \
-> std::decay_t<std::result_of_t<decltype(&cls::fn)(cls*, _T1)>> { \
return self.fn(std::forward<_T1>(t1)); \
}
#include "Actor.cpp"
#include "Blueprint.cpp"
@ -12,6 +35,7 @@
#include "Control.cpp"
#include "Exception.cpp"
#include "Sensor.cpp"
#include "SensorData.cpp"
#include "Transform.cpp"
#include "World.cpp"
@ -24,6 +48,7 @@ BOOST_PYTHON_MODULE(libcarla) {
export_blueprint();
export_actor();
export_sensor();
export_sensor_data();
export_world();
export_client();
export_exception();