From 03af644506e67360be620e8ae6b534d89474550d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 2 May 2019 17:38:10 +0200 Subject: [PATCH] python: expose minimum of GL::DefaultFramebuffer. Some non-trivial tricks had to be done in order to expose the GL::defaultFramebuffer variable without causing leaks or double frees. Also, the AbstractFramebuffer has a protected destructor so this needs another special handling. --- src/python/corrade/EnumOperators.h | 45 +++++++++++++++++++++++ src/python/magnum/NonDestructible.h | 55 ++++++++++++++++++++++++++++ src/python/magnum/gl.cpp | 31 ++++++++++++++++ src/python/magnum/test/test_gl.py | 5 +++ src/python/magnum/test/test_gl_gl.py | 5 +++ 5 files changed, 141 insertions(+) create mode 100644 src/python/corrade/EnumOperators.h create mode 100644 src/python/magnum/NonDestructible.h diff --git a/src/python/corrade/EnumOperators.h b/src/python/corrade/EnumOperators.h new file mode 100644 index 0000000..07155f5 --- /dev/null +++ b/src/python/corrade/EnumOperators.h @@ -0,0 +1,45 @@ +#ifndef corrade_EnumOperators_h +#define corrade_EnumOperators_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is 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 Software. + + THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include + +/* Pybind's py::arithmetic() is useless because it doesn't work on + strongly-typed enums with custom operators */ +namespace corrade { + +template void enumOperators(pybind11::enum_& e) { + e + .def("__or__", [](const T& a, const T& b) { return T(typename std::underlying_type::type(a | b)); }) + .def("__and__", [](const T& a, const T& b) { return T(typename std::underlying_type::type(a & b)); }) + .def("__xor__", [](const T& a, const T& b) { return T(typename std::underlying_type::type(a ^ b)); }) + .def("__invert__", [](const T& a) { return T(typename std::underlying_type::type(~a)); }) + .def("__bool__", [](const T& a) { return bool(typename std::underlying_type::type(a)); }); +} + +} + +#endif diff --git a/src/python/magnum/NonDestructible.h b/src/python/magnum/NonDestructible.h new file mode 100644 index 0000000..2e5d1e0 --- /dev/null +++ b/src/python/magnum/NonDestructible.h @@ -0,0 +1,55 @@ +#ifndef magnum_NonDestructible_h +#define magnum_NonDestructible_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is 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 Software. + + THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "magnum/bootstrap.h" + +namespace pybind11 { + template class class_; +} + +/* This is a variant of https://github.com/pybind/pybind11/issues/1178, + implemented on the client side instead of patching pybind itself */ +namespace magnum { + +template struct NonDestructibleBaseDeleter; +template struct NonDestructibleBaseDeleter { + void operator()(T*) { CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +}; +template struct NonDestructibleBaseDeleter { + void operator()(T* ptr) { delete ptr; } +}; + +template using NonDestructible = py::class_::value>>>; + +template using NonDestructibleBase = py::class_::value>>>; + +} + +#endif diff --git a/src/python/magnum/gl.cpp b/src/python/magnum/gl.cpp index ca0afc7..cc15ffa 100644 --- a/src/python/magnum/gl.cpp +++ b/src/python/magnum/gl.cpp @@ -27,9 +27,12 @@ #include #include #include +#include #include "corrade/PyArrayView.h" +#include "corrade/EnumOperators.h" #include "magnum/bootstrap.h" +#include "magnum/NonDestructible.h" namespace magnum { namespace { @@ -152,6 +155,34 @@ void gl(py::module& m) { self.setData(data, usage); }, "Set buffer data", py::arg("data"), py::arg("usage") = GL::BufferUsage::StaticDraw) /** @todo more */; + + /* Framebuffers */ + py::enum_ framebufferClear{m, "FramebufferClear", "Mask for framebuffer clearing"}; + framebufferClear + .value("COLOR", GL::FramebufferClear::Color) + .value("DEPTH", GL::FramebufferClear::Depth) + .value("STENCIL", GL::FramebufferClear::Stencil); + corrade::enumOperators(framebufferClear); + + NonDestructible abstractFramebuffer{m, + "AbstractFramebuffer", "Base for default and named framebuffers"}; + + abstractFramebuffer + /* Using lambdas to avoid method chaining getting into signatures */ + .def("clear", [](GL::AbstractFramebuffer& self, GL::FramebufferClear mask) { + self.clear(mask); + }); + + NonDestructibleBase defaultFramebuffer{m, + "DefaultFramebuffer", "Default framebuffer"}; + + /* An equivalent to this would be + m.attr("default_framebuffer") = &GL::defaultFramebuffer; + (have to use a & to make it choose return_value_policy::reference + instead of return_value_policy::copy), but this is more explicit --- + returning a raw pointer from functions makes pybind wrap it in an + unique_ptr, which would cause double-free / memory corruption later */ + py::setattr(m, "default_framebuffer", py::cast(GL::defaultFramebuffer, py::return_value_policy::reference)); } }} diff --git a/src/python/magnum/test/test_gl.py b/src/python/magnum/test/test_gl.py index 719ca6a..25d2d23 100644 --- a/src/python/magnum/test/test_gl.py +++ b/src/python/magnum/test/test_gl.py @@ -34,3 +34,8 @@ class Attribute(unittest.TestCase): self.assertEqual(a.location, 2) self.assertEqual(a.components, gl.Attribute.Components.TWO) self.assertEqual(a.data_type, gl.Attribute.DataType.FLOAT) + +class FramebufferClear(unittest.TestCase): + def test_ops(self): + self.assertEqual(gl.FramebufferClear.COLOR|gl.FramebufferClear.COLOR, gl.FramebufferClear.COLOR) + self.assertFalse(gl.FramebufferClear.COLOR & ~gl.FramebufferClear.COLOR) diff --git a/src/python/magnum/test/test_gl_gl.py b/src/python/magnum/test/test_gl_gl.py index 092936a..0d4fc4f 100644 --- a/src/python/magnum/test/test_gl_gl.py +++ b/src/python/magnum/test/test_gl_gl.py @@ -44,3 +44,8 @@ class Buffer(GLTestCase): def test_set_data(self): a = gl.Buffer() a.set_data(b'hello', gl.BufferUsage.STATIC_DRAW) + +class DefaultFramebuffer(GLTestCase): + def test(self): + # Using it should not crash, leak or cause double-free issues + self.assertTrue(gl.default_framebuffer is not None)