Browse Source

python: move refcounting in GL types also to holders.

Much cleaner now, yay.
pull/2/head
Vladimír Vondruš 7 years ago
parent
commit
c28f36e04a
  1. 6
      src/Magnum/CMakeLists.txt
  2. 30
      src/Magnum/GL/CMakeLists.txt
  3. 40
      src/Magnum/GL/Python.h
  4. 45
      src/python/magnum/gl.cpp
  5. 5
      src/python/magnum/meshtools.cpp

6
src/Magnum/CMakeLists.txt

@ -29,7 +29,11 @@ if(WITH_PYTHON)
install(FILES Python.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}) install(FILES Python.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR})
endif() endif()
find_package(Magnum COMPONENTS SceneGraph) find_package(Magnum COMPONENTS GL SceneGraph)
if(Magnum_GL_FOUND)
add_subdirectory(GL)
endif()
if(Magnum_SceneGraph_FOUND) if(Magnum_SceneGraph_FOUND)
add_subdirectory(SceneGraph) add_subdirectory(SceneGraph)

30
src/Magnum/GL/CMakeLists.txt

@ -0,0 +1,30 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
# Vladimír Vondruš <mosra@centrum.cz>
#
# 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.
#
if(WITH_PYTHON)
add_custom_target(MagnumGLPython SOURCES Python.h)
set_target_properties(MagnumGLPython PROPERTIES FOLDER "Magnum/Python")
install(FILES Python.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/GL)
endif()

40
src/python/magnum/PyGL.h → src/Magnum/GL/Python.h

@ -1,5 +1,5 @@
#ifndef magnum_PyGL_h #ifndef Magnum_GL_Python_h
#define magnum_PyGL_h #define Magnum_GL_Python_h
/* /*
This file is part of Magnum. This file is part of Magnum.
@ -25,29 +25,37 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <pybind11/pybind11.h> #include <memory> /* :( */
#include <vector> #include <vector>
#include <Magnum/GL/Mesh.h> #include <pybind11/pybind11.h>
#include <Magnum/GL/Framebuffer.h> #include <Magnum/GL/GL.h>
#include "Magnum/Python.h"
#include "magnum/bootstrap.h" namespace Magnum { namespace GL {
namespace magnum { /* Stores additional stuff needed for proper refcounting of buffers owned by
a mesh. For some reason it *has to be* templated, otherwise
PYBIND11_DECLARE_HOLDER_TYPE doesn't work. Ugh. */
template<class T> struct PyMeshHolder: std::unique_ptr<T> {
static_assert(std::is_same<T, GL::Mesh>::value, "mesh holder has to hold a mesh");
struct PyMesh: GL::Mesh { explicit PyMeshHolder(T* object): std::unique_ptr<T>{object} {}
explicit PyMesh(GL::MeshPrimitive primitive): GL::Mesh(primitive) {}
explicit PyMesh(MeshPrimitive primitive): GL::Mesh(primitive) {}
explicit PyMesh(GL::Mesh&& mesh): GL::Mesh(std::move(mesh)) {}
std::vector<py::object> buffers; std::vector<pybind11::object> buffers;
}; };
struct PyFramebuffer: GL::Framebuffer { template<class T> struct PyFramebufferHolder: std::unique_ptr<T, PyNonDestructibleBaseDeleter<T, std::is_destructible<T>::value>> {
explicit PyFramebuffer(const Range2Di& viewport): GL::Framebuffer{viewport} {} static_assert(std::is_same<T, GL::Framebuffer>::value, "framebuffer holder has to hold a framebuffer");
std::vector<py::object> attached; explicit PyFramebufferHolder(T* object): std::unique_ptr<T, PyNonDestructibleBaseDeleter<T, std::is_destructible<T>::value>>{object} {}
std::vector<pybind11::object> attachments;
}; };
} }}
PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::GL::PyMeshHolder<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::GL::PyFramebufferHolder<T>)
#endif #endif

45
src/python/magnum/gl.cpp

@ -41,10 +41,23 @@
#include "Corrade/Python.h" #include "Corrade/Python.h"
#include "Magnum/Python.h" #include "Magnum/Python.h"
#include "Magnum/GL/Python.h"
#include "corrade/EnumOperators.h" #include "corrade/EnumOperators.h"
#include "magnum/bootstrap.h" #include "magnum/bootstrap.h"
#include "magnum/PyGL.h"
namespace magnum { namespace {
/* Otherwise pybind yells that `generic_type: type "Framebuffer" has a
non-default holder type while its base "Magnum::GL::AbstractFramebuffer"
does not` -- we're using PyFramebufferHolder for it */
template<class T> struct NonDefaultFramebufferHolder: std::unique_ptr<T, PyNonDestructibleBaseDeleter<T, std::is_destructible<T>::value>> {
explicit NonDefaultFramebufferHolder(T* object): std::unique_ptr<T, PyNonDestructibleBaseDeleter<T, std::is_destructible<T>::value>>{object} {}
};
}}
PYBIND11_DECLARE_HOLDER_TYPE(T, magnum::NonDefaultFramebufferHolder<T>)
namespace magnum { namespace magnum {
@ -290,7 +303,7 @@ void gl(py::module& m) {
.value("STENCIL", GL::FramebufferClear::Stencil); .value("STENCIL", GL::FramebufferClear::Stencil);
corrade::enumOperators(framebufferClear); corrade::enumOperators(framebufferClear);
PyNonDestructibleClass<GL::AbstractFramebuffer> abstractFramebuffer{m, py::class_<GL::AbstractFramebuffer, NonDefaultFramebufferHolder<GL::AbstractFramebuffer>> abstractFramebuffer{m,
"AbstractFramebuffer", "Base for default and named framebuffers"}; "AbstractFramebuffer", "Base for default and named framebuffers"};
abstractFramebuffer abstractFramebuffer
@ -301,10 +314,10 @@ void gl(py::module& m) {
.def("read", static_cast<void(GL::AbstractFramebuffer::*)(const Range2Di&, const MutableImageView2D&)>(&GL::AbstractFramebuffer::read), .def("read", static_cast<void(GL::AbstractFramebuffer::*)(const Range2Di&, const MutableImageView2D&)>(&GL::AbstractFramebuffer::read),
"Read block of pixels from the framebuffer to an image view"); "Read block of pixels from the framebuffer to an image view");
PyNonDestructibleClass<GL::DefaultFramebuffer, GL::AbstractFramebuffer> defaultFramebuffer{m, py::class_<GL::DefaultFramebuffer, GL::AbstractFramebuffer, NonDefaultFramebufferHolder<GL::DefaultFramebuffer>> defaultFramebuffer{m,
"DefaultFramebuffer", "Default framebuffer"}; "DefaultFramebuffer", "Default framebuffer"};
PyNonDestructibleClass<PyFramebuffer, GL::AbstractFramebuffer> framebuffer{m, py::class_<GL::Framebuffer, GL::AbstractFramebuffer, GL::PyFramebufferHolder<GL::Framebuffer>> framebuffer{m,
"Framebuffer", "Framebuffer"}; "Framebuffer", "Framebuffer"};
py::class_<GL::Framebuffer::ColorAttachment>{framebuffer, "ColorAttachment", "Color attachment"} py::class_<GL::Framebuffer::ColorAttachment>{framebuffer, "ColorAttachment", "Color attachment"}
@ -324,15 +337,17 @@ void gl(py::module& m) {
framebuffer framebuffer
.def(py::init<const Range2Di&>(), "Constructor") .def(py::init<const Range2Di&>(), "Constructor")
.def_property_readonly("id", &GL::Framebuffer::id, "OpenGL framebuffer ID") .def_property_readonly("id", &GL::Framebuffer::id, "OpenGL framebuffer ID")
.def("attach_renderbuffer", [](PyFramebuffer& self, GL::Framebuffer::BufferAttachment attachment, GL::Renderbuffer& renderbuffer) { .def("attach_renderbuffer", [](GL::Framebuffer& self, GL::Framebuffer::BufferAttachment attachment, GL::Renderbuffer& renderbuffer) {
self.attachRenderbuffer(attachment, renderbuffer); self.attachRenderbuffer(attachment, renderbuffer);
/* Keep a reference to the renderbuffer to avoid it being deleted /* Keep a reference to the renderbuffer to avoid it being deleted
before the framebuffer */ before the framebuffer */
self.attached.emplace_back(pyObjectFromInstance(renderbuffer)); pyObjectHolderFor<GL::PyFramebufferHolder>(self).attachments.emplace_back(pyObjectFromInstance(renderbuffer));
}, "Attach renderbuffer to given buffer") }, "Attach renderbuffer to given buffer")
.def_readonly("attached", &PyFramebuffer::attached, "Renderbuffer and texture objects referenced by the framebuffer"); .def_property_readonly("attached", [](GL::Framebuffer& self) {
return pyObjectHolderFor<GL::PyFramebufferHolder>(self).attachments;
}, "Renderbuffer and texture objects referenced by the framebuffer");
/* An equivalent to this would be /* An equivalent to this would be
m.attr("default_framebuffer") = &GL::defaultFramebuffer; m.attr("default_framebuffer") = &GL::defaultFramebuffer;
@ -362,12 +377,12 @@ void gl(py::module& m) {
#endif #endif
; ;
py::class_<PyMesh>{m, "Mesh", "Mesh"} py::class_<GL::Mesh, GL::PyMeshHolder<GL::Mesh>>{m, "Mesh", "Mesh"}
.def(py::init<GL::MeshPrimitive>(), "Constructor", py::arg("primitive") = GL::MeshPrimitive::Triangles) .def(py::init<GL::MeshPrimitive>(), "Constructor", py::arg("primitive") = GL::MeshPrimitive::Triangles)
.def(py::init<MeshPrimitive>(), "Constructor") .def(py::init<MeshPrimitive>(), "Constructor")
.def_property_readonly("id", &GL::Mesh::id, "OpenGL vertex array ID") .def_property_readonly("id", &GL::Mesh::id, "OpenGL vertex array ID")
.def_property("primitive", &GL::Mesh::primitive, .def_property("primitive", &GL::Mesh::primitive,
[](PyMesh& self, py::object primitive) { [](GL::Mesh& self, py::object primitive) {
if(py::isinstance<MeshPrimitive>(primitive)) if(py::isinstance<MeshPrimitive>(primitive))
self.setPrimitive(py::cast<MeshPrimitive>(primitive)); self.setPrimitive(py::cast<MeshPrimitive>(primitive));
else if(py::isinstance<GL::MeshPrimitive>(primitive)) else if(py::isinstance<GL::MeshPrimitive>(primitive))
@ -376,25 +391,27 @@ void gl(py::module& m) {
}, "Primitive type") }, "Primitive type")
/* Have to use a lambda because it returns GL::Mesh which is not /* Have to use a lambda because it returns GL::Mesh which is not
tracked (unlike PyMesh) */ tracked (unlike PyMesh) */
.def_property("count", &GL::Mesh::count, [](PyMesh& self, UnsignedInt count) { .def_property("count", &GL::Mesh::count, [](GL::Mesh& self, UnsignedInt count) {
self.setCount(count); self.setCount(count);
}, "Vertex/index count") }, "Vertex/index count")
/* Using lambdas to avoid method chaining getting into signatures */ /* Using lambdas to avoid method chaining getting into signatures */
.def("add_vertex_buffer", [](PyMesh& self, GL::Buffer& buffer, GLintptr offset, GLsizei stride, const GL::DynamicAttribute& attribute) { .def("add_vertex_buffer", [](GL::Mesh& self, GL::Buffer& buffer, GLintptr offset, GLsizei stride, const GL::DynamicAttribute& attribute) {
self.addVertexBuffer(buffer, offset, stride, attribute); self.addVertexBuffer(buffer, offset, stride, attribute);
/* Keep a reference to the buffer to avoid it being deleted before /* Keep a reference to the buffer to avoid it being deleted before
the mesh */ the mesh */
self.buffers.emplace_back(pyObjectFromInstance(buffer)); pyObjectHolderFor<GL::PyMeshHolder>(self).buffers.emplace_back(pyObjectFromInstance(buffer));
}, "Add vertex buffer", py::arg("buffer"), py::arg("offset"), py::arg("stride"), py::arg("attribute")) }, "Add vertex buffer", py::arg("buffer"), py::arg("offset"), py::arg("stride"), py::arg("attribute"))
.def("draw", [](PyMesh& self, GL::AbstractShaderProgram& shader) { .def("draw", [](GL::Mesh& self, GL::AbstractShaderProgram& shader) {
self.draw(shader); self.draw(shader);
}, "Draw the mesh") }, "Draw the mesh")
/** @todo more */ /** @todo more */
.def_readonly("buffers", &PyMesh::buffers, "Buffer objects referenced by the mesh"); .def_property_readonly("buffers", [](GL::Mesh& self) {
return pyObjectHolderFor<GL::PyMeshHolder>(self).buffers;
}, "Buffer objects referenced by the mesh");
/* Renderer */ /* Renderer */
{ {

5
src/python/magnum/meshtools.cpp

@ -30,7 +30,6 @@
#include <Magnum/Trade/MeshData3D.h> #include <Magnum/Trade/MeshData3D.h>
#include "magnum/bootstrap.h" #include "magnum/bootstrap.h"
#include "magnum/PyGL.h"
namespace magnum { namespace magnum {
@ -46,10 +45,10 @@ void meshtools(py::module& m) {
m m
.def("compile", [](const Trade::MeshData2D& data) { .def("compile", [](const Trade::MeshData2D& data) {
return PyMesh{MeshTools::compile(data)}; return MeshTools::compile(data);
}, "Compile 2D mesh data") }, "Compile 2D mesh data")
.def("compile", [](const Trade::MeshData3D& data) { .def("compile", [](const Trade::MeshData3D& data) {
return PyMesh{MeshTools::compile(data)}; return MeshTools::compile(data);
}, "Compile 3D mesh data"); }, "Compile 3D mesh data");
} }

Loading…
Cancel
Save