🐛 Fixed PythonSink

This commit is contained in:
Emmanuel Durand
2020-02-07 14:42:41 -05:00
parent bfbb83b27f
commit 4c3e02c8db
14 changed files with 112 additions and 59 deletions
+5 -4
View File
@@ -21,7 +21,7 @@ shared_ptr<GraphObject> 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});
}
/*************/
+1 -1
View File
@@ -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
+9 -2
View File
@@ -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<PythonEmbedded*>(PyCapsule_Import("splash._splash", 0));
auto that = static_cast<PythonEmbedded*>(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);
@@ -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
+1 -1
View File
@@ -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);
}
+35 -26
View File
@@ -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<string>(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<Sink>(root->getObject(self->sinkName));
self->sink = dynamic_pointer_cast<Sink>(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<Sink>(root->getObject(self->sinkName));
self->sink = dynamic_pointer_cast<Sink>(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<string>(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<string>(*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<Sink>(root->getObject(self->sinkName));
self->sink = dynamic_pointer_cast<Sink>(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<int>();
self->height = realSize[1].as<int>();
}
@@ -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<int>(), size[1].as<int>());
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<int>(keepRatio)});
that->setObjectAttribute(*self->filterName, "keepRatio", {static_cast<int>(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;
+12 -11
View File
@@ -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<Splash::Sink> 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<std::string> sourceName;
std::unique_ptr<std::string> sinkName;
std::unique_ptr<std::string> filterName;
std::shared_ptr<Splash::Sink> sink;
bool linked;
bool opened;
PyObject* lastBuffer;
};
PythonSinkObject pythonSinkObject;
+17 -3
View File
@@ -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<Framebuffer>(_root);
_fbo->getColorTexture()->setAttribute("filtering", {1});
_fbo->setParameters(false, true);
_fbo->setParameters(false, _sixteenBpc);
// Setup the virtual screen
_screen = make_shared<Object>(_root);
@@ -719,8 +729,12 @@ void Filter::registerDefaultShaderAttributes()
addAttribute("sizeOverride",
[&](const Values& args) {
_sizeOverride[0] = args[0].as<int>();
_sizeOverride[1] = args[1].as<int>();
auto width = args[0].as<int>();
auto height = args[1].as<int>();
addTask([=]() {
_sizeOverride[0] = width;
_sizeOverride[1] = height;
});
return true;
},
[&]() -> Values {
+7
View File
@@ -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<std::string, Values> _filterUniforms; //!< Contains all filter uniforms
Values _colorCurves{}; //!< RGB points for the color curves, active if at least 3 points are set
+6
View File
@@ -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
+16 -4
View File
@@ -2,6 +2,7 @@
#include <fstream>
#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<GraphObject>& obj)
{
auto objAsTexture = dynamic_pointer_cast<Texture>(obj);
if (objAsTexture)
if (auto objAsFilter = dynamic_pointer_cast<Filter>(obj); objAsFilter)
{
objAsFilter->setSixteenBpc(false);
_inputTexture = dynamic_pointer_cast<Texture>(obj);
return true;
}
else if (auto objAsTexture = dynamic_pointer_cast<Texture>(obj); objAsTexture)
{
_inputTexture = objAsTexture;
return true;
@@ -59,9 +65,15 @@ bool Sink::linkIt(const shared_ptr<GraphObject>& obj)
/*************/
void Sink::unlinkIt(const shared_ptr<GraphObject>& obj)
{
auto objAsTexture = dynamic_pointer_cast<Texture>(obj);
if (objAsTexture)
if (auto objAsFilter = dynamic_pointer_cast<Filter>(obj); objAsFilter)
{
objAsFilter->setSixteenBpc(true);
_inputTexture.reset();
}
else if (auto objAsTexture = dynamic_pointer_cast<Texture>(obj); objAsTexture)
{
_inputTexture.reset();
}
}
/*************/
+1 -1
View File
@@ -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)
@@ -68,10 +68,6 @@
"size" : [ 1280, 1024 ],
"srgb" : [ 1 ],
"type" : "window"
},
"pythonScript" : {
"type" : "python",
"file" : ["./integration_tests.py"]
}
},
"scenes" : [
@@ -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()