Session loading and saving in thread.

This commit is contained in:
brunoherbelin
2020-05-09 13:40:47 +02:00
parent 48d5991c3e
commit 069009fc06
11 changed files with 304 additions and 130 deletions

View File

@@ -50,12 +50,6 @@ void ImageShader::reset()
{
Shader::reset();
// setup double texturing (TODO : do it only once)
program_->use();
program_->setUniform("iChannel0", 0);
program_->setUniform("iChannel1", 1);
program_->enduse();
// default mask
mask = 0;
custom_textureindex = mask_presets[0];

265
Mixer.cpp
View File

@@ -22,22 +22,86 @@ using namespace tinyxml2;
#include "Mixer.h"
// static objects for multithreaded session loading
static bool sessionLoadPending_ = false;
static bool sessionLoadFinished_ = false;
static Session *loadedSession;
static std::atomic<bool> sessionLoadPending_ = false;
static std::atomic<bool> sessionLoadFinished_ = false;
static std::string sessionThreadFilename_ = "";
static void loadSession(const std::string& filename, )
static void loadSession(const std::string& filename, Session *session)
{
sessionLoadPending_ = true;
sessionLoadFinished_ = false;
sessionThreadFilename_ = "";
// actual loading of xml file
SessionCreator creator( session );
if (!creator.load(filename)) {
// error loading
Log::Info("Failed to load Session file %s.", filename.c_str());
}
else {
// loaded ok
Log::Info("Session file %s loaded. %d source(s) created.", filename.c_str(), session->numSource());
}
sessionThreadFilename_ = filename;
sessionLoadFinished_ = true;
sessionLoadPending_ = false;
}
// static objects for multithreaded session saving
static std::atomic<bool> sessionSavePending_ = false;
static std::atomic<bool> sessionSaveFinished_ = false;
static void saveSession(const std::string& filename, Session *session)
{
// reset
sessionSavePending_ = true;
sessionSaveFinished_ = false;
sessionThreadFilename_ = "";
// creation of XML doc
XMLDocument xmlDoc;
Mixer::Mixer() : session_(nullptr), current_view_(nullptr)
XMLElement *version = xmlDoc.NewElement(APP_NAME);
version->SetAttribute("major", XML_VERSION_MAJOR);
version->SetAttribute("minor", XML_VERSION_MINOR);
xmlDoc.InsertEndChild(version);
// 1. list of sources
XMLElement *sessionNode = xmlDoc.NewElement("Session");
xmlDoc.InsertEndChild(sessionNode);
SourceList::iterator iter;
for (iter = session->begin(); iter != session->end(); iter++)
{
SessionVisitor sv(&xmlDoc, sessionNode);
// source visitor
(*iter)->accept(sv);
}
// 2. config of views
XMLElement *views = xmlDoc.NewElement("Views");
xmlDoc.InsertEndChild(views);
{
XMLElement *mixing = xmlDoc.NewElement( "Mixing" );
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), &xmlDoc));
views->InsertEndChild(mixing);
XMLElement *geometry = xmlDoc.NewElement( "Geometry" );
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), &xmlDoc));
views->InsertEndChild(geometry);
}
// save file to disk
XMLSaveDoc(&xmlDoc, filename);
// all ok
sessionThreadFilename_ = filename;
sessionSaveFinished_ = true;
sessionSavePending_ = false;
}
Mixer::Mixer() : session_(nullptr), back_session_(nullptr), current_view_(nullptr)
{
// this initializes with a new empty session
newSession();
@@ -49,6 +113,17 @@ Mixer::Mixer() : session_(nullptr), current_view_(nullptr)
void Mixer::update()
{
// swap front and back sessions when loading is finished
if (sessionLoadFinished_) {
swap();
sessionFilename_ = sessionThreadFilename_;
sessionLoadFinished_ = false;
}
if (sessionSaveFinished_) {
sessionFilename_ = sessionThreadFilename_;
sessionSaveFinished_ = false;
}
// compute dt
if (update_time_ == GST_CLOCK_TIME_NONE)
update_time_ = gst_util_get_timestamp ();
@@ -244,119 +319,99 @@ View *Mixer::currentView()
return current_view_;
}
void Mixer::save(const std::string& filename)
void Mixer::save()
{
XMLDocument xmlDoc;
XMLElement *version = xmlDoc.NewElement(APP_NAME);
version->SetAttribute("major", XML_VERSION_MAJOR);
version->SetAttribute("minor", XML_VERSION_MINOR);
xmlDoc.InsertEndChild(version);
// block: list of sources
XMLElement *session = xmlDoc.NewElement("Session");
xmlDoc.InsertEndChild(session);
SourceList::iterator iter;
for (iter = session_->begin(); iter != session_->end(); iter++)
{
SessionVisitor sv(&xmlDoc, session);
// source visitor
(*iter)->accept(sv);
}
// block: config of views
XMLElement *views = xmlDoc.NewElement("Views");
xmlDoc.InsertEndChild(views);
{
XMLElement *mixing = xmlDoc.NewElement( "Mixing" );
mixing->InsertEndChild( SessionVisitor::NodeToXML(*mixing_.scene.root(), &xmlDoc));
views->InsertEndChild(mixing);
XMLElement *geometry = xmlDoc.NewElement( "Geometry" );
geometry->InsertEndChild( SessionVisitor::NodeToXML(*geometry_.scene.root(), &xmlDoc));
views->InsertEndChild(geometry);
}
// save file
XMLSaveDoc(&xmlDoc, filename);
if (!sessionFilename_.empty())
saveas(sessionFilename_);
}
bool Mixer::open(const std::string& filename)
void Mixer::saveas(const std::string& filename)
{
XMLDocument xmlDoc;
XMLError eResult = xmlDoc.LoadFile(filename.c_str());
if (XMLResultError(eResult))
return false;
// optional copy of views config
session_->config(View::MIXING)->copyTransform( mixing_.scene.root() );
session_->config(View::GEOMETRY)->copyTransform( geometry_.scene.root() );
XMLElement *version = xmlDoc.FirstChildElement(APP_NAME);
if (version == nullptr) {
Log::Warning("Not a %s session file: %s", APP_NAME, filename.c_str());
return false;
}
int version_major = -1, version_minor = -1;
version->QueryIntAttribute("major", &version_major); // TODO incompatible if major is different?
version->QueryIntAttribute("minor", &version_minor);
if (version_major != XML_VERSION_MAJOR || version_minor != XML_VERSION_MINOR){
Log::Warning("%s is in an older versions of session file format. Data might be lost.", filename.c_str());
}
// launch a thread to save the session
std::thread (saveSession, filename, session_).detach();
// ok, ready to read sources
SessionCreator creator( xmlDoc.FirstChildElement("Session") );
Session *new_session = creator.session();
// XMLDocument xmlDoc;
// if all good, change to new session
if (new_session) {
// XMLElement *version = xmlDoc.NewElement(APP_NAME);
// version->SetAttribute("major", XML_VERSION_MAJOR);
// version->SetAttribute("minor", XML_VERSION_MINOR);
// xmlDoc.InsertEndChild(version);
// validate new session
newSession( new_session );
// // block: list of sources
// XMLElement *session = xmlDoc.NewElement("Session");
// xmlDoc.InsertEndChild(session);
// SourceList::iterator iter;
// for (iter = session_->begin(); iter != session_->end(); iter++)
// {
// SessionVisitor sv(&xmlDoc, session);
// // source visitor
// (*iter)->accept(sv);
// }
// insert nodes of sources into views
SourceList::iterator source_iter;
for (source_iter = new_session->begin(); source_iter != new_session->end(); source_iter++)
{
mixing_.scene.fg()->attach( (*source_iter)->group(View::MIXING) );
geometry_.scene.fg()->attach( (*source_iter)->group(View::GEOMETRY) );
}
// // block: config of views
// XMLElement *views = xmlDoc.NewElement("Views");
// xmlDoc.InsertEndChild(views);
// {
// XMLElement *mixing = xmlDoc.NewElement( "Mixing" );
// mixing->InsertEndChild( SessionVisitor::NodeToXML(*mixing_.scene.root(), &xmlDoc));
// views->InsertEndChild(mixing);
}
else {
Log::Warning("Session file %s not loaded.", filename.c_str());
return false;
}
// XMLElement *geometry = xmlDoc.NewElement( "Geometry" );
// geometry->InsertEndChild( SessionVisitor::NodeToXML(*geometry_.scene.root(), &xmlDoc));
// views->InsertEndChild(geometry);
// }
// config of view settings (optionnal)
XMLElement *views = xmlDoc.FirstChildElement("Views");
if (views != nullptr) {
// ok, ready to read views
SessionCreator::XMLToNode( views->FirstChildElement("Mixing"), *mixing_.scene.root());
SessionCreator::XMLToNode( views->FirstChildElement("Geometry"), *geometry_.scene.root());
}
Log::Info("Session file %s loaded. %d source(s) created.", filename.c_str(), session_->numSource());
return true;
// // save file
// XMLSaveDoc(&xmlDoc, filename);
}
void Mixer::newSession(Session *newsession)
void Mixer::open(const std::string& filename)
{
// delete session & detatch nodes from views
if (back_session_)
delete back_session_;
// create empty session
back_session_ = new Session;
// launch a thread to load the session into back_session
std::thread (loadSession, filename, back_session_).detach();
}
void Mixer::swap()
{
if (!back_session_)
return;
if (session_) {
// remove nodes from views
// detatch current session's nodes from views
for (auto source_iter = session_->begin(); source_iter != session_->end(); source_iter++)
{
mixing_.scene.fg()->detatch( (*source_iter)->group(View::MIXING) );
geometry_.scene.fg()->detatch( (*source_iter)->group(View::GEOMETRY) );
}
// clear session: delete all sources
delete session_;
}
// validate new session
if (newsession)
session_ = newsession;
else
session_ = new Session;
// swap back and front
Session *tmp = session_;
session_ = back_session_;
back_session_ = tmp;
// attach new session's nodes to views
for (auto source_iter = session_->begin(); source_iter != session_->end(); source_iter++)
{
mixing_.scene.fg()->attach( (*source_iter)->group(View::MIXING) );
geometry_.scene.fg()->attach( (*source_iter)->group(View::GEOMETRY) );
}
// optional copy of views config
mixing_.scene.root()->copyTransform( session_->config(View::MIXING) );
geometry_.scene.root()->copyTransform( session_->config(View::GEOMETRY) );
// no current source
current_source_ = session_->end();
@@ -364,8 +419,24 @@ void Mixer::newSession(Session *newsession)
// reset timer
update_time_ = GST_CLOCK_TIME_NONE;
}
// default views
void Mixer::newSession()
{
// delete previous back session if needed
if (back_session_)
delete back_session_;
// create empty session
back_session_ = new Session;
// swap current with empty
swap();
// default view config
mixing_.restoreSettings();
geometry_.restoreSettings();
sessionFilename_ = "newsession.vmx";
}

14
Mixer.h
View File

@@ -60,17 +60,21 @@ public:
Session *session() { return session_; }
// load and save session
bool open(const std::string& filename);
void save(const std::string& filename);
void newSession(Session *newsession = nullptr);
void open(const std::string& filename);
void saveas(const std::string& filename);
void save();
void newSession();
protected:
Session *session_;
std::string sessionFilename_;
Session *back_session_;
void swap();
void insertSource(Source *s);
void setCurrentSource(SourceList::iterator it);
Session *session_;
SourceList::iterator current_source_;
int current_source_index_;

View File

@@ -50,6 +50,16 @@ Node::~Node ()
}
void Node::copyTransform(Node *other)
{
if (!other)
return;
transform_ = glm::identity<glm::mat4>();
scale_ = other->scale_;
rotation_ = other->rotation_;
translation_ = other->translation_;
}
void Node::update( float )
{
// update transform matrix from attributes

View File

@@ -65,14 +65,14 @@ public:
// accept all kind of visitors
virtual void accept (Visitor& v);
void copyTransform(Node *other);
// public members, to manipulate with care
bool visible_;
uint refcount_;
glm::mat4 transform_;
glm::vec3 scale_, rotation_, translation_;
};

View File

@@ -7,8 +7,12 @@
Session::Session()
{
config_[View::RENDERING] = new Group;
config_[View::GEOMETRY] = new Group;
config_[View::MIXING] = new Group;
}
Session::~Session()
{
// delete all sources

View File

@@ -32,9 +32,14 @@ public:
// result of render
inline FrameBuffer *frame() const { return render_.frameBuffer(); }
// configuration for group nodes of views
inline Group *config(View::Mode m) const { return config_.at(m); }
protected:
RenderView render_;
SourceList sources_;
std::map<View::Mode, Group*> config_;
};
#endif // SESSION_H

View File

@@ -1,6 +1,7 @@
#include "SessionCreator.h"
#include "Log.h"
#include "defines.h"
#include "Scene.h"
#include "Primitives.h"
#include "Mesh.h"
@@ -14,12 +15,48 @@
using namespace tinyxml2;
SessionCreator::SessionCreator(tinyxml2::XMLElement *sessionNode): Visitor()
SessionCreator::SessionCreator(Session *session): Visitor(), session_(session)
{
session_ = nullptr;
xmlDoc_ = new XMLDocument;
}
bool SessionCreator::load(const std::string& filename)
{
XMLError eResult = xmlDoc_->LoadFile(filename.c_str());
if ( XMLResultError(eResult))
return false;
XMLElement *version = xmlDoc_->FirstChildElement(APP_NAME);
if (version == nullptr) {
Log::Warning("%s is not a %s session file.", filename.c_str(), APP_NAME);
return false;
}
int version_major = -1, version_minor = -1;
version->QueryIntAttribute("major", &version_major); // TODO incompatible if major is different?
version->QueryIntAttribute("minor", &version_minor);
if (version_major != XML_VERSION_MAJOR || version_minor != XML_VERSION_MINOR){
Log::Warning("%s is in a different versions of session file. Loading might fail.", filename.c_str());
return false;
}
// ok, ready to read sources
loadSession( xmlDoc_->FirstChildElement("Session") );
// excellent, session was created: load optionnal config
if (session_){
loadConfig( xmlDoc_->FirstChildElement("Views") );
}
return true;
}
void SessionCreator::loadSession(XMLElement *sessionNode)
{
if (sessionNode != nullptr) {
session_ = new Session;
// create a session if not provided
if (!session_)
session_ = new Session;
int counter = 0;
XMLElement* sourceNode = sessionNode->FirstChildElement("Source");
@@ -34,16 +71,23 @@ SessionCreator::SessionCreator(tinyxml2::XMLElement *sessionNode): Visitor()
new_media_source->accept(*this);
session_->addSource(new_media_source);
}
// TODO : else other types of source
// TODO : create other types of source
}
Log::Info("Created %d Sources", counter);
}
else
Log::Warning("Session seems empty.");
}
void SessionCreator::loadConfig(XMLElement *viewsNode)
{
if (viewsNode != nullptr) {
// ok, ready to read views
SessionCreator::XMLToNode( viewsNode->FirstChildElement("Mixing"), *session_->config(View::MIXING));
SessionCreator::XMLToNode( viewsNode->FirstChildElement("Geometry"), *session_->config(View::GEOMETRY));
}
}
void SessionCreator::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
{

View File

@@ -8,12 +8,17 @@ class Session;
class SessionCreator : public Visitor {
tinyxml2::XMLDocument *xmlDoc_;
tinyxml2::XMLElement *xmlCurrent_;
Session *session_;
public:
SessionCreator(tinyxml2::XMLElement *root);
void loadSession(tinyxml2::XMLElement *sessionNode);
void loadConfig(tinyxml2::XMLElement *viewsNode);
public:
SessionCreator(Session *session = nullptr);
bool load(const std::string& filename);
inline Session *session() const { return session_; }
// Elements of Scene

View File

@@ -50,26 +50,30 @@ bool ShadingProgram::initialized()
void ShadingProgram::compile()
{
const char* vcode = vertex_code_.c_str();
const char* vcode = vertex_code_.c_str();
vertex_id_ = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_id_, 1, &vcode, NULL);
glCompileShader(vertex_id_);
const char* fcode = fragment_code_.c_str();
const char* fcode = fragment_code_.c_str();
fragment_id_ = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_id_, 1, &fcode, NULL);
glCompileShader(fragment_id_);
checkCompileErr();
checkCompileErr();
}
void ShadingProgram::link()
{
id_ = glCreateProgram();
id_ = glCreateProgram();
glAttachShader(id_, vertex_id_);
glAttachShader(id_, fragment_id_);
glLinkProgram(id_);
checkLinkingErr();
glLinkProgram(id_);
checkLinkingErr();
glUseProgram(id_);
glUniform1i(glGetUniformLocation(id_, "iChannel0"), 0);
glUniform1i(glGetUniformLocation(id_, "iChannel1"), 1);
glUseProgram(0);
glDeleteShader(vertex_id_);
glDeleteShader(fragment_id_);
}

View File

@@ -57,12 +57,14 @@ void ShowAbout(bool* p_open);
// static objects for multithreaded file dialog
static bool FileDialogPending_ = false;
static bool FileDialogFinished_ = false;
static bool FileDialogLoadFinished_ = false;
static bool FileDialogSaveFinished_ = false;
static std::string FileDialogFilename_ = "";
static void FileDialogOpen(std::string path)
{
FileDialogPending_ = true;
FileDialogLoadFinished_ = false;
char const * lTheOpenFileName;
char const * lFilterPatterns[2] = { "*.vmx" };
@@ -78,7 +80,29 @@ static void FileDialogOpen(std::string path)
FileDialogFilename_ = std::string( lTheOpenFileName );
}
FileDialogFinished_ = true;
FileDialogLoadFinished_ = true;
}
static void FileDialogSave(std::string path)
{
FileDialogPending_ = true;
FileDialogSaveFinished_ = false;
char const * save_file_name;
char const * save_pattern[2] = { "*.vmx" };
save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session");
if (!save_file_name)
{
FileDialogFilename_ = "";
}
else
{
FileDialogFilename_ = std::string( save_file_name );
}
FileDialogSaveFinished_ = true;
}
@@ -314,11 +338,16 @@ void UserInterface::NewFrame()
navigator.Render();
// handle FileDialog
if (FileDialogFinished_) {
FileDialogFinished_ = false;
if (FileDialogLoadFinished_) {
FileDialogLoadFinished_ = false;
FileDialogPending_ = false;
Mixer::manager().open(FileDialogFilename_);
}
if (FileDialogSaveFinished_) {
FileDialogSaveFinished_ = false;
FileDialogPending_ = false;
Mixer::manager().saveas(FileDialogFilename_);
}
}
void UserInterface::Render()
@@ -389,9 +418,13 @@ void UserInterface::showMenuFile()
navigator.hidePannel();
}
if (ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save", "Ctrl+S", false, !FileDialogPending_)) {
// UserInterface::manager().OpenFileMedia();
Mixer::manager().save();
navigator.hidePannel();
}
if (ImGui::MenuItem( ICON_FA_FOLDER_OPEN " Save as", NULL, false, !FileDialogPending_)) {
std::thread (FileDialogSave, "./").detach();
navigator.hidePannel();
}
// TODO : Save As...
ImGui::Separator();
if (ImGui::MenuItem( ICON_FA_POWER_OFF " Quit", "Ctrl+Q")) {