diff --git a/hecl/CMakeLists.txt b/hecl/CMakeLists.txt index 7b805408b..22370cb9c 100644 --- a/hecl/CMakeLists.txt +++ b/hecl/CMakeLists.txt @@ -25,7 +25,7 @@ set(BOO_INCLUDE_DIR extern/boo/include) add_subdirectory(bintoc) add_subdirectory(extern) add_definitions(${BOO_SYS_DEFINES}) -include_directories(include ${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${BOO_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) +include_directories(include blender ${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${BOO_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) add_subdirectory(lib) add_subdirectory(blender) add_subdirectory(driver) diff --git a/hecl/blender/BlenderConnection.cpp b/hecl/blender/BlenderConnection.cpp index aefccc567..7e391e756 100644 --- a/hecl/blender/BlenderConnection.cpp +++ b/hecl/blender/BlenderConnection.cpp @@ -34,7 +34,7 @@ namespace hecl { logvisor::Module BlenderLog("BlenderConnection"); -BlenderConnection* SharedBlenderConnection = nullptr; +BlenderToken SharedBlenderToken; #ifdef __APPLE__ #define DEFAULT_BLENDER_BIN "/Applications/Blender.app/Contents/MacOS/blender" @@ -952,4 +952,14 @@ void BlenderConnection::quitBlender() _readLine(lineBuf, sizeof(lineBuf)); } +BlenderConnection& BlenderConnection::SharedConnection() +{ + return SharedBlenderToken.getBlenderConnection(); +} + +void BlenderConnection::Shutdown() +{ + SharedBlenderToken.shutdown(); +} + } diff --git a/hecl/blender/BlenderConnection.hpp b/hecl/blender/BlenderConnection.hpp index 4edafc93b..7ae7fd16a 100644 --- a/hecl/blender/BlenderConnection.hpp +++ b/hecl/blender/BlenderConnection.hpp @@ -24,12 +24,13 @@ #include "hecl/HMDLMeta.hpp" #include #include +#include namespace hecl { extern logvisor::Module BlenderLog; -extern class BlenderConnection* SharedBlenderConnection; +extern class BlenderToken SharedBlenderToken; class HMDLBuffers; class BlenderConnection @@ -681,31 +682,35 @@ public: void quitBlender(); - static BlenderConnection& SharedConnection() - { - if (!SharedBlenderConnection) - SharedBlenderConnection = new BlenderConnection(hecl::VerbosityLevel); - return *SharedBlenderConnection; - } - void closeStream() { if (m_lock) deleteBlend(); } - static void Shutdown() + static BlenderConnection& SharedConnection(); + static void Shutdown(); +}; + +class BlenderToken +{ + std::experimental::optional m_conn; +public: + BlenderConnection& getBlenderConnection() { - if (SharedBlenderConnection) + if (!m_conn) + m_conn.emplace(); + return *m_conn; + } + void shutdown() + { + if (m_conn) { - SharedBlenderConnection->closeStream(); - SharedBlenderConnection->quitBlender(); - delete SharedBlenderConnection; - SharedBlenderConnection = nullptr; + m_conn->quitBlender(); + m_conn = std::experimental::nullopt; BlenderLog.report(logvisor::Info, "BlenderConnection Shutdown Successful"); } } - }; class HMDLBuffers diff --git a/hecl/include/hecl/ClientProcess.hpp b/hecl/include/hecl/ClientProcess.hpp index c55176caa..09ff5d28c 100644 --- a/hecl/include/hecl/ClientProcess.hpp +++ b/hecl/include/hecl/ClientProcess.hpp @@ -2,6 +2,8 @@ #define HECL_CLIENT_PROCESS_HPP #include "hecl.hpp" +#include "Database.hpp" +#include "BlenderConnection.hpp" #include #include #include @@ -26,7 +28,7 @@ public: Cook } m_type; bool m_complete = false; - virtual void run()=0; + virtual void run(BlenderToken& btok)=0; Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {} }; struct BufferTransaction : Transaction @@ -35,7 +37,7 @@ public: void* m_targetBuf; size_t m_maxLen; size_t m_offset; - void run(); + void run(BlenderToken& btok); BufferTransaction(ClientProcess& parent, const ProjectPath& path, void* target, size_t maxLen, size_t offset) : Transaction(parent, Type::Buffer), @@ -45,10 +47,11 @@ public: struct CookTransaction : Transaction { ProjectPath m_path; - int m_returnVal = 0; - void run(); - CookTransaction(ClientProcess& parent, const ProjectPath& path) - : Transaction(parent, Type::Cook), m_path(path) {} + Database::IDataSpec* m_dataSpec; + bool m_returnResult = false; + void run(BlenderToken& btok); + CookTransaction(ClientProcess& parent, const ProjectPath& path, Database::IDataSpec* spec) + : Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec) {} }; private: std::list> m_pendingQueue; @@ -59,6 +62,7 @@ private: { ClientProcess& m_proc; std::thread m_thr; + BlenderToken m_blendTok; Worker(ClientProcess& proc); void proc(); }; @@ -69,8 +73,8 @@ public: ~ClientProcess() {shutdown();} const BufferTransaction* addBufferTransaction(const hecl::ProjectPath& path, void* target, size_t maxLen, size_t offset); - const CookTransaction* addCookTransaction(const hecl::ProjectPath& path); - int syncCook(const hecl::ProjectPath& path); + const CookTransaction* addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec); + bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, BlenderToken& btok); void swapCompletedQueue(std::list>& queue); void shutdown(); }; diff --git a/hecl/include/hecl/Database.hpp b/hecl/include/hecl/Database.hpp index eec06e109..64b603e32 100644 --- a/hecl/include/hecl/Database.hpp +++ b/hecl/include/hecl/Database.hpp @@ -22,6 +22,8 @@ namespace hecl { +class BlenderToken; + namespace Database { class Project; @@ -64,7 +66,9 @@ public: */ class IDataSpec { + const DataSpecEntry* m_specEntry; public: + IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {} virtual ~IDataSpec() {} using FProgress = hecl::Database::FProgress; using FCookProgress = std::function; @@ -100,13 +104,14 @@ public: virtual void doExtract(const ExtractPassInfo& info, FProgress progress) {(void)info;(void)progress;} - virtual bool canCook(const ProjectPath& path) + virtual bool canCook(const ProjectPath& path, BlenderToken& btok) {(void)path;LogModule.report(logvisor::Error, "not implemented");return false;} - virtual const DataSpecEntry* overrideDataSpec(const hecl::ProjectPath& path, - const hecl::Database::DataSpecEntry* oldEntry) + virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, + const Database::DataSpecEntry* oldEntry, + BlenderToken& btok) {(void)path;return oldEntry;} virtual void doCook(const ProjectPath& path, const ProjectPath& cookedPath, - bool fast, FCookProgress progress) + bool fast, BlenderToken& btok, FCookProgress progress) {(void)path;(void)cookedPath;(void)fast;(void)progress;} /** @@ -130,6 +135,8 @@ public: {(void)info;(void)implicitsOut;} virtual void doPackage(const PackagePassInfo& info) {(void)info;} + + const DataSpecEntry* getDataSpecEntry() const {return m_specEntry;} }; /** diff --git a/hecl/lib/ClientProcess.cpp b/hecl/lib/ClientProcess.cpp index cd662587f..09beac56a 100644 --- a/hecl/lib/ClientProcess.cpp +++ b/hecl/lib/ClientProcess.cpp @@ -1,6 +1,7 @@ #include "hecl/ClientProcess.hpp" #include "hecl/Database.hpp" #include "athena/FileReader.hpp" +#include "BlenderConnection.hpp" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -13,115 +14,6 @@ namespace hecl { static logvisor::Module Log("hecl::ClientProcess"); -static bool ExecProcessAndWait(bool verbose, - const SystemChar* exePath, - const SystemChar* workDir, - const SystemChar* args[], - int& returnCode) -{ -#if _WIN32 - std::wstring cmdLine; - for (const SystemChar** it=args ; *it ; ++it) - { - if (it != args) - cmdLine.append(L' '); - cmdLine.append(*it); - } - - STARTUPINFO sinfo = {sizeof(STARTUPINFO)}; - HANDLE nulHandle = NULL; - if (!verbose) - { - SECURITY_ATTRIBUTES sattrs = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; - nulHandle = CreateFileW(L"nul", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sattrs, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - sinfo.hStdError = nulHandle; - sinfo.hStdOutput = nulHandle; - sinfo.dwFlags = STARTF_USESTDHANDLES; - } - - PROCESS_INFORMATION pinfo; - if (!CreateProcessW(exePath, cmdLine.c_str(), NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, workDir, &sinfo, &pinfo)) - { - LPWSTR messageBuffer = nullptr; - size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); - Log.report(logvisor::Fatal, L"error launching '%s': %s", exePath, messageBuffer); - } - - if (nulHandle) - CloseHandle(nulHandle); - - CloseHandle(pinfo.hThread); - WaitForSingleObject(pinfo.hProcess, INFINITE); - DWORD exitCode; - GetExitCodeProcess(pinfo.hProcess, &exitCode); - returnCode = exitCode; - CloseHandle(pinfo.hProcess); - - if (exitCode == 0) - return true; - - return false; - -#else - /* Assemble command args */ - std::vector assembleArgs; - size_t argCount = 0; - for (const SystemChar** it=args ; *it ; ++it) ++argCount; - assembleArgs.reserve(argCount+2); - assembleArgs.push_back(exePath); - for (const SystemChar** it=args ; *it ; ++it) assembleArgs.push_back(*it); - assembleArgs.push_back(nullptr); - - if (verbose) - { - printf("cd %s\n", workDir); - for (const SystemChar* arg : assembleArgs) - if (arg) - printf("%s ", arg); - printf("\n"); - fflush(stdout); - } - - pid_t pid = fork(); - if (!pid) - { - chdir(workDir); - - if (!verbose) - { - int devNull = open("/dev/null", O_WRONLY); - dup2(devNull, STDOUT_FILENO); - dup2(devNull, STDERR_FILENO); - } - - if (execvp(exePath, (char*const*)assembleArgs.data()) < 0) - { - Log.report(logvisor::Fatal, _S("error execing '%s': %s"), exePath, strerror(errno)); - } - - exit(1); - } - - int exitStatus; - if (waitpid(pid, &exitStatus, 0) < 0) - Log.report(logvisor::Fatal, "unable to wait for hecl process to complete: %s", strerror(errno)); - - if (WIFEXITED(exitStatus)) - { - returnCode = WEXITSTATUS(exitStatus); - if (WEXITSTATUS(exitStatus) == 0) - { - return true; - } - } - - return false; - -#endif -} - static int GetCPUCount() { #if _WIN32 @@ -133,7 +25,7 @@ static int GetCPUCount() #endif } -void ClientProcess::BufferTransaction::run() +void ClientProcess::BufferTransaction::run(BlenderToken& btok) { athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false); if (r.hasError()) @@ -148,9 +40,9 @@ void ClientProcess::BufferTransaction::run() m_complete = true; } -void ClientProcess::CookTransaction::run() +void ClientProcess::CookTransaction::run(BlenderToken& btok) { - m_returnVal = m_parent.syncCook(m_path); + m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok); m_complete = true; } @@ -170,7 +62,7 @@ void ClientProcess::Worker::proc() std::unique_ptr trans = std::move(m_proc.m_pendingQueue.front()); m_proc.m_pendingQueue.pop_front(); lk.unlock(); - trans->run(); + trans->run(m_blendTok); lk.lock(); m_proc.m_completedQueue.push_back(std::move(trans)); } @@ -199,30 +91,28 @@ ClientProcess::addBufferTransaction(const ProjectPath& path, void* target, } const ClientProcess::CookTransaction* -ClientProcess::addCookTransaction(const hecl::ProjectPath& path) +ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec) { std::unique_lock lk(m_mutex); - CookTransaction* ret = new CookTransaction(*this, path); + CookTransaction* ret = new CookTransaction(*this, path, spec); m_pendingQueue.emplace_back(ret); m_cv.notify_one(); return ret; } -int ClientProcess::syncCook(const hecl::ProjectPath& path) +bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, BlenderToken& btok) { - const SystemChar* workDir = path.getProject().getProjectWorkingPath().getAbsolutePath().c_str(); - const SystemChar* args[] = {_S("cook"), path.getAbsolutePath().c_str(), nullptr}; - int returnCode; - const SystemChar* heclOverride = hecl::GetEnv(_S("HECL_BIN")); - if (heclOverride) + if (spec->canCook(path, btok)) { - if (ExecProcessAndWait(m_verbosity != 0, heclOverride, workDir, args, returnCode)) - return returnCode; + const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok); + if (specEnt) + { + hecl::ProjectPath cooked = path.getCookedPath(*specEnt); + spec->doCook(path, cooked, false, btok, [](const SystemChar*) {}); + return true; + } } - if (!ExecProcessAndWait(m_verbosity != 0, _S("hecl"), workDir, args, returnCode)) - Log.report(logvisor::Fatal, _S("unable to background-cook '%s'"), - path.getAbsolutePath().c_str()); - return returnCode; + return false; } void ClientProcess::swapCompletedQueue(std::list>& queue) diff --git a/hecl/lib/Database/Project.cpp b/hecl/lib/Database/Project.cpp index b2127c370..3b7cba414 100644 --- a/hecl/lib/Database/Project.cpp +++ b/hecl/lib/Database/Project.cpp @@ -10,6 +10,7 @@ #endif #include "hecl/Database.hpp" +#include "BlenderConnection.hpp" namespace hecl { @@ -379,9 +380,9 @@ static void VisitFile(const ProjectPath& path, bool force, bool fast, { for (SpecInst& spec : specInsts) { - if (spec.second->canCook(path)) + if (spec.second->canCook(path, hecl::SharedBlenderToken)) { - const DataSpecEntry* override = spec.second->overrideDataSpec(path, spec.first); + const DataSpecEntry* override = spec.second->overrideDataSpec(path, spec.first, hecl::SharedBlenderToken); if (!override) continue; ProjectPath cooked = path.getCookedPath(*override); @@ -391,7 +392,7 @@ static void VisitFile(const ProjectPath& path, bool force, bool fast, path.getModtime() > cooked.getModtime()) { progress.reportFile(override); - spec.second->doCook(path, cooked, fast, + spec.second->doCook(path, cooked, fast, hecl::SharedBlenderToken, [&](const SystemChar* extra) { progress.reportFile(override, extra);