mirror of
https://gitlab.com/splashmapper/splash.git
synced 2026-06-16 12:33:50 +02:00
🐛 Fixed PythonSink
This commit is contained in:
@@ -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});
|
||||
}
|
||||
|
||||
/*************/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/*************/
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user