mirror of https://github.com/AxioDL/metaforce.git
Initial ClientProcess class, require Blender 2.77
This commit is contained in:
parent
4931b9811a
commit
bbf8639a6a
|
@ -2,7 +2,7 @@ bl_info = {
|
|||
"name": "HECL",
|
||||
"author": "Jack Andersen <jackoalan@gmail.com>",
|
||||
"version": (1, 0),
|
||||
"blender": (2, 76),
|
||||
"blender": (2, 77),
|
||||
"tracker_url": "https://github.com/AxioDL/hecl/issues/new",
|
||||
"location": "Properties > Scene > HECL",
|
||||
"description": "Enables blender to gather meshes, materials, and textures for hecl",
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 87ceeb9392418f05c63da8465cc4dc419fa07d50
|
||||
Subproject commit 6cbd086204bbe0bce4ad21c179c3e4cf8ea717b8
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef HECL_CLIENT_PROCESS_HPP
|
||||
#define HECL_CLIENT_PROCESS_HPP
|
||||
|
||||
#include "hecl.hpp"
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
|
||||
class ClientProcess
|
||||
{
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
int m_verbosity;
|
||||
|
||||
public:
|
||||
struct Transaction
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Buffer,
|
||||
Cook
|
||||
} m_type;
|
||||
bool m_complete = false;
|
||||
virtual void run()=0;
|
||||
Transaction(Type tp) : m_type(tp) {}
|
||||
};
|
||||
struct BufferTransaction : Transaction
|
||||
{
|
||||
ProjectPath m_path;
|
||||
void* m_targetBuf;
|
||||
size_t m_maxLen;
|
||||
size_t m_offset;
|
||||
void run();
|
||||
BufferTransaction(const ProjectPath& path, void* target, size_t maxLen, size_t offset)
|
||||
: Transaction(Type::Buffer), m_path(path), m_targetBuf(target), m_maxLen(maxLen), m_offset(offset) {}
|
||||
};
|
||||
struct CookTransaction : Transaction
|
||||
{
|
||||
ProjectPath m_path;
|
||||
bool m_verbose;
|
||||
int m_returnVal = 0;
|
||||
void run();
|
||||
CookTransaction(const ProjectPath& path, bool verbose)
|
||||
: Transaction(Type::Cook), m_path(path), m_verbose(verbose) {}
|
||||
};
|
||||
private:
|
||||
std::list<std::unique_ptr<Transaction>> m_pendingQueue;
|
||||
std::list<std::unique_ptr<Transaction>> m_completedQueue;
|
||||
bool m_running = true;
|
||||
|
||||
struct Worker
|
||||
{
|
||||
ClientProcess& m_proc;
|
||||
std::thread m_thr;
|
||||
Worker(ClientProcess& proc);
|
||||
void proc();
|
||||
};
|
||||
std::vector<Worker> m_workers;
|
||||
|
||||
public:
|
||||
ClientProcess(int verbosityLevel=1);
|
||||
~ClientProcess() {shutdown();}
|
||||
void addBufferTransaction(const hecl::ProjectPath& path, void* target,
|
||||
size_t maxLen, size_t offset);
|
||||
void addCookTransaction(const hecl::ProjectPath& path);
|
||||
void swapCompletedQueue(std::list<std::unique_ptr<Transaction>>& queue);
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HECL_CLIENT_PROCESS_HPP
|
|
@ -102,6 +102,9 @@ public:
|
|||
|
||||
virtual bool canCook(const ProjectPath& path)
|
||||
{(void)path;LogModule.report(logvisor::Error, "not implemented");return false;}
|
||||
virtual const DataSpecEntry* overrideDataSpec(const hecl::ProjectPath& path,
|
||||
const hecl::Database::DataSpecEntry* oldEntry)
|
||||
{(void)path;return oldEntry;}
|
||||
virtual void doCook(const ProjectPath& path, const ProjectPath& cookedPath,
|
||||
bool fast, FCookProgress progress)
|
||||
{(void)path;(void)cookedPath;(void)fast;(void)progress;}
|
||||
|
|
|
@ -32,6 +32,8 @@ add_library(hecl-common
|
|||
../include/hecl/Frontend.hpp
|
||||
../include/hecl/Database.hpp
|
||||
../include/hecl/Runtime.hpp
|
||||
../include/hecl/ClientProcess.hpp
|
||||
ClientProcess.cpp
|
||||
atdna_HMDLMeta.cpp
|
||||
atdna_Frontend.cpp
|
||||
atdna_Runtime.cpp
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
#include "hecl/ClientProcess.hpp"
|
||||
#include "athena/FileReader.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace hecl
|
||||
{
|
||||
static logvisor::Module Log("hecl::ClientProcess");
|
||||
|
||||
static bool ExecProcessAndWait(bool verbose,
|
||||
const SystemChar* exePath,
|
||||
const SystemChar* args[])
|
||||
{
|
||||
#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, NULL, &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);
|
||||
CloseHandle(pinfo.hProcess);
|
||||
|
||||
if (exitCode == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
#else
|
||||
/* Assemble command args */
|
||||
std::vector<const SystemChar*> assembleArgs;
|
||||
size_t argCount = 0;
|
||||
for (const SystemChar** it=args ; *it ; ++it) ++argCount;
|
||||
assembleArgs.reserve(argCount+1);
|
||||
assembleArgs.push_back(exePath);
|
||||
for (const SystemChar** it=args ; *it ; ++it) assembleArgs.push_back(*it);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (!pid)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return false;
|
||||
else
|
||||
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))
|
||||
if (WEXITSTATUS(exitStatus) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static int GetCPUCount()
|
||||
{
|
||||
#if _WIN32
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
#else
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClientProcess::BufferTransaction::run()
|
||||
{
|
||||
athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
|
||||
if (r.hasError())
|
||||
{
|
||||
Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
|
||||
m_path.getAbsolutePath().c_str());
|
||||
return;
|
||||
}
|
||||
if (m_offset)
|
||||
r.seek(m_offset, athena::Begin);
|
||||
r.readBytesToBuf(m_targetBuf, m_maxLen);
|
||||
}
|
||||
|
||||
void ClientProcess::CookTransaction::run()
|
||||
{
|
||||
const SystemChar* args[] = {_S("cook"), m_path.getAbsolutePath().c_str()};
|
||||
if (!ExecProcessAndWait(m_verbose, _S("hecl"), args))
|
||||
Log.report(logvisor::Fatal, _S("unable to background-cook '%s'"),
|
||||
m_path.getAbsolutePath().c_str());
|
||||
}
|
||||
|
||||
ClientProcess::Worker::Worker(ClientProcess& proc)
|
||||
: m_proc(proc)
|
||||
{
|
||||
m_thr = std::thread(std::bind(&Worker::proc, this));
|
||||
}
|
||||
|
||||
void ClientProcess::Worker::proc()
|
||||
{
|
||||
while (m_proc.m_running)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_proc.m_mutex);
|
||||
if (m_proc.m_pendingQueue.size())
|
||||
{
|
||||
std::unique_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
|
||||
m_proc.m_pendingQueue.pop_front();
|
||||
lk.unlock();
|
||||
trans->run();
|
||||
lk.lock();
|
||||
m_proc.m_completedQueue.push_back(std::move(trans));
|
||||
}
|
||||
m_proc.m_cv.wait(lk);
|
||||
}
|
||||
}
|
||||
|
||||
ClientProcess::ClientProcess(int verbosityLevel)
|
||||
: m_verbosity(verbosityLevel)
|
||||
{
|
||||
int cpuCount = GetCPUCount();
|
||||
m_workers.reserve(cpuCount);
|
||||
for (int i=0 ; i<cpuCount ; ++i)
|
||||
m_workers.emplace_back(*this);
|
||||
}
|
||||
|
||||
void ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
|
||||
size_t maxLen, size_t offset)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_pendingQueue.emplace_back(new BufferTransaction(path, target, maxLen, offset));
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void ClientProcess::addCookTransaction(const hecl::ProjectPath& path)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_pendingQueue.emplace_back(new CookTransaction(path, m_verbosity != 0));
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void ClientProcess::swapCompletedQueue(std::list<std::unique_ptr<Transaction>>& queue)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
queue.swap(m_completedQueue);
|
||||
}
|
||||
|
||||
void ClientProcess::shutdown()
|
||||
{
|
||||
if (!m_running)
|
||||
return;
|
||||
m_running = false;
|
||||
m_cv.notify_all();
|
||||
for (Worker& worker : m_workers)
|
||||
if (worker.m_thr.joinable())
|
||||
worker.m_thr.join();
|
||||
}
|
||||
|
||||
}
|
|
@ -381,17 +381,20 @@ static void VisitFile(const ProjectPath& path, bool force, bool fast,
|
|||
{
|
||||
if (spec.second->canCook(path))
|
||||
{
|
||||
ProjectPath cooked = path.getCookedPath(*spec.first);
|
||||
const DataSpecEntry* override = spec.second->overrideDataSpec(path, spec.first);
|
||||
if (!override)
|
||||
continue;
|
||||
ProjectPath cooked = path.getCookedPath(*override);
|
||||
if (fast)
|
||||
cooked = cooked.getWithExtension(_S(".fast"));
|
||||
if (force || cooked.getPathType() == ProjectPath::Type::None ||
|
||||
path.getModtime() > cooked.getModtime())
|
||||
{
|
||||
progress.reportFile(spec.first);
|
||||
progress.reportFile(override);
|
||||
spec.second->doCook(path, cooked, fast,
|
||||
[&](const SystemChar* extra)
|
||||
{
|
||||
progress.reportFile(spec.first, extra);
|
||||
progress.reportFile(override, extra);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue