From 4c3e02c8db96b60472030b383bd7bd3f7a607db0 Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Fri, 7 Feb 2020 14:42:41 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20PythonSink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/controller.cpp | 9 +-- src/controller/controller.h | 2 +- src/controller/controller_pythonembedded.cpp | 11 +++- src/controller/controller_pythonembedded.h | 1 + src/controller/geometriccalibrator.h | 2 +- src/controller/python/python_sink.cpp | 61 +++++++++++-------- src/controller/python/python_sink.h | 23 +++---- src/graphics/filter.cpp | 20 +++++- src/graphics/filter.h | 7 +++ src/graphics/framebuffer.h | 6 ++ src/sink/sink.cpp | 20 ++++-- tests/CMakeLists.txt | 2 +- tests/integration_tests/integrationTests.json | 4 -- .../test_cases/test_wrapped_sink.py | 3 +- 14 files changed, 112 insertions(+), 59 deletions(-) diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index 798b67fd..ed4236ea 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -21,7 +21,7 @@ shared_ptr ControllerObject::getObjectPtr(const string& name) const } /*************/ -bool ControllerObject::checkObject(const std::string& name) const +bool ControllerObject::checkObjectExists(const std::string& name) const { auto objects = getObjectList(); if (std::find(objects.cbegin(), objects.cend(), name) != objects.cend()) @@ -349,9 +349,10 @@ void ControllerObject::setInScene(const string& name, const Values& values) cons { auto tree = _root->getTree(); auto attrPath = "/" + _root->getName() + "/attributes/" + name; - if (!tree->hasLeafAt(attrPath)) - return; - tree->setValueForLeafAt(attrPath, values); + if (tree->hasLeafAt(attrPath)) + tree->setValueForLeafAt(attrPath, values); + else + _root->addTreeCommand(_root->getName(), RootObject::Command::callRoot, {name, values}); } /*************/ diff --git a/src/controller/controller.h b/src/controller/controller.h index 37cbb5a0..04100162 100644 --- a/src/controller/controller.h +++ b/src/controller/controller.h @@ -62,7 +62,7 @@ class ControllerObject : public GraphObject * \param name Object name * \return Return true if the object exists, false otherwise */ - bool checkObject(const std::string& name) const; + bool checkObjectExists(const std::string& name) const; /** * Get a ptr to the named object diff --git a/src/controller/controller_pythonembedded.cpp b/src/controller/controller_pythonembedded.cpp index 4b2fb09a..4fb2d47e 100644 --- a/src/controller/controller_pythonembedded.cpp +++ b/src/controller/controller_pythonembedded.cpp @@ -17,13 +17,20 @@ namespace Splash atomic_int PythonEmbedded::_pythonInstances{0}; PyThreadState* PythonEmbedded::_pythonGlobalThreadState{nullptr}; PyObject* PythonEmbedded::SplashError{nullptr}; +std::string PythonEmbedded::_capsuleName{"splash._splash"}; /*******************/ // Embedded Python // /*******************/ PythonEmbedded* PythonEmbedded::getInstance() { - auto that = static_cast(PyCapsule_Import("splash._splash", 0)); + auto that = static_cast(PyCapsule_Import(_capsuleName.c_str(), 0)); + if (!that) + { + PyErr_SetString(SplashError, "Could not load Splash capsule"); + return nullptr; + } + if (!that->_pythonModule) { // If _pythonModule is not set, it is most certainly because the Splash Python method @@ -1214,7 +1221,7 @@ void PythonEmbedded::loop() // Set the current instance in a capsule auto module = PyImport_ImportModule("splash"); - auto capsule = PyCapsule_New((void*)this, "splash._splash", nullptr); + auto capsule = PyCapsule_New((void*)this, _capsuleName.c_str(), nullptr); PyDict_SetItemString(PyModule_GetDict(module), "_splash", capsule); Py_DECREF(capsule); diff --git a/src/controller/controller_pythonembedded.h b/src/controller/controller_pythonembedded.h index 32cf42c5..0138457f 100644 --- a/src/controller/controller_pythonembedded.h +++ b/src/controller/controller_pythonembedded.h @@ -84,6 +84,7 @@ class PythonEmbedded : public ControllerObject void stop(); private: + static std::string _capsuleName; std::string _filepath{""}; //!< Path to the python script std::string _scriptName{""}; //!< Name of the module (filename minus .py) Values _pythonArgs{}; //!< Command line arguments to send to the scripts diff --git a/src/controller/geometriccalibrator.h b/src/controller/geometriccalibrator.h index 98342517..31f71056 100644 --- a/src/controller/geometriccalibrator.h +++ b/src/controller/geometriccalibrator.h @@ -172,7 +172,7 @@ class GeometricCalibrator : public ControllerObject */ void waitForObjectCreation(const std::string& name) { - while (!checkObject(name)) + while (!checkObjectExists(name)) std::this_thread::sleep_for(15ms); } diff --git a/src/controller/python/python_sink.cpp b/src/controller/python/python_sink.cpp index 4bc916dd..887e288e 100644 --- a/src/controller/python/python_sink.cpp +++ b/src/controller/python/python_sink.cpp @@ -26,8 +26,8 @@ void PythonSink::pythonSinkDealloc(PythonSinkObject* self) if (that) { - that->setInScene("deleteObject", {self->sinkName}); - that->setInScene("deleteObject", {self->filterName}); + that->setInScene("deleteObject", {*self->sinkName}); + that->setInScene("deleteObject", {*self->filterName}); } Py_XDECREF(self->lastBuffer); @@ -69,17 +69,21 @@ int PythonSink::pythonSinkInit(PythonSinkObject* self, PyObject* args, PyObject* self->width = width; self->height = height; + self->keepRatio = false; self->framerate = 30; + self->linked = false; + self->opened = false; + self->lastBuffer = nullptr; auto index = self->sinkIndex.fetch_add(1); - self->sinkName = that->getName() + "_pythonsink_" + to_string(index); - that->setInScene("addObject", {"sink", self->sinkName, root->getName()}); + self->sinkName = make_unique(that->getName() + "_pythonsink_" + to_string(index)); + that->setInScene("addObject", {"sink", *self->sinkName, root->getName()}); // Wait until the sink is created int triesLeft = SPLASH_PYTHON_MAX_TRIES; while (!self->sink && --triesLeft) { - self->sink = dynamic_pointer_cast(root->getObject(self->sinkName)); + self->sink = dynamic_pointer_cast(root->getObject(*self->sinkName)); this_thread::sleep_for(chrono::milliseconds(5)); } @@ -116,6 +120,8 @@ PyDoc_STRVAR(pythonSinkLink_doc__, PyObject* PythonSink::pythonSinkLink(PythonSinkObject* self, PyObject* args, PyObject* kwds) { + assert(self != nullptr); + auto that = PythonEmbedded::getInstance(); if (!that) { @@ -131,7 +137,7 @@ PyObject* PythonSink::pythonSinkLink(PythonSinkObject* self, PyObject* args, PyO } if (!self->sink) - self->sink = dynamic_pointer_cast(root->getObject(self->sinkName)); + self->sink = dynamic_pointer_cast(root->getObject(*self->sinkName)); if (!self->sink) { @@ -156,7 +162,7 @@ PyObject* PythonSink::pythonSinkLink(PythonSinkObject* self, PyObject* args, PyO if (source) { - self->sourceName = string(source); + self->sourceName = make_unique(source); } else { @@ -166,7 +172,7 @@ PyObject* PythonSink::pythonSinkLink(PythonSinkObject* self, PyObject* args, PyO } auto objects = that->getObjectList(); - auto objectIt = std::find(objects.begin(), objects.end(), self->sourceName); + auto objectIt = std::find(objects.begin(), objects.end(), *self->sourceName); if (objectIt == objects.end()) { PyErr_Warn(PyExc_Warning, "The specified source object does not exist"); @@ -174,14 +180,17 @@ PyObject* PythonSink::pythonSinkLink(PythonSinkObject* self, PyObject* args, PyO return Py_False; } - self->filterName = self->sinkName + "_filter_" + self->sourceName; + self->filterName = make_unique(*self->sinkName + "_filter_" + *self->sourceName); // Filter is added locally, we don't need (nor want) it in any other Scene - that->setInScene("addObject", {"filter", self->filterName, root->getName()}); - that->setInScene("link", {self->sourceName, self->filterName}); - that->setInScene("link", {self->filterName, self->sinkName}); - that->setObjectAttribute(self->sinkName, "framerate", {self->framerate}); - that->setObjectAttribute(self->filterName, "sizeOverride", {self->width, self->height}); + that->setInScene("addObject", {"filter", *self->filterName, root->getName()}); + // Wait for the object to be created + while (!that->checkObjectExists(*self->filterName)) + this_thread::sleep_for(50ms); + that->setInScene("link", {*self->sourceName, *self->filterName}); + that->setInScene("link", {*self->filterName, *self->sinkName}); + that->setObjectAttribute(*self->sinkName, "framerate", {self->framerate}); + that->setObjectAttribute(*self->filterName, "sizeOverride", {self->width, self->height}); self->linked = true; @@ -218,7 +227,7 @@ PyObject* PythonSink::pythonSinkUnlink(PythonSinkObject* self) } if (!self->sink) - self->sink = dynamic_pointer_cast(root->getObject(self->sinkName)); + self->sink = dynamic_pointer_cast(root->getObject(*self->sinkName)); if (!self->sink) { @@ -241,14 +250,14 @@ PyObject* PythonSink::pythonSinkUnlink(PythonSinkObject* self) Py_XDECREF(result); } - that->setInScene("unlink", {self->sourceName, self->filterName}); - that->setInScene("unlink", {self->filterName, self->sinkName}); - that->setInScene("deleteObject", {self->filterName}); + that->setInScene("unlink", {*self->sourceName, *self->filterName}); + that->setInScene("unlink", {*self->filterName, *self->sinkName}); + that->setInScene("deleteObject", {*self->filterName}); // Wait for the filter to be truly deleted. We do not try to get a shared_ptr // of the object, because we want it to be deleted. So we get the object list auto objectList = that->getObjectList(); - while (std::find(objectList.begin(), objectList.end(), self->filterName) != objectList.end()) + while (std::find(objectList.begin(), objectList.end(), *self->filterName) != objectList.end()) { this_thread::sleep_for(chrono::milliseconds(5)); objectList = that->getObjectList(); @@ -311,7 +320,7 @@ PyObject* PythonSink::pythonSinkGrab(PythonSinkObject* self) // Keeping the ratio may also have had some effects if (self->keepRatio) { - auto realSize = that->getObjectAttribute(self->filterName, "sizeOverride"); + auto realSize = that->getObjectAttribute(*self->filterName, "sizeOverride"); self->width = realSize[0].as(); self->height = realSize[1].as(); } @@ -374,7 +383,7 @@ PyObject* PythonSink::pythonSinkSetSize(PythonSinkObject* self, PyObject* args, self->width = width; self->height = height; - that->setObjectAttribute(self->filterName, "sizeOverride", {self->width, self->height}); + that->setObjectAttribute(*self->filterName, "sizeOverride", {self->width, self->height}); Py_INCREF(Py_True); return Py_True; @@ -401,7 +410,7 @@ PyObject* PythonSink::pythonSinkGetSize(PythonSinkObject* self) return Py_False; } - Values size = that->getObjectAttribute(self->filterName, "sizeOverride"); + Values size = that->getObjectAttribute(*self->filterName, "sizeOverride"); if (size.size() == 2) return Py_BuildValue("ii", size[0].as(), size[1].as()); else @@ -442,7 +451,7 @@ PyObject* PythonSink::pythonSinkSetFramerate(PythonSinkObject* self, PyObject* a } self->framerate = framerate; - that->setObjectAttribute(self->sinkName, "framerate", {self->framerate}); + that->setObjectAttribute(*self->sinkName, "framerate", {self->framerate}); Py_INCREF(Py_True); return Py_True; @@ -482,7 +491,7 @@ PyObject* PythonSink::pythonSinkKeepRatio(PythonSinkObject* self, PyObject* args } self->keepRatio = keepRatio; - that->setObjectAttribute(self->filterName, "keepRatio", {static_cast(keepRatio)}); + that->setObjectAttribute(*self->filterName, "keepRatio", {static_cast(keepRatio)}); Py_INCREF(Py_True); return Py_True; @@ -516,7 +525,7 @@ PyObject* PythonSink::pythonSinkOpen(PythonSinkObject* self) return Py_False; } - that->setObjectAttribute(self->sinkName, "opened", {1}); + that->setObjectAttribute(*self->sinkName, "opened", {1}); self->opened = true; Py_INCREF(Py_True); @@ -551,7 +560,7 @@ PyObject* PythonSink::pythonSinkClose(PythonSinkObject* self) return Py_False; } - that->setObjectAttribute(self->sinkName, "opened", {0}); + that->setObjectAttribute(*self->sinkName, "opened", {0}); self->opened = false; PyObject* tmp = nullptr; diff --git a/src/controller/python/python_sink.h b/src/controller/python/python_sink.h index 702c30d3..25b1ee71 100644 --- a/src/controller/python/python_sink.h +++ b/src/controller/python/python_sink.h @@ -41,18 +41,19 @@ class PythonSink public: struct PythonSinkObject { + PyObject_HEAD static std::atomic_int sinkIndex; - PyObject_HEAD std::string sourceName{""}; - uint32_t width{512}; - uint32_t height{512}; - bool keepRatio{false}; - uint32_t framerate{30}; - std::string sinkName{""}; - std::string filterName{""}; - std::shared_ptr sink{nullptr}; - bool linked{false}; - bool opened{false}; - PyObject* lastBuffer{nullptr}; + uint32_t width; + uint32_t height; + bool keepRatio; + uint32_t framerate; + std::unique_ptr sourceName; + std::unique_ptr sinkName; + std::unique_ptr filterName; + std::shared_ptr sink; + bool linked; + bool opened; + PyObject* lastBuffer; }; PythonSinkObject pythonSinkObject; diff --git a/src/graphics/filter.cpp b/src/graphics/filter.cpp index 5c47704e..bf725de7 100644 --- a/src/graphics/filter.cpp +++ b/src/graphics/filter.cpp @@ -153,6 +153,16 @@ void Filter::setKeepRatio(bool keepRatio) updateSizeWrtRatio(); } +/*************/ +void Filter::setSixteenBpc(bool active) +{ + _sixteenBpc = active; + if (!_fbo) + return; + + _fbo->setParameters(false, active); +} + /*************/ void Filter::updateSizeWrtRatio() { @@ -324,7 +334,7 @@ void Filter::setOutput() { _fbo = make_unique(_root); _fbo->getColorTexture()->setAttribute("filtering", {1}); - _fbo->setParameters(false, true); + _fbo->setParameters(false, _sixteenBpc); // Setup the virtual screen _screen = make_shared(_root); @@ -719,8 +729,12 @@ void Filter::registerDefaultShaderAttributes() addAttribute("sizeOverride", [&](const Values& args) { - _sizeOverride[0] = args[0].as(); - _sizeOverride[1] = args[1].as(); + auto width = args[0].as(); + auto height = args[1].as(); + addTask([=]() { + _sizeOverride[0] = width; + _sizeOverride[1] = height; + }); return true; }, [&]() -> Values { diff --git a/src/graphics/filter.h b/src/graphics/filter.h index 8c254039..0e884d1e 100644 --- a/src/graphics/filter.h +++ b/src/graphics/filter.h @@ -106,6 +106,12 @@ class Filter : public Texture */ void setKeepRatio(bool keepRatio); + /** + * Set the bit depth to 16 bpc + * \param active If true, set to 16bpc, otherwise 8bpc + */ + void setSixteenBpc(bool active); + /** * \brief Render the filter */ @@ -139,6 +145,7 @@ class Filter : public Texture static constexpr int _defaultSize[2]{512, 512}; int _sizeOverride[2]{-1, -1}; //!< If set to positive values, overrides the size given by input textures bool _keepRatio{false}; + bool _sixteenBpc{true}; std::unordered_map _filterUniforms; //!< Contains all filter uniforms Values _colorCurves{}; //!< RGB points for the color curves, active if at least 3 points are set diff --git a/src/graphics/framebuffer.h b/src/graphics/framebuffer.h index 64c0493f..8330d53b 100644 --- a/src/graphics/framebuffer.h +++ b/src/graphics/framebuffer.h @@ -69,6 +69,12 @@ class Framebuffer : public GraphObject */ static void blit(const Framebuffer& src, const Framebuffer& dst); + /** + * Get bit depth + * \return Return the bit depth for each channel + */ + uint32_t getBitDepth() const { return _16bits ? 16 : 8; } + /** * Get the color texture * \return The color texture diff --git a/src/sink/sink.cpp b/src/sink/sink.cpp index f1bbc851..a7741e07 100644 --- a/src/sink/sink.cpp +++ b/src/sink/sink.cpp @@ -2,6 +2,7 @@ #include +#include "./graphics/filter.h" #include "./utils/timer.h" using namespace std; @@ -46,8 +47,13 @@ string Sink::getCaps() const /*************/ bool Sink::linkIt(const shared_ptr& obj) { - auto objAsTexture = dynamic_pointer_cast(obj); - if (objAsTexture) + if (auto objAsFilter = dynamic_pointer_cast(obj); objAsFilter) + { + objAsFilter->setSixteenBpc(false); + _inputTexture = dynamic_pointer_cast(obj); + return true; + } + else if (auto objAsTexture = dynamic_pointer_cast(obj); objAsTexture) { _inputTexture = objAsTexture; return true; @@ -59,9 +65,15 @@ bool Sink::linkIt(const shared_ptr& obj) /*************/ void Sink::unlinkIt(const shared_ptr& obj) { - auto objAsTexture = dynamic_pointer_cast(obj); - if (objAsTexture) + if (auto objAsFilter = dynamic_pointer_cast(obj); objAsFilter) + { + objAsFilter->setSixteenBpc(true); _inputTexture.reset(); + } + else if (auto objAsTexture = dynamic_pointer_cast(obj); objAsTexture) + { + _inputTexture.reset(); + } } /*************/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 148b9bf2..2bd1369b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -140,7 +140,7 @@ endif() add_custom_command(OUTPUT integration_tests COMMAND if [ ! -d ${CMAKE_CURRENT_SOURCE_DIR}/assets ]; then $(git clone https://gitlab.com/sat-metalab/splash-assets ${CMAKE_CURRENT_SOURCE_DIR}/assets); fi COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/assets && git pull origin master - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../src/splash ${CMAKE_CURRENT_SOURCE_DIR}/integration_tests/integrationTests.json + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../src/splash -P ${CMAKE_CURRENT_SOURCE_DIR}/integration_tests/integration_tests.py ${CMAKE_CURRENT_SOURCE_DIR}/integration_tests/integrationTests.json DEPENDS splash ) add_custom_target(check_integration DEPENDS integration_tests) diff --git a/tests/integration_tests/integrationTests.json b/tests/integration_tests/integrationTests.json index 7bd49d5e..01ac651f 100644 --- a/tests/integration_tests/integrationTests.json +++ b/tests/integration_tests/integrationTests.json @@ -68,10 +68,6 @@ "size" : [ 1280, 1024 ], "srgb" : [ 1 ], "type" : "window" - }, - "pythonScript" : { - "type" : "python", - "file" : ["./integration_tests.py"] } }, "scenes" : [ diff --git a/tests/integration_tests/test_cases/test_wrapped_sink.py b/tests/integration_tests/test_cases/test_wrapped_sink.py index c081d566..a07a1d9e 100644 --- a/tests/integration_tests/test_cases/test_wrapped_sink.py +++ b/tests/integration_tests/test_cases/test_wrapped_sink.py @@ -9,10 +9,9 @@ class TestWrappedSink(SplashTestCase): print("Test the wrapped sink") sink = splash.Sink() + sink.link_to("image") sink.set_size(8, 8) sink.set_framerate(15) - sink.link_to("image") - sink.link_to("image") sink.open() sleep(0.5) image = sink.grab()