2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-07-19 18:45:52 +00:00

New code style refactor

This commit is contained in:
Jack Andersen 2018-12-07 19:18:42 -10:00
parent 54c466276b
commit 72193079ae
74 changed files with 14647 additions and 17058 deletions

View File

@ -18,18 +18,17 @@
extern logvisor::Module LogModule; extern logvisor::Module LogModule;
struct ToolPassInfo struct ToolPassInfo {
{ hecl::SystemString pname;
hecl::SystemString pname; hecl::SystemString cwd;
hecl::SystemString cwd; std::vector<hecl::SystemString> args;
std::vector<hecl::SystemString> args; std::vector<hecl::SystemChar> flags;
std::vector<hecl::SystemChar> flags; hecl::SystemString output;
hecl::SystemString output; hecl::Database::Project* project = nullptr;
hecl::Database::Project* project = nullptr; unsigned verbosityLevel = 0;
unsigned verbosityLevel = 0; bool force = false;
bool force = false; bool yes = false;
bool yes = false; bool gui = false;
bool gui = false;
}; };
#define RED "\033[0;31m" #define RED "\033[0;31m"
@ -48,228 +47,189 @@ struct ToolPassInfo
extern bool XTERM_COLOR; extern bool XTERM_COLOR;
class ToolBase class ToolBase {
{
protected: protected:
const ToolPassInfo& m_info; const ToolPassInfo& m_info;
bool m_good = false; bool m_good = false;
bool continuePrompt() bool continuePrompt() {
{ if (!m_info.yes) {
if (!m_info.yes) if (XTERM_COLOR)
{ hecl::Printf(_SYS_STR("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) "));
if (XTERM_COLOR) else
hecl::Printf(_SYS_STR("\n" BLUE BOLD "Continue?" NORMAL " (Y/n) ")); hecl::Printf(_SYS_STR("\nContinue? (Y/n) "));
else fflush(stdout);
hecl::Printf(_SYS_STR("\nContinue? (Y/n) "));
fflush(stdout);
int ch; int ch;
#ifndef _WIN32 #ifndef _WIN32
struct termios tioOld, tioNew; struct termios tioOld, tioNew;
tcgetattr(0, &tioOld); tcgetattr(0, &tioOld);
tioNew = tioOld; tioNew = tioOld;
tioNew.c_lflag &= ~ICANON; tioNew.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &tioNew); tcsetattr(0, TCSANOW, &tioNew);
while ((ch = getchar())) while ((ch = getchar()))
#else #else
while ((ch = getch())) while ((ch = getch()))
#endif
{
if (ch == 'n' || ch == 'N')
{
hecl::Printf(_SYS_STR("\n"));
return false;
}
if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n')
break;
}
#ifndef _WIN32
tcsetattr(0, TCSANOW, &tioOld);
#endif #endif
{
if (ch == 'n' || ch == 'N') {
hecl::Printf(_SYS_STR("\n"));
return false;
} }
hecl::Printf(_SYS_STR("\n")); if (ch == 'y' || ch == 'Y' || ch == '\r' || ch == '\n')
return true; break;
}
#ifndef _WIN32
tcsetattr(0, TCSANOW, &tioOld);
#endif
} }
hecl::Printf(_SYS_STR("\n"));
return true;
}
public: public:
ToolBase(const ToolPassInfo& info) ToolBase(const ToolPassInfo& info) : m_info(info) {
: m_info(info) hecl::VerbosityLevel = info.verbosityLevel;
{ hecl::GuiMode = info.gui;
hecl::VerbosityLevel = info.verbosityLevel; }
hecl::GuiMode = info.gui; virtual ~ToolBase() = default;
} virtual hecl::SystemString toolName() const = 0;
virtual ~ToolBase() = default; virtual int run() = 0;
virtual hecl::SystemString toolName() const=0; virtual void cancel() {}
virtual int run()=0; inline operator bool() const { return m_good; }
virtual void cancel() {}
inline operator bool() const {return m_good;}
}; };
class HelpOutput class HelpOutput {
{
public: public:
typedef void(*HelpFunc)(HelpOutput&); typedef void (*HelpFunc)(HelpOutput&);
private: private:
FILE* m_sout; FILE* m_sout;
HelpFunc m_helpFunc; HelpFunc m_helpFunc;
int m_lineWidth; int m_lineWidth;
hecl::SystemString m_wrapBuffer; hecl::SystemString m_wrapBuffer;
void _wrapBuf(hecl::SystemString& string) void _wrapBuf(hecl::SystemString& string) {
{ int counter;
int counter; hecl::SystemString::iterator it = string.begin();
hecl::SystemString::iterator it = string.begin();
while (it != string.end()) while (it != string.end()) {
{ std::ptrdiff_t v = it - string.begin();
std::ptrdiff_t v = it - string.begin();
/* copy string until the end of the line is reached */ /* copy string until the end of the line is reached */
for (counter=WRAP_INDENT ; counter < m_lineWidth ; ++counter) for (counter = WRAP_INDENT; counter < m_lineWidth; ++counter) {
{ if (it >= string.end())
if (it >= string.end()) return;
return; if (*it == _SYS_STR('\n')) {
if (*it == _SYS_STR('\n')) counter = WRAP_INDENT;
{ ++it;
counter = WRAP_INDENT;
++it;
}
if (counter == WRAP_INDENT)
{
for (int i=0 ; i<WRAP_INDENT ; ++i)
it = string.insert(it, _SYS_STR(' ')) + 1;
}
if (it >= string.end())
return;
if (*it != _SYS_STR('\n'))
++it;
}
/* check for whitespace */
if (isspace(*it))
{
*it = _SYS_STR('\n');
counter = WRAP_INDENT;
++it;
}
else
{
/* check for nearest whitespace back in string */
for (hecl::SystemString::iterator k=it ; k!=string.begin() ; --k)
{
if (isspace(*k))
{
counter = WRAP_INDENT;
if (k - string.begin() < v)
k = string.insert(it, _SYS_STR('\n'));
else
*k = _SYS_STR('\n');
it = k + 1;
break;
}
}
}
} }
if (counter == WRAP_INDENT) {
for (int i = 0; i < WRAP_INDENT; ++i)
it = string.insert(it, _SYS_STR(' ')) + 1;
}
if (it >= string.end())
return;
if (*it != _SYS_STR('\n'))
++it;
}
/* check for whitespace */
if (isspace(*it)) {
*it = _SYS_STR('\n');
counter = WRAP_INDENT;
++it;
} else {
/* check for nearest whitespace back in string */
for (hecl::SystemString::iterator k = it; k != string.begin(); --k) {
if (isspace(*k)) {
counter = WRAP_INDENT;
if (k - string.begin() < v)
k = string.insert(it, _SYS_STR('\n'));
else
*k = _SYS_STR('\n');
it = k + 1;
break;
}
}
}
} }
}
public: public:
HelpOutput(HelpFunc helpFunc)
: m_sout(NULL), m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth()) {}
HelpOutput(HelpFunc helpFunc) void go() {
: m_sout(NULL), m_helpFunc(helpFunc), m_lineWidth(hecl::GuiMode ? 120 : hecl::ConsoleWidth())
{}
void go()
{
#if _WIN32 #if _WIN32
m_sout = stdout; m_sout = stdout;
m_helpFunc(*this); m_helpFunc(*this);
#else #else
m_sout = popen("less -R", "w"); m_sout = popen("less -R", "w");
if (m_sout) if (m_sout) {
{ m_helpFunc(*this);
m_helpFunc(*this); pclose(m_sout);
pclose(m_sout); } else {
} m_sout = stdout;
else m_helpFunc(*this);
{ }
m_sout = stdout;
m_helpFunc(*this);
}
#endif #endif
} }
void print(const hecl::SystemChar* str) void print(const hecl::SystemChar* str) { hecl::FPrintf(m_sout, _SYS_STR("%s"), str); }
{
hecl::FPrintf(m_sout, _SYS_STR("%s"), str);
}
void printBold(const hecl::SystemChar* str) void printBold(const hecl::SystemChar* str) {
{ if (XTERM_COLOR)
if (XTERM_COLOR) hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL ""), str);
hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL ""), str); else
else hecl::FPrintf(m_sout, _SYS_STR("%s"), str);
hecl::FPrintf(m_sout, _SYS_STR("%s"), str); }
}
void secHead(const hecl::SystemChar* headName) void secHead(const hecl::SystemChar* headName) {
{ if (XTERM_COLOR)
if (XTERM_COLOR) hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL "\n"), headName);
hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL "\n"), headName); else
else hecl::FPrintf(m_sout, _SYS_STR("%s\n"), headName);
hecl::FPrintf(m_sout, _SYS_STR("%s\n"), headName); }
}
void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) void optionHead(const hecl::SystemChar* flag, const hecl::SystemChar* synopsis) {
{ if (XTERM_COLOR)
if (XTERM_COLOR) hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL " (%s)\n"), flag, synopsis);
hecl::FPrintf(m_sout, _SYS_STR("" BOLD "%s" NORMAL " (%s)\n"), flag, synopsis); else
else hecl::FPrintf(m_sout, _SYS_STR("%s (%s)\n"), flag, synopsis);
hecl::FPrintf(m_sout, _SYS_STR("%s (%s)\n"), flag, synopsis); }
}
void beginWrap() void beginWrap() { m_wrapBuffer.clear(); }
{
m_wrapBuffer.clear();
}
void wrap(const hecl::SystemChar* str) void wrap(const hecl::SystemChar* str) { m_wrapBuffer += str; }
{
m_wrapBuffer += str;
}
void wrapBold(const hecl::SystemChar* str) void wrapBold(const hecl::SystemChar* str) {
{ if (XTERM_COLOR)
if (XTERM_COLOR) m_wrapBuffer += _SYS_STR("" BOLD "");
m_wrapBuffer += _SYS_STR("" BOLD ""); m_wrapBuffer += str;
m_wrapBuffer += str; if (XTERM_COLOR)
if (XTERM_COLOR) m_wrapBuffer += _SYS_STR("" NORMAL "");
m_wrapBuffer += _SYS_STR("" NORMAL ""); }
}
void endWrap() void endWrap() {
{ _wrapBuf(m_wrapBuffer);
_wrapBuf(m_wrapBuffer); m_wrapBuffer += _SYS_STR('\n');
m_wrapBuffer += _SYS_STR('\n'); hecl::FPrintf(m_sout, _SYS_STR("%s"), m_wrapBuffer.c_str());
hecl::FPrintf(m_sout, _SYS_STR("%s"), m_wrapBuffer.c_str()); m_wrapBuffer.clear();
m_wrapBuffer.clear(); }
}
}; };
static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, static hecl::SystemString MakePathArgAbsolute(const hecl::SystemString& arg, const hecl::SystemString& cwd) {
const hecl::SystemString& cwd)
{
#if _WIN32 #if _WIN32
if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _SYS_STR(':')) if (arg.size() >= 2 && iswalpha(arg[0]) && arg[1] == _SYS_STR(':'))
return arg; return arg;
if (arg[0] == _SYS_STR('\\') || arg[0] == _SYS_STR('/')) if (arg[0] == _SYS_STR('\\') || arg[0] == _SYS_STR('/'))
return arg; return arg;
return cwd + _SYS_STR('\\') + arg; return cwd + _SYS_STR('\\') + arg;
#else #else
if (arg[0] == _SYS_STR('/') || arg[0] == _SYS_STR('\\')) if (arg[0] == _SYS_STR('/') || arg[0] == _SYS_STR('\\'))
return arg; return arg;
if (cwd.back() == _SYS_STR('/') || cwd.back() == _SYS_STR('\\')) if (cwd.back() == _SYS_STR('/') || cwd.back() == _SYS_STR('\\'))
return cwd + arg; return cwd + arg;
return cwd + _SYS_STR('/') + arg; return cwd + _SYS_STR('/') + arg;
#endif #endif
} }

View File

@ -4,178 +4,157 @@
#include <cstdio> #include <cstdio>
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
class ToolCook final : public ToolBase class ToolCook final : public ToolBase {
{ std::vector<hecl::ProjectPath> m_selectedItems;
std::vector<hecl::ProjectPath> m_selectedItems; std::unique_ptr<hecl::Database::Project> m_fallbackProj;
std::unique_ptr<hecl::Database::Project> m_fallbackProj; hecl::Database::Project* m_useProj;
hecl::Database::Project* m_useProj; const hecl::Database::DataSpecEntry* m_spec = nullptr;
const hecl::Database::DataSpecEntry* m_spec = nullptr; bool m_recursive = false;
bool m_recursive = false; bool m_fast = false;
bool m_fast = false;
public: public:
ToolCook(const ToolPassInfo& info) ToolCook(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
: ToolBase(info), m_useProj(info.project) /* Check for recursive flag */
{ for (hecl::SystemChar arg : info.flags)
/* Check for recursive flag */ if (arg == _SYS_STR('r'))
for (hecl::SystemChar arg : info.flags) m_recursive = true;
if (arg == _SYS_STR('r'))
m_recursive = true;
/* Scan args */ /* Scan args */
if (info.args.size()) if (info.args.size()) {
{ /* See if project path is supplied via args and use that over the getcwd one */
/* See if project path is supplied via args and use that over the getcwd one */ m_selectedItems.reserve(info.args.size());
m_selectedItems.reserve(info.args.size()); for (const hecl::SystemString& arg : info.args) {
for (const hecl::SystemString& arg : info.args) if (arg.empty())
{ continue;
if (arg.empty()) else if (!arg.compare(_SYS_STR("--fast"))) {
continue; m_fast = true;
else if (!arg.compare(_SYS_STR("--fast"))) continue;
{ } else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
m_fast = true; hecl::SystemString specName(arg.begin() + 7, arg.end());
continue; for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
} if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) m_spec = spec;
{ break;
hecl::SystemString specName(arg.begin() + 7, arg.end());
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY)
{
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str()))
{
m_spec = spec;
break;
}
}
if (!m_spec)
LogModule.report(logvisor::Fatal, "unable to find data spec '%s'", specName.c_str());
continue;
}
else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root)
{
if (!m_fallbackProj)
{
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
}
else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal,
_SYS_STR("hecl cook can only process multiple items in the same project; ")
_SYS_STR("'%s' and '%s' are different projects"),
m_fallbackProj->getProjectRootPath().getAbsolutePath().data(),
root.getAbsolutePath().data());
m_selectedItems.emplace_back(*m_useProj, subPath);
}
} }
} }
if (!m_useProj) if (!m_spec)
LogModule.report(logvisor::Fatal, "unable to find data spec '%s'", specName.c_str());
continue;
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
} else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal, LogModule.report(logvisor::Fatal,
"hecl cook must be ran within a project directory or " _SYS_STR("hecl cook can only process multiple items in the same project; ")
"provided a path within a project"); _SYS_STR("'%s' and '%s' are different projects"),
m_fallbackProj->getProjectRootPath().getAbsolutePath().data(),
/* Default case: recursive at root */ root.getAbsolutePath().data());
if (m_selectedItems.empty()) m_selectedItems.emplace_back(*m_useProj, subPath);
{
m_selectedItems.reserve(1);
m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
m_recursive = true;
} }
}
} }
if (!m_useProj)
LogModule.report(logvisor::Fatal,
"hecl cook must be ran within a project directory or "
"provided a path within a project");
static void Help(HelpOutput& help) /* Default case: recursive at root */
{ if (m_selectedItems.empty()) {
help.secHead(_SYS_STR("NAME")); m_selectedItems.reserve(1);
help.beginWrap(); m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\n")); m_recursive = true;
help.endWrap(); }
}
help.secHead(_SYS_STR("SYNOPSIS")); static void Help(HelpOutput& help) {
help.beginWrap(); help.secHead(_SYS_STR("NAME"));
help.wrap(_SYS_STR("hecl cook [-rf] [--fast] [--spec=<spec>] [<pathspec>...]\n")); help.beginWrap();
help.endWrap(); help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION")); help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("This command initiates a cooking pass on the project database. Cooking ") help.wrap(_SYS_STR("hecl cook [-rf] [--fast] [--spec=<spec>] [<pathspec>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command initiates a cooking pass on the project database. Cooking ")
_SYS_STR("is analogous to compiling in software development. The resulting object buffers ") _SYS_STR("is analogous to compiling in software development. The resulting object buffers ")
_SYS_STR("are cached within the project database. HECL performs the following ") _SYS_STR("are cached within the project database. HECL performs the following ")
_SYS_STR("tasks for each object during the cook process:\n\n")); _SYS_STR("tasks for each object during the cook process:\n\n"));
help.wrapBold(_SYS_STR("- Object Gather: ")); help.wrapBold(_SYS_STR("- Object Gather: "));
help.wrap(_SYS_STR("Files added with ")); help.wrap(_SYS_STR("Files added with "));
help.wrapBold(_SYS_STR("hecl add")); help.wrapBold(_SYS_STR("hecl add"));
help.wrap(_SYS_STR(" are queried for their dependent files (e.g. ")); help.wrap(_SYS_STR(" are queried for their dependent files (e.g. "));
help.wrapBold(_SYS_STR(".blend")); help.wrapBold(_SYS_STR(".blend"));
help.wrap(_SYS_STR(" files return any linked ")); help.wrap(_SYS_STR(" files return any linked "));
help.wrapBold(_SYS_STR(".png")); help.wrapBold(_SYS_STR(".png"));
help.wrap(_SYS_STR(" images). If the dependent files are unable to be found, the cook process aborts.\n\n")); help.wrap(_SYS_STR(" images). If the dependent files are unable to be found, the cook process aborts.\n\n"));
help.wrapBold(_SYS_STR("- Modtime Comparison: ")); help.wrapBold(_SYS_STR("- Modtime Comparison: "));
help.wrap(_SYS_STR("Files that have previously finished a cook pass are inspected for their time of ") help.wrap(_SYS_STR("Files that have previously finished a cook pass are inspected for their time of ")
_SYS_STR("last modification. If the file hasn't changed since its previous cook-pass, the ") _SYS_STR("last modification. If the file hasn't changed since its previous cook-pass, the ") _SYS_STR(
_SYS_STR("process is skipped. If the file has been moved or deleted, the object is automatically ") "process is skipped. If the file has been moved or deleted, the object is automatically ")
_SYS_STR("removed from the project database.\n\n")); _SYS_STR("removed from the project database.\n\n"));
help.wrapBold(_SYS_STR("- Cook: ")); help.wrapBold(_SYS_STR("- Cook: "));
help.wrap(_SYS_STR("A type-specific procedure compiles the file's contents into an efficient format ") help.wrap(_SYS_STR("A type-specific procedure compiles the file's contents into an efficient format ")
_SYS_STR("for use by the runtime. A data-buffer is provided to HECL.\n\n")); _SYS_STR("for use by the runtime. A data-buffer is provided to HECL.\n\n"));
help.wrapBold(_SYS_STR("- Hash and Compress: ")); help.wrapBold(_SYS_STR("- Hash and Compress: "));
help.wrap(_SYS_STR("The data-buffer is hashed and compressed before being cached in the object database.\n\n")); help.wrap(_SYS_STR("The data-buffer is hashed and compressed before being cached in the object database.\n\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)")); help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Specifies working file(s) containing production data to be cooked by HECL. ") help.wrap(_SYS_STR("Specifies working file(s) containing production data to be cooked by HECL. ")
_SYS_STR("Glob-strings may be specified (e.g. ")); _SYS_STR("Glob-strings may be specified (e.g. "));
help.wrapBold(_SYS_STR("*.blend")); help.wrapBold(_SYS_STR("*.blend"));
help.wrap(_SYS_STR(") to automatically cook all matching current-directory files in the project database. ") help.wrap(_SYS_STR(") to automatically cook all matching current-directory files in the project database. ")
_SYS_STR("If no path specified, all files in the project database are cooked.\n")); _SYS_STR("If no path specified, all files in the project database are cooked.\n"));
help.endWrap(); help.endWrap();
help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion")); help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n")); help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n"));
help.endWrap(); help.endWrap();
help.optionHead(_SYS_STR("-f"), _SYS_STR("force")); help.optionHead(_SYS_STR("-f"), _SYS_STR("force"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n")); help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n"));
help.endWrap(); help.endWrap();
help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook")); help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n")); help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n"));
help.endWrap(); help.endWrap();
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification")); help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ") help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ")
_SYS_STR("This build of hecl supports the following values of <spec>:\n")); _SYS_STR("This build of hecl supports the following values of <spec>:\n"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
{ if (!spec->m_factory)
if (!spec->m_factory) continue;
continue; help.wrap(_SYS_STR(" "));
help.wrap(_SYS_STR(" ")); help.wrapBold(spec->m_name.data());
help.wrapBold(spec->m_name.data()); help.wrap(_SYS_STR("\n"));
help.wrap(_SYS_STR("\n"));
}
} }
}
hecl::SystemString toolName() const {return _SYS_STR("cook");} hecl::SystemString toolName() const { return _SYS_STR("cook"); }
int run() int run() {
{ hecl::MultiProgressPrinter printer(true);
hecl::MultiProgressPrinter printer(true); hecl::ClientProcess cp(&printer);
hecl::ClientProcess cp(&printer); for (const hecl::ProjectPath& path : m_selectedItems)
for (const hecl::ProjectPath& path : m_selectedItems) m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp);
m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp); cp.waitUntilComplete();
cp.waitUntilComplete(); return 0;
return 0; }
}
void cancel() void cancel() { m_useProj->interruptCook(); }
{
m_useProj->interruptCook();
}
}; };

View File

@ -11,175 +11,156 @@
#include "hecl/MultiProgressPrinter.hpp" #include "hecl/MultiProgressPrinter.hpp"
class ToolExtract final : public ToolBase class ToolExtract final : public ToolBase {
{ hecl::Database::IDataSpec::ExtractPassInfo m_einfo;
hecl::Database::IDataSpec::ExtractPassInfo m_einfo; struct SpecExtractPass {
struct SpecExtractPass const hecl::Database::DataSpecEntry* m_entry;
{ std::unique_ptr<hecl::Database::IDataSpec> m_instance;
const hecl::Database::DataSpecEntry* m_entry; SpecExtractPass(const hecl::Database::DataSpecEntry* entry, std::unique_ptr<hecl::Database::IDataSpec>&& instance)
std::unique_ptr<hecl::Database::IDataSpec> m_instance; : m_entry(entry), m_instance(std::move(instance)) {}
SpecExtractPass(const hecl::Database::DataSpecEntry* entry, SpecExtractPass(const SpecExtractPass& other) = delete;
std::unique_ptr<hecl::Database::IDataSpec>&& instance) SpecExtractPass(SpecExtractPass&& other) = default;
: m_entry(entry), m_instance(std::move(instance)) {} };
SpecExtractPass(const SpecExtractPass& other) = delete; std::vector<SpecExtractPass> m_specPasses;
SpecExtractPass(SpecExtractPass&& other) = default; std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
}; std::unique_ptr<hecl::Database::Project> m_fallbackProj;
std::vector<SpecExtractPass> m_specPasses; hecl::Database::Project* m_useProj = nullptr;
std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj = nullptr;
public: public:
ToolExtract(const ToolPassInfo& info) ToolExtract(const ToolPassInfo& info) : ToolBase(info) {
: ToolBase(info) if (!m_info.args.size())
{ LogModule.report(logvisor::Fatal, "hecl extract needs a source path as its first argument");
if (!m_info.args.size())
LogModule.report(logvisor::Fatal, "hecl extract needs a source path as its first argument");
if (!info.project) if (!info.project) {
{ hecl::SystemString rootDir;
hecl::SystemString rootDir;
if (info.output.empty()) if (info.output.empty()) {
{ /* Get name from input file and init project there */
/* Get name from input file and init project there */ hecl::SystemString baseFile = info.args.front();
hecl::SystemString baseFile = info.args.front(); size_t slashPos = baseFile.rfind(_SYS_STR('/'));
size_t slashPos = baseFile.rfind(_SYS_STR('/')); if (slashPos == hecl::SystemString::npos)
if (slashPos == hecl::SystemString::npos) slashPos = baseFile.rfind(_SYS_STR('\\'));
slashPos = baseFile.rfind(_SYS_STR('\\')); if (slashPos != hecl::SystemString::npos)
if (slashPos != hecl::SystemString::npos) baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end()); size_t dotPos = baseFile.rfind(_SYS_STR('.'));
size_t dotPos = baseFile.rfind(_SYS_STR('.')); if (dotPos != hecl::SystemString::npos)
if (dotPos != hecl::SystemString::npos) baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
if (baseFile.empty()) if (baseFile.empty())
LogModule.report(logvisor::Fatal, "hecl extract must be ran within a project directory"); LogModule.report(logvisor::Fatal, "hecl extract must be ran within a project directory");
rootDir = info.cwd + baseFile; rootDir = info.cwd + baseFile;
} } else {
else if (hecl::PathRelative(info.output.c_str()))
{ rootDir = info.cwd + info.output;
if (hecl::PathRelative(info.output.c_str()))
rootDir = info.cwd + info.output;
else
rootDir = info.output;
}
size_t ErrorRef = logvisor::ErrorCount;
hecl::ProjectRootPath newProjRoot(rootDir);
newProjRoot.makeDir();
m_fallbackProj.reset(new hecl::Database::Project(newProjRoot));
if (logvisor::ErrorCount > ErrorRef)
LogModule.report(logvisor::Fatal, "unable to init project at '%s'", rootDir.c_str());
LogModule.report(logvisor::Info, _SYS_STR("initialized project at '%s/.hecl'"), rootDir.c_str());
m_useProj = m_fallbackProj.get();
}
else else
m_useProj = info.project; rootDir = info.output;
}
m_einfo.srcpath = m_info.args.front(); size_t ErrorRef = logvisor::ErrorCount;
m_einfo.force = info.force; hecl::ProjectRootPath newProjRoot(rootDir);
m_einfo.extractArgs.reserve(info.args.size()); newProjRoot.makeDir();
auto it=info.args.cbegin(); m_fallbackProj.reset(new hecl::Database::Project(newProjRoot));
++it; if (logvisor::ErrorCount > ErrorRef)
for (; it != info.args.cend(); ++it) LogModule.report(logvisor::Fatal, "unable to init project at '%s'", rootDir.c_str());
m_einfo.extractArgs.push_back(*it); LogModule.report(logvisor::Info, _SYS_STR("initialized project at '%s/.hecl'"), rootDir.c_str());
m_useProj = m_fallbackProj.get();
} else
m_useProj = info.project;
m_specPasses.reserve(hecl::Database::DATA_SPEC_REGISTRY.size()); m_einfo.srcpath = m_info.args.front();
for (const hecl::Database::DataSpecEntry* entry : hecl::Database::DATA_SPEC_REGISTRY) m_einfo.force = info.force;
{ m_einfo.extractArgs.reserve(info.args.size());
if (entry->m_factory) auto it = info.args.cbegin();
{ ++it;
auto ds = entry->m_factory(*m_useProj, hecl::Database::DataSpecTool::Extract); for (; it != info.args.cend(); ++it)
if (ds && ds->canExtract(m_einfo, m_reps)) m_einfo.extractArgs.push_back(*it);
m_specPasses.emplace_back(entry, std::move(ds));
} m_specPasses.reserve(hecl::Database::DATA_SPEC_REGISTRY.size());
} for (const hecl::Database::DataSpecEntry* entry : hecl::Database::DATA_SPEC_REGISTRY) {
if (entry->m_factory) {
auto ds = entry->m_factory(*m_useProj, hecl::Database::DataSpecTool::Extract);
if (ds && ds->canExtract(m_einfo, m_reps))
m_specPasses.emplace_back(entry, std::move(ds));
}
} }
}
static void Help(HelpOutput& help) static void Help(HelpOutput& help) {
{ help.secHead(_SYS_STR("NAME"));
help.secHead(_SYS_STR("NAME")); help.beginWrap();
help.beginWrap(); help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n"));
help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n")); help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS")); help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n")); help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION")); help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ") help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ")
_SYS_STR("package format. Each object is decoded to a working format and added to the project.\n\n")); _SYS_STR("package format. Each object is decoded to a working format and added to the project.\n\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file")); help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ") help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ")
_SYS_STR("An optional subnode specifies a named hierarchical-node specific ") _SYS_STR("An optional subnode specifies a named hierarchical-node specific ")
_SYS_STR("to the game architecture (levels/areas).")); _SYS_STR("to the game architecture (levels/areas)."));
help.endWrap(); help.endWrap();
}
hecl::SystemString toolName() const { return _SYS_STR("extract"); }
static void _recursivePrint(int level, hecl::Database::IDataSpec::ExtractReport& rep) {
for (int l = 0; l < level; ++l)
hecl::Printf(_SYS_STR(" "));
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD "%s" NORMAL ""), rep.name.c_str());
else
hecl::Printf(_SYS_STR("%s"), rep.name.c_str());
if (rep.desc.size())
hecl::Printf(_SYS_STR(" [%s]"), rep.desc.c_str());
hecl::Printf(_SYS_STR("\n"));
for (hecl::Database::IDataSpec::ExtractReport& child : rep.childOpts)
_recursivePrint(level + 1, child);
}
int run() {
if (m_specPasses.empty()) {
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("NOTHING TO EXTRACT\n"));
return 1;
} }
hecl::SystemString toolName() const {return _SYS_STR("extract");} if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO EXTRACT:\n"));
static void _recursivePrint(int level, hecl::Database::IDataSpec::ExtractReport& rep) for (hecl::Database::IDataSpec::ExtractReport& rep : m_reps) {
{ _recursivePrint(0, rep);
for (int l=0 ; l<level ; ++l) hecl::Printf(_SYS_STR("\n"));
hecl::Printf(_SYS_STR(" ")); }
fflush(stdout);
if (continuePrompt()) {
for (SpecExtractPass& ds : m_specPasses) {
if (XTERM_COLOR) if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD "%s" NORMAL ""), rep.name.c_str()); hecl::Printf(_SYS_STR("" MAGENTA BOLD "Using DataSpec %s:" NORMAL "\n"), ds.m_entry->m_name.data());
else else
hecl::Printf(_SYS_STR("%s"), rep.name.c_str()); hecl::Printf(_SYS_STR("Using DataSpec %s:\n"), ds.m_entry->m_name.data());
if (rep.desc.size()) ds.m_instance->doExtract(m_einfo, {true});
hecl::Printf(_SYS_STR(" [%s]"), rep.desc.c_str()); hecl::Printf(_SYS_STR("\n\n"));
hecl::Printf(_SYS_STR("\n")); }
for (hecl::Database::IDataSpec::ExtractReport& child : rep.childOpts)
_recursivePrint(level + 1, child);
} }
int run() return 0;
{ }
if (m_specPasses.empty())
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" RED BOLD "NOTHING TO EXTRACT" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("NOTHING TO EXTRACT\n"));
return 1;
}
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO EXTRACT:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO EXTRACT:\n"));
for (hecl::Database::IDataSpec::ExtractReport& rep : m_reps)
{
_recursivePrint(0, rep);
hecl::Printf(_SYS_STR("\n"));
}
fflush(stdout);
if (continuePrompt())
{
for (SpecExtractPass& ds : m_specPasses)
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" MAGENTA BOLD "Using DataSpec %s:" NORMAL "\n"), ds.m_entry->m_name.data());
else
hecl::Printf(_SYS_STR("Using DataSpec %s:\n"), ds.m_entry->m_name.data());
ds.m_instance->doExtract(m_einfo, {true});
hecl::Printf(_SYS_STR("\n\n"));
}
}
return 0;
}
}; };

View File

@ -4,89 +4,78 @@
#include <cstdio> #include <cstdio>
#include <functional> #include <functional>
class ToolHelp final : public ToolBase class ToolHelp final : public ToolBase {
{
public: public:
ToolHelp(const ToolPassInfo& info) ToolHelp(const ToolPassInfo& info) : ToolBase(info) {
: ToolBase(info) if (m_info.args.empty()) {
{ LogModule.report(logvisor::Error, "help requires a tool name argument");
if (m_info.args.empty()) return;
{ }
LogModule.report(logvisor::Error, "help requires a tool name argument"); m_good = true;
return; }
}
m_good = true; ~ToolHelp() {}
static void Help(HelpOutput& help) {
help.printBold(
_SYS_STR(" ___________ \n") _SYS_STR(
" ,.-'\"...........``~., \n") _SYS_STR(" "
",.-\".......................\"-., \n")
_SYS_STR(" ,/..................................\":, \n") _SYS_STR(
" .,?........................................, \n")
_SYS_STR(" /...........................................,}\n") _SYS_STR(
" ./........................................,:`^`..}\n")
_SYS_STR(" ./.......................................,:\"...../\n") _SYS_STR(
" ?.....__..................................:`...../\n")
_SYS_STR(" /__.(...\"~-,_...........................,:`....../\n") _SYS_STR(
" /(_....\"~,_....\"~,_.....................,:`...._/ \n")
_SYS_STR(" {.._$;_....\"=,_.....\"-,_......,.-~-,},.~\";/....} \n") _SYS_STR(
" ((...*~_......\"=-._...\";,,./`........../\"..../ \n")
_SYS_STR(" ,,,___.`~,......\"~.,....................`......}....../ \n") _SYS_STR(
"............(....`=-,,...`.........................(...;_,,-\" \n")
_SYS_STR("............/.`~,......`-.................................../ \n")
_SYS_STR(".............`~.*-,.....................................|,./...,__ "
"\n") _SYS_STR(",,_..........}.>-._..................................."
"|.......`=~-, \n") _SYS_STR(
".....`=~-,__......`,................................. \n")
_SYS_STR("...................`=~-,,.,........................... \n")
_SYS_STR(".........................`:,,..........................`\n")
_SYS_STR(
"...........................`=-,...............,%%`>--==`` \n")
_SYS_STR(
".................................._.........._,-%%...` \n")
_SYS_STR("...................................,\n"));
}
static void ShowHelp(const hecl::SystemString& toolName) {
/* Select tool's help-text streamer */
HelpOutput::HelpFunc helpFunc = NULL;
if (toolName == _SYS_STR("init"))
helpFunc = ToolInit::Help;
else if (toolName == _SYS_STR("spec"))
helpFunc = ToolSpec::Help;
else if (toolName == _SYS_STR("extract"))
helpFunc = ToolExtract::Help;
else if (toolName == _SYS_STR("cook"))
helpFunc = ToolCook::Help;
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
helpFunc = ToolPackage::Help;
else if (toolName == _SYS_STR("help"))
helpFunc = ToolHelp::Help;
else {
LogModule.report(logvisor::Error, _SYS_STR("unrecognized tool '%s' - can't help"), toolName.c_str());
return;
} }
~ToolHelp() HelpOutput ho(helpFunc);
{ ho.go();
} }
hecl::SystemString toolName() const { return _SYS_STR("help"); }
int run() {
static void Help(HelpOutput& help) ShowHelp(m_info.args.front());
{ return 0;
help.printBold( }
_SYS_STR(" ___________ \n")
_SYS_STR(" ,.-'\"...........``~., \n")
_SYS_STR(" ,.-\".......................\"-., \n")
_SYS_STR(" ,/..................................\":, \n")
_SYS_STR(" .,?........................................, \n")
_SYS_STR(" /...........................................,}\n")
_SYS_STR(" ./........................................,:`^`..}\n")
_SYS_STR(" ./.......................................,:\"...../\n")
_SYS_STR(" ?.....__..................................:`...../\n")
_SYS_STR(" /__.(...\"~-,_...........................,:`....../\n")
_SYS_STR(" /(_....\"~,_....\"~,_.....................,:`...._/ \n")
_SYS_STR(" {.._$;_....\"=,_.....\"-,_......,.-~-,},.~\";/....} \n")
_SYS_STR(" ((...*~_......\"=-._...\";,,./`........../\"..../ \n")
_SYS_STR(" ,,,___.`~,......\"~.,....................`......}....../ \n")
_SYS_STR("............(....`=-,,...`.........................(...;_,,-\" \n")
_SYS_STR("............/.`~,......`-.................................../ \n")
_SYS_STR(".............`~.*-,.....................................|,./...,__ \n")
_SYS_STR(",,_..........}.>-._...................................|.......`=~-, \n")
_SYS_STR(".....`=~-,__......`,................................. \n")
_SYS_STR("...................`=~-,,.,........................... \n")
_SYS_STR(".........................`:,,..........................`\n")
_SYS_STR("...........................`=-,...............,%%`>--==`` \n")
_SYS_STR(".................................._.........._,-%%...` \n")
_SYS_STR("...................................,\n"));
}
static void ShowHelp(const hecl::SystemString& toolName)
{
/* Select tool's help-text streamer */
HelpOutput::HelpFunc helpFunc = NULL;
if (toolName == _SYS_STR("init"))
helpFunc = ToolInit::Help;
else if (toolName == _SYS_STR("spec"))
helpFunc = ToolSpec::Help;
else if (toolName == _SYS_STR("extract"))
helpFunc = ToolExtract::Help;
else if (toolName == _SYS_STR("cook"))
helpFunc = ToolCook::Help;
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
helpFunc = ToolPackage::Help;
else if (toolName == _SYS_STR("help"))
helpFunc = ToolHelp::Help;
else
{
LogModule.report(logvisor::Error, _SYS_STR("unrecognized tool '%s' - can't help"), toolName.c_str());
return;
}
HelpOutput ho(helpFunc);
ho.go();
}
hecl::SystemString toolName() const {return _SYS_STR("help");}
int run()
{
ShowHelp(m_info.args.front());
return 0;
}
}; };

View File

@ -10,148 +10,128 @@
#include "nod/DiscWii.hpp" #include "nod/DiscWii.hpp"
#include "athena/FileReader.hpp" #include "athena/FileReader.hpp"
class ToolImage final : public ToolBase class ToolImage final : public ToolBase {
{ std::unique_ptr<hecl::Database::Project> m_fallbackProj;
std::unique_ptr<hecl::Database::Project> m_fallbackProj; hecl::Database::Project* m_useProj;
hecl::Database::Project* m_useProj;
public: public:
ToolImage(const ToolPassInfo& info) ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
: ToolBase(info), m_useProj(info.project) if (!info.project)
{ LogModule.report(logvisor::Fatal, "hecl image must be ran within a project directory");
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl image must be ran within a project directory");
/* Scan args */ /* Scan args */
if (info.args.size()) if (info.args.size()) {
{ /* See if project path is supplied via args and use that over the getcwd one */
/* See if project path is supplied via args and use that over the getcwd one */ for (const hecl::SystemString& arg : info.args) {
for (const hecl::SystemString& arg : info.args) if (arg.empty())
{ continue;
if (arg.empty())
continue;
hecl::SystemString subPath; hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath); hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) if (root) {
{ if (!m_fallbackProj) {
if (!m_fallbackProj) m_fallbackProj.reset(new hecl::Database::Project(root));
{ m_useProj = m_fallbackProj.get();
m_fallbackProj.reset(new hecl::Database::Project(root)); break;
m_useProj = m_fallbackProj.get(); }
break;
}
}
}
} }
if (!m_useProj) }
LogModule.report(logvisor::Fatal,
"hecl image must be ran within a project directory or "
"provided a path within a project");
} }
if (!m_useProj)
LogModule.report(logvisor::Fatal,
"hecl image must be ran within a project directory or "
"provided a path within a project");
}
~ToolImage() ~ToolImage() {}
{
}
static void Help(HelpOutput& help) static void Help(HelpOutput& help) {
{ help.secHead(_SYS_STR("NAME"));
help.secHead(_SYS_STR("NAME")); help.beginWrap();
help.beginWrap(); help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n"));
help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n")); help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS")); help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("hecl image [<input-dir>]\n")); help.wrap(_SYS_STR("hecl image [<input-dir>]\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION")); help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ") help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ")
_SYS_STR("Wii disc image. `hecl package` must have been run previously to be effective.\n")); _SYS_STR("Wii disc image. `hecl package` must have been run previously to be effective.\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory")); help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ") help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ")
_SYS_STR("Project must contain an out/sys and out/files directory to succeed.\n")); _SYS_STR("Project must contain an out/sys and out/files directory to succeed.\n"));
help.endWrap(); help.endWrap();
}
hecl::SystemString toolName() const { return _SYS_STR("image"); }
int run() {
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO IMAGE:\n"));
hecl::Printf(_SYS_STR(" %s\n"), m_useProj->getProjectRootPath().getAbsolutePath().data());
fflush(stdout);
if (continuePrompt()) {
hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), _SYS_STR("out"));
if (!outPath.isDirectory()) {
LogModule.report(logvisor::Error, _SYS_STR("%s is not a directory"), outPath.getAbsolutePath().data());
return 1;
}
hecl::ProjectPath bootBinPath(outPath, _SYS_STR("sys/boot.bin"));
if (!bootBinPath.isFile()) {
LogModule.report(logvisor::Error, _SYS_STR("%s is not a file"), bootBinPath.getAbsolutePath().data());
return 1;
}
athena::io::FileReader r(bootBinPath.getAbsolutePath());
if (r.hasError()) {
LogModule.report(logvisor::Error, _SYS_STR("unable to open %s"), bootBinPath.getAbsolutePath().data());
return 1;
}
std::string id = r.readString(6);
r.close();
hecl::SystemStringConv idView(id);
hecl::SystemString fileOut = hecl::SystemString(outPath.getAbsolutePath()) + _SYS_STR('/') + idView.c_str();
hecl::MultiProgressPrinter printer(true);
auto progFunc = [&printer](float totalProg, nod::SystemStringView fileName, size_t fileBytesXfered) {
printer.print(fileName.data(), nullptr, totalProg);
};
if (id[0] == 'G') {
fileOut += _SYS_STR(".gcm");
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == -1)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("Generating %s as GameCube image"), fileOut.c_str());
nod::DiscBuilderGCN db(fileOut, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
} else {
fileOut += _SYS_STR(".iso");
bool dualLayer;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == -1)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("Generating %s as %s-layer Wii image"), fileOut.c_str(),
dualLayer ? _SYS_STR("dual") : _SYS_STR("single"));
nod::DiscBuilderWii db(fileOut, dualLayer, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
}
} }
hecl::SystemString toolName() const {return _SYS_STR("image");} return 0;
}
int run()
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO IMAGE:\n"));
hecl::Printf(_SYS_STR(" %s\n"), m_useProj->getProjectRootPath().getAbsolutePath().data());
fflush(stdout);
if (continuePrompt())
{
hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), _SYS_STR("out"));
if (!outPath.isDirectory())
{
LogModule.report(logvisor::Error, _SYS_STR("%s is not a directory"), outPath.getAbsolutePath().data());
return 1;
}
hecl::ProjectPath bootBinPath(outPath, _SYS_STR("sys/boot.bin"));
if (!bootBinPath.isFile())
{
LogModule.report(logvisor::Error, _SYS_STR("%s is not a file"), bootBinPath.getAbsolutePath().data());
return 1;
}
athena::io::FileReader r(bootBinPath.getAbsolutePath());
if (r.hasError())
{
LogModule.report(logvisor::Error, _SYS_STR("unable to open %s"), bootBinPath.getAbsolutePath().data());
return 1;
}
std::string id = r.readString(6);
r.close();
hecl::SystemStringConv idView(id);
hecl::SystemString fileOut = hecl::SystemString(outPath.getAbsolutePath()) + _SYS_STR('/') + idView.c_str();
hecl::MultiProgressPrinter printer(true);
auto progFunc = [&printer](float totalProg, nod::SystemStringView fileName, size_t fileBytesXfered)
{
printer.print(fileName.data(), nullptr, totalProg);
};
if (id[0] == 'G')
{
fileOut += _SYS_STR(".gcm");
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == -1)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("Generating %s as GameCube image"), fileOut.c_str());
nod::DiscBuilderGCN db(fileOut, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
}
else
{
fileOut += _SYS_STR(".iso");
bool dualLayer;
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == -1)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("Generating %s as %s-layer Wii image"), fileOut.c_str(),
dualLayer ? _SYS_STR("dual") : _SYS_STR("single"));
nod::DiscBuilderWii db(fileOut, dualLayer, progFunc);
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
return 1;
}
}
return 0;
}
}; };
#endif #endif

View File

@ -3,84 +3,75 @@
#include "ToolBase.hpp" #include "ToolBase.hpp"
#include <cstdio> #include <cstdio>
class ToolInit final : public ToolBase class ToolInit final : public ToolBase {
{ const hecl::SystemString* m_dir = NULL;
const hecl::SystemString* m_dir = NULL;
public: public:
ToolInit(const ToolPassInfo& info) ToolInit(const ToolPassInfo& info) : ToolBase(info) {
: ToolBase(info) hecl::Sstat theStat;
{ const hecl::SystemString* dir;
hecl::Sstat theStat; if (info.args.size())
const hecl::SystemString* dir; dir = &info.args.front();
if (info.args.size()) else
dir = &info.args.front(); dir = &info.cwd;
else
dir = &info.cwd;
if (hecl::Stat(dir->c_str(), &theStat)) if (hecl::Stat(dir->c_str(), &theStat)) {
{ hecl::MakeDir(dir->c_str());
hecl::MakeDir(dir->c_str()); if (hecl::Stat(dir->c_str(), &theStat)) {
if (hecl::Stat(dir->c_str(), &theStat)) LogModule.report(logvisor::Fatal, _SYS_STR("unable to stat '%s'"), dir->c_str());
{ return;
LogModule.report(logvisor::Fatal, _SYS_STR("unable to stat '%s'"), dir->c_str()); }
return; }
} if (!S_ISDIR(theStat.st_mode)) {
} LogModule.report(logvisor::Fatal, _SYS_STR("'%s' is not a directory"), dir->c_str());
if (!S_ISDIR(theStat.st_mode)) return;
{
LogModule.report(logvisor::Fatal, _SYS_STR("'%s' is not a directory"), dir->c_str());
return;
}
hecl::SystemString testPath = *dir + _SYS_STR("/.hecl/beacon");
if (!hecl::Stat(testPath.c_str(), &theStat))
{
LogModule.report(logvisor::Fatal, _SYS_STR("project already exists at '%s'"), dir->c_str());
return;
}
m_dir = dir;
} }
int run() hecl::SystemString testPath = *dir + _SYS_STR("/.hecl/beacon");
{ if (!hecl::Stat(testPath.c_str(), &theStat)) {
if (!m_dir) LogModule.report(logvisor::Fatal, _SYS_STR("project already exists at '%s'"), dir->c_str());
return 1; return;
size_t ErrorRef = logvisor::ErrorCount;
hecl::Database::Project proj((hecl::ProjectRootPath(*m_dir)));
if (logvisor::ErrorCount > ErrorRef)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("initialized project at '%s/.hecl'"), m_dir->c_str());
return 0;
} }
static void Help(HelpOutput& help) m_dir = dir;
{ }
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS")); int run() {
help.beginWrap(); if (!m_dir)
help.wrap(_SYS_STR("hecl init [<dir>]\n")); return 1;
help.endWrap(); size_t ErrorRef = logvisor::ErrorCount;
hecl::Database::Project proj((hecl::ProjectRootPath(*m_dir)));
if (logvisor::ErrorCount > ErrorRef)
return 1;
LogModule.report(logvisor::Info, _SYS_STR("initialized project at '%s/.hecl'"), m_dir->c_str());
return 0;
}
help.secHead(_SYS_STR("DESCRIPTION")); static void Help(HelpOutput& help) {
help.beginWrap(); help.secHead(_SYS_STR("NAME"));
help.wrap(_SYS_STR("Creates a ")); help.beginWrap();
help.wrapBold(_SYS_STR(".hecl")); help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n"));
help.wrap(_SYS_STR(" directory within the selected directory with an initialized database index. ") help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl init [<dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("Creates a "));
help.wrapBold(_SYS_STR(".hecl"));
help.wrap(_SYS_STR(" directory within the selected directory with an initialized database index. ")
_SYS_STR("This constitutes an empty HECL project, ready for making stuff!!\n")); _SYS_STR("This constitutes an empty HECL project, ready for making stuff!!\n"));
help.endWrap(); help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path")); help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n")); help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n"));
help.endWrap(); help.endWrap();
} }
hecl::SystemString toolName() const {return _SYS_STR("init");} hecl::SystemString toolName() const { return _SYS_STR("init"); }
}; };

View File

@ -5,212 +5,181 @@
#include "ToolBase.hpp" #include "ToolBase.hpp"
#include <cstdio> #include <cstdio>
class ToolPackage final : public ToolBase class ToolPackage final : public ToolBase {
{ std::vector<hecl::ProjectPath> m_selectedItems;
std::vector<hecl::ProjectPath> m_selectedItems; std::unique_ptr<hecl::Database::Project> m_fallbackProj;
std::unique_ptr<hecl::Database::Project> m_fallbackProj; hecl::Database::Project* m_useProj;
hecl::Database::Project* m_useProj; const hecl::Database::DataSpecEntry* m_spec = nullptr;
const hecl::Database::DataSpecEntry* m_spec = nullptr; bool m_fast = false;
bool m_fast = false;
void AddSelectedItem(const hecl::ProjectPath& path) void AddSelectedItem(const hecl::ProjectPath& path) {
{ for (const hecl::ProjectPath& item : m_selectedItems)
for (const hecl::ProjectPath& item : m_selectedItems) if (item == path)
if (item == path) return;
return; m_selectedItems.push_back(path);
m_selectedItems.push_back(path); }
}
void CheckFile(const hecl::ProjectPath& path) void CheckFile(const hecl::ProjectPath& path) {
{ if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!world.blend")))
if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!world.blend"))) AddSelectedItem(path);
AddSelectedItem(path);
#if RUNTIME_ORIGINAL_IDS #if RUNTIME_ORIGINAL_IDS
else if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!original_ids.yaml"))) else if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!original_ids.yaml"))) {
{ auto pathComps = path.getPathComponents();
auto pathComps = path.getPathComponents(); if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out"))
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out")) AddSelectedItem(path);
AddSelectedItem(path); }
}
#endif #endif
}
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) {
if (path.isFile()) {
CheckFile(path);
return;
} }
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) size_t origSize = m_selectedItems.size();
{ hecl::DirectoryEnumerator dEnum(path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false,
if (path.isFile()) false, true);
{ for (const auto& ent : dEnum) {
CheckFile(path); hecl::ProjectPath childPath(path, ent.m_name);
return; if (ent.m_isDir)
} FindSelectedItems(childPath, checkGeneral && childPath.getPathComponents().size() <= 2);
else
size_t origSize = m_selectedItems.size(); CheckFile(childPath);
hecl::DirectoryEnumerator dEnum(path.getAbsolutePath(),
hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const auto& ent : dEnum)
{
hecl::ProjectPath childPath(path, ent.m_name);
if (ent.m_isDir)
FindSelectedItems(childPath, checkGeneral && childPath.getPathComponents().size() <= 2);
else
CheckFile(childPath);
}
/* Directory with 2 components not "Shared" or macOS app bundle
* and no nested !world.blend files == General PAK */
if (checkGeneral && origSize == m_selectedItems.size())
{
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out") &&
pathComps[1] != _SYS_STR("Shared") && pathComps[0].find(_SYS_STR(".app")) == hecl::SystemString::npos)
AddSelectedItem(path);
}
} }
/* Directory with 2 components not "Shared" or macOS app bundle
* and no nested !world.blend files == General PAK */
if (checkGeneral && origSize == m_selectedItems.size()) {
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out") && pathComps[1] != _SYS_STR("Shared") &&
pathComps[0].find(_SYS_STR(".app")) == hecl::SystemString::npos)
AddSelectedItem(path);
}
}
public: public:
ToolPackage(const ToolPassInfo& info) ToolPackage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
: ToolBase(info), m_useProj(info.project) if (!info.project)
{ LogModule.report(logvisor::Fatal, "hecl package must be ran within a project directory");
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl package must be ran within a project directory");
/* Scan args */ /* Scan args */
if (info.args.size()) if (info.args.size()) {
{ /* See if project path is supplied via args and use that over the getcwd one */
/* See if project path is supplied via args and use that over the getcwd one */ m_selectedItems.reserve(info.args.size());
m_selectedItems.reserve(info.args.size()); for (const hecl::SystemString& arg : info.args) {
for (const hecl::SystemString& arg : info.args) if (arg.empty())
{ continue;
if (arg.empty()) else if (!arg.compare(_SYS_STR("--fast"))) {
continue; m_fast = true;
else if (!arg.compare(_SYS_STR("--fast"))) continue;
{ } else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
m_fast = true; hecl::SystemString specName(arg.begin() + 7, arg.end());
continue; for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
} if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) m_spec = spec;
{ break;
hecl::SystemString specName(arg.begin() + 7, arg.end());
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY)
{
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str()))
{
m_spec = spec;
break;
}
}
if (!m_spec)
LogModule.report(logvisor::Fatal, "unable to find data spec '%s'", specName.c_str());
continue;
}
else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root)
{
if (!m_fallbackProj)
{
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
}
else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal,
_SYS_STR("hecl package can only process multiple items in the same project; ")
_SYS_STR("'%s' and '%s' are different projects"),
m_fallbackProj->getProjectRootPath().getAbsolutePath().data(),
root.getAbsolutePath().data());
FindSelectedItems({*m_useProj, subPath}, true);
}
} }
} }
if (!m_useProj) if (!m_spec)
LogModule.report(logvisor::Fatal, "unable to find data spec '%s'", specName.c_str());
continue;
} else if (arg.size() >= 2 && arg[0] == _SYS_STR('-') && arg[1] == _SYS_STR('-'))
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
} else if (m_fallbackProj->getProjectRootPath() != root)
LogModule.report(logvisor::Fatal, LogModule.report(logvisor::Fatal,
"hecl package must be ran within a project directory or " _SYS_STR("hecl package can only process multiple items in the same project; ")
"provided a path within a project"); _SYS_STR("'%s' and '%s' are different projects"),
m_fallbackProj->getProjectRootPath().getAbsolutePath().data(),
root.getAbsolutePath().data());
/* Default case: recursive at root */ FindSelectedItems({*m_useProj, subPath}, true);
if (m_selectedItems.empty()) }
FindSelectedItems({*m_useProj, _SYS_STR("")}, true); }
} }
if (!m_useProj)
LogModule.report(logvisor::Fatal,
"hecl package must be ran within a project directory or "
"provided a path within a project");
static void Help(HelpOutput& help) /* Default case: recursive at root */
{ if (m_selectedItems.empty())
help.secHead(_SYS_STR("NAME")); FindSelectedItems({*m_useProj, _SYS_STR("")}, true);
help.beginWrap(); }
help.wrap(_SYS_STR("hecl-pack\n")
_SYS_STR("hecl-package - Package objects within the project database\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS")); static void Help(HelpOutput& help) {
help.beginWrap(); help.secHead(_SYS_STR("NAME"));
help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\n")); help.beginWrap();
help.endWrap(); help.wrap(_SYS_STR("hecl-pack\n") _SYS_STR("hecl-package - Package objects within the project database\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION")); help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ") help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\n"));
_SYS_STR("is analogous to linking in software development. All objects necessary to ") help.endWrap();
_SYS_STR("generate a complete package are gathered, grouped, and indexed within a .upak file.\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("DESCRIPTION"));
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification")); help.beginWrap();
help.beginWrap(); help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ")
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking and generating the package. ") _SYS_STR("is analogous to linking in software development. All objects necessary to ") _SYS_STR(
"generate a complete package are gathered, grouped, and indexed within a .upak file.\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking and generating the package. ")
_SYS_STR("This build of hecl supports the following values of <spec>:\n")); _SYS_STR("This build of hecl supports the following values of <spec>:\n"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
{ if (!spec->m_factory)
if (!spec->m_factory) continue;
continue; help.wrap(_SYS_STR(" "));
help.wrap(_SYS_STR(" ")); help.wrapBold(spec->m_name.data());
help.wrapBold(spec->m_name.data()); help.wrap(_SYS_STR("\n"));
help.wrap(_SYS_STR("\n")); }
} help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("OPTIONS")); help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory")); help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap(); help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting package from. ") help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting package from. ")
_SYS_STR("If any dependent files fall outside this subdirectory, they will be implicitly ") _SYS_STR("If any dependent files fall outside this subdirectory, they will be implicitly ")
_SYS_STR("gathered and packaged.\n")); _SYS_STR("gathered and packaged.\n"));
help.endWrap(); help.endWrap();
}
hecl::SystemString toolName() const { return _SYS_STR("package"); }
int run() {
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO PACKAGE:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO PACKAGE:\n"));
for (auto& item : m_selectedItems)
hecl::Printf(_SYS_STR(" %s\n"), item.getRelativePath().data());
fflush(stdout);
if (continuePrompt()) {
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems) {
if (!m_useProj->packagePath(path, printer, m_fast, m_spec, &cp))
LogModule.report(logvisor::Error, _SYS_STR("Unable to package %s"), path.getAbsolutePath().data());
}
cp.waitUntilComplete();
} }
hecl::SystemString toolName() const {return _SYS_STR("package");} return 0;
}
int run() void cancel() { m_useProj->interruptCook(); }
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" GREEN BOLD "ABOUT TO PACKAGE:" NORMAL "\n"));
else
hecl::Printf(_SYS_STR("ABOUT TO PACKAGE:\n"));
for (auto& item : m_selectedItems)
hecl::Printf(_SYS_STR(" %s\n"), item.getRelativePath().data());
fflush(stdout);
if (continuePrompt())
{
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems)
{
if (!m_useProj->packagePath(path, printer, m_fast, m_spec, &cp))
LogModule.report(logvisor::Error, _SYS_STR("Unable to package %s"), path.getAbsolutePath().data());
}
cp.waitUntilComplete();
}
return 0;
}
void cancel()
{
m_useProj->interruptCook();
}
}; };

View File

@ -4,153 +4,128 @@
#include <cstdio> #include <cstdio>
#include <map> #include <map>
class ToolSpec final : public ToolBase class ToolSpec final : public ToolBase {
{ enum Mode { MLIST = 0, MENABLE, MDISABLE } mode = MLIST;
enum Mode
{
MLIST = 0,
MENABLE,
MDISABLE
} mode = MLIST;
public: public:
ToolSpec(const ToolPassInfo& info) ToolSpec(const ToolPassInfo& info) : ToolBase(info) {
: ToolBase(info) if (info.args.empty())
{ return;
if (info.args.empty())
return;
if (!info.project) if (!info.project)
LogModule.report(logvisor::Fatal, LogModule.report(logvisor::Fatal, "hecl spec must be ran within a project directory");
"hecl spec must be ran within a project directory");
const auto& specs = info.project->getDataSpecs(); const auto& specs = info.project->getDataSpecs();
hecl::SystemString firstArg = info.args.front(); hecl::SystemString firstArg = info.args.front();
hecl::ToLower(firstArg); hecl::ToLower(firstArg);
if (!firstArg.compare(_SYS_STR("enable"))) if (!firstArg.compare(_SYS_STR("enable")))
mode = MENABLE; mode = MENABLE;
else if (!firstArg.compare(_SYS_STR("disable"))) else if (!firstArg.compare(_SYS_STR("disable")))
mode = MDISABLE; mode = MDISABLE;
else
return;
if (info.args.size() < 2)
LogModule.report(logvisor::Fatal, "Speclist argument required");
auto it = info.args.begin();
++it;
for (; it != info.args.end(); ++it) {
bool found = false;
for (auto& spec : specs) {
if (!it->compare(spec.spec.m_name)) {
found = true;
break;
}
}
if (!found)
LogModule.report(logvisor::Fatal, _SYS_STR("'%s' is not found in the dataspec registry"), it->c_str());
}
}
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-spec - Configure target data options\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl spec [enable|disable] [<specname>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(
_SYS_STR("This command configures the HECL project with the user's preferred target DataSpecs.\n\n")
_SYS_STR("Providing enable/disable argument will bulk-set the enable status of the provided spec(s)")
_SYS_STR("list. If enable/disable is not provided, a list of supported DataSpecs is printed.\n\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<specname>..."), _SYS_STR("DataSpec name(s)"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies platform-names to enable/disable"));
help.endWrap();
}
hecl::SystemString toolName() const { return _SYS_STR("spec"); }
int run() {
if (!m_info.project) {
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD CYAN "%s" NORMAL "\n"), spec->m_name.data());
else else
return; hecl::Printf(_SYS_STR("%s\n"), spec->m_name.data());
hecl::Printf(_SYS_STR(" %s\n"), spec->m_desc.data());
if (info.args.size() < 2) }
LogModule.report(logvisor::Fatal, "Speclist argument required"); return 0;
auto it = info.args.begin();
++it;
for (;it != info.args.end();
++it)
{
bool found = false;
for (auto& spec : specs)
{
if (!it->compare(spec.spec.m_name))
{
found = true;
break;
}
}
if (!found)
LogModule.report(logvisor::Fatal,
_SYS_STR("'%s' is not found in the dataspec registry"),
it->c_str());
}
} }
static void Help(HelpOutput& help) const auto& specs = m_info.project->getDataSpecs();
{ if (mode == MLIST) {
help.secHead(_SYS_STR("NAME")); for (auto& spec : specs) {
help.beginWrap(); if (XTERM_COLOR)
help.wrap(_SYS_STR("hecl-spec - Configure target data options\n")); hecl::Printf(_SYS_STR("" BOLD CYAN "%s" NORMAL ""), spec.spec.m_name.data());
help.endWrap(); else
hecl::Printf(_SYS_STR("%s"), spec.spec.m_name.data());
help.secHead(_SYS_STR("SYNOPSIS")); if (spec.active) {
help.beginWrap(); if (XTERM_COLOR)
help.wrap(_SYS_STR("hecl spec [enable|disable] [<specname>...]\n")); hecl::Printf(_SYS_STR(" " BOLD GREEN "[ENABLED]" NORMAL ""));
help.endWrap(); else
hecl::Printf(_SYS_STR(" [ENABLED]"));
help.secHead(_SYS_STR("DESCRIPTION")); }
help.beginWrap(); hecl::Printf(_SYS_STR("\n %s\n"), spec.spec.m_desc.data());
help.wrap(_SYS_STR("This command configures the HECL project with the user's preferred target DataSpecs.\n\n") }
_SYS_STR("Providing enable/disable argument will bulk-set the enable status of the provided spec(s)") return 0;
_SYS_STR("list. If enable/disable is not provided, a list of supported DataSpecs is printed.\n\n"));
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<specname>..."), _SYS_STR("DataSpec name(s)"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies platform-names to enable/disable"));
help.endWrap();
} }
hecl::SystemString toolName() const {return _SYS_STR("spec");} std::vector<hecl::SystemString> opSpecs;
auto it = m_info.args.begin();
int run() ++it;
{ for (; it != m_info.args.end(); ++it) {
if (!m_info.project) hecl::SystemString itName = *it;
{ hecl::ToLower(itName);
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) for (auto& spec : specs) {
{ hecl::SystemString compName(spec.spec.m_name);
if (XTERM_COLOR) hecl::ToLower(compName);
hecl::Printf(_SYS_STR("" BOLD CYAN "%s" NORMAL "\n"), spec->m_name.data()); if (!itName.compare(compName)) {
else opSpecs.emplace_back(spec.spec.m_name);
hecl::Printf(_SYS_STR("%s\n"), spec->m_name.data()); break;
hecl::Printf(_SYS_STR(" %s\n"), spec->m_desc.data());
}
return 0;
} }
}
const auto& specs = m_info.project->getDataSpecs();
if (mode == MLIST)
{
for (auto& spec : specs)
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD CYAN "%s" NORMAL ""), spec.spec.m_name.data());
else
hecl::Printf(_SYS_STR("%s"), spec.spec.m_name.data());
if (spec.active)
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR(" " BOLD GREEN "[ENABLED]" NORMAL ""));
else
hecl::Printf(_SYS_STR(" [ENABLED]"));
}
hecl::Printf(_SYS_STR("\n %s\n"), spec.spec.m_desc.data());
}
return 0;
}
std::vector<hecl::SystemString> opSpecs;
auto it = m_info.args.begin();
++it;
for (; it != m_info.args.end() ; ++it)
{
hecl::SystemString itName = *it;
hecl::ToLower(itName);
for (auto& spec : specs)
{
hecl::SystemString compName(spec.spec.m_name);
hecl::ToLower(compName);
if (!itName.compare(compName))
{
opSpecs.emplace_back(spec.spec.m_name);
break;
}
}
}
if (opSpecs.size())
{
if (mode == MENABLE)
m_info.project->enableDataSpecs(opSpecs);
else if (mode == MDISABLE)
m_info.project->disableDataSpecs(opSpecs);
}
return 0;
} }
if (opSpecs.size()) {
if (mode == MENABLE)
m_info.project->enableDataSpecs(opSpecs);
else if (mode == MDISABLE)
m_info.project->disableDataSpecs(opSpecs);
}
return 0;
}
}; };

View File

@ -35,7 +35,6 @@ logvisor::Module LogModule("hecl::Driver");
bool XTERM_COLOR = false; bool XTERM_COLOR = false;
/* /*
#define HECL_GIT 1234567 #define HECL_GIT 1234567
#define HECL_GIT_S "1234567" #define HECL_GIT_S "1234567"
@ -44,49 +43,45 @@ bool XTERM_COLOR = false;
*/ */
/* Main usage message */ /* Main usage message */
static void printHelp(const hecl::SystemChar* pname) static void printHelp(const hecl::SystemChar* pname) {
{ if (XTERM_COLOR)
if (XTERM_COLOR) hecl::Printf(_SYS_STR("" BOLD "HECL" NORMAL ""));
hecl::Printf(_SYS_STR("" BOLD "HECL" NORMAL "")); else
else hecl::Printf(_SYS_STR("HECL"));
hecl::Printf(_SYS_STR("HECL"));
#if HECL_HAS_NOD #if HECL_HAS_NOD
# define TOOL_LIST "extract|init|cook|package|image|help" #define TOOL_LIST "extract|init|cook|package|image|help"
#else #else
# define TOOL_LIST "extract|init|cook|package|help" #define TOOL_LIST "extract|init|cook|package|help"
#endif #endif
#if HECL_GIT #if HECL_GIT
hecl::Printf(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: %s " TOOL_LIST "\n"), pname); hecl::Printf(_SYS_STR(" Commit " HECL_GIT_S " " HECL_BRANCH_S "\nUsage: %s " TOOL_LIST "\n"), pname);
#elif HECL_VER #elif HECL_VER
hecl::Printf(_SYS_STR(" Version " HECL_VER_S "\nUsage: %s " TOOL_LIST "\n"), pname); hecl::Printf(_SYS_STR(" Version " HECL_VER_S "\nUsage: %s " TOOL_LIST "\n"), pname);
#else #else
hecl::Printf(_SYS_STR("\nUsage: %s " TOOL_LIST "\n"), pname); hecl::Printf(_SYS_STR("\nUsage: %s " TOOL_LIST "\n"), pname);
#endif #endif
} }
/* Regex patterns */ /* Regex patterns */
static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript|std::regex::optimize); static const hecl::SystemRegex regOPEN(_SYS_STR("-o([^\"]*|\\S*)"), std::regex::ECMAScript | std::regex::optimize);
static ToolBase* ToolPtr = nullptr; static ToolBase* ToolPtr = nullptr;
/* SIGINT will gracefully close blender connections and delete blends in progress */ /* SIGINT will gracefully close blender connections and delete blends in progress */
static void SIGINTHandler(int sig) static void SIGINTHandler(int sig) {
{ if (ToolPtr)
if (ToolPtr) ToolPtr->cancel();
ToolPtr->cancel(); hecl::blender::Connection::Shutdown();
hecl::blender::Connection::Shutdown(); logvisor::KillProcessTree();
logvisor::KillProcessTree(); exit(1);
exit(1);
} }
static logvisor::Module AthenaLog("Athena"); static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file, static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, const char* fmt, ...) {
const char*, int line, const char* fmt, ...) va_list ap;
{ va_start(ap, fmt);
va_list ap; AthenaLog.report(logvisor::Level(level), fmt, ap);
va_start(ap, fmt); va_end(ap);
AthenaLog.report(logvisor::Level(level), fmt, ap);
va_end(ap);
} }
static hecl::SystemChar cwdbuf[1024]; static hecl::SystemChar cwdbuf[1024];
@ -100,232 +95,210 @@ static void SIGWINCHHandler(int sig) {}
int main(int argc, const char** argv) int main(int argc, const char** argv)
#endif #endif
{ {
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
{ printf("%s\n", HECL_DLPACKAGE);
printf("%s\n", HECL_DLPACKAGE); return 100;
return 100; }
}
#if _WIN32 #if _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
#else #else
std::setlocale(LC_ALL, "en-US.UTF-8"); std::setlocale(LC_ALL, "en-US.UTF-8");
#endif #endif
/* Xterm check */ /* Xterm check */
#if _WIN32 #if _WIN32
const char* conemuANSI = getenv("ConEmuANSI"); const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON")) if (conemuANSI && !strcmp(conemuANSI, "ON"))
XTERM_COLOR = true; XTERM_COLOR = true;
#else #else
const char* term = getenv("TERM"); const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5)) if (term && !strncmp(term, "xterm", 5))
XTERM_COLOR = true; XTERM_COLOR = true;
signal(SIGWINCH, SIGWINCHHandler); signal(SIGWINCH, SIGWINCHHandler);
#endif #endif
signal(SIGINT, SIGINTHandler); signal(SIGINT, SIGINTHandler);
logvisor::RegisterStandardExceptions(); logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger(); logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc); atSetExceptionHandler(AthenaExc);
/* Basic usage check */ /* Basic usage check */
if (argc == 1) if (argc == 1) {
{ printHelp(argv[0]);
printHelp(argv[0]);
#if WIN_PAUSE #if WIN_PAUSE
system("PAUSE"); system("PAUSE");
#endif #endif
return 0; return 0;
} } else if (argc == 0) {
else if (argc == 0) printHelp(_SYS_STR("hecl"));
{
printHelp(_SYS_STR("hecl"));
#if WIN_PAUSE #if WIN_PAUSE
system("PAUSE"); system("PAUSE");
#endif #endif
return 0; return 0;
} }
/* Prepare DataSpecs */ /* Prepare DataSpecs */
HECLRegisterDataSpecs(); HECLRegisterDataSpecs();
/* Assemble common tool pass info */ /* Assemble common tool pass info */
ToolPassInfo info; ToolPassInfo info;
info.pname = argv[0]; info.pname = argv[0];
if (hecl::Getcwd(cwdbuf, 1024)) if (hecl::Getcwd(cwdbuf, 1024)) {
{ info.cwd = cwdbuf;
info.cwd = cwdbuf; if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\'))
if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\'))
#if _WIN32 #if _WIN32
info.cwd += _SYS_STR('\\'); info.cwd += _SYS_STR('\\');
#else #else
info.cwd += _SYS_STR('/'); info.cwd += _SYS_STR('/');
#endif #endif
if (hecl::PathRelative(argv[0])) if (hecl::PathRelative(argv[0]))
ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/'); ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
hecl::SystemString Argv0(argv[0]); hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\")); hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos) if (lastIdx != hecl::SystemString::npos)
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx); ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
}
/* Concatenate args */
std::vector<hecl::SystemString> args;
args.reserve(argc - 2);
for (int i = 2; i < argc; ++i)
args.push_back(hecl::SystemString(argv[i]));
if (!args.empty()) {
/* Extract output argument */
for (auto it = args.cbegin(); it != args.cend();) {
const hecl::SystemString& arg = *it;
hecl::SystemRegexMatch oMatch;
if (std::regex_search(arg, oMatch, regOPEN)) {
const hecl::SystemString& token = oMatch[1].str();
if (token.size()) {
if (info.output.empty())
info.output = oMatch[1].str();
it = args.erase(it);
} else {
it = args.erase(it);
if (it == args.end())
break;
if (info.output.empty())
info.output = *it;
it = args.erase(it);
}
continue;
}
++it;
} }
/* Concatenate args */ /* Iterate flags */
std::vector<hecl::SystemString> args; for (auto it = args.cbegin(); it != args.cend();) {
args.reserve(argc-2); const hecl::SystemString& arg = *it;
for (int i=2 ; i<argc ; ++i) if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-')) {
args.push_back(hecl::SystemString(argv[i])); ++it;
continue;
}
if (!args.empty()) for (auto chit = arg.cbegin() + 1; chit != arg.cend(); ++chit) {
{ if (*chit == _SYS_STR('v'))
/* Extract output argument */ ++info.verbosityLevel;
for (auto it = args.cbegin() ; it != args.cend() ;) else if (*chit == _SYS_STR('f'))
{ info.force = true;
const hecl::SystemString& arg = *it; else if (*chit == _SYS_STR('y'))
hecl::SystemRegexMatch oMatch; info.yes = true;
if (std::regex_search(arg, oMatch, regOPEN)) else if (*chit == _SYS_STR('g'))
{ info.gui = true;
const hecl::SystemString& token = oMatch[1].str();
if (token.size())
{
if (info.output.empty())
info.output = oMatch[1].str();
it = args.erase(it);
}
else
{
it = args.erase(it);
if (it == args.end())
break;
if (info.output.empty())
info.output = *it;
it = args.erase(it);
}
continue;
}
++it;
}
/* Iterate flags */
for (auto it = args.cbegin() ; it != args.cend() ;)
{
const hecl::SystemString& arg = *it;
if (arg.size() < 2 || arg[0] != _SYS_STR('-') || arg[1] == _SYS_STR('-'))
{
++it;
continue;
}
for (auto chit = arg.cbegin() + 1 ; chit != arg.cend() ; ++chit)
{
if (*chit == _SYS_STR('v'))
++info.verbosityLevel;
else if (*chit == _SYS_STR('f'))
info.force = true;
else if (*chit == _SYS_STR('y'))
info.yes = true;
else if (*chit == _SYS_STR('g'))
info.gui = true;
else
info.flags.push_back(*chit);
}
it = args.erase(it);
}
/* Gather remaining args */
info.args.reserve(args.size());
for (const hecl::SystemString& arg : args)
info.args.push_back(arg);
}
/* Attempt to find hecl project */
hecl::ProjectRootPath rootPath = hecl::SearchForProject(info.cwd);
std::unique_ptr<hecl::Database::Project> project;
if (rootPath)
{
size_t ErrorRef = logvisor::ErrorCount;
hecl::Database::Project* newProj = new hecl::Database::Project(rootPath);
if (logvisor::ErrorCount > ErrorRef)
{
#if WIN_PAUSE
system("PAUSE");
#endif
delete newProj;
return 1;
}
project.reset(newProj);
info.project = newProj;
}
/* Construct selected tool */
hecl::SystemString toolName(argv[1]);
hecl::ToLower(toolName);
std::unique_ptr<ToolBase> tool;
size_t ErrorRef = logvisor::ErrorCount;
if (toolName == _SYS_STR("init"))
tool.reset(new ToolInit(info));
else if (toolName == _SYS_STR("spec"))
tool.reset(new ToolSpec(info));
else if (toolName == _SYS_STR("extract"))
tool.reset(new ToolExtract(info));
else if (toolName == _SYS_STR("cook"))
tool.reset(new ToolCook(info));
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
tool.reset(new ToolPackage(info));
#if HECL_HAS_NOD
else if (toolName == _SYS_STR("image"))
tool.reset(new ToolImage(info));
#endif
else if (toolName == _SYS_STR("help"))
tool.reset(new ToolHelp(info));
else
{
FILE* fp = hecl::Fopen(argv[1], _SYS_STR("rb"));
if (!fp)
LogModule.report(logvisor::Error, _SYS_STR("unrecognized tool '%s'"), toolName.c_str());
else else
{ info.flags.push_back(*chit);
/* Shortcut-case: implicit extract */ }
fclose(fp);
info.args.insert(info.args.begin(), argv[1]); it = args.erase(it);
tool.reset(new ToolExtract(info));
}
} }
if (logvisor::ErrorCount > ErrorRef) /* Gather remaining args */
{ info.args.reserve(args.size());
for (const hecl::SystemString& arg : args)
info.args.push_back(arg);
}
/* Attempt to find hecl project */
hecl::ProjectRootPath rootPath = hecl::SearchForProject(info.cwd);
std::unique_ptr<hecl::Database::Project> project;
if (rootPath) {
size_t ErrorRef = logvisor::ErrorCount;
hecl::Database::Project* newProj = new hecl::Database::Project(rootPath);
if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE #if WIN_PAUSE
system("PAUSE"); system("PAUSE");
#endif #endif
return 1; delete newProj;
return 1;
} }
project.reset(newProj);
info.project = newProj;
}
if (info.verbosityLevel) /* Construct selected tool */
LogModule.report(logvisor::Info, _SYS_STR("Constructed tool '%s' %d\n"), hecl::SystemString toolName(argv[1]);
tool->toolName().c_str(), info.verbosityLevel); hecl::ToLower(toolName);
std::unique_ptr<ToolBase> tool;
/* Run tool */ size_t ErrorRef = logvisor::ErrorCount;
ErrorRef = logvisor::ErrorCount; if (toolName == _SYS_STR("init"))
ToolPtr = tool.get(); tool.reset(new ToolInit(info));
int retval = tool->run(); else if (toolName == _SYS_STR("spec"))
ToolPtr = nullptr; tool.reset(new ToolSpec(info));
if (logvisor::ErrorCount > ErrorRef) else if (toolName == _SYS_STR("extract"))
{ tool.reset(new ToolExtract(info));
hecl::blender::Connection::Shutdown(); else if (toolName == _SYS_STR("cook"))
tool.reset(new ToolCook(info));
else if (toolName == _SYS_STR("package") || toolName == _SYS_STR("pack"))
tool.reset(new ToolPackage(info));
#if HECL_HAS_NOD
else if (toolName == _SYS_STR("image"))
tool.reset(new ToolImage(info));
#endif
else if (toolName == _SYS_STR("help"))
tool.reset(new ToolHelp(info));
else {
FILE* fp = hecl::Fopen(argv[1], _SYS_STR("rb"));
if (!fp)
LogModule.report(logvisor::Error, _SYS_STR("unrecognized tool '%s'"), toolName.c_str());
else {
/* Shortcut-case: implicit extract */
fclose(fp);
info.args.insert(info.args.begin(), argv[1]);
tool.reset(new ToolExtract(info));
}
}
if (logvisor::ErrorCount > ErrorRef) {
#if WIN_PAUSE #if WIN_PAUSE
system("PAUSE"); system("PAUSE");
#endif #endif
return 1; return 1;
} }
if (info.verbosityLevel)
LogModule.report(logvisor::Info, _SYS_STR("Constructed tool '%s' %d\n"), tool->toolName().c_str(),
info.verbosityLevel);
/* Run tool */
ErrorRef = logvisor::ErrorCount;
ToolPtr = tool.get();
int retval = tool->run();
ToolPtr = nullptr;
if (logvisor::ErrorCount > ErrorRef) {
hecl::blender::Connection::Shutdown(); hecl::blender::Connection::Shutdown();
#if WIN_PAUSE #if WIN_PAUSE
system("PAUSE"); system("PAUSE");
#endif #endif
return retval; return 1;
}
hecl::blender::Connection::Shutdown();
#if WIN_PAUSE
system("PAUSE");
#endif
return retval;
} }

2
hecl/extern/athena vendored

@ -1 +1 @@
Subproject commit e1b29fda7acf3a17a297a02a63a5f11e94eb2328 Subproject commit 7243c687a82eef87a4df604eae6725fae2570573

2
hecl/extern/boo vendored

@ -1 +1 @@
Subproject commit 2c2c72bfd1e59815bfb90041974ef1e9f57325cb Subproject commit 058ea23a00ce01ac93bf9ad369fa80858b185a00

View File

@ -2,8 +2,7 @@
#include "hecl/Frontend.hpp" #include "hecl/Frontend.hpp"
namespace hecl::Backend namespace hecl::Backend {
{
struct ExtensionSlot; struct ExtensionSlot;
using IR = Frontend::IR; using IR = Frontend::IR;
@ -11,286 +10,271 @@ using Diagnostics = Frontend::Diagnostics;
using SourceLocation = Frontend::SourceLocation; using SourceLocation = Frontend::SourceLocation;
using ArithmeticOp = IR::Instruction::ArithmeticOpType; using ArithmeticOp = IR::Instruction::ArithmeticOpType;
enum class TexGenSrc : uint8_t enum class TexGenSrc : uint8_t { Position, Normal, UV };
{
Position, enum class BlendFactor : uint8_t {
Normal, Zero,
UV One,
SrcColor,
InvSrcColor,
DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
Original = 0xff
}; };
enum class BlendFactor : uint8_t enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
{
Zero, enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
One,
SrcColor, struct TextureInfo {
InvSrcColor, TexGenSrc src;
DstColor, int mapIdx;
InvDstColor, int uvIdx;
SrcAlpha, int mtxIdx;
InvSrcAlpha, bool normalize;
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
Original = 0xff
}; };
enum class ZTest : uint8_t enum class ReflectionType { None, Simple, Indirect };
{
None,
LEqual,
Greater,
Equal,
GEqual,
Original = 0xff
};
enum class CullMode : uint8_t class IBackend {
{
None,
Backface,
Frontface,
Original = 0xff
};
struct TextureInfo
{
TexGenSrc src;
int mapIdx;
int uvIdx;
int mtxIdx;
bool normalize;
};
enum class ReflectionType
{
None,
Simple,
Indirect
};
class IBackend
{
public: public:
virtual void reset(const IR& ir, Diagnostics& diag)=0; virtual void reset(const IR& ir, Diagnostics& diag) = 0;
}; };
/** /**
* @brief Hash subclass for identifying shaders and their metadata * @brief Hash subclass for identifying shaders and their metadata
*/ */
class ShaderTag : public Hash class ShaderTag : public Hash {
{ union {
union uint64_t m_meta = 0;
{ struct {
uint64_t m_meta = 0; uint8_t m_colorCount;
struct uint8_t m_uvCount;
{ uint8_t m_weightCount;
uint8_t m_colorCount; uint8_t m_skinSlotCount;
uint8_t m_uvCount; uint8_t m_primitiveType;
uint8_t m_weightCount; uint8_t m_reflectionType;
uint8_t m_skinSlotCount; bool m_depthTest : 1;
uint8_t m_primitiveType; bool m_depthWrite : 1;
uint8_t m_reflectionType; bool m_backfaceCulling : 1;
bool m_depthTest:1; bool m_alphaTest : 1;
bool m_depthWrite:1;
bool m_backfaceCulling:1;
bool m_alphaTest:1;
};
}; };
};
public: public:
ShaderTag() = default; ShaderTag() = default;
ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling, Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest) bool alphaTest)
: Hash(source), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), : Hash(source)
m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), , m_colorCount(c)
m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling), , m_uvCount(u)
m_alphaTest(alphaTest) , m_weightCount(w)
{hash ^= m_meta;} , m_skinSlotCount(s)
ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, , m_primitiveType(uint8_t(pt))
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling, , m_reflectionType(uint8_t(reflectionType))
bool alphaTest) , m_depthTest(depthTest)
: Hash(ir.m_hash), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), , m_depthWrite(depthWrite)
m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), , m_backfaceCulling(backfaceCulling)
m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling), , m_alphaTest(alphaTest) {
m_alphaTest(alphaTest) hash ^= m_meta;
{hash ^= m_meta;} }
ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling, Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest) bool alphaTest)
: Hash(hashin), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), : Hash(ir.m_hash)
m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), , m_colorCount(c)
m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling), , m_uvCount(u)
m_alphaTest(alphaTest) , m_weightCount(w)
{hash ^= m_meta;} , m_skinSlotCount(s)
ShaderTag(uint64_t comphashin, uint64_t meta) , m_primitiveType(uint8_t(pt))
: Hash(comphashin), m_meta(meta) {} , m_reflectionType(uint8_t(reflectionType))
ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {} , m_depthTest(depthTest)
uint8_t getColorCount() const {return m_colorCount;} , m_depthWrite(depthWrite)
uint8_t getUvCount() const {return m_uvCount;} , m_backfaceCulling(backfaceCulling)
uint8_t getWeightCount() const {return m_weightCount;} , m_alphaTest(alphaTest) {
uint8_t getSkinSlotCount() const {return m_skinSlotCount;} hash ^= m_meta;
boo::Primitive getPrimType() const {return boo::Primitive(m_primitiveType);} }
Backend::ReflectionType getReflectionType() const {return Backend::ReflectionType(m_reflectionType);} ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
bool getDepthTest() const {return m_depthTest;} Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool getDepthWrite() const {return m_depthWrite;} bool alphaTest)
bool getBackfaceCulling() const {return m_backfaceCulling;} : Hash(hashin)
bool getAlphaTest() const {return m_alphaTest;} , m_colorCount(c)
uint64_t getMetaData() const {return m_meta;} , m_uvCount(u)
, m_weightCount(w)
, m_skinSlotCount(s)
, m_primitiveType(uint8_t(pt))
, m_reflectionType(uint8_t(reflectionType))
, m_depthTest(depthTest)
, m_depthWrite(depthWrite)
, m_backfaceCulling(backfaceCulling)
, m_alphaTest(alphaTest) {
hash ^= m_meta;
}
ShaderTag(uint64_t comphashin, uint64_t meta) : Hash(comphashin), m_meta(meta) {}
ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {}
uint8_t getColorCount() const { return m_colorCount; }
uint8_t getUvCount() const { return m_uvCount; }
uint8_t getWeightCount() const { return m_weightCount; }
uint8_t getSkinSlotCount() const { return m_skinSlotCount; }
boo::Primitive getPrimType() const { return boo::Primitive(m_primitiveType); }
Backend::ReflectionType getReflectionType() const { return Backend::ReflectionType(m_reflectionType); }
bool getDepthTest() const { return m_depthTest; }
bool getDepthWrite() const { return m_depthWrite; }
bool getBackfaceCulling() const { return m_backfaceCulling; }
bool getAlphaTest() const { return m_alphaTest; }
uint64_t getMetaData() const { return m_meta; }
std::vector<boo::VertexElementDescriptor> vertexFormat() const std::vector<boo::VertexElementDescriptor> vertexFormat() const {
{ std::vector<boo::VertexElementDescriptor> ret;
std::vector<boo::VertexElementDescriptor> ret; size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount;
size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount; ret.resize(elemCount);
ret.resize(elemCount);
ret[0].semantic = boo::VertexSemantic::Position3; ret[0].semantic = boo::VertexSemantic::Position3;
ret[1].semantic = boo::VertexSemantic::Normal3; ret[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2; size_t e = 2;
for (size_t i=0 ; i<m_colorCount ; ++i, ++e) for (size_t i = 0; i < m_colorCount; ++i, ++e) {
{ ret[e].semantic = boo::VertexSemantic::ColorUNorm;
ret[e].semantic = boo::VertexSemantic::ColorUNorm; ret[e].semanticIdx = i;
ret[e].semanticIdx = i;
}
for (size_t i=0 ; i<m_uvCount ; ++i, ++e)
{
ret[e].semantic = boo::VertexSemantic::UV2;
ret[e].semanticIdx = i;
}
for (size_t i=0 ; i<m_weightCount ; ++i, ++e)
{
ret[e].semantic = boo::VertexSemantic::Weight;
ret[e].semanticIdx = i;
}
return ret;
} }
boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext, for (size_t i = 0; i < m_uvCount; ++i, ++e) {
std::pair<BlendFactor, BlendFactor> blendFactors) const; ret[e].semantic = boo::VertexSemantic::UV2;
ret[e].semanticIdx = i;
}
for (size_t i = 0; i < m_weightCount; ++i, ++e) {
ret[e].semantic = boo::VertexSemantic::Weight;
ret[e].semanticIdx = i;
}
return ret;
}
boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const;
}; };
struct Function struct Function {
{ std::string_view m_source;
std::string_view m_source; std::string_view m_entry;
std::string_view m_entry; Function() = default;
Function() = default; Function(std::string_view source, std::string_view entry) : m_source(source), m_entry(entry) {}
Function(std::string_view source, std::string_view entry)
: m_source(source), m_entry(entry) {}
}; };
struct ExtensionSlot struct ExtensionSlot {
{ Function lighting;
Function lighting; Function post;
Function post; size_t blockCount = 0;
size_t blockCount = 0; const char** blockNames = nullptr;
const char** blockNames = nullptr; size_t texCount = 0;
size_t texCount = 0; const Backend::TextureInfo* texs = nullptr;
const Backend::TextureInfo* texs = nullptr; Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original; Backend::BlendFactor dstFactor = Backend::BlendFactor::Original;
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original; Backend::ZTest depthTest = Backend::ZTest::Original;
Backend::ZTest depthTest = Backend::ZTest::Original; Backend::CullMode cullMode = Backend::CullMode::Backface;
Backend::CullMode cullMode = Backend::CullMode::Backface; bool noDepthWrite = false;
bool noDepthWrite = false; bool noColorWrite = false;
bool noColorWrite = false; bool noAlphaWrite = false;
bool noAlphaWrite = false; bool noAlphaOverwrite = false;
bool noAlphaOverwrite = false; bool noReflection = false;
bool noReflection = false; bool forceAlphaTest = false;
bool forceAlphaTest = false;
ExtensionSlot(size_t blockCount = 0, ExtensionSlot(size_t blockCount = 0, const char** blockNames = nullptr, size_t texCount = 0,
const char** blockNames = nullptr, const Backend::TextureInfo* texs = nullptr,
size_t texCount = 0, Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
const Backend::TextureInfo* texs = nullptr, Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original, Backend::ZTest depthTest = Backend::ZTest::Original,
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original, Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
Backend::ZTest depthTest = Backend::ZTest::Original, bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noReflection = false, bool forceAlphaTest = false)
bool noDepthWrite = false, : blockCount(blockCount)
bool noColorWrite = false, , blockNames(blockNames)
bool noAlphaWrite = false, , texCount(texCount)
bool noAlphaOverwrite = false, , texs(texs)
bool noReflection = false, , srcFactor(srcFactor)
bool forceAlphaTest = false) , dstFactor(dstFactor)
: blockCount(blockCount), blockNames(blockNames), texCount(texCount), texs(texs), , depthTest(depthTest)
srcFactor(srcFactor), dstFactor(dstFactor), depthTest(depthTest), cullMode(cullMode), , cullMode(cullMode)
noDepthWrite(noDepthWrite), noColorWrite(noColorWrite), noAlphaWrite(noAlphaWrite), , noDepthWrite(noDepthWrite)
noAlphaOverwrite(noAlphaOverwrite), noReflection(noReflection), forceAlphaTest(forceAlphaTest) {} , noColorWrite(noColorWrite)
, noAlphaWrite(noAlphaWrite)
, noAlphaOverwrite(noAlphaOverwrite)
, noReflection(noReflection)
, forceAlphaTest(forceAlphaTest) {}
mutable uint64_t m_hash = 0; mutable uint64_t m_hash = 0;
void calculateHash() const void calculateHash() const {
{ XXH64_state_t st;
XXH64_state_t st; XXH64_reset(&st, 0);
XXH64_reset(&st, 0); if (!lighting.m_source.empty())
if (!lighting.m_source.empty()) XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size());
XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size()); if (!post.m_source.empty())
if (!post.m_source.empty()) XXH64_update(&st, post.m_source.data(), post.m_source.size());
XXH64_update(&st, post.m_source.data(), post.m_source.size()); for (size_t i = 0; i < texCount; ++i) {
for (size_t i = 0; i < texCount; ++i) const Backend::TextureInfo& tinfo = texs[i];
{ XXH64_update(&st, &tinfo, sizeof(tinfo));
const Backend::TextureInfo& tinfo = texs[i];
XXH64_update(&st, &tinfo, sizeof(tinfo));
}
XXH64_update(&st, &srcFactor, offsetof(ExtensionSlot, m_hash) - offsetof(ExtensionSlot, srcFactor));
m_hash = XXH64_digest(&st);
}
uint64_t hash() const
{
if (m_hash == 0)
calculateHash();
return m_hash;
} }
XXH64_update(&st, &srcFactor, offsetof(ExtensionSlot, m_hash) - offsetof(ExtensionSlot, srcFactor));
m_hash = XXH64_digest(&st);
}
uint64_t hash() const {
if (m_hash == 0)
calculateHash();
return m_hash;
}
}; };
inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext, inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const std::pair<BlendFactor, BlendFactor> blendFactors) const {
{ boo::ZTest zTest;
boo::ZTest zTest; switch (ext.depthTest) {
switch (ext.depthTest) case hecl::Backend::ZTest::Original:
{ default:
case hecl::Backend::ZTest::Original: zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None;
default: break;
zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; case hecl::Backend::ZTest::None:
break; zTest = boo::ZTest::None;
case hecl::Backend::ZTest::None: break;
zTest = boo::ZTest::None; case hecl::Backend::ZTest::LEqual:
break; zTest = boo::ZTest::LEqual;
case hecl::Backend::ZTest::LEqual: break;
zTest = boo::ZTest::LEqual; case hecl::Backend::ZTest::Greater:
break; zTest = boo::ZTest::Greater;
case hecl::Backend::ZTest::Greater: break;
zTest = boo::ZTest::Greater; case hecl::Backend::ZTest::Equal:
break; zTest = boo::ZTest::Equal;
case hecl::Backend::ZTest::Equal: break;
zTest = boo::ZTest::Equal; case hecl::Backend::ZTest::GEqual:
break; zTest = boo::ZTest::GEqual;
case hecl::Backend::ZTest::GEqual: break;
zTest = boo::ZTest::GEqual; }
break;
}
return { return {boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? blendFactors.first : ext.srcFactor),
boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? blendFactors.first : ext.srcFactor), boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? blendFactors.second : ext.dstFactor),
boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? blendFactors.second : ext.dstFactor), getPrimType(),
getPrimType(), zTest, ext.noDepthWrite ? false : getDepthWrite(), zTest,
!ext.noColorWrite, !ext.noAlphaWrite, ext.noDepthWrite ? false : getDepthWrite(),
(ext.cullMode == hecl::Backend::CullMode::Original) ? !ext.noColorWrite,
(getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : !ext.noAlphaWrite,
boo::CullMode(ext.cullMode), !ext.noAlphaOverwrite (ext.cullMode == hecl::Backend::CullMode::Original)
}; ? (getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)
: boo::CullMode(ext.cullMode),
!ext.noAlphaOverwrite};
} }
} } // namespace hecl::Backend
namespace std namespace std {
{ template <>
template <> struct hash<hecl::Backend::ShaderTag> struct hash<hecl::Backend::ShaderTag> {
{ size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept { return val.valSizeT(); }
size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept
{return val.valSizeT();}
}; };
} } // namespace std

View File

@ -2,51 +2,39 @@
#include "ProgrammableCommon.hpp" #include "ProgrammableCommon.hpp"
namespace hecl::Backend namespace hecl::Backend {
{
#define HECL_GLSL_VERT_UNIFORM_BLOCK_NAME "HECLVertUniform" #define HECL_GLSL_VERT_UNIFORM_BLOCK_NAME "HECLVertUniform"
#define HECL_GLSL_TEXMTX_UNIFORM_BLOCK_NAME "HECLTexMtxUniform" #define HECL_GLSL_TEXMTX_UNIFORM_BLOCK_NAME "HECLTexMtxUniform"
struct GLSL : ProgrammableCommon struct GLSL : ProgrammableCommon {
{ void reset(const IR& ir, Diagnostics& diag);
void reset(const IR& ir, Diagnostics& diag); std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
std::string makeVert(unsigned col, unsigned uv, unsigned w, const TextureInfo* extTexs, ReflectionType reflectionType) const;
unsigned skinSlots, size_t extTexCount, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
const TextureInfo* extTexs, ReflectionType reflectionType) const; BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
bool alphaTest, ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
BlendFactor srcFactor, BlendFactor dstFactor, size_t extTexCount, const TextureInfo* extTexs) const;
const Function& lighting=Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames,
bool alphaTest,
ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting,
const Function& post,
size_t extTexCount, const TextureInfo* extTexs) const;
private: private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const; std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const; std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const; std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const; std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const; std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const std::string EmitVec3(const atVec4f& vec) const {
{ athena::simd_floats f(vec.simd);
athena::simd_floats f(vec.simd); return hecl::Format("vec3(%g,%g,%g)", f[0], f[1], f[2]);
return hecl::Format("vec3(%g,%g,%g)", f[0], f[1], f[2]); }
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
{ return hecl::Format("vec3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
return hecl::Format("vec3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str()); }
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
}; };
} } // namespace hecl::Backend

File diff suppressed because it is too large Load Diff

View File

@ -2,47 +2,36 @@
#include "ProgrammableCommon.hpp" #include "ProgrammableCommon.hpp"
namespace hecl::Backend namespace hecl::Backend {
{
struct HLSL : ProgrammableCommon struct HLSL : ProgrammableCommon {
{ void reset(const IR& ir, Diagnostics& diag);
void reset(const IR& ir, Diagnostics& diag); std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
std::string makeVert(unsigned col, unsigned uv, unsigned w, const TextureInfo* extTexs, ReflectionType reflectionType) const;
unsigned skinSlots, size_t extTexCount, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
const TextureInfo* extTexs, ReflectionType reflectionType) const; BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
bool alphaTest, ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
BlendFactor srcFactor, BlendFactor dstFactor, size_t extTexCount, const TextureInfo* extTexs) const;
const Function& lighting=Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames,
bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting,
const Function& post, size_t extTexCount,
const TextureInfo* extTexs) const;
private: private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const; std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const; std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const; std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const; std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const; std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const std::string EmitVec3(const atVec4f& vec) const {
{ athena::simd_floats f(vec.simd);
athena::simd_floats f(vec.simd); return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]); }
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
{ return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str()); }
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
}; };
} } // namespace hecl::Backend

View File

@ -2,48 +2,37 @@
#include "ProgrammableCommon.hpp" #include "ProgrammableCommon.hpp"
namespace hecl::Backend namespace hecl::Backend {
{
struct Metal : ProgrammableCommon struct Metal : ProgrammableCommon {
{ void reset(const IR& ir, Diagnostics& diag);
void reset(const IR& ir, Diagnostics& diag); std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
std::string makeVert(unsigned col, unsigned uv, unsigned w, const TextureInfo* extTexs, ReflectionType reflectionType) const;
unsigned skinSlots, size_t extTexCount, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
const TextureInfo* extTexs, ReflectionType reflectionType) const; BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting = Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
BlendFactor srcFactor, BlendFactor dstFactor, size_t extTexCount, const TextureInfo* extTexs) const;
const Function& lighting=Function()) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest,
ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting,
const Function& post, size_t extTexCount,
const TextureInfo* extTexs) const;
private: private:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const; std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const; std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots) const; std::string GenerateVertUniformStruct(unsigned skinSlots) const;
std::string GenerateFragOutStruct() const; std::string GenerateFragOutStruct() const;
std::string GenerateAlphaTest() const; std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const; std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const std::string EmitVec3(const atVec4f& vec) const {
{ athena::simd_floats f(vec.simd);
athena::simd_floats f(vec.simd); return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]); }
}
std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const {
{ return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str()); }
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const; std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
}; };
} } // namespace hecl::Backend

View File

@ -7,133 +7,82 @@
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
namespace hecl::Backend namespace hecl::Backend {
{
struct ProgrammableCommon : IBackend struct ProgrammableCommon : IBackend {
{ std::string m_colorExpr;
std::string m_colorExpr; std::string m_alphaExpr;
std::string m_alphaExpr; BlendFactor m_blendSrc;
BlendFactor m_blendSrc; BlendFactor m_blendDst;
BlendFactor m_blendDst; bool m_lighting = false;
bool m_lighting = false;
struct TexSampling struct TexSampling {
{ int mapIdx = -1;
int mapIdx = -1; int tcgIdx = -1;
int tcgIdx = -1; };
}; std::vector<TexSampling> m_texSamplings;
std::vector<TexSampling> m_texSamplings; unsigned m_texMapEnd = 0;
unsigned m_texMapEnd = 0; unsigned m_extMapStart = 8;
unsigned m_extMapStart = 8;
struct TexCoordGen struct TexCoordGen {
{ TexGenSrc m_src;
TexGenSrc m_src; int m_uvIdx = 0;
int m_uvIdx = 0; int m_mtx = -1;
int m_mtx = -1; bool m_norm = false;
bool m_norm = false; std::string m_gameFunction;
std::string m_gameFunction; std::vector<atVec4f> m_gameArgs;
std::vector<atVec4f> m_gameArgs; };
}; std::vector<TexCoordGen> m_tcgs;
std::vector<TexCoordGen> m_tcgs; std::vector<size_t> m_texMtxRefs;
std::vector<size_t> m_texMtxRefs;
using IBackend::reset; using IBackend::reset;
void reset(const IR& ir, Diagnostics& diag, const char* backendName); void reset(const IR& ir, Diagnostics& diag, const char* backendName);
static const char* BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor); static const char* BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor);
private: private:
unsigned addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize); unsigned addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize);
unsigned addTexSampling(unsigned mapIdx, unsigned tcgIdx); unsigned addTexSampling(unsigned mapIdx, unsigned tcgIdx);
std::string RecursiveTraceColor(const IR& ir, Diagnostics& diag, std::string RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle);
const IR::Instruction& inst, bool toSwizzle); std::string RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle);
std::string RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, unsigned RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, int mtx, bool normalize);
const IR::Instruction& inst, bool toSwizzle);
unsigned RecursiveTraceTexGen(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst,
int mtx, bool normalize);
std::string EmitSamplingUseRaw(unsigned samplingIdx) const std::string EmitSamplingUseRaw(unsigned samplingIdx) const { return hecl::Format("sampling%u", samplingIdx); }
{
return hecl::Format("sampling%u", samplingIdx);
}
std::string EmitSamplingUseRGB(unsigned samplingIdx) const std::string EmitSamplingUseRGB(unsigned samplingIdx) const { return hecl::Format("sampling%u.rgb", samplingIdx); }
{
return hecl::Format("sampling%u.rgb", samplingIdx);
}
std::string EmitSamplingUseAlpha(unsigned samplingIdx) const std::string EmitSamplingUseAlpha(unsigned samplingIdx) const { return hecl::Format("sampling%u.a", samplingIdx); }
{
return hecl::Format("sampling%u.a", samplingIdx);
}
std::string EmitColorRegUseRaw(unsigned idx) const std::string EmitColorRegUseRaw(unsigned idx) const { return hecl::Format("colorReg%u", idx); }
{
return hecl::Format("colorReg%u", idx);
}
std::string EmitColorRegUseRGB(unsigned idx) const std::string EmitColorRegUseRGB(unsigned idx) const { return hecl::Format("colorReg%u.rgb", idx); }
{
return hecl::Format("colorReg%u.rgb", idx);
}
std::string EmitColorRegUseAlpha(unsigned idx) const std::string EmitColorRegUseAlpha(unsigned idx) const { return hecl::Format("colorReg%u.a", idx); }
{
return hecl::Format("colorReg%u.a", idx);
}
std::string EmitLightingRaw() const std::string EmitLightingRaw() const { return std::string("lighting"); }
{
return std::string("lighting");
}
std::string EmitLightingRGB() const std::string EmitLightingRGB() const { return std::string("lighting.rgb"); }
{
return std::string("lighting.rgb");
}
std::string EmitLightingAlpha() const std::string EmitLightingAlpha() const { return std::string("lighting.a"); }
{
return std::string("lighting.a");
}
virtual std::string EmitVec3(const atVec4f& vec) const=0; virtual std::string EmitVec3(const atVec4f& vec) const = 0;
virtual std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const=0; virtual std::string EmitVec3(const std::string& a, const std::string& b, const std::string& c) const = 0;
std::string EmitVal(float val) const std::string EmitVal(float val) const { return hecl::Format("%g", val); }
{
return hecl::Format("%g", val);
}
std::string EmitAdd(const std::string& a, const std::string& b) const std::string EmitAdd(const std::string& a, const std::string& b) const { return '(' + a + '+' + b + ')'; }
{
return '(' + a + '+' + b + ')';
}
std::string EmitSub(const std::string& a, const std::string& b) const std::string EmitSub(const std::string& a, const std::string& b) const { return '(' + a + '-' + b + ')'; }
{
return '(' + a + '-' + b + ')';
}
std::string EmitMult(const std::string& a, const std::string& b) const std::string EmitMult(const std::string& a, const std::string& b) const { return '(' + a + '*' + b + ')'; }
{
return '(' + a + '*' + b + ')';
}
std::string EmitDiv(const std::string& a, const std::string& b) const std::string EmitDiv(const std::string& a, const std::string& b) const { return '(' + a + '/' + b + ')'; }
{
return '(' + a + '/' + b + ')';
}
std::string EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, std::string EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const std::string& a, const atInt8 swiz[4]) const; const atInt8 swiz[4]) const;
std::string EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, std::string EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const std::string& a, const atInt8 swiz[4]) const; const atInt8 swiz[4]) const;
}; };
} } // namespace hecl::Backend

View File

@ -29,12 +29,11 @@ class BitVector {
enum { BITWORD_SIZE = (unsigned)sizeof(BitWord) * CHAR_BIT }; enum { BITWORD_SIZE = (unsigned)sizeof(BitWord) * CHAR_BIT };
static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32, static_assert(BITWORD_SIZE == 64 || BITWORD_SIZE == 32, "Unsupported word size");
"Unsupported word size");
BitWord *Bits; // Actual bits. BitWord* Bits; // Actual bits.
unsigned Size; // Size of bitvector in bits. unsigned Size; // Size of bitvector in bits.
unsigned Capacity; // Number of BitWords allocated in the Bits array. unsigned Capacity; // Number of BitWords allocated in the Bits array.
public: public:
typedef unsigned size_type; typedef unsigned size_type;
@ -42,20 +41,20 @@ public:
class reference { class reference {
friend class BitVector; friend class BitVector;
BitWord *WordRef; BitWord* WordRef;
unsigned BitPos; unsigned BitPos;
reference(); // Undefined reference(); // Undefined
public: public:
reference(BitVector &b, unsigned Idx) { reference(BitVector& b, unsigned Idx) {
WordRef = &b.Bits[Idx / BITWORD_SIZE]; WordRef = &b.Bits[Idx / BITWORD_SIZE];
BitPos = Idx % BITWORD_SIZE; BitPos = Idx % BITWORD_SIZE;
} }
reference(const reference&) = default; reference(const reference&) = default;
reference &operator=(reference t) { reference& operator=(reference t) {
*this = bool(t); *this = bool(t);
return *this; return *this;
} }
@ -68,29 +67,24 @@ public:
return *this; return *this;
} }
operator bool() const { operator bool() const { return ((*WordRef) & (BitWord(1) << BitPos)) != 0; }
return ((*WordRef) & (BitWord(1) << BitPos)) != 0;
}
}; };
/// BitVector default ctor - Creates an empty bitvector. /// BitVector default ctor - Creates an empty bitvector.
BitVector() : Size(0), Capacity(0) { BitVector() : Size(0), Capacity(0) { Bits = nullptr; }
Bits = nullptr;
}
/// BitVector ctor - Creates a bitvector of specified number of bits. All /// BitVector ctor - Creates a bitvector of specified number of bits. All
/// bits are initialized to the specified value. /// bits are initialized to the specified value.
explicit BitVector(unsigned s, bool t = false) : Size(s) { explicit BitVector(unsigned s, bool t = false) : Size(s) {
Capacity = NumBitWords(s); Capacity = NumBitWords(s);
Bits = (BitWord *)std::malloc(Capacity * sizeof(BitWord)); Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
init_words(Bits, Capacity, t); init_words(Bits, Capacity, t);
if (t) if (t)
clear_unused_bits(); clear_unused_bits();
} }
/// BitVector copy ctor. /// BitVector copy ctor.
BitVector(const BitVector &RHS) : Size(RHS.size()) { BitVector(const BitVector& RHS) : Size(RHS.size()) {
if (Size == 0) { if (Size == 0) {
Bits = nullptr; Bits = nullptr;
Capacity = 0; Capacity = 0;
@ -98,19 +92,16 @@ public:
} }
Capacity = NumBitWords(RHS.size()); Capacity = NumBitWords(RHS.size());
Bits = (BitWord *)std::malloc(Capacity * sizeof(BitWord)); Bits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
std::memcpy(Bits, RHS.Bits, Capacity * sizeof(BitWord)); std::memcpy(Bits, RHS.Bits, Capacity * sizeof(BitWord));
} }
BitVector(BitVector &&RHS) BitVector(BitVector&& RHS) : Bits(RHS.Bits), Size(RHS.Size), Capacity(RHS.Capacity) {
: Bits(RHS.Bits), Size(RHS.Size), Capacity(RHS.Capacity) {
RHS.Bits = nullptr; RHS.Bits = nullptr;
RHS.Size = RHS.Capacity = 0; RHS.Size = RHS.Capacity = 0;
} }
~BitVector() { ~BitVector() { std::free(Bits); }
std::free(Bits);
}
/// empty - Tests whether there are no bits in this bitvector. /// empty - Tests whether there are no bits in this bitvector.
bool empty() const { return Size == 0; } bool empty() const { return Size == 0; }
@ -148,9 +139,7 @@ public:
} }
/// none - Returns true if none of the bits are set. /// none - Returns true if none of the bits are set.
bool none() const { bool none() const { return !any(); }
return !any();
}
/// find_first - Returns the index of the first set bit, -1 if none /// find_first - Returns the index of the first set bit, -1 if none
/// of the bits are set. /// of the bits are set.
@ -178,7 +167,7 @@ public:
return WordPos * BITWORD_SIZE + countTrailingZeros(Copy); return WordPos * BITWORD_SIZE + countTrailingZeros(Copy);
// Check subsequent words. // Check subsequent words.
for (unsigned i = WordPos+1; i < NumBitWords(size()); ++i) for (unsigned i = WordPos + 1; i < NumBitWords(size()); ++i)
if (Bits[i] != 0) if (Bits[i] != 0)
return i * BITWORD_SIZE + countTrailingZeros(Bits[i]); return i * BITWORD_SIZE + countTrailingZeros(Bits[i]);
return -1; return -1;
@ -199,8 +188,7 @@ public:
break; break;
} }
} }
if (good) if (good) {
{
unsigned space = BucketSz - (idx % BucketSz); unsigned space = BucketSz - (idx % BucketSz);
if (space >= Length) if (space >= Length)
return idx; return idx;
@ -210,16 +198,14 @@ public:
} }
/// clear - Clear all bits. /// clear - Clear all bits.
void clear() { void clear() { Size = 0; }
Size = 0;
}
/// resize - Grow or shrink the bitvector. /// resize - Grow or shrink the bitvector.
void resize(unsigned N, bool t = false) { void resize(unsigned N, bool t = false) {
if (N > Capacity * BITWORD_SIZE) { if (N > Capacity * BITWORD_SIZE) {
unsigned OldCapacity = Capacity; unsigned OldCapacity = Capacity;
grow(N); grow(N);
init_words(&Bits[OldCapacity], (Capacity-OldCapacity), t); init_words(&Bits[OldCapacity], (Capacity - OldCapacity), t);
} }
// Set any old unused bits that are now included in the BitVector. This // Set any old unused bits that are now included in the BitVector. This
@ -241,24 +227,25 @@ public:
} }
// Set, reset, flip // Set, reset, flip
BitVector &set() { BitVector& set() {
init_words(Bits, Capacity, true); init_words(Bits, Capacity, true);
clear_unused_bits(); clear_unused_bits();
return *this; return *this;
} }
BitVector &set(unsigned Idx) { BitVector& set(unsigned Idx) {
assert(Bits && "Bits never allocated"); assert(Bits && "Bits never allocated");
Bits[Idx / BITWORD_SIZE] |= BitWord(1) << (Idx % BITWORD_SIZE); Bits[Idx / BITWORD_SIZE] |= BitWord(1) << (Idx % BITWORD_SIZE);
return *this; return *this;
} }
/// set - Efficiently set a range of bits in [I, E) /// set - Efficiently set a range of bits in [I, E)
BitVector &set(unsigned I, unsigned E) { BitVector& set(unsigned I, unsigned E) {
assert(I <= E && "Attempted to set backwards range!"); assert(I <= E && "Attempted to set backwards range!");
assert(E <= size() && "Attempted to set out-of-bounds range!"); assert(E <= size() && "Attempted to set out-of-bounds range!");
if (I == E) return *this; if (I == E)
return *this;
if (I / BITWORD_SIZE == E / BITWORD_SIZE) { if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
BitWord EMask = 1UL << (E % BITWORD_SIZE); BitWord EMask = 1UL << (E % BITWORD_SIZE);
@ -282,22 +269,23 @@ public:
return *this; return *this;
} }
BitVector &reset() { BitVector& reset() {
init_words(Bits, Capacity, false); init_words(Bits, Capacity, false);
return *this; return *this;
} }
BitVector &reset(unsigned Idx) { BitVector& reset(unsigned Idx) {
Bits[Idx / BITWORD_SIZE] &= ~(BitWord(1) << (Idx % BITWORD_SIZE)); Bits[Idx / BITWORD_SIZE] &= ~(BitWord(1) << (Idx % BITWORD_SIZE));
return *this; return *this;
} }
/// reset - Efficiently reset a range of bits in [I, E) /// reset - Efficiently reset a range of bits in [I, E)
BitVector &reset(unsigned I, unsigned E) { BitVector& reset(unsigned I, unsigned E) {
assert(I <= E && "Attempted to reset backwards range!"); assert(I <= E && "Attempted to reset backwards range!");
assert(E <= size() && "Attempted to reset out-of-bounds range!"); assert(E <= size() && "Attempted to reset out-of-bounds range!");
if (I == E) return *this; if (I == E)
return *this;
if (I / BITWORD_SIZE == E / BITWORD_SIZE) { if (I / BITWORD_SIZE == E / BITWORD_SIZE) {
BitWord EMask = 1UL << (E % BITWORD_SIZE); BitWord EMask = 1UL << (E % BITWORD_SIZE);
@ -321,38 +309,36 @@ public:
return *this; return *this;
} }
BitVector &flip() { BitVector& flip() {
for (unsigned i = 0; i < NumBitWords(size()); ++i) for (unsigned i = 0; i < NumBitWords(size()); ++i)
Bits[i] = ~Bits[i]; Bits[i] = ~Bits[i];
clear_unused_bits(); clear_unused_bits();
return *this; return *this;
} }
BitVector &flip(unsigned Idx) { BitVector& flip(unsigned Idx) {
Bits[Idx / BITWORD_SIZE] ^= BitWord(1) << (Idx % BITWORD_SIZE); Bits[Idx / BITWORD_SIZE] ^= BitWord(1) << (Idx % BITWORD_SIZE);
return *this; return *this;
} }
// Indexing. // Indexing.
reference operator[](unsigned Idx) { reference operator[](unsigned Idx) {
assert (Idx < Size && "Out-of-bounds Bit access."); assert(Idx < Size && "Out-of-bounds Bit access.");
return reference(*this, Idx); return reference(*this, Idx);
} }
bool operator[](unsigned Idx) const { bool operator[](unsigned Idx) const {
assert (Idx < Size && "Out-of-bounds Bit access."); assert(Idx < Size && "Out-of-bounds Bit access.");
BitWord Mask = BitWord(1) << (Idx % BITWORD_SIZE); BitWord Mask = BitWord(1) << (Idx % BITWORD_SIZE);
return (Bits[Idx / BITWORD_SIZE] & Mask) != 0; return (Bits[Idx / BITWORD_SIZE] & Mask) != 0;
} }
bool test(unsigned Idx) const { bool test(unsigned Idx) const { return (*this)[Idx]; }
return (*this)[Idx];
}
/// Test if any common bits are set. /// Test if any common bits are set.
bool anyCommon(const BitVector &RHS) const { bool anyCommon(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size()); unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size()); unsigned RHSWords = NumBitWords(RHS.size());
for (unsigned i = 0, e = std::min(ThisWords, RHSWords); i != e; ++i) for (unsigned i = 0, e = std::min(ThisWords, RHSWords); i != e; ++i)
if (Bits[i] & RHS.Bits[i]) if (Bits[i] & RHS.Bits[i])
return true; return true;
@ -360,9 +346,9 @@ public:
} }
// Comparison operators. // Comparison operators.
bool operator==(const BitVector &RHS) const { bool operator==(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size()); unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size()); unsigned RHSWords = NumBitWords(RHS.size());
unsigned i; unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i) for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
if (Bits[i] != RHS.Bits[i]) if (Bits[i] != RHS.Bits[i])
@ -381,14 +367,12 @@ public:
return true; return true;
} }
bool operator!=(const BitVector &RHS) const { bool operator!=(const BitVector& RHS) const { return !(*this == RHS); }
return !(*this == RHS);
}
/// Intersection, union, disjoint union. /// Intersection, union, disjoint union.
BitVector &operator&=(const BitVector &RHS) { BitVector& operator&=(const BitVector& RHS) {
unsigned ThisWords = NumBitWords(size()); unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size()); unsigned RHSWords = NumBitWords(RHS.size());
unsigned i; unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i) for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
Bits[i] &= RHS.Bits[i]; Bits[i] &= RHS.Bits[i];
@ -403,9 +387,9 @@ public:
} }
/// reset - Reset bits that are set in RHS. Same as *this &= ~RHS. /// reset - Reset bits that are set in RHS. Same as *this &= ~RHS.
BitVector &reset(const BitVector &RHS) { BitVector& reset(const BitVector& RHS) {
unsigned ThisWords = NumBitWords(size()); unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size()); unsigned RHSWords = NumBitWords(RHS.size());
unsigned i; unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i) for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
Bits[i] &= ~RHS.Bits[i]; Bits[i] &= ~RHS.Bits[i];
@ -414,22 +398,22 @@ public:
/// test - Check if (This - RHS) is zero. /// test - Check if (This - RHS) is zero.
/// This is the same as reset(RHS) and any(). /// This is the same as reset(RHS) and any().
bool test(const BitVector &RHS) const { bool test(const BitVector& RHS) const {
unsigned ThisWords = NumBitWords(size()); unsigned ThisWords = NumBitWords(size());
unsigned RHSWords = NumBitWords(RHS.size()); unsigned RHSWords = NumBitWords(RHS.size());
unsigned i; unsigned i;
for (i = 0; i != std::min(ThisWords, RHSWords); ++i) for (i = 0; i != std::min(ThisWords, RHSWords); ++i)
if ((Bits[i] & ~RHS.Bits[i]) != 0) if ((Bits[i] & ~RHS.Bits[i]) != 0)
return true; return true;
for (; i != ThisWords ; ++i) for (; i != ThisWords; ++i)
if (Bits[i] != 0) if (Bits[i] != 0)
return true; return true;
return false; return false;
} }
BitVector &operator|=(const BitVector &RHS) { BitVector& operator|=(const BitVector& RHS) {
if (size() < RHS.size()) if (size() < RHS.size())
resize(RHS.size()); resize(RHS.size());
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i) for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
@ -437,7 +421,7 @@ public:
return *this; return *this;
} }
BitVector &operator^=(const BitVector &RHS) { BitVector& operator^=(const BitVector& RHS) {
if (size() < RHS.size()) if (size() < RHS.size())
resize(RHS.size()); resize(RHS.size());
for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i) for (size_t i = 0, e = NumBitWords(RHS.size()); i != e; ++i)
@ -446,8 +430,9 @@ public:
} }
// Assignment operator. // Assignment operator.
const BitVector &operator=(const BitVector &RHS) { const BitVector& operator=(const BitVector& RHS) {
if (this == &RHS) return *this; if (this == &RHS)
return *this;
Size = RHS.size(); Size = RHS.size();
unsigned RHSWords = NumBitWords(Size); unsigned RHSWords = NumBitWords(Size);
@ -461,7 +446,7 @@ public:
// Grow the bitvector to have enough elements. // Grow the bitvector to have enough elements.
Capacity = RHSWords; Capacity = RHSWords;
assert(Capacity > 0 && "negative capacity?"); assert(Capacity > 0 && "negative capacity?");
BitWord *NewBits = (BitWord *)std::malloc(Capacity * sizeof(BitWord)); BitWord* NewBits = (BitWord*)std::malloc(Capacity * sizeof(BitWord));
std::memcpy(NewBits, RHS.Bits, Capacity * sizeof(BitWord)); std::memcpy(NewBits, RHS.Bits, Capacity * sizeof(BitWord));
// Destroy the old bits. // Destroy the old bits.
@ -471,8 +456,9 @@ public:
return *this; return *this;
} }
const BitVector &operator=(BitVector &&RHS) { const BitVector& operator=(BitVector&& RHS) {
if (this == &RHS) return *this; if (this == &RHS)
return *this;
std::free(Bits); std::free(Bits);
Bits = RHS.Bits; Bits = RHS.Bits;
@ -485,7 +471,7 @@ public:
return *this; return *this;
} }
void swap(BitVector &RHS) { void swap(BitVector& RHS) {
std::swap(Bits, RHS.Bits); std::swap(Bits, RHS.Bits);
std::swap(Size, RHS.Size); std::swap(Size, RHS.Size);
std::swap(Capacity, RHS.Capacity); std::swap(Capacity, RHS.Capacity);
@ -505,70 +491,56 @@ public:
/// setBitsInMask - Add '1' bits from Mask to this vector. Don't resize. /// setBitsInMask - Add '1' bits from Mask to this vector. Don't resize.
/// This computes "*this |= Mask". /// This computes "*this |= Mask".
void setBitsInMask(const uint32_t *Mask, unsigned MaskWords = ~0u) { void setBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, false>(Mask, MaskWords); }
applyMask<true, false>(Mask, MaskWords);
}
/// clearBitsInMask - Clear any bits in this vector that are set in Mask. /// clearBitsInMask - Clear any bits in this vector that are set in Mask.
/// Don't resize. This computes "*this &= ~Mask". /// Don't resize. This computes "*this &= ~Mask".
void clearBitsInMask(const uint32_t *Mask, unsigned MaskWords = ~0u) { void clearBitsInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, false>(Mask, MaskWords); }
applyMask<false, false>(Mask, MaskWords);
}
/// setBitsNotInMask - Add a bit to this vector for every '0' bit in Mask. /// setBitsNotInMask - Add a bit to this vector for every '0' bit in Mask.
/// Don't resize. This computes "*this |= ~Mask". /// Don't resize. This computes "*this |= ~Mask".
void setBitsNotInMask(const uint32_t *Mask, unsigned MaskWords = ~0u) { void setBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<true, true>(Mask, MaskWords); }
applyMask<true, true>(Mask, MaskWords);
}
/// clearBitsNotInMask - Clear a bit in this vector for every '0' bit in Mask. /// clearBitsNotInMask - Clear a bit in this vector for every '0' bit in Mask.
/// Don't resize. This computes "*this &= Mask". /// Don't resize. This computes "*this &= Mask".
void clearBitsNotInMask(const uint32_t *Mask, unsigned MaskWords = ~0u) { void clearBitsNotInMask(const uint32_t* Mask, unsigned MaskWords = ~0u) { applyMask<false, true>(Mask, MaskWords); }
applyMask<false, true>(Mask, MaskWords);
}
private: private:
unsigned NumBitWords(unsigned S) const { unsigned NumBitWords(unsigned S) const { return (S + BITWORD_SIZE - 1) / BITWORD_SIZE; }
return (S + BITWORD_SIZE-1) / BITWORD_SIZE;
}
// Set the unused bits in the high words. // Set the unused bits in the high words.
void set_unused_bits(bool t = true) { void set_unused_bits(bool t = true) {
// Set high words first. // Set high words first.
unsigned UsedWords = NumBitWords(Size); unsigned UsedWords = NumBitWords(Size);
if (Capacity > UsedWords) if (Capacity > UsedWords)
init_words(&Bits[UsedWords], (Capacity-UsedWords), t); init_words(&Bits[UsedWords], (Capacity - UsedWords), t);
// Then set any stray high bits of the last used word. // Then set any stray high bits of the last used word.
unsigned ExtraBits = Size % BITWORD_SIZE; unsigned ExtraBits = Size % BITWORD_SIZE;
if (ExtraBits) { if (ExtraBits) {
BitWord ExtraBitMask = ~0UL << ExtraBits; BitWord ExtraBitMask = ~0UL << ExtraBits;
if (t) if (t)
Bits[UsedWords-1] |= ExtraBitMask; Bits[UsedWords - 1] |= ExtraBitMask;
else else
Bits[UsedWords-1] &= ~ExtraBitMask; Bits[UsedWords - 1] &= ~ExtraBitMask;
} }
} }
// Clear the unused bits in the high words. // Clear the unused bits in the high words.
void clear_unused_bits() { void clear_unused_bits() { set_unused_bits(false); }
set_unused_bits(false);
}
void grow(unsigned NewSize) { void grow(unsigned NewSize) {
Capacity = std::max(NumBitWords(NewSize), Capacity * 2); Capacity = std::max(NumBitWords(NewSize), Capacity * 2);
assert(Capacity > 0 && "realloc-ing zero space"); assert(Capacity > 0 && "realloc-ing zero space");
Bits = (BitWord *)std::realloc(Bits, Capacity * sizeof(BitWord)); Bits = (BitWord*)std::realloc(Bits, Capacity * sizeof(BitWord));
clear_unused_bits(); clear_unused_bits();
} }
void init_words(BitWord *B, unsigned NumWords, bool t) { void init_words(BitWord* B, unsigned NumWords, bool t) { memset(B, 0 - (int)t, NumWords * sizeof(BitWord)); }
memset(B, 0 - (int)t, NumWords*sizeof(BitWord));
}
template<bool AddBits, bool InvertMask> template <bool AddBits, bool InvertMask>
void applyMask(const uint32_t *Mask, unsigned MaskWords) { void applyMask(const uint32_t* Mask, unsigned MaskWords) {
static_assert(BITWORD_SIZE % 32 == 0, "Unsupported BitWord size."); static_assert(BITWORD_SIZE % 32 == 0, "Unsupported BitWord size.");
MaskWords = std::min(MaskWords, (size() + 31) / 32); MaskWords = std::min(MaskWords, (size() + 31) / 32);
const unsigned Scale = BITWORD_SIZE / 32; const unsigned Scale = BITWORD_SIZE / 32;
@ -578,17 +550,23 @@ private:
// This inner loop should unroll completely when BITWORD_SIZE > 32. // This inner loop should unroll completely when BITWORD_SIZE > 32.
for (unsigned b = 0; b != BITWORD_SIZE; b += 32) { for (unsigned b = 0; b != BITWORD_SIZE; b += 32) {
uint32_t M = *Mask++; uint32_t M = *Mask++;
if (InvertMask) M = ~M; if (InvertMask)
if (AddBits) BW |= BitWord(M) << b; M = ~M;
else BW &= ~(BitWord(M) << b); if (AddBits)
BW |= BitWord(M) << b;
else
BW &= ~(BitWord(M) << b);
} }
Bits[i] = BW; Bits[i] = BW;
} }
for (unsigned b = 0; MaskWords; b += 32, --MaskWords) { for (unsigned b = 0; MaskWords; b += 32, --MaskWords) {
uint32_t M = *Mask++; uint32_t M = *Mask++;
if (InvertMask) M = ~M; if (InvertMask)
if (AddBits) Bits[i] |= BitWord(M) << b; M = ~M;
else Bits[i] &= ~(BitWord(M) << b); if (AddBits)
Bits[i] |= BitWord(M) << b;
else
Bits[i] &= ~(BitWord(M) << b);
} }
if (AddBits) if (AddBits)
clear_unused_bits(); clear_unused_bits();
@ -599,18 +577,12 @@ public:
size_t getMemorySize() const { return Capacity * sizeof(BitWord); } size_t getMemorySize() const { return Capacity * sizeof(BitWord); }
}; };
static inline size_t capacity_in_bytes(const BitVector &X) { static inline size_t capacity_in_bytes(const BitVector& X) { return X.getMemorySize(); }
return X.getMemorySize();
}
} // end namespace llvm } // end namespace llvm
} // end namespace hecl } // end namespace hecl
namespace std { namespace std {
/// Implement std::swap in terms of BitVector swap. /// Implement std::swap in terms of BitVector swap.
inline void inline void swap(hecl::llvm::BitVector& LHS, hecl::llvm::BitVector& RHS) { LHS.swap(RHS); }
swap(hecl::llvm::BitVector &LHS, hecl::llvm::BitVector &RHS) {
LHS.swap(RHS);
}
} // end namespace std } // end namespace std

File diff suppressed because it is too large Load Diff

View File

@ -4,69 +4,64 @@
#include "athena/DNA.hpp" #include "athena/DNA.hpp"
#include "athena/MemoryReader.hpp" #include "athena/MemoryReader.hpp"
namespace hecl::blender namespace hecl::blender {
{
struct SDNABlock : public athena::io::DNA<athena::Little> struct SDNABlock : public athena::io::DNA<athena::Little> {
{ AT_DECL_DNA
DNAFourCC magic;
DNAFourCC nameMagic;
Value<atUint32> numNames;
Vector<String<-1>, AT_DNA_COUNT(numNames)> names;
Align<4> align1;
DNAFourCC typeMagic;
Value<atUint32> numTypes;
Vector<String<-1>, AT_DNA_COUNT(numTypes)> types;
Align<4> align2;
DNAFourCC tlenMagic;
Vector<Value<atUint16>, AT_DNA_COUNT(numTypes)> tlens;
Align<4> align3;
DNAFourCC strcMagic;
Value<atUint32> numStrcs;
struct SDNAStruct : public athena::io::DNA<athena::Little> {
AT_DECL_DNA AT_DECL_DNA
DNAFourCC magic; Value<atUint16> type;
DNAFourCC nameMagic; Value<atUint16> numFields;
Value<atUint32> numNames; struct SDNAField : public athena::io::DNA<athena::Little> {
Vector<String<-1>, AT_DNA_COUNT(numNames)> names; AT_DECL_DNA
Align<4> align1; Value<atUint16> type;
DNAFourCC typeMagic; Value<atUint16> name;
Value<atUint32> numTypes; atUint32 offset;
Vector<String<-1>, AT_DNA_COUNT(numTypes)> types;
Align<4> align2;
DNAFourCC tlenMagic;
Vector<Value<atUint16>, AT_DNA_COUNT(numTypes)> tlens;
Align<4> align3;
DNAFourCC strcMagic;
Value<atUint32> numStrcs;
struct SDNAStruct : public athena::io::DNA<athena::Little>
{
AT_DECL_DNA
Value<atUint16> type;
Value<atUint16> numFields;
struct SDNAField : public athena::io::DNA<athena::Little>
{
AT_DECL_DNA
Value<atUint16> type;
Value<atUint16> name;
atUint32 offset;
};
Vector<SDNAField, AT_DNA_COUNT(numFields)> fields;
void computeOffsets(const SDNABlock& block);
const SDNAField* lookupField(const SDNABlock& block, const char* n) const;
}; };
Vector<SDNAStruct, AT_DNA_COUNT(numStrcs)> strcs; Vector<SDNAField, AT_DNA_COUNT(numFields)> fields;
const SDNAStruct* lookupStruct(const char* n, int& idx) const; void computeOffsets(const SDNABlock& block);
const SDNAField* lookupField(const SDNABlock& block, const char* n) const;
};
Vector<SDNAStruct, AT_DNA_COUNT(numStrcs)> strcs;
const SDNAStruct* lookupStruct(const char* n, int& idx) const;
}; };
struct FileBlock : public athena::io::DNA<athena::Little> struct FileBlock : public athena::io::DNA<athena::Little> {
{ AT_DECL_DNA
AT_DECL_DNA DNAFourCC type;
DNAFourCC type; Value<atUint32> size;
Value<atUint32> size; Value<atUint64> ptr;
Value<atUint64> ptr; Value<atUint32> sdnaIdx;
Value<atUint32> sdnaIdx; Value<atUint32> count;
Value<atUint32> count;
}; };
class SDNARead class SDNARead {
{ std::vector<uint8_t> m_data;
std::vector<uint8_t> m_data; SDNABlock m_sdnaBlock;
SDNABlock m_sdnaBlock;
public: public:
explicit SDNARead(SystemStringView path); explicit SDNARead(SystemStringView path);
operator bool() const { return !m_data.empty(); } operator bool() const { return !m_data.empty(); }
const SDNABlock& sdnaBlock() const { return m_sdnaBlock; } const SDNABlock& sdnaBlock() const { return m_sdnaBlock; }
void enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const; void enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const;
}; };
BlendType GetBlendType(SystemStringView path); BlendType GetBlendType(SystemStringView path);
} } // namespace hecl::blender

View File

@ -2,24 +2,22 @@
#include <memory> #include <memory>
namespace hecl::blender namespace hecl::blender {
{
class Connection; class Connection;
class Token class Token {
{ std::unique_ptr<Connection> m_conn;
std::unique_ptr<Connection> m_conn;
public:
Connection& getBlenderConnection();
void shutdown();
Token() = default; public:
~Token(); Connection& getBlenderConnection();
Token(const Token&)=delete; void shutdown();
Token& operator=(const Token&)=delete;
Token(Token&&)=default; Token() = default;
Token& operator=(Token&&)=default; ~Token();
Token(const Token&) = delete;
Token& operator=(const Token&) = delete;
Token(Token&&) = default;
Token& operator=(Token&&) = default;
}; };
} } // namespace hecl::blender

230
hecl/include/hecl/CVar.hpp Executable file → Normal file
View File

@ -6,154 +6,146 @@
#include <athena/Global.hpp> #include <athena/Global.hpp>
#include <athena/DNAYaml.hpp> #include <athena/DNAYaml.hpp>
namespace hecl namespace hecl {
{ namespace DNACVAR {
namespace DNACVAR enum class EType : atUint8 { Boolean, Integer, Float, Literal, Vec4f };
{
enum class EType : atUint8
{
Boolean,
Integer,
Float,
Literal,
Vec4f
};
enum EFlags enum EFlags {
{ None = -1,
None = -1, System = (1 << 0),
System = (1 << 0), Game = (1 << 1),
Game = (1 << 1), Editor = (1 << 2),
Editor = (1 << 2), Gui = (1 << 3),
Gui = (1 << 3), Cheat = (1 << 4),
Cheat = (1 << 4), Hidden = (1 << 5),
Hidden = (1 << 5), ReadOnly = (1 << 6),
ReadOnly = (1 << 6), Archive = (1 << 7),
Archive = (1 << 7), InternalArchivable = (1 << 8),
InternalArchivable = (1 << 8), Modified = (1 << 9),
Modified = (1 << 9), ModifyRestart = (1 << 10) /*!< If this bit is set, any modification will inform the user that a restart is required */
ModifyRestart = (1 << 10) /*!< If this bit is set, any modification will inform the user that a restart is required */
}; };
ENABLE_BITWISE_ENUM(EFlags) ENABLE_BITWISE_ENUM(EFlags)
class CVar : public athena::io::DNA<athena::Big> class CVar : public athena::io::DNA<athena::Big> {
{
public: public:
AT_DECL_DNA AT_DECL_DNA
String<-1> m_name; String<-1> m_name;
String<-1> m_value; String<-1> m_value;
}; };
struct CVarContainer : public athena::io::DNA<athena::Big> struct CVarContainer : public athena::io::DNA<athena::Big> {
{ AT_DECL_DNA
AT_DECL_DNA Value<atUint32> magic = 'CVAR';
Value<atUint32> magic = 'CVAR'; Value<atUint32> cvarCount;
Value<atUint32> cvarCount; Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
Vector<CVar, AT_DNA_COUNT(cvarCount)> cvars;
}; };
} } // namespace DNACVAR
class CVarManager; class CVarManager;
class CVar : protected DNACVAR::CVar class CVar : protected DNACVAR::CVar {
{ friend class CVarManager;
friend class CVarManager; Delete _d;
Delete _d;
public: public:
typedef std::function<void(CVar*)> ListenerFunc; typedef std::function<void(CVar*)> ListenerFunc;
using EType = DNACVAR::EType; using EType = DNACVAR::EType;
using EFlags = DNACVAR::EFlags; using EFlags = DNACVAR::EFlags;
CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags, CVarManager& parent); CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags,
CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags, CVarManager& parent); CVarManager& parent);
CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent); CVar(std::string_view name, std::string_view value, std::string_view help, EFlags flags, CVarManager& parent);
CVar(std::string_view name, bool value, std::string_view help, EFlags flags, CVarManager& parent); CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent);
CVar(std::string_view name, int value, std::string_view help, EFlags flags, CVarManager& parent); CVar(std::string_view name, bool value, std::string_view help, EFlags flags, CVarManager& parent);
CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent); CVar(std::string_view name, int value, std::string_view help, EFlags flags, CVarManager& parent);
CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent);
std::string_view name() const { return m_name; }
std::string_view rawHelp() const { return m_help; }
std::string help() const;
std::string value() const { return m_value; }
std::string_view name() const { return m_name; } atVec4f toVec4f(bool* isValid = nullptr) const;
std::string_view rawHelp() const { return m_help; } float toFloat(bool* isValid = nullptr) const;
std::string help() const; bool toBoolean(bool* isValid = nullptr) const;
std::string value() const { return m_value; } int toInteger(bool* isValid = nullptr) const;
const std::wstring toWideLiteral(bool* isValid = nullptr) const;
const std::string toLiteral(bool* isValid = nullptr) const;
atVec4f toVec4f(bool* isValid = nullptr) const; bool fromVec4f(const atVec4f& val);
float toFloat(bool* isValid = nullptr) const; bool fromFloat(float val);
bool toBoolean(bool* isValid = nullptr) const; bool fromBoolean(bool val);
int toInteger(bool* isValid = nullptr) const; bool fromInteger(int val);
const std::wstring toWideLiteral(bool* isValid = nullptr) const; bool fromLiteral(std::string_view val);
const std::string toLiteral(bool* isValid = nullptr) const; bool fromLiteral(std::wstring_view val);
bool fromLiteralToType(std::string_view val, bool setDefault = false);
bool fromLiteralToType(std::wstring_view val, bool setDefault = false);
bool fromVec4f(const atVec4f& val); bool isFloat() const { return m_type == EType::Float; }
bool fromFloat(float val); bool isBoolean() const { return m_type == EType::Boolean; }
bool fromBoolean(bool val); bool isInteger() const { return m_type == EType::Integer; }
bool fromInteger(int val); bool isLiteral() const { return m_type == EType::Literal; }
bool fromLiteral(std::string_view val); bool isVec4f() const { return m_type == EType::Vec4f; }
bool fromLiteral(std::wstring_view val); bool isModified() const;
bool fromLiteralToType(std::string_view val, bool setDefault = false); bool modificationRequiresRestart() const;
bool fromLiteralToType(std::wstring_view val, bool setDefault = false); bool isReadOnly() const;
bool isCheat() const;
bool isHidden() const;
bool isArchive() const;
bool isInternalArchivable() const;
bool wasDeserialized() const;
bool hasDefaultValue() const;
void clearModified();
void setModified();
bool isFloat() const { return m_type == EType::Float; } EType type() const { return m_type; }
bool isBoolean() const { return m_type == EType::Boolean; } EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); }
bool isInteger() const { return m_type == EType::Integer; }
bool isLiteral() const { return m_type == EType::Literal; }
bool isVec4f() const { return m_type == EType::Vec4f; }
bool isModified() const;
bool modificationRequiresRestart() const;
bool isReadOnly() const;
bool isCheat() const;
bool isHidden() const;
bool isArchive() const;
bool isInternalArchivable() const;
bool wasDeserialized() const;
bool hasDefaultValue() const;
void clearModified();
void setModified();
EType type() const { return m_type; } /*!
EFlags flags() const { return (m_unlocked ? m_oldFlags : m_flags); } * \brief Unlocks the CVar for writing if it is ReadOnly.
* <b>Handle with care!!!</b> if you use unlock(), make sure
* you lock the cvar using lock()
* \see lock
*/
void unlock();
/*! /*!
* \brief Unlocks the CVar for writing if it is ReadOnly. * \brief Locks the CVar to prevent writing if it is ReadOnly.
* <b>Handle with care!!!</b> if you use unlock(), make sure * Unlike its partner function unlock, lock is harmless
* you lock the cvar using lock() * \see unlock
* \see lock */
*/ void lock();
void unlock();
/*! void addListener(ListenerFunc func) { m_listeners.push_back(func); }
* \brief Locks the CVar to prevent writing if it is ReadOnly.
* Unlike its partner function unlock, lock is harmless
* \see unlock
*/
void lock();
void addListener(ListenerFunc func) { m_listeners.push_back(func); }
private: private:
void dispatch(); void dispatch();
EType m_type; EType m_type;
std::string m_help; std::string m_help;
std::string m_defaultValue; std::string m_defaultValue;
EFlags m_flags; EFlags m_flags;
EFlags m_oldFlags; EFlags m_oldFlags;
bool m_unlocked = false; bool m_unlocked = false;
bool m_wasDeserialized = false; bool m_wasDeserialized = false;
CVarManager& m_mgr; CVarManager& m_mgr;
std::vector<ListenerFunc> m_listeners; std::vector<ListenerFunc> m_listeners;
}; };
class CVarUnlocker class CVarUnlocker {
{ CVar* m_cvar;
CVar* m_cvar;
public: public:
CVarUnlocker(CVar* cvar) : m_cvar(cvar) { if (m_cvar) m_cvar->unlock(); } CVarUnlocker(CVar* cvar) : m_cvar(cvar) {
~CVarUnlocker() { if (m_cvar) m_cvar->lock(); } if (m_cvar)
m_cvar->unlock();
}
~CVarUnlocker() {
if (m_cvar)
m_cvar->lock();
}
}; };
} } // namespace hecl

View File

@ -5,8 +5,7 @@
#undef min #undef min
#undef max #undef max
namespace hecl namespace hecl {
{
using namespace std::literals; using namespace std::literals;
@ -18,75 +17,45 @@ using namespace std::literals;
#define DEFAULT_GRAPHICS_API "OpenGL"sv #define DEFAULT_GRAPHICS_API "OpenGL"sv
#endif #endif
struct CVarCommons struct CVarCommons {
{ CVarManager& m_mgr;
CVarManager& m_mgr; CVar* m_graphicsApi = nullptr;
CVar* m_graphicsApi = nullptr; CVar* m_drawSamples = nullptr;
CVar* m_drawSamples = nullptr; CVar* m_texAnisotropy = nullptr;
CVar* m_texAnisotropy = nullptr; CVar* m_deepColor = nullptr;
CVar* m_deepColor = nullptr;
CVarCommons(CVarManager& manager) : m_mgr(manager) CVarCommons(CVarManager& manager) : m_mgr(manager) {
{ m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv, "API to use for rendering graphics"sv, DEFAULT_GRAPHICS_API,
m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive |
"API to use for rendering graphics"sv, hecl::CVar::EFlags::ModifyRestart);
DEFAULT_GRAPHICS_API, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); m_drawSamples = m_mgr.findOrMakeCVar("drawSamples"sv, "Number of MSAA samples to use for render targets"sv, 1,
m_drawSamples = m_mgr.findOrMakeCVar("drawSamples"sv, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive |
"Number of MSAA samples to use for render targets"sv, hecl::CVar::EFlags::ModifyRestart);
1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); m_texAnisotropy = m_mgr.findOrMakeCVar(
m_texAnisotropy = m_mgr.findOrMakeCVar("texAnisotropy"sv, "texAnisotropy"sv, "Number of anisotropic samples to use for sampling textures"sv, 1,
"Number of anisotropic samples to use for sampling textures"sv, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); m_deepColor = m_mgr.findOrMakeCVar(
m_deepColor = m_mgr.findOrMakeCVar("deepColor"sv, "deepColor"sv, "Allow framebuffer with color depth greater-then 24-bits"sv, false,
"Allow framebuffer with color depth greater-then 24-bits"sv, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
false, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); }
}
std::string getGraphicsApi() const std::string getGraphicsApi() const { return m_graphicsApi->toLiteral(); }
{
return m_graphicsApi->toLiteral();
}
void setGraphicsApi(std::string_view api) void setGraphicsApi(std::string_view api) { m_graphicsApi->fromLiteral(api); }
{
m_graphicsApi->fromLiteral(api);
}
uint32_t getSamples() const uint32_t getSamples() const { return std::max(uint32_t(1), uint32_t(m_drawSamples->toInteger())); }
{
return std::max(uint32_t(1), uint32_t(m_drawSamples->toInteger()));
}
void setSamples(uint32_t v) void setSamples(uint32_t v) { m_drawSamples->fromInteger(std::max(uint32_t(1), v)); }
{
m_drawSamples->fromInteger(std::max(uint32_t(1), v));
}
uint32_t getAnisotropy() const uint32_t getAnisotropy() const { return std::max(uint32_t(1), uint32_t(m_texAnisotropy->toInteger())); }
{
return std::max(uint32_t(1), uint32_t(m_texAnisotropy->toInteger()));
}
void setAnisotropy(uint32_t v) void setAnisotropy(uint32_t v) { m_texAnisotropy->fromInteger(std::max(uint32_t(1), v)); }
{
m_texAnisotropy->fromInteger(std::max(uint32_t(1), v));
}
bool getDeepColor() const bool getDeepColor() const { return m_deepColor->toBoolean(); }
{
return m_deepColor->toBoolean();
}
void setDeepColor(bool b) void setDeepColor(bool b) { m_deepColor->fromBoolean(b); }
{
m_deepColor->fromBoolean(b);
}
void serialize() void serialize() { m_mgr.serialize(); }
{
m_mgr.serialize();
}
}; };
} } // namespace hecl

121
hecl/include/hecl/CVarManager.hpp Executable file → Normal file
View File

@ -5,86 +5,85 @@
#include "CVar.hpp" #include "CVar.hpp"
#include "hecl/SystemChar.hpp" #include "hecl/SystemChar.hpp"
namespace hecl namespace hecl {
{ namespace Runtime {
namespace Runtime
{
class FileStoreManager; class FileStoreManager;
} }
extern CVar* com_developer; extern CVar* com_developer;
extern CVar* com_configfile; extern CVar* com_configfile;
extern CVar* com_enableCheats; extern CVar* com_enableCheats;
class CVarManager final class CVarManager final {
{ using CVarContainer = DNACVAR::CVarContainer;
using CVarContainer = DNACVAR::CVarContainer; template <typename T>
template <typename T> CVar* _newCVar(std::string_view name, std::string_view help, const T& value, CVar::EFlags flags) {
CVar* _newCVar(std::string_view name, std::string_view help, const T& value, CVar::EFlags flags) if (CVar* ret = registerCVar(std::make_unique<CVar>(name, value, help, flags, *this))) {
{ deserialize(ret);
if (CVar* ret = registerCVar(std::make_unique<CVar>(name, value, help, flags, *this))) return ret;
{
deserialize(ret);
return ret;
}
return nullptr;
} }
return nullptr;
}
hecl::Runtime::FileStoreManager& m_store;
bool m_useBinary;
static CVarManager* m_instance;
hecl::Runtime::FileStoreManager& m_store;
bool m_useBinary;
static CVarManager* m_instance;
public: public:
CVarManager() = delete; CVarManager() = delete;
CVarManager(const CVarManager&) = delete; CVarManager(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&) = delete; CVarManager& operator=(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&&) = delete; CVarManager& operator=(const CVarManager&&) = delete;
CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false); CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false);
~CVarManager(); ~CVarManager();
CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags flags) CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags flags) {
{ return _newCVar<atVec4f>(name, help, value, flags); } return _newCVar<atVec4f>(name, help, value, flags);
CVar* newCVar(std::string_view name, std::string_view help, std::string_view value, CVar::EFlags flags) }
{ return _newCVar<std::string_view>(name, help, value, flags); } CVar* newCVar(std::string_view name, std::string_view help, std::string_view value, CVar::EFlags flags) {
CVar* newCVar(std::string_view name, std::string_view help, bool value, CVar::EFlags flags) return _newCVar<std::string_view>(name, help, value, flags);
{ return _newCVar<bool>(name, help, value, flags); } }
CVar* newCVar(std::string_view name, std::string_view help, float value, CVar::EFlags flags) CVar* newCVar(std::string_view name, std::string_view help, bool value, CVar::EFlags flags) {
{ return _newCVar<float>(name, help, value, flags); } return _newCVar<bool>(name, help, value, flags);
CVar* newCVar(std::string_view name, std::string_view help, int value, CVar::EFlags flags) }
{ return _newCVar<int>(name, help, value, flags); } CVar* newCVar(std::string_view name, std::string_view help, float value, CVar::EFlags flags) {
return _newCVar<float>(name, help, value, flags);
}
CVar* newCVar(std::string_view name, std::string_view help, int value, CVar::EFlags flags) {
return _newCVar<int>(name, help, value, flags);
}
CVar* registerCVar(std::unique_ptr<CVar>&& cvar); CVar* registerCVar(std::unique_ptr<CVar>&& cvar);
CVar* findCVar(std::string_view name); CVar* findCVar(std::string_view name);
template<class... _Args> template <class... _Args>
CVar* findOrMakeCVar(std::string_view name, _Args&&... args) CVar* findOrMakeCVar(std::string_view name, _Args&&... args) {
{ if (CVar* cv = findCVar(name))
if (CVar* cv = findCVar(name)) return cv;
return cv; return newCVar(name, std::forward<_Args>(args)...);
return newCVar(name, std::forward<_Args>(args)...); }
}
std::vector<CVar*> archivedCVars() const; std::vector<CVar*> archivedCVars() const;
std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::None) const; std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::None) const;
void deserialize(CVar* cvar); void deserialize(CVar* cvar);
void serialize(); void serialize();
static CVarManager* instance(); static CVarManager* instance();
void list(class Console* con, const std::vector<std::string>& args); void list(class Console* con, const std::vector<std::string>& args);
void setCVar(class Console* con, const std::vector<std::string>& args); void setCVar(class Console* con, const std::vector<std::string>& args);
void getCVar(class Console* con, const std::vector<std::string>& args); void getCVar(class Console* con, const std::vector<std::string>& args);
void setDeveloperMode(bool v, bool setDeserialized = false);
bool restartRequired() const;
void setDeveloperMode(bool v, bool setDeserialized=false); void parseCommandLine(const std::vector<SystemString>& args);
bool restartRequired() const;
void parseCommandLine(const std::vector<SystemString>& args);
private: private:
bool suppressDeveloper(); bool suppressDeveloper();
void restoreDeveloper(bool oldDeveloper); void restoreDeveloper(bool oldDeveloper);
std::unordered_map<std::string, std::unique_ptr<CVar>> m_cvars; std::unordered_map<std::string, std::unique_ptr<CVar>> m_cvars;
std::unordered_map<std::string, std::string> m_deferedCVars; std::unordered_map<std::string, std::string> m_deferedCVars;
}; };
} } // namespace hecl

View File

@ -9,111 +9,89 @@
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
namespace hecl namespace hecl {
{
class ClientProcess class ClientProcess {
{ std::mutex m_mutex;
std::mutex m_mutex; std::condition_variable m_cv;
std::condition_variable m_cv; std::condition_variable m_initCv;
std::condition_variable m_initCv; std::condition_variable m_waitCv;
std::condition_variable m_waitCv; const MultiProgressPrinter* m_progPrinter;
const MultiProgressPrinter* m_progPrinter; int m_completedCooks = 0;
int m_completedCooks = 0; int m_addedCooks = 0;
int m_addedCooks = 0;
public: public:
struct Transaction struct Transaction {
{ ClientProcess& m_parent;
ClientProcess& m_parent; enum class Type { Buffer, Cook, Lambda } m_type;
enum class Type bool m_complete = false;
{ virtual void run(blender::Token& btok) = 0;
Buffer, Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {}
Cook, };
Lambda struct BufferTransaction final : Transaction {
} m_type; ProjectPath m_path;
bool m_complete = false; void* m_targetBuf;
virtual void run(blender::Token& btok)=0; size_t m_maxLen;
Transaction(ClientProcess& parent, Type tp) : m_parent(parent), m_type(tp) {} size_t m_offset;
}; void run(blender::Token& btok);
struct BufferTransaction final : Transaction BufferTransaction(ClientProcess& parent, const ProjectPath& path, void* target, size_t maxLen, size_t offset)
{ : Transaction(parent, Type::Buffer), m_path(path), m_targetBuf(target), m_maxLen(maxLen), m_offset(offset) {}
ProjectPath m_path; };
void* m_targetBuf; struct CookTransaction final : Transaction {
size_t m_maxLen; ProjectPath m_path;
size_t m_offset; Database::IDataSpec* m_dataSpec;
void run(blender::Token& btok); bool m_returnResult = false;
BufferTransaction(ClientProcess& parent, const ProjectPath& path, bool m_force;
void* target, size_t maxLen, size_t offset) bool m_fast;
: Transaction(parent, Type::Buffer), void run(blender::Token& btok);
m_path(path), m_targetBuf(target), CookTransaction(ClientProcess& parent, const ProjectPath& path, bool force, bool fast, Database::IDataSpec* spec)
m_maxLen(maxLen), m_offset(offset) {} : Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec), m_force(force), m_fast(fast) {}
}; };
struct CookTransaction final : Transaction struct LambdaTransaction final : Transaction {
{ std::function<void(blender::Token&)> m_func;
ProjectPath m_path; void run(blender::Token& btok);
Database::IDataSpec* m_dataSpec; LambdaTransaction(ClientProcess& parent, std::function<void(blender::Token&)>&& func)
bool m_returnResult = false; : Transaction(parent, Type::Lambda), m_func(std::move(func)) {}
bool m_force; };
bool m_fast;
void run(blender::Token& btok);
CookTransaction(ClientProcess& parent, const ProjectPath& path,
bool force, bool fast, Database::IDataSpec* spec)
: Transaction(parent, Type::Cook), m_path(path), m_dataSpec(spec),
m_force(force), m_fast(fast) {}
};
struct LambdaTransaction final : Transaction
{
std::function<void(blender::Token&)> m_func;
void run(blender::Token& btok);
LambdaTransaction(ClientProcess& parent, std::function<void(blender::Token&)>&& func)
: Transaction(parent, Type::Lambda), m_func(std::move(func)) {}
};
private: private:
std::list<std::shared_ptr<Transaction>> m_pendingQueue; std::list<std::shared_ptr<Transaction>> m_pendingQueue;
std::list<std::shared_ptr<Transaction>> m_completedQueue; std::list<std::shared_ptr<Transaction>> m_completedQueue;
int m_inProgress = 0; int m_inProgress = 0;
bool m_running = true; bool m_running = true;
struct Worker struct Worker {
{ ClientProcess& m_proc;
ClientProcess& m_proc; int m_idx;
int m_idx; std::thread m_thr;
std::thread m_thr; blender::Token m_blendTok;
blender::Token m_blendTok; bool m_didInit = false;
bool m_didInit = false; Worker(ClientProcess& proc, int idx);
Worker(ClientProcess& proc, int idx); void proc();
void proc(); };
}; std::vector<Worker> m_workers;
std::vector<Worker> m_workers; static ThreadLocalPtr<ClientProcess::Worker> ThreadWorker;
static ThreadLocalPtr<ClientProcess::Worker> ThreadWorker;
public: public:
ClientProcess(const MultiProgressPrinter* progPrinter=nullptr); ClientProcess(const MultiProgressPrinter* progPrinter = nullptr);
~ClientProcess() {shutdown();} ~ClientProcess() { shutdown(); }
std::shared_ptr<const BufferTransaction> std::shared_ptr<const BufferTransaction> addBufferTransaction(const hecl::ProjectPath& path, void* target,
addBufferTransaction(const hecl::ProjectPath& path, void* target, size_t maxLen, size_t offset);
size_t maxLen, size_t offset); std::shared_ptr<const CookTransaction> addCookTransaction(const hecl::ProjectPath& path, bool force, bool fast,
std::shared_ptr<const CookTransaction> Database::IDataSpec* spec);
addCookTransaction(const hecl::ProjectPath& path, bool force, std::shared_ptr<const LambdaTransaction> addLambdaTransaction(std::function<void(blender::Token&)>&& func);
bool fast, Database::IDataSpec* spec); bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force, bool fast);
std::shared_ptr<const LambdaTransaction> void swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue);
addLambdaTransaction(std::function<void(blender::Token&)>&& func); void waitUntilComplete();
bool syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, void shutdown();
bool force, bool fast); bool isBusy() const { return m_pendingQueue.size() || m_inProgress; }
void swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue);
void waitUntilComplete();
void shutdown();
bool isBusy() const { return m_pendingQueue.size() || m_inProgress; }
static int GetThreadWorkerIdx() static int GetThreadWorkerIdx() {
{ Worker* w = ThreadWorker.get();
Worker* w = ThreadWorker.get(); if (w)
if (w) return w->m_idx;
return w->m_idx; return -1;
return -1; }
}
}; };
} } // namespace hecl

View File

@ -5,66 +5,87 @@
#include "boo/graphicsdev/D3D.hpp" #include "boo/graphicsdev/D3D.hpp"
#include "boo/graphicsdev/Metal.hpp" #include "boo/graphicsdev/Metal.hpp"
namespace hecl namespace hecl {
{
namespace PlatformType namespace PlatformType {
{
using PlatformEnum = boo::IGraphicsDataFactory::Platform; using PlatformEnum = boo::IGraphicsDataFactory::Platform;
struct Null {}; struct Null {};
struct OpenGL { static constexpr PlatformEnum Enum = PlatformEnum::OpenGL; static const char* Name; struct OpenGL {
static constexpr PlatformEnum Enum = PlatformEnum::OpenGL;
static const char* Name;
#if BOO_HAS_GL #if BOO_HAS_GL
using Context = boo::GLDataFactory::Context; using Context = boo::GLDataFactory::Context;
#endif #endif
}; };
struct D3D11 { static constexpr PlatformEnum Enum = PlatformEnum::D3D11; static const char* Name; struct D3D11 {
static constexpr PlatformEnum Enum = PlatformEnum::D3D11;
static const char* Name;
#if _WIN32 #if _WIN32
using Context = boo::D3D11DataFactory::Context; using Context = boo::D3D11DataFactory::Context;
#endif #endif
}; };
struct Metal { static constexpr PlatformEnum Enum = PlatformEnum::Metal; static const char* Name; struct Metal {
static constexpr PlatformEnum Enum = PlatformEnum::Metal;
static const char* Name;
#if BOO_HAS_METAL #if BOO_HAS_METAL
using Context = boo::MetalDataFactory::Context; using Context = boo::MetalDataFactory::Context;
#endif #endif
}; };
struct Vulkan { static constexpr PlatformEnum Enum = PlatformEnum::Vulkan; static const char* Name; struct Vulkan {
static constexpr PlatformEnum Enum = PlatformEnum::Vulkan;
static const char* Name;
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
using Context = boo::VulkanDataFactory::Context; using Context = boo::VulkanDataFactory::Context;
#endif #endif
}; };
struct NX { static constexpr PlatformEnum Enum = PlatformEnum::NX; static const char* Name; struct NX {
static constexpr PlatformEnum Enum = PlatformEnum::NX;
static const char* Name;
#if BOO_HAS_NX #if BOO_HAS_NX
using Context = boo::NXDataFactory::Context; using Context = boo::NXDataFactory::Context;
#endif #endif
}; };
} } // namespace PlatformType
namespace PipelineStage namespace PipelineStage {
{
using StageEnum = boo::PipelineStage; using StageEnum = boo::PipelineStage;
struct Null { static constexpr StageEnum Enum = StageEnum::Null; static const char* Name; }; struct Null {
struct Vertex { static constexpr StageEnum Enum = StageEnum::Vertex; static const char* Name; }; static constexpr StageEnum Enum = StageEnum::Null;
struct Fragment { static constexpr StageEnum Enum = StageEnum::Fragment; static const char* Name; }; static const char* Name;
struct Geometry { static constexpr StageEnum Enum = StageEnum::Geometry; static const char* Name; }; };
struct Control { static constexpr StageEnum Enum = StageEnum::Control; static const char* Name; }; struct Vertex {
struct Evaluation { static constexpr StageEnum Enum = StageEnum::Evaluation; static const char* Name; }; static constexpr StageEnum Enum = StageEnum::Vertex;
} static const char* Name;
};
struct Fragment {
static constexpr StageEnum Enum = StageEnum::Fragment;
static const char* Name;
};
struct Geometry {
static constexpr StageEnum Enum = StageEnum::Geometry;
static const char* Name;
};
struct Control {
static constexpr StageEnum Enum = StageEnum::Control;
static const char* Name;
};
struct Evaluation {
static constexpr StageEnum Enum = StageEnum::Evaluation;
static const char* Name;
};
} // namespace PipelineStage
#ifdef __APPLE__ #ifdef __APPLE__
using StageBinaryData = std::shared_ptr<uint8_t>; using StageBinaryData = std::shared_ptr<uint8_t>;
static inline StageBinaryData MakeStageBinaryData(size_t sz) static inline StageBinaryData MakeStageBinaryData(size_t sz) {
{ return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
} }
#else #else
using StageBinaryData = std::shared_ptr<uint8_t[]>; using StageBinaryData = std::shared_ptr<uint8_t[]>;
static inline StageBinaryData MakeStageBinaryData(size_t sz) static inline StageBinaryData MakeStageBinaryData(size_t sz) { return StageBinaryData(new uint8_t[sz]); }
{
return StageBinaryData(new uint8_t[sz]);
}
#endif #endif
template<typename P, typename S> template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text); std::pair<StageBinaryData, size_t> CompileShader(std::string_view text);
} } // namespace hecl

View File

@ -7,108 +7,89 @@
#include "boo/IWindow.hpp" #include "boo/IWindow.hpp"
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
namespace hecl namespace hecl {
{
class CVarManager; class CVarManager;
class CVar; class CVar;
struct SConsoleCommand struct SConsoleCommand {
{ enum class ECommandFlags { Normal = 0, Cheat = (1 << 0), Developer = (1 << 1) };
enum class ECommandFlags std::string m_displayName;
{ std::string m_helpString;
Normal = 0, std::string m_usage;
Cheat = (1 << 0), std::function<void(class Console*, const std::vector<std::string>&)> m_func;
Developer = (1 << 1) ECommandFlags m_flags;
};
std::string m_displayName;
std::string m_helpString;
std::string m_usage;
std::function<void(class Console*, const std::vector<std::string>&)> m_func;
ECommandFlags m_flags;
}; };
ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags) ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags)
class Console class Console {
{ friend class LogVisorAdapter;
friend class LogVisorAdapter; struct LogVisorAdapter : logvisor::ILogger {
struct LogVisorAdapter : logvisor::ILogger Console* m_con;
{ LogVisorAdapter(Console* con) : m_con(con) {}
Console* m_con;
LogVisorAdapter(Console* con)
: m_con(con) {}
~LogVisorAdapter() {} ~LogVisorAdapter() {}
void report(const char* modName, logvisor::Level severity, void report(const char* modName, logvisor::Level severity, const char* format, va_list ap);
const char* format, va_list ap); void report(const char* modName, logvisor::Level severity, const wchar_t* format, va_list ap);
void report(const char* modName, logvisor::Level severity, void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
const wchar_t* format, va_list ap); const char* format, va_list ap);
void reportSource(const char* modName, logvisor::Level severity, void reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum,
const char* file, unsigned linenum, const wchar_t* format, va_list ap);
const char* format, va_list ap); };
void reportSource(const char* modName, logvisor::Level severity,
const char* file, unsigned linenum,
const wchar_t* format, va_list ap);
};
public: public:
static Console* m_instance; static Console* m_instance;
enum class Level enum class Level {
{ Info, /**< Non-error informative message */
Info, /**< Non-error informative message */ Warning, /**< Non-error warning message */
Warning, /**< Non-error warning message */ Error, /**< Recoverable error message */
Error, /**< Recoverable error message */ Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */
Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */ };
};
enum State enum State { Closed, Closing, Opened, Opening };
{
Closed,
Closing,
Opened,
Opening
};
private: private:
CVarManager* m_cvarMgr; CVarManager* m_cvarMgr;
std::unordered_map<std::string, SConsoleCommand> m_commands; std::unordered_map<std::string, SConsoleCommand> m_commands;
std::vector<std::pair<std::string, Level>> m_log; std::vector<std::pair<std::string, Level>> m_log;
int m_logOffset; int m_logOffset;
std::string m_commandString; std::string m_commandString;
std::vector<std::string> m_commandHistory; std::vector<std::string> m_commandHistory;
int m_cursorPosition = -1; int m_cursorPosition = -1;
int m_currentCommand = -1; int m_currentCommand = -1;
size_t m_maxLines = 0; size_t m_maxLines = 0;
bool m_overwrite : 1; bool m_overwrite : 1;
bool m_cursorAtEnd : 1; bool m_cursorAtEnd : 1;
State m_state = State::Closed; State m_state = State::Closed;
CVar* m_conSpeed; CVar* m_conSpeed;
CVar* m_conHeight; CVar* m_conHeight;
float m_cachedConSpeed; float m_cachedConSpeed;
float m_cachedConHeight; float m_cachedConHeight;
bool m_showCursor = true; bool m_showCursor = true;
float m_cursorTime = 0.f; float m_cursorTime = 0.f;
public: public:
Console(CVarManager*); Console(CVarManager*);
void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function<void(Console*, const std::vector<std::string>&)>&& func, SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal); void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage,
void unregisterCommand(std::string_view name); const std::function<void(Console*, const std::vector<std::string>&)>&& func,
SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal);
void unregisterCommand(std::string_view name);
void executeString(const std::string& strToExec); void executeString(const std::string& strToExec);
void help(Console* con, const std::vector<std::string>& args); void help(Console* con, const std::vector<std::string>& args);
void listCommands(Console* con, const std::vector<std::string>& args); void listCommands(Console* con, const std::vector<std::string>& args);
bool commandExists(std::string_view cmd); bool commandExists(std::string_view cmd);
void report(Level level, const char *fmt, va_list list); void report(Level level, const char* fmt, va_list list);
void report(Level level, const char* fmt, ...); void report(Level level, const char* fmt, ...);
void proc(); void proc();
void draw(boo::IGraphicsCommandQueue* gfxQ); void draw(boo::IGraphicsCommandQueue* gfxQ);
void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat); void handleCharCode(unsigned long chr, boo::EModifierKey mod, bool repeat);
void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat); void handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool repeat);
void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod); void handleSpecialKeyUp(boo::ESpecialKey sp, boo::EModifierKey mod);
void dumpLog(); void dumpLog();
static Console* instance(); static Console* instance();
static void RegisterLogger(Console* con); static void RegisterLogger(Console* con);
bool isOpen() { return m_state == State::Opened; } bool isOpen() { return m_state == State::Opened; }
}; };
} } // namespace hecl

View File

@ -21,12 +21,10 @@
#define RUNTIME_ORIGINAL_IDS 0 #define RUNTIME_ORIGINAL_IDS 0
namespace hecl namespace hecl {
{
class ClientProcess; class ClientProcess;
namespace Database namespace Database {
{
class Project; class Project;
extern logvisor::Module LogModule; extern logvisor::Module LogModule;
@ -34,27 +32,23 @@ extern logvisor::Module LogModule;
/** /**
* @brief Nodegraph class for gathering dependency-resolved objects for packaging * @brief Nodegraph class for gathering dependency-resolved objects for packaging
*/ */
class PackageDepsgraph class PackageDepsgraph {
{
public: public:
struct Node struct Node {
{ enum class Type { Data, Group } type;
enum class Type ProjectPath path;
{ ProjectPath cookedPath;
Data, class ObjectBase* projectObj;
Group Node* sub;
} type; Node* next;
ProjectPath path; };
ProjectPath cookedPath;
class ObjectBase* projectObj;
Node* sub;
Node* next;
};
private: private:
friend class Project; friend class Project;
std::vector<Node> m_nodes; std::vector<Node> m_nodes;
public: public:
const Node* getRootNode() const {return &m_nodes[0];} const Node* getRootNode() const { return &m_nodes[0]; }
}; };
/** /**
@ -63,78 +57,88 @@ public:
* The DataSpec class manages interfaces for unpackaging, cooking, and packaging * The DataSpec class manages interfaces for unpackaging, cooking, and packaging
* of data for interacting with a specific system/game-engine. * of data for interacting with a specific system/game-engine.
*/ */
class IDataSpec class IDataSpec {
{ const DataSpecEntry* m_specEntry;
const DataSpecEntry* m_specEntry;
public: public:
IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {} IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {}
virtual ~IDataSpec() {} virtual ~IDataSpec() {}
using FCookProgress = std::function<void(const SystemChar*)>; using FCookProgress = std::function<void(const SystemChar*)>;
/** /**
* @brief Extract Pass Info * @brief Extract Pass Info
* *
* An extract pass iterates through a source package or image and * An extract pass iterates through a source package or image and
* reverses the cooking process by emitting editable resources * reverses the cooking process by emitting editable resources
*/ */
struct ExtractPassInfo struct ExtractPassInfo {
{ SystemString srcpath;
SystemString srcpath; std::vector<SystemString> extractArgs;
std::vector<SystemString> extractArgs; bool force;
bool force; };
};
/** /**
* @brief Extract Report Representation * @brief Extract Report Representation
* *
* Constructed by canExtract() to advise the user of the content about * Constructed by canExtract() to advise the user of the content about
* to be extracted * to be extracted
*/ */
struct ExtractReport struct ExtractReport {
{ SystemString name;
SystemString name; SystemString desc;
SystemString desc; std::vector<ExtractReport> childOpts;
std::vector<ExtractReport> childOpts; };
};
virtual void setThreadProject() {} virtual void setThreadProject() {}
virtual bool canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps) virtual bool canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps) {
{(void)info;(void)reps;LogModule.report(logvisor::Error, "not implemented");return false;} (void)info;
virtual void doExtract(const ExtractPassInfo& info, const MultiProgressPrinter& progress) (void)reps;
{(void)info;(void)progress;} LogModule.report(logvisor::Error, "not implemented");
return false;
}
virtual void doExtract(const ExtractPassInfo& info, const MultiProgressPrinter& progress) {
(void)info;
(void)progress;
}
virtual bool canCook(const ProjectPath& path, blender::Token& btok, int cookPass = -1) virtual bool canCook(const ProjectPath& path, blender::Token& btok, int cookPass = -1) {
{(void)path;LogModule.report(logvisor::Error, "not implemented");(void)cookPass;return false;} (void)path;
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, LogModule.report(logvisor::Error, "not implemented");
const Database::DataSpecEntry* oldEntry, (void)cookPass;
blender::Token& btok) const return false;
{(void)path;return oldEntry;} }
virtual void doCook(const ProjectPath& path, const ProjectPath& cookedPath, virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, const Database::DataSpecEntry* oldEntry,
bool fast, blender::Token& btok, FCookProgress progress) blender::Token& btok) const {
{(void)path;(void)cookedPath;(void)fast;(void)progress;} (void)path;
return oldEntry;
}
virtual void doCook(const ProjectPath& path, const ProjectPath& cookedPath, bool fast, blender::Token& btok,
FCookProgress progress) {
(void)path;
(void)cookedPath;
(void)fast;
(void)progress;
}
virtual bool canPackage(const ProjectPath& path) virtual bool canPackage(const ProjectPath& path) {
{(void)path;return false;} (void)path;
virtual void doPackage(const ProjectPath& path, const Database::DataSpecEntry* entry, return false;
bool fast, blender::Token& btok, const MultiProgressPrinter& progress, }
ClientProcess* cp=nullptr) virtual void doPackage(const ProjectPath& path, const Database::DataSpecEntry* entry, bool fast, blender::Token& btok,
{(void)path;} const MultiProgressPrinter& progress, ClientProcess* cp = nullptr) {
(void)path;
}
virtual void interruptCook() {} virtual void interruptCook() {}
const DataSpecEntry* getDataSpecEntry() const {return m_specEntry;} const DataSpecEntry* getDataSpecEntry() const { return m_specEntry; }
}; };
/** /**
* @brief Pre-emptive indication of what the constructed DataSpec is used for * @brief Pre-emptive indication of what the constructed DataSpec is used for
*/ */
enum class DataSpecTool enum class DataSpecTool { Extract, Cook, Package };
{
Extract,
Cook,
Package
};
extern std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY; extern std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
@ -143,18 +147,16 @@ extern std::vector<const struct DataSpecEntry*> DATA_SPEC_REGISTRY;
* *
* Auto-registers with data spec registry * Auto-registers with data spec registry
*/ */
struct DataSpecEntry struct DataSpecEntry {
{ SystemStringView m_name;
SystemStringView m_name; SystemStringView m_desc;
SystemStringView m_desc; SystemStringView m_pakExt;
SystemStringView m_pakExt; int m_numCookPasses;
int m_numCookPasses; std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses, DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory) std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses), : m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses), m_factory(std::move(factory)) {}
m_factory(std::move(factory)) {}
}; };
/** /**
@ -166,79 +168,74 @@ struct DataSpecEntry
* *
* DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!! * DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!!
*/ */
class ObjectBase class ObjectBase {
{ friend class Project;
friend class Project; SystemString m_path;
SystemString m_path;
protected: protected:
/**
* @brief Byte-order of target system
*/
enum class DataEndianness {
None,
Big, /**< Big-endian (PowerPC) */
Little /**< Little-endian (Intel) */
};
/** /**
* @brief Byte-order of target system * @brief Data-formats of target system
*/ */
enum class DataEndianness enum class DataPlatform {
{ None,
None, Generic, /**< Scanline textures and 3-way shader bundle (GLSL, HLSL, SPIR-V) */
Big, /**< Big-endian (PowerPC) */ Revolution, /**< Tiled textures and GX register buffers */
Little /**< Little-endian (Intel) */ Cafe /**< Swizzled textures and R700 shader objects */
}; };
/** typedef std::function<void(const void* data, size_t len)> FDataAppender;
* @brief Data-formats of target system
*/
enum class DataPlatform
{
None,
Generic, /**< Scanline textures and 3-way shader bundle (GLSL, HLSL, SPIR-V) */
Revolution, /**< Tiled textures and GX register buffers */
Cafe /**< Swizzled textures and R700 shader objects */
};
typedef std::function<void(const void* data, size_t len)> FDataAppender; /**
* @brief Optional private method implemented by subclasses to cook objects
* @param dataAppender subclass calls this function zero or more times to provide cooked-data linearly
* @param endianness byte-order to target
* @param platform data-formats to target
* @return true if cook succeeded
*
* This method is called during IProject::cookPath().
* Part of the cooking process may include embedding database-refs to dependencies.
* This method should store the 64-bit value provided by IDataObject::id() when doing this.
*/
virtual bool cookObject(FDataAppender dataAppender, DataEndianness endianness, DataPlatform platform) {
(void)dataAppender;
(void)endianness;
(void)platform;
return true;
}
/** typedef std::function<void(ObjectBase*)> FDepAdder;
* @brief Optional private method implemented by subclasses to cook objects
* @param dataAppender subclass calls this function zero or more times to provide cooked-data linearly
* @param endianness byte-order to target
* @param platform data-formats to target
* @return true if cook succeeded
*
* This method is called during IProject::cookPath().
* Part of the cooking process may include embedding database-refs to dependencies.
* This method should store the 64-bit value provided by IDataObject::id() when doing this.
*/
virtual bool cookObject(FDataAppender dataAppender,
DataEndianness endianness, DataPlatform platform)
{(void)dataAppender;(void)endianness;(void)platform;return true;}
typedef std::function<void(ObjectBase*)> FDepAdder; /**
* @brief Optional private method implemented by CProjectObject subclasses to resolve dependencies
* @param depAdder subclass calls this function zero or more times to register each dependency
*
* This method is called during IProject::packagePath().
* Dependencies registered via this method will eventually have this method called on themselves
* as well. This is a non-recursive operation, no need for subclasses to implement recursion-control.
*/
virtual void gatherDeps(FDepAdder depAdder) { (void)depAdder; }
/** /**
* @brief Optional private method implemented by CProjectObject subclasses to resolve dependencies * @brief Get a packagable FourCC representation of the object's type
* @param depAdder subclass calls this function zero or more times to register each dependency * @return FourCC of the type
* */
* This method is called during IProject::packagePath(). virtual FourCC getType() const { return FourCC("NULL"); }
* Dependencies registered via this method will eventually have this method called on themselves
* as well. This is a non-recursive operation, no need for subclasses to implement recursion-control.
*/
virtual void gatherDeps(FDepAdder depAdder)
{(void)depAdder;}
/**
* @brief Get a packagable FourCC representation of the object's type
* @return FourCC of the type
*/
virtual FourCC getType() const
{return FourCC("NULL");}
public: public:
ObjectBase(SystemStringView path) ObjectBase(SystemStringView path) : m_path(path) {}
: m_path(path) {}
SystemStringView getPath() const {return m_path;}
SystemStringView getPath() const { return m_path; }
}; };
/** /**
* @brief Main project interface * @brief Main project interface
* *
@ -246,237 +243,226 @@ public:
* resources in their ideal editor-formats. This interface exposes all * resources in their ideal editor-formats. This interface exposes all
* primary operations to perform on a given project. * primary operations to perform on a given project.
*/ */
class Project class Project {
{
public: public:
struct ProjectDataSpec struct ProjectDataSpec {
{ const DataSpecEntry& spec;
const DataSpecEntry& spec; ProjectPath cookedPath;
ProjectPath cookedPath; bool active;
bool active; };
};
private: private:
ProjectRootPath m_rootPath; ProjectRootPath m_rootPath;
ProjectPath m_workRoot; ProjectPath m_workRoot;
ProjectPath m_dotPath; ProjectPath m_dotPath;
ProjectPath m_cookedRoot; ProjectPath m_cookedRoot;
std::vector<ProjectDataSpec> m_compiledSpecs; std::vector<ProjectDataSpec> m_compiledSpecs;
std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache; std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache;
std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs; std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs;
std::unique_ptr<IDataSpec> m_lastPackageSpec; std::unique_ptr<IDataSpec> m_lastPackageSpec;
bool m_valid = false; bool m_valid = false;
public: public:
Project(const ProjectRootPath& rootPath); Project(const ProjectRootPath& rootPath);
operator bool() const {return m_valid;} operator bool() const { return m_valid; }
/** /**
* @brief Configuration file handle * @brief Configuration file handle
* *
* Holds a path to a line-delimited textual configuration file; * Holds a path to a line-delimited textual configuration file;
* opening a locked handle for read/write transactions * opening a locked handle for read/write transactions
*/ */
class ConfigFile class ConfigFile {
{ SystemString m_filepath;
SystemString m_filepath; std::vector<std::string> m_lines;
std::vector<std::string> m_lines; FILE* m_lockedFile = NULL;
FILE* m_lockedFile = NULL;
public:
ConfigFile(const Project& project, SystemStringView name,
SystemStringView subdir=_SYS_STR("/.hecl/"));
std::vector<std::string>& lockAndRead();
void addLine(std::string_view line);
void removeLine(std::string_view refLine);
bool checkForLine(std::string_view refLine);
void unlockAndDiscard();
bool unlockAndCommit();
};
ConfigFile m_specs;
ConfigFile m_paths;
ConfigFile m_groups;
/** public:
* @brief A rough description of how 'expensive' a given cook operation is ConfigFile(const Project& project, SystemStringView name, SystemStringView subdir = _SYS_STR("/.hecl/"));
* std::vector<std::string>& lockAndRead();
* This is used to provide pretty colors during the cook operation void addLine(std::string_view line);
*/ void removeLine(std::string_view refLine);
enum class Cost bool checkForLine(std::string_view refLine);
{ void unlockAndDiscard();
None, bool unlockAndCommit();
Light, };
Medium, ConfigFile m_specs;
Heavy ConfigFile m_paths;
}; ConfigFile m_groups;
/** /**
* @brief Get the path of the project's root-directory * @brief A rough description of how 'expensive' a given cook operation is
* @return project root path *
* * This is used to provide pretty colors during the cook operation
* Self explanatory */
*/ enum class Cost { None, Light, Medium, Heavy };
const ProjectRootPath& getProjectRootPath() const {return m_rootPath;}
/** /**
* @brief Get the path of project's working directory * @brief Get the path of the project's root-directory
* @return project working path * @return project root path
*/ *
const ProjectPath& getProjectWorkingPath() const {return m_workRoot;} * Self explanatory
*/
const ProjectRootPath& getProjectRootPath() const { return m_rootPath; }
/** /**
* @brief Get the path of project's cooked directory for a specific DataSpec * @brief Get the path of project's working directory
* @param DataSpec to retrieve path for * @return project working path
* @return project cooked path */
* const ProjectPath& getProjectWorkingPath() const { return m_workRoot; }
* The cooked path matches the directory layout of the working directory
*/
const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
/** /**
* @brief Add given file(s) to the database * @brief Get the path of project's cooked directory for a specific DataSpec
* @param path file or pattern within project * @param DataSpec to retrieve path for
* @return true on success * @return project cooked path
* *
* This method blocks while object hashing takes place * The cooked path matches the directory layout of the working directory
*/ */
bool addPaths(const std::vector<ProjectPath>& paths); const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
/** /**
* @brief Remove a given file or file-pattern from the database * @brief Add given file(s) to the database
* @param paths file(s) or pattern(s) within project * @param path file or pattern within project
* @param recursive traverse into matched subdirectories * @return true on success
* @return true on success *
* * This method blocks while object hashing takes place
* This method will not delete actual working files from the project */
* directory. It will delete associated cooked objects though. bool addPaths(const std::vector<ProjectPath>& paths);
*/
bool removePaths(const std::vector<ProjectPath>& paths, bool recursive=false);
/** /**
* @brief Register a working sub-directory as a Dependency Group * @brief Remove a given file or file-pattern from the database
* @param path directory to register as Dependency Group * @param paths file(s) or pattern(s) within project
* @return true on success * @param recursive traverse into matched subdirectories
* * @return true on success
* Dependency Groups are used at runtime to stage burst load-transactions. *
* They may only be added to directories and will automatically claim * This method will not delete actual working files from the project
* subdirectories as well. * directory. It will delete associated cooked objects though.
* */
* Cooked objects in dependency groups will be packaged contiguously bool removePaths(const std::vector<ProjectPath>& paths, bool recursive = false);
* and automatically duplicated if shared with other dependency groups.
* This contiguous storage makes for optimal loading from slow block-devices
* like optical drives.
*/
bool addGroup(const ProjectPath& path);
/** /**
* @brief Unregister a working sub-directory as a dependency group * @brief Register a working sub-directory as a Dependency Group
* @param path directory to unregister as Dependency Group * @param path directory to register as Dependency Group
* @return true on success * @return true on success
*/ *
bool removeGroup(const ProjectPath& path); * Dependency Groups are used at runtime to stage burst load-transactions.
* They may only be added to directories and will automatically claim
* subdirectories as well.
*
* Cooked objects in dependency groups will be packaged contiguously
* and automatically duplicated if shared with other dependency groups.
* This contiguous storage makes for optimal loading from slow block-devices
* like optical drives.
*/
bool addGroup(const ProjectPath& path);
/** /**
* @brief Re-reads the data store holding user's spec preferences * @brief Unregister a working sub-directory as a dependency group
* * @param path directory to unregister as Dependency Group
* Call periodically in a long-term use of the hecl::Database::Project class. * @return true on success
* Install filesystem event-hooks if possible. */
*/ bool removeGroup(const ProjectPath& path);
void rescanDataSpecs();
/** /**
* @brief Return map populated with dataspecs targetable by this project interface * @brief Re-reads the data store holding user's spec preferences
* @return Platform map with name-string keys and enable-status values *
*/ * Call periodically in a long-term use of the hecl::Database::Project class.
const std::vector<ProjectDataSpec>& getDataSpecs() const {return m_compiledSpecs;} * Install filesystem event-hooks if possible.
*/
void rescanDataSpecs();
/** /**
* @brief Enable persistent user preference for particular spec string(s) * @brief Return map populated with dataspecs targetable by this project interface
* @param specs String(s) representing unique spec(s) from getDataSpecs * @return Platform map with name-string keys and enable-status values
* @return true on success */
*/ const std::vector<ProjectDataSpec>& getDataSpecs() const { return m_compiledSpecs; }
bool enableDataSpecs(const std::vector<SystemString>& specs);
/** /**
* @brief Disable persistent user preference for particular spec string(s) * @brief Enable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs * @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success * @return true on success
*/ */
bool disableDataSpecs(const std::vector<SystemString>& specs); bool enableDataSpecs(const std::vector<SystemString>& specs);
/** /**
* @brief Begin cook process for specified directory * @brief Disable persistent user preference for particular spec string(s)
* @param path directory of intermediates to cook * @param specs String(s) representing unique spec(s) from getDataSpecs
* @param feedbackCb a callback to run reporting cook-progress * @return true on success
* @param recursive traverse subdirectories to cook as well */
* @param fast enables faster (draft) extraction for supported data types bool disableDataSpecs(const std::vector<SystemString>& specs);
* @param spec if non-null, cook using a manually-selected dataspec
* @param cp if non-null, cook asynchronously via the ClientProcess
* @param cookPass cookPath() should be called the number of times
* prescribed in DataSpecEntry at the root-most invocation.
* This value conveys the pass index through the call tree.
* Negative values mean "cook always".
* @return true on success
*
* Object cooking is generally an expensive process for large projects.
* This method blocks execution during the procedure, with periodic
* feedback delivered via feedbackCb.
*/
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb,
bool recursive=false, bool force=false, bool fast=false,
const DataSpecEntry* spec=nullptr, ClientProcess* cp=nullptr,
int cookPass = -1);
/** /**
* @brief Begin package process for specified !world.blend or directory * @brief Begin cook process for specified directory
* @param path Path to !world.blend or directory * @param path directory of intermediates to cook
* @param feedbackCb a callback to run reporting cook-progress * @param feedbackCb a callback to run reporting cook-progress
* @param fast enables faster (draft) extraction for supported data types * @param recursive traverse subdirectories to cook as well
* @param spec if non-null, cook using a manually-selected dataspec * @param fast enables faster (draft) extraction for supported data types
* @param cp if non-null, cook asynchronously via the ClientProcess * @param spec if non-null, cook using a manually-selected dataspec
*/ * @param cp if non-null, cook asynchronously via the ClientProcess
bool packagePath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, * @param cookPass cookPath() should be called the number of times
bool fast=false, const DataSpecEntry* spec=nullptr, * prescribed in DataSpecEntry at the root-most invocation.
ClientProcess* cp=nullptr); * This value conveys the pass index through the call tree.
* Negative values mean "cook always".
* @return true on success
*
* Object cooking is generally an expensive process for large projects.
* This method blocks execution during the procedure, with periodic
* feedback delivered via feedbackCb.
*/
bool cookPath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool recursive = false,
bool force = false, bool fast = false, const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr,
int cookPass = -1);
/** /**
* @brief Interrupts a cook in progress (call from SIGINT handler) * @brief Begin package process for specified !world.blend or directory
* * @param path Path to !world.blend or directory
* Database corruption is bad! HECL spreads its data objects through * @param feedbackCb a callback to run reporting cook-progress
* the filesystem; this ensures that open objects are cleanly * @param fast enables faster (draft) extraction for supported data types
* finalized or discarded before stopping. * @param spec if non-null, cook using a manually-selected dataspec
* * @param cp if non-null, cook asynchronously via the ClientProcess
* Note that this method returns immediately; the resumed cookPath() */
* call will return as quickly as possible. bool packagePath(const ProjectPath& path, const MultiProgressPrinter& feedbackCb, bool fast = false,
*/ const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr);
void interruptCook();
/** /**
* @brief Delete cooked objects for directory * @brief Interrupts a cook in progress (call from SIGINT handler)
* @param path directory of intermediates to clean *
* @param recursive traverse subdirectories to clean as well * Database corruption is bad! HECL spreads its data objects through
* @return true on success * the filesystem; this ensures that open objects are cleanly
* * finalized or discarded before stopping.
* Developers understand how useful 'clean' is. While ideally not required, *
* it's useful for verifying that a rebuild from ground-up is doable. * Note that this method returns immediately; the resumed cookPath()
*/ * call will return as quickly as possible.
bool cleanPath(const ProjectPath& path, bool recursive=false); */
void interruptCook();
/** /**
* @brief Constructs a full depsgraph of the project-subpath provided * @brief Delete cooked objects for directory
* @param path Subpath of project to root depsgraph at * @param path directory of intermediates to clean
* @return Populated depsgraph ready to traverse * @param recursive traverse subdirectories to clean as well
*/ * @return true on success
PackageDepsgraph buildPackageDepsgraph(const ProjectPath& path); *
* Developers understand how useful 'clean' is. While ideally not required,
* it's useful for verifying that a rebuild from ground-up is doable.
*/
bool cleanPath(const ProjectPath& path, bool recursive = false);
/** Add ProjectPath to bridge cache */ /**
void addBridgePathToCache(uint64_t id, const ProjectPath& path); * @brief Constructs a full depsgraph of the project-subpath provided
* @param path Subpath of project to root depsgraph at
* @return Populated depsgraph ready to traverse
*/
PackageDepsgraph buildPackageDepsgraph(const ProjectPath& path);
/** Clear all ProjectPaths in bridge cache */ /** Add ProjectPath to bridge cache */
void clearBridgePathCache(); void addBridgePathToCache(uint64_t id, const ProjectPath& path);
/** Lookup ProjectPath from bridge cache */ /** Clear all ProjectPaths in bridge cache */
const ProjectPath* lookupBridgePath(uint64_t id) const; void clearBridgePathCache();
/** Lookup ProjectPath from bridge cache */
const ProjectPath* lookupBridgePath(uint64_t id) const;
}; };
} } // namespace Database
} } // namespace hecl

View File

@ -5,8 +5,7 @@
#include <string> #include <string>
#include "athena/DNA.hpp" #include "athena/DNA.hpp"
namespace hecl namespace hecl {
{
/** /**
* @brief FourCC representation used within HECL's database * @brief FourCC representation used within HECL's database
@ -15,73 +14,73 @@ namespace hecl
* while fitting comfortably in a 32-bit word. HECL uses a four-char array * while fitting comfortably in a 32-bit word. HECL uses a four-char array
* to remain endian-independent. * to remain endian-independent.
*/ */
class FourCC class FourCC {
{
protected: protected:
union union {
{ char fcc[4];
char fcc[4]; uint32_t num;
uint32_t num; };
};
public: public:
FourCC() /* Sentinel FourCC */ FourCC() /* Sentinel FourCC */
: num(0) {} : num(0) {}
FourCC(const FourCC& other) FourCC(const FourCC& other) { num = other.num; }
{num = other.num;} FourCC(const char* name) : num(*(uint32_t*)name) {}
FourCC(const char* name) FourCC(uint32_t n) : num(n) {}
: num(*(uint32_t*)name) {} bool operator==(const FourCC& other) const { return num == other.num; }
FourCC(uint32_t n) bool operator!=(const FourCC& other) const { return num != other.num; }
: num(n) {} bool operator==(const char* other) const { return num == *(uint32_t*)other; }
bool operator==(const FourCC& other) const {return num == other.num;} bool operator!=(const char* other) const { return num != *(uint32_t*)other; }
bool operator!=(const FourCC& other) const {return num != other.num;} bool operator==(int32_t other) const { return num == other; }
bool operator==(const char* other) const {return num == *(uint32_t*)other;} bool operator!=(int32_t other) const { return num != other; }
bool operator!=(const char* other) const {return num != *(uint32_t*)other;} bool operator==(uint32_t other) const { return num == other; }
bool operator==(int32_t other) const { return num == other;} bool operator!=(uint32_t other) const { return num != other; }
bool operator!=(int32_t other) const { return num != other;} std::string toString() const { return std::string(fcc, 4); }
bool operator==(uint32_t other) const {return num == other;} uint32_t toUint32() const { return num; }
bool operator!=(uint32_t other) const {return num != other;} operator uint32_t() const { return num; }
std::string toString() const {return std::string(fcc, 4);} const char* getChars() const { return fcc; }
uint32_t toUint32() const {return num;} char* getChars() { return fcc; }
operator uint32_t() const {return num;}
const char* getChars() const {return fcc;}
char* getChars() {return fcc;}
}; };
#define FOURCC(chars) FourCC(SBIG(chars)) #define FOURCC(chars) FourCC(SBIG(chars))
using BigDNA = athena::io::DNA<athena::Big>; using BigDNA = athena::io::DNA<athena::Big>;
/** FourCC with DNA read/write */ /** FourCC with DNA read/write */
class DNAFourCC final : public BigDNA, public FourCC class DNAFourCC final : public BigDNA, public FourCC {
{
public: public:
DNAFourCC() : FourCC() {} DNAFourCC() : FourCC() {}
DNAFourCC(const FourCC& other) DNAFourCC(const FourCC& other) : FourCC() { num = other.toUint32(); }
: FourCC() {num = other.toUint32();} DNAFourCC(const char* name) : FourCC(name) {}
DNAFourCC(const char* name) DNAFourCC(uint32_t n) : FourCC(n) {}
: FourCC(name) {} AT_DECL_EXPLICIT_DNA_YAML
DNAFourCC(uint32_t n)
: FourCC(n) {}
AT_DECL_EXPLICIT_DNA_YAML
}; };
template <> inline void DNAFourCC::Enumerate<BigDNA::Read>(typename Read::StreamT& r) template <>
{ r.readUBytesToBuf(fcc, 4); } inline void DNAFourCC::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
template <> inline void DNAFourCC::Enumerate<BigDNA::Write>(typename Write::StreamT& w) r.readUBytesToBuf(fcc, 4);
{ w.writeUBytes((atUint8*)fcc, 4); } }
template <> inline void DNAFourCC::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) template <>
{ std::string rs = r.readString(nullptr); strncpy(fcc, rs.c_str(), 4); } inline void DNAFourCC::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
template <> inline void DNAFourCC::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) w.writeUBytes((atUint8*)fcc, 4);
{ w.writeString(nullptr, std::string(fcc, 4)); } }
template <> inline void DNAFourCC::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) template <>
{ s += 4; } inline void DNAFourCC::Enumerate<BigDNA::ReadYaml>(typename ReadYaml::StreamT& r) {
std::string rs = r.readString(nullptr);
strncpy(fcc, rs.c_str(), 4);
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::WriteYaml>(typename WriteYaml::StreamT& w) {
w.writeString(nullptr, std::string(fcc, 4));
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& s) {
s += 4;
} }
namespace std } // namespace hecl
{
template <> struct hash<hecl::FourCC>
{
size_t operator()(const hecl::FourCC& val) const noexcept
{return val.toUint32();}
};
}
namespace std {
template <>
struct hash<hecl::FourCC> {
size_t operator()(const hecl::FourCC& val) const noexcept { return val.toUint32(); }
};
} // namespace std

View File

@ -8,372 +8,341 @@
#include <hecl/hecl.hpp> #include <hecl/hecl.hpp>
#include <boo/graphicsdev/IGraphicsDataFactory.hpp> #include <boo/graphicsdev/IGraphicsDataFactory.hpp>
namespace hecl::Frontend namespace hecl::Frontend {
{
using namespace std::literals; using namespace std::literals;
struct SourceLocation struct SourceLocation {
{ int line = -1;
int line = -1; int col = -1;
int col = -1; SourceLocation() = default;
SourceLocation() = default; SourceLocation(int l, int c) : line(l), col(c) {}
SourceLocation(int l, int c) : line(l), col(c) {}
}; };
class Diagnostics class Diagnostics {
{ std::string m_name;
std::string m_name; std::string m_source;
std::string m_source; std::string m_backend = "Backend";
std::string m_backend = "Backend"; std::string sourceDiagString(const SourceLocation& l, bool ansi = false) const;
std::string sourceDiagString(const SourceLocation& l, bool ansi=false) const;
public:
void reset(std::string_view name, std::string_view source) {m_name = name; m_source = source;}
void reset(std::string_view name) {m_name = name; m_source.clear();}
void setBackend(std::string_view backend) {m_backend = backend;}
void reportScannerErr(const SourceLocation& l, const char* format, ...);
void reportParserErr(const SourceLocation& l, const char* format, ...);
void reportBackendErr(const SourceLocation& l, const char* format, ...);
std::string_view getName() const {return m_name;}
std::string_view getSource() const {return m_source;}
};
struct Token
{
enum class Kind
{
None,
Eof,
Lf,
Plus,
Minus,
Times,
Div,
Lpar,
Rpar,
Comma,
Period,
Ident,
Number
};
static std::string_view KindToStr(Kind k)
{
switch (k)
{
case Kind::None:
default:
return "none"sv;
case Kind::Eof: return "eof"sv;
case Kind::Lf: return "lf"sv;
case Kind::Plus: return "+"sv;
case Kind::Minus: return "-"sv;
case Kind::Times: return "*"sv;
case Kind::Div: return "/"sv;
case Kind::Lpar: return "("sv;
case Kind::Rpar: return ")"sv;
case Kind::Comma: return ","sv;
case Kind::Period: return "."sv;
case Kind::Ident: return "ident"sv;
case Kind::Number: return "number"sv;
}
}
Kind kind = Kind::None;
std::string str;
SourceLocation loc;
Token() = default;
Token(Kind kind, const SourceLocation& loc)
: kind(kind), loc(loc) {}
Token(Kind kind, std::string&& str, const SourceLocation& loc)
: kind(kind), str(std::move(str)), loc(loc) {}
std::string toString() const
{
if (str.empty())
return hecl::Format("%d:%d: %s", loc.line, loc.col, KindToStr(kind).data());
else
return hecl::Format("%d:%d: %s (%s)", loc.line, loc.col, KindToStr(kind).data(), str.c_str());
}
};
class Scanner
{
friend class Parser;
static constexpr char LF = '\n';
static constexpr char COMMENT = '#';
Diagnostics& m_diag;
std::string_view m_source;
std::string_view::const_iterator m_sourceIt;
char ch;
SourceLocation loc;
int lfcol;
std::string lastLine;
std::string currentLine;
Token::Kind CharToTokenKind(char ch)
{
switch (ch)
{
case '(': return Token::Kind::Lpar;
case ')': return Token::Kind::Rpar;
case ',': return Token::Kind::Comma;
case '.': return Token::Kind::Period;
case '+': return Token::Kind::Plus;
case '-': return Token::Kind::Minus;
case '*': return Token::Kind::Times;
case '/': return Token::Kind::Div;
default: return Token::Kind::None;
}
}
template <class... Args>
void error(const SourceLocation& loc, const char* s, Args&&... args)
{
m_diag.reportScannerErr(loc, s, args...);
}
static bool isDigit(char c)
{
return c >= '0' && c <= '9';
}
static bool isStartIdent(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c == '_');
}
static bool isMidIdent(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c == '_') || isDigit(c);
}
int _read();
bool read();
static char chr(char c) { return (c < 32) ? '.' : c; }
public: public:
Scanner(Diagnostics& diag) : m_diag(diag) {} void reset(std::string_view name, std::string_view source) {
m_name = name;
m_source = source;
}
void reset(std::string_view name) {
m_name = name;
m_source.clear();
}
void setBackend(std::string_view backend) { m_backend = backend; }
void reportScannerErr(const SourceLocation& l, const char* format, ...);
void reportParserErr(const SourceLocation& l, const char* format, ...);
void reportBackendErr(const SourceLocation& l, const char* format, ...);
void reset(std::string_view in) std::string_view getName() const { return m_name; }
{ std::string_view getSource() const { return m_source; }
m_source = in;
m_sourceIt = in.cbegin();
ch = 0;
loc.line = 1;
loc.col = 0;
lfcol = 0;
lastLine = std::string();
currentLine = std::string();
read();
}
Token next();
}; };
struct IRNode struct Token {
{ enum class Kind { None, Eof, Lf, Plus, Minus, Times, Div, Lpar, Rpar, Comma, Period, Ident, Number };
friend struct IR;
enum class Op static std::string_view KindToStr(Kind k) {
{ switch (k) {
Add, Sub, Mul, Div case Kind::None:
}; default:
enum class Kind return "none"sv;
{ case Kind::Eof:
None, Call, Imm, Binop, Swizzle return "eof"sv;
}; case Kind::Lf:
Kind kind = Kind::None; return "lf"sv;
std::string str; case Kind::Plus:
float val; return "+"sv;
Op op; case Kind::Minus:
std::unique_ptr<IRNode> left; return "-"sv;
std::unique_ptr<IRNode> right; case Kind::Times:
std::list<IRNode> children; return "*"sv;
SourceLocation loc; case Kind::Div:
return "/"sv;
static std::string_view OpToStr(Op op) case Kind::Lpar:
{ return "("sv;
switch (op) case Kind::Rpar:
{ return ")"sv;
case Op::Add: return "+"sv; case Kind::Comma:
case Op::Sub: return "-"sv; return ","sv;
case Op::Mul: return "*"sv; case Kind::Period:
case Op::Div: return "/"sv; return "."sv;
default: return ""sv; case Kind::Ident:
} return "ident"sv;
case Kind::Number:
return "number"sv;
} }
}
static std::string_view KindToStr(Kind k) Kind kind = Kind::None;
{ std::string str;
switch (k) SourceLocation loc;
{
case Kind::None: Token() = default;
default: Token(Kind kind, const SourceLocation& loc) : kind(kind), loc(loc) {}
return "none"sv; Token(Kind kind, std::string&& str, const SourceLocation& loc) : kind(kind), str(std::move(str)), loc(loc) {}
case Kind::Call: return "call"sv;
case Kind::Imm: return "imm"sv; std::string toString() const {
case Kind::Binop: return "binop"sv; if (str.empty())
case Kind::Swizzle: return "swizzle"sv; return hecl::Format("%d:%d: %s", loc.line, loc.col, KindToStr(kind).data());
} else
return hecl::Format("%d:%d: %s (%s)", loc.line, loc.col, KindToStr(kind).data(), str.c_str());
}
};
class Scanner {
friend class Parser;
static constexpr char LF = '\n';
static constexpr char COMMENT = '#';
Diagnostics& m_diag;
std::string_view m_source;
std::string_view::const_iterator m_sourceIt;
char ch;
SourceLocation loc;
int lfcol;
std::string lastLine;
std::string currentLine;
Token::Kind CharToTokenKind(char ch) {
switch (ch) {
case '(':
return Token::Kind::Lpar;
case ')':
return Token::Kind::Rpar;
case ',':
return Token::Kind::Comma;
case '.':
return Token::Kind::Period;
case '+':
return Token::Kind::Plus;
case '-':
return Token::Kind::Minus;
case '*':
return Token::Kind::Times;
case '/':
return Token::Kind::Div;
default:
return Token::Kind::None;
} }
}
IRNode() = default; template <class... Args>
IRNode(Kind kind, std::string&& str, const SourceLocation& loc) void error(const SourceLocation& loc, const char* s, Args&&... args) {
: kind(kind), str(std::move(str)), loc(loc) {} m_diag.reportScannerErr(loc, s, args...);
IRNode(Kind kind, float val, const SourceLocation& loc) }
: kind(kind), val(val), loc(loc) {}
IRNode(Kind kind, std::string&& str, std::list<IRNode>&& children, const SourceLocation& loc)
: kind(kind), str(std::move(str)), children(std::move(children)), loc(loc) {}
IRNode(Op op, IRNode&& left, IRNode&& right, const SourceLocation& loc)
: kind(Kind::Binop), op(op), left(new IRNode(std::move(left))), right(new IRNode(std::move(right))), loc(loc) {}
IRNode(Kind kind, std::string&& str, IRNode&& node, const SourceLocation& loc)
: kind(kind), str(std::move(str)), left(new IRNode(std::move(node))), loc(loc) {}
std::string toString(bool stripUVAnims = false) const { return fmt(0, stripUVAnims); } static bool isDigit(char c) { return c >= '0' && c <= '9'; }
static bool isStartIdent(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_'); }
static bool isMidIdent(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') || isDigit(c);
}
int _read();
bool read();
static char chr(char c) { return (c < 32) ? '.' : c; }
public:
Scanner(Diagnostics& diag) : m_diag(diag) {}
void reset(std::string_view in) {
m_source = in;
m_sourceIt = in.cbegin();
ch = 0;
loc.line = 1;
loc.col = 0;
lfcol = 0;
lastLine = std::string();
currentLine = std::string();
read();
}
Token next();
};
struct IRNode {
friend struct IR;
enum class Op { Add, Sub, Mul, Div };
enum class Kind { None, Call, Imm, Binop, Swizzle };
Kind kind = Kind::None;
std::string str;
float val;
Op op;
std::unique_ptr<IRNode> left;
std::unique_ptr<IRNode> right;
std::list<IRNode> children;
SourceLocation loc;
static std::string_view OpToStr(Op op) {
switch (op) {
case Op::Add:
return "+"sv;
case Op::Sub:
return "-"sv;
case Op::Mul:
return "*"sv;
case Op::Div:
return "/"sv;
default:
return ""sv;
}
}
static std::string_view KindToStr(Kind k) {
switch (k) {
case Kind::None:
default:
return "none"sv;
case Kind::Call:
return "call"sv;
case Kind::Imm:
return "imm"sv;
case Kind::Binop:
return "binop"sv;
case Kind::Swizzle:
return "swizzle"sv;
}
}
IRNode() = default;
IRNode(Kind kind, std::string&& str, const SourceLocation& loc) : kind(kind), str(std::move(str)), loc(loc) {}
IRNode(Kind kind, float val, const SourceLocation& loc) : kind(kind), val(val), loc(loc) {}
IRNode(Kind kind, std::string&& str, std::list<IRNode>&& children, const SourceLocation& loc)
: kind(kind), str(std::move(str)), children(std::move(children)), loc(loc) {}
IRNode(Op op, IRNode&& left, IRNode&& right, const SourceLocation& loc)
: kind(Kind::Binop), op(op), left(new IRNode(std::move(left))), right(new IRNode(std::move(right))), loc(loc) {}
IRNode(Kind kind, std::string&& str, IRNode&& node, const SourceLocation& loc)
: kind(kind), str(std::move(str)), left(new IRNode(std::move(node))), loc(loc) {}
std::string toString(bool stripUVAnims = false) const { return fmt(0, stripUVAnims); }
private: private:
static std::string rep(int n, std::string_view s); static std::string rep(int n, std::string_view s);
std::string fmt(int level, bool stripUVAnims) const; std::string fmt(int level, bool stripUVAnims) const;
std::string describe() const; std::string describe() const;
}; };
class Parser class Parser {
{ Scanner m_scanner;
Scanner m_scanner;
Token t; Token t;
Token la; Token la;
Token::Kind sym; Token::Kind sym;
void scan() void scan() {
{ t = la;
t = la; la = m_scanner.next();
la = m_scanner.next(); sym = la.kind;
sym = la.kind; }
}
template <class... Args> template <class... Args>
void error(const char* s, Args&&... args) void error(const char* s, Args&&... args) {
{ m_scanner.m_diag.reportParserErr(la.loc, s, args...);
m_scanner.m_diag.reportParserErr(la.loc, s, args...); }
}
void check(Token::Kind expected); void check(Token::Kind expected);
IRNode call(); IRNode call();
static bool imm(const IRNode& a, const IRNode& b); static bool imm(const IRNode& a, const IRNode& b);
IRNode expr(); IRNode expr();
IRNode sum(); IRNode sum();
IRNode factor(); IRNode factor();
IRNode value(); IRNode value();
public: public:
Parser(Diagnostics& diag) Parser(Diagnostics& diag) : m_scanner(diag) {}
: m_scanner(diag) {}
void reset(std::string_view in) { la = Token(); m_scanner.reset(in); } void reset(std::string_view in) {
std::list<IRNode> parse(); la = Token();
m_scanner.reset(in);
}
std::list<IRNode> parse();
}; };
using BigDNA = athena::io::DNA<athena::Big>; using BigDNA = athena::io::DNA<athena::Big>;
struct IR : BigDNA struct IR : BigDNA {
{ AT_DECL_EXPLICIT_DNA
enum OpType : uint8_t {
None, /**< NOP */
Call, /**< Deferred function insertion for HECL backend using specified I/O regs */
LoadImm, /**< Load a constant (numeric literal) into register */
Arithmetic, /**< Perform binary arithmetic between registers */
Swizzle /**< Vector insertion/extraction/swizzling operation */
};
using RegID = atUint16;
struct Instruction : BigDNA {
AT_DECL_EXPLICIT_DNA AT_DECL_EXPLICIT_DNA
enum OpType : uint8_t OpType m_op = OpType::None;
{ RegID m_target = RegID(-1);
None, /**< NOP */ SourceLocation m_loc;
Call, /**< Deferred function insertion for HECL backend using specified I/O regs */
LoadImm, /**< Load a constant (numeric literal) into register */
Arithmetic, /**< Perform binary arithmetic between registers */
Swizzle /**< Vector insertion/extraction/swizzling operation */
};
using RegID = atUint16; struct Call : BigDNA {
AT_DECL_DNA
String<-1> m_name;
Value<atUint16> m_argInstCount;
Vector<atUint16, AT_DNA_COUNT(m_argInstCount)> m_argInstIdxs;
} m_call;
struct Instruction : BigDNA struct LoadImm : BigDNA {
{ AT_DECL_DNA
AT_DECL_EXPLICIT_DNA Value<atVec4f> m_immVec = {};
} m_loadImm;
OpType m_op = OpType::None; enum ArithmeticOpType : uint8_t { None, Add, Subtract, Multiply, Divide };
RegID m_target = RegID(-1);
SourceLocation m_loc;
struct Call : BigDNA struct Arithmetic : BigDNA {
{ AT_DECL_DNA
AT_DECL_DNA Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
String<-1> m_name; Value<atUint16> m_instIdxs[2];
Value<atUint16> m_argInstCount; } m_arithmetic;
Vector<atUint16, AT_DNA_COUNT(m_argInstCount)> m_argInstIdxs;
} m_call;
struct LoadImm : BigDNA struct Swizzle : BigDNA {
{ AT_DECL_DNA
AT_DECL_DNA Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
Value<atVec4f> m_immVec = {}; Value<atUint16> m_instIdx;
} m_loadImm; } m_swizzle;
enum ArithmeticOpType : uint8_t Instruction(OpType type, RegID target, const SourceLocation& loc) : m_op(type), m_target(target), m_loc(loc) {}
{ int getChildCount() const;
None, const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
Add, const atVec4f& getImmVec() const;
Subtract,
Multiply,
Divide
};
struct Arithmetic : BigDNA Instruction(athena::io::IStreamReader& reader) { read(reader); }
{ };
AT_DECL_DNA
Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
Value<atUint16> m_instIdxs[2];
} m_arithmetic;
struct Swizzle : BigDNA atUint64 m_hash = 0;
{ atUint16 m_regCount = 0;
AT_DECL_DNA std::vector<Instruction> m_instructions;
Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
Value<atUint16> m_instIdx;
} m_swizzle;
Instruction(OpType type, RegID target, const SourceLocation& loc) boo::BlendFactor m_blendSrc = boo::BlendFactor::One;
: m_op(type), m_target(target), m_loc(loc) {} boo::BlendFactor m_blendDst = boo::BlendFactor::Zero;
int getChildCount() const; bool m_doAlpha = false;
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
const atVec4f& getImmVec() const;
Instruction(athena::io::IStreamReader& reader) {read(reader);} static atInt8 swizzleCompIdx(char aChar);
}; int addInstruction(const IRNode& n, IR::RegID target);
atUint64 m_hash = 0;
atUint16 m_regCount = 0;
std::vector<Instruction> m_instructions;
boo::BlendFactor m_blendSrc = boo::BlendFactor::One;
boo::BlendFactor m_blendDst = boo::BlendFactor::Zero;
bool m_doAlpha = false;
static atInt8 swizzleCompIdx(char aChar);
int addInstruction(const IRNode& n, IR::RegID target);
}; };
class Frontend class Frontend {
{ Diagnostics m_diag;
Diagnostics m_diag; Parser m_parser;
Parser m_parser;
public: public:
IR compileSource(std::string_view source, std::string_view diagName); IR compileSource(std::string_view source, std::string_view diagName);
Diagnostics& getDiagnostics() { return m_diag; } Diagnostics& getDiagnostics() { return m_diag; }
Frontend() : m_parser(m_diag) {} Frontend() : m_parser(m_diag) {}
}; };
} } // namespace hecl::Frontend

View File

@ -3,30 +3,26 @@
#include "hecl/hecl.hpp" #include "hecl/hecl.hpp"
#include "athena/DNA.hpp" #include "athena/DNA.hpp"
namespace hecl namespace hecl {
{
enum class HMDLTopology : atUint32 enum class HMDLTopology : atUint32 {
{ Triangles,
Triangles, TriStrips,
TriStrips,
}; };
#define HECL_HMDL_META_SZ 32 #define HECL_HMDL_META_SZ 32
struct HMDLMeta : athena::io::DNA<athena::Big> struct HMDLMeta : athena::io::DNA<athena::Big> {
{ AT_DECL_DNA
AT_DECL_DNA Value<atUint32> magic = 'TACO';
Value<atUint32> magic = 'TACO'; Value<HMDLTopology> topology;
Value<HMDLTopology> topology; Value<atUint32> vertStride;
Value<atUint32> vertStride; Value<atUint32> vertCount;
Value<atUint32> vertCount; Value<atUint32> indexCount;
Value<atUint32> indexCount; Value<atUint32> colorCount;
Value<atUint32> colorCount; Value<atUint32> uvCount;
Value<atUint32> uvCount; Value<atUint16> weightCount;
Value<atUint16> weightCount; Value<atUint16> bankCount;
Value<atUint16> bankCount;
}; };
} } // namespace hecl

View File

@ -17,20 +17,18 @@
/// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't /// \brief Extend the default __GNUC_PREREQ even if glibc's features.h isn't
/// available. /// available.
#ifndef LLVM_GNUC_PREREQ #ifndef LLVM_GNUC_PREREQ
# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
# define LLVM_GNUC_PREREQ(maj, min, patch) \ #define LLVM_GNUC_PREREQ(maj, min, patch) \
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \ ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= ((maj) << 20) + ((min) << 10) + (patch))
((maj) << 20) + ((min) << 10) + (patch)) #elif defined(__GNUC__) && defined(__GNUC_MINOR__)
# elif defined(__GNUC__) && defined(__GNUC_MINOR__) #define LLVM_GNUC_PREREQ(maj, min, patch) ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
# define LLVM_GNUC_PREREQ(maj, min, patch) \ #else
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10)) #define LLVM_GNUC_PREREQ(maj, min, patch) 0
# else #endif
# define LLVM_GNUC_PREREQ(maj, min, patch) 0
# endif
#endif #endif
#ifndef __has_builtin #ifndef __has_builtin
# define __has_builtin(x) 0 #define __has_builtin(x) 0
#endif #endif
#include "hecl.hpp" #include "hecl.hpp"
@ -61,7 +59,8 @@ enum ZeroBehavior {
}; };
namespace detail { namespace detail {
template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter { template <typename T, std::size_t SizeOfT>
struct TrailingZerosCounter {
static std::size_t count(T Val, ZeroBehavior) { static std::size_t count(T Val, ZeroBehavior) {
if (!Val) if (!Val)
return std::numeric_limits<T>::digits; return std::numeric_limits<T>::digits;
@ -85,7 +84,8 @@ template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
}; };
#if __GNUC__ >= 4 || defined(_MSC_VER) #if __GNUC__ >= 4 || defined(_MSC_VER)
template <typename T> struct TrailingZerosCounter<T, 4> { template <typename T>
struct TrailingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) { static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0) if (ZB != ZB_Undefined && Val == 0)
return 32; return 32;
@ -101,7 +101,8 @@ template <typename T> struct TrailingZerosCounter<T, 4> {
}; };
#if !defined(_MSC_VER) || defined(_M_X64) #if !defined(_MSC_VER) || defined(_M_X64)
template <typename T> struct TrailingZerosCounter<T, 8> { template <typename T>
struct TrailingZerosCounter<T, 8> {
static std::size_t count(T Val, ZeroBehavior ZB) { static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0) if (ZB != ZB_Undefined && Val == 0)
return 64; return 64;
@ -128,14 +129,14 @@ template <typename T> struct TrailingZerosCounter<T, 8> {
/// valid arguments. /// valid arguments.
template <typename T> template <typename T>
std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) { std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
!std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed."); "Only unsigned integral types are allowed.");
return detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB); return detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
} }
namespace detail { namespace detail {
template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter { template <typename T, std::size_t SizeOfT>
struct LeadingZerosCounter {
static std::size_t count(T Val, ZeroBehavior) { static std::size_t count(T Val, ZeroBehavior) {
if (!Val) if (!Val)
return std::numeric_limits<T>::digits; return std::numeric_limits<T>::digits;
@ -154,7 +155,8 @@ template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
}; };
#if __GNUC__ >= 4 || defined(_MSC_VER) #if __GNUC__ >= 4 || defined(_MSC_VER)
template <typename T> struct LeadingZerosCounter<T, 4> { template <typename T>
struct LeadingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) { static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0) if (ZB != ZB_Undefined && Val == 0)
return 32; return 32;
@ -170,7 +172,8 @@ template <typename T> struct LeadingZerosCounter<T, 4> {
}; };
#if !defined(_MSC_VER) || defined(_M_X64) #if !defined(_MSC_VER) || defined(_M_X64)
template <typename T> struct LeadingZerosCounter<T, 8> { template <typename T>
struct LeadingZerosCounter<T, 8> {
static std::size_t count(T Val, ZeroBehavior ZB) { static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0) if (ZB != ZB_Undefined && Val == 0)
return 64; return 64;
@ -197,8 +200,7 @@ template <typename T> struct LeadingZerosCounter<T, 8> {
/// valid arguments. /// valid arguments.
template <typename T> template <typename T>
std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
!std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed."); "Only unsigned integral types are allowed.");
return detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB); return detail::LeadingZerosCounter<T, sizeof(T)>::count(Val, ZB);
} }
@ -210,7 +212,8 @@ std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
/// ///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are /// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments. /// valid arguments.
template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) { template <typename T>
T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0) if (ZB == ZB_Max && Val == 0)
return std::numeric_limits<T>::max(); return std::numeric_limits<T>::max();
@ -224,14 +227,14 @@ template <typename T> T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) {
/// ///
/// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are /// \param ZB the behavior on an input of 0. Only ZB_Max and ZB_Undefined are
/// valid arguments. /// valid arguments.
template <typename T> T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) { template <typename T>
T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) {
if (ZB == ZB_Max && Val == 0) if (ZB == ZB_Max && Val == 0)
return std::numeric_limits<T>::max(); return std::numeric_limits<T>::max();
// Use ^ instead of - because both gcc and llvm can remove the associated ^ // Use ^ instead of - because both gcc and llvm can remove the associated ^
// in the __builtin_clz intrinsic on x86. // in the __builtin_clz intrinsic on x86.
return countLeadingZeros(Val, ZB_Undefined) ^ return countLeadingZeros(Val, ZB_Undefined) ^ (std::numeric_limits<T>::digits - 1);
(std::numeric_limits<T>::digits - 1);
} }
/// \brief Macro compressed bit reversal table for 256 bits. /// \brief Macro compressed bit reversal table for 256 bits.
@ -241,7 +244,7 @@ static const unsigned char BitReverseTable256[256] = {
#define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64 #define R2(n) n, n + 2 * 64, n + 1 * 64, n + 3 * 64
#define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16) #define R4(n) R2(n), R2(n + 2 * 16), R2(n + 1 * 16), R2(n + 3 * 16)
#define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4) #define R6(n) R4(n), R4(n + 2 * 4), R4(n + 1 * 4), R4(n + 3 * 4)
R6(0), R6(2), R6(1), R6(3) R6(0), R6(2), R6(1), R6(3)
#undef R2 #undef R2
#undef R4 #undef R4
#undef R6 #undef R6
@ -264,33 +267,31 @@ T reverseBits(T Val) {
// ambiguity. // ambiguity.
/// Hi_32 - This function returns the high 32 bits of a 64 bit value. /// Hi_32 - This function returns the high 32 bits of a 64 bit value.
constexpr inline uint32_t Hi_32(uint64_t Value) { constexpr inline uint32_t Hi_32(uint64_t Value) { return static_cast<uint32_t>(Value >> 32); }
return static_cast<uint32_t>(Value >> 32);
}
/// Lo_32 - This function returns the low 32 bits of a 64 bit value. /// Lo_32 - This function returns the low 32 bits of a 64 bit value.
constexpr inline uint32_t Lo_32(uint64_t Value) { constexpr inline uint32_t Lo_32(uint64_t Value) { return static_cast<uint32_t>(Value); }
return static_cast<uint32_t>(Value);
}
/// Make_64 - This functions makes a 64-bit integer from a high / low pair of /// Make_64 - This functions makes a 64-bit integer from a high / low pair of
/// 32-bit integers. /// 32-bit integers.
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { return ((uint64_t)High << 32) | (uint64_t)Low; }
return ((uint64_t)High << 32) | (uint64_t)Low;
}
/// isInt - Checks if an integer fits into the given bit width. /// isInt - Checks if an integer fits into the given bit width.
template <unsigned N> constexpr inline bool isInt(int64_t x) { template <unsigned N>
return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1))); constexpr inline bool isInt(int64_t x) {
return N >= 64 || (-(INT64_C(1) << (N - 1)) <= x && x < (INT64_C(1) << (N - 1)));
} }
// Template specializations to get better code for common cases. // Template specializations to get better code for common cases.
template <> constexpr inline bool isInt<8>(int64_t x) { template <>
constexpr inline bool isInt<8>(int64_t x) {
return static_cast<int8_t>(x) == x; return static_cast<int8_t>(x) == x;
} }
template <> constexpr inline bool isInt<16>(int64_t x) { template <>
constexpr inline bool isInt<16>(int64_t x) {
return static_cast<int16_t>(x) == x; return static_cast<int16_t>(x) == x;
} }
template <> constexpr inline bool isInt<32>(int64_t x) { template <>
constexpr inline bool isInt<32>(int64_t x) {
return static_cast<int32_t>(x) == x; return static_cast<int32_t>(x) == x;
} }
@ -298,8 +299,7 @@ template <> constexpr inline bool isInt<32>(int64_t x) {
/// left by S. /// left by S.
template <unsigned N, unsigned S> template <unsigned N, unsigned S>
constexpr inline bool isShiftedInt(int64_t x) { constexpr inline bool isShiftedInt(int64_t x) {
static_assert( static_assert(N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide."); static_assert(N + S <= 64, "isShiftedInt<N, S> with N + S > 64 is too wide.");
return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0); return isInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
} }
@ -313,35 +313,34 @@ constexpr inline bool isShiftedInt(int64_t x) {
/// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting /// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting
/// left too many places. /// left too many places.
template <unsigned N> template <unsigned N>
constexpr inline typename std::enable_if<(N < 64), bool>::type constexpr inline typename std::enable_if<(N < 64), bool>::type isUInt(uint64_t X) {
isUInt(uint64_t X) {
static_assert(N > 0, "isUInt<0> doesn't make sense"); static_assert(N > 0, "isUInt<0> doesn't make sense");
return X < (UINT64_C(1) << (N)); return X < (UINT64_C(1) << (N));
} }
template <unsigned N> template <unsigned N>
constexpr inline typename std::enable_if<N >= 64, bool>::type constexpr inline typename std::enable_if<N >= 64, bool>::type isUInt(uint64_t X) {
isUInt(uint64_t X) {
return true; return true;
} }
// Template specializations to get better code for common cases. // Template specializations to get better code for common cases.
template <> constexpr inline bool isUInt<8>(uint64_t x) { template <>
constexpr inline bool isUInt<8>(uint64_t x) {
return static_cast<uint8_t>(x) == x; return static_cast<uint8_t>(x) == x;
} }
template <> constexpr inline bool isUInt<16>(uint64_t x) { template <>
constexpr inline bool isUInt<16>(uint64_t x) {
return static_cast<uint16_t>(x) == x; return static_cast<uint16_t>(x) == x;
} }
template <> constexpr inline bool isUInt<32>(uint64_t x) { template <>
constexpr inline bool isUInt<32>(uint64_t x) {
return static_cast<uint32_t>(x) == x; return static_cast<uint32_t>(x) == x;
} }
/// Checks if a unsigned integer is an N bit number shifted left by S. /// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S> template <unsigned N, unsigned S>
constexpr inline bool isShiftedUInt(uint64_t x) { constexpr inline bool isShiftedUInt(uint64_t x) {
static_assert( static_assert(N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)");
N > 0, "isShiftedUInt<0> doesn't make sense (refers to a 0-bit number)"); static_assert(N + S <= 64, "isShiftedUInt<N, S> with N + S > 64 is too wide.");
static_assert(N + S <= 64,
"isShiftedUInt<N, S> with N + S > 64 is too wide.");
// Per the two static_asserts above, S must be strictly less than 64. So // Per the two static_asserts above, S must be strictly less than 64. So
// 1 << S is not undefined behavior. // 1 << S is not undefined behavior.
return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0); return isUInt<N + S>(x) && (x % (UINT64_C(1) << S) == 0);
@ -362,7 +361,7 @@ inline uint64_t maxUIntN(uint64_t N) {
inline int64_t minIntN(int64_t N) { inline int64_t minIntN(int64_t N) {
assert(N > 0 && N <= 64 && "integer width out of range"); assert(N > 0 && N <= 64 && "integer width out of range");
return -(UINT64_C(1)<<(N-1)); return -(UINT64_C(1) << (N - 1));
} }
/// Gets the maximum value for a N-bit signed integer. /// Gets the maximum value for a N-bit signed integer.
@ -376,72 +375,50 @@ inline int64_t maxIntN(int64_t N) {
/// isUIntN - Checks if an unsigned integer fits into the given (dynamic) /// isUIntN - Checks if an unsigned integer fits into the given (dynamic)
/// bit width. /// bit width.
inline bool isUIntN(unsigned N, uint64_t x) { inline bool isUIntN(unsigned N, uint64_t x) { return N >= 64 || x <= maxUIntN(N); }
return N >= 64 || x <= maxUIntN(N);
}
/// isIntN - Checks if an signed integer fits into the given (dynamic) /// isIntN - Checks if an signed integer fits into the given (dynamic)
/// bit width. /// bit width.
inline bool isIntN(unsigned N, int64_t x) { inline bool isIntN(unsigned N, int64_t x) { return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); }
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
}
/// isMask_32 - This function returns true if the argument is a non-empty /// isMask_32 - This function returns true if the argument is a non-empty
/// sequence of ones starting at the least significant bit with the remainder /// sequence of ones starting at the least significant bit with the remainder
/// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true. /// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true.
constexpr inline bool isMask_32(uint32_t Value) { constexpr inline bool isMask_32(uint32_t Value) { return Value && ((Value + 1) & Value) == 0; }
return Value && ((Value + 1) & Value) == 0;
}
/// isMask_64 - This function returns true if the argument is a non-empty /// isMask_64 - This function returns true if the argument is a non-empty
/// sequence of ones starting at the least significant bit with the remainder /// sequence of ones starting at the least significant bit with the remainder
/// zero (64 bit version). /// zero (64 bit version).
constexpr inline bool isMask_64(uint64_t Value) { constexpr inline bool isMask_64(uint64_t Value) { return Value && ((Value + 1) & Value) == 0; }
return Value && ((Value + 1) & Value) == 0;
}
/// isShiftedMask_32 - This function returns true if the argument contains a /// isShiftedMask_32 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (32 bit version.) /// non-empty sequence of ones with the remainder zero (32 bit version.)
/// Ex. isShiftedMask_32(0x0000FF00U) == true. /// Ex. isShiftedMask_32(0x0000FF00U) == true.
constexpr inline bool isShiftedMask_32(uint32_t Value) { constexpr inline bool isShiftedMask_32(uint32_t Value) { return Value && isMask_32((Value - 1) | Value); }
return Value && isMask_32((Value - 1) | Value);
}
/// isShiftedMask_64 - This function returns true if the argument contains a /// isShiftedMask_64 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (64 bit version.) /// non-empty sequence of ones with the remainder zero (64 bit version.)
constexpr inline bool isShiftedMask_64(uint64_t Value) { constexpr inline bool isShiftedMask_64(uint64_t Value) { return Value && isMask_64((Value - 1) | Value); }
return Value && isMask_64((Value - 1) | Value);
}
/// isPowerOf2_32 - This function returns true if the argument is a power of /// isPowerOf2_32 - This function returns true if the argument is a power of
/// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.) /// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr inline bool isPowerOf2_32(uint32_t Value) { constexpr inline bool isPowerOf2_32(uint32_t Value) { return Value && !(Value & (Value - 1)); }
return Value && !(Value & (Value - 1));
}
/// isPowerOf2_64 - This function returns true if the argument is a power of two /// isPowerOf2_64 - This function returns true if the argument is a power of two
/// > 0 (64 bit edition.) /// > 0 (64 bit edition.)
constexpr inline bool isPowerOf2_64(uint64_t Value) { constexpr inline bool isPowerOf2_64(uint64_t Value) { return Value && !(Value & (Value - int64_t(1L))); }
return Value && !(Value & (Value - int64_t(1L)));
}
/// ByteSwap_16 - This function returns a byte-swapped representation of the /// ByteSwap_16 - This function returns a byte-swapped representation of the
/// 16-bit argument, Value. /// 16-bit argument, Value.
inline uint16_t ByteSwap_16(uint16_t Value) { inline uint16_t ByteSwap_16(uint16_t Value) { return hecl::bswap16(Value); }
return hecl::bswap16(Value);
}
/// ByteSwap_32 - This function returns a byte-swapped representation of the /// ByteSwap_32 - This function returns a byte-swapped representation of the
/// 32-bit argument, Value. /// 32-bit argument, Value.
inline uint32_t ByteSwap_32(uint32_t Value) { inline uint32_t ByteSwap_32(uint32_t Value) { return hecl::bswap32(Value); }
return hecl::bswap32(Value);
}
/// ByteSwap_64 - This function returns a byte-swapped representation of the /// ByteSwap_64 - This function returns a byte-swapped representation of the
/// 64-bit argument, Value. /// 64-bit argument, Value.
inline uint64_t ByteSwap_64(uint64_t Value) { inline uint64_t ByteSwap_64(uint64_t Value) { return hecl::bswap64(Value); }
return hecl::bswap64(Value);
}
/// \brief Count the number of ones from the most significant bit to the first /// \brief Count the number of ones from the most significant bit to the first
/// zero bit. /// zero bit.
@ -453,8 +430,7 @@ inline uint64_t ByteSwap_64(uint64_t Value) {
/// ZB_Undefined are valid arguments. /// ZB_Undefined are valid arguments.
template <typename T> template <typename T>
std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) { std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
!std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed."); "Only unsigned integral types are allowed.");
return countLeadingZeros(~Value, ZB); return countLeadingZeros(~Value, ZB);
} }
@ -469,14 +445,14 @@ std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
/// ZB_Undefined are valid arguments. /// ZB_Undefined are valid arguments.
template <typename T> template <typename T>
std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) { std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer && static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
!std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed."); "Only unsigned integral types are allowed.");
return countTrailingZeros(~Value, ZB); return countTrailingZeros(~Value, ZB);
} }
namespace detail { namespace detail {
template <typename T, std::size_t SizeOfT> struct PopulationCounter { template <typename T, std::size_t SizeOfT>
struct PopulationCounter {
static unsigned count(T Value) { static unsigned count(T Value) {
// Generic version, forward to 32 bits. // Generic version, forward to 32 bits.
static_assert(SizeOfT <= 4, "Not implemented!"); static_assert(SizeOfT <= 4, "Not implemented!");
@ -491,7 +467,8 @@ template <typename T, std::size_t SizeOfT> struct PopulationCounter {
} }
}; };
template <typename T> struct PopulationCounter<T, 8> { template <typename T>
struct PopulationCounter<T, 8> {
static unsigned count(T Value) { static unsigned count(T Value) {
#if __GNUC__ >= 4 #if __GNUC__ >= 4
return __builtin_popcountll(Value); return __builtin_popcountll(Value);
@ -511,8 +488,7 @@ template <typename T> struct PopulationCounter<T, 8> {
/// Returns 0 if the word is zero. /// Returns 0 if the word is zero.
template <typename T> template <typename T>
inline unsigned countPopulation(T Value) { inline unsigned countPopulation(T Value) {
static_assert(std::numeric_limits<T>::is_integer && static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
!std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed."); "Only unsigned integral types are allowed.");
return detail::PopulationCounter<T, sizeof(T)>::count(Value); return detail::PopulationCounter<T, sizeof(T)>::count(Value);
} }
@ -529,28 +505,20 @@ inline double Log2(double Value) {
/// Log2_32 - This function returns the floor log base 2 of the specified value, /// Log2_32 - This function returns the floor log base 2 of the specified value,
/// -1 if the value is zero. (32 bit edition.) /// -1 if the value is zero. (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2 /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) { inline unsigned Log2_32(uint32_t Value) { return 31 - countLeadingZeros(Value); }
return 31 - countLeadingZeros(Value);
}
/// Log2_64 - This function returns the floor log base 2 of the specified value, /// Log2_64 - This function returns the floor log base 2 of the specified value,
/// -1 if the value is zero. (64 bit edition.) /// -1 if the value is zero. (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) { inline unsigned Log2_64(uint64_t Value) { return 63 - countLeadingZeros(Value); }
return 63 - countLeadingZeros(Value);
}
/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified /// Log2_32_Ceil - This function returns the ceil log base 2 of the specified
/// value, 32 if the value is zero. (32 bit edition). /// value, 32 if the value is zero. (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3 /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) { inline unsigned Log2_32_Ceil(uint32_t Value) { return 32 - countLeadingZeros(Value - 1); }
return 32 - countLeadingZeros(Value - 1);
}
/// Log2_64_Ceil - This function returns the ceil log base 2 of the specified /// Log2_64_Ceil - This function returns the ceil log base 2 of the specified
/// value, 64 if the value is zero. (64 bit edition.) /// value, 64 if the value is zero. (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) { inline unsigned Log2_64_Ceil(uint64_t Value) { return 64 - countLeadingZeros(Value - 1); }
return 64 - countLeadingZeros(Value - 1);
}
/// GreatestCommonDivisor64 - Return the greatest common divisor of the two /// GreatestCommonDivisor64 - Return the greatest common divisor of the two
/// values using Euclid's algorithm. /// values using Euclid's algorithm.
@ -626,9 +594,8 @@ constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) {
/// ///
/// Alignment should be a power of two. This method rounds up, so /// Alignment should be a power of two. This method rounds up, so
/// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8. /// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8.
inline uintptr_t alignAddr(const void *Addr, size_t Alignment) { inline uintptr_t alignAddr(const void* Addr, size_t Alignment) {
assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && "Alignment is not a power of two!");
"Alignment is not a power of two!");
assert((uintptr_t)Addr + Alignment - 1 >= (uintptr_t)Addr); assert((uintptr_t)Addr + Alignment - 1 >= (uintptr_t)Addr);
@ -637,7 +604,7 @@ inline uintptr_t alignAddr(const void *Addr, size_t Alignment) {
/// \brief Returns the necessary adjustment for aligning \c Ptr to \c Alignment /// \brief Returns the necessary adjustment for aligning \c Ptr to \c Alignment
/// bytes, rounding up. /// bytes, rounding up.
inline size_t alignmentAdjustment(const void *Ptr, size_t Alignment) { inline size_t alignmentAdjustment(const void* Ptr, size_t Alignment) {
return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr; return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr;
} }
@ -656,7 +623,8 @@ inline uint64_t NextPowerOf2(uint64_t A) {
/// Returns the power of two which is less than or equal to the given value. /// Returns the power of two which is less than or equal to the given value.
/// Essentially, it is a floor operation across the domain of powers of two. /// Essentially, it is a floor operation across the domain of powers of two.
inline uint64_t PowerOf2Floor(uint64_t A) { inline uint64_t PowerOf2Floor(uint64_t A) {
if (!A) return 0; if (!A)
return 0;
return 1ull << (63 - countLeadingZeros(A, ZB_Undefined)); return 1ull << (63 - countLeadingZeros(A, ZB_Undefined));
} }
@ -696,7 +664,8 @@ inline uint64_t alignTo(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
/// Returns the next integer (mod 2**64) that is greater than or equal to /// Returns the next integer (mod 2**64) that is greater than or equal to
/// \p Value and is a multiple of \c Align. \c Align must be non-zero. /// \p Value and is a multiple of \c Align. \c Align must be non-zero.
template <uint64_t Align> constexpr inline uint64_t alignTo(uint64_t Value) { template <uint64_t Align>
constexpr inline uint64_t alignTo(uint64_t Value) {
static_assert(Align != 0u, "Align must be non-zero"); static_assert(Align != 0u, "Align must be non-zero");
return (Value + Align - 1) / Align * Align; return (Value + Align - 1) / Align * Align;
} }
@ -725,13 +694,12 @@ inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) {
/// Returns the offset to the next integer (mod 2**64) that is greater than /// Returns the offset to the next integer (mod 2**64) that is greater than
/// or equal to \p Value and is a multiple of \p Align. \p Align must be /// or equal to \p Value and is a multiple of \p Align. \p Align must be
/// non-zero. /// non-zero.
inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { return alignTo(Value, Align) - Value; }
return alignTo(Value, Align) - Value;
}
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer. /// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires 0 < B <= 32. /// Requires 0 < B <= 32.
template <unsigned B> constexpr inline int32_t SignExtend32(uint32_t X) { template <unsigned B>
constexpr inline int32_t SignExtend32(uint32_t X) {
static_assert(B > 0, "Bit width can't be 0."); static_assert(B > 0, "Bit width can't be 0.");
static_assert(B <= 32, "Bit width out of range."); static_assert(B <= 32, "Bit width out of range.");
return int32_t(X << (32 - B)) >> (32 - B); return int32_t(X << (32 - B)) >> (32 - B);
@ -747,7 +715,8 @@ inline int32_t SignExtend32(uint32_t X, unsigned B) {
/// Sign-extend the number in the bottom B bits of X to a 64-bit integer. /// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires 0 < B < 64. /// Requires 0 < B < 64.
template <unsigned B> constexpr inline int64_t SignExtend64(uint64_t x) { template <unsigned B>
constexpr inline int64_t SignExtend64(uint64_t x) {
static_assert(B > 0, "Bit width can't be 0."); static_assert(B > 0, "Bit width can't be 0.");
static_assert(B <= 64, "Bit width out of range."); static_assert(B <= 64, "Bit width out of range.");
return int64_t(x << (64 - B)) >> (64 - B); return int64_t(x << (64 - B)) >> (64 - B);
@ -764,8 +733,7 @@ inline int64_t SignExtend64(uint64_t X, unsigned B) {
/// Subtract two unsigned integers, X and Y, of type T and return the absolute /// Subtract two unsigned integers, X and Y, of type T and return the absolute
/// value of the result. /// value of the result.
template <typename T> template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type typename std::enable_if<std::is_unsigned<T>::value, T>::type AbsoluteDifference(T X, T Y) {
AbsoluteDifference(T X, T Y) {
return std::max(X, Y) - std::min(X, Y); return std::max(X, Y) - std::min(X, Y);
} }
@ -773,10 +741,9 @@ AbsoluteDifference(T X, T Y) {
/// maximum representable value of T on overflow. ResultOverflowed indicates if /// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T. /// the result is larger than the maximum representable value of type T.
template <typename T> template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingAdd(T X, T Y, bool* ResultOverflowed = nullptr) {
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy; bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29 // Hacker's Delight, p. 29
T Z = X + Y; T Z = X + Y;
Overflowed = (Z < X || Z < Y); Overflowed = (Z < X || Z < Y);
@ -790,10 +757,10 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
/// maximum representable value of T on overflow. ResultOverflowed indicates if /// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T. /// the result is larger than the maximum representable value of type T.
template <typename T> template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiply(T X, T Y,
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { bool* ResultOverflowed = nullptr) {
bool Dummy; bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 30 has a different algorithm, but we don't use that // Hacker's Delight, p. 30 has a different algorithm, but we don't use that
// because it fails for uint16_t (where multiplication can have undefined // because it fails for uint16_t (where multiplication can have undefined
@ -836,10 +803,10 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
/// overflow. ResultOverflowed indicates if the result is larger than the /// overflow. ResultOverflowed indicates if the result is larger than the
/// maximum representable value of type T. /// maximum representable value of type T.
template <typename T> template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiplyAdd(T X, T Y, T A,
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) { bool* ResultOverflowed = nullptr) {
bool Dummy; bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
T Product = SaturatingMultiply(X, Y, &Overflowed); T Product = SaturatingMultiply(X, Y, &Overflowed);
if (Overflowed) if (Overflowed)
@ -850,6 +817,5 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC. /// Use this rather than HUGE_VALF; the latter causes warnings on MSVC.
extern const float huge_valf; extern const float huge_valf;
} // End llvm namespace } // namespace llvm
} // End hecl namespace } // namespace hecl

View File

@ -3,57 +3,53 @@
#include "hecl.hpp" #include "hecl.hpp"
#include <thread> #include <thread>
namespace hecl namespace hecl {
{
class MultiProgressPrinter class MultiProgressPrinter {
{ std::thread m_logThread;
std::thread m_logThread; mutable std::mutex m_logLock;
mutable std::mutex m_logLock; bool m_newLineAfter;
bool m_newLineAfter;
struct TermInfo struct TermInfo {
{
#if _WIN32 #if _WIN32
HANDLE console; HANDLE console;
#endif #endif
int width; int width;
bool xtermColor = false; bool xtermColor = false;
bool truncate = false; bool truncate = false;
} m_termInfo; } m_termInfo;
struct ThreadStat struct ThreadStat {
{ hecl::SystemString m_message, m_submessage;
hecl::SystemString m_message, m_submessage; float m_factor = 0.f;
float m_factor = 0.f; bool m_active = false;
bool m_active = false; void print(const TermInfo& tinfo) const;
void print(const TermInfo& tinfo) const; };
}; mutable std::vector<ThreadStat> m_threadStats;
mutable std::vector<ThreadStat> m_threadStats;
mutable float m_mainFactor = -1.f;
mutable int m_indeterminateCounter = 0;
mutable int m_curThreadLines = 0;
mutable int m_curProgLines = 0;
mutable int m_latestThread = -1;
mutable bool m_running = false;
mutable bool m_dirty = false;
mutable bool m_mainIndeterminate = false;
uint64_t m_lastLogCounter = 0;
void LogProc();
void DoPrint();
void DrawIndeterminateBar();
void MoveCursorUp(int n);
mutable float m_mainFactor = -1.f;
mutable int m_indeterminateCounter = 0;
mutable int m_curThreadLines = 0;
mutable int m_curProgLines = 0;
mutable int m_latestThread = -1;
mutable bool m_running = false;
mutable bool m_dirty = false;
mutable bool m_mainIndeterminate = false;
uint64_t m_lastLogCounter = 0;
void LogProc();
void DoPrint();
void DrawIndeterminateBar();
void MoveCursorUp(int n);
public: public:
MultiProgressPrinter(bool activate = false); MultiProgressPrinter(bool activate = false);
~MultiProgressPrinter(); ~MultiProgressPrinter();
void print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, void print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, float factor = -1.f,
float factor = -1.f, int threadIdx = 0) const; int threadIdx = 0) const;
void setMainFactor(float factor) const; void setMainFactor(float factor) const;
void setMainIndeterminate(bool indeterminate) const; void setMainIndeterminate(bool indeterminate) const;
void startNewLine() const; void startNewLine() const;
void flush() const; void flush() const;
}; };
} } // namespace hecl

View File

@ -11,241 +11,208 @@
/* CMake-curated rep classes for the application */ /* CMake-curated rep classes for the application */
#include "ApplicationReps.hpp" #include "ApplicationReps.hpp"
namespace hecl namespace hecl {
{
#if HECL_RUNTIME #if HECL_RUNTIME
template<typename P, typename S> template <typename P, typename S>
class StageRuntimeObject : public StageRep<P, S> class StageRuntimeObject : public StageRep<P, S> {
{ boo::ObjToken<boo::IShaderStage> m_stage;
boo::ObjToken<boo::IShaderStage> m_stage;
public: public:
static constexpr StageTargetType TargetType = StageTargetType::Runtime; static constexpr StageTargetType TargetType = StageTargetType::Runtime;
static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageRuntimeCollection; static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageRuntimeCollection;
static constexpr bool HasHash = false; static constexpr bool HasHash = false;
StageRuntimeObject() = default; StageRuntimeObject() = default;
StageRuntimeObject(StageConverter<P, S>& conv, FactoryCtx& ctx, const StageBinary<P, S>& in) StageRuntimeObject(StageConverter<P, S>& conv, FactoryCtx& ctx, const StageBinary<P, S>& in) {
{ m_stage = static_cast<typename P::Context&>(ctx).newShaderStage(in.data(), in.size(), S::Enum);
m_stage = static_cast<typename P::Context&>(ctx).newShaderStage(in.data(), in.size(), S::Enum); }
} boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; }
boo::ObjToken<boo::IShaderStage> stage() const { return m_stage; }
}; };
#endif #endif
class HECLIR : public PipelineRep<PlatformType::Null> class HECLIR : public PipelineRep<PlatformType::Null> {
{ const hecl::Backend::IR& m_ir;
const hecl::Backend::IR& m_ir; const hecl::Backend::ShaderTag& m_tag;
const hecl::Backend::ShaderTag& m_tag; const hecl::Backend::ExtensionSlot& m_extension;
const hecl::Backend::ExtensionSlot& m_extension; uint64_t m_hash;
uint64_t m_hash;
public:
HECLIR(const hecl::Backend::IR& ir, const hecl::Backend::ShaderTag& tag,
const hecl::Backend::ExtensionSlot& extension)
: m_ir(ir), m_tag(tag), m_extension(extension)
{
m_hash = tag.val64();
m_hash ^= extension.hash();
}
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
const hecl::Backend::IR& ir() const { return m_ir; } public:
const hecl::Backend::ShaderTag& tag() const { return m_tag; } HECLIR(const hecl::Backend::IR& ir, const hecl::Backend::ShaderTag& tag,
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; } const hecl::Backend::ExtensionSlot& extension)
: m_ir(ir), m_tag(tag), m_extension(extension) {
m_hash = tag.val64();
m_hash ^= extension.hash();
}
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
const hecl::Backend::IR& ir() const { return m_ir; }
const hecl::Backend::ShaderTag& tag() const { return m_tag; }
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
}; };
template<typename P, class BackendTp> template <typename P, class BackendTp>
class HECLBackendImpl : public PipelineRep<P> class HECLBackendImpl : public PipelineRep<P> {
{ hecl::Backend::ShaderTag m_tag;
hecl::Backend::ShaderTag m_tag; BackendTp m_backend;
BackendTp m_backend; const hecl::Backend::ExtensionSlot& m_extension;
const hecl::Backend::ExtensionSlot& m_extension;
public: public:
static constexpr bool HasHash = false; static constexpr bool HasHash = false;
HECLBackendImpl(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLIR& in) HECLBackendImpl(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLIR& in)
: m_tag(in.tag()), m_extension(in.extension()) : m_tag(in.tag()), m_extension(in.extension()) {
{ hecl::Backend::Diagnostics diag;
hecl::Backend::Diagnostics diag; m_backend.reset(in.ir(), diag);
m_backend.reset(in.ir(), diag); }
} std::string makeVert() const {
std::string makeVert() const return m_backend.makeVert(m_tag.getColorCount(), m_tag.getUvCount(), m_tag.getWeightCount(),
{ m_tag.getSkinSlotCount(), m_extension.texCount, m_extension.texs,
return m_backend.makeVert( m_tag.getReflectionType());
m_tag.getColorCount(), m_tag.getUvCount(), m_tag.getWeightCount(), }
m_tag.getSkinSlotCount(), m_extension.texCount, std::string makeFrag() const {
m_extension.texs, m_tag.getReflectionType()); return m_backend.makeFrag(m_extension.blockCount, m_extension.blockNames,
} m_tag.getAlphaTest() || m_extension.forceAlphaTest, m_tag.getReflectionType(),
std::string makeFrag() const m_backend.m_blendSrc, m_backend.m_blendDst, m_extension.lighting, m_extension.post,
{ m_extension.texCount, m_extension.texs);
return m_backend.makeFrag(m_extension.blockCount, m_extension.blockNames, }
m_tag.getAlphaTest() || m_extension.forceAlphaTest, m_tag.getReflectionType(), const hecl::Backend::ShaderTag& getTag() const { return m_tag; }
m_backend.m_blendSrc, m_backend.m_blendDst, const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
m_extension.lighting, m_extension.post, std::pair<hecl::Backend::BlendFactor, hecl::Backend::BlendFactor> blendFactors() const {
m_extension.texCount, m_extension.texs); return {m_backend.m_blendSrc, m_backend.m_blendDst};
} }
const hecl::Backend::ShaderTag& getTag() const { return m_tag; }
const hecl::Backend::ExtensionSlot& extension() const { return m_extension; }
std::pair<hecl::Backend::BlendFactor, hecl::Backend::BlendFactor> blendFactors() const
{ return {m_backend.m_blendSrc, m_backend.m_blendDst}; }
}; };
template<typename P> template <typename P>
class HECLBackend : public PipelineRep<P> class HECLBackend : public PipelineRep<P> {
{
public: public:
static constexpr bool HasHash = false; static constexpr bool HasHash = false;
}; };
template<> template <>
class HECLBackend<PlatformType::OpenGL> : public HECLBackendImpl<PlatformType::OpenGL, hecl::Backend::GLSL> class HECLBackend<PlatformType::OpenGL> : public HECLBackendImpl<PlatformType::OpenGL, hecl::Backend::GLSL> {
{
public: public:
using HECLBackendImpl::HECLBackendImpl; using HECLBackendImpl::HECLBackendImpl;
}; };
template<> template <>
class HECLBackend<PlatformType::Vulkan> : public HECLBackendImpl<PlatformType::Vulkan, hecl::Backend::GLSL> class HECLBackend<PlatformType::Vulkan> : public HECLBackendImpl<PlatformType::Vulkan, hecl::Backend::GLSL> {
{
public: public:
using HECLBackendImpl::HECLBackendImpl; using HECLBackendImpl::HECLBackendImpl;
}; };
template<> template <>
class HECLBackend<PlatformType::D3D11> : public HECLBackendImpl<PlatformType::D3D11, hecl::Backend::HLSL> class HECLBackend<PlatformType::D3D11> : public HECLBackendImpl<PlatformType::D3D11, hecl::Backend::HLSL> {
{
public: public:
using HECLBackendImpl::HECLBackendImpl; using HECLBackendImpl::HECLBackendImpl;
}; };
template<> template <>
class HECLBackend<PlatformType::Metal> : public HECLBackendImpl<PlatformType::Metal, hecl::Backend::Metal> class HECLBackend<PlatformType::Metal> : public HECLBackendImpl<PlatformType::Metal, hecl::Backend::Metal> {
{
public: public:
using HECLBackendImpl::HECLBackendImpl; using HECLBackendImpl::HECLBackendImpl;
}; };
template<> template <>
class HECLBackend<PlatformType::NX> : public HECLBackendImpl<PlatformType::NX, hecl::Backend::GLSL> class HECLBackend<PlatformType::NX> : public HECLBackendImpl<PlatformType::NX, hecl::Backend::GLSL> {
{
public: public:
using HECLBackendImpl::HECLBackendImpl; using HECLBackendImpl::HECLBackendImpl;
}; };
template<template<typename, typename> class T, typename P, typename... Rest> template <template <typename, typename> class T, typename P, typename... Rest>
StageCollection<T<P, Rest...>>::StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in) StageCollection<T<P, Rest...>>::StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in) {
{ m_vertex = conv.getVertexConverter().convert(ctx, StageSourceText<P, PipelineStage::Vertex>(in.makeVert()));
m_vertex = conv.getVertexConverter().convert(ctx, StageSourceText<P, PipelineStage::Vertex>(in.makeVert())); m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText<P, PipelineStage::Fragment>(in.makeFrag()));
m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText<P, PipelineStage::Fragment>(in.makeFrag())); m_vtxFmtData = in.getTag().vertexFormat();
m_vtxFmtData = in.getTag().vertexFormat(); m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data());
m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data()); m_additionalInfo = in.getTag().additionalInfo(in.extension(), in.blendFactors());
m_additionalInfo = in.getTag().additionalInfo(in.extension(), in.blendFactors()); MakeHash();
MakeHash();
} }
#if HECL_RUNTIME #if HECL_RUNTIME
template<typename P> template <typename P>
class FinalPipeline : public PipelineRep<P> class FinalPipeline : public PipelineRep<P> {
{ boo::ObjToken<boo::IShaderPipeline> m_pipeline;
boo::ObjToken<boo::IShaderPipeline> m_pipeline;
public: public:
static constexpr PipelineTargetType TargetType = PipelineTargetType::FinalPipeline; static constexpr PipelineTargetType TargetType = PipelineTargetType::FinalPipeline;
static constexpr bool HasHash = false; static constexpr bool HasHash = false;
FinalPipeline(PipelineConverter<P>& conv, FactoryCtx& ctx, FinalPipeline(PipelineConverter<P>& conv, FactoryCtx& ctx,
const StageCollection<StageRuntimeObject<P, PipelineStage::Null>>& in) const StageCollection<StageRuntimeObject<P, PipelineStage::Null>>& in) {
{ m_pipeline = static_cast<typename P::Context&>(ctx).newShaderPipeline(
m_pipeline = static_cast<typename P::Context&>(ctx).newShaderPipeline( in.m_vertex.stage(), in.m_fragment.stage(), in.m_geometry.stage(), in.m_control.stage(),
in.m_vertex.stage(), in.m_fragment.stage(), in.m_geometry.stage(), in.m_evaluation.stage(), in.m_vtxFmt, in.m_additionalInfo);
in.m_control.stage(), in.m_evaluation.stage(), in.m_vtxFmt, in.m_additionalInfo); }
} boo::ObjToken<boo::IShaderPipeline> pipeline() const { return m_pipeline; }
boo::ObjToken<boo::IShaderPipeline> pipeline() const { return m_pipeline; }
}; };
#endif #endif
template<typename... Args> struct pack {}; template <typename... Args>
struct pack {};
struct null_t {}; struct null_t {};
template<typename P> struct ShaderDB {}; template <typename P>
struct ShaderDB {};
#define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>, #define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection<T<P, PipelineStage::Null>>,
#define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \ #define PIPELINE_RUNTIME_SPECIALIZATIONS(P) \
HECLBackend<P>, \ HECLBackend<P>, \
STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) \ STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P) STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \
STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, P) \ STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) FinalPipeline<P>,
STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, P) \ #define PIPELINE_OFFLINE_SPECIALIZATIONS(P) HECLBackend<P>, STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P)
FinalPipeline<P>, #define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \
#define PIPELINE_OFFLINE_SPECIALIZATIONS(P) \ StageBinary<P, S>, HECL_APPLICATION_STAGE_REPS(P, S) StageRuntimeObject<P, S>,
HECLBackend<P>, \ #define STAGE_OFFLINE_SPECIALIZATIONS(P, S) HECL_APPLICATION_STAGE_REPS(P, S)
STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, P)
#define STAGE_RUNTIME_SPECIALIZATIONS(P, S) \
StageBinary<P, S>, \
HECL_APPLICATION_STAGE_REPS(P, S) \
StageRuntimeObject<P, S>,
#define STAGE_OFFLINE_SPECIALIZATIONS(P, S) \
HECL_APPLICATION_STAGE_REPS(P, S)
#define SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, S) \ #define SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, S) \
template<> struct ShaderDB<P>::StageDB<S> \ template <> \
{ \ struct ShaderDB<P>::StageDB<S> { \
using StageTypes = pack< \ using StageTypes = pack<STAGE_RUNTIME_SPECIALIZATIONS(P, S) null_t>; \
STAGE_RUNTIME_SPECIALIZATIONS(P, S) \ };
null_t \ #define SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, S) \
>; \ template <> \
}; struct ShaderDB<P>::StageDB<S> { \
#define SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, S) \ using StageTypes = pack<null_t>; \
template<> struct ShaderDB<P>::StageDB<S> \ };
{ \ #define SPECIALIZE_OFFLINE_STAGE(P, S) \
using StageTypes = pack< \ template <> \
null_t \ struct ShaderDB<P>::StageDB<S> { \
>; \ using StageTypes = pack<STAGE_OFFLINE_SPECIALIZATIONS(P, S) null_t>; \
}; };
#define SPECIALIZE_OFFLINE_STAGE(P, S) \ #define SPECIALIZE_RUNTIME_AVAILABLE_PLATFORM(P) \
template<> struct ShaderDB<P>::StageDB<S> \ template <> \
{ \ struct ShaderDB<P> { \
using StageTypes = pack< \ using PipelineTypes = pack<PIPELINE_RUNTIME_SPECIALIZATIONS(P) null_t>; \
STAGE_OFFLINE_SPECIALIZATIONS(P, S) \ template <typename S> \
null_t \ struct StageDB {}; \
>; \ }; \
}; SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Vertex) \
#define SPECIALIZE_RUNTIME_AVAILABLE_PLATFORM(P) \ SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Fragment) \
template<> struct ShaderDB<P> \ SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Geometry) \
{ \ SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Control) \
using PipelineTypes = pack< \ SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Evaluation)
PIPELINE_RUNTIME_SPECIALIZATIONS(P) \ #define SPECIALIZE_RUNTIME_UNAVAILABLE_PLATFORM(P) \
null_t \ template <> \
>; \ struct ShaderDB<P> { \
template<typename S> struct StageDB {}; \ using PipelineTypes = pack<null_t>; \
}; \ template <typename S> \
SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Vertex) \ struct StageDB {}; \
SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Fragment) \ }; \
SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Geometry) \ SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Vertex) \
SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Control) \ SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Fragment) \
SPECIALIZE_RUNTIME_AVAILABLE_STAGE(P, hecl::PipelineStage::Evaluation) SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Geometry) \
#define SPECIALIZE_RUNTIME_UNAVAILABLE_PLATFORM(P) \ SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Control) \
template<> struct ShaderDB<P> \ SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Evaluation)
{ \ #define SPECIALIZE_OFFLINE_PLATFORM(P) \
using PipelineTypes = pack< \ template <> \
null_t \ struct ShaderDB<P> { \
>; \ using PipelineTypes = pack<PIPELINE_OFFLINE_SPECIALIZATIONS(P) null_t>; \
template<typename S> struct StageDB {}; \ template <typename S> \
}; \ struct StageDB {}; \
SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Vertex) \ }; \
SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Fragment) \ SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Vertex) \
SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Geometry) \ SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Fragment) \
SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Control) \ SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Geometry) \
SPECIALIZE_RUNTIME_UNAVAILABLE_STAGE(P, hecl::PipelineStage::Evaluation) SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Control) \
#define SPECIALIZE_OFFLINE_PLATFORM(P) \ SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Evaluation)
template<> struct ShaderDB<P> \
{ \
using PipelineTypes = pack< \
PIPELINE_OFFLINE_SPECIALIZATIONS(P) \
null_t \
>; \
template<typename S> struct StageDB {}; \
}; \
SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Vertex) \
SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Fragment) \
SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Geometry) \
SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Control) \
SPECIALIZE_OFFLINE_STAGE(P, hecl::PipelineStage::Evaluation)
#if HECL_RUNTIME #if HECL_RUNTIME
#if BOO_HAS_GL #if BOO_HAS_GL
@ -283,16 +250,15 @@ SPECIALIZE_OFFLINE_PLATFORM(hecl::PlatformType::NX)
class ShaderCacheZipStream; class ShaderCacheZipStream;
template<typename P, typename S> template <typename P, typename S>
class StageConverter class StageConverter {
{ friend class PipelineConverter<P>;
friend class PipelineConverter<P>;
#if HECL_RUNTIME #if HECL_RUNTIME
using StageTargetTp = StageRuntimeObject<P, S>; using StageTargetTp = StageRuntimeObject<P, S>;
#else #else
using StageTargetTp = StageBinary<P, S>; using StageTargetTp = StageBinary<P, S>;
#endif #endif
std::unordered_map<uint64_t, StageTargetTp> m_stageCache; std::unordered_map<uint64_t, StageTargetTp> m_stageCache;
#if 0 /* Horrible compiler memory explosion - DO NOT USE! */ #if 0 /* Horrible compiler memory explosion - DO NOT USE! */
template <typename ToTp, typename FromTp> template <typename ToTp, typename FromTp>
@ -347,305 +313,291 @@ class StageConverter
} }
#endif #endif
using StageTypes = typename ShaderDB<P>::template StageDB<S>::StageTypes; using StageTypes = typename ShaderDB<P>::template StageDB<S>::StageTypes;
template <typename ToTp, typename FromTp> template <typename ToTp, typename FromTp>
static constexpr bool is_stage_constructible_v = static constexpr bool is_stage_constructible_v =
std::is_constructible<ToTp, StageConverter<P, S>&, FactoryCtx&, FromTp>::value; std::is_constructible<ToTp, StageConverter<P, S>&, FactoryCtx&, FromTp>::value;
template<typename OriginTp, typename ToTp, typename T, typename... Targs> template <typename OriginTp, typename ToTp, typename T, typename... Targs>
struct _next_type { using type = std::conditional_t<is_stage_constructible_v<ToTp, OriginTp>, struct _next_type {
ToTp, using type = std::conditional_t<is_stage_constructible_v<ToTp, OriginTp>, ToTp,
typename _next_type<OriginTp, T, Targs...>::type>; }; typename _next_type<OriginTp, T, Targs...>::type>;
template<typename OriginTp, typename ToTp> };
struct _next_type<OriginTp, ToTp, null_t> { using type = null_t; }; template <typename OriginTp, typename ToTp>
template<typename OriginTp, typename... AllTypes> struct _next_type<OriginTp, ToTp, null_t> {
struct next_type { using type = null_t; }; using type = null_t;
template<typename OriginTp, typename... AllTypes> };
struct next_type<OriginTp, pack<AllTypes...>> : _next_type<OriginTp, AllTypes...> {}; template <typename OriginTp, typename... AllTypes>
template <typename OriginTp> struct next_type {
using next_type_t = typename next_type<OriginTp, StageTypes>::type; using type = null_t;
};
template <typename OriginTp, typename... AllTypes>
struct next_type<OriginTp, pack<AllTypes...>> : _next_type<OriginTp, AllTypes...> {};
template <typename OriginTp>
using next_type_t = typename next_type<OriginTp, StageTypes>::type;
/* StageSourceText derivative -> StageBinary */ /* StageSourceText derivative -> StageBinary */
template<class ToTp, class FromTp, class NextTp> template <class ToTp, class FromTp, class NextTp>
std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::is_base_of_v<StageSourceText<P, S>, NextTp>,
std::is_base_of_v<StageSourceText<P, S>, NextTp>, StageBinary<P, S>> StageBinary<P, S>>
_DoDerivative(FactoryCtx& ctx, const FromTp& in) _DoDerivative(FactoryCtx& ctx, const FromTp& in) {
{ return StageBinary<P, S>(*this, ctx, NextTp(*this, ctx, in));
return StageBinary<P, S>(*this, ctx, NextTp(*this, ctx, in)); }
}
/* StageBinary derivative -> StageBinary */ /* StageBinary derivative -> StageBinary */
template<class ToTp, class FromTp, class NextTp> template <class ToTp, class FromTp, class NextTp>
std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::is_base_of_v<StageBinary<P, S>, NextTp>,
std::is_base_of_v<StageBinary<P, S>, NextTp>, StageBinary<P, S>> StageBinary<P, S>>
_DoDerivative(FactoryCtx& ctx, const FromTp& in) _DoDerivative(FactoryCtx& ctx, const FromTp& in) {
{ return NextTp(*this, ctx, in);
return NextTp(*this, ctx, in); }
}
/* Non-StageSourceText derivative -> StageBinary */ /* Non-StageSourceText derivative -> StageBinary */
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && !std::is_base_of_v<StageSourceText<P, S>, FromTp>,
!std::is_base_of_v<StageSourceText<P, S>, FromTp>, StageBinary<P, S>> StageBinary<P, S>>
_Do(FactoryCtx& ctx, const FromTp& in) _Do(FactoryCtx& ctx, const FromTp& in) {
{ using NextTp = next_type_t<FromTp>;
using NextTp = next_type_t<FromTp>; static_assert(!std::is_same_v<NextTp, null_t>, "Unable to resolve StageBinary or StageSourceText derivative");
static_assert(!std::is_same_v<NextTp, null_t>, return _DoDerivative<ToTp, FromTp, NextTp>(ctx, in);
"Unable to resolve StageBinary or StageSourceText derivative"); }
return _DoDerivative<ToTp, FromTp, NextTp>(ctx, in);
}
/* StageSourceText derivative -> StageBinary */ /* StageSourceText derivative -> StageBinary */
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageBinary<P, S>> && std::is_base_of_v<StageSourceText<P, S>, FromTp>,
std::is_base_of_v<StageSourceText<P, S>, FromTp>, StageBinary<P, S>> StageBinary<P, S>>
_Do(FactoryCtx& ctx, const FromTp& in) _Do(FactoryCtx& ctx, const FromTp& in) {
{ return StageBinary<P, S>(*this, ctx, in);
return StageBinary<P, S>(*this, ctx, in); }
}
/* Non-StageBinary derivative -> StageRuntimeObject */ /* Non-StageBinary derivative -> StageRuntimeObject */
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<std::is_same_v<ToTp, StageRuntimeObject<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageRuntimeObject<P, S>> && !std::is_base_of_v<StageBinary<P, S>, FromTp>,
!std::is_base_of_v<StageBinary<P, S>, FromTp>, StageRuntimeObject<P, S>> StageRuntimeObject<P, S>>
_Do(FactoryCtx& ctx, const FromTp& in) _Do(FactoryCtx& ctx, const FromTp& in) {
{ return StageRuntimeObject<P, S>(*this, ctx, _Do<StageBinary<P, S>, FromTp>(ctx, in));
return StageRuntimeObject<P, S>(*this, ctx, _Do<StageBinary<P, S>, FromTp>(ctx, in)); }
}
/* StageBinary derivative -> StageRuntimeObject */ /* StageBinary derivative -> StageRuntimeObject */
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<std::is_same_v<ToTp, StageRuntimeObject<P, S>> && std::enable_if_t<std::is_same_v<ToTp, StageRuntimeObject<P, S>> && std::is_base_of_v<StageBinary<P, S>, FromTp>,
std::is_base_of_v<StageBinary<P, S>, FromTp>, StageRuntimeObject<P, S>> StageRuntimeObject<P, S>>
_Do(FactoryCtx& ctx, const FromTp& in) _Do(FactoryCtx& ctx, const FromTp& in) {
{ return StageRuntimeObject<P, S>(*this, ctx, in);
return StageRuntimeObject<P, S>(*this, ctx, in); }
}
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
ToTp Do(FactoryCtx& ctx, const FromTp& in) ToTp Do(FactoryCtx& ctx, const FromTp& in) {
{ return _Do<ToTp, FromTp>(ctx, in);
return _Do<ToTp, FromTp>(ctx, in); }
}
public: public:
#if HECL_RUNTIME #if HECL_RUNTIME
void loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r); void loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r);
#endif #endif
template<class FromTp> template <class FromTp>
StageTargetTp convert(FactoryCtx& ctx, const FromTp& in) StageTargetTp convert(FactoryCtx& ctx, const FromTp& in) {
{ if (FromTp::HasHash) {
if (FromTp::HasHash) uint64_t hash = in.Hash();
{ auto search = m_stageCache.find(hash);
uint64_t hash = in.Hash(); if (search != m_stageCache.end())
auto search = m_stageCache.find(hash); return search->second;
if (search != m_stageCache.end()) return m_stageCache.insert(std::make_pair(hash, Do<StageTargetTp>(ctx, in))).first->second;
return search->second;
return m_stageCache.insert(std::make_pair(hash, Do<StageTargetTp>(ctx, in))).first->second;
}
return Do<StageTargetTp>(ctx, in);
} }
return Do<StageTargetTp>(ctx, in);
}
}; };
class PipelineConverterBase class PipelineConverterBase {
{ boo::IGraphicsDataFactory* m_gfxF;
boo::IGraphicsDataFactory* m_gfxF; boo::IGraphicsDataFactory::Platform m_platform;
boo::IGraphicsDataFactory::Platform m_platform;
protected: protected:
PipelineConverterBase(boo::IGraphicsDataFactory* gfxF, boo::IGraphicsDataFactory::Platform platform) PipelineConverterBase(boo::IGraphicsDataFactory* gfxF, boo::IGraphicsDataFactory::Platform platform)
: m_gfxF(gfxF), m_platform(platform) {} : m_gfxF(gfxF), m_platform(platform) {}
public: public:
virtual ~PipelineConverterBase() = default; virtual ~PipelineConverterBase() = default;
#if HECL_RUNTIME #if HECL_RUNTIME
template<class FromTp> template <class FromTp>
boo::ObjToken<boo::IShaderPipeline> convert(FactoryCtx& ctx, const FromTp& in); boo::ObjToken<boo::IShaderPipeline> convert(FactoryCtx& ctx, const FromTp& in);
template<class FromTp> template <class FromTp>
boo::ObjToken<boo::IShaderPipeline> convert(const FromTp& in); boo::ObjToken<boo::IShaderPipeline> convert(const FromTp& in);
#endif #endif
}; };
template<typename P> template <typename P>
class PipelineConverter : public PipelineConverterBase class PipelineConverter : public PipelineConverterBase {
{
#if HECL_RUNTIME #if HECL_RUNTIME
using PipelineTargetTp = FinalPipeline<P>; using PipelineTargetTp = FinalPipeline<P>;
#else #else
using PipelineTargetTp = StageCollection<StageBinary<P>>; using PipelineTargetTp = StageCollection<StageBinary<P>>;
#endif #endif
std::unordered_map<uint64_t, PipelineTargetTp> m_pipelineCache; std::unordered_map<uint64_t, PipelineTargetTp> m_pipelineCache;
StageConverter<P, PipelineStage::Vertex> m_vertexConverter; StageConverter<P, PipelineStage::Vertex> m_vertexConverter;
StageConverter<P, PipelineStage::Fragment> m_fragmentConverter; StageConverter<P, PipelineStage::Fragment> m_fragmentConverter;
StageConverter<P, PipelineStage::Geometry> m_geometryConverter; StageConverter<P, PipelineStage::Geometry> m_geometryConverter;
StageConverter<P, PipelineStage::Control> m_controlConverter; StageConverter<P, PipelineStage::Control> m_controlConverter;
StageConverter<P, PipelineStage::Evaluation> m_evaluationConverter; StageConverter<P, PipelineStage::Evaluation> m_evaluationConverter;
using PipelineTypes = typename ShaderDB<P>::PipelineTypes; using PipelineTypes = typename ShaderDB<P>::PipelineTypes;
template <typename ToTp, typename FromTp> template <typename ToTp, typename FromTp>
static constexpr bool is_pipeline_constructible_v = static constexpr bool is_pipeline_constructible_v =
std::is_constructible<ToTp, PipelineConverter<P>&, FactoryCtx&, FromTp>::value; std::is_constructible<ToTp, PipelineConverter<P>&, FactoryCtx&, FromTp>::value;
template<typename FinalTp, typename OriginTp, typename... AllTypes> template <typename FinalTp, typename OriginTp, typename... AllTypes>
struct is_eventually_constructible : std::false_type {}; struct is_eventually_constructible : std::false_type {};
template<typename FinalTp, typename OriginTp, typename ToTp, typename T, typename... Targs> template <typename FinalTp, typename OriginTp, typename ToTp, typename T, typename... Targs>
struct _is_eventually_constructible struct _is_eventually_constructible
: std::conditional_t<is_pipeline_constructible_v<FinalTp, OriginTp>, : std::conditional_t<is_pipeline_constructible_v<FinalTp, OriginTp>, std::true_type,
std::true_type, std::conditional_t<is_pipeline_constructible_v<ToTp, OriginTp>,
std::conditional_t<is_pipeline_constructible_v<ToTp, OriginTp>, is_eventually_constructible<FinalTp, ToTp, PipelineTypes>,
is_eventually_constructible<FinalTp, ToTp, PipelineTypes>, _is_eventually_constructible<FinalTp, OriginTp, T, Targs...>>> {};
_is_eventually_constructible<FinalTp, OriginTp, T, Targs...>>> {}; template <typename FinalTp, typename OriginTp, typename ToTp>
template<typename FinalTp, typename OriginTp, typename ToTp> struct _is_eventually_constructible<FinalTp, OriginTp, ToTp, null_t> : std::false_type {};
struct _is_eventually_constructible<FinalTp, OriginTp, ToTp, null_t> : std::false_type {}; template <typename FinalTp, typename OriginTp, typename... AllTypes>
template<typename FinalTp, typename OriginTp, typename... AllTypes> struct is_eventually_constructible<FinalTp, OriginTp, pack<AllTypes...>>
struct is_eventually_constructible<FinalTp, OriginTp, pack<AllTypes...>> : _is_eventually_constructible<FinalTp, OriginTp, AllTypes...> {};
: _is_eventually_constructible<FinalTp, OriginTp, AllTypes...> {}; template <typename FinalTp, typename OriginTp>
template <typename FinalTp, typename OriginTp> static constexpr bool is_eventually_constructible_v =
static constexpr bool is_eventually_constructible_v = is_eventually_constructible<FinalTp, OriginTp, PipelineTypes>::value;
is_eventually_constructible<FinalTp, OriginTp, PipelineTypes>::value;
template<typename FinalTp, typename OriginTp, typename ToTp, typename T, typename... Targs> template <typename FinalTp, typename OriginTp, typename ToTp, typename T, typename... Targs>
struct _next_type { using type = std::conditional_t<is_pipeline_constructible_v<FinalTp, ToTp> && struct _next_type {
is_eventually_constructible_v<ToTp, OriginTp>, using type =
ToTp, std::conditional_t<is_pipeline_constructible_v<FinalTp, ToTp> && is_eventually_constructible_v<ToTp, OriginTp>,
typename _next_type<FinalTp, OriginTp, T, Targs...>::type>; }; ToTp, typename _next_type<FinalTp, OriginTp, T, Targs...>::type>;
template<typename FinalTp, typename OriginTp, typename ToTp> };
struct _next_type<FinalTp, OriginTp, ToTp, null_t> { using type = null_t; }; template <typename FinalTp, typename OriginTp, typename ToTp>
template<typename FinalTp, typename OriginTp, typename... AllTypes> struct _next_type<FinalTp, OriginTp, ToTp, null_t> {
struct next_type { using type = null_t; }; using type = null_t;
template<typename FinalTp, typename OriginTp, typename... AllTypes> };
struct next_type<FinalTp, OriginTp, pack<AllTypes...>> : _next_type<FinalTp, OriginTp, AllTypes...> {}; template <typename FinalTp, typename OriginTp, typename... AllTypes>
template <typename FinalTp, typename OriginTp> struct next_type {
using next_type_t = typename next_type<FinalTp, OriginTp, PipelineTypes>::type; using type = null_t;
};
template <typename FinalTp, typename OriginTp, typename... AllTypes>
struct next_type<FinalTp, OriginTp, pack<AllTypes...>> : _next_type<FinalTp, OriginTp, AllTypes...> {};
template <typename FinalTp, typename OriginTp>
using next_type_t = typename next_type<FinalTp, OriginTp, PipelineTypes>::type;
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<!is_pipeline_constructible_v<ToTp, FromTp>, ToTp> std::enable_if_t<!is_pipeline_constructible_v<ToTp, FromTp>, ToTp> _Do(FactoryCtx& ctx, const FromTp& in) {
_Do(FactoryCtx& ctx, const FromTp& in) using NextTp = next_type_t<ToTp, FromTp>;
{ return ToTp(*this, ctx, _Do<NextTp, FromTp>(ctx, in));
using NextTp = next_type_t<ToTp, FromTp>; }
return ToTp(*this, ctx, _Do<NextTp, FromTp>(ctx, in));
}
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
std::enable_if_t<is_pipeline_constructible_v<ToTp, FromTp>, ToTp> std::enable_if_t<is_pipeline_constructible_v<ToTp, FromTp>, ToTp> _Do(FactoryCtx& ctx, const FromTp& in) {
_Do(FactoryCtx& ctx, const FromTp& in) return ToTp(*this, ctx, in);
{ }
return ToTp(*this, ctx, in);
}
template<class ToTp, class FromTp> template <class ToTp, class FromTp>
ToTp Do(FactoryCtx& ctx, const FromTp& in) ToTp Do(FactoryCtx& ctx, const FromTp& in) {
{ /* No idea why this fails; it works fine with manual template arguments (clang bug?) */
/* No idea why this fails; it works fine with manual template arguments (clang bug?) */ // static_assert(is_eventually_constructible_v<ToTp, FromTp>, "Unable to resolve pipeline conversion chain");
//static_assert(is_eventually_constructible_v<ToTp, FromTp>, "Unable to resolve pipeline conversion chain"); return _Do<ToTp, FromTp>(ctx, in);
return _Do<ToTp, FromTp>(ctx, in); }
}
public: public:
PipelineConverter(boo::IGraphicsDataFactory* gfxF) : PipelineConverterBase(gfxF, P::Enum) {} PipelineConverter(boo::IGraphicsDataFactory* gfxF) : PipelineConverterBase(gfxF, P::Enum) {}
#if HECL_RUNTIME #if HECL_RUNTIME
bool loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path); bool loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path);
#endif #endif
template<class FromTp> template <class FromTp>
PipelineTargetTp convert(FactoryCtx& ctx, const FromTp& in) PipelineTargetTp convert(FactoryCtx& ctx, const FromTp& in) {
{ if (FromTp::HasHash) {
if (FromTp::HasHash) uint64_t hash = in.Hash();
{ auto search = m_pipelineCache.find(hash);
uint64_t hash = in.Hash(); if (search != m_pipelineCache.end())
auto search = m_pipelineCache.find(hash); return search->second;
if (search != m_pipelineCache.end()) return m_pipelineCache.insert(std::make_pair(hash, Do<PipelineTargetTp>(ctx, in))).first->second;
return search->second;
return m_pipelineCache.insert(std::make_pair(hash, Do<PipelineTargetTp>(ctx, in))).first->second;
}
return Do<PipelineTargetTp>(ctx, in);
} }
return Do<PipelineTargetTp>(ctx, in);
}
StageConverter<P, PipelineStage::Vertex>& getVertexConverter() { return m_vertexConverter; } StageConverter<P, PipelineStage::Vertex>& getVertexConverter() { return m_vertexConverter; }
StageConverter<P, PipelineStage::Fragment>& getFragmentConverter() { return m_fragmentConverter; } StageConverter<P, PipelineStage::Fragment>& getFragmentConverter() { return m_fragmentConverter; }
StageConverter<P, PipelineStage::Geometry>& getGeometryConverter() { return m_geometryConverter; } StageConverter<P, PipelineStage::Geometry>& getGeometryConverter() { return m_geometryConverter; }
StageConverter<P, PipelineStage::Control>& getControlConverter() { return m_controlConverter; } StageConverter<P, PipelineStage::Control>& getControlConverter() { return m_controlConverter; }
StageConverter<P, PipelineStage::Evaluation>& getEvaluationConverter() { return m_evaluationConverter; } StageConverter<P, PipelineStage::Evaluation>& getEvaluationConverter() { return m_evaluationConverter; }
}; };
#if HECL_RUNTIME #if HECL_RUNTIME
template<class FromTp> template <class FromTp>
inline boo::ObjToken<boo::IShaderPipeline> PipelineConverterBase::convert(FactoryCtx& ctx, const FromTp& in) inline boo::ObjToken<boo::IShaderPipeline> PipelineConverterBase::convert(FactoryCtx& ctx, const FromTp& in) {
{ assert(ctx.platform() == m_platform && "PipelineConverterBase platform mismatch");
assert(ctx.platform() == m_platform && "PipelineConverterBase platform mismatch"); switch (m_platform) {
switch (m_platform)
{
#if BOO_HAS_GL #if BOO_HAS_GL
case boo::IGraphicsDataFactory::Platform::OpenGL: case boo::IGraphicsDataFactory::Platform::OpenGL:
return static_cast<PipelineConverter<PlatformType::OpenGL>&>(*this).convert(ctx, in).pipeline(); return static_cast<PipelineConverter<PlatformType::OpenGL>&>(*this).convert(ctx, in).pipeline();
#endif #endif
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
case boo::IGraphicsDataFactory::Platform::Vulkan: case boo::IGraphicsDataFactory::Platform::Vulkan:
return static_cast<PipelineConverter<PlatformType::Vulkan>&>(*this).convert(ctx, in).pipeline(); return static_cast<PipelineConverter<PlatformType::Vulkan>&>(*this).convert(ctx, in).pipeline();
#endif #endif
#if _WIN32 #if _WIN32
case boo::IGraphicsDataFactory::Platform::D3D11: case boo::IGraphicsDataFactory::Platform::D3D11:
return static_cast<PipelineConverter<PlatformType::D3D11>&>(*this).convert(ctx, in).pipeline(); return static_cast<PipelineConverter<PlatformType::D3D11>&>(*this).convert(ctx, in).pipeline();
#endif #endif
#if BOO_HAS_METAL #if BOO_HAS_METAL
case boo::IGraphicsDataFactory::Platform::Metal: case boo::IGraphicsDataFactory::Platform::Metal:
return static_cast<PipelineConverter<PlatformType::Metal>&>(*this).convert(ctx, in).pipeline(); return static_cast<PipelineConverter<PlatformType::Metal>&>(*this).convert(ctx, in).pipeline();
#endif #endif
#if BOO_HAS_NX #if BOO_HAS_NX
case boo::IGraphicsDataFactory::Platform::NX: case boo::IGraphicsDataFactory::Platform::NX:
return static_cast<PipelineConverter<PlatformType::NX>&>(*this).convert(ctx, in).pipeline(); return static_cast<PipelineConverter<PlatformType::NX>&>(*this).convert(ctx, in).pipeline();
#endif #endif
default: default:
return {}; return {};
} }
} }
template<class FromTp> template <class FromTp>
inline boo::ObjToken<boo::IShaderPipeline> PipelineConverterBase::convert(const FromTp& in) inline boo::ObjToken<boo::IShaderPipeline> PipelineConverterBase::convert(const FromTp& in) {
{ boo::ObjToken<boo::IShaderPipeline> ret;
boo::ObjToken<boo::IShaderPipeline> ret; m_gfxF->commitTransaction([this, &ret, &in](boo::IGraphicsDataFactory::Context& ctx) {
m_gfxF->commitTransaction([this, &ret, &in](boo::IGraphicsDataFactory::Context& ctx) ret = convert(ctx, in);
{ return true;
ret = convert(ctx, in); } BooTrace);
return true; return ret;
} BooTrace);
return ret;
} }
inline std::unique_ptr<PipelineConverterBase> NewPipelineConverter(boo::IGraphicsDataFactory* gfxF) inline std::unique_ptr<PipelineConverterBase> NewPipelineConverter(boo::IGraphicsDataFactory* gfxF) {
{ switch (gfxF->platform()) {
switch (gfxF->platform())
{
#if BOO_HAS_GL #if BOO_HAS_GL
case boo::IGraphicsDataFactory::Platform::OpenGL: case boo::IGraphicsDataFactory::Platform::OpenGL:
return std::make_unique<PipelineConverter<PlatformType::OpenGL>>(gfxF); return std::make_unique<PipelineConverter<PlatformType::OpenGL>>(gfxF);
#endif #endif
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
case boo::IGraphicsDataFactory::Platform::Vulkan: case boo::IGraphicsDataFactory::Platform::Vulkan:
return std::make_unique<PipelineConverter<PlatformType::Vulkan>>(gfxF); return std::make_unique<PipelineConverter<PlatformType::Vulkan>>(gfxF);
#endif #endif
#if _WIN32 #if _WIN32
case boo::IGraphicsDataFactory::Platform::D3D11: case boo::IGraphicsDataFactory::Platform::D3D11:
return std::make_unique<PipelineConverter<PlatformType::D3D11>>(gfxF); return std::make_unique<PipelineConverter<PlatformType::D3D11>>(gfxF);
#endif #endif
#if BOO_HAS_METAL #if BOO_HAS_METAL
case boo::IGraphicsDataFactory::Platform::Metal: case boo::IGraphicsDataFactory::Platform::Metal:
setenv("HECL_NO_METAL_COMPILER", "1", 1); setenv("HECL_NO_METAL_COMPILER", "1", 1);
return std::make_unique<PipelineConverter<PlatformType::Metal>>(gfxF); return std::make_unique<PipelineConverter<PlatformType::Metal>>(gfxF);
#endif #endif
#if BOO_HAS_NX #if BOO_HAS_NX
case boo::IGraphicsDataFactory::Platform::NX: case boo::IGraphicsDataFactory::Platform::NX:
return std::make_unique<PipelineConverter<PlatformType::NX>>(gfxF); return std::make_unique<PipelineConverter<PlatformType::NX>>(gfxF);
#endif #endif
default: default:
return {}; return {};
} }
} }
extern PipelineConverterBase* conv; extern PipelineConverterBase* conv;
#endif #endif
} } // namespace hecl

View File

@ -1,33 +1,26 @@
#pragma once #pragma once
#include "Compilers.hpp" #include "Compilers.hpp"
extern "C" unsigned long long XXH64 (const void* input, size_t length, unsigned long long seed); extern "C" unsigned long long XXH64(const void* input, size_t length, unsigned long long seed);
#define HECL_RUNTIME 1 #define HECL_RUNTIME 1
namespace hecl namespace hecl {
{
using AdditionalPipelineInfo = boo::AdditionalPipelineInfo; using AdditionalPipelineInfo = boo::AdditionalPipelineInfo;
enum class StageTargetType enum class StageTargetType { SourceText, Binary, Runtime };
{
SourceText, enum class PipelineTargetType {
Binary, StageSourceTextCollection,
Runtime StageBinaryCollection,
StageRuntimeCollection,
FinalPipeline
}; };
enum class PipelineTargetType template <typename P, typename S>
{
StageSourceTextCollection,
StageBinaryCollection,
StageRuntimeCollection,
FinalPipeline
};
template<typename P, typename S>
class StageConverter; class StageConverter;
template<typename P> template <typename P>
class PipelineConverter; class PipelineConverter;
#if HECL_RUNTIME #if HECL_RUNTIME
@ -36,200 +29,189 @@ using FactoryCtx = boo::IGraphicsDataFactory::Context;
struct FactoryCtx {}; struct FactoryCtx {};
#endif #endif
template<typename P, typename S> template <typename P, typename S>
class StageRep class StageRep {
{
public: public:
using Platform = P; using Platform = P;
using Stage = S; using Stage = S;
}; };
template<typename P> template <typename P>
class PipelineRep class PipelineRep {
{
public: public:
using Platform = P; using Platform = P;
}; };
class GeneralShader : public hecl::PipelineRep<hecl::PlatformType::Null> {}; class GeneralShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
class TessellationShader : public hecl::PipelineRep<hecl::PlatformType::Null> {}; class TessellationShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
template<typename P, typename S> template <typename P, typename S>
class StageSourceText : public StageRep<P, S> class StageSourceText : public StageRep<P, S> {
{ std::string_view m_text;
std::string_view m_text; uint64_t m_hash;
uint64_t m_hash;
public:
static constexpr StageTargetType TargetType = StageTargetType::SourceText;
static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageSourceTextCollection;
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
explicit StageSourceText(std::string_view text) public:
: m_text(text), m_hash(XXH64(m_text.data(), m_text.size(), 0)) {} static constexpr StageTargetType TargetType = StageTargetType::SourceText;
std::string_view text() const { return m_text; } static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageSourceTextCollection;
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
explicit StageSourceText(std::string_view text) : m_text(text), m_hash(XXH64(m_text.data(), m_text.size(), 0)) {}
std::string_view text() const { return m_text; }
}; };
template<typename P, typename S> template <typename P, typename S>
class StageBinary : public StageRep<P, S> class StageBinary : public StageRep<P, S> {
{ StageBinaryData m_ownedData;
StageBinaryData m_ownedData; const uint8_t* m_data = nullptr;
const uint8_t* m_data = nullptr; size_t m_size = 0;
size_t m_size = 0; uint64_t m_hash = 0;
uint64_t m_hash = 0;
public:
static constexpr StageTargetType TargetType = StageTargetType::Binary;
static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageBinaryCollection;
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
StageBinary(const uint8_t* data, size_t size) public:
: m_data(data), m_size(size) static constexpr StageTargetType TargetType = StageTargetType::Binary;
{ m_hash = XXH64(m_data, m_size, 0); } static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageBinaryCollection;
StageBinary(StageBinaryData data, size_t size) static constexpr bool HasHash = true;
: m_ownedData(std::move(data)), uint64_t Hash() const { return m_hash; }
m_data(m_ownedData.get()), m_size(size)
{ m_hash = XXH64(m_data, m_size, 0); } StageBinary(const uint8_t* data, size_t size) : m_data(data), m_size(size) { m_hash = XXH64(m_data, m_size, 0); }
explicit StageBinary(std::pair<StageBinaryData, size_t> data) StageBinary(StageBinaryData data, size_t size)
: StageBinary(data.first, data.second) {} : m_ownedData(std::move(data)), m_data(m_ownedData.get()), m_size(size) {
StageBinary(StageConverter<P, S>& conv, FactoryCtx& ctx, const StageSourceText<P, S>& in) m_hash = XXH64(m_data, m_size, 0);
: StageBinary(CompileShader<P, S>(in.text())) {} }
const uint8_t* data() const { return m_data; } explicit StageBinary(std::pair<StageBinaryData, size_t> data) : StageBinary(data.first, data.second) {}
size_t size() const { return m_size; } StageBinary(StageConverter<P, S>& conv, FactoryCtx& ctx, const StageSourceText<P, S>& in)
: StageBinary(CompileShader<P, S>(in.text())) {}
const uint8_t* data() const { return m_data; }
size_t size() const { return m_size; }
}; };
template<typename P> template <typename P>
class FinalPipeline; class FinalPipeline;
template<typename P> template <typename P>
class HECLBackend; class HECLBackend;
template <class T> template <class T>
using __IsStageSubclass = typename std::disjunction< using __IsStageSubclass =
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>, typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Fragment>, T>, std::is_base_of<StageRep<typename T::Platform, PipelineStage::Fragment>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Geometry>, T>, std::is_base_of<StageRep<typename T::Platform, PipelineStage::Geometry>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Control>, T>, std::is_base_of<StageRep<typename T::Platform, PipelineStage::Control>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Evaluation>, T>>; std::is_base_of<StageRep<typename T::Platform, PipelineStage::Evaluation>, T>>;
template <class T> template <class T>
inline constexpr bool __IsStageSubclass_v = __IsStageSubclass<T>::value; inline constexpr bool __IsStageSubclass_v = __IsStageSubclass<T>::value;
template<typename T> class StageCollection; template <typename T>
template<template<typename, typename> class T, typename P, typename... Rest> class StageCollection;
class StageCollection<T<P, Rest...>> : public PipelineRep<P> template <template <typename, typename> class T, typename P, typename... Rest>
{ class StageCollection<T<P, Rest...>> : public PipelineRep<P> {
using base = PipelineRep<P>; using base = PipelineRep<P>;
friend class FinalPipeline<P>; friend class FinalPipeline<P>;
static_assert(__IsStageSubclass_v<T<P, PipelineStage::Vertex>>, static_assert(__IsStageSubclass_v<T<P, PipelineStage::Vertex>>,
"Stage Collection may only be specialized with StageRep subclasses"); "Stage Collection may only be specialized with StageRep subclasses");
T<P, PipelineStage::Vertex> m_vertex; T<P, PipelineStage::Vertex> m_vertex;
T<P, PipelineStage::Fragment> m_fragment; T<P, PipelineStage::Fragment> m_fragment;
T<P, PipelineStage::Geometry> m_geometry; T<P, PipelineStage::Geometry> m_geometry;
T<P, PipelineStage::Control> m_control; T<P, PipelineStage::Control> m_control;
T<P, PipelineStage::Evaluation> m_evaluation; T<P, PipelineStage::Evaluation> m_evaluation;
AdditionalPipelineInfo m_additionalInfo; AdditionalPipelineInfo m_additionalInfo;
std::vector<boo::VertexElementDescriptor> m_vtxFmtData; std::vector<boo::VertexElementDescriptor> m_vtxFmtData;
boo::VertexFormatInfo m_vtxFmt; boo::VertexFormatInfo m_vtxFmt;
uint64_t m_hash; uint64_t m_hash;
public: public:
static constexpr PipelineTargetType TargetType = T<P, PipelineStage::Vertex>::PipelineTarget; static constexpr PipelineTargetType TargetType = T<P, PipelineStage::Vertex>::PipelineTarget;
static constexpr bool HasHash = T<P, PipelineStage::Vertex>::HasHash; static constexpr bool HasHash = T<P, PipelineStage::Vertex>::HasHash;
template<typename U = StageCollection<T<P, Rest...>>> template <typename U = StageCollection<T<P, Rest...>>>
std::enable_if_t<U::HasHash, uint64_t> Hash() const { return m_hash; } std::enable_if_t<U::HasHash, uint64_t> Hash() const {
template<typename U = StageCollection<T<P, Rest...>>> return m_hash;
void MakeHash(std::enable_if_t<!U::HasHash>* = 0) {} }
template<typename U = StageCollection<T<P, Rest...>>> template <typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<U::HasHash>* = 0) void MakeHash(std::enable_if_t<!U::HasHash>* = 0) {}
{ template <typename U = StageCollection<T<P, Rest...>>>
m_hash = 0; void MakeHash(std::enable_if_t<U::HasHash>* = 0) {
m_hash ^= m_vertex.Hash(); m_hash = 0;
m_hash ^= m_fragment.Hash(); m_hash ^= m_vertex.Hash();
m_hash ^= m_geometry.Hash(); m_hash ^= m_fragment.Hash();
m_hash ^= m_control.Hash(); m_hash ^= m_geometry.Hash();
m_hash ^= m_evaluation.Hash(); m_hash ^= m_control.Hash();
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0); m_hash ^= m_evaluation.Hash();
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
}
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in);
template <typename I>
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) {
m_vertex = conv.getVertexConverter().convert(ctx, in);
m_fragment = conv.getFragmentConverter().convert(ctx, in);
m_vtxFmt = in.VtxFmt;
m_additionalInfo = in.PipelineInfo;
MakeHash();
}
template <typename I>
StageCollection(
PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::conjunction_v<std::is_base_of<TessellationShader, I>,
std::negation<std::is_same<P, PlatformType::Metal>>>>* = 0) {
m_vertex = conv.getVertexConverter().convert(ctx, in);
m_fragment = conv.getFragmentConverter().convert(ctx, in);
if (in.HasTessellation) {
m_control = conv.getControlConverter().convert(ctx, in);
m_evaluation = conv.getEvaluationConverter().convert(ctx, in);
} }
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const HECLBackend<P>& in); m_vtxFmt = in.VtxFmt;
template<typename I> m_additionalInfo = in.PipelineInfo;
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in, MakeHash();
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) }
{ template <typename I>
m_vertex = conv.getVertexConverter().convert(ctx, in); StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
m_fragment = conv.getFragmentConverter().convert(ctx, in); typename std::enable_if_t<std::conjunction_v<std::is_base_of<TessellationShader, I>,
m_vtxFmt = in.VtxFmt; std::is_same<P, PlatformType::Metal>>>* = 0) {
m_additionalInfo = in.PipelineInfo; if (in.HasTessellation) {
MakeHash(); m_control = conv.getControlConverter().convert(ctx, in);
m_evaluation = conv.getEvaluationConverter().convert(ctx, in);
} else {
m_vertex = conv.getVertexConverter().convert(ctx, in);
} }
template<typename I> m_fragment = conv.getFragmentConverter().convert(ctx, in);
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in, m_vtxFmt = in.VtxFmt;
typename std::enable_if_t<std::conjunction_v< m_additionalInfo = in.PipelineInfo;
std::is_base_of<TessellationShader, I>, MakeHash();
std::negation<std::is_same<P, PlatformType::Metal>>>>* = 0) }
{ StageCollection(const T<P, PipelineStage::Vertex>& vertex, const T<P, PipelineStage::Fragment>& fragment,
m_vertex = conv.getVertexConverter().convert(ctx, in); const T<P, PipelineStage::Geometry>& geometry, const T<P, PipelineStage::Control>& control,
m_fragment = conv.getFragmentConverter().convert(ctx, in); const T<P, PipelineStage::Evaluation>& evaluation, const AdditionalPipelineInfo& info,
if (in.HasTessellation) const boo::VertexFormatInfo& vtxFmt)
{ : m_vertex(vertex)
m_control = conv.getControlConverter().convert(ctx, in); , m_fragment(fragment)
m_evaluation = conv.getEvaluationConverter().convert(ctx, in); , m_geometry(geometry)
} , m_control(control)
m_vtxFmt = in.VtxFmt; , m_evaluation(evaluation)
m_additionalInfo = in.PipelineInfo; , m_additionalInfo(info)
MakeHash(); , m_vtxFmt(vtxFmt) {}
}
template<typename I>
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::conjunction_v<
std::is_base_of<TessellationShader, I>,
std::is_same<P, PlatformType::Metal>>>* = 0)
{
if (in.HasTessellation)
{
m_control = conv.getControlConverter().convert(ctx, in);
m_evaluation = conv.getEvaluationConverter().convert(ctx, in);
}
else
{
m_vertex = conv.getVertexConverter().convert(ctx, in);
}
m_fragment = conv.getFragmentConverter().convert(ctx, in);
m_vtxFmt = in.VtxFmt;
m_additionalInfo = in.PipelineInfo;
MakeHash();
}
StageCollection(const T<P, PipelineStage::Vertex>& vertex,
const T<P, PipelineStage::Fragment>& fragment,
const T<P, PipelineStage::Geometry>& geometry,
const T<P, PipelineStage::Control>& control,
const T<P, PipelineStage::Evaluation>& evaluation,
const AdditionalPipelineInfo& info,
const boo::VertexFormatInfo& vtxFmt)
: m_vertex(vertex), m_fragment(fragment), m_geometry(geometry),
m_control(control), m_evaluation(evaluation), m_additionalInfo(info),
m_vtxFmt(vtxFmt) {}
}; };
} } // namespace hecl
#ifndef _WIN32 #ifndef _WIN32
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \ #define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Vertex> \ template <> \
T<P, hecl::PipelineStage::Vertex>::Prototype; \ const hecl::StageBinary<P, hecl::PipelineStage::Vertex> T<P, hecl::PipelineStage::Vertex>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Fragment> \ template <> \
T<P, hecl::PipelineStage::Fragment>::Prototype; \ const hecl::StageBinary<P, hecl::PipelineStage::Fragment> T<P, hecl::PipelineStage::Fragment>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Geometry> \ template <> \
T<P, hecl::PipelineStage::Geometry>::Prototype; \ const hecl::StageBinary<P, hecl::PipelineStage::Geometry> T<P, hecl::PipelineStage::Geometry>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Control> \ template <> \
T<P, hecl::PipelineStage::Control>::Prototype; \ const hecl::StageBinary<P, hecl::PipelineStage::Control> T<P, hecl::PipelineStage::Control>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Evaluation> \ template <> \
T<P, hecl::PipelineStage::Evaluation>::Prototype; const hecl::StageBinary<P, hecl::PipelineStage::Evaluation> T<P, hecl::PipelineStage::Evaluation>::Prototype;
#else #else
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) #define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P)
#endif #endif
#define STAGEOBJECT_PROTOTYPE_DECLARATIONS(T) \ #define STAGEOBJECT_PROTOTYPE_DECLARATIONS(T) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::OpenGL) \ _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::OpenGL) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Vulkan) \ _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Vulkan) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::D3D11) \ _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::D3D11) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Metal) \ _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Metal) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::NX) _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::NX)

View File

@ -4,54 +4,49 @@
#include "boo/graphicsdev/IGraphicsDataFactory.hpp" #include "boo/graphicsdev/IGraphicsDataFactory.hpp"
#include <unordered_map> #include <unordered_map>
namespace hecl namespace hecl {
{
struct HMDLMeta; struct HMDLMeta;
namespace Runtime namespace Runtime {
{
/** /**
* @brief Per-platform file store resolution * @brief Per-platform file store resolution
*/ */
class FileStoreManager class FileStoreManager {
{ SystemString m_domain;
SystemString m_domain; SystemString m_storeRoot;
SystemString m_storeRoot;
public: public:
FileStoreManager(SystemStringView domain); FileStoreManager(SystemStringView domain);
SystemStringView getDomain() const {return m_domain;} SystemStringView getDomain() const { return m_domain; }
/** /**
* @brief Returns the full path to the file store, including domain * @brief Returns the full path to the file store, including domain
* @return Full path to store e.g /home/foo/.hecl/bar * @return Full path to store e.g /home/foo/.hecl/bar
*/ */
SystemStringView getStoreRoot() const {return m_storeRoot;} SystemStringView getStoreRoot() const { return m_storeRoot; }
}; };
/** /**
* @brief Integrated reader/constructor/container for HMDL data * @brief Integrated reader/constructor/container for HMDL data
*/ */
struct HMDLData struct HMDLData {
{ boo::ObjToken<boo::IGraphicsBufferS> m_vbo;
boo::ObjToken<boo::IGraphicsBufferS> m_vbo; boo::ObjToken<boo::IGraphicsBufferS> m_ibo;
boo::ObjToken<boo::IGraphicsBufferS> m_ibo; std::unique_ptr<boo::VertexElementDescriptor[]> m_vtxFmtData;
std::unique_ptr<boo::VertexElementDescriptor[]> m_vtxFmtData; boo::VertexFormatInfo m_vtxFmt;
boo::VertexFormatInfo m_vtxFmt;
HMDLData(boo::IGraphicsDataFactory::Context& ctx, HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo);
const void* metaData, const void* vbo, const void* ibo);
boo::ObjToken<boo::IShaderDataBinding> boo::ObjToken<boo::IShaderDataBinding> newShaderDataBindng(boo::IGraphicsDataFactory::Context& ctx,
newShaderDataBindng(boo::IGraphicsDataFactory::Context& ctx, const boo::ObjToken<boo::IShaderPipeline>& shader,
const boo::ObjToken<boo::IShaderPipeline>& shader, size_t ubufCount,
size_t ubufCount, const boo::ObjToken<boo::IGraphicsBuffer>* ubufs, const boo::ObjToken<boo::IGraphicsBuffer>* ubufs,
const boo::PipelineStage* ubufStages, const boo::PipelineStage* ubufStages, size_t texCount,
size_t texCount, const boo::ObjToken<boo::ITexture>* texs) const boo::ObjToken<boo::ITexture>* texs) {
{return ctx.newShaderDataBinding(shader, m_vbo.get(), nullptr, m_ibo.get(), return ctx.newShaderDataBinding(shader, m_vbo.get(), nullptr, m_ibo.get(), ubufCount, ubufs, ubufStages, nullptr,
ubufCount, ubufs, ubufStages, nullptr, nullptr, nullptr, texCount, texs, nullptr, nullptr);
texCount, texs, nullptr, nullptr);} }
}; };
} } // namespace Runtime
} } // namespace hecl

View File

@ -2,11 +2,8 @@
#include "hecl/SystemChar.hpp" #include "hecl/SystemChar.hpp"
namespace hecl namespace hecl {
{
hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name); hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name);
} }

View File

@ -18,8 +18,7 @@
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
namespace hecl namespace hecl {
{
#if _WIN32 && UNICODE #if _WIN32 && UNICODE
#define HECL_UCS2 1 #define HECL_UCS2 1
@ -27,15 +26,13 @@ namespace hecl
#if HECL_UCS2 #if HECL_UCS2
typedef wchar_t SystemChar; typedef wchar_t SystemChar;
static inline size_t StrLen(const SystemChar* str) {return wcslen(str);} static inline size_t StrLen(const SystemChar* str) { return wcslen(str); }
typedef std::wstring SystemString; typedef std::wstring SystemString;
typedef std::wstring_view SystemStringView; typedef std::wstring_view SystemStringView;
static inline void ToLower(SystemString& str) static inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towlower); }
{std::transform(str.begin(), str.end(), str.begin(), towlower);} static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towupper); }
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), towupper);}
#ifndef _SYS_STR #ifndef _SYS_STR
#define _SYS_STR(val) L ## val #define _SYS_STR(val) L##val
#endif #endif
#ifndef FMT_CSTR_SYS #ifndef FMT_CSTR_SYS
#define FMT_CSTR_SYS "S" #define FMT_CSTR_SYS "S"
@ -43,13 +40,11 @@ static inline void ToUpper(SystemString& str)
typedef struct _stat Sstat; typedef struct _stat Sstat;
#else #else
typedef char SystemChar; typedef char SystemChar;
static inline size_t StrLen(const SystemChar* str) {return strlen(str);} static inline size_t StrLen(const SystemChar* str) { return strlen(str); }
typedef std::string SystemString; typedef std::string SystemString;
typedef std::string_view SystemStringView; typedef std::string_view SystemStringView;
static inline void ToLower(SystemString& str) static inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), tolower); }
{std::transform(str.begin(), str.end(), str.begin(), tolower);} static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), toupper); }
static inline void ToUpper(SystemString& str)
{std::transform(str.begin(), str.end(), str.begin(), toupper);}
#ifndef _SYS_STR #ifndef _SYS_STR
#define _SYS_STR(val) val #define _SYS_STR(val) val
#endif #endif
@ -59,5 +54,4 @@ static inline void ToUpper(SystemString& str)
typedef struct stat Sstat; typedef struct stat Sstat;
#endif #endif
} } // namespace hecl

View File

@ -6,8 +6,7 @@
#include <atomic> #include <atomic>
#include "BitVector.hpp" #include "BitVector.hpp"
namespace hecl namespace hecl {
{
#define HECL_UBUFPOOL_ALLOCATION_BLOCK 262144 #define HECL_UBUFPOOL_ALLOCATION_BLOCK 262144
@ -18,188 +17,160 @@ namespace hecl
* widgets. These can potentially have numerous binding instances, so this avoids * widgets. These can potentially have numerous binding instances, so this avoids
* allocating a full GPU buffer object for each. */ * allocating a full GPU buffer object for each. */
template <typename UniformStruct> template <typename UniformStruct>
class UniformBufferPool class UniformBufferPool {
{
public: public:
/* Resolve div_t type using ssize_t as basis */ /* Resolve div_t type using ssize_t as basis */
#if _WIN32 #if _WIN32
using IndexTp = SSIZE_T; using IndexTp = SSIZE_T;
#else #else
using IndexTp = ssize_t; using IndexTp = ssize_t;
#endif #endif
private: private:
struct InvalidTp {}; struct InvalidTp {};
using DivTp = std::conditional_t<std::is_same<IndexTp, long long>::value, std::lldiv_t, using DivTp = std::conditional_t<
std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t, std::is_same<IndexTp, long long>::value, std::lldiv_t,
std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>; std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t,
static_assert(!std::is_same<DivTp, InvalidTp>::value, "unsupported IndexTp for DivTp resolution"); std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>;
static_assert(!std::is_same<DivTp, InvalidTp>::value, "unsupported IndexTp for DivTp resolution");
/** Size of single element, rounded up to 256-multiple */ /** Size of single element, rounded up to 256-multiple */
static constexpr IndexTp m_stride = ROUND_UP_256(sizeof(UniformStruct)); static constexpr IndexTp m_stride = ROUND_UP_256(sizeof(UniformStruct));
static_assert(m_stride <= HECL_UBUFPOOL_ALLOCATION_BLOCK, "Stride too large for uniform pool"); static_assert(m_stride <= HECL_UBUFPOOL_ALLOCATION_BLOCK, "Stride too large for uniform pool");
/** Number of rounded elements per 256K bucket */ /** Number of rounded elements per 256K bucket */
static constexpr IndexTp m_countPerBucket = HECL_UBUFPOOL_ALLOCATION_BLOCK / m_stride; static constexpr IndexTp m_countPerBucket = HECL_UBUFPOOL_ALLOCATION_BLOCK / m_stride;
/** Buffer size per bucket (ideally 256K) */ /** Buffer size per bucket (ideally 256K) */
static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket;
/** BitVector indicating free allocation blocks */ /** BitVector indicating free allocation blocks */
hecl::llvm::BitVector m_freeBlocks; hecl::llvm::BitVector m_freeBlocks;
/** Efficient way to get bucket and block simultaneously */ /** Efficient way to get bucket and block simultaneously */
DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); }
/** Factory pointer for building additional buffers */ /** Factory pointer for building additional buffers */
boo::IGraphicsDataFactory* m_factory = nullptr; boo::IGraphicsDataFactory* m_factory = nullptr;
/** Private bucket info */ /** Private bucket info */
struct Bucket struct Bucket {
{ boo::ObjToken<boo::IGraphicsBufferD> buffer;
boo::ObjToken<boo::IGraphicsBufferD> buffer; uint8_t* cpuBuffer = nullptr;
uint8_t* cpuBuffer = nullptr; std::atomic_size_t useCount = {};
std::atomic_size_t useCount = {}; bool dirty = false;
bool dirty = false; Bucket() = default;
Bucket() = default; Bucket(const Bucket& other) = delete;
Bucket(const Bucket& other) = delete; Bucket& operator=(const Bucket& other) = delete;
Bucket& operator=(const Bucket& other) = delete; Bucket(Bucket&& other) = default;
Bucket(Bucket&& other) = default; Bucket& operator=(Bucket&& other) = default;
Bucket& operator=(Bucket&& other) = default;
void updateBuffer() void updateBuffer() {
{ if (cpuBuffer) {
if (cpuBuffer) buffer->unmap();
{ cpuBuffer = nullptr;
buffer->unmap(); }
cpuBuffer = nullptr; dirty = false;
} }
dirty = false;
void increment(UniformBufferPool& pool) {
if (useCount.fetch_add(1) == 0)
buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Uniform, pool.m_stride, pool.m_countPerBucket BooTrace);
}
void decrement(UniformBufferPool& pool) {
if (useCount.fetch_sub(1) == 1) {
if (cpuBuffer) {
buffer->unmap();
cpuBuffer = nullptr;
} }
buffer.reset();
void increment(UniformBufferPool& pool) }
{ }
if (useCount.fetch_add(1) == 0) };
buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Uniform, std::vector<std::unique_ptr<Bucket>> m_buckets;
pool.m_stride, pool.m_countPerBucket BooTrace);
}
void decrement(UniformBufferPool& pool)
{
if (useCount.fetch_sub(1) == 1)
{
if (cpuBuffer)
{
buffer->unmap();
cpuBuffer = nullptr;
}
buffer.reset();
}
}
};
std::vector<std::unique_ptr<Bucket>> m_buckets;
public: public:
/** User block-owning token */ /** User block-owning token */
class Token class Token {
{ friend class UniformBufferPool;
friend class UniformBufferPool; UniformBufferPool* m_pool = nullptr;
UniformBufferPool* m_pool = nullptr; IndexTp m_index = -1;
IndexTp m_index = -1; DivTp m_div;
DivTp m_div; Token(UniformBufferPool* pool) : m_pool(pool) {
Token(UniformBufferPool* pool) auto& freeSpaces = pool->m_freeBlocks;
: m_pool(pool) int idx = freeSpaces.find_first();
{ if (idx == -1) {
auto& freeSpaces = pool->m_freeBlocks; pool->m_buckets.push_back(std::make_unique<Bucket>());
int idx = freeSpaces.find_first(); m_index = freeSpaces.size();
if (idx == -1) freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true);
{ } else {
pool->m_buckets.push_back(std::make_unique<Bucket>()); m_index = idx;
m_index = freeSpaces.size(); }
freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true); freeSpaces.reset(m_index);
} m_div = pool->getBucketDiv(m_index);
else
{
m_index = idx;
}
freeSpaces.reset(m_index);
m_div = pool->getBucketDiv(m_index);
Bucket& bucket = *m_pool->m_buckets[m_div.quot]; Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.increment(*m_pool); bucket.increment(*m_pool);
}
public:
Token() = default;
Token(const Token& other) = delete;
Token& operator=(const Token& other) = delete;
Token& operator=(Token&& other)
{
m_pool = other.m_pool;
m_index = other.m_index;
m_div = other.m_div;
other.m_index = -1;
return *this;
}
Token(Token&& other)
: m_pool(other.m_pool), m_index(other.m_index),
m_div(other.m_div)
{
other.m_index = -1;
}
~Token()
{
if (m_index != -1)
{
m_pool->m_freeBlocks.set(m_index);
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.decrement(*m_pool);
}
}
UniformStruct& access()
{
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
if (!bucket.cpuBuffer)
bucket.cpuBuffer = reinterpret_cast<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<UniformStruct&>(bucket.cpuBuffer[m_div.rem * m_pool->m_stride]);
}
std::pair<boo::ObjToken<boo::IGraphicsBufferD>, IndexTp> getBufferInfo() const
{
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
return {bucket.buffer, m_div.rem * m_pool->m_stride};
}
operator bool() const { return m_pool != nullptr && m_index != -1; }
};
UniformBufferPool() = default;
UniformBufferPool(const UniformBufferPool& other) = delete;
UniformBufferPool& operator=(const UniformBufferPool& other) = delete;
/** Load dirty buffer data into GPU */
void updateBuffers()
{
for (auto& bucket : m_buckets)
if (bucket->dirty)
bucket->updateBuffer();
} }
/** Allocate free block into client-owned Token */ public:
Token allocateBlock(boo::IGraphicsDataFactory* factory) Token() = default;
{ Token(const Token& other) = delete;
m_factory = factory; Token& operator=(const Token& other) = delete;
return Token(this); Token& operator=(Token&& other) {
m_pool = other.m_pool;
m_index = other.m_index;
m_div = other.m_div;
other.m_index = -1;
return *this;
}
Token(Token&& other) : m_pool(other.m_pool), m_index(other.m_index), m_div(other.m_div) { other.m_index = -1; }
~Token() {
if (m_index != -1) {
m_pool->m_freeBlocks.set(m_index);
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.decrement(*m_pool);
}
} }
void doDestroy() UniformStruct& access() {
{ Bucket& bucket = *m_pool->m_buckets[m_div.quot];
for (auto& bucket : m_buckets) if (!bucket.cpuBuffer)
bucket->buffer.reset(); bucket.cpuBuffer = reinterpret_cast<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<UniformStruct&>(bucket.cpuBuffer[m_div.rem * m_pool->m_stride]);
} }
std::pair<boo::ObjToken<boo::IGraphicsBufferD>, IndexTp> getBufferInfo() const {
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
return {bucket.buffer, m_div.rem * m_pool->m_stride};
}
operator bool() const { return m_pool != nullptr && m_index != -1; }
};
UniformBufferPool() = default;
UniformBufferPool(const UniformBufferPool& other) = delete;
UniformBufferPool& operator=(const UniformBufferPool& other) = delete;
/** Load dirty buffer data into GPU */
void updateBuffers() {
for (auto& bucket : m_buckets)
if (bucket->dirty)
bucket->updateBuffer();
}
/** Allocate free block into client-owned Token */
Token allocateBlock(boo::IGraphicsDataFactory* factory) {
m_factory = factory;
return Token(this);
}
void doDestroy() {
for (auto& bucket : m_buckets)
bucket->buffer.reset();
}
}; };
} } // namespace hecl

View File

@ -6,8 +6,7 @@
#include <atomic> #include <atomic>
#include "BitVector.hpp" #include "BitVector.hpp"
namespace hecl namespace hecl {
{
#define HECL_VBUFPOOL_ALLOCATION_BLOCK 262144 #define HECL_VBUFPOOL_ALLOCATION_BLOCK 262144
@ -18,192 +17,167 @@ namespace hecl
* widgets. These can potentially have numerous binding instances, so this avoids * widgets. These can potentially have numerous binding instances, so this avoids
* allocating a full GPU buffer object for each. */ * allocating a full GPU buffer object for each. */
template <typename VertStruct> template <typename VertStruct>
class VertexBufferPool class VertexBufferPool {
{
public: public:
/* Resolve div_t type using ssize_t as basis */ /* Resolve div_t type using ssize_t as basis */
#if _WIN32 #if _WIN32
using IndexTp = SSIZE_T; using IndexTp = SSIZE_T;
#else #else
using IndexTp = ssize_t; using IndexTp = ssize_t;
#endif #endif
private: private:
struct InvalidTp {}; struct InvalidTp {};
using DivTp = std::conditional_t<std::is_same<IndexTp, long long>::value, std::lldiv_t, using DivTp = std::conditional_t<
std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t, std::is_same<IndexTp, long long>::value, std::lldiv_t,
std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>; std::conditional_t<std::is_same<IndexTp, long>::value, std::ldiv_t,
static_assert(!std::is_same<DivTp, InvalidTp>::value, "unsupported IndexTp for DivTp resolution"); std::conditional_t<std::is_same<IndexTp, int>::value, std::div_t, InvalidTp>>>;
static_assert(!std::is_same<DivTp, InvalidTp>::value, "unsupported IndexTp for DivTp resolution");
/** Size of single element */ /** Size of single element */
static constexpr IndexTp m_stride = sizeof(VertStruct); static constexpr IndexTp m_stride = sizeof(VertStruct);
static_assert(m_stride <= HECL_VBUFPOOL_ALLOCATION_BLOCK, "Stride too large for vertex pool"); static_assert(m_stride <= HECL_VBUFPOOL_ALLOCATION_BLOCK, "Stride too large for vertex pool");
/** Number of elements per 256K bucket */ /** Number of elements per 256K bucket */
static constexpr IndexTp m_countPerBucket = HECL_VBUFPOOL_ALLOCATION_BLOCK / m_stride; static constexpr IndexTp m_countPerBucket = HECL_VBUFPOOL_ALLOCATION_BLOCK / m_stride;
/** Buffer size per bucket (ideally 256K) */ /** Buffer size per bucket (ideally 256K) */
static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket; static constexpr IndexTp m_sizePerBucket = m_stride * m_countPerBucket;
/** BitVector indicating free allocation elements */ /** BitVector indicating free allocation elements */
hecl::llvm::BitVector m_freeElements; hecl::llvm::BitVector m_freeElements;
/** Efficient way to get bucket and element simultaneously */ /** Efficient way to get bucket and element simultaneously */
DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); } DivTp getBucketDiv(IndexTp idx) const { return std::div(idx, m_countPerBucket); }
/** Factory pointer for building additional buffers */ /** Factory pointer for building additional buffers */
boo::IGraphicsDataFactory* m_factory = nullptr; boo::IGraphicsDataFactory* m_factory = nullptr;
/** Private bucket info */ /** Private bucket info */
struct Bucket struct Bucket {
{ boo::ObjToken<boo::IGraphicsBufferD> buffer;
boo::ObjToken<boo::IGraphicsBufferD> buffer; uint8_t* cpuBuffer = nullptr;
uint8_t* cpuBuffer = nullptr; std::atomic_size_t useCount = {};
std::atomic_size_t useCount = {}; bool dirty = false;
bool dirty = false; Bucket() = default;
Bucket() = default; Bucket(const Bucket& other) = delete;
Bucket(const Bucket& other) = delete; Bucket& operator=(const Bucket& other) = delete;
Bucket& operator=(const Bucket& other) = delete; Bucket(Bucket&& other) = delete;
Bucket(Bucket&& other) = delete; Bucket& operator=(Bucket&& other) = delete;
Bucket& operator=(Bucket&& other) = delete;
void updateBuffer() void updateBuffer() {
{ if (cpuBuffer) {
if (cpuBuffer) buffer->unmap();
{ cpuBuffer = nullptr;
buffer->unmap(); }
cpuBuffer = nullptr; dirty = false;
} }
dirty = false;
void increment(VertexBufferPool& pool) {
if (useCount.fetch_add(1) == 0)
buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Vertex, pool.m_stride, pool.m_countPerBucket BooTrace);
}
void decrement(VertexBufferPool& pool) {
if (useCount.fetch_sub(1) == 1) {
if (cpuBuffer) {
buffer->unmap();
cpuBuffer = nullptr;
} }
buffer.reset();
void increment(VertexBufferPool& pool) }
{ }
if (useCount.fetch_add(1) == 0) };
buffer = pool.m_factory->newPoolBuffer(boo::BufferUse::Vertex, std::vector<std::unique_ptr<Bucket>> m_buckets;
pool.m_stride, pool.m_countPerBucket BooTrace);
}
void decrement(VertexBufferPool& pool)
{
if (useCount.fetch_sub(1) == 1)
{
if (cpuBuffer)
{
buffer->unmap();
cpuBuffer = nullptr;
}
buffer.reset();
}
}
};
std::vector<std::unique_ptr<Bucket>> m_buckets;
public: public:
/** User element-owning token */ /** User element-owning token */
class Token class Token {
{ friend class VertexBufferPool;
friend class VertexBufferPool; VertexBufferPool* m_pool = nullptr;
VertexBufferPool* m_pool = nullptr; IndexTp m_index = -1;
IndexTp m_index = -1; IndexTp m_count = 0;
IndexTp m_count = 0; DivTp m_div;
DivTp m_div; Token(VertexBufferPool* pool, IndexTp count) : m_pool(pool), m_count(count) {
Token(VertexBufferPool* pool, IndexTp count) assert(count <= pool->m_countPerBucket && "unable to fit in bucket");
: m_pool(pool), m_count(count) auto& freeSpaces = pool->m_freeElements;
{ int idx = freeSpaces.find_first_contiguous(count, pool->m_countPerBucket);
assert(count <= pool->m_countPerBucket && "unable to fit in bucket"); if (idx == -1) {
auto& freeSpaces = pool->m_freeElements; pool->m_buckets.push_back(std::make_unique<Bucket>());
int idx = freeSpaces.find_first_contiguous(count, pool->m_countPerBucket); m_index = freeSpaces.size();
if (idx == -1) freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true);
{ } else {
pool->m_buckets.push_back(std::make_unique<Bucket>()); m_index = idx;
m_index = freeSpaces.size(); }
freeSpaces.resize(freeSpaces.size() + pool->m_countPerBucket, true); freeSpaces.reset(m_index, m_index + count);
} m_div = pool->getBucketDiv(m_index);
else
{
m_index = idx;
}
freeSpaces.reset(m_index, m_index + count);
m_div = pool->getBucketDiv(m_index);
Bucket& bucket = *m_pool->m_buckets[m_div.quot]; Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.increment(*pool); bucket.increment(*pool);
}
public:
Token() = default;
Token(const Token& other) = delete;
Token& operator=(const Token& other) = delete;
Token& operator=(Token&& other)
{
m_pool = other.m_pool;
m_index = other.m_index;
m_count = other.m_count;
m_div = other.m_div;
other.m_index = -1;
return *this;
}
Token(Token&& other)
: m_pool(other.m_pool), m_index(other.m_index),
m_count(other.m_count), m_div(other.m_div)
{
other.m_index = -1;
}
~Token()
{
if (m_index != -1)
{
m_pool->m_freeElements.set(m_index, m_index + m_count);
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.decrement(*m_pool);
}
}
VertStruct* access()
{
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
if (!bucket.cpuBuffer)
bucket.cpuBuffer = reinterpret_cast<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<VertStruct*>(&bucket.cpuBuffer[m_div.rem * m_pool->m_stride]);
}
std::pair<boo::ObjToken<boo::IGraphicsBufferD>, IndexTp> getBufferInfo() const
{
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
return {bucket.buffer, m_div.rem};
}
operator bool() const { return m_pool != nullptr && m_index != -1; }
};
VertexBufferPool() = default;
VertexBufferPool(const VertexBufferPool& other) = delete;
VertexBufferPool& operator=(const VertexBufferPool& other) = delete;
/** Load dirty buffer data into GPU */
void updateBuffers()
{
for (auto& bucket : m_buckets)
if (bucket->dirty)
bucket->updateBuffer();
} }
/** Allocate free block into client-owned Token */ public:
Token allocateBlock(boo::IGraphicsDataFactory* factory, IndexTp count) Token() = default;
{ Token(const Token& other) = delete;
m_factory = factory; Token& operator=(const Token& other) = delete;
return Token(this, count); Token& operator=(Token&& other) {
m_pool = other.m_pool;
m_index = other.m_index;
m_count = other.m_count;
m_div = other.m_div;
other.m_index = -1;
return *this;
}
Token(Token&& other) : m_pool(other.m_pool), m_index(other.m_index), m_count(other.m_count), m_div(other.m_div) {
other.m_index = -1;
} }
void doDestroy() ~Token() {
{ if (m_index != -1) {
for (auto& bucket : m_buckets) m_pool->m_freeElements.set(m_index, m_index + m_count);
bucket->buffer.reset(); Bucket& bucket = *m_pool->m_buckets[m_div.quot];
bucket.decrement(*m_pool);
}
} }
static constexpr IndexTp bucketCapacity() { return m_countPerBucket; } VertStruct* access() {
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
if (!bucket.cpuBuffer)
bucket.cpuBuffer = reinterpret_cast<uint8_t*>(bucket.buffer->map(m_sizePerBucket));
bucket.dirty = true;
return reinterpret_cast<VertStruct*>(&bucket.cpuBuffer[m_div.rem * m_pool->m_stride]);
}
std::pair<boo::ObjToken<boo::IGraphicsBufferD>, IndexTp> getBufferInfo() const {
Bucket& bucket = *m_pool->m_buckets[m_div.quot];
return {bucket.buffer, m_div.rem};
}
operator bool() const { return m_pool != nullptr && m_index != -1; }
};
VertexBufferPool() = default;
VertexBufferPool(const VertexBufferPool& other) = delete;
VertexBufferPool& operator=(const VertexBufferPool& other) = delete;
/** Load dirty buffer data into GPU */
void updateBuffers() {
for (auto& bucket : m_buckets)
if (bucket->dirty)
bucket->updateBuffer();
}
/** Allocate free block into client-owned Token */
Token allocateBlock(boo::IGraphicsDataFactory* factory, IndexTp count) {
m_factory = factory;
return Token(this, count);
}
void doDestroy() {
for (auto& bucket : m_buckets)
bucket->buffer.reset();
}
static constexpr IndexTp bucketCapacity() { return m_countPerBucket; }
}; };
} } // namespace hecl

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,5 @@
#endif #endif
#include "windows.h" #include "windows.h"
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen); void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen);
int asprintf(char** buf, const char* format, ...); int asprintf(char** buf, const char* format, ...);

View File

@ -6,389 +6,356 @@
static logvisor::Module Log("hecl::Backend::GLSL"); static logvisor::Module Log("hecl::Backend::GLSL");
namespace hecl::Backend namespace hecl::Backend {
{
std::string GLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const std::string GLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "objPos.xy";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "objPos.xy"; return "objNorm.xy";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "objNorm.xy"; return hecl::Format("uvIn[%u]", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("uvIn[%u]", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string GLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const std::string GLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "vec4(objPos.xyz, 1.0)";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "vec4(objPos.xyz, 1.0)"; return "vec4(objNorm.xyz, 1.0)";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "vec4(objNorm.xyz, 1.0)"; return hecl::Format("vec4(uvIn[%u], 0.0, 1.0)", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("vec4(uvIn[%u], 0.0, 1.0)", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string GLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const std::string GLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
{ std::string retval =
std::string retval = "layout(location=0) in vec3 posIn;\n"
"layout(location=0) in vec3 posIn;\n" "layout(location=1) in vec3 normIn;\n";
"layout(location=1) in vec3 normIn;\n";
unsigned idx = 2; unsigned idx = 2;
if (col) if (col) {
{ retval += hecl::Format("layout(location=%u) in vec4 colIn[%u];\n", idx, col);
retval += hecl::Format("layout(location=%u) in vec4 colIn[%u];\n", idx, col); idx += col;
idx += col; }
}
if (uv) if (uv) {
{ retval += hecl::Format("layout(location=%u) in vec2 uvIn[%u];\n", idx, uv);
retval += hecl::Format("layout(location=%u) in vec2 uvIn[%u];\n", idx, uv); idx += uv;
idx += uv; }
}
if (w) if (w) {
{ retval += hecl::Format("layout(location=%u) in vec4 weightIn[%u];\n", idx, w);
retval += hecl::Format("layout(location=%u) in vec4 weightIn[%u];\n", idx, w); }
}
return retval; return retval;
} }
std::string GLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const std::string GLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
{ std::string retval =
std::string retval = "struct VertToFrag\n"
"struct VertToFrag\n" "{\n"
"{\n" " vec4 mvPos;\n"
" vec4 mvPos;\n" " vec4 mvNorm;\n";
" vec4 mvNorm;\n";
if (m_tcgs.size()) if (m_tcgs.size())
retval += hecl::Format(" vec2 tcgs[%u];\n", unsigned(m_tcgs.size())); retval += hecl::Format(" vec2 tcgs[%u];\n", unsigned(m_tcgs.size()));
if (extTexCount) if (extTexCount)
retval += hecl::Format(" vec2 extTcgs[%u];\n", unsigned(extTexCount)); retval += hecl::Format(" vec2 extTcgs[%u];\n", unsigned(extTexCount));
if (reflectionCoords) if (reflectionCoords)
retval += " vec2 reflectTcgs[2];\n" retval +=
" float reflectAlpha;\n"; " vec2 reflectTcgs[2];\n"
" float reflectAlpha;\n";
return retval + "};\n"; return retval + "};\n";
} }
std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const {
{ std::string retval;
std::string retval; if (skinSlots == 0) {
if (skinSlots == 0) retval =
{ "UBINDING0 uniform HECLVertUniform\n"
retval = "UBINDING0 uniform HECLVertUniform\n" "{\n"
"{\n" " mat4 mv;\n"
" mat4 mv;\n" " mat4 mvInv;\n"
" mat4 mvInv;\n" " mat4 proj;\n"
" mat4 proj;\n" "};\n";
"};\n"; } else {
} retval = hecl::Format(
"UBINDING0 uniform HECLVertUniform\n"
"{\n"
" mat4 objs[%u];\n"
" mat4 objsInv[%u];\n"
" mat4 mv;\n"
" mat4 mvInv;\n"
" mat4 proj;\n"
"};\n",
skinSlots, skinSlots);
}
retval +=
"struct HECLTCGMatrix\n"
"{\n"
" mat4 mtx;\n"
" mat4 postMtx;\n"
"};\n"
"UBINDING1 uniform HECLTexMtxUniform\n"
"{\n"
" HECLTCGMatrix texMtxs[8];\n"
"};\n";
if (reflectionCoords)
retval +=
"UBINDING3 uniform HECLReflectMtx\n"
"{\n"
" mat4 indMtx;\n"
" mat4 reflectMtx;\n"
" float reflectAlpha;\n"
"};\n"
"\n";
return retval;
}
std::string GLSL::GenerateAlphaTest() const {
return " if (colorOut.a < 0.01)\n"
" {\n"
" discard;\n"
" }\n";
}
std::string GLSL::GenerateReflectionExpr(ReflectionType type) const {
switch (type) {
case ReflectionType::None:
default:
return "vec3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "texture(reflectionTex, vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "texture(reflectionTex, (texture(reflectionIndTex, vtf.reflectTcgs[0]).rg - "
"vec2(0.5, 0.5)) * vec2(0.5, 0.5) + vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
}
}
void GLSL::reset(const IR& ir, Diagnostics& diag) {
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "GLSL");
}
std::string GLSL::makeVert(unsigned col, unsigned uv, unsigned w, unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const {
extTexCount = std::min(int(extTexCount), BOO_GLSL_MAX_TEXTURE_COUNT - int(m_tcgs.size()));
std::string retval = GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s, reflectionType != ReflectionType::None) +
"SBINDING(0) out VertToFrag vtf;\n\n"
"void main()\n{\n";
if (s) {
/* skinned */
retval +=
" vec4 objPos = vec4(0.0,0.0,0.0,0.0);\n"
" vec4 objNorm = vec4(0.0,0.0,0.0,0.0);\n";
for (size_t i = 0; i < s; ++i)
retval += hecl::Format(" objPos += (objs[%" PRISize "] * vec4(posIn, 1.0)) * weightIn[%" PRISize "][%" PRISize
"];\n"
" objNorm += (objsInv[%" PRISize "] * vec4(normIn, 1.0)) * weightIn[%" PRISize
"][%" PRISize "];\n",
i, i / 4, i % 4, i, i / 4, i % 4);
retval +=
" objPos[3] = 1.0;\n"
" objNorm = vec4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = mv * objPos;\n"
" vtf.mvNorm = vec4(normalize((mvInv * objNorm).xyz), 0.0);\n"
" gl_Position = proj * vtf.mvPos;\n";
} else {
/* non-skinned */
retval +=
" vec4 objPos = vec4(posIn, 1.0);\n"
" vec4 objNorm = vec4(normIn, 0.0);\n"
" vtf.mvPos = mv * objPos;\n"
" vtf.mvNorm = mvInv * objNorm;\n"
" gl_Position = proj * vtf.mvPos;\n";
}
retval += " vec4 tmpProj;\n";
int tcgIdx = 0;
for (const TexCoordGen& tcg : m_tcgs) {
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs[%u] = %s;\n", tcgIdx, EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else else
{ retval += hecl::Format(
retval = hecl::Format("UBINDING0 uniform HECLVertUniform\n" " tmpProj = texMtxs[%u].postMtx * vec4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
"{\n" " vtf.tcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
" mat4 objs[%u];\n" tcg.m_mtx, tcg.m_norm ? "normalize" : "", tcg.m_mtx, EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(),
" mat4 objsInv[%u];\n" tcgIdx);
" mat4 mv;\n" ++tcgIdx;
" mat4 mvInv;\n" }
" mat4 proj;\n"
"};\n",
skinSlots, skinSlots);
}
retval += "struct HECLTCGMatrix\n" for (int i = 0; i < extTexCount; ++i) {
"{\n" const TextureInfo& extTex = extTexs[i];
" mat4 mtx;\n" if (extTex.mtxIdx < 0)
" mat4 postMtx;\n" retval += hecl::Format(" vtf.extTcgs[%u] = %s;\n", i, EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
"};\n"
"UBINDING1 uniform HECLTexMtxUniform\n"
"{\n"
" HECLTCGMatrix texMtxs[8];\n"
"};\n";
if (reflectionCoords)
retval += "UBINDING3 uniform HECLReflectMtx\n"
"{\n"
" mat4 indMtx;\n"
" mat4 reflectMtx;\n"
" float reflectAlpha;\n"
"};\n"
"\n";
return retval;
}
std::string GLSL::GenerateAlphaTest() const
{
return " if (colorOut.a < 0.01)\n"
" {\n"
" discard;\n"
" }\n";
}
std::string GLSL::GenerateReflectionExpr(ReflectionType type) const
{
switch (type)
{
case ReflectionType::None:
default:
return "vec3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "texture(reflectionTex, vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "texture(reflectionTex, (texture(reflectionIndTex, vtf.reflectTcgs[0]).rg - "
"vec2(0.5, 0.5)) * vec2(0.5, 0.5) + vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
}
}
void GLSL::reset(const IR& ir, Diagnostics& diag)
{
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "GLSL");
}
std::string GLSL::makeVert(unsigned col, unsigned uv, unsigned w,
unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const
{
extTexCount = std::min(int(extTexCount), BOO_GLSL_MAX_TEXTURE_COUNT - int(m_tcgs.size()));
std::string retval =
GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s, reflectionType != ReflectionType::None) +
"SBINDING(0) out VertToFrag vtf;\n\n"
"void main()\n{\n";
if (s)
{
/* skinned */
retval += " vec4 objPos = vec4(0.0,0.0,0.0,0.0);\n"
" vec4 objNorm = vec4(0.0,0.0,0.0,0.0);\n";
for (size_t i=0 ; i<s ; ++i)
retval += hecl::Format(" objPos += (objs[%" PRISize "] * vec4(posIn, 1.0)) * weightIn[%" PRISize "][%" PRISize "];\n"
" objNorm += (objsInv[%" PRISize "] * vec4(normIn, 1.0)) * weightIn[%" PRISize "][%" PRISize "];\n",
i, i/4, i%4, i, i/4, i%4);
retval += " objPos[3] = 1.0;\n"
" objNorm = vec4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = mv * objPos;\n"
" vtf.mvNorm = vec4(normalize((mvInv * objNorm).xyz), 0.0);\n"
" gl_Position = proj * vtf.mvPos;\n";
}
else else
{ retval += hecl::Format(
/* non-skinned */ " tmpProj = texMtxs[%u].postMtx * vec4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
retval += " vec4 objPos = vec4(posIn, 1.0);\n" " vtf.extTcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
" vec4 objNorm = vec4(normIn, 0.0);\n" extTex.mtxIdx, extTex.normalize ? "normalize" : "", extTex.mtxIdx,
" vtf.mvPos = mv * objPos;\n" EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
" vtf.mvNorm = mvInv * objNorm;\n" }
" gl_Position = proj * vtf.mvPos;\n";
}
retval += " vec4 tmpProj;\n"; if (reflectionType != ReflectionType::None)
retval +=
" vtf.reflectTcgs[0] = normalize((indMtx * vec4(posIn, 1.0)).xz) * vec2(0.5, 0.5) + vec2(0.5, 0.5);\n"
" vtf.reflectTcgs[1] = (reflectMtx * vec4(posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectAlpha;\n";
int tcgIdx = 0; return retval + "}\n";
for (const TexCoordGen& tcg : m_tcgs)
{
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs[%u] = %s;\n", tcgIdx,
EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = texMtxs[%u].postMtx * vec4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
" vtf.tcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
tcg.m_mtx, tcg.m_norm ? "normalize" : "",
tcg.m_mtx, EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(), tcgIdx);
++tcgIdx;
}
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (extTex.mtxIdx < 0)
retval += hecl::Format(" vtf.extTcgs[%u] = %s;\n", i,
EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = texMtxs[%u].postMtx * vec4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
" vtf.extTcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
extTex.mtxIdx, extTex.normalize ? "normalize" : "",
extTex.mtxIdx, EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
}
if (reflectionType != ReflectionType::None)
retval += " vtf.reflectTcgs[0] = normalize((indMtx * vec4(posIn, 1.0)).xz) * vec2(0.5, 0.5) + vec2(0.5, 0.5);\n"
" vtf.reflectTcgs[1] = (reflectMtx * vec4(posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectAlpha;\n";
return retval + "}\n";
} }
std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
const Function& lighting) const std::string lightingSrc;
{ if (!lighting.m_source.empty())
std::string lightingSrc; lightingSrc = lighting.m_source;
if (!lighting.m_source.empty()) else
lightingSrc = lighting.m_source; lightingSrc =
"const vec4 colorReg0 = vec4(1.0);\n"
"const vec4 colorReg1 = vec4(1.0);\n"
"const vec4 colorReg2 = vec4(1.0);\n"
"const vec4 mulColor = vec4(1.0);\n"
"\n";
std::string texMapDecl;
for (unsigned i = 0; i < m_texMapEnd; ++i)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D tex%u;\n", i, i);
if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format(
"TBINDING%u uniform sampler2D reflectionIndTex;\n"
"TBINDING%u uniform sampler2D reflectionTex;\n",
m_texMapEnd, m_texMapEnd + 1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionTex;\n", m_texMapEnd);
std::string retval = std::string("#extension GL_ARB_shader_image_load_store: enable\n") + "#define BLEND_SRC_" +
BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + "#define BLEND_DST_" +
BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) +
(!alphaTest ? "#ifdef GL_ARB_shader_image_load_store\n"
"layout(early_fragment_tests) in;\n"
"#endif\n"
: "") +
"layout(location=0) out vec4 colorOut;\n" + texMapDecl + "SBINDING(0) in VertToFrag vtf;\n\n" +
lightingSrc + "\n" + "void main()\n{\n";
if (m_lighting) {
if (!lighting.m_entry.empty())
retval +=
hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", lighting.m_entry.data());
else else
lightingSrc = "const vec4 colorReg0 = vec4(1.0);\n" retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n";
"const vec4 colorReg1 = vec4(1.0);\n" }
"const vec4 colorReg2 = vec4(1.0);\n"
"const vec4 mulColor = vec4(1.0);\n"
"\n";
std::string texMapDecl; unsigned sampIdx = 0;
for (unsigned i=0 ; i<m_texMapEnd ; ++i) for (const TexSampling& sampling : m_texSamplings)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D tex%u;\n", i, i); retval += hecl::Format(" vec4 sampling%u = texture(tex%u, vtf.tcgs[%u]);\n", sampIdx++, sampling.mapIdx,
if (reflectionType == ReflectionType::Indirect) sampling.tcgIdx);
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionIndTex;\n"
"TBINDING%u uniform sampler2D reflectionTex;\n",
m_texMapEnd, m_texMapEnd+1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionTex;\n",
m_texMapEnd);
std::string retval = std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
std::string("#extension GL_ARB_shader_image_load_store: enable\n") +
"#define BLEND_SRC_" + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) +
(!alphaTest ?
"#ifdef GL_ARB_shader_image_load_store\n"
"layout(early_fragment_tests) in;\n"
"#endif\n" : "") +
"layout(location=0) out vec4 colorOut;\n" +
texMapDecl +
"SBINDING(0) in VertToFrag vtf;\n\n" +
lightingSrc + "\n" +
"void main()\n{\n";
if (m_alphaExpr.size())
retval += " colorOut = vec4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
else
retval += " colorOut = vec4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
if (m_lighting) return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
{
if (!lighting.m_entry.empty())
retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n",
lighting.m_entry.data());
else
retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" vec4 sampling%u = texture(tex%u, vtf.tcgs[%u]);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_alphaExpr.size())
retval += " colorOut = vec4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
else
retval += " colorOut = vec4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
} }
std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
bool alphaTest, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
ReflectionType reflectionType, size_t extTexCount, const TextureInfo* extTexs) const {
BlendFactor srcFactor, BlendFactor dstFactor, std::string lightingSrc;
const Function& lighting, if (!lighting.m_source.empty())
const Function& post, lightingSrc = lighting.m_source;
size_t extTexCount, const TextureInfo* extTexs) const else
{ lightingSrc =
std::string lightingSrc; "const vec4 colorReg0 = vec4(1.0);\n"
if (!lighting.m_source.empty()) "const vec4 colorReg1 = vec4(1.0);\n"
lightingSrc = lighting.m_source; "const vec4 colorReg2 = vec4(1.0);\n"
else "const vec4 mulColor = vec4(1.0);\n"
lightingSrc = "const vec4 colorReg0 = vec4(1.0);\n" "\n";
"const vec4 colorReg1 = vec4(1.0);\n"
"const vec4 colorReg2 = vec4(1.0);\n"
"const vec4 mulColor = vec4(1.0);\n"
"\n";
std::string postSrc; std::string postSrc;
if (!post.m_source.empty()) if (!post.m_source.empty())
postSrc = post.m_source; postSrc = post.m_source;
std::string postEntry; std::string postEntry;
if (!post.m_entry.empty()) if (!post.m_entry.empty())
postEntry = post.m_entry; postEntry = post.m_entry;
std::string texMapDecl; std::string texMapDecl;
for (unsigned i=0 ; i<m_texMapEnd ; ++i) for (unsigned i = 0; i < m_texMapEnd; ++i)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D tex%u;\n", i, i); texMapDecl += hecl::Format("TBINDING%u uniform sampler2D tex%u;\n", i, i);
if (reflectionType == ReflectionType::Indirect) if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionIndTex;\n" texMapDecl += hecl::Format(
"TBINDING%u uniform sampler2D reflectionTex;\n", "TBINDING%u uniform sampler2D reflectionIndTex;\n"
m_texMapEnd, m_texMapEnd+1); "TBINDING%u uniform sampler2D reflectionTex;\n",
else if (reflectionType == ReflectionType::Simple) m_texMapEnd, m_texMapEnd + 1);
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionTex;\n", else if (reflectionType == ReflectionType::Simple)
m_texMapEnd); texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionTex;\n", m_texMapEnd);
uint32_t extTexBits = 0; uint32_t extTexBits = 0;
for (int i=0 ; i<extTexCount ; ++i) for (int i = 0; i < extTexCount; ++i) {
{ const TextureInfo& extTex = extTexs[i];
const TextureInfo& extTex = extTexs[i]; if (!(extTexBits & (1 << extTex.mapIdx))) {
if (!(extTexBits & (1 << extTex.mapIdx))) texMapDecl += hecl::Format("TBINDING%u uniform sampler2D extTex%u;\n", extTex.mapIdx, extTex.mapIdx);
{ extTexBits |= (1 << extTex.mapIdx);
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D extTex%u;\n",
extTex.mapIdx, extTex.mapIdx);
extTexBits |= (1 << extTex.mapIdx);
}
} }
}
std::string retval = std::string retval = std::string("#extension GL_ARB_shader_image_load_store: enable\n") + "#define BLEND_SRC_" +
std::string("#extension GL_ARB_shader_image_load_store: enable\n") + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + "#define BLEND_DST_" +
"#define BLEND_SRC_" + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" + GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + (!alphaTest ? "\n#ifdef GL_ARB_shader_image_load_store\n"
(!alphaTest ? "layout(early_fragment_tests) in;\n"
"\n#ifdef GL_ARB_shader_image_load_store\n" "#endif\n"
"layout(early_fragment_tests) in;\n" : "") +
"#endif\n" : "") + "\nlayout(location=0) out vec4 colorOut;\n" + texMapDecl + "SBINDING(0) in VertToFrag vtf;\n\n" +
"\nlayout(location=0) out vec4 colorOut;\n" + lightingSrc + "\n" + postSrc + "\nvoid main()\n{\n";
texMapDecl +
"SBINDING(0) in VertToFrag vtf;\n\n" +
lightingSrc + "\n" +
postSrc +
"\nvoid main()\n{\n";
if (m_lighting) if (m_lighting) {
{ if (!lighting.m_entry.empty())
if (!lighting.m_entry.empty()) retval +=
retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", lighting.m_entry.data());
lighting.m_entry.data());
else
retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" vec4 sampling%u = texture(tex%u, vtf.tcgs[%u]);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_alphaExpr.size())
retval += " colorOut = " + postEntry + "(vec4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n";
else else
retval += " colorOut = " + postEntry + "(vec4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n"; retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n";
}
return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" vec4 sampling%u = texture(tex%u, vtf.tcgs[%u]);\n", sampIdx++, sampling.mapIdx,
sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_alphaExpr.size())
retval += " colorOut = " + postEntry + "(vec4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr +
")) * mulColor;\n";
else
retval += " colorOut = " + postEntry + "(vec4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
} }
} // namespace hecl::Backend

File diff suppressed because it is too large Load Diff

View File

@ -6,376 +6,351 @@
static logvisor::Module Log("hecl::Backend::HLSL"); static logvisor::Module Log("hecl::Backend::HLSL");
namespace hecl::Backend namespace hecl::Backend {
{
std::string HLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const std::string HLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "objPos.xy\n";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "objPos.xy\n"; return "objNorm.xy\n";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "objNorm.xy\n"; return hecl::Format("v.uvIn[%u]", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("v.uvIn[%u]", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string HLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const std::string HLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "float4(objPos.xyz, 1.0)\n"; return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "float4(objNorm.xyz, 1.0)\n"; return hecl::Format("float4(v.uvIn[%u], 0.0, 1.0)", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("float4(v.uvIn[%u], 0.0, 1.0)", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string HLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const std::string HLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
{ std::string retval =
std::string retval = "struct VertData\n"
"struct VertData\n" "{\n"
"{\n" " float3 posIn : POSITION;\n"
" float3 posIn : POSITION;\n" " float3 normIn : NORMAL;\n";
" float3 normIn : NORMAL;\n";
if (col) if (col)
retval += hecl::Format(" float4 colIn[%u] : COLOR;\n", col); retval += hecl::Format(" float4 colIn[%u] : COLOR;\n", col);
if (uv) if (uv)
retval += hecl::Format(" float2 uvIn[%u] : UV;\n", uv); retval += hecl::Format(" float2 uvIn[%u] : UV;\n", uv);
if (w) if (w)
retval += hecl::Format(" float4 weightIn[%u] : WEIGHT;\n", w); retval += hecl::Format(" float4 weightIn[%u] : WEIGHT;\n", w);
return retval + "};\n"; return retval + "};\n";
} }
std::string HLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const std::string HLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
{ std::string retval =
std::string retval = "struct VertToFrag\n"
"struct VertToFrag\n" "{\n"
"{\n" " float4 mvpPos : SV_Position;\n"
" float4 mvpPos : SV_Position;\n" " float4 mvPos : POSITION;\n"
" float4 mvPos : POSITION;\n" " float4 mvNorm : NORMAL;\n";
" float4 mvNorm : NORMAL;\n";
if (m_tcgs.size()) if (m_tcgs.size())
retval += hecl::Format(" float2 tcgs[%u] : UV;\n", unsigned(m_tcgs.size())); retval += hecl::Format(" float2 tcgs[%u] : UV;\n", unsigned(m_tcgs.size()));
if (extTexCount) if (extTexCount)
retval += hecl::Format(" float2 extTcgs[%u] : EXTUV;\n", unsigned(extTexCount)); retval += hecl::Format(" float2 extTcgs[%u] : EXTUV;\n", unsigned(extTexCount));
if (reflectionCoords) if (reflectionCoords)
retval += " float2 reflectTcgs[2] : REFLECTUV;\n" retval +=
" float reflectAlpha : REFLECTALPHA;\n"; " float2 reflectTcgs[2] : REFLECTUV;\n"
" float reflectAlpha : REFLECTALPHA;\n";
return retval + "};\n"; return retval + "};\n";
} }
std::string HLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const std::string HLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const {
{ std::string retval;
std::string retval; if (skinSlots == 0) {
if (skinSlots == 0) retval =
{ "cbuffer HECLVertUniform : register(b0)\n"
retval = "cbuffer HECLVertUniform : register(b0)\n" "{\n"
"{\n" " float4x4 mv;\n"
" float4x4 mv;\n" " float4x4 mvInv;\n"
" float4x4 mvInv;\n" " float4x4 proj;\n"
" float4x4 proj;\n" "};\n";
"};\n"; } else {
} retval = hecl::Format(
"cbuffer HECLVertUniform : register(b0)\n"
"{\n"
" float4x4 objs[%u];\n"
" float4x4 objsInv[%u];\n"
" float4x4 mv;\n"
" float4x4 mvInv;\n"
" float4x4 proj;\n"
"};\n",
skinSlots, skinSlots);
}
retval +=
"struct TCGMtx\n"
"{\n"
" float4x4 mtx;\n"
" float4x4 postMtx;\n"
"};\n"
"cbuffer HECLTCGMatrix : register(b1)\n"
"{\n"
" TCGMtx texMtxs[8];\n"
"};\n";
if (reflectionCoords)
retval +=
"cbuffer HECLReflectMtx : register(b3)\n"
"{\n"
" float4x4 indMtx;\n"
" float4x4 reflectMtx;\n"
" float reflectAlpha;\n"
"};\n"
"\n";
return retval;
}
std::string HLSL::GenerateAlphaTest() const {
return " if (colorOut.a < 0.01)\n"
" {\n"
" discard;\n"
" }\n";
}
std::string HLSL::GenerateReflectionExpr(ReflectionType type) const {
switch (type) {
case ReflectionType::None:
default:
return "float3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "reflectionTex.Sample(reflectSamp, vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "reflectionTex.Sample(reflectSamp, (reflectionIndTex.Sample(samp, vtf.reflectTcgs[0]).rg - "
"float2(0.5, 0.5)) * float2(0.5, 0.5) + vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
}
}
void HLSL::reset(const IR& ir, Diagnostics& diag) {
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "HLSL");
}
std::string HLSL::makeVert(unsigned col, unsigned uv, unsigned w, unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const {
std::string retval = GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s, reflectionType != ReflectionType::None) + "\n" +
"VertToFrag main(in VertData v)\n"
"{\n"
" VertToFrag vtf;\n";
if (s) {
/* skinned */
retval +=
" float4 objPos = float4(0.0,0.0,0.0,0.0);\n"
" float4 objNorm = float4(0.0,0.0,0.0,0.0);\n";
for (size_t i = 0; i < s; ++i)
retval += hecl::Format(
" objPos += mul(mv[%" PRISize "], float4(v.posIn, 1.0)) * v.weightIn[%" PRISize "][%" PRISize
"];\n"
" objNorm += mul(mvInv[%" PRISize "], float4(v.normIn, 1.0)) * v.weightIn[%" PRISize "][%" PRISize "];\n",
i, i / 4, i % 4, i, i / 4, i % 4);
retval +=
" objPos[3] = 1.0;\n"
" objNorm = float4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = mul(mv, objPos);\n"
" vtf.mvNorm = float4(normalize(mul(mvInv, objNorm).xyz), 0.0);\n"
" vtf.mvpPos = mul(proj, vtf.mvPos);\n";
} else {
/* non-skinned */
retval +=
" float4 objPos = float4(posIn, 1.0);\n"
" float4 objNorm = float4(normIn, 0.0);\n"
" vtf.mvPos = mul(mv, objPos);\n"
" vtf.mvNorm = mul(mvInv, objNorm);\n"
" gl_Position = mul(proj, vtf.mvPos);\n";
}
retval += " float4 tmpProj;\n";
int tcgIdx = 0;
for (const TexCoordGen& tcg : m_tcgs) {
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs[%u] = %s;\n", tcgIdx, EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else else
{ retval += hecl::Format(
retval = hecl::Format("cbuffer HECLVertUniform : register(b0)\n" " tmpProj = mul(texMtxs[%u].postMtx, float4(%s(mul(texMtxs[%u].mtx, %s).xyz), 1.0));\n"
"{\n" " vtf.tcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
" float4x4 objs[%u];\n" tcg.m_mtx, tcg.m_norm ? "normalize" : "", tcg.m_mtx, EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(),
" float4x4 objsInv[%u];\n" tcgIdx);
" float4x4 mv;\n" ++tcgIdx;
" float4x4 mvInv;\n" }
" float4x4 proj;\n"
"};\n",
skinSlots, skinSlots);
}
retval += "struct TCGMtx\n"
"{\n"
" float4x4 mtx;\n"
" float4x4 postMtx;\n"
"};\n"
"cbuffer HECLTCGMatrix : register(b1)\n"
"{\n"
" TCGMtx texMtxs[8];\n"
"};\n";
if (reflectionCoords) for (int i = 0; i < extTexCount; ++i) {
retval += "cbuffer HECLReflectMtx : register(b3)\n" const TextureInfo& extTex = extTexs[i];
"{\n" if (extTex.mtxIdx < 0)
" float4x4 indMtx;\n" retval += hecl::Format(" vtf.extTcgs[%u] = %s;\n", i, EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
" float4x4 reflectMtx;\n"
" float reflectAlpha;\n"
"};\n"
"\n";
return retval;
}
std::string HLSL::GenerateAlphaTest() const
{
return " if (colorOut.a < 0.01)\n"
" {\n"
" discard;\n"
" }\n";
}
std::string HLSL::GenerateReflectionExpr(ReflectionType type) const
{
switch (type)
{
case ReflectionType::None:
default:
return "float3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "reflectionTex.Sample(reflectSamp, vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "reflectionTex.Sample(reflectSamp, (reflectionIndTex.Sample(samp, vtf.reflectTcgs[0]).rg - "
"float2(0.5, 0.5)) * float2(0.5, 0.5) + vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha";
}
}
void HLSL::reset(const IR& ir, Diagnostics& diag)
{
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "HLSL");
}
std::string HLSL::makeVert(unsigned col, unsigned uv, unsigned w,
unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const
{
std::string retval =
GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s, reflectionType != ReflectionType::None) + "\n" +
"VertToFrag main(in VertData v)\n"
"{\n"
" VertToFrag vtf;\n";
if (s)
{
/* skinned */
retval += " float4 objPos = float4(0.0,0.0,0.0,0.0);\n"
" float4 objNorm = float4(0.0,0.0,0.0,0.0);\n";
for (size_t i=0 ; i<s ; ++i)
retval += hecl::Format(" objPos += mul(mv[%" PRISize "], float4(v.posIn, 1.0)) * v.weightIn[%" PRISize "][%" PRISize "];\n"
" objNorm += mul(mvInv[%" PRISize "], float4(v.normIn, 1.0)) * v.weightIn[%" PRISize "][%" PRISize "];\n",
i, i/4, i%4, i, i/4, i%4);
retval += " objPos[3] = 1.0;\n"
" objNorm = float4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = mul(mv, objPos);\n"
" vtf.mvNorm = float4(normalize(mul(mvInv, objNorm).xyz), 0.0);\n"
" vtf.mvpPos = mul(proj, vtf.mvPos);\n";
}
else else
{ retval += hecl::Format(
/* non-skinned */ " tmpProj = mul(texMtxs[%u].postMtx, float4(%s(mul(texMtxs[%u].mtx, %s).xyz), 1.0));\n"
retval += " float4 objPos = float4(posIn, 1.0);\n" " vtf.extTcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
" float4 objNorm = float4(normIn, 0.0);\n" extTex.mtxIdx, extTex.normalize ? "normalize" : "", extTex.mtxIdx,
" vtf.mvPos = mul(mv, objPos);\n" EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
" vtf.mvNorm = mul(mvInv, objNorm);\n" }
" gl_Position = mul(proj, vtf.mvPos);\n";
}
retval += " float4 tmpProj;\n"; if (reflectionType != ReflectionType::None)
retval +=
" vtf.reflectTcgs[0] = normalize(mul(indMtx, float4(v.posIn, 1.0)).xz) * float2(0.5, 0.5) + float2(0.5, "
"0.5);\n"
" vtf.reflectTcgs[1] = mul(reflectMtx, float4(v.posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectAlpha;\n";
int tcgIdx = 0; return retval +
for (const TexCoordGen& tcg : m_tcgs) " return vtf;\n"
{ "}\n";
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs[%u] = %s;\n", tcgIdx,
EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = mul(texMtxs[%u].postMtx, float4(%s(mul(texMtxs[%u].mtx, %s).xyz), 1.0));\n"
" vtf.tcgs[%u] = (tmpProj / tmpProj.w).xy;\n", tcg.m_mtx,
tcg.m_norm ? "normalize" : "", tcg.m_mtx,
EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(), tcgIdx);
++tcgIdx;
}
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (extTex.mtxIdx < 0)
retval += hecl::Format(" vtf.extTcgs[%u] = %s;\n", i,
EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = mul(texMtxs[%u].postMtx, float4(%s(mul(texMtxs[%u].mtx, %s).xyz), 1.0));\n"
" vtf.extTcgs[%u] = (tmpProj / tmpProj.w).xy;\n",
extTex.mtxIdx, extTex.normalize ? "normalize" : "", extTex.mtxIdx,
EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
}
if (reflectionType != ReflectionType::None)
retval += " vtf.reflectTcgs[0] = normalize(mul(indMtx, float4(v.posIn, 1.0)).xz) * float2(0.5, 0.5) + float2(0.5, 0.5);\n"
" vtf.reflectTcgs[1] = mul(reflectMtx, float4(v.posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectAlpha;\n";
return retval + " return vtf;\n"
"}\n";
} }
std::string HLSL::makeFrag(size_t blockCount, const char** blockNames, std::string HLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
bool alphaTest, ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
BlendFactor srcFactor, BlendFactor dstFactor, std::string lightingSrc;
const Function& lighting) const if (!lighting.m_source.empty())
{ lightingSrc = lighting.m_source;
std::string lightingSrc; else
if (!lighting.m_source.empty()) lightingSrc =
lightingSrc = lighting.m_source; "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
std::string texMapDecl;
if (m_texMapEnd)
texMapDecl = hecl::Format("Texture2D texs[%u] : register(t0);\n", m_texMapEnd);
if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format(
"Texture2D reflectionIndTex : register(t%u);\n"
"Texture2D reflectionTex : register(t%u);\n",
m_texMapEnd, m_texMapEnd + 1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format("Texture2D reflectionTex : register(t%u);\n", m_texMapEnd);
std::string retval = std::string("#define BLEND_SRC_") + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
"SamplerState samp : register(s0);\n"
"SamplerState clampSamp : register(s1);\n"
"SamplerState reflectSamp : register(s2);\n" +
GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) + texMapDecl + "\n" +
lightingSrc + "\n" + (!alphaTest ? "\n[earlydepthstencil]\n" : "\n") +
"float4 main(in VertToFrag vtf) : SV_Target0\n{\n";
if (m_lighting) {
if (!lighting.m_entry.empty())
retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n",
lighting.m_entry.data());
else else
lightingSrc = "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
"static const float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n" }
"static const float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
std::string texMapDecl; unsigned sampIdx = 0;
if (m_texMapEnd) for (const TexSampling& sampling : m_texSamplings)
texMapDecl = hecl::Format("Texture2D texs[%u] : register(t0);\n", m_texMapEnd); retval += hecl::Format(" float4 sampling%u = texs[%u].Sample(samp, vtf.tcgs[%u]);\n", sampIdx++, sampling.mapIdx,
if (reflectionType == ReflectionType::Indirect) sampling.tcgIdx);
texMapDecl += hecl::Format("Texture2D reflectionIndTex : register(t%u);\n"
"Texture2D reflectionTex : register(t%u);\n",
m_texMapEnd, m_texMapEnd+1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format("Texture2D reflectionTex : register(t%u);\n",
m_texMapEnd);
std::string retval =
std::string("#define BLEND_SRC_") + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
"SamplerState samp : register(s0);\n"
"SamplerState clampSamp : register(s1);\n"
"SamplerState reflectSamp : register(s2);\n" +
GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) +
texMapDecl + "\n" +
lightingSrc + "\n" +
(!alphaTest ? "\n[earlydepthstencil]\n" : "\n") +
"float4 main(in VertToFrag vtf) : SV_Target0\n{\n";
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_lighting) retval += " float4 colorOut;\n";
{ if (m_alphaExpr.size())
if (!lighting.m_entry.empty()) retval += " colorOut = float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", else
lighting.m_entry.data()); retval += " colorOut = float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
else
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0; return retval + (alphaTest ? GenerateAlphaTest() : "") + " return colorOut;\n}\n";
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = texs[%u].Sample(samp, vtf.tcgs[%u]);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
retval += " float4 colorOut;\n";
if (m_alphaExpr.size())
retval += " colorOut = float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
else
retval += " colorOut = float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") + " return colorOut;\n}\n";
} }
std::string HLSL::makeFrag(size_t blockCount, const char** blockNames, std::string HLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
bool alphaTest, ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting, const Function& post,
BlendFactor srcFactor, BlendFactor dstFactor, size_t extTexCount, const TextureInfo* extTexs) const {
const Function& lighting, std::string lightingSrc;
const Function& post, size_t extTexCount, if (!lighting.m_source.empty())
const TextureInfo* extTexs) const lightingSrc = lighting.m_source;
{ else
std::string lightingSrc; lightingSrc =
if (!lighting.m_source.empty()) "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n"
lightingSrc = lighting.m_source; "static const float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
else "static const float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
lightingSrc = "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" "static const float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
"static const float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
"static const float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
std::string postSrc; std::string postSrc;
if (!post.m_source.empty()) if (!post.m_source.empty())
postSrc = post.m_source; postSrc = post.m_source;
std::string postEntry; std::string postEntry;
if (!post.m_entry.empty()) if (!post.m_entry.empty())
postEntry = post.m_entry; postEntry = post.m_entry;
std::string texMapDecl; std::string texMapDecl;
if (m_texMapEnd) if (m_texMapEnd)
texMapDecl = hecl::Format("Texture2D texs[%u] : register(t0);\n", m_texMapEnd); texMapDecl = hecl::Format("Texture2D texs[%u] : register(t0);\n", m_texMapEnd);
if (reflectionType == ReflectionType::Indirect) if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format("Texture2D reflectionIndTex : register(t%u);\n" texMapDecl += hecl::Format(
"Texture2D reflectionTex : register(t%u);\n", "Texture2D reflectionIndTex : register(t%u);\n"
m_texMapEnd, m_texMapEnd+1); "Texture2D reflectionTex : register(t%u);\n",
else if (reflectionType == ReflectionType::Simple) m_texMapEnd, m_texMapEnd + 1);
texMapDecl += hecl::Format("Texture2D reflectionTex : register(t%u);\n", else if (reflectionType == ReflectionType::Simple)
m_texMapEnd); texMapDecl += hecl::Format("Texture2D reflectionTex : register(t%u);\n", m_texMapEnd);
uint32_t extTexBits = 0; uint32_t extTexBits = 0;
for (int i=0 ; i<extTexCount ; ++i) for (int i = 0; i < extTexCount; ++i) {
{ const TextureInfo& extTex = extTexs[i];
const TextureInfo& extTex = extTexs[i]; if (!(extTexBits & (1 << extTex.mapIdx))) {
if (!(extTexBits & (1 << extTex.mapIdx))) texMapDecl += hecl::Format("Texture2D extTex%u : register(t%u);\n", extTex.mapIdx, extTex.mapIdx);
{ extTexBits |= (1 << extTex.mapIdx);
texMapDecl += hecl::Format("Texture2D extTex%u : register(t%u);\n",
extTex.mapIdx, extTex.mapIdx);
extTexBits |= (1 << extTex.mapIdx);
}
} }
}
std::string retval = std::string retval = std::string("#define BLEND_SRC_") + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" +
std::string("#define BLEND_SRC_") + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + "#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" + "SamplerState samp : register(s0);\n"
"SamplerState samp : register(s0);\n" "SamplerState clampSamp : register(s1);\n"
"SamplerState clampSamp : register(s1);\n" "SamplerState reflectSamp : register(s2);\n" +
"SamplerState reflectSamp : register(s2);\n" + GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + texMapDecl +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" + lightingSrc + "\n" + postSrc + (!alphaTest ? "\n[earlydepthstencil]\n" : "\n") +
texMapDecl + "\n" + "float4 main(in VertToFrag vtf) : SV_Target0\n{\n";
lightingSrc + "\n" +
postSrc +
(!alphaTest ? "\n[earlydepthstencil]\n" : "\n") +
"float4 main(in VertToFrag vtf) : SV_Target0\n{\n";
if (m_lighting) {
if (m_lighting) if (!lighting.m_entry.empty())
{ retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n",
if (!lighting.m_entry.empty()) lighting.m_entry.data());
retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n",
lighting.m_entry.data());
else
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = texs[%u].Sample(samp, vtf.tcgs[%u]);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
retval += " float4 colorOut;\n";
if (m_alphaExpr.size())
retval += " colorOut = " + postEntry + "(" + (postEntry.size() ? "vtf, " : "") + "float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n";
else else
retval += " colorOut = " + postEntry + "(" + (postEntry.size() ? "vtf, " : "") + "float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n"; retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
return retval + (alphaTest ? GenerateAlphaTest() : "") + " return colorOut;\n}\n"; unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = texs[%u].Sample(samp, vtf.tcgs[%u]);\n", sampIdx++, sampling.mapIdx,
sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
retval += " float4 colorOut;\n";
if (m_alphaExpr.size())
retval += " colorOut = " + postEntry + "(" + (postEntry.size() ? "vtf, " : "") + "float4(" + m_colorExpr +
" + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n";
else
retval += " colorOut = " + postEntry + "(" + (postEntry.size() ? "vtf, " : "") + "float4(" + m_colorExpr +
" + " + reflectionExpr + ", 1.0)) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") + " return colorOut;\n}\n";
} }
} } // namespace hecl::Backend

View File

@ -5,455 +5,433 @@
static logvisor::Module Log("hecl::Backend::Metal"); static logvisor::Module Log("hecl::Backend::Metal");
namespace hecl::Backend namespace hecl::Backend {
{
std::string Metal::EmitTexGenSource2(TexGenSrc src, int uvIdx) const std::string Metal::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "objPos.xy\n";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "objPos.xy\n"; return "objNorm.xy\n";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "objNorm.xy\n"; return hecl::Format("v.uvIn%u", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("v.uvIn%u", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string Metal::EmitTexGenSource4(TexGenSrc src, int uvIdx) const std::string Metal::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
{ switch (src) {
switch (src) case TexGenSrc::Position:
{ return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Position: case TexGenSrc::Normal:
return "float4(objPos.xyz, 1.0)\n"; return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::Normal: case TexGenSrc::UV:
return "float4(objNorm.xyz, 1.0)\n"; return hecl::Format("float4(v.uvIn%u, 0.0, 1.0)", uvIdx);
case TexGenSrc::UV: default:
return hecl::Format("float4(v.uvIn%u, 0.0, 1.0)", uvIdx); break;
default: break; }
} return std::string();
return std::string();
} }
std::string Metal::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const std::string Metal::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
{ std::string retval =
std::string retval = "struct VertData\n"
"struct VertData\n" "{\n"
"{\n" " float3 posIn [[ attribute(0) ]];\n"
" float3 posIn [[ attribute(0) ]];\n" " float3 normIn [[ attribute(1) ]];\n";
" float3 normIn [[ attribute(1) ]];\n";
unsigned idx = 2; unsigned idx = 2;
if (col) if (col) {
{ for (unsigned i = 0; i < col; ++i, ++idx)
for (unsigned i=0 ; i<col ; ++i, ++idx) retval += hecl::Format(" float4 colIn%u [[ attribute(%u) ]];\n", i, idx);
retval += hecl::Format(" float4 colIn%u [[ attribute(%u) ]];\n", i, idx); }
}
if (uv) if (uv) {
{ for (unsigned i = 0; i < uv; ++i, ++idx)
for (unsigned i=0 ; i<uv ; ++i, ++idx) retval += hecl::Format(" float2 uvIn%u [[ attribute(%u) ]];\n", i, idx);
retval += hecl::Format(" float2 uvIn%u [[ attribute(%u) ]];\n", i, idx); }
}
if (w) if (w) {
{ for (unsigned i = 0; i < w; ++i, ++idx)
for (unsigned i=0 ; i<w ; ++i, ++idx) retval += hecl::Format(" float4 weightIn%u [[ attribute(%u) ]];\n", i, idx);
retval += hecl::Format(" float4 weightIn%u [[ attribute(%u) ]];\n", i, idx); }
}
return retval + "};\n"; return retval + "};\n";
} }
std::string Metal::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const std::string Metal::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
{ std::string retval =
std::string retval = "struct VertToFrag\n"
"struct VertToFrag\n" "{\n"
"{\n" " float4 mvpPos [[ position ]];\n"
" float4 mvpPos [[ position ]];\n" " float4 mvPos;\n"
" float4 mvPos;\n" " float4 mvNorm;\n";
" float4 mvNorm;\n";
if (m_tcgs.size()) if (m_tcgs.size())
for (size_t i=0 ; i<m_tcgs.size() ; ++i) for (size_t i = 0; i < m_tcgs.size(); ++i)
retval += hecl::Format(" float2 tcgs%" PRISize ";\n", i); retval += hecl::Format(" float2 tcgs%" PRISize ";\n", i);
if (extTexCount) if (extTexCount)
for (size_t i=0 ; i<extTexCount ; ++i) for (size_t i = 0; i < extTexCount; ++i)
retval += hecl::Format(" float2 extTcgs%" PRISize ";\n", i); retval += hecl::Format(" float2 extTcgs%" PRISize ";\n", i);
if (reflectionCoords) if (reflectionCoords)
retval += " float2 reflectTcgs0;\n" retval +=
" float2 reflectTcgs1;\n" " float2 reflectTcgs0;\n"
" float reflectAlpha;\n"; " float2 reflectTcgs1;\n"
" float reflectAlpha;\n";
return retval + "};\n"; return retval + "};\n";
} }
std::string Metal::GenerateVertUniformStruct(unsigned skinSlots) const std::string Metal::GenerateVertUniformStruct(unsigned skinSlots) const {
{ std::string retval;
std::string retval; if (skinSlots == 0) {
if (skinSlots == 0) retval =
{ "struct HECLVertUniform\n"
retval = "struct HECLVertUniform\n" "{\n"
"{\n" " float4x4 mv;\n"
" float4x4 mv;\n" " float4x4 mvInv;\n"
" float4x4 mvInv;\n" " float4x4 proj;\n"
" float4x4 proj;\n" "};\n"
"};\n" "struct TexMtxs {float4x4 mtx; float4x4 postMtx;};\n"
"struct TexMtxs {float4x4 mtx; float4x4 postMtx;};\n" "struct ReflectTexMtxs {float4x4 indMtx; float4x4 reflectMtx; float reflectAlpha;};\n";
"struct ReflectTexMtxs {float4x4 indMtx; float4x4 reflectMtx; float reflectAlpha;};\n"; } else {
} retval = hecl::Format(
"struct HECLVertUniform\n"
"{\n"
" float4x4 objs[%u];\n"
" float4x4 objsInv[%u];\n"
" float4x4 mv;\n"
" float4x4 mvInv;\n"
" float4x4 proj;\n"
"};\n"
"struct TexMtxs {float4x4 mtx; float4x4 postMtx;};\n"
"struct ReflectTexMtxs {float4x4 indMtx; float4x4 reflectMtx; float reflectAlpha;};\n",
skinSlots, skinSlots);
}
return retval;
}
std::string Metal::GenerateFragOutStruct() const {
return "struct FragOut\n"
"{\n"
" float4 color [[ color(0) ]];\n"
" //float depth [[ depth(less) ]];\n"
"};\n";
}
std::string Metal::GenerateAlphaTest() const {
return " if (out.color.a < 0.01)\n"
" {\n"
" discard_fragment();\n"
" }\n";
}
std::string Metal::GenerateReflectionExpr(ReflectionType type) const {
switch (type) {
case ReflectionType::None:
default:
return "float3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "reflectionTex.sample(reflectSamp, vtf.reflectTcgs1).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "reflectionTex.sample(reflectSamp, (reflectionIndTex.sample(samp, vtf.reflectTcgs0).rg - "
"float2(0.5, 0.5)) * float2(0.5, 0.5) + vtf.reflectTcgs1).rgb * vtf.reflectAlpha";
}
}
void Metal::reset(const IR& ir, Diagnostics& diag) {
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "Metal");
}
std::string Metal::makeVert(unsigned col, unsigned uv, unsigned w, unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const {
std::string tmStr = ",\nconstant TexMtxs* texMtxs [[ buffer(3) ]]";
if (reflectionType != ReflectionType::None)
tmStr += ",\nconstant ReflectTexMtxs& reflectMtxs [[ buffer(5) ]]";
std::string retval = "#include <metal_stdlib>\nusing namespace metal;\n" + GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s) +
"\nvertex VertToFrag vmain(VertData v [[ stage_in ]],\n"
" constant HECLVertUniform& vu [[ buffer(2) ]]" +
tmStr +
")\n"
"{\n"
" VertToFrag vtf;\n";
if (s) {
/* skinned */
retval +=
" float4 objPos = float4(0.0,0.0,0.0,0.0);\n"
" float4 objNorm = float4(0.0,0.0,0.0,0.0);\n";
for (size_t i = 0; i < s; ++i)
retval += hecl::Format(
" objPos += (vu.objs[%" PRISize "] * float4(v.posIn, 1.0)) * v.weightIn%" PRISize "[%" PRISize
"];\n"
" objNorm += (vu.objsInv[%" PRISize "] * float4(v.normIn, 1.0)) * v.weightIn%" PRISize "[%" PRISize "];\n",
i, i / 4, i % 4, i, i / 4, i % 4);
retval +=
" objPos[3] = 1.0;\n"
" objNorm = float4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = vu.mv * objPos;\n"
" vtf.mvNorm = float4(normalize((vu.mvInv * objNorm).xyz), 0.0);\n"
" vtf.mvpPos = vu.proj * vtf.mvPos;\n";
} else {
/* non-skinned */
retval +=
" float4 objPos = float4(v.posIn, 1.0);\n"
" float4 objNorm = float4(v.normIn, 0.0);\n"
" vtf.mvPos = vu.mv * objPos;\n"
" vtf.mvNorm = vu.mvInv * objNorm;\n"
" vtf.mvpPos = vu.proj * vtf.mvPos;\n";
}
retval += " float4 tmpProj;\n";
int tcgIdx = 0;
for (const TexCoordGen& tcg : m_tcgs) {
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs%u = %s;\n", tcgIdx, EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else else
{ retval += hecl::Format(
retval = hecl::Format("struct HECLVertUniform\n" " tmpProj = texMtxs[%u].postMtx * float4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
"{\n" " vtf.tcgs%u = (tmpProj / tmpProj.w).xy;\n",
" float4x4 objs[%u];\n" tcg.m_mtx, tcg.m_norm ? "normalize" : "", tcg.m_mtx, EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(),
" float4x4 objsInv[%u];\n" tcgIdx);
" float4x4 mv;\n" ++tcgIdx;
" float4x4 mvInv;\n" }
" float4x4 proj;\n"
"};\n"
"struct TexMtxs {float4x4 mtx; float4x4 postMtx;};\n"
"struct ReflectTexMtxs {float4x4 indMtx; float4x4 reflectMtx; float reflectAlpha;};\n",
skinSlots, skinSlots);
}
return retval;
}
std::string Metal::GenerateFragOutStruct() const for (int i = 0; i < extTexCount; ++i) {
{ const TextureInfo& extTex = extTexs[i];
return "struct FragOut\n" if (extTex.mtxIdx < 0)
"{\n" retval += hecl::Format(" vtf.extTcgs%u = %s;\n", i, EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
" float4 color [[ color(0) ]];\n"
" //float depth [[ depth(less) ]];\n"
"};\n";
}
std::string Metal::GenerateAlphaTest() const
{
return " if (out.color.a < 0.01)\n"
" {\n"
" discard_fragment();\n"
" }\n";
}
std::string Metal::GenerateReflectionExpr(ReflectionType type) const
{
switch (type)
{
case ReflectionType::None:
default:
return "float3(0.0, 0.0, 0.0)";
case ReflectionType::Simple:
return "reflectionTex.sample(reflectSamp, vtf.reflectTcgs1).rgb * vtf.reflectAlpha";
case ReflectionType::Indirect:
return "reflectionTex.sample(reflectSamp, (reflectionIndTex.sample(samp, vtf.reflectTcgs0).rg - "
"float2(0.5, 0.5)) * float2(0.5, 0.5) + vtf.reflectTcgs1).rgb * vtf.reflectAlpha";
}
}
void Metal::reset(const IR& ir, Diagnostics& diag)
{
/* Common programmable interpretation */
ProgrammableCommon::reset(ir, diag, "Metal");
}
std::string Metal::makeVert(unsigned col, unsigned uv, unsigned w,
unsigned s, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const
{
std::string tmStr = ",\nconstant TexMtxs* texMtxs [[ buffer(3) ]]";
if (reflectionType != ReflectionType::None)
tmStr += ",\nconstant ReflectTexMtxs& reflectMtxs [[ buffer(5) ]]";
std::string retval = "#include <metal_stdlib>\nusing namespace metal;\n" +
GenerateVertInStruct(col, uv, w) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateVertUniformStruct(s) +
"\nvertex VertToFrag vmain(VertData v [[ stage_in ]],\n"
" constant HECLVertUniform& vu [[ buffer(2) ]]" + tmStr + ")\n"
"{\n"
" VertToFrag vtf;\n";
if (s)
{
/* skinned */
retval += " float4 objPos = float4(0.0,0.0,0.0,0.0);\n"
" float4 objNorm = float4(0.0,0.0,0.0,0.0);\n";
for (size_t i=0 ; i<s ; ++i)
retval += hecl::Format(" objPos += (vu.objs[%" PRISize "] * float4(v.posIn, 1.0)) * v.weightIn%" PRISize "[%" PRISize "];\n"
" objNorm += (vu.objsInv[%" PRISize "] * float4(v.normIn, 1.0)) * v.weightIn%" PRISize "[%" PRISize "];\n",
i, i/4, i%4, i, i/4, i%4);
retval += " objPos[3] = 1.0;\n"
" objNorm = float4(normalize(objNorm.xyz), 0.0);\n"
" vtf.mvPos = vu.mv * objPos;\n"
" vtf.mvNorm = float4(normalize((vu.mvInv * objNorm).xyz), 0.0);\n"
" vtf.mvpPos = vu.proj * vtf.mvPos;\n";
}
else else
{ retval += hecl::Format(
/* non-skinned */ " tmpProj = texMtxs[%u].postMtx * float4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
retval += " float4 objPos = float4(v.posIn, 1.0);\n" " vtf.extTcgs%u = (tmpProj / tmpProj.w).xy;\n",
" float4 objNorm = float4(v.normIn, 0.0);\n" extTex.mtxIdx, extTex.normalize ? "normalize" : "", extTex.mtxIdx,
" vtf.mvPos = vu.mv * objPos;\n" EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
" vtf.mvNorm = vu.mvInv * objNorm;\n" }
" vtf.mvpPos = vu.proj * vtf.mvPos;\n";
}
retval += " float4 tmpProj;\n"; if (reflectionType != ReflectionType::None)
retval +=
" vtf.reflectTcgs0 = normalize((reflectMtxs.indMtx * float4(v.posIn, 1.0)).xz) * float2(0.5, 0.5) + "
"float2(0.5, 0.5);\n"
" vtf.reflectTcgs1 = (reflectMtxs.reflectMtx * float4(v.posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectMtxs.reflectAlpha;\n";
int tcgIdx = 0; return retval + " return vtf;\n}\n";
for (const TexCoordGen& tcg : m_tcgs)
{
if (tcg.m_mtx < 0)
retval += hecl::Format(" vtf.tcgs%u = %s;\n", tcgIdx,
EmitTexGenSource2(tcg.m_src, tcg.m_uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = texMtxs[%u].postMtx * float4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
" vtf.tcgs%u = (tmpProj / tmpProj.w).xy;\n", tcg.m_mtx,
tcg.m_norm ? "normalize" : "", tcg.m_mtx,
EmitTexGenSource4(tcg.m_src, tcg.m_uvIdx).c_str(), tcgIdx);
++tcgIdx;
}
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (extTex.mtxIdx < 0)
retval += hecl::Format(" vtf.extTcgs%u = %s;\n", i,
EmitTexGenSource2(extTex.src, extTex.uvIdx).c_str());
else
retval += hecl::Format(" tmpProj = texMtxs[%u].postMtx * float4(%s((texMtxs[%u].mtx * %s).xyz), 1.0);\n"
" vtf.extTcgs%u = (tmpProj / tmpProj.w).xy;\n", extTex.mtxIdx,
extTex.normalize ? "normalize" : "", extTex.mtxIdx,
EmitTexGenSource4(extTex.src, extTex.uvIdx).c_str(), i);
}
if (reflectionType != ReflectionType::None)
retval += " vtf.reflectTcgs0 = normalize((reflectMtxs.indMtx * float4(v.posIn, 1.0)).xz) * float2(0.5, 0.5) + float2(0.5, 0.5);\n"
" vtf.reflectTcgs1 = (reflectMtxs.reflectMtx * float4(v.posIn, 1.0)).xy;\n"
" vtf.reflectAlpha = reflectMtxs.reflectAlpha;\n";
return retval + " return vtf;\n}\n";
} }
std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
BlendFactor srcFactor, BlendFactor dstFactor, std::string lightingSrc;
const Function& lighting) const if (!lighting.m_source.empty())
{ lightingSrc = lighting.m_source;
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string texMapDecl; std::string texMapDecl;
if (m_texMapEnd) if (m_texMapEnd)
for (int i=0 ; i<m_texMapEnd ; ++i) for (int i = 0; i < m_texMapEnd; ++i)
texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", i, i); texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", i, i);
if (reflectionType == ReflectionType::Indirect) if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format(",\ntexture2d<float> reflectionIndTex [[ texture(%u) ]]\n" texMapDecl += hecl::Format(
",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n", ",\ntexture2d<float> reflectionIndTex [[ texture(%u) ]]\n"
m_texMapEnd, m_texMapEnd+1); ",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n",
else if (reflectionType == ReflectionType::Simple) m_texMapEnd, m_texMapEnd + 1);
texMapDecl += hecl::Format(",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n", else if (reflectionType == ReflectionType::Simple)
m_texMapEnd); texMapDecl += hecl::Format(",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n", m_texMapEnd);
std::string blockCall; std::string blockCall;
for (size_t i=0 ; i<blockCount ; ++i) for (size_t i = 0; i < blockCount; ++i) {
{ texMapDecl += hecl::Format(",\nconstant %s& block%" PRISize " [[ buffer(%" PRISize ") ]]", blockNames[i], i, i + 4);
texMapDecl += hecl::Format(",\nconstant %s& block%" PRISize " [[ buffer(%" PRISize ") ]]", blockNames[i], i, i + 4); if (blockCall.size())
if (blockCall.size()) blockCall += ", ";
blockCall += ", "; blockCall += hecl::Format("block%" PRISize, i);
blockCall += hecl::Format("block%" PRISize, i); }
}
std::string retval = std::string("#include <metal_stdlib>\nusing namespace metal;\n") + std::string retval =
"#define BLEND_SRC_" + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + std::string("#include <metal_stdlib>\nusing namespace metal;\n") + "#define BLEND_SRC_" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + "#define BLEND_DST_" +
GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) + "\n" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
GenerateFragOutStruct() + "\n" + GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) + "\n" + GenerateFragOutStruct() + "\n" +
lightingSrc + "\n" + lightingSrc + "\n" +
"fragment FragOut fmain(VertToFrag vtf [[ stage_in ]],\n" "fragment FragOut fmain(VertToFrag vtf [[ stage_in ]],\n"
"sampler samp [[ sampler(0) ]], sampler clampSamp [[ sampler(1) ]], sampler reflectSamp [[ sampler(2) ]]" + texMapDecl + ")\n" "sampler samp [[ sampler(0) ]], sampler clampSamp [[ sampler(1) ]], sampler reflectSamp [[ sampler(2) ]]" +
"{\n" texMapDecl +
" FragOut out;\n"; ")\n"
"{\n"
" FragOut out;\n";
if (!lighting.m_source.empty()) if (!lighting.m_source.empty()) {
{ retval +=
retval += " float4 colorReg0 = block0.colorReg0;\n" " float4 colorReg0 = block0.colorReg0;\n"
" float4 colorReg1 = block0.colorReg1;\n" " float4 colorReg1 = block0.colorReg1;\n"
" float4 colorReg2 = block0.colorReg2;\n" " float4 colorReg2 = block0.colorReg2;\n"
" float4 mulColor = block0.mulColor;\n"; " float4 mulColor = block0.mulColor;\n";
} } else {
else retval +=
{ " float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n"
retval += " float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" " float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n" " float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n" " float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
" float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n"; }
}
if (m_lighting) if (m_lighting) {
{
if (!lighting.m_entry.empty())
retval += hecl::Format(" float4 lighting = %s(%s, vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n",
lighting.m_entry.data(), blockCall.c_str());
else
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = tex%u.sample(samp, vtf.tcgs%u);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_alphaExpr.size())
retval += " out.color = float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
else
retval += " out.color = float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") +
" //out.depth = 1.0 - float(int((1.0 - vtf.mvpPos.z) * 16777216.0)) / 16777216.0;\n"
" return out;\n"
"}\n";
}
std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest,
ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting,
const Function& post, size_t extTexCount,
const TextureInfo* extTexs) const
{
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string lightingEntry;
if (!lighting.m_entry.empty()) if (!lighting.m_entry.empty())
lightingEntry = lighting.m_entry; retval += hecl::Format(" float4 lighting = %s(%s, vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n",
lighting.m_entry.data(), blockCall.c_str());
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
int extTexBits = 0;
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
extTexBits |= 1 << extTex.mapIdx;
}
std::string texMapDecl;
if (m_texMapEnd)
for (int i=0 ; i<m_texMapEnd ; ++i)
if (!(extTexBits & (1 << i)))
texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", i, i);
if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format(",\ntexture2d<float> reflectionIndTex [[ texture(%u) ]]\n"
",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n",
m_texMapEnd, m_texMapEnd+1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format(",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n",
m_texMapEnd);
std::string extTexCall;
int extTexBits2 = 0;
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (!(extTexBits2 & (1 << extTex.mapIdx)))
{
if (extTexCall.size())
extTexCall += ", ";
extTexCall += hecl::Format("tex%u", extTex.mapIdx);
texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", extTex.mapIdx, extTex.mapIdx);
extTexBits2 |= 1 << extTex.mapIdx;
}
}
std::string blockCall;
for (size_t i=0 ; i<blockCount ; ++i)
{
texMapDecl += hecl::Format(",\nconstant %s& block%" PRISize " [[ buffer(%" PRISize ") ]]", blockNames[i], i, i + 4);
if (blockCall.size())
blockCall += ", ";
blockCall += hecl::Format("block%" PRISize, i);
}
std::string retval = std::string("#include <metal_stdlib>\nusing namespace metal;\n") +
"#define BLEND_SRC_" + BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" +
"#define BLEND_DST_" + BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" +
GenerateFragOutStruct() + "\n" +
lightingSrc + "\n" +
postSrc + "\n" +
"fragment FragOut fmain(VertToFrag vtf [[ stage_in ]],\n"
"sampler samp [[ sampler(0) ]], sampler clampSamp [[ sampler(1) ]], sampler reflectSamp [[ sampler(2) ]]" + texMapDecl + ")\n"
"{\n"
" FragOut out;\n";
if (!lighting.m_source.empty())
{
retval += " float4 colorReg0 = block0.colorReg0;\n"
" float4 colorReg1 = block0.colorReg1;\n"
" float4 colorReg2 = block0.colorReg2;\n"
" float4 mulColor = block0.mulColor;\n";
}
else else
{ retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
retval += " float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" }
" float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
}
if (m_lighting) unsigned sampIdx = 0;
{ for (const TexSampling& sampling : m_texSamplings)
if (!lighting.m_entry.empty()) retval += hecl::Format(" float4 sampling%u = tex%u.sample(samp, vtf.tcgs%u);\n", sampIdx++, sampling.mapIdx,
{ sampling.tcgIdx);
retval += " float4 lighting = " + lightingEntry + "(" + blockCall + ", vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf" +
(!strncmp(lighting.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? (", samp, clampSamp," + extTexCall) : "") : "") + ");\n";
}
else
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0; std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = tex%u.sample(samp, vtf.tcgs%u);\n",
sampIdx++, sampling.mapIdx, sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType); if (m_alphaExpr.size())
retval += " out.color = float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ") * mulColor;\n";
else
retval += " out.color = float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0) * mulColor;\n";
if (m_alphaExpr.size()) return retval + (alphaTest ? GenerateAlphaTest() : "") +
{ " //out.depth = 1.0 - float(int((1.0 - vtf.mvpPos.z) * 16777216.0)) / 16777216.0;\n"
retval += " out.color = " + postEntry + "(" + " return out;\n"
(postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") + "}\n";
(!strncmp(post.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") +
"float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n";
}
else
{
retval += " out.color = " + postEntry + "(" +
(postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") +
(!strncmp(post.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") +
"float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n";
}
return retval + (alphaTest ? GenerateAlphaTest() : "") +
" //out.depth = 1.0 - float(int((1.0 - vtf.mvpPos.z) * 16777216.0)) / 16777216.0;\n"
" return out;\n"
"}\n";
} }
std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting,
const Function& post, size_t extTexCount, const TextureInfo* extTexs) const {
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string lightingEntry;
if (!lighting.m_entry.empty())
lightingEntry = lighting.m_entry;
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
int extTexBits = 0;
for (int i = 0; i < extTexCount; ++i) {
const TextureInfo& extTex = extTexs[i];
extTexBits |= 1 << extTex.mapIdx;
}
std::string texMapDecl;
if (m_texMapEnd)
for (int i = 0; i < m_texMapEnd; ++i)
if (!(extTexBits & (1 << i)))
texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", i, i);
if (reflectionType == ReflectionType::Indirect)
texMapDecl += hecl::Format(
",\ntexture2d<float> reflectionIndTex [[ texture(%u) ]]\n"
",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n",
m_texMapEnd, m_texMapEnd + 1);
else if (reflectionType == ReflectionType::Simple)
texMapDecl += hecl::Format(",\ntexture2d<float> reflectionTex [[ texture(%u) ]]\n", m_texMapEnd);
std::string extTexCall;
int extTexBits2 = 0;
for (int i = 0; i < extTexCount; ++i) {
const TextureInfo& extTex = extTexs[i];
if (!(extTexBits2 & (1 << extTex.mapIdx))) {
if (extTexCall.size())
extTexCall += ", ";
extTexCall += hecl::Format("tex%u", extTex.mapIdx);
texMapDecl += hecl::Format(",\ntexture2d<float> tex%u [[ texture(%u) ]]", extTex.mapIdx, extTex.mapIdx);
extTexBits2 |= 1 << extTex.mapIdx;
}
}
std::string blockCall;
for (size_t i = 0; i < blockCount; ++i) {
texMapDecl += hecl::Format(",\nconstant %s& block%" PRISize " [[ buffer(%" PRISize ") ]]", blockNames[i], i, i + 4);
if (blockCall.size())
blockCall += ", ";
blockCall += hecl::Format("block%" PRISize, i);
}
std::string retval =
std::string("#include <metal_stdlib>\nusing namespace metal;\n") + "#define BLEND_SRC_" +
BlendFactorToDefine(srcFactor, m_blendSrc) + "\n" + "#define BLEND_DST_" +
BlendFactorToDefine(dstFactor, m_blendDst) + "\n" +
GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" + GenerateFragOutStruct() +
"\n" + lightingSrc + "\n" + postSrc + "\n" +
"fragment FragOut fmain(VertToFrag vtf [[ stage_in ]],\n"
"sampler samp [[ sampler(0) ]], sampler clampSamp [[ sampler(1) ]], sampler reflectSamp [[ sampler(2) ]]" +
texMapDecl +
")\n"
"{\n"
" FragOut out;\n";
if (!lighting.m_source.empty()) {
retval +=
" float4 colorReg0 = block0.colorReg0;\n"
" float4 colorReg1 = block0.colorReg1;\n"
" float4 colorReg2 = block0.colorReg2;\n"
" float4 mulColor = block0.mulColor;\n";
} else {
retval +=
" float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 colorReg1 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 colorReg2 = float4(1.0, 1.0, 1.0, 1.0);\n"
" float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n";
}
if (m_lighting) {
if (!lighting.m_entry.empty()) {
retval +=
" float4 lighting = " + lightingEntry + "(" + blockCall +
", vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf" +
(!strncmp(lighting.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? (", samp, clampSamp," + extTexCall) : "")
: "") +
");\n";
} else
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
unsigned sampIdx = 0;
for (const TexSampling& sampling : m_texSamplings)
retval += hecl::Format(" float4 sampling%u = tex%u.sample(samp, vtf.tcgs%u);\n", sampIdx++, sampling.mapIdx,
sampling.tcgIdx);
std::string reflectionExpr = GenerateReflectionExpr(reflectionType);
if (m_alphaExpr.size()) {
retval += " out.color = " + postEntry + "(" +
(postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") +
(!strncmp(post.m_entry.data(), "EXT", 3)
? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "")
: ""))
: "") +
"float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n";
} else {
retval += " out.color = " + postEntry + "(" +
(postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") +
(!strncmp(post.m_entry.data(), "EXT", 3)
? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "")
: ""))
: "") +
"float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n";
}
return retval + (alphaTest ? GenerateAlphaTest() : "") +
" //out.depth = 1.0 - float(int((1.0 - vtf.mvpPos.z) * 16777216.0)) / 16777216.0;\n"
" return out;\n"
"}\n";
} }
} // namespace hecl::Backend

View File

@ -1,370 +1,307 @@
#include "hecl/Backend/ProgrammableCommon.hpp" #include "hecl/Backend/ProgrammableCommon.hpp"
#include <map> #include <map>
namespace hecl::Backend namespace hecl::Backend {
{
const char* ProgrammableCommon::BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) const char* ProgrammableCommon::BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
{ switch (factor) {
switch (factor) case BlendFactor::Zero:
{ return "ZERO";
case BlendFactor::Zero: case BlendFactor::One:
return "ZERO"; return "ONE";
case BlendFactor::One: case BlendFactor::SrcColor:
return "ONE"; return "SRCCOLOR";
case BlendFactor::SrcColor: case BlendFactor::InvSrcColor:
return "SRCCOLOR"; return "INVSRCCOLOR";
case BlendFactor::InvSrcColor: case BlendFactor::DstColor:
return "INVSRCCOLOR"; return "DSTCOLOR";
case BlendFactor::DstColor: case BlendFactor::InvDstColor:
return "DSTCOLOR"; return "INVDSTCOLOR";
case BlendFactor::InvDstColor: case BlendFactor::SrcAlpha:
return "INVDSTCOLOR"; return "SRCALPHA";
case BlendFactor::SrcAlpha: case BlendFactor::InvSrcAlpha:
return "SRCALPHA"; return "INVSRCALPHA";
case BlendFactor::InvSrcAlpha: case BlendFactor::DstAlpha:
return "INVSRCALPHA"; return "DSTALPHA";
case BlendFactor::DstAlpha: case BlendFactor::InvDstAlpha:
return "DSTALPHA"; return "INVDSTALPHA";
case BlendFactor::InvDstAlpha: case BlendFactor::SrcColor1:
return "INVDSTALPHA"; return "SRCCOLOR1";
case BlendFactor::SrcColor1: case BlendFactor::InvSrcColor1:
return "SRCCOLOR1"; return "INVSRCCOLOR1";
case BlendFactor::InvSrcColor1: default:
return "INVSRCCOLOR1"; return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
default: }
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
} }
unsigned ProgrammableCommon::addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize) unsigned ProgrammableCommon::addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize) {
{ for (unsigned i = 0; i < m_tcgs.size(); ++i) {
for (unsigned i=0 ; i<m_tcgs.size() ; ++i) TexCoordGen& tcg = m_tcgs[i];
{ if (tcg.m_src == src && tcg.m_uvIdx == uvIdx && tcg.m_mtx == mtx && tcg.m_norm == normalize)
TexCoordGen& tcg = m_tcgs[i]; return i;
if (tcg.m_src == src && tcg.m_uvIdx == uvIdx && tcg.m_mtx == mtx && tcg.m_norm == normalize) }
return i; m_tcgs.emplace_back();
} TexCoordGen& newTcg = m_tcgs.back();
m_tcgs.emplace_back(); newTcg.m_src = src;
TexCoordGen& newTcg = m_tcgs.back(); newTcg.m_uvIdx = uvIdx;
newTcg.m_src = src; newTcg.m_mtx = mtx;
newTcg.m_uvIdx = uvIdx; newTcg.m_norm = normalize;
newTcg.m_mtx = mtx; return m_tcgs.size() - 1;
newTcg.m_norm = normalize;
return m_tcgs.size() - 1;
} }
unsigned ProgrammableCommon::addTexSampling(unsigned mapIdx, unsigned tcgIdx) unsigned ProgrammableCommon::addTexSampling(unsigned mapIdx, unsigned tcgIdx) {
{ for (unsigned i = 0; i < m_texSamplings.size(); ++i) {
for (unsigned i=0 ; i<m_texSamplings.size() ; ++i) TexSampling& samp = m_texSamplings[i];
{ if (samp.mapIdx == mapIdx && samp.tcgIdx == tcgIdx)
TexSampling& samp = m_texSamplings[i]; return i;
if (samp.mapIdx == mapIdx && samp.tcgIdx == tcgIdx) }
return i; m_texSamplings.emplace_back();
} TexSampling& samp = m_texSamplings.back();
m_texSamplings.emplace_back(); samp.mapIdx = mapIdx;
TexSampling& samp = m_texSamplings.back(); samp.tcgIdx = tcgIdx;
samp.mapIdx = mapIdx; if (m_texMapEnd < mapIdx + 1)
samp.tcgIdx = tcgIdx; m_texMapEnd = mapIdx + 1;
if (m_texMapEnd < mapIdx + 1) return m_texSamplings.size() - 1;
m_texMapEnd = mapIdx + 1;
return m_texSamplings.size() - 1;
} }
unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, int mtx,
const IR::Instruction& inst, int mtx, bool normalize) bool normalize) {
{ if (inst.m_op != IR::OpType::Call)
if (inst.m_op != IR::OpType::Call) diag.reportBackendErr(inst.m_loc, "TexCoordGen resolution requires function");
diag.reportBackendErr(inst.m_loc, "TexCoordGen resolution requires function");
const std::string& tcgName = inst.m_call.m_name; const std::string& tcgName = inst.m_call.m_name;
if (!tcgName.compare("UV")) if (!tcgName.compare("UV")) {
{ if (inst.getChildCount() < 1)
if (inst.getChildCount() < 1) diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument"); const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
const IR::Instruction& idxInst = inst.getChildInst(ir, 0); auto& idxImm = idxInst.getImmVec();
auto& idxImm = idxInst.getImmVec(); return addTexCoordGen(TexGenSrc::UV, int(idxImm.simd[0]), mtx, normalize);
return addTexCoordGen(TexGenSrc::UV, int(idxImm.simd[0]), mtx, normalize); } else if (!tcgName.compare("Normal"))
} return addTexCoordGen(TexGenSrc::Normal, -1, mtx, normalize);
else if (!tcgName.compare("Normal")) else if (!tcgName.compare("View"))
return addTexCoordGen(TexGenSrc::Normal, -1, mtx, normalize); return addTexCoordGen(TexGenSrc::Position, -1, mtx, normalize);
else if (!tcgName.compare("View"))
return addTexCoordGen(TexGenSrc::Position, -1, mtx, normalize);
/* Otherwise treat as game-specific function */ /* Otherwise treat as game-specific function */
const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0); const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0);
unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, m_texMtxRefs.size(), unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, m_texMtxRefs.size(), normalize || tcgName.back() == 'N');
normalize || tcgName.back() == 'N'); TexCoordGen& tcg = m_tcgs[idx];
TexCoordGen& tcg = m_tcgs[idx]; m_texMtxRefs.push_back(idx);
m_texMtxRefs.push_back(idx); tcg.m_gameFunction = tcgName;
tcg.m_gameFunction = tcgName; tcg.m_gameArgs.clear();
tcg.m_gameArgs.clear(); for (int i = 1; i < inst.getChildCount(); ++i) {
for (int i=1 ; i<inst.getChildCount() ; ++i) const IR::Instruction& ci = inst.getChildInst(ir, i);
{ tcg.m_gameArgs.push_back(ci.getImmVec());
const IR::Instruction& ci = inst.getChildInst(ir, i); }
tcg.m_gameArgs.push_back(ci.getImmVec()); return idx;
}
return idx;
} }
std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& diag, std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
const IR::Instruction& inst, bool toSwizzle) bool toSwizzle) {
{ switch (inst.m_op) {
switch (inst.m_op) case IR::OpType::Call: {
{ const std::string& name = inst.m_call.m_name;
case IR::OpType::Call: bool normalize = false;
{ if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN"))) {
const std::string& name = inst.m_call.m_name; if (inst.getChildCount() < 2)
bool normalize = false; diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN")))
{
if (inst.getChildCount() < 2)
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
const IR::Instruction& mapInst = inst.getChildInst(ir, 0); const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
auto& mapImm = mapInst.getImmVec(); auto& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]); unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1); const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize); unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx)) : return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx)); : EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx));
} } else if (!name.compare("ColorReg")) {
else if (!name.compare("ColorReg")) const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
{ unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
const IR::Instruction& idxInst = inst.getChildInst(ir, 0); return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseRGB(idx);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]); } else if (!name.compare("Lighting")) {
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseRGB(idx); m_lighting = true;
} return toSwizzle ? EmitLightingRaw() : EmitLightingRGB();
else if (!name.compare("Lighting")) } else if (!name.compare("vec3")) {
{ if (inst.getChildCount() < 3)
m_lighting = true; diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments");
return toSwizzle ? EmitLightingRaw() : EmitLightingRGB(); const IR::Instruction& aInst = inst.getChildInst(ir, 0);
} const IR::Instruction& bInst = inst.getChildInst(ir, 1);
else if (!name.compare("vec3")) const IR::Instruction& cInst = inst.getChildInst(ir, 2);
{ return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false), RecursiveTraceAlpha(ir, diag, bInst, false),
if (inst.getChildCount() < 3) RecursiveTraceAlpha(ir, diag, cInst, false));
diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments"); } else
const IR::Instruction& aInst = inst.getChildInst(ir, 0); diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
const IR::Instruction& bInst = inst.getChildInst(ir, 1); break;
const IR::Instruction& cInst = inst.getChildInst(ir, 2); }
return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false), case IR::OpType::LoadImm: {
RecursiveTraceAlpha(ir, diag, bInst, false), const atVec4f& vec = inst.m_loadImm.m_immVec;
RecursiveTraceAlpha(ir, diag, cInst, false)); return EmitVec3(vec);
} }
else case IR::OpType::Arithmetic: {
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str()); ArithmeticOp op = inst.m_arithmetic.m_op;
break; const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceColor(ir, diag, bInst, false);
switch (op) {
case ArithmeticOp::Add: {
return EmitAdd(aTrace, bTrace);
} }
case IR::OpType::LoadImm: case ArithmeticOp::Subtract: {
{ return EmitSub(aTrace, bTrace);
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVec3(vec);
} }
case IR::OpType::Arithmetic: case ArithmeticOp::Multiply: {
{ return EmitMult(aTrace, bTrace);
ArithmeticOp op = inst.m_arithmetic.m_op;
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceColor(ir, diag, bInst, false);
switch (op)
{
case ArithmeticOp::Add:
{
return EmitAdd(aTrace, bTrace);
}
case ArithmeticOp::Subtract:
{
return EmitSub(aTrace, bTrace);
}
case ArithmeticOp::Multiply:
{
return EmitMult(aTrace, bTrace);
}
case ArithmeticOp::Divide:
{
return EmitDiv(aTrace, bTrace);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
}
} }
case IR::OpType::Swizzle: case ArithmeticOp::Divide: {
{ return EmitDiv(aTrace, bTrace);
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, true);
return EmitSwizzle3(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
} }
default: default:
diag.reportBackendErr(inst.m_loc, "invalid color op"); diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
} }
}
case IR::OpType::Swizzle: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, true);
return EmitSwizzle3(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid color op");
}
return std::string(); return std::string();
} }
std::string ProgrammableCommon::RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, std::string ProgrammableCommon::RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
const IR::Instruction& inst, bool toSwizzle) bool toSwizzle) {
{ switch (inst.m_op) {
switch (inst.m_op) case IR::OpType::Call: {
{ const std::string& name = inst.m_call.m_name;
case IR::OpType::Call: bool normalize = false;
{ if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN"))) {
const std::string& name = inst.m_call.m_name; if (inst.getChildCount() < 2)
bool normalize = false; diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN")))
{
if (inst.getChildCount() < 2)
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
const IR::Instruction& mapInst = inst.getChildInst(ir, 0); const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
const atVec4f& mapImm = mapInst.getImmVec(); const atVec4f& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]); unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1); const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize); unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx)) : return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
EmitSamplingUseAlpha(addTexSampling(mapIdx, texGenIdx)); : EmitSamplingUseAlpha(addTexSampling(mapIdx, texGenIdx));
} } else if (!name.compare("ColorReg")) {
else if (!name.compare("ColorReg")) const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
{ unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
const IR::Instruction& idxInst = inst.getChildInst(ir, 0); return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseAlpha(idx);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]); } else if (!name.compare("Lighting")) {
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseAlpha(idx); m_lighting = true;
} return toSwizzle ? EmitLightingRaw() : EmitLightingAlpha();
else if (!name.compare("Lighting")) } else
{ diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
m_lighting = true; break;
return toSwizzle ? EmitLightingRaw() : EmitLightingAlpha(); }
} case IR::OpType::LoadImm: {
else const atVec4f& vec = inst.m_loadImm.m_immVec;
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str()); return EmitVal(vec.simd[0]);
break; }
case IR::OpType::Arithmetic: {
ArithmeticOp op = inst.m_arithmetic.m_op;
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceAlpha(ir, diag, bInst, false);
switch (op) {
case ArithmeticOp::Add: {
return EmitAdd(aTrace, bTrace);
} }
case IR::OpType::LoadImm: case ArithmeticOp::Subtract: {
{ return EmitSub(aTrace, bTrace);
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVal(vec.simd[0]);
} }
case IR::OpType::Arithmetic: case ArithmeticOp::Multiply: {
{ return EmitMult(aTrace, bTrace);
ArithmeticOp op = inst.m_arithmetic.m_op;
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceAlpha(ir, diag, bInst, false);
switch (op)
{
case ArithmeticOp::Add:
{
return EmitAdd(aTrace, bTrace);
}
case ArithmeticOp::Subtract:
{
return EmitSub(aTrace, bTrace);
}
case ArithmeticOp::Multiply:
{
return EmitMult(aTrace, bTrace);
}
case ArithmeticOp::Divide:
{
return EmitDiv(aTrace, bTrace);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
}
} }
case IR::OpType::Swizzle: case ArithmeticOp::Divide: {
{ return EmitDiv(aTrace, bTrace);
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, true);
return EmitSwizzle1(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
} }
default: default:
diag.reportBackendErr(inst.m_loc, "invalid alpha op"); diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
} }
}
case IR::OpType::Swizzle: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, true);
return EmitSwizzle1(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid alpha op");
}
return std::string(); return std::string();
} }
void ProgrammableCommon::reset(const IR& ir, Diagnostics& diag, const char* backendName) void ProgrammableCommon::reset(const IR& ir, Diagnostics& diag, const char* backendName) {
{ m_lighting = false;
m_lighting = false; m_texSamplings.clear();
m_texSamplings.clear(); m_texMapEnd = 0;
m_texMapEnd = 0; m_tcgs.clear();
m_tcgs.clear(); m_texMtxRefs.clear();
m_texMtxRefs.clear(); m_colorExpr.clear();
m_colorExpr.clear(); m_alphaExpr.clear();
m_alphaExpr.clear();
diag.setBackend(backendName); diag.setBackend(backendName);
/* Final instruction is the root call by hecl convention */ /* Final instruction is the root call by hecl convention */
const IR::Instruction& rootCall = ir.m_instructions.back(); const IR::Instruction& rootCall = ir.m_instructions.back();
if (!rootCall.m_call.m_name.compare("HECLOpaque")) if (!rootCall.m_call.m_name.compare("HECLOpaque")) {
{ m_blendSrc = BlendFactor::One;
m_blendSrc = BlendFactor::One; m_blendDst = BlendFactor::Zero;
m_blendDst = BlendFactor::Zero; } else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
} m_blendSrc = BlendFactor::SrcAlpha;
else if (!rootCall.m_call.m_name.compare("HECLAlpha")) m_blendDst = BlendFactor::InvSrcAlpha;
{ } else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
m_blendSrc = BlendFactor::SrcAlpha; m_blendSrc = BlendFactor::SrcAlpha;
m_blendDst = BlendFactor::InvSrcAlpha; m_blendDst = BlendFactor::One;
} } else {
else if (!rootCall.m_call.m_name.compare("HECLAdditive")) diag.reportBackendErr(rootCall.m_loc, "%s backend doesn't handle '%s' root", backendName,
{ rootCall.m_call.m_name.c_str());
m_blendSrc = BlendFactor::SrcAlpha; return;
m_blendDst = BlendFactor::One; }
}
else
{
diag.reportBackendErr(rootCall.m_loc, "%s backend doesn't handle '%s' root",
backendName, rootCall.m_call.m_name.c_str());
return;
}
/* Follow Color Chain */ /* Follow Color Chain */
const IR::Instruction& colorRoot = const IR::Instruction& colorRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(0));
ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(0)); m_colorExpr = RecursiveTraceColor(ir, diag, colorRoot, false);
m_colorExpr = RecursiveTraceColor(ir, diag, colorRoot, false);
/* Follow Alpha Chain */ /* Follow Alpha Chain */
if (rootCall.m_call.m_argInstIdxs.size() > 1) if (rootCall.m_call.m_argInstIdxs.size() > 1) {
{ const IR::Instruction& alphaRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(1));
const IR::Instruction& alphaRoot = m_alphaExpr = RecursiveTraceAlpha(ir, diag, alphaRoot, false);
ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(1)); }
m_alphaExpr = RecursiveTraceAlpha(ir, diag, alphaRoot, false);
}
} }
static const char SWIZZLE_CHARS[] = "rgba"; static const char SWIZZLE_CHARS[] = "rgba";
std::string ProgrammableCommon::EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, std::string ProgrammableCommon::EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const std::string& a, const atInt8 swiz[4]) const const atInt8 swiz[4]) const {
{ std::string retval = a + '.';
std::string retval = a + '.'; for (int i = 0; i < 3; ++i) {
for (int i=0 ; i<3 ; ++i) if (swiz[i] < 0 || swiz[i] > 3)
{ diag.reportBackendErr(loc, "unable to use swizzle as RGB value");
if (swiz[i] < 0 || swiz[i] > 3) retval += SWIZZLE_CHARS[swiz[i]];
diag.reportBackendErr(loc, "unable to use swizzle as RGB value"); }
retval += SWIZZLE_CHARS[swiz[i]]; return retval;
}
return retval;
} }
std::string ProgrammableCommon::EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, std::string ProgrammableCommon::EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const std::string& a, const atInt8 swiz[4]) const const atInt8 swiz[4]) const {
{ std::string retval = a + '.';
std::string retval = a + '.'; if (swiz[0] < 0 || swiz[0] > 3)
if (swiz[0] < 0 || swiz[0] > 3) diag.reportBackendErr(loc, "unable to use swizzle as Alpha value");
diag.reportBackendErr(loc, "unable to use swizzle as Alpha value"); retval += SWIZZLE_CHARS[swiz[0]];
retval += SWIZZLE_CHARS[swiz[0]]; return retval;
return retval;
} }
} } // namespace hecl::Backend

File diff suppressed because it is too large Load Diff

View File

@ -5,183 +5,164 @@
#undef min #undef min
#undef max #undef max
namespace hecl::blender namespace hecl::blender {
{
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec) atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec) {
{ atVec3f res;
atVec3f res; athena::simd_floats resf;
athena::simd_floats resf; athena::simd_floats mtxf[3];
athena::simd_floats mtxf[3]; for (int i = 0; i < 3; ++i)
for (int i = 0; i < 3; ++i) mtx[i].simd.copy_to(mtxf[i]);
mtx[i].simd.copy_to(mtxf[i]); athena::simd_floats vecf(vec.val.simd);
athena::simd_floats vecf(vec.val.simd); resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2] + mtxf[0][3];
resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2] + mtxf[0][3]; resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2] + mtxf[1][3];
resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2] + mtxf[1][3]; resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2] + mtxf[2][3];
resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2] + mtxf[2][3]; res.simd.copy_from(resf);
res.simd.copy_from(resf); return res;
return res;
} }
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec) atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec) {
{ atVec3f res;
atVec3f res; athena::simd_floats resf;
athena::simd_floats resf; athena::simd_floats mtxf[3];
athena::simd_floats mtxf[3]; for (int i = 0; i < 3; ++i)
for (int i = 0; i < 3; ++i) mtx[i].simd.copy_to(mtxf[i]);
mtx[i].simd.copy_to(mtxf[i]); athena::simd_floats vecf(vec.val.simd);
athena::simd_floats vecf(vec.val.simd); resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2];
resf[0] = mtxf[0][0] * vecf[0] + mtxf[0][1] * vecf[1] + mtxf[0][2] * vecf[2]; resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2];
resf[1] = mtxf[1][0] * vecf[0] + mtxf[1][1] * vecf[1] + mtxf[1][2] * vecf[2]; resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2];
resf[2] = mtxf[2][0] * vecf[0] + mtxf[2][1] * vecf[1] + mtxf[2][2] * vecf[2]; res.simd.copy_from(resf);
res.simd.copy_from(resf); return res;
return res;
} }
HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const {
{ /* If skinned, compute max weight vec count */
/* If skinned, compute max weight vec count */ size_t weightCount = 0;
size_t weightCount = 0; for (const SkinBanks::Bank& bank : skinBanks.banks)
for (const SkinBanks::Bank& bank : skinBanks.banks) weightCount = std::max(weightCount, bank.m_boneIdxs.size());
weightCount = std::max(weightCount, bank.m_boneIdxs.size()); size_t weightVecCount = weightCount / 4;
size_t weightVecCount = weightCount / 4; if (weightCount % 4)
if (weightCount % 4) ++weightVecCount;
++weightVecCount;
/* Prepare HMDL meta */ /* Prepare HMDL meta */
HMDLMeta metaOut; HMDLMeta metaOut;
metaOut.topology = topology; metaOut.topology = topology;
metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4; metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4;
metaOut.colorCount = colorLayerCount; metaOut.colorCount = colorLayerCount;
metaOut.uvCount = uvLayerCount; metaOut.uvCount = uvLayerCount;
metaOut.weightCount = weightVecCount; metaOut.weightCount = weightVecCount;
metaOut.bankCount = skinBanks.banks.size(); metaOut.bankCount = skinBanks.banks.size();
/* Total all verts from all surfaces (for ibo length) */ /* Total all verts from all surfaces (for ibo length) */
size_t boundVerts = 0; size_t boundVerts = 0;
for (const Surface& surf : surfaces) for (const Surface& surf : surfaces)
boundVerts += surf.verts.size(); boundVerts += surf.verts.size();
/* Maintain unique vert pool for VBO */ /* Maintain unique vert pool for VBO */
std::vector<std::pair<const Surface*, const Surface::Vert*>> vertPool; std::vector<std::pair<const Surface*, const Surface::Vert*>> vertPool;
vertPool.reserve(boundVerts); vertPool.reserve(boundVerts);
/* Target surfaces representation */ /* Target surfaces representation */
std::vector<HMDLBuffers::Surface> outSurfaces; std::vector<HMDLBuffers::Surface> outSurfaces;
outSurfaces.reserve(surfaces.size()); outSurfaces.reserve(surfaces.size());
/* Index buffer */ /* Index buffer */
std::vector<atUint32> iboData; std::vector<atUint32> iboData;
iboData.reserve(boundVerts); iboData.reserve(boundVerts);
for (const Surface& surf : surfaces) for (const Surface& surf : surfaces) {
{ size_t iboStart = iboData.size();
size_t iboStart = iboData.size(); for (const Surface::Vert& v : surf.verts) {
for (const Surface::Vert& v : surf.verts) if (v.iPos == 0xffffffff) {
{ iboData.push_back(0xffffffff);
if (v.iPos == 0xffffffff) continue;
{ }
iboData.push_back(0xffffffff);
continue;
}
size_t ti = 0; size_t ti = 0;
bool found = false; bool found = false;
for (const std::pair<const Surface*, const Surface::Vert*>& tv : vertPool) for (const std::pair<const Surface*, const Surface::Vert*>& tv : vertPool) {
{ if (v == *tv.second && surf.skinBankIdx == tv.first->skinBankIdx) {
if (v == *tv.second && surf.skinBankIdx == tv.first->skinBankIdx) iboData.push_back(ti);
{ found = true;
iboData.push_back(ti); break;
found = true;
break;
}
++ti;
}
if (!found)
{
iboData.push_back(vertPool.size());
vertPool.emplace_back(&surf, &v);
}
} }
outSurfaces.emplace_back(surf, iboStart, iboData.size() - iboStart); ++ti;
}
if (!found) {
iboData.push_back(vertPool.size());
vertPool.emplace_back(&surf, &v);
}
}
outSurfaces.emplace_back(surf, iboStart, iboData.size() - iboStart);
}
metaOut.vertCount = vertPool.size();
metaOut.indexCount = iboData.size();
size_t vboSz = metaOut.vertCount * metaOut.vertStride;
poolSkinIndex.allocate(vertPool.size());
HMDLBuffers ret(std::move(metaOut), vboSz, iboData, std::move(outSurfaces), skinBanks);
athena::io::MemoryWriter vboW(ret.m_vboData.get(), vboSz);
uint32_t curPoolIdx = 0;
for (const std::pair<const Surface*, const Surface::Vert*>& sv : vertPool) {
const Surface& s = *sv.first;
const Surface::Vert& v = *sv.second;
if (absoluteCoords) {
atVec3f preXfPos = MtxVecMul4RM(sceneXf, pos[v.iPos]);
vboW.writeVec3fLittle(preXfPos);
atVec3f preXfNorm = MtxVecMul3RM(sceneXf, norm[v.iNorm]);
athena::simd_floats f(preXfNorm.simd * preXfNorm.simd);
float mag = f[0] + f[1] + f[2];
if (mag > FLT_EPSILON)
mag = 1.f / std::sqrt(mag);
preXfNorm.simd *= mag;
vboW.writeVec3fLittle(preXfNorm);
} else {
vboW.writeVec3fLittle(pos[v.iPos]);
vboW.writeVec3fLittle(norm[v.iNorm]);
} }
metaOut.vertCount = vertPool.size(); for (size_t i = 0; i < colorLayerCount; ++i) {
metaOut.indexCount = iboData.size(); const Vector3f& c = color[v.iColor[i]];
athena::simd_floats f(c.val.simd);
size_t vboSz = metaOut.vertCount * metaOut.vertStride; vboW.writeUByte(std::max(0, std::min(255, int(f[0] * 255))));
poolSkinIndex.allocate(vertPool.size()); vboW.writeUByte(std::max(0, std::min(255, int(f[1] * 255))));
HMDLBuffers ret(std::move(metaOut), vboSz, iboData, std::move(outSurfaces), skinBanks); vboW.writeUByte(std::max(0, std::min(255, int(f[2] * 255))));
athena::io::MemoryWriter vboW(ret.m_vboData.get(), vboSz); vboW.writeUByte(255);
uint32_t curPoolIdx = 0;
for (const std::pair<const Surface*, const Surface::Vert*>& sv : vertPool)
{
const Surface& s = *sv.first;
const Surface::Vert& v = *sv.second;
if (absoluteCoords)
{
atVec3f preXfPos = MtxVecMul4RM(sceneXf, pos[v.iPos]);
vboW.writeVec3fLittle(preXfPos);
atVec3f preXfNorm = MtxVecMul3RM(sceneXf, norm[v.iNorm]);
athena::simd_floats f(preXfNorm.simd * preXfNorm.simd);
float mag = f[0] + f[1] + f[2];
if (mag > FLT_EPSILON)
mag = 1.f / std::sqrt(mag);
preXfNorm.simd *= mag;
vboW.writeVec3fLittle(preXfNorm);
}
else
{
vboW.writeVec3fLittle(pos[v.iPos]);
vboW.writeVec3fLittle(norm[v.iNorm]);
}
for (size_t i=0 ; i<colorLayerCount ; ++i)
{
const Vector3f& c = color[v.iColor[i]];
athena::simd_floats f(c.val.simd);
vboW.writeUByte(std::max(0, std::min(255, int(f[0] * 255))));
vboW.writeUByte(std::max(0, std::min(255, int(f[1] * 255))));
vboW.writeUByte(std::max(0, std::min(255, int(f[2] * 255))));
vboW.writeUByte(255);
}
for (size_t i=0 ; i<uvLayerCount ; ++i)
vboW.writeVec2fLittle(uv[v.iUv[i]]);
if (weightVecCount)
{
const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx];
const std::vector<SkinBind>& binds = skins[v.iSkin];
auto it = bank.m_boneIdxs.cbegin();
for (size_t i=0 ; i<weightVecCount ; ++i)
{
atVec4f vec = {};
for (size_t j=0 ; j<4 ; ++j)
{
if (it == bank.m_boneIdxs.cend())
break;
for (const SkinBind& bind : binds)
if (bind.boneIdx == *it)
{
vec.simd[j] = bind.weight;
break;
}
++it;
}
vboW.writeVec4fLittle(vec);
}
}
/* mapping pool verts to skin indices */
poolSkinIndex.m_poolToSkinIndex[curPoolIdx] = sv.second->iSkin;
++curPoolIdx;
} }
return ret; for (size_t i = 0; i < uvLayerCount; ++i)
vboW.writeVec2fLittle(uv[v.iUv[i]]);
if (weightVecCount) {
const SkinBanks::Bank& bank = skinBanks.banks[s.skinBankIdx];
const std::vector<SkinBind>& binds = skins[v.iSkin];
auto it = bank.m_boneIdxs.cbegin();
for (size_t i = 0; i < weightVecCount; ++i) {
atVec4f vec = {};
for (size_t j = 0; j < 4; ++j) {
if (it == bank.m_boneIdxs.cend())
break;
for (const SkinBind& bind : binds)
if (bind.boneIdx == *it) {
vec.simd[j] = bind.weight;
break;
}
++it;
}
vboW.writeVec4fLittle(vec);
}
}
/* mapping pool verts to skin indices */
poolSkinIndex.m_poolToSkinIndex[curPoolIdx] = sv.second->iSkin;
++curPoolIdx;
}
return ret;
} }
} } // namespace hecl::blender

View File

@ -2,207 +2,181 @@
#include "athena/FileReader.hpp" #include "athena/FileReader.hpp"
#include <zlib.h> #include <zlib.h>
namespace hecl::blender namespace hecl::blender {
{
void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block) void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block) {
{ atUint32 offset = 0;
atUint32 offset = 0; for (SDNAField& f : fields) {
for (SDNAField& f : fields) const auto& name = block.names[f.name];
{ f.offset = offset;
const auto& name = block.names[f.name]; if (name.front() == '*') {
f.offset = offset; offset += 8;
if (name.front() == '*') } else {
{ atUint32 length = block.tlens[f.type];
offset += 8; auto bracket = name.find('[');
} if (bracket != std::string::npos)
else length *= strtoul(name.data() + bracket + 1, nullptr, 10);
{ offset += length;
atUint32 length = block.tlens[f.type];
auto bracket = name.find('[');
if (bracket != std::string::npos)
length *= strtoul(name.data() + bracket + 1, nullptr, 10);
offset += length;
}
} }
}
} }
const SDNABlock::SDNAStruct::SDNAField* SDNABlock::SDNAStruct::lookupField(const SDNABlock& block, const char* n) const const SDNABlock::SDNAStruct::SDNAField* SDNABlock::SDNAStruct::lookupField(const SDNABlock& block,
{ const char* n) const {
for (const SDNAField& field : fields) for (const SDNAField& field : fields) {
{ const auto& name = block.names[field.name];
const auto& name = block.names[field.name]; auto bracket = name.find('[');
auto bracket = name.find('['); if (bracket != std::string::npos) {
if (bracket != std::string::npos) if (!name.compare(0, bracket, n))
{ return &field;
if (!name.compare(0, bracket, n)) } else if (!name.compare(n))
return &field; return &field;
} }
else if (!name.compare(n)) return nullptr;
return &field;
}
return nullptr;
} }
const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, int& idx) const const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, int& idx) const {
{ idx = 0;
idx = 0; for (const SDNAStruct& strc : strcs) {
for (const SDNAStruct& strc : strcs) const auto& name = types[strc.type];
{ if (!name.compare(n))
const auto& name = types[strc.type]; return &strc;
if (!name.compare(n)) ++idx;
return &strc; }
++idx; return nullptr;
}
return nullptr;
} }
void SDNARead::enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const void SDNARead::enumerate(const std::function<bool(const FileBlock& block, athena::io::MemoryReader& r)>& func) const {
{ athena::io::MemoryReader r(m_data.data(), m_data.size());
athena::io::MemoryReader r(m_data.data(), m_data.size()); r.seek(12);
r.seek(12); while (r.position() < r.length()) {
while (r.position() < r.length()) FileBlock block;
{ block.read(r);
FileBlock block; if (block.type == FOURCC('ENDB'))
block.read(r); break;
if (block.type == FOURCC('ENDB')) athena::io::MemoryReader r2(m_data.data() + r.position(), block.size);
break; if (!func(block, r2))
athena::io::MemoryReader r2(m_data.data() + r.position(), block.size); break;
if (!func(block, r2)) r.seek(block.size);
break; }
r.seek(block.size);
}
} }
SDNARead::SDNARead(SystemStringView path) SDNARead::SDNARead(SystemStringView path) {
{ athena::io::FileReader r(path);
athena::io::FileReader r(path); if (r.hasError())
if (r.hasError()) return;
return;
atUint64 length = r.length(); atUint64 length = r.length();
char magicBuf[7]; char magicBuf[7];
r.readUBytesToBuf(magicBuf, 7); r.readUBytesToBuf(magicBuf, 7);
r.seek(0, athena::Begin); r.seek(0, athena::Begin);
if (strncmp(magicBuf, "BLENDER", 7)) if (strncmp(magicBuf, "BLENDER", 7)) {
{ /* Try gzip decompression */
/* Try gzip decompression */ std::unique_ptr<uint8_t[]> compBuf(new uint8_t[4096]);
std::unique_ptr<uint8_t[]> compBuf(new uint8_t[4096]); m_data.resize((length * 2 + 4095) & ~4095);
m_data.resize((length * 2 + 4095) & ~4095); z_stream zstrm = {};
z_stream zstrm = {}; inflateInit2(&zstrm, 16 + MAX_WBITS);
inflateInit2(&zstrm, 16 + MAX_WBITS); zstrm.next_out = (Bytef*)m_data.data();
zstrm.next_out = (Bytef*)m_data.data(); zstrm.avail_out = m_data.size();
zstrm.avail_out = m_data.size(); zstrm.total_out = 0;
zstrm.total_out = 0;
atUint64 rs; atUint64 rs;
while ((rs = r.readUBytesToBuf(compBuf.get(), 4096))) while ((rs = r.readUBytesToBuf(compBuf.get(), 4096))) {
{ int inflateRet;
int inflateRet; zstrm.next_in = compBuf.get();
zstrm.next_in = compBuf.get(); zstrm.avail_in = rs;
zstrm.avail_in = rs; while (zstrm.avail_in) {
while (zstrm.avail_in) if (!zstrm.avail_out) {
{ zstrm.avail_out = m_data.size();
if (!zstrm.avail_out) m_data.resize(zstrm.avail_out * 2);
{ zstrm.next_out = (Bytef*)m_data.data() + zstrm.avail_out;
zstrm.avail_out = m_data.size();
m_data.resize(zstrm.avail_out * 2);
zstrm.next_out = (Bytef*)m_data.data() + zstrm.avail_out;
}
inflateRet = inflate(&zstrm, Z_NO_FLUSH);
if (inflateRet == Z_STREAM_END)
break;
if (inflateRet != Z_OK)
{
inflateEnd(&zstrm);
m_data = std::vector<uint8_t>();
return;
}
}
if (inflateRet == Z_STREAM_END)
break;
} }
inflateRet = inflate(&zstrm, Z_NO_FLUSH);
inflateEnd(&zstrm); if (inflateRet == Z_STREAM_END)
break;
if (strncmp((char*)m_data.data(), "BLENDER", 7)) if (inflateRet != Z_OK) {
{ inflateEnd(&zstrm);
m_data = std::vector<uint8_t>(); m_data = std::vector<uint8_t>();
return; return;
} }
} }
else if (inflateRet == Z_STREAM_END)
{ break;
m_data.resize(length);
r.readUBytesToBuf(m_data.data(), length);
} }
enumerate([this](const FileBlock& block, athena::io::MemoryReader& r) inflateEnd(&zstrm);
{
if (block.type == FOURCC('DNA1')) if (strncmp((char*)m_data.data(), "BLENDER", 7)) {
{ m_data = std::vector<uint8_t>();
m_sdnaBlock.read(r); return;
for (SDNABlock::SDNAStruct& s : m_sdnaBlock.strcs) }
s.computeOffsets(m_sdnaBlock); } else {
return false; m_data.resize(length);
r.readUBytesToBuf(m_data.data(), length);
}
enumerate([this](const FileBlock& block, athena::io::MemoryReader& r) {
if (block.type == FOURCC('DNA1')) {
m_sdnaBlock.read(r);
for (SDNABlock::SDNAStruct& s : m_sdnaBlock.strcs)
s.computeOffsets(m_sdnaBlock);
return false;
}
return true;
});
}
BlendType GetBlendType(SystemStringView path) {
SDNARead r(path);
if (!r)
return BlendType::None;
int idPropIdx;
const auto* idPropStruct = r.sdnaBlock().lookupStruct("IDProperty", idPropIdx);
if (!idPropStruct)
return BlendType::None;
const auto* typeField = idPropStruct->lookupField(r.sdnaBlock(), "type");
if (!typeField)
return BlendType::None;
atUint32 typeOffset = typeField->offset;
const auto* nameField = idPropStruct->lookupField(r.sdnaBlock(), "name");
if (!nameField)
return BlendType::None;
atUint32 nameOffset = nameField->offset;
const auto* dataField = idPropStruct->lookupField(r.sdnaBlock(), "data");
if (!dataField)
return BlendType::None;
atUint32 dataOffset = dataField->offset;
int idPropDataIdx;
const auto* idPropDataStruct = r.sdnaBlock().lookupStruct("IDPropertyData", idPropDataIdx);
if (!idPropDataStruct)
return BlendType::None;
const auto* valField = idPropDataStruct->lookupField(r.sdnaBlock(), "val");
if (!valField)
return BlendType::None;
atUint32 valOffset = dataOffset + valField->offset;
BlendType ret = BlendType::None;
r.enumerate(
[idPropIdx, typeOffset, nameOffset, valOffset, &ret](const FileBlock& block, athena::io::MemoryReader& r) {
if (block.type == FOURCC('DATA') && block.sdnaIdx == idPropIdx) {
r.seek(typeOffset, athena::Begin);
if (r.readUByte() != 1)
return true;
r.seek(nameOffset, athena::Begin);
if (r.readString() != "hecl_type")
return true;
r.seek(valOffset, athena::Begin);
ret = BlendType(r.readUint32Little());
return false;
} }
return true; return true;
}); });
return ret;
} }
BlendType GetBlendType(SystemStringView path) } // namespace hecl::blender
{
SDNARead r(path);
if (!r)
return BlendType::None;
int idPropIdx;
const auto* idPropStruct = r.sdnaBlock().lookupStruct("IDProperty", idPropIdx);
if (!idPropStruct)
return BlendType::None;
const auto* typeField = idPropStruct->lookupField(r.sdnaBlock(), "type");
if (!typeField)
return BlendType::None;
atUint32 typeOffset = typeField->offset;
const auto* nameField = idPropStruct->lookupField(r.sdnaBlock(), "name");
if (!nameField)
return BlendType::None;
atUint32 nameOffset = nameField->offset;
const auto* dataField = idPropStruct->lookupField(r.sdnaBlock(), "data");
if (!dataField)
return BlendType::None;
atUint32 dataOffset = dataField->offset;
int idPropDataIdx;
const auto* idPropDataStruct = r.sdnaBlock().lookupStruct("IDPropertyData", idPropDataIdx);
if (!idPropDataStruct)
return BlendType::None;
const auto* valField = idPropDataStruct->lookupField(r.sdnaBlock(), "val");
if (!valField)
return BlendType::None;
atUint32 valOffset = dataOffset + valField->offset;
BlendType ret = BlendType::None;
r.enumerate([idPropIdx, typeOffset, nameOffset, valOffset, &ret](const FileBlock& block, athena::io::MemoryReader& r)
{
if (block.type == FOURCC('DATA') && block.sdnaIdx == idPropIdx)
{
r.seek(typeOffset, athena::Begin);
if (r.readUByte() != 1)
return true;
r.seek(nameOffset, athena::Begin);
if (r.readString() != "hecl_type")
return true;
r.seek(valOffset, athena::Begin);
ret = BlendType(r.readUint32Little());
return false;
}
return true;
});
return ret;
}
}

770
hecl/lib/CVar.cpp Executable file → Normal file
View File

@ -6,436 +6,397 @@
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
namespace hecl namespace hecl {
{
extern CVar* com_developer; extern CVar* com_developer;
extern CVar* com_enableCheats; extern CVar* com_enableCheats;
using namespace std::literals; using namespace std::literals;
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags,
: m_mgr(parent) CVarManager& parent)
{ : m_mgr(parent) {
m_name = std::string(name); m_name = std::string(name);
m_value = std::string(value); m_value = std::string(value);
m_defaultValue = std::string(value); m_defaultValue = std::string(value);
m_help = help; m_help = help;
m_type = type; m_type = type;
m_flags = flags; m_flags = flags;
} }
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, CVar::EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, std::string_view value, std::string_view help, CVar::EFlags flags,
: m_mgr(parent) CVarManager& parent)
{ : m_mgr(parent) {
m_flags = flags; m_flags = flags;
m_name = std::string(name); m_name = std::string(name);
m_help = help; m_help = help;
m_type = EType::Literal; m_type = EType::Literal;
// Unlock the cvar for writing if readonly // Unlock the cvar for writing if readonly
unlock(); unlock();
fromLiteral(value); fromLiteral(value);
m_defaultValue = m_value; m_defaultValue = m_value;
// Lock the cvar // Lock the cvar
lock(); lock();
// Clear the modified flag, just incase lock didn't do it. // Clear the modified flag, just incase lock didn't do it.
m_flags = flags; m_flags = flags;
} }
CVar::CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent)
: m_mgr(parent) : m_mgr(parent) {
{ m_name = std::string(name);
m_name = std::string(name); m_help = help;
m_help = help; m_type = EType::Vec4f;
m_type = EType::Vec4f; m_flags = flags;
m_flags = flags;
// Unlock the cvar for writing if readonly // Unlock the cvar for writing if readonly
unlock(); unlock();
fromVec4f(value); fromVec4f(value);
m_defaultValue = m_value; m_defaultValue = m_value;
// Lock the cvar // Lock the cvar
lock(); lock();
// Clear the modified flag, just incase lock didn't do it. // Clear the modified flag, just incase lock didn't do it.
m_flags = flags; m_flags = flags;
} }
CVar::CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent)
: m_mgr(parent) : m_mgr(parent) {
{ m_name = std::string(name);
m_name = std::string(name); m_help = help;
m_help = help; m_type = EType::Float;
m_type = EType::Float; m_flags = flags;
m_flags = flags;
// Unlock the cvar for writing if readonly // Unlock the cvar for writing if readonly
unlock(); unlock();
fromFloat(value); fromFloat(value);
m_defaultValue = m_value; m_defaultValue = m_value;
// Lock the cvar // Lock the cvar
lock(); lock();
// Clear the modified flag, just incase lock didn't do it. // Clear the modified flag, just incase lock didn't do it.
m_flags = flags; m_flags = flags;
} }
CVar::CVar(std::string_view name, bool value, std::string_view help, CVar::EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, bool value, std::string_view help, CVar::EFlags flags, CVarManager& parent)
: m_mgr(parent) : m_mgr(parent) {
{ m_name = std::string(name);
m_name = std::string(name); m_help = help;
m_help = help; m_type = EType::Boolean;
m_type = EType::Boolean; m_flags = flags;
m_flags = flags;
// Unlock the cvar for writing if readonly // Unlock the cvar for writing if readonly
unlock(); unlock();
fromBoolean(value); fromBoolean(value);
m_defaultValue = m_value; m_defaultValue = m_value;
// Lock the cvar // Lock the cvar
lock(); lock();
// Clear the modified flag, just incase lock didn't do it. // Clear the modified flag, just incase lock didn't do it.
m_flags = flags; m_flags = flags;
} }
CVar::CVar(std::string_view name, int value, std::string_view help, CVar::EFlags flags, CVarManager& parent) CVar::CVar(std::string_view name, int value, std::string_view help, CVar::EFlags flags, CVarManager& parent)
: m_mgr(parent) : m_mgr(parent) {
{ m_name = std::string(name);
m_name = std::string(name); m_help = help;
m_help = help; m_type = EType::Integer;
m_type = EType::Integer; m_flags = flags;
m_flags = flags;
// Unlock the cvar for writing if readonly // Unlock the cvar for writing if readonly
unlock(); unlock();
fromInteger(value); fromInteger(value);
m_defaultValue = m_value; m_defaultValue = m_value;
// Lock the cvar // Lock the cvar
lock(); lock();
// Clear the modified flag, just incase lock didn't do it. // Clear the modified flag, just incase lock didn't do it.
m_flags = flags; m_flags = flags;
} }
std::string CVar::help() const std::string CVar::help() const {
{ return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") +
return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") + (isReadOnly() ? " [ReadOnly]" : ""));
(isReadOnly() ? " [ReadOnly]" : ""));
} }
atVec4f CVar::toVec4f(bool* isValid) const atVec4f CVar::toVec4f(bool* isValid) const {
{ if (m_type != EType::Vec4f) {
if (m_type != EType::Vec4f)
{
if (isValid != nullptr)
*isValid = false;
return atVec4f{};
}
if (isValid != nullptr) if (isValid != nullptr)
*isValid = true; *isValid = false;
return atVec4f{};
}
if (isValid != nullptr)
*isValid = true;
atVec4f vec;
athena::simd_floats f;
std::sscanf(m_value.c_str(), "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
vec.simd.copy_from(f);
return vec;
}
float CVar::toFloat(bool* isValid) const {
if (m_type != EType::Float) {
if (isValid)
*isValid = false;
return 0.0f;
}
return strtof(m_value.c_str(), nullptr);
}
bool CVar::toBoolean(bool* isValid) const {
if (m_type != EType::Boolean) {
if (isValid)
*isValid = false;
return false;
}
// We don't want to modify the original value;
std::string tmp = m_value;
athena::utility::tolower(tmp);
if (!tmp.compare("yes") || !tmp.compare("true") || !tmp.compare("1")) {
if (isValid)
*isValid = true;
return true;
} else if (!tmp.compare("no") || !tmp.compare("false") || !tmp.compare("0")) {
if (isValid)
*isValid = true;
return false;
}
if (isValid)
*isValid = false;
return false;
}
int CVar::toInteger(bool* isValid) const {
if (m_type != EType::Integer) {
if (isValid)
*isValid = false;
return 0;
}
return strtol(m_value.c_str(), nullptr, 0);
}
const std::string CVar::toLiteral(bool* isValid) const {
if (m_type != EType::Literal && (com_developer && com_developer->toBoolean())) {
if (isValid != nullptr)
*isValid = false;
} else if (isValid != nullptr)
*isValid = true;
// Even if it's not a literal, it's still safe to return
return m_value;
}
const std::wstring CVar::toWideLiteral(bool* isValid) const {
if (m_type != EType::Literal && (com_developer && com_developer->toBoolean())) {
if (isValid != nullptr)
*isValid = false;
} else if (isValid != nullptr)
*isValid = true;
// Even if it's not a literal, it's still safe to return
return hecl::UTF8ToWide(m_value);
}
bool CVar::fromVec4f(const atVec4f& val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Vec4f)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
athena::simd_floats f(val.simd);
m_value.assign(hecl::Format("%f %f %f %f", f[0], f[1], f[2], f[3]));
m_flags |= EFlags::Modified;
return true;
}
bool CVar::fromFloat(float val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Float)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(hecl::Format("%f", val));
setModified();
return true;
}
bool CVar::fromBoolean(bool val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Boolean)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
if (val)
m_value = "true"sv;
else
m_value = "false"sv;
setModified();
return true;
}
bool CVar::fromInteger(int val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Integer)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value = hecl::Format("%i", val);
setModified();
return true;
}
bool CVar::fromLiteral(std::string_view val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Literal)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(val);
setModified();
return true;
}
bool CVar::fromLiteral(std::wstring_view val) {
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Literal)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(hecl::WideToUTF8(val));
setModified();
return true;
}
bool CVar::fromLiteralToType(std::string_view val, bool setDefault) {
switch (m_type) {
case EType::Literal:
return fromLiteral(val);
case EType::Boolean: {
std::stringstream ss;
ss << std::boolalpha << val;
bool v;
ss >> v;
return fromBoolean(v);
}
case EType::Float: {
std::stringstream ss;
ss << val;
float v;
ss >> v;
return fromFloat(v);
}
case EType::Integer: {
std::stringstream ss;
ss << val;
int v;
ss >> v;
return fromInteger(v);
}
case EType::Vec4f: {
atVec4f vec; atVec4f vec;
athena::simd_floats f; athena::simd_floats f;
std::sscanf(m_value.c_str(), "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]); std::sscanf(val.data(), "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
vec.simd.copy_from(f); vec.simd.copy_from(f);
return fromVec4f(vec);
return vec; }
}
if (setDefault)
m_value = m_defaultValue;
return false;
} }
float CVar::toFloat(bool* isValid) const bool CVar::fromLiteralToType(std::wstring_view val, bool setDefault) {
{ switch (m_type) {
if (m_type != EType::Float) case EType::Literal:
{ return fromLiteral(val);
if (isValid) case EType::Boolean: {
*isValid = false; std::wstringstream ss;
return 0.0f; ss << std::boolalpha << val;
} bool v;
ss >> v;
return strtof(m_value.c_str(), nullptr); return fromBoolean(v);
}
case EType::Float: {
std::wstringstream ss;
ss << val;
float v;
ss >> v;
return fromFloat(v);
}
case EType::Integer: {
std::wstringstream ss;
ss << val;
int v;
ss >> v;
return fromInteger(v);
}
case EType::Vec4f: {
atVec4f vec;
athena::simd_floats f;
std::swscanf(val.data(), L"%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
vec.simd.copy_from(f);
return fromVec4f(vec);
}
}
if (setDefault)
m_value = m_defaultValue;
return false;
} }
bool CVar::toBoolean(bool* isValid) const bool CVar::isModified() const { return int(m_flags & EFlags::Modified) != 0; }
{
if (m_type != EType::Boolean)
{
if (isValid)
*isValid = false;
return false;
}
// We don't want to modify the original value;
std::string tmp = m_value;
athena::utility::tolower(tmp);
if (!tmp.compare("yes") || !tmp.compare("true") || !tmp.compare("1"))
{
if (isValid)
*isValid = true;
return true;
}
else if (!tmp.compare("no") || !tmp.compare("false") || !tmp.compare("0"))
{
if (isValid)
*isValid = true;
return false;
}
if (isValid)
*isValid = false;
return false;
}
int CVar::toInteger(bool* isValid) const
{
if (m_type != EType::Integer)
{
if (isValid)
*isValid = false;
return 0;
}
return strtol(m_value.c_str(), nullptr, 0);
}
const std::string CVar::toLiteral(bool* isValid) const
{
if (m_type != EType::Literal && (com_developer && com_developer->toBoolean()))
{
if (isValid != nullptr)
*isValid = false;
}
else if (isValid != nullptr)
*isValid = true;
// Even if it's not a literal, it's still safe to return
return m_value;
}
const std::wstring CVar::toWideLiteral(bool* isValid) const
{
if (m_type != EType::Literal && (com_developer && com_developer->toBoolean()))
{
if (isValid != nullptr)
*isValid = false;
}
else if (isValid != nullptr)
*isValid = true;
// Even if it's not a literal, it's still safe to return
return hecl::UTF8ToWide(m_value);
}
bool CVar::fromVec4f(const atVec4f& val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Vec4f)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
athena::simd_floats f(val.simd);
m_value.assign(hecl::Format("%f %f %f %f", f[0], f[1], f[2], f[3]));
m_flags |= EFlags::Modified;
return true;
}
bool CVar::fromFloat(float val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Float)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(hecl::Format("%f", val));
setModified();
return true;
}
bool CVar::fromBoolean(bool val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Boolean)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
if (val)
m_value = "true"sv;
else
m_value = "false"sv;
setModified();
return true;
}
bool CVar::fromInteger(int val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Integer)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value = hecl::Format("%i", val);
setModified();
return true;
}
bool CVar::fromLiteral(std::string_view val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Literal)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(val);
setModified();
return true;
}
bool CVar::fromLiteral(std::wstring_view val)
{
if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean()))
return false;
else if (isCheat())
return false;
if (m_type != EType::Literal)
return false;
if (isReadOnly() && (com_developer && !com_developer->toBoolean()))
return false;
m_value.assign(hecl::WideToUTF8(val));
setModified();
return true;
}
bool CVar::fromLiteralToType(std::string_view val, bool setDefault)
{
switch (m_type)
{
case EType::Literal: return fromLiteral(val);
case EType::Boolean:
{
std::stringstream ss;
ss << std::boolalpha << val;
bool v;
ss >> v;
return fromBoolean(v);
}
case EType::Float:
{
std::stringstream ss;
ss << val;
float v;
ss >> v;
return fromFloat(v);
}
case EType::Integer:
{
std::stringstream ss;
ss << val;
int v;
ss >> v;
return fromInteger(v);
}
case EType::Vec4f:
{
atVec4f vec;
athena::simd_floats f;
std::sscanf(val.data(), "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
vec.simd.copy_from(f);
return fromVec4f(vec);
}
}
if (setDefault)
m_value = m_defaultValue;
return false;
}
bool CVar::fromLiteralToType(std::wstring_view val, bool setDefault)
{
switch (m_type)
{
case EType::Literal: return fromLiteral(val);
case EType::Boolean:
{
std::wstringstream ss;
ss << std::boolalpha << val;
bool v;
ss >> v;
return fromBoolean(v);
}
case EType::Float:
{
std::wstringstream ss;
ss << val;
float v;
ss >> v;
return fromFloat(v);
}
case EType::Integer:
{
std::wstringstream ss;
ss << val;
int v;
ss >> v;
return fromInteger(v);
}
case EType::Vec4f:
{
atVec4f vec;
athena::simd_floats f;
std::swscanf(val.data(), L"%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
vec.simd.copy_from(f);
return fromVec4f(vec);
}
}
if (setDefault)
m_value = m_defaultValue;
return false;
}
bool CVar::isModified() const { return int(m_flags & EFlags::Modified) != 0;}
bool CVar::modificationRequiresRestart() const { return int(m_flags & EFlags::ModifyRestart) != 0; } bool CVar::modificationRequiresRestart() const { return int(m_flags & EFlags::ModifyRestart) != 0; }
bool CVar::isReadOnly() const { return int(m_flags & EFlags::ReadOnly) != 0; } bool CVar::isReadOnly() const { return int(m_flags & EFlags::ReadOnly) != 0; }
@ -452,37 +413,30 @@ bool CVar::wasDeserialized() const { return m_wasDeserialized; }
bool CVar::hasDefaultValue() const { return m_defaultValue == m_value; } bool CVar::hasDefaultValue() const { return m_defaultValue == m_value; }
void CVar::clearModified() void CVar::clearModified() {
{ if (!modificationRequiresRestart())
if (!modificationRequiresRestart()) m_flags &= ~EFlags::Modified;
m_flags &= ~EFlags::Modified;
} }
void CVar::setModified() { m_flags |= EFlags::Modified; } void CVar::setModified() { m_flags |= EFlags::Modified; }
void CVar::unlock() void CVar::unlock() {
{ if (isReadOnly() && !m_unlocked) {
if (isReadOnly() && !m_unlocked) m_oldFlags = m_flags;
{ m_flags &= ~EFlags::ReadOnly;
m_oldFlags = m_flags; m_unlocked = true;
m_flags &= ~EFlags::ReadOnly; }
m_unlocked = true;
}
} }
void CVar::lock() void CVar::lock() {
{ if (!isReadOnly() && m_unlocked) {
if (!isReadOnly() && m_unlocked) m_flags = m_oldFlags;
{ m_unlocked = false;
m_flags = m_oldFlags; }
m_unlocked = false;
}
} }
void CVar::dispatch() void CVar::dispatch() {
{ for (const ListenerFunc& listen : m_listeners)
for (const ListenerFunc& listen : m_listeners) listen(this);
listen(this);
} }
} } // namespace hecl

489
hecl/lib/CVarManager.cpp Executable file → Normal file
View File

@ -7,8 +7,7 @@
#include <memory> #include <memory>
#include <regex> #include <regex>
namespace hecl namespace hecl {
{
CVar* com_developer = nullptr; CVar* com_developer = nullptr;
CVar* com_configfile = nullptr; CVar* com_configfile = nullptr;
@ -19,333 +18,289 @@ CVarManager* CVarManager::m_instance = nullptr;
static logvisor::Module CVarLog("CVarManager"); static logvisor::Module CVarLog("CVarManager");
CVarManager::CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary) CVarManager::CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary)
: m_store(store), : m_store(store), m_useBinary(useBinary) {
m_useBinary(useBinary) m_instance = this;
{ com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System);
m_instance = this; com_developer = newCVar("developer", "Enables developer mode", false,
com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System); (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::InternalArchivable));
com_developer = newCVar("developer", "Enables developer mode", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::InternalArchivable)); com_enableCheats =
com_enableCheats = newCVar("cheats", "Enable cheats", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::Hidden)); newCVar("cheats", "Enable cheats", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::Hidden));
} }
CVarManager::~CVarManager() CVarManager::~CVarManager() {}
{
CVar* CVarManager::registerCVar(std::unique_ptr<CVar>&& cvar) {
std::string tmp(cvar->name());
athena::utility::tolower(tmp);
if (m_cvars.find(tmp) != m_cvars.end())
return nullptr;
CVar* ret = cvar.get();
m_cvars[tmp] = std::move(cvar);
return ret;
} }
CVar* CVarManager::registerCVar(std::unique_ptr<CVar>&& cvar) CVar* CVarManager::findCVar(std::string_view name) {
{ std::string lower(name);
std::string tmp(cvar->name()); athena::utility::tolower(lower);
athena::utility::tolower(tmp); auto search = m_cvars.find(lower);
if (m_cvars.find(tmp) != m_cvars.end()) if (search == m_cvars.end())
return nullptr; return nullptr;
CVar* ret = cvar.get(); return search->second.get();
m_cvars[tmp] = std::move(cvar);
return ret;
} }
CVar* CVarManager::findCVar(std::string_view name) std::vector<CVar*> CVarManager::archivedCVars() const {
{ std::vector<CVar*> ret;
std::string lower(name); for (const auto& pair : m_cvars)
athena::utility::tolower(lower); if (pair.second->isArchive())
auto search = m_cvars.find(lower); ret.push_back(pair.second.get());
if (search == m_cvars.end())
return nullptr;
return search->second.get(); return ret;
} }
std::vector<CVar*> CVarManager::archivedCVars() const std::vector<CVar*> CVarManager::cvars(CVar::EFlags filter) const {
{ std::vector<CVar*> ret;
std::vector<CVar*> ret; for (const auto& pair : m_cvars)
for (const auto& pair : m_cvars) if (filter == CVar::EFlags::None || (pair.second->flags() & filter) != 0)
if (pair.second->isArchive()) ret.push_back(pair.second.get());
ret.push_back(pair.second.get());
return ret; return ret;
} }
std::vector<CVar*> CVarManager::cvars(CVar::EFlags filter) const void CVarManager::deserialize(CVar* cvar) {
{ if (!cvar)
std::vector<CVar*> ret; return;
for (const auto& pair : m_cvars)
if (filter == CVar::EFlags::None || (pair.second->flags() & filter) != 0)
ret.push_back(pair.second.get());
return ret; /* First let's check for a deferred value */
} std::string lowName = cvar->name().data();
athena::utility::tolower(lowName);
if (m_deferedCVars.find(lowName) != m_deferedCVars.end()) {
std::string val = m_deferedCVars[lowName];
m_deferedCVars.erase(lowName);
if (cvar->fromLiteralToType(val))
return;
}
void CVarManager::deserialize(CVar* cvar) /* We were either unable to find a deferred value or got an invalid value */
{ if (!cvar->isArchive() && !cvar->isInternalArchivable())
if (!cvar) return;
return;
/* First let's check for a deferred value */
std::string lowName = cvar->name().data();
athena::utility::tolower(lowName);
if (m_deferedCVars.find(lowName) != m_deferedCVars.end())
{
std::string val = m_deferedCVars[lowName];
m_deferedCVars.erase(lowName);
if (cvar->fromLiteralToType(val))
return;
}
/* We were either unable to find a deferred value or got an invalid value */
if (!cvar->isArchive() && !cvar->isInternalArchivable())
return;
#if _WIN32 #if _WIN32
hecl::SystemString filename = hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral(); hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral();
#else #else
hecl::SystemString filename = hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral(); hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral();
#endif #endif
hecl::Sstat st; hecl::Sstat st;
if (m_useBinary) if (m_useBinary) {
{ CVarContainer container;
CVarContainer container; filename += _SYS_STR(".bin");
filename += _SYS_STR(".bin"); if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode)) return;
return; athena::io::FileReader reader(filename);
athena::io::FileReader reader(filename); if (reader.isOpen())
if (reader.isOpen()) container.read(reader);
container.read(reader);
if (container.cvars.size() > 0) if (container.cvars.size() > 0) {
{ auto serialized = std::find_if(container.cvars.begin(), container.cvars.end(),
auto serialized = std::find_if(container.cvars.begin(), [&cvar](const DNACVAR::CVar& c) { return c.m_name == cvar->name(); });
container.cvars.end(),
[&cvar](const DNACVAR::CVar& c) { return c.m_name == cvar->name(); });
if (serialized != container.cvars.end()) if (serialized != container.cvars.end()) {
{ DNACVAR::CVar& tmp = *serialized;
DNACVAR::CVar& tmp = *serialized;
if (cvar->m_value != tmp.m_value) if (cvar->m_value != tmp.m_value) {
{ cvar->unlock();
cvar->unlock(); cvar->fromLiteralToType(tmp.m_value, true);
cvar->fromLiteralToType(tmp.m_value, true); cvar->m_wasDeserialized = true;
cvar->m_wasDeserialized = true; cvar->lock();
cvar->lock();
}
}
} }
}
} }
else } else {
{ filename += _SYS_STR(".yaml");
filename += _SYS_STR(".yaml"); if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode)) return;
return; athena::io::FileReader reader(filename);
athena::io::FileReader reader(filename); if (reader.isOpen()) {
if (reader.isOpen()) athena::io::YAMLDocReader docReader;
{ if (docReader.parse(&reader)) {
athena::io::YAMLDocReader docReader; std::unique_ptr<athena::io::YAMLNode> root = docReader.releaseRootNode();
if (docReader.parse(&reader)) auto serialized = std::find_if(root->m_mapChildren.begin(), root->m_mapChildren.end(),
{ [&cvar](const auto& c) { return c.first == cvar->name(); });
std::unique_ptr<athena::io::YAMLNode> root = docReader.releaseRootNode();
auto serialized = std::find_if(root->m_mapChildren.begin(),
root->m_mapChildren.end(),
[&cvar](const auto& c) { return c.first == cvar->name(); });
if (serialized != root->m_mapChildren.end()) if (serialized != root->m_mapChildren.end()) {
{ const std::unique_ptr<athena::io::YAMLNode>& tmp = serialized->second;
const std::unique_ptr<athena::io::YAMLNode>& tmp = serialized->second;
if (cvar->m_value != tmp->m_scalarString) if (cvar->m_value != tmp->m_scalarString) {
{ cvar->unlock();
cvar->unlock(); cvar->fromLiteralToType(tmp->m_scalarString, true);
cvar->fromLiteralToType(tmp->m_scalarString, true); cvar->m_wasDeserialized = true;
cvar->m_wasDeserialized = true; cvar->lock();
cvar->lock(); }
}
}
}
} }
}
} }
}
} }
void CVarManager::serialize() void CVarManager::serialize() {
{
#if _WIN32 #if _WIN32
hecl::SystemString filename = hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral(); hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toWideLiteral();
#else #else
hecl::SystemString filename = hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral(); hecl::SystemString filename =
hecl::SystemString(m_store.getStoreRoot()) + _SYS_STR('/') + com_configfile->toLiteral();
#endif #endif
if (m_useBinary) if (m_useBinary) {
{ CVarContainer container;
CVarContainer container; for (const auto& pair : m_cvars)
for (const auto& pair : m_cvars) if (pair.second->isArchive() ||
if (pair.second->isArchive() || (pair.second->isInternalArchivable() && (pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
pair.second->wasDeserialized() && !pair.second->hasDefaultValue())) container.cvars.push_back(*pair.second);
container.cvars.push_back(*pair.second); container.cvarCount = atUint32(container.cvars.size());
container.cvarCount = atUint32(container.cvars.size());
filename += _SYS_STR(".bin"); filename += _SYS_STR(".bin");
athena::io::FileWriter writer(filename); athena::io::FileWriter writer(filename);
if (writer.isOpen()) if (writer.isOpen())
container.write(writer); container.write(writer);
} } else {
else filename += _SYS_STR(".yaml");
{
filename += _SYS_STR(".yaml");
athena::io::FileReader r(filename); athena::io::FileReader r(filename);
athena::io::YAMLDocWriter docWriter(nullptr, r.isOpen() ? &r : nullptr); athena::io::YAMLDocWriter docWriter(nullptr, r.isOpen() ? &r : nullptr);
r.close(); r.close();
docWriter.setStyle(athena::io::YAMLNodeStyle::Block); docWriter.setStyle(athena::io::YAMLNodeStyle::Block);
for (const auto& pair : m_cvars) for (const auto& pair : m_cvars)
if (pair.second->isArchive() || (pair.second->isInternalArchivable() && if (pair.second->isArchive() ||
pair.second->wasDeserialized() && !pair.second->hasDefaultValue())) (pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
docWriter.writeString(pair.second->name().data(), pair.second->toLiteral()); docWriter.writeString(pair.second->name().data(), pair.second->toLiteral());
athena::io::FileWriter w(filename); athena::io::FileWriter w(filename);
if (w.isOpen()) if (w.isOpen())
docWriter.finish(&w); docWriter.finish(&w);
} }
} }
CVarManager* CVarManager::instance() CVarManager* CVarManager::instance() { return m_instance; }
{
return m_instance; void CVarManager::list(Console* con, const std::vector<std::string>& /*args*/) {
for (const auto& cvar : m_cvars) {
if (!cvar.second->isHidden())
con->report(Console::Level::Info, "%s: %s", cvar.second->name().data(), cvar.second->help().c_str());
}
} }
void CVarManager::list(Console* con, const std::vector<std::string>& /*args*/) void CVarManager::setCVar(Console* con, const std::vector<std::string>& args) {
{ if (args.size() < 2) {
for (const auto& cvar : m_cvars) con->report(Console::Level::Info, "Usage setCvar <cvar> <value>");
{ return;
if (!cvar.second->isHidden()) }
con->report(Console::Level::Info, "%s: %s", cvar.second->name().data(), cvar.second->help().c_str());
} std::string cvName = args[0];
athena::utility::tolower(cvName);
if (m_cvars.find(cvName) == m_cvars.end()) {
con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str());
return;
}
const auto& cv = m_cvars[cvName];
std::string oldVal = cv->value();
std::string value = args[1];
auto it = args.begin() + 2;
for (; it != args.end(); ++it)
value += " " + *it;
/* Check to make sure we're not redundantly assigning the value */
if (cv->value() == value)
return;
if (!cv->fromLiteralToType(value))
con->report(Console::Level::Warning, "Unable to set cvar '%s' to value '%s'", cv->name().data(), value.c_str());
else
con->report(Console::Level::Info, "Set '%s' from '%s' to '%s'", cv->name().data(), oldVal.c_str(), value.c_str());
} }
void CVarManager::setCVar(Console* con, const std::vector<std::string> &args) void CVarManager::getCVar(Console* con, const std::vector<std::string>& args) {
{ if (args.empty()) {
if (args.size() < 2) con->report(Console::Level::Info, "Usage getCVar <cvar>");
{ return;
con->report(Console::Level::Info, "Usage setCvar <cvar> <value>"); }
return;
}
std::string cvName = args[0]; std::string cvName = args[0];
athena::utility::tolower(cvName); athena::utility::tolower(cvName);
if (m_cvars.find(cvName) == m_cvars.end()) if (m_cvars.find(cvName) == m_cvars.end()) {
{ con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str());
con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str()); return;
return; }
}
const auto& cv = m_cvars[cvName]; const auto& cv = m_cvars[cvName];
std::string oldVal = cv->value(); con->report(Console::Level::Info, "'%s' = '%s'", cv->name().data(), cv->value().c_str());
std::string value = args[1];
auto it = args.begin() + 2;
for (; it != args.end(); ++it)
value += " " + *it;
/* Check to make sure we're not redundantly assigning the value */
if (cv->value() == value)
return;
if (!cv->fromLiteralToType(value))
con->report(Console::Level::Warning, "Unable to set cvar '%s' to value '%s'", cv->name().data(), value.c_str());
else
con->report(Console::Level::Info, "Set '%s' from '%s' to '%s'", cv->name().data(), oldVal.c_str(), value.c_str());
} }
void CVarManager::getCVar(Console* con, const std::vector<std::string> &args) void CVarManager::setDeveloperMode(bool v, bool setDeserialized) {
{ com_developer->unlock();
if (args.empty()) com_developer->fromBoolean(v);
{ if (setDeserialized)
con->report(Console::Level::Info, "Usage getCVar <cvar>"); com_developer->m_wasDeserialized = true;
return; com_developer->lock();
} com_developer->setModified();
std::string cvName = args[0];
athena::utility::tolower(cvName);
if (m_cvars.find(cvName) == m_cvars.end())
{
con->report(Console::Level::Error, "CVar '%s' does not exist", args[0].c_str());
return;
}
const auto& cv = m_cvars[cvName];
con->report(Console::Level::Info, "'%s' = '%s'", cv->name().data(), cv->value().c_str());
} }
void CVarManager::setDeveloperMode(bool v, bool setDeserialized) bool CVarManager::restartRequired() const {
{ for (const auto& cv : m_cvars) {
com_developer->unlock(); if (cv.second->isModified() && cv.second->modificationRequiresRestart())
com_developer->fromBoolean(v); return true;
if (setDeserialized) }
com_developer->m_wasDeserialized = true;
com_developer->lock();
com_developer->setModified();
return false;
} }
bool CVarManager::restartRequired() const void CVarManager::parseCommandLine(const std::vector<SystemString>& args) {
{ bool oldDeveloper = suppressDeveloper();
for (const auto& cv : m_cvars) std::string developerName = com_developer->name().data();
{ athena::utility::tolower(developerName);
if (cv.second->isModified() && cv.second->modificationRequiresRestart()) for (const SystemString& arg : args) {
return true; if (arg[0] == _SYS_STR('+')) {
} std::string tmp = SystemUTF8Conv(arg).c_str();
return false; std::smatch matches;
} if (std::regex_match(tmp, matches, cmdLineRegex)) {
std::string cvarName = matches[1].str();
void CVarManager::parseCommandLine(const std::vector<SystemString>& args) std::string cvarValue = matches[2].str();
{ if (CVar* cv = findCVar(cvarName)) {
bool oldDeveloper = suppressDeveloper(); cv->fromLiteralToType(cvarValue);
std::string developerName = com_developer->name().data(); athena::utility::tolower(cvarName);
athena::utility::tolower(developerName); if (developerName == cvarName)
for (const SystemString& arg : args) /* Make sure we're not overriding developer mode when we restore */
{ oldDeveloper = com_developer->toBoolean();
if (arg[0] == _SYS_STR('+')) } else {
{ /* Unable to find an existing CVar, let's defer for the time being 8 */
std::string tmp = SystemUTF8Conv(arg).c_str(); athena::utility::tolower(cvarName);
m_deferedCVars[cvarName] = cvarValue;
std::smatch matches;
if (std::regex_match(tmp, matches, cmdLineRegex))
{
std::string cvarName = matches[1].str();
std::string cvarValue = matches[2].str();
if (CVar* cv = findCVar(cvarName))
{
cv->fromLiteralToType(cvarValue);
athena::utility::tolower(cvarName);
if (developerName == cvarName)
/* Make sure we're not overriding developer mode when we restore */
oldDeveloper = com_developer->toBoolean();
}
else
{
/* Unable to find an existing CVar, let's defer for the time being 8 */
athena::utility::tolower(cvarName);
m_deferedCVars[cvarName] = cvarValue;
}
}
} }
}
} }
}
restoreDeveloper(oldDeveloper); restoreDeveloper(oldDeveloper);
} }
bool CVarManager::suppressDeveloper() bool CVarManager::suppressDeveloper() {
{ bool oldDeveloper = com_developer->toBoolean();
bool oldDeveloper = com_developer->toBoolean(); CVarUnlocker unlock(com_developer);
CVarUnlocker unlock(com_developer); com_developer->fromBoolean(true);
com_developer->fromBoolean(true);
return oldDeveloper; return oldDeveloper;
} }
void CVarManager::restoreDeveloper(bool oldDeveloper) void CVarManager::restoreDeveloper(bool oldDeveloper) {
{ CVarUnlocker unlock(com_developer);
CVarUnlocker unlock(com_developer); com_developer->fromBoolean(oldDeveloper);
com_developer->fromBoolean(oldDeveloper);
} }
} } // namespace hecl

View File

@ -13,225 +13,192 @@
#define HECL_MULTIPROCESSOR 1 #define HECL_MULTIPROCESSOR 1
namespace hecl namespace hecl {
{
static logvisor::Module CP_Log("hecl::ClientProcess"); static logvisor::Module CP_Log("hecl::ClientProcess");
ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker; ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker;
static int GetCPUCount() static int GetCPUCount() {
{
#if _WIN32 #if _WIN32
SYSTEM_INFO sysinfo; SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo); GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors; return sysinfo.dwNumberOfProcessors;
#else #else
return sysconf(_SC_NPROCESSORS_ONLN); return sysconf(_SC_NPROCESSORS_ONLN);
#endif #endif
} }
void ClientProcess::BufferTransaction::run(blender::Token& btok) void ClientProcess::BufferTransaction::run(blender::Token& btok) {
{ athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false); if (r.hasError()) {
if (r.hasError()) CP_Log.report(logvisor::Fatal, _SYS_STR("unable to background-buffer '%s'"), m_path.getAbsolutePath().data());
{ return;
CP_Log.report(logvisor::Fatal, _SYS_STR("unable to background-buffer '%s'"), }
m_path.getAbsolutePath().data()); if (m_offset)
return; r.seek(m_offset, athena::Begin);
r.readBytesToBuf(m_targetBuf, m_maxLen);
m_complete = true;
}
void ClientProcess::CookTransaction::run(blender::Token& btok) {
m_dataSpec->setThreadProject();
m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok, m_force, m_fast);
std::unique_lock<std::mutex> lk(m_parent.m_mutex);
++m_parent.m_completedCooks;
m_parent.m_progPrinter->setMainFactor(m_parent.m_completedCooks / float(m_parent.m_addedCooks));
m_complete = true;
}
void ClientProcess::LambdaTransaction::run(blender::Token& btok) {
m_func(btok);
m_complete = true;
}
ClientProcess::Worker::Worker(ClientProcess& proc, int idx) : m_proc(proc), m_idx(idx) {
m_thr = std::thread(std::bind(&Worker::proc, this));
}
void ClientProcess::Worker::proc() {
ClientProcess::ThreadWorker.reset(this);
char thrName[64];
snprintf(thrName, 64, "HECL Worker %d", m_idx);
logvisor::RegisterThreadName(thrName);
std::unique_lock<std::mutex> lk(m_proc.m_mutex);
while (m_proc.m_running) {
if (!m_didInit) {
m_proc.m_initCv.notify_one();
m_didInit = true;
} }
if (m_offset) while (m_proc.m_running && m_proc.m_pendingQueue.size()) {
r.seek(m_offset, athena::Begin); std::shared_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
r.readBytesToBuf(m_targetBuf, m_maxLen); ++m_proc.m_inProgress;
m_complete = true; m_proc.m_pendingQueue.pop_front();
} lk.unlock();
trans->run(m_blendTok);
void ClientProcess::CookTransaction::run(blender::Token& btok) lk.lock();
{ m_proc.m_completedQueue.push_back(std::move(trans));
m_dataSpec->setThreadProject(); --m_proc.m_inProgress;
m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok, m_force, m_fast);
std::unique_lock<std::mutex> lk(m_parent.m_mutex);
++m_parent.m_completedCooks;
m_parent.m_progPrinter->setMainFactor(m_parent.m_completedCooks / float(m_parent.m_addedCooks));
m_complete = true;
}
void ClientProcess::LambdaTransaction::run(blender::Token& btok)
{
m_func(btok);
m_complete = true;
}
ClientProcess::Worker::Worker(ClientProcess& proc, int idx)
: m_proc(proc), m_idx(idx)
{
m_thr = std::thread(std::bind(&Worker::proc, this));
}
void ClientProcess::Worker::proc()
{
ClientProcess::ThreadWorker.reset(this);
char thrName[64];
snprintf(thrName, 64, "HECL Worker %d", m_idx);
logvisor::RegisterThreadName(thrName);
std::unique_lock<std::mutex> lk(m_proc.m_mutex);
while (m_proc.m_running)
{
if (!m_didInit)
{
m_proc.m_initCv.notify_one();
m_didInit = true;
}
while (m_proc.m_running && m_proc.m_pendingQueue.size())
{
std::shared_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
++m_proc.m_inProgress;
m_proc.m_pendingQueue.pop_front();
lk.unlock();
trans->run(m_blendTok);
lk.lock();
m_proc.m_completedQueue.push_back(std::move(trans));
--m_proc.m_inProgress;
}
m_proc.m_waitCv.notify_one();
if (!m_proc.m_running)
break;
m_proc.m_cv.wait(lk);
} }
lk.unlock(); m_proc.m_waitCv.notify_one();
m_blendTok.shutdown(); if (!m_proc.m_running)
break;
m_proc.m_cv.wait(lk);
}
lk.unlock();
m_blendTok.shutdown();
} }
ClientProcess::ClientProcess(const MultiProgressPrinter* progPrinter) ClientProcess::ClientProcess(const MultiProgressPrinter* progPrinter) : m_progPrinter(progPrinter) {
: m_progPrinter(progPrinter)
{
#if HECL_MULTIPROCESSOR #if HECL_MULTIPROCESSOR
const int cpuCount = GetCPUCount(); const int cpuCount = GetCPUCount();
#else #else
constexpr int cpuCount = 1; constexpr int cpuCount = 1;
#endif #endif
m_workers.reserve(cpuCount); m_workers.reserve(cpuCount);
for (int i=0 ; i<cpuCount ; ++i) for (int i = 0; i < cpuCount; ++i) {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); m_workers.emplace_back(*this, m_workers.size());
m_workers.emplace_back(*this, m_workers.size()); m_initCv.wait(lk);
m_initCv.wait(lk); }
}
} }
std::shared_ptr<const ClientProcess::BufferTransaction> std::shared_ptr<const ClientProcess::BufferTransaction> ClientProcess::addBufferTransaction(const ProjectPath& path,
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target, void* target, size_t maxLen,
size_t maxLen, size_t offset) size_t offset) {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset); m_pendingQueue.emplace_back(ret);
m_pendingQueue.emplace_back(ret); m_cv.notify_one();
m_cv.notify_one(); return ret;
return ret;
} }
std::shared_ptr<const ClientProcess::CookTransaction> std::shared_ptr<const ClientProcess::CookTransaction> ClientProcess::addCookTransaction(const hecl::ProjectPath& path,
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, bool force, bool force, bool fast,
bool fast, Database::IDataSpec* spec) Database::IDataSpec* spec) {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); auto ret = std::make_shared<CookTransaction>(*this, path, force, fast, spec);
auto ret = std::make_shared<CookTransaction>(*this, path, force, fast, spec); m_pendingQueue.emplace_back(ret);
m_pendingQueue.emplace_back(ret); m_cv.notify_one();
m_cv.notify_one(); ++m_addedCooks;
++m_addedCooks; m_progPrinter->setMainFactor(m_completedCooks / float(m_addedCooks));
m_progPrinter->setMainFactor(m_completedCooks / float(m_addedCooks)); return ret;
return ret;
} }
std::shared_ptr<const ClientProcess::LambdaTransaction> std::shared_ptr<const ClientProcess::LambdaTransaction>
ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func) ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func) {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func)); m_pendingQueue.emplace_back(ret);
m_pendingQueue.emplace_back(ret); m_cv.notify_one();
m_cv.notify_one(); return ret;
return ret;
} }
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force,
bool force, bool fast) bool fast) {
{ if (spec->canCook(path, btok)) {
if (spec->canCook(path, btok)) const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
{ if (specEnt) {
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok); hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
if (specEnt) if (fast)
{ cooked = cooked.getWithExtension(_SYS_STR(".fast"));
hecl::ProjectPath cooked = path.getCookedPath(*specEnt); cooked.makeDirChain(false);
if (fast) if (force || cooked.getPathType() == ProjectPath::Type::None || path.getModtime() > cooked.getModtime()) {
cooked = cooked.getWithExtension(_SYS_STR(".fast")); if (m_progPrinter) {
cooked.makeDirChain(false); hecl::SystemString str;
if (force || cooked.getPathType() == ProjectPath::Type::None || if (path.getAuxInfo().empty())
path.getModtime() > cooked.getModtime()) str = hecl::SysFormat(_SYS_STR("Cooking %s"), path.getRelativePath().data());
{ else
if (m_progPrinter) str = hecl::SysFormat(_SYS_STR("Cooking %s|%s"), path.getRelativePath().data(), path.getAuxInfo().data());
{ m_progPrinter->print(str.c_str(), nullptr, -1.f, hecl::ClientProcess::GetThreadWorkerIdx());
hecl::SystemString str; m_progPrinter->flush();
if (path.getAuxInfo().empty()) } else {
str = hecl::SysFormat(_SYS_STR("Cooking %s"), path.getRelativePath().data()); if (path.getAuxInfo().empty())
else LogModule.report(logvisor::Info, _SYS_STR("Cooking %s"), path.getRelativePath().data());
str = hecl::SysFormat(_SYS_STR("Cooking %s|%s"), path.getRelativePath().data(), path.getAuxInfo().data()); else
m_progPrinter->print(str.c_str(), nullptr, -1.f, hecl::ClientProcess::GetThreadWorkerIdx()); LogModule.report(logvisor::Info, _SYS_STR("Cooking %s|%s"), path.getRelativePath().data(),
m_progPrinter->flush(); path.getAuxInfo().data());
}
else
{
if (path.getAuxInfo().empty())
LogModule.report(logvisor::Info, _SYS_STR("Cooking %s"),
path.getRelativePath().data());
else
LogModule.report(logvisor::Info, _SYS_STR("Cooking %s|%s"),
path.getRelativePath().data(),
path.getAuxInfo().data());
}
spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
if (m_progPrinter)
{
hecl::SystemString str;
if (path.getAuxInfo().empty())
str = hecl::SysFormat(_SYS_STR("Cooked %s"), path.getRelativePath().data());
else
str = hecl::SysFormat(_SYS_STR("Cooked %s|%s"), path.getRelativePath().data(), path.getAuxInfo().data());
m_progPrinter->print(str.c_str(), nullptr, -1.f, hecl::ClientProcess::GetThreadWorkerIdx());
m_progPrinter->flush();
}
}
return true;
} }
spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
if (m_progPrinter) {
hecl::SystemString str;
if (path.getAuxInfo().empty())
str = hecl::SysFormat(_SYS_STR("Cooked %s"), path.getRelativePath().data());
else
str = hecl::SysFormat(_SYS_STR("Cooked %s|%s"), path.getRelativePath().data(), path.getAuxInfo().data());
m_progPrinter->print(str.c_str(), nullptr, -1.f, hecl::ClientProcess::GetThreadWorkerIdx());
m_progPrinter->flush();
}
}
return true;
} }
return false; }
return false;
} }
void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue) void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue) {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); queue.swap(m_completedQueue);
queue.swap(m_completedQueue);
} }
void ClientProcess::waitUntilComplete() void ClientProcess::waitUntilComplete() {
{ std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); while (isBusy())
while (isBusy()) m_waitCv.wait(lk);
m_waitCv.wait(lk);
} }
void ClientProcess::shutdown() void ClientProcess::shutdown() {
{ if (!m_running)
if (!m_running) return;
return; std::unique_lock<std::mutex> lk(m_mutex);
std::unique_lock<std::mutex> lk(m_mutex); m_pendingQueue.clear();
m_pendingQueue.clear(); m_running = false;
m_running = false; m_cv.notify_all();
m_cv.notify_all(); lk.unlock();
lk.unlock(); for (Worker& worker : m_workers)
for (Worker& worker : m_workers) if (worker.m_thr.joinable())
if (worker.m_thr.joinable()) worker.m_thr.join();
worker.m_thr.join();
} }
} } // namespace hecl

View File

@ -14,314 +14,276 @@ extern pD3DCompile D3DCompilePROC;
#include <memory> #include <memory>
#endif #endif
namespace hecl namespace hecl {
{
logvisor::Module Log("hecl::Compilers"); logvisor::Module Log("hecl::Compilers");
namespace PlatformType namespace PlatformType {
{
const char* OpenGL::Name = "OpenGL"; const char* OpenGL::Name = "OpenGL";
const char* Vulkan::Name = "Vulkan"; const char* Vulkan::Name = "Vulkan";
const char* D3D11::Name = "D3D11"; const char* D3D11::Name = "D3D11";
const char* Metal::Name = "Metal"; const char* Metal::Name = "Metal";
const char* NX::Name = "NX"; const char* NX::Name = "NX";
} } // namespace PlatformType
namespace PipelineStage namespace PipelineStage {
{
const char* Null::Name = "Null"; const char* Null::Name = "Null";
const char* Vertex::Name = "Vertex"; const char* Vertex::Name = "Vertex";
const char* Fragment::Name = "Fragment"; const char* Fragment::Name = "Fragment";
const char* Geometry::Name = "Geometry"; const char* Geometry::Name = "Geometry";
const char* Control::Name = "Control"; const char* Control::Name = "Control";
const char* Evaluation::Name = "Evaluation"; const char* Evaluation::Name = "Evaluation";
} } // namespace PipelineStage
template<typename P> struct ShaderCompiler {}; template <typename P>
struct ShaderCompiler {};
template<> struct ShaderCompiler<PlatformType::OpenGL> template <>
{ struct ShaderCompiler<PlatformType::OpenGL> {
template<typename S> template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
{ std::string str = "#version 330\n";
std::string str = "#version 330\n"; str += BOO_GLSL_BINDING_HEAD;
str += BOO_GLSL_BINDING_HEAD; str += text;
str += text; std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1);
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1); memcpy(ret.first.get(), str.data(), ret.second);
memcpy(ret.first.get(), str.data(), ret.second); return ret;
return ret; }
}
}; };
template<> struct ShaderCompiler<PlatformType::Vulkan> template <>
{ struct ShaderCompiler<PlatformType::Vulkan> {
static constexpr EShLanguage ShaderTypes[] = static constexpr EShLanguage ShaderTypes[] = {EShLangVertex, /* Invalid */
{ EShLangVertex, EShLangFragment, EShLangGeometry,
EShLangVertex, /* Invalid */ EShLangTessControl, EShLangTessEvaluation};
EShLangVertex,
EShLangFragment,
EShLangGeometry,
EShLangTessControl,
EShLangTessEvaluation
};
template<typename S> template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
{ EShLanguage lang = ShaderTypes[int(S::Enum)];
EShLanguage lang = ShaderTypes[int(S::Enum)]; const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules); glslang::TShader shader(lang);
glslang::TShader shader(lang); const char* strings[] = {"#version 330\n", BOO_GLSL_BINDING_HEAD, text.data()};
const char* strings[] = { "#version 330\n", BOO_GLSL_BINDING_HEAD, text.data() }; shader.setStrings(strings, 3);
shader.setStrings(strings, 3); if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) {
if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) printf("%s\n", text.data());
{ Log.report(logvisor::Fatal, "unable to compile shader\n%s", shader.getInfoLog());
printf("%s\n", text.data()); return {};
Log.report(logvisor::Fatal, "unable to compile shader\n%s", shader.getInfoLog());
return {};
}
glslang::TProgram prog;
prog.addShader(&shader);
if (!prog.link(messages))
{
Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog());
return {};
}
std::vector<unsigned int> out;
glslang::GlslangToSpv(*prog.getIntermediate(lang), out);
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(out.size() * 4), out.size() * 4);
memcpy(ret.first.get(), out.data(), ret.second);
return ret;
} }
glslang::TProgram prog;
prog.addShader(&shader);
if (!prog.link(messages)) {
Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog());
return {};
}
std::vector<unsigned int> out;
glslang::GlslangToSpv(*prog.getIntermediate(lang), out);
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(out.size() * 4), out.size() * 4);
memcpy(ret.first.get(), out.data(), ret.second);
return ret;
}
}; };
#if _WIN32 #if _WIN32
static const char* D3DShaderTypes[] = static const char* D3DShaderTypes[] = {nullptr, "vs_5_0", "ps_5_0", "gs_5_0", "hs_5_0", "ds_5_0"};
{ template <>
nullptr, struct ShaderCompiler<PlatformType::D3D11> {
"vs_5_0",
"ps_5_0",
"gs_5_0",
"hs_5_0",
"ds_5_0"
};
template<> struct ShaderCompiler<PlatformType::D3D11>
{
#if _DEBUG && 0 #if _DEBUG && 0
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0 #define BOO_D3DCOMPILE_FLAG D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0
#else #else
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_OPTIMIZATION_LEVEL3 #define BOO_D3DCOMPILE_FLAG D3DCOMPILE_OPTIMIZATION_LEVEL3
#endif #endif
template<typename S> template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
{ ComPtr<ID3DBlob> errBlob;
ComPtr<ID3DBlob> errBlob; ComPtr<ID3DBlob> blobOut;
ComPtr<ID3DBlob> blobOut; if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main",
if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main", D3DShaderTypes[int(S::Enum)], BOO_D3DCOMPILE_FLAG, 0, &blobOut, &errBlob))) {
D3DShaderTypes[int(S::Enum)], BOO_D3DCOMPILE_FLAG, 0, &blobOut, &errBlob))) printf("%s\n", text.data());
{ Log.report(logvisor::Fatal, "error compiling shader: %s", errBlob->GetBufferPointer());
printf("%s\n", text.data()); return {};
Log.report(logvisor::Fatal, "error compiling shader: %s", errBlob->GetBufferPointer());
return {};
}
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()),
blobOut->GetBufferSize());
memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
return ret;
} }
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()), blobOut->GetBufferSize());
memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
return ret;
}
}; };
#endif #endif
#if __APPLE__ #if __APPLE__
template<> struct ShaderCompiler<PlatformType::Metal> template <>
{ struct ShaderCompiler<PlatformType::Metal> {
static bool m_didCompilerSearch; static bool m_didCompilerSearch;
static bool m_hasCompiler; static bool m_hasCompiler;
static bool SearchForCompiler() static bool SearchForCompiler() {
{ m_didCompilerSearch = true;
m_didCompilerSearch = true; const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER");
const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER"); if (no_metal_compiler && atoi(no_metal_compiler))
if (no_metal_compiler && atoi(no_metal_compiler)) return false;
return false;
pid_t pid = fork(); pid_t pid = fork();
if (!pid) if (!pid) {
{ execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL);
execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL); /* xcrun returns 72 if metal command not found;
/* xcrun returns 72 if metal command not found; * emulate that if xcrun not found */
* emulate that if xcrun not found */ exit(72);
exit(72);
}
int status, ret;
while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
if (ret < 0)
return false;
return WEXITSTATUS(status) == 1;
} }
template<typename S> int status, ret;
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
{ if (ret < 0)
if (!m_didCompilerSearch) return false;
m_hasCompiler = SearchForCompiler(); return WEXITSTATUS(status) == 1;
}
std::string str = "#include <metal_stdlib>\n" template <typename S>
"using namespace metal;\n"; static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
str += text; if (!m_didCompilerSearch)
std::pair<StageBinaryData, size_t> ret; m_hasCompiler = SearchForCompiler();
if (!m_hasCompiler) std::string str =
{ "#include <metal_stdlib>\n"
/* First byte unset to indicate source data */ "using namespace metal;\n";
ret.first = MakeStageBinaryData(str.size() + 2); str += text;
ret.first.get()[0] = 0; std::pair<StageBinaryData, size_t> ret;
ret.second = str.size() + 2;
memcpy(&ret.first.get()[1], str.data(), str.size() + 1); if (!m_hasCompiler) {
/* First byte unset to indicate source data */
ret.first = MakeStageBinaryData(str.size() + 2);
ret.first.get()[0] = 0;
ret.second = str.size() + 2;
memcpy(&ret.first.get()[1], str.data(), str.size() + 1);
} else {
int compilerOut[2];
int compilerIn[2];
pipe(compilerOut);
pipe(compilerIn);
pid_t pid = getpid();
const char* tmpdir = getenv("TMPDIR");
char libFile[1024];
snprintf(libFile, 1024, "%sboo_metal_shader%d.metallib", tmpdir, pid);
/* Pipe source write to compiler */
pid_t compilerPid = fork();
if (!compilerPid) {
dup2(compilerIn[0], STDIN_FILENO);
dup2(compilerOut[1], STDOUT_FILENO);
close(compilerOut[0]);
close(compilerOut[1]);
close(compilerIn[0]);
close(compilerIn[1]);
execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable",
"-Wno-unused-const-variable", "-Wno-unused-function", "-c", "-x", "metal", "-", NULL);
fprintf(stderr, "execlp fail %s\n", strerror(errno));
exit(1);
}
close(compilerIn[0]);
close(compilerOut[1]);
/* Pipe compiler to linker */
pid_t linkerPid = fork();
if (!linkerPid) {
dup2(compilerOut[0], STDIN_FILENO);
close(compilerOut[0]);
close(compilerIn[1]);
/* metallib doesn't like outputting to a pipe, so temp file will have to do */
execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", libFile, NULL);
fprintf(stderr, "execlp fail %s\n", strerror(errno));
exit(1);
}
close(compilerOut[0]);
/* Stream in source */
const char* inPtr = str.data();
size_t inRem = str.size();
while (inRem) {
ssize_t writeRes = write(compilerIn[1], inPtr, inRem);
if (writeRes < 0) {
fprintf(stderr, "write fail %s\n", strerror(errno));
break;
} }
else inPtr += writeRes;
{ inRem -= writeRes;
int compilerOut[2]; }
int compilerIn[2]; close(compilerIn[1]);
pipe(compilerOut);
pipe(compilerIn);
pid_t pid = getpid(); /* Wait for completion */
const char* tmpdir = getenv("TMPDIR"); int compilerStat, linkerStat;
char libFile[1024]; while (waitpid(compilerPid, &compilerStat, 0) < 0) {
snprintf(libFile, 1024, "%sboo_metal_shader%d.metallib", tmpdir, pid); if (errno == EINTR)
continue;
Log.report(logvisor::Fatal, "waitpid fail %s", strerror(errno));
return {};
}
/* Pipe source write to compiler */ if (WEXITSTATUS(compilerStat)) {
pid_t compilerPid = fork(); Log.report(logvisor::Fatal, "compile fail");
if (!compilerPid) return {};
{ }
dup2(compilerIn[0], STDIN_FILENO);
dup2(compilerOut[1], STDOUT_FILENO);
close(compilerOut[0]); while (waitpid(linkerPid, &linkerStat, 0) < 0) {
close(compilerOut[1]); if (errno == EINTR)
close(compilerIn[0]); continue;
close(compilerIn[1]); Log.report(logvisor::Fatal, "waitpid fail %s", strerror(errno));
return {};
}
execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable", if (WEXITSTATUS(linkerStat)) {
"-Wno-unused-const-variable", "-Wno-unused-function", "-c", "-x", "metal", "-", NULL); Log.report(logvisor::Fatal, "link fail");
fprintf(stderr, "execlp fail %s\n", strerror(errno)); return {};
exit(1); }
}
close(compilerIn[0]);
close(compilerOut[1]);
/* Pipe compiler to linker */ /* Copy temp file into buffer with first byte set to indicate binary data */
pid_t linkerPid = fork(); FILE* fin = fopen(libFile, "rb");
if (!linkerPid) fseek(fin, 0, SEEK_END);
{ long libLen = ftell(fin);
dup2(compilerOut[0], STDIN_FILENO); fseek(fin, 0, SEEK_SET);
ret.first = MakeStageBinaryData(libLen + 1);
close(compilerOut[0]); ret.first.get()[0] = 1;
close(compilerIn[1]); ret.second = libLen + 1;
fread(&ret.first.get()[1], 1, libLen, fin);
/* metallib doesn't like outputting to a pipe, so temp file will have to do */ fclose(fin);
execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", libFile, NULL); unlink(libFile);
fprintf(stderr, "execlp fail %s\n", strerror(errno));
exit(1);
}
close(compilerOut[0]);
/* Stream in source */
const char* inPtr = str.data();
size_t inRem = str.size();
while (inRem)
{
ssize_t writeRes = write(compilerIn[1], inPtr, inRem);
if (writeRes < 0)
{
fprintf(stderr, "write fail %s\n", strerror(errno));
break;
}
inPtr += writeRes;
inRem -= writeRes;
}
close(compilerIn[1]);
/* Wait for completion */
int compilerStat, linkerStat;
while (waitpid(compilerPid, &compilerStat, 0) < 0)
{
if (errno == EINTR)
continue;
Log.report(logvisor::Fatal, "waitpid fail %s", strerror(errno));
return {};
}
if (WEXITSTATUS(compilerStat))
{
Log.report(logvisor::Fatal, "compile fail");
return {};
}
while (waitpid(linkerPid, &linkerStat, 0) < 0)
{
if (errno == EINTR)
continue;
Log.report(logvisor::Fatal, "waitpid fail %s", strerror(errno));
return {};
}
if (WEXITSTATUS(linkerStat))
{
Log.report(logvisor::Fatal, "link fail");
return {};
}
/* Copy temp file into buffer with first byte set to indicate binary data */
FILE* fin = fopen(libFile, "rb");
fseek(fin, 0, SEEK_END);
long libLen = ftell(fin);
fseek(fin, 0, SEEK_SET);
ret.first = MakeStageBinaryData(libLen + 1);
ret.first.get()[0] = 1;
ret.second = libLen + 1;
fread(&ret.first.get()[1], 1, libLen, fin);
fclose(fin);
unlink(libFile);
}
return ret;
} }
return ret;
}
}; };
bool ShaderCompiler<PlatformType::Metal>::m_didCompilerSearch = false; bool ShaderCompiler<PlatformType::Metal>::m_didCompilerSearch = false;
bool ShaderCompiler<PlatformType::Metal>::m_hasCompiler = false; bool ShaderCompiler<PlatformType::Metal>::m_hasCompiler = false;
#endif #endif
#if HECL_NOUVEAU_NX #if HECL_NOUVEAU_NX
template<> struct ShaderCompiler<PlatformType::NX> template <>
{ struct ShaderCompiler<PlatformType::NX> {
template<typename S> template <typename S>
static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text) static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text) {
{ std::string str = "#version 330\n";
std::string str = "#version 330\n"; str += BOO_GLSL_BINDING_HEAD;
str += BOO_GLSL_BINDING_HEAD; str += text;
str += text; std::pair<std::shared_ptr<uint8_t[]>, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1);
std::pair<std::shared_ptr<uint8_t[]>, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1); memcpy(ret.first.get(), str.data(), str.size() + 1);
memcpy(ret.first.get(), str.data(), str.size() + 1); return ret;
return ret; }
}
}; };
#endif #endif
template<typename P, typename S> template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text) std::pair<StageBinaryData, size_t> CompileShader(std::string_view text) {
{ return ShaderCompiler<P>::template Compile<S>(text);
return ShaderCompiler<P>::template Compile<S>(text);
} }
#define SPECIALIZE_COMPILE_SHADER(P) \ #define SPECIALIZE_COMPILE_SHADER(P) \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Vertex>(std::string_view text); \ template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Vertex>(std::string_view text); \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Fragment>(std::string_view text); \ template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Fragment>(std::string_view text); \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Geometry>(std::string_view text); \ template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Geometry>(std::string_view text); \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Control>(std::string_view text); \ template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Control>(std::string_view text); \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Evaluation>(std::string_view text); template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Evaluation>(std::string_view text);
SPECIALIZE_COMPILE_SHADER(PlatformType::OpenGL) SPECIALIZE_COMPILE_SHADER(PlatformType::OpenGL)
SPECIALIZE_COMPILE_SHADER(PlatformType::Vulkan) SPECIALIZE_COMPILE_SHADER(PlatformType::Vulkan)
#if _WIN32 #if _WIN32
@ -334,4 +296,4 @@ SPECIALIZE_COMPILE_SHADER(PlatformType::Metal)
SPECIALIZE_COMPILE_SHADER(PlatformType::NX) SPECIALIZE_COMPILE_SHADER(PlatformType::NX)
#endif #endif
} } // namespace hecl

View File

@ -4,497 +4,424 @@
#include "hecl/hecl.hpp" #include "hecl/hecl.hpp"
#include "athena/Utility.hpp" #include "athena/Utility.hpp"
namespace hecl namespace hecl {
{
Console* Console::m_instance = nullptr; Console* Console::m_instance = nullptr;
Console::Console(CVarManager* cvarMgr) Console::Console(CVarManager* cvarMgr) : m_cvarMgr(cvarMgr), m_overwrite(false), m_cursorAtEnd(false) {
: m_cvarMgr(cvarMgr) m_instance = this;
, m_overwrite(false) registerCommand("help", "Prints information about a given function", "<command>",
, m_cursorAtEnd(false) std::bind(&Console::help, this, std::placeholders::_1, std::placeholders::_2));
{ registerCommand("listCommands", "Prints a list of all available Commands", "",
m_instance = this; std::bind(&Console::listCommands, this, std::placeholders::_1, std::placeholders::_2));
registerCommand("help", "Prints information about a given function", "<command>", std::bind(&Console::help, this, std::placeholders::_1, std::placeholders::_2)); registerCommand("listCVars", "Lists all available CVars", "",
registerCommand("listCommands", "Prints a list of all available Commands", "", std::bind(&Console::listCommands, this, std::placeholders::_1, std::placeholders::_2)); std::bind(&CVarManager::list, m_cvarMgr, std::placeholders::_1, std::placeholders::_2));
registerCommand("listCVars", "Lists all available CVars", "", std::bind(&CVarManager::list, m_cvarMgr, std::placeholders::_1, std::placeholders::_2)); registerCommand("setCVar", "Sets a given Console Variable to the specified value", "<cvar> <value>",
registerCommand("setCVar", "Sets a given Console Variable to the specified value", "<cvar> <value>", std::bind(&CVarManager::setCVar, m_cvarMgr, std::placeholders::_1, std::placeholders::_2)); std::bind(&CVarManager::setCVar, m_cvarMgr, std::placeholders::_1, std::placeholders::_2));
registerCommand("getCVar", "Prints the value stored in the specified Console Variable", "<cvar>", std::bind(&CVarManager::getCVar, m_cvarMgr, std::placeholders::_1, std::placeholders::_2)); registerCommand("getCVar", "Prints the value stored in the specified Console Variable", "<cvar>",
m_conSpeed = cvarMgr->findOrMakeCVar("con_speed", "Speed at which the console opens and closes, calculated as pixels per second", 1.f, std::bind(&CVarManager::getCVar, m_cvarMgr, std::placeholders::_1, std::placeholders::_2));
hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); m_conSpeed = cvarMgr->findOrMakeCVar("con_speed",
m_conHeight = cvarMgr->findOrMakeCVar("con_height", "Maximum absolute height of the console, height is calculated from the top of the window, expects values ranged from [0.f,1.f]", 0.5f, "Speed at which the console opens and closes, calculated as pixels per second",
hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); 1.f, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive);
m_conHeight = cvarMgr->findOrMakeCVar("con_height",
"Maximum absolute height of the console, height is calculated from the top of "
"the window, expects values ranged from [0.f,1.f]",
0.5f, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive);
} }
void Console::registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function<void(Console*, const std::vector<std::string> &)>&& func, SConsoleCommand::ECommandFlags cmdFlags) void Console::registerCommand(std::string_view name, std::string_view helpText, std::string_view usage,
{ const std::function<void(Console*, const std::vector<std::string>&)>&& func,
std::string lowName = name.data(); SConsoleCommand::ECommandFlags cmdFlags) {
athena::utility::tolower(lowName); std::string lowName = name.data();
if (m_commands.find(lowName) == m_commands.end()) athena::utility::tolower(lowName);
m_commands[lowName] = SConsoleCommand{name.data(), helpText.data(), usage.data(), std::move(func), cmdFlags}; if (m_commands.find(lowName) == m_commands.end())
m_commands[lowName] = SConsoleCommand{name.data(), helpText.data(), usage.data(), std::move(func), cmdFlags};
} }
void Console::unregisterCommand(std::string_view name) void Console::unregisterCommand(std::string_view name) {
{ std::string lowName = name.data();
std::string lowName = name.data(); athena::utility::tolower(lowName);
athena::utility::tolower(lowName); if (m_commands.find(lowName) != m_commands.end())
if (m_commands.find(lowName) != m_commands.end()) m_commands.erase(m_commands.find(lowName));
m_commands.erase(m_commands.find(lowName));
} }
void Console::executeString(const std::string& str) void Console::executeString(const std::string& str) {
{ if (str.empty())
if (str.empty()) return;
/* First let's split semi-colon delimited commands */
std::vector<std::string> commands = athena::utility::split(str, ';');
if (commands.empty())
return;
for (std::string command : commands) {
command = athena::utility::trim(command);
std::vector<std::string> tmpArgs = athena::utility::split(command, ' ');
if (tmpArgs.empty())
continue;
std::vector<std::string> args;
args.reserve(tmpArgs.size());
/* detect string literals */
bool isInLiteral = false;
std::string curLiteral;
int depth = 0;
for (std::string arg : tmpArgs) {
if ((arg.front() == '\'' || arg.front() == '"')) {
++depth;
isInLiteral = true;
curLiteral += arg;
} else if ((arg.back() == '\'' || arg.back() == '"') && isInLiteral) {
--depth;
curLiteral += arg;
args.push_back(curLiteral);
if (depth <= 0) {
depth = 0;
isInLiteral = false;
curLiteral.clear();
}
} else if (isInLiteral)
curLiteral += arg;
else
args.push_back(arg);
}
if (isInLiteral) {
if ((curLiteral.back() != '\'' && curLiteral.back() != '"') || depth > 1) {
report(Level::Warning, "Unterminated string literal");
return; return;
}
args.push_back(curLiteral);
}
/* First let's split semi-colon delimited commands */ std::string commandName = args[0];
std::vector<std::string> commands = athena::utility::split(str, ';'); args.erase(args.begin());
if (commands.empty()) std::string lowComName = commandName;
athena::utility::tolower(lowComName);
if (m_commands.find(lowComName) != m_commands.end()) {
const SConsoleCommand& cmd = m_commands[lowComName];
if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Developer) && !com_developer->toBoolean()) {
report(Level::Error, "This command can only be executed in developer mode", commandName.c_str());
return; return;
}
for (std::string command : commands) if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Cheat) && !com_enableCheats->toBoolean()) {
{ report(Level::Error, "This command can only be executed with cheats enabled", commandName.c_str());
command = athena::utility::trim(command); return;
std::vector<std::string> tmpArgs = athena::utility::split(command, ' '); }
if (tmpArgs.empty()) m_commands[lowComName].m_func(this, args);
continue; } else if (const CVar* cv = m_cvarMgr->findCVar(commandName)) {
std::vector<std::string> args; args.insert(args.begin(), commandName);
args.reserve(tmpArgs.size()); if (args.size() > 1)
/* detect string literals */ m_cvarMgr->setCVar(this, args);
bool isInLiteral = false; else
std::string curLiteral; m_cvarMgr->getCVar(this, args);
int depth = 0; } else
for (std::string arg : tmpArgs) report(Level::Error, "Command '%s' is not valid!", commandName.c_str());
{ }
if ((arg.front() == '\'' || arg.front() == '"')) }
{
++depth; void Console::help(Console* /*con*/, const std::vector<std::string>& args) {
isInLiteral = true; if (args.empty()) {
curLiteral += arg; report(Level::Info, "Expected usage: help <command>");
} return;
else if ((arg.back() == '\'' || arg.back() == '"') && isInLiteral) }
{ std::string cmd = args.front();
--depth; athena::utility::tolower(cmd);
curLiteral += arg; auto it = m_commands.find(cmd);
args.push_back(curLiteral); if (it == m_commands.end()) {
if (depth <= 0) report(Level::Error, "No such command '%s'", args.front().c_str());
{ return;
depth = 0; }
isInLiteral = false;
curLiteral.clear(); report(Level::Info, "%s: %s", it->second.m_displayName.c_str(), it->second.m_helpString.c_str());
} if (!it->second.m_usage.empty())
} report(Level::Info, "Usage: %s %s", it->second.m_displayName.c_str(), it->second.m_usage.c_str());
else if (isInLiteral) }
curLiteral += arg;
else void Console::listCommands(Console* /*con*/, const std::vector<std::string>& /*args*/) {
args.push_back(arg); for (const auto& comPair : m_commands)
report(Level::Info, "'%s': %s", comPair.second.m_displayName.c_str(), comPair.second.m_helpString.c_str());
}
bool Console::commandExists(std::string_view cmd) {
std::string cmdName = cmd.data();
athena::utility::tolower(cmdName);
return m_commands.find(cmdName) != m_commands.end();
}
void Console::report(Level level, const char* fmt, va_list list) {
char tmp[2048];
vsnprintf(tmp, 2048, fmt, list);
std::vector<std::string> lines = athena::utility::split(tmp, '\n');
for (const std::string& line : lines) {
std::string v = athena::utility::sprintf("%s", line.c_str());
m_log.emplace_back(v, level);
}
printf("%s\n", tmp);
}
void Console::report(Level level, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
report(level, fmt, ap);
va_end(ap);
}
void Console::proc() {
if (m_conHeight->isModified()) {
m_cachedConHeight = m_conHeight->toFloat();
m_conHeight->clearModified();
}
if (m_conSpeed->isModified()) {
m_cachedConSpeed = m_conSpeed->toFloat();
m_conSpeed->clearModified();
}
if (m_state == State::Opened) {
printf("\r%s ", m_commandString.c_str());
fflush(stdout);
} else if (m_state == State::Opening)
m_state = State::Opened;
else if (m_state == State::Closing)
m_state = State::Closed;
if (m_cursorPosition > int(m_commandString.size() - 1))
m_cursorPosition = int(m_commandString.size() - 1);
if (m_cursorPosition < -1)
m_cursorPosition = -1;
if (m_logOffset > int(m_log.size() - 1))
m_logOffset = int(m_log.size() - 1);
if (m_logOffset < 0)
m_logOffset = 0;
}
void Console::draw(boo::IGraphicsCommandQueue* /*gfxQ*/) {}
void Console::handleCharCode(unsigned long chr, boo::EModifierKey /*mod*/, bool /*repeat*/) {
if (chr == U'`' || chr == U'~') {
if (m_state == State::Closed || m_state == State::Closing)
m_state = State::Opening;
else
m_state = State::Closing;
}
if (m_state == State::Opened) {
if (!m_commandString.empty() && m_cursorPosition + 1 < int(m_commandString.size())) {
if (m_overwrite)
m_commandString[unsigned(m_cursorPosition + 1)] = char(chr);
else
m_commandString.insert(m_commandString.begin() + m_cursorPosition + 1, char(chr));
} else
m_commandString += char(chr);
++m_cursorPosition;
}
}
void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool /*repeat*/) {
if (m_state != Opened)
return;
switch (sp) {
case boo::ESpecialKey::Insert:
m_overwrite ^= 1;
break;
case boo::ESpecialKey::Backspace: {
if (!m_commandString.empty()) {
if (int(mod & boo::EModifierKey::Ctrl) != 0) {
size_t index = m_commandString.rfind(' ', size_t(m_cursorPosition - 1));
if (index == std::string::npos) {
m_commandString.clear();
m_cursorPosition = -1;
} else {
m_commandString.erase(index, (index - m_commandString.size()));
m_cursorPosition = int(index);
} }
break;
}
if (m_cursorPosition < 0)
break;
if (isInLiteral) m_commandString.erase(size_t(m_cursorPosition), 1);
{ --m_cursorPosition;
if ((curLiteral.back() != '\'' && curLiteral.back() != '"') || depth > 1) }
{ break;
report(Level::Warning, "Unterminated string literal"); }
return; case boo::ESpecialKey::Delete: {
} if (!m_commandString.empty()) {
args.push_back(curLiteral); // Don't try to delete if the cursor is at the end of the line
} if ((m_cursorPosition + 1) >= int(m_commandString.size()))
break;
std::string commandName = args[0]; if (int(mod & boo::EModifierKey::Ctrl) != 0) {
args.erase(args.begin()); size_t index = m_commandString.find_first_of(' ', size_t(m_cursorPosition + 1));
if (index != std::string::npos)
std::string lowComName = commandName; m_commandString.erase(size_t(m_cursorPosition + 1), index + 1);
athena::utility::tolower(lowComName);
if (m_commands.find(lowComName) != m_commands.end())
{
const SConsoleCommand& cmd = m_commands[lowComName];
if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Developer) && !com_developer->toBoolean())
{
report(Level::Error, "This command can only be executed in developer mode", commandName.c_str());
return;
}
if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Cheat) && !com_enableCheats->toBoolean())
{
report(Level::Error, "This command can only be executed with cheats enabled", commandName.c_str());
return;
}
m_commands[lowComName].m_func(this, args);
}
else if (const CVar* cv = m_cvarMgr->findCVar(commandName))
{
args.insert(args.begin(), commandName);
if (args.size() > 1)
m_cvarMgr->setCVar(this, args);
else
m_cvarMgr->getCVar(this, args);
}
else else
report(Level::Error, "Command '%s' is not valid!", commandName.c_str()); m_commandString.erase(size_t(m_cursorPosition + 1), size_t(m_cursorPosition + 1) - m_commandString.size());
break;
}
m_commandString.erase(size_t(m_cursorPosition + 1), 1);
} }
} break;
}
case boo::ESpecialKey::PgUp: {
if (m_logOffset < int(m_log.size() - m_maxLines) - 1)
m_logOffset++;
break;
}
case boo::ESpecialKey::PgDown: {
if (m_logOffset > 0)
m_logOffset--;
break;
}
case boo::ESpecialKey::Enter: {
printf("\n");
executeString(m_commandString);
m_cursorPosition = -1;
m_commandHistory.insert(m_commandHistory.begin(), m_commandString);
m_commandString.clear();
m_showCursor = true;
m_cursorTime = 0.f;
break;
}
case boo::ESpecialKey::Left: {
if (m_cursorPosition < 0)
break;
void Console::help(Console* /*con*/, const std::vector<std::string>& args) if (int(mod & boo::EModifierKey::Ctrl) != 0)
{ m_cursorPosition = int(m_commandString.rfind(' ', size_t(m_cursorPosition) - 1));
if (args.empty()) else
{ m_cursorPosition--;
report(Level::Info, "Expected usage: help <command>");
return;
}
std::string cmd = args.front();
athena::utility::tolower(cmd);
auto it = m_commands.find(cmd);
if (it == m_commands.end())
{
report(Level::Error, "No such command '%s'", args.front().c_str());
return;
}
report(Level::Info, "%s: %s", it->second.m_displayName.c_str(), it->second.m_helpString.c_str()); m_showCursor = true;
if (!it->second.m_usage.empty()) m_cursorTime = 0.f;
report(Level::Info, "Usage: %s %s", it->second.m_displayName.c_str(), it->second.m_usage.c_str()); break;
} }
case boo::ESpecialKey::Right: {
if (m_cursorPosition >= int(m_commandString.size() - 1))
break;
void Console::listCommands(Console* /*con*/, const std::vector<std::string>& /*args*/) if (int(mod & boo::EModifierKey::Ctrl) != 0) {
{ if (m_commandString[size_t(m_cursorPosition)] == ' ')
for (const auto& comPair : m_commands) m_cursorPosition++;
report(Level::Info, "'%s': %s", comPair.second.m_displayName.c_str(), comPair.second.m_helpString.c_str());
}
bool Console::commandExists(std::string_view cmd) size_t tmpPos = m_commandString.find(' ', size_t(m_cursorPosition));
{ if (tmpPos == std::string::npos)
std::string cmdName = cmd.data();
athena::utility::tolower(cmdName);
return m_commands.find(cmdName) != m_commands.end();
}
void Console::report(Level level, const char* fmt, va_list list)
{
char tmp[2048];
vsnprintf(tmp, 2048, fmt, list);
std::vector<std::string> lines = athena::utility::split(tmp, '\n');
for (const std::string& line : lines)
{
std::string v = athena::utility::sprintf("%s", line.c_str());
m_log.emplace_back(v, level);
}
printf("%s\n", tmp);
}
void Console::report(Level level, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
report(level, fmt, ap);
va_end(ap);
}
void Console::proc()
{
if (m_conHeight->isModified())
{
m_cachedConHeight = m_conHeight->toFloat();
m_conHeight->clearModified();
}
if (m_conSpeed->isModified())
{
m_cachedConSpeed = m_conSpeed->toFloat();
m_conSpeed->clearModified();
}
if (m_state == State::Opened)
{
printf("\r%s ", m_commandString.c_str());
fflush(stdout);
}
else if (m_state == State::Opening)
m_state = State::Opened;
else if (m_state == State::Closing)
m_state = State::Closed;
if (m_cursorPosition > int(m_commandString.size() - 1))
m_cursorPosition = int(m_commandString.size() - 1); m_cursorPosition = int(m_commandString.size() - 1);
if (m_cursorPosition < -1) else
m_cursorPosition = -1; m_cursorPosition = int(tmpPos);
} else
m_cursorPosition++;
if (m_logOffset > int(m_log.size() - 1)) m_showCursor = true;
m_logOffset = int(m_log.size() - 1); m_cursorTime = 0.f;
if (m_logOffset < 0) break;
m_logOffset = 0; }
case boo::ESpecialKey::Up: {
if (m_commandHistory.size() == 0)
break;
m_currentCommand++;
if (m_currentCommand > int(m_commandHistory.size() - 1))
m_currentCommand = int(m_commandHistory.size() - 1);
m_commandString = m_commandHistory[size_t(m_currentCommand)];
m_cursorPosition = int(m_commandString.size() - 1);
break;
}
case boo::ESpecialKey::Down: {
if (m_commandHistory.empty())
break;
m_currentCommand--;
if (m_currentCommand >= 0) {
m_commandString = m_commandHistory[size_t(m_currentCommand)];
} else if (m_currentCommand <= -1) {
m_currentCommand = -1;
m_commandString.clear();
}
m_cursorPosition = int(m_commandString.size());
break;
}
case boo::ESpecialKey::Home:
m_cursorPosition = -1;
break;
case boo::ESpecialKey::End:
m_cursorPosition = int(m_commandString.size() - 1);
break;
default:
break;
}
} }
void Console::draw(boo::IGraphicsCommandQueue* /*gfxQ*/) void Console::handleSpecialKeyUp(boo::ESpecialKey /*sp*/, boo::EModifierKey /*mod*/) {}
{
}
void Console::handleCharCode(unsigned long chr, boo::EModifierKey /*mod*/, bool /*repeat*/) void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const char* format, va_list ap) {
{ char tmp[2048];
if (chr == U'`' || chr == U'~') vsnprintf(tmp, 2048, format, ap);
{ std::vector<std::string> lines = athena::utility::split(tmp, '\n');
if (m_state == State::Closed || m_state == State::Closing) for (const std::string& line : lines) {
m_state = State::Opening; std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str());
else
m_state = State::Closing;
}
if (m_state == State::Opened)
{
if (!m_commandString.empty() && m_cursorPosition + 1 < int(m_commandString.size()))
{
if (m_overwrite)
m_commandString[unsigned(m_cursorPosition + 1)] = char(chr);
else
m_commandString.insert(m_commandString.begin() + m_cursorPosition + 1, char(chr));
}
else
m_commandString += char(chr);
++m_cursorPosition;
}
}
void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, bool /*repeat*/)
{
if (m_state != Opened)
return;
switch (sp)
{
case boo::ESpecialKey::Insert:
m_overwrite ^= 1;
break;
case boo::ESpecialKey::Backspace:
{
if (!m_commandString.empty())
{
if (int(mod & boo::EModifierKey::Ctrl) != 0)
{
size_t index = m_commandString.rfind(' ', size_t(m_cursorPosition - 1));
if (index == std::string::npos)
{
m_commandString.clear();
m_cursorPosition = -1;
}
else
{
m_commandString.erase(index, (index - m_commandString.size()));
m_cursorPosition = int(index);
}
break;
}
if (m_cursorPosition < 0)
break;
m_commandString.erase(size_t(m_cursorPosition), 1);
--m_cursorPosition;
}
break;
}
case boo::ESpecialKey::Delete:
{
if (!m_commandString.empty())
{
// Don't try to delete if the cursor is at the end of the line
if ((m_cursorPosition + 1) >= int(m_commandString.size()))
break;
if (int(mod & boo::EModifierKey::Ctrl) != 0)
{
size_t index = m_commandString.find_first_of(' ', size_t(m_cursorPosition + 1));
if (index != std::string::npos)
m_commandString.erase(size_t(m_cursorPosition + 1), index + 1);
else
m_commandString.erase(size_t(m_cursorPosition + 1), size_t(m_cursorPosition + 1) - m_commandString.size());
break;
}
m_commandString.erase(size_t(m_cursorPosition + 1), 1);
}
break;
}
case boo::ESpecialKey::PgUp:
{
if (m_logOffset < int(m_log.size() - m_maxLines) - 1)
m_logOffset++;
break;
}
case boo::ESpecialKey::PgDown:
{
if (m_logOffset > 0)
m_logOffset--;
break;
}
case boo::ESpecialKey::Enter:
{
printf("\n");
executeString(m_commandString);
m_cursorPosition = -1;
m_commandHistory.insert(m_commandHistory.begin(), m_commandString);
m_commandString.clear();
m_showCursor = true;
m_cursorTime = 0.f;
break;
}
case boo::ESpecialKey::Left:
{
if (m_cursorPosition < 0)
break;
if (int(mod & boo::EModifierKey::Ctrl) != 0)
m_cursorPosition = int(m_commandString.rfind(' ', size_t(m_cursorPosition) - 1));
else
m_cursorPosition--;
m_showCursor = true;
m_cursorTime = 0.f;
break;
}
case boo::ESpecialKey::Right:
{
if (m_cursorPosition >= int(m_commandString.size() - 1))
break;
if (int(mod & boo::EModifierKey::Ctrl) != 0)
{
if (m_commandString[size_t(m_cursorPosition)] == ' ')
m_cursorPosition++;
size_t tmpPos = m_commandString.find(' ', size_t(m_cursorPosition));
if (tmpPos == std::string::npos)
m_cursorPosition = int(m_commandString.size() - 1);
else
m_cursorPosition = int(tmpPos);
}
else
m_cursorPosition++;
m_showCursor = true;
m_cursorTime = 0.f;
break;
}
case boo::ESpecialKey::Up:
{
if (m_commandHistory.size() == 0)
break;
m_currentCommand++;
if (m_currentCommand > int(m_commandHistory.size() - 1))
m_currentCommand = int(m_commandHistory.size() - 1);
m_commandString = m_commandHistory[size_t(m_currentCommand)];
m_cursorPosition = int(m_commandString.size() - 1);
break;
}
case boo::ESpecialKey::Down:
{
if (m_commandHistory.empty())
break;
m_currentCommand--;
if (m_currentCommand >= 0)
{
m_commandString = m_commandHistory[size_t(m_currentCommand)];
}
else if (m_currentCommand <= -1)
{
m_currentCommand = -1;
m_commandString.clear();
}
m_cursorPosition = int(m_commandString.size());
break;
}
case boo::ESpecialKey::Home:
m_cursorPosition = -1;
break;
case boo::ESpecialKey::End:
m_cursorPosition = int(m_commandString.size() - 1);
break;
default:
break;
}
}
void Console::handleSpecialKeyUp(boo::ESpecialKey /*sp*/, boo::EModifierKey /*mod*/)
{
}
void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const char *format, va_list ap)
{
char tmp[2048];
vsnprintf(tmp, 2048, format, ap);
std::vector<std::string> lines = athena::utility::split(tmp, '\n');
for (const std::string& line : lines)
{
std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str());
m_con->m_log.emplace_back(v, Console::Level(severity));
}
}
void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const wchar_t* format, va_list ap)
{
wchar_t tmp[2048];
vswprintf(tmp, 2048, format, ap);
std::vector<std::string> lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n');
for (const std::string& line : lines)
{
std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str());
m_con->m_log.emplace_back(v, Console::Level(severity));
}
}
void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, const char* format, va_list ap)
{
char tmp[2048];
vsnprintf(tmp, 2048, format, ap);
std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, tmp, file, linenum);
m_con->m_log.emplace_back(v, Console::Level(severity)); m_con->m_log.emplace_back(v, Console::Level(severity));
}
} }
void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, const wchar_t* format, va_list ap) void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const wchar_t* format,
{ va_list ap) {
wchar_t tmp[2048]; wchar_t tmp[2048];
vswprintf(tmp, 2048, format, ap); vswprintf(tmp, 2048, format, ap);
std::vector<std::string> lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n'); std::vector<std::string> lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n');
for (const std::string& line : lines) for (const std::string& line : lines) {
{ std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str());
std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, line.c_str(), file, linenum); m_con->m_log.emplace_back(v, Console::Level(severity));
m_con->m_log.emplace_back(v, Console::Level(severity)); }
}
void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file,
unsigned linenum, const char* format, va_list ap) {
char tmp[2048];
vsnprintf(tmp, 2048, format, ap);
std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, tmp, file, linenum);
m_con->m_log.emplace_back(v, Console::Level(severity));
}
void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file,
unsigned linenum, const wchar_t* format, va_list ap) {
wchar_t tmp[2048];
vswprintf(tmp, 2048, format, ap);
std::vector<std::string> lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n');
for (const std::string& line : lines) {
std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, line.c_str(), file, linenum);
m_con->m_log.emplace_back(v, Console::Level(severity));
}
}
void Console::dumpLog() {
for (const auto& l : m_log) {
switch (l.second) {
case Level::Info:
printf("%s\n", l.first.c_str());
break;
case Level::Warning:
printf("[Warning] %s\n", l.first.c_str());
break;
case Level::Error:
printf("[ Error ] %s\n", l.first.c_str());
break;
case Level::Fatal:
printf("[ Fatal ] %s\n", l.first.c_str());
break;
} }
}
} }
void Console::dumpLog() void Console::RegisterLogger(Console* con) { logvisor::MainLoggers.emplace_back(new LogVisorAdapter(con)); }
{
for (const auto& l : m_log)
{
switch(l.second)
{
case Level::Info:
printf("%s\n", l.first.c_str());
break;
case Level::Warning:
printf("[Warning] %s\n", l.first.c_str());
break;
case Level::Error:
printf("[ Error ] %s\n", l.first.c_str());
break;
case Level::Fatal:
printf("[ Fatal ] %s\n", l.first.c_str());
break;
}
}
}
void Console::RegisterLogger(Console* con) Console* Console::instance() { return m_instance; }
{ } // namespace hecl
logvisor::MainLoggers.emplace_back(new LogVisorAdapter(con));
}
Console* Console::instance()
{
return m_instance;
}
}

View File

@ -12,99 +12,93 @@
#define BOLD "\x1b[1m" #define BOLD "\x1b[1m"
#define NORMAL "\x1b[0m" #define NORMAL "\x1b[0m"
namespace hecl::Frontend namespace hecl::Frontend {
{
std::string Diagnostics::sourceDiagString(const SourceLocation& l, bool ansi) const std::string Diagnostics::sourceDiagString(const SourceLocation& l, bool ansi) const {
{ std::string::const_iterator it = m_source.begin();
std::string::const_iterator it = m_source.begin(); for (int i = 1; i < l.line; ++i) {
for (int i=1 ; i<l.line ; ++i)
{
while (*it != '\n' && it != m_source.end())
++it;
if (*it == '\n')
++it;
}
std::string::const_iterator begin = it;
while (*it != '\n' && it != m_source.end()) while (*it != '\n' && it != m_source.end())
++it; ++it;
std::string::const_iterator end = it; if (*it == '\n')
++it;
}
std::string::const_iterator begin = it;
while (*it != '\n' && it != m_source.end())
++it;
std::string::const_iterator end = it;
std::string retval(begin, end); std::string retval(begin, end);
retval += '\n'; retval += '\n';
for (int i=1 ; i<l.col ; ++i) for (int i = 1; i < l.col; ++i)
retval += ' '; retval += ' ';
if (ansi) if (ansi)
retval += GREEN "^" NORMAL; retval += GREEN "^" NORMAL;
else else
retval += '^'; retval += '^';
return retval; return retval;
} }
void Diagnostics::reportScannerErr(const SourceLocation& l, const char* fmt, ...) void Diagnostics::reportScannerErr(const SourceLocation& l, const char* fmt, ...) {
{ va_list ap;
va_list ap; va_start(ap, fmt);
va_start(ap, fmt); char* result = nullptr;
char* result = nullptr;
#ifdef _WIN32 #ifdef _WIN32
int length = _vscprintf(fmt, ap); int length = _vscprintf(fmt, ap);
result = (char*)malloc(length); result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap); vsnprintf(result, length, fmt, ap);
#else #else
vasprintf(&result, fmt, ap); vasprintf(&result, fmt, ap);
#endif #endif
va_end(ap); va_end(ap);
if (logvisor::XtermColor) if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[Scanner]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", LogModule.report(logvisor::Fatal, CYAN "[Scanner]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_name.c_str(),
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str()); l.line, l.col, result, sourceDiagString(l, true).c_str());
else else
LogModule.report(logvisor::Fatal, "[Scanner] %s @%d:%d\n%s\n%s", LogModule.report(logvisor::Fatal, "[Scanner] %s @%d:%d\n%s\n%s", m_name.c_str(), l.line, l.col, result,
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str()); sourceDiagString(l, false).c_str());
free(result); free(result);
} }
void Diagnostics::reportParserErr(const SourceLocation& l, const char* fmt, ...) void Diagnostics::reportParserErr(const SourceLocation& l, const char* fmt, ...) {
{ va_list ap;
va_list ap; va_start(ap, fmt);
va_start(ap, fmt); char* result = nullptr;
char* result = nullptr;
#ifdef _WIN32 #ifdef _WIN32
int length = _vscprintf(fmt, ap); int length = _vscprintf(fmt, ap);
result = (char*)malloc(length); result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap); vsnprintf(result, length, fmt, ap);
#else #else
vasprintf(&result, fmt, ap); vasprintf(&result, fmt, ap);
#endif #endif
va_end(ap); va_end(ap);
if (logvisor::XtermColor) if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[Parser]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", LogModule.report(logvisor::Fatal, CYAN "[Parser]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_name.c_str(),
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str()); l.line, l.col, result, sourceDiagString(l, true).c_str());
else else
LogModule.report(logvisor::Fatal, "[Parser] %s @%d:%d\n%s\n%s", LogModule.report(logvisor::Fatal, "[Parser] %s @%d:%d\n%s\n%s", m_name.c_str(), l.line, l.col, result,
m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str()); sourceDiagString(l, false).c_str());
free(result); free(result);
} }
void Diagnostics::reportBackendErr(const SourceLocation& l, const char* fmt, ...) void Diagnostics::reportBackendErr(const SourceLocation& l, const char* fmt, ...) {
{ va_list ap;
va_list ap; va_start(ap, fmt);
va_start(ap, fmt); char* result = nullptr;
char* result = nullptr;
#ifdef _WIN32 #ifdef _WIN32
int length = _vscprintf(fmt, ap); int length = _vscprintf(fmt, ap);
result = (char*)malloc(length); result = (char*)malloc(length);
vsnprintf(result, length, fmt, ap); vsnprintf(result, length, fmt, ap);
#else #else
vasprintf(&result, fmt, ap); vasprintf(&result, fmt, ap);
#endif #endif
va_end(ap); va_end(ap);
if (logvisor::XtermColor) if (logvisor::XtermColor)
LogModule.report(logvisor::Fatal, CYAN "[%s]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", LogModule.report(logvisor::Fatal, CYAN "[%s]" NORMAL " %s " YELLOW "@%d:%d " NORMAL "\n%s\n%s", m_backend.c_str(),
m_backend.c_str(), m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str()); m_name.c_str(), l.line, l.col, result, sourceDiagString(l, true).c_str());
else else
LogModule.report(logvisor::Fatal, "[%s] %s @%d:%d\n%s\n%s", LogModule.report(logvisor::Fatal, "[%s] %s @%d:%d\n%s\n%s", m_backend.c_str(), m_name.c_str(), l.line, l.col,
m_backend.c_str(), m_name.c_str(), l.line, l.col, result, sourceDiagString(l, false).c_str()); result, sourceDiagString(l, false).c_str());
free(result); free(result);
} }
} } // namespace hecl::Frontend

View File

@ -1,256 +1,225 @@
#include "hecl/Frontend.hpp" #include "hecl/Frontend.hpp"
namespace hecl::Frontend namespace hecl::Frontend {
{
int IR::Instruction::getChildCount() const int IR::Instruction::getChildCount() const {
{ switch (m_op) {
switch (m_op) case OpType::Call:
{ return m_call.m_argInstIdxs.size();
case OpType::Call: case OpType::Arithmetic:
return m_call.m_argInstIdxs.size(); return 2;
case OpType::Arithmetic: case OpType::Swizzle:
return 2; return 1;
case OpType::Swizzle: default:
return 1; LogModule.report(logvisor::Fatal, "invalid op type");
default: }
LogModule.report(logvisor::Fatal, "invalid op type"); return -1;
}
return -1;
} }
const IR::Instruction& IR::Instruction::getChildInst(const IR& ir, size_t idx) const const IR::Instruction& IR::Instruction::getChildInst(const IR& ir, size_t idx) const {
{ switch (m_op) {
switch (m_op) case OpType::Call:
{ return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
case OpType::Call: case OpType::Arithmetic:
return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx)); if (idx > 1)
case OpType::Arithmetic: LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
if (idx > 1) return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1"); case OpType::Swizzle:
return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]); if (idx > 0)
case OpType::Swizzle: LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
if (idx > 0) return ir.m_instructions.at(m_swizzle.m_instIdx);
LogModule.report(logvisor::Fatal, "swizzle child idx must be 0"); default:
return ir.m_instructions.at(m_swizzle.m_instIdx); LogModule.report(logvisor::Fatal, "invalid op type");
default: }
LogModule.report(logvisor::Fatal, "invalid op type"); return *this;
}
return *this;
} }
const atVec4f& IR::Instruction::getImmVec() const const atVec4f& IR::Instruction::getImmVec() const {
{ if (m_op != OpType::LoadImm)
if (m_op != OpType::LoadImm) LogModule.report(logvisor::Fatal, "invalid op type");
LogModule.report(logvisor::Fatal, "invalid op type"); return m_loadImm.m_immVec;
return m_loadImm.m_immVec;
} }
template <class Op> template <class Op>
void IR::Instruction::Enumerate(typename Op::StreamT& s) void IR::Instruction::Enumerate(typename Op::StreamT& s) {
{ Do<Op>({"op"}, m_op, s);
Do<Op>({"op"}, m_op, s); Do<Op>({"target"}, m_target, s);
Do<Op>({"target"}, m_target, s); switch (m_op) {
switch (m_op) default:
{ break;
default: break; case OpType::Call:
case OpType::Call: Do<Op>({"call"}, m_call, s);
Do<Op>({"call"}, m_call, s); break;
break; case OpType::LoadImm:
case OpType::LoadImm: Do<Op>({"loadImm"}, m_loadImm, s);
Do<Op>({"loadImm"}, m_loadImm, s); break;
break; case OpType::Arithmetic:
case OpType::Arithmetic: Do<Op>({"arithmetic"}, m_arithmetic, s);
Do<Op>({"arithmetic"}, m_arithmetic, s); break;
break; case OpType::Swizzle:
case OpType::Swizzle: Do<Op>({"swizzle"}, m_swizzle, s);
Do<Op>({"swizzle"}, m_swizzle, s); break;
break; }
}
} }
atInt8 IR::swizzleCompIdx(char aChar) atInt8 IR::swizzleCompIdx(char aChar) {
{ switch (aChar) {
switch (aChar) case 'x':
{ case 'r':
case 'x': return 0;
case 'r': case 'y':
return 0; case 'g':
case 'y': return 1;
case 'g': case 'z':
return 1; case 'b':
case 'z': return 2;
case 'b': case 'w':
return 2; case 'a':
case 'w': return 3;
case 'a': default:
return 3; break;
default: }
break; return -1;
} }
int IR::addInstruction(const IRNode& n, IR::RegID target) {
if (n.kind == IRNode::Kind::None)
return -1; return -1;
} switch (n.kind) {
case IRNode::Kind::Call: {
int IR::addInstruction(const IRNode& n, IR::RegID target) if (!n.str.compare("vec3") && n.children.size() >= 3) {
{ atVec4f vec = {};
if (n.kind == IRNode::Kind::None) auto it = n.children.cbegin();
return -1; int i;
switch (n.kind) athena::simd_floats f;
{ for (i = 0; i < 3; ++i, ++it) {
case IRNode::Kind::Call: if (it->kind != IRNode::Kind::Imm)
{ break;
if (!n.str.compare("vec3") && n.children.size() >= 3) f[i] = it->val;
{ }
atVec4f vec = {}; vec.simd.copy_from(f);
auto it = n.children.cbegin(); if (i == 3) {
int i;
athena::simd_floats f;
for (i=0 ; i<3 ; ++i, ++it)
{
if (it->kind != IRNode::Kind::Imm)
break;
f[i] = it->val;
}
vec.simd.copy_from(f);
if (i == 3)
{
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec = vec;
return m_instructions.size() - 1;
}
}
else if (!n.str.compare("vec4") && n.children.size() >= 4)
{
atVec4f vec = {};
auto it = n.children.cbegin();
int i;
athena::simd_floats f;
for (i=0 ; i<4 ; ++i, ++it)
{
if (it->kind != IRNode::Kind::Imm)
break;
f[i] = it->val;
}
vec.simd.copy_from(f);
if (i == 4)
{
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec = vec;
return m_instructions.size() - 1;
}
}
std::vector<atUint16> argInstIdxs;
argInstIdxs.reserve(n.children.size());
IR::RegID tgt = target;
for (auto& c : n.children)
argInstIdxs.push_back(addInstruction(c, tgt++));
m_instructions.emplace_back(OpType::Call, target, n.loc);
Instruction::Call& inst = m_instructions.back().m_call;
inst.m_name = n.str;
inst.m_argInstCount = atUint16(argInstIdxs.size());
inst.m_argInstIdxs = argInstIdxs;
return m_instructions.size() - 1;
}
case IRNode::Kind::Imm:
{
m_instructions.emplace_back(OpType::LoadImm, target, n.loc); m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm; Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec.simd = athena::simd<float>(n.val); inst.m_immVec = vec;
return m_instructions.size() - 1; return m_instructions.size() - 1;
} }
case IRNode::Kind::Binop: } else if (!n.str.compare("vec4") && n.children.size() >= 4) {
{ atVec4f vec = {};
atUint16 left = addInstruction(*n.left, target); auto it = n.children.cbegin();
atUint16 right = addInstruction(*n.right, target + 1); int i;
m_instructions.emplace_back(OpType::Arithmetic, target, n.loc); athena::simd_floats f;
Instruction::Arithmetic& inst = m_instructions.back().m_arithmetic; for (i = 0; i < 4; ++i, ++it) {
inst.m_op = Instruction::ArithmeticOpType(int(n.op) + 1); if (it->kind != IRNode::Kind::Imm)
inst.m_instIdxs[0] = left; break;
inst.m_instIdxs[1] = right; f[i] = it->val;
}
vec.simd.copy_from(f);
if (i == 4) {
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec = vec;
return m_instructions.size() - 1; return m_instructions.size() - 1;
}
} }
case IRNode::Kind::Swizzle:
{ std::vector<atUint16> argInstIdxs;
atUint16 left = addInstruction(*n.left, target); argInstIdxs.reserve(n.children.size());
m_instructions.emplace_back(OpType::Swizzle, target, n.loc); IR::RegID tgt = target;
Instruction::Swizzle& inst = m_instructions.back().m_swizzle; for (auto& c : n.children)
for (int i=0 ; i<n.str.size() && i<4 ; ++i) argInstIdxs.push_back(addInstruction(c, tgt++));
inst.m_idxs[i] = swizzleCompIdx(n.str[i]); m_instructions.emplace_back(OpType::Call, target, n.loc);
inst.m_instIdx = left; Instruction::Call& inst = m_instructions.back().m_call;
return m_instructions.size() - 1; inst.m_name = n.str;
} inst.m_argInstCount = atUint16(argInstIdxs.size());
default: inst.m_argInstIdxs = argInstIdxs;
return -1; return m_instructions.size() - 1;
} }
case IRNode::Kind::Imm: {
m_instructions.emplace_back(OpType::LoadImm, target, n.loc);
Instruction::LoadImm& inst = m_instructions.back().m_loadImm;
inst.m_immVec.simd = athena::simd<float>(n.val);
return m_instructions.size() - 1;
}
case IRNode::Kind::Binop: {
atUint16 left = addInstruction(*n.left, target);
atUint16 right = addInstruction(*n.right, target + 1);
m_instructions.emplace_back(OpType::Arithmetic, target, n.loc);
Instruction::Arithmetic& inst = m_instructions.back().m_arithmetic;
inst.m_op = Instruction::ArithmeticOpType(int(n.op) + 1);
inst.m_instIdxs[0] = left;
inst.m_instIdxs[1] = right;
return m_instructions.size() - 1;
}
case IRNode::Kind::Swizzle: {
atUint16 left = addInstruction(*n.left, target);
m_instructions.emplace_back(OpType::Swizzle, target, n.loc);
Instruction::Swizzle& inst = m_instructions.back().m_swizzle;
for (int i = 0; i < n.str.size() && i < 4; ++i)
inst.m_idxs[i] = swizzleCompIdx(n.str[i]);
inst.m_instIdx = left;
return m_instructions.size() - 1;
}
default:
return -1;
}
} }
template <> template <>
void IR::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) void IR::Enumerate<BigDNA::Read>(typename Read::StreamT& reader) {
{ m_hash = reader.readUint64Big();
m_hash = reader.readUint64Big(); m_regCount = reader.readUint16Big();
m_regCount = reader.readUint16Big(); atUint16 instCount = reader.readUint16Big();
atUint16 instCount = reader.readUint16Big(); m_instructions.clear();
m_instructions.clear(); m_instructions.reserve(instCount);
m_instructions.reserve(instCount); for (atUint16 i = 0; i < instCount; ++i)
for (atUint16 i=0 ; i<instCount ; ++i) m_instructions.emplace_back(reader);
m_instructions.emplace_back(reader);
/* Pre-resolve blending mode */ /* Pre-resolve blending mode */
const IR::Instruction& rootCall = m_instructions.back(); const IR::Instruction& rootCall = m_instructions.back();
m_doAlpha = false; m_doAlpha = false;
if (!rootCall.m_call.m_name.compare("HECLOpaque")) if (!rootCall.m_call.m_name.compare("HECLOpaque")) {
{ m_blendSrc = boo::BlendFactor::One;
m_blendSrc = boo::BlendFactor::One; m_blendDst = boo::BlendFactor::Zero;
m_blendDst = boo::BlendFactor::Zero; } else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
} m_blendSrc = boo::BlendFactor::SrcAlpha;
else if (!rootCall.m_call.m_name.compare("HECLAlpha")) m_blendDst = boo::BlendFactor::InvSrcAlpha;
{ m_doAlpha = true;
m_blendSrc = boo::BlendFactor::SrcAlpha; } else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
m_blendDst = boo::BlendFactor::InvSrcAlpha; m_blendSrc = boo::BlendFactor::SrcAlpha;
m_doAlpha = true; m_blendDst = boo::BlendFactor::One;
} m_doAlpha = true;
else if (!rootCall.m_call.m_name.compare("HECLAdditive")) }
{
m_blendSrc = boo::BlendFactor::SrcAlpha;
m_blendDst = boo::BlendFactor::One;
m_doAlpha = true;
}
} }
template <> template <>
void IR::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) void IR::Enumerate<BigDNA::Write>(typename Write::StreamT& writer) {
{ writer.writeUint64Big(m_hash);
writer.writeUint64Big(m_hash); writer.writeUint16Big(m_regCount);
writer.writeUint16Big(m_regCount); writer.writeUint16Big(m_instructions.size());
writer.writeUint16Big(m_instructions.size()); for (const Instruction& inst : m_instructions)
for (const Instruction& inst : m_instructions) inst.write(writer);
inst.write(writer);
} }
template <> template <>
void IR::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& sz) void IR::Enumerate<BigDNA::BinarySize>(typename BinarySize::StreamT& sz) {
{ sz += 12;
sz += 12; for (const Instruction& inst : m_instructions)
for (const Instruction& inst : m_instructions) inst.binarySize(sz);
inst.binarySize(sz);
} }
IR Frontend::compileSource(std::string_view source, std::string_view diagName) IR Frontend::compileSource(std::string_view source, std::string_view diagName) {
{ m_diag.reset(diagName, source);
m_diag.reset(diagName, source); m_parser.reset(source);
m_parser.reset(source); auto insts = m_parser.parse();
auto insts = m_parser.parse(); IR ir;
IR ir; std::string stripString;
std::string stripString; if (!insts.empty())
if (!insts.empty()) stripString = insts.front().toString(true);
stripString = insts.front().toString(true); ir.m_hash = Hash(stripString).val64();
ir.m_hash = Hash(stripString).val64(); for (auto& inst : insts)
for (auto& inst : insts) ir.addInstruction(inst, 0);
ir.addInstruction(inst, 0); return ir;
return ir;
} }
} } // namespace hecl::Frontend

View File

@ -3,8 +3,7 @@
/* Syntatical token parsing system */ /* Syntatical token parsing system */
namespace hecl::Frontend namespace hecl::Frontend {
{
/* /*
* hecl = { lf } call { lf { lf } call } { lf } . * hecl = { lf } call { lf { lf } call } { lf } .
@ -15,281 +14,244 @@ namespace hecl::Frontend
* value = ( call [ "." ident ] ) * value = ( call [ "." ident ] )
* | [ "-" ] number * | [ "-" ] number
* . * .
*/ */
std::string IRNode::rep(int n, std::string_view s) std::string IRNode::rep(int n, std::string_view s) {
{ std::string buf;
std::string buf; buf.reserve(n * s.size());
buf.reserve(n * s.size()); for (int i = 0; i < n; i++)
for (int i = 0; i < n; i++) buf.append(s);
buf.append(s); return buf;
return buf;
} }
std::string IRNode::fmt(int level, bool stripUVAnims) const std::string IRNode::fmt(int level, bool stripUVAnims) const {
{ std::string buf;
std::string buf; auto indent = rep(level, "\t"sv);
auto indent = rep(level, "\t"sv); switch (kind) {
switch (kind) case Kind::Call:
{ if (stripUVAnims && (str == "Texture" || str == "TextureN") && children.size() >= 2) {
case Kind::Call: auto it = children.begin();
if (stripUVAnims && (str == "Texture" || str == "TextureN") && children.size() >= 2) IRNode& uvNode = const_cast<IRNode&>(*++it);
{ if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View") {
auto it = children.begin(); std::string replacementName(str);
IRNode& uvNode = const_cast<IRNode&>(*++it); if (uvNode.str.back() == 'N' && replacementName.back() != 'N')
if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View") replacementName += 'N';
{ IRNode replacementNode(Kind::Call, std::move(replacementName), std::move(uvNode.children), loc);
std::string replacementName(str); auto ret = replacementNode.fmt(level, false);
if (uvNode.str.back() == 'N' && replacementName.back() != 'N') uvNode.children = std::move(replacementNode.children);
replacementName += 'N'; return ret;
IRNode replacementNode(Kind::Call, std::move(replacementName), }
std::move(uvNode.children), loc);
auto ret = replacementNode.fmt(level, false);
uvNode.children = std::move(replacementNode.children);
return ret;
}
}
buf.append(indent);
buf.append("Call "sv).append(str);
if (!children.empty())
{
buf.append(" {\n"sv);
for (const IRNode& n : children)
{
buf.append(n.fmt(level + 1, stripUVAnims));
buf.append("\n"sv);
}
buf.append(indent);
buf.append("}"sv);
}
break;
case Kind::Imm:
buf.append(indent);
buf.append("Imm "sv).append(hecl::Format("%f", val));
break;
case Kind::Binop:
buf.append(indent);
buf.append("Binop "sv).append(OpToStr(op)).append(" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(right->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
case Kind::Swizzle:
buf.append(indent);
buf.append("Swizzle \""sv).append(str);
buf.append("\" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
default:
break;
} }
return buf; buf.append(indent);
buf.append("Call "sv).append(str);
if (!children.empty()) {
buf.append(" {\n"sv);
for (const IRNode& n : children) {
buf.append(n.fmt(level + 1, stripUVAnims));
buf.append("\n"sv);
}
buf.append(indent);
buf.append("}"sv);
}
break;
case Kind::Imm:
buf.append(indent);
buf.append("Imm "sv).append(hecl::Format("%f", val));
break;
case Kind::Binop:
buf.append(indent);
buf.append("Binop "sv).append(OpToStr(op)).append(" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(right->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
case Kind::Swizzle:
buf.append(indent);
buf.append("Swizzle \""sv).append(str);
buf.append("\" {\n"sv);
buf.append(left->fmt(level + 1, stripUVAnims)).append("\n"sv);
buf.append(indent).append("}"sv);
break;
default:
break;
}
return buf;
} }
std::string IRNode::describe() const {
std::string IRNode::describe() const std::vector<std::string> l;
{ l.push_back("kind="s + KindToStr(kind).data());
std::vector<std::string> l; if (!str.empty())
l.push_back("kind="s + KindToStr(kind).data()); l.push_back("str="s + str);
if (!str.empty()) if (kind == Kind::Binop) {
l.push_back("str="s + str); l.push_back("op="s + OpToStr(op).data());
if (kind == Kind::Binop) l.push_back("left="s + left->toString());
{ l.push_back("right="s + right->toString());
l.push_back("op="s + OpToStr(op).data()); }
l.push_back("left="s + left->toString()); if (kind == Kind::Swizzle)
l.push_back("right="s + right->toString()); l.push_back("node="s + left->toString());
} if (kind == Kind::Call) {
if (kind == Kind::Swizzle) std::string str = "children=["s;
l.push_back("node="s + left->toString()); for (auto it = children.begin(); it != children.end(); ++it) {
if (kind == Kind::Call) str += it->toString();
{ if (&*it != &children.back())
std::string str = "children=["s; str += ';';
for (auto it = children.begin(); it != children.end(); ++it)
{
str += it->toString();
if (&*it != &children.back())
str += ';';
}
str += ']';
l.push_back(str);
}
std::string str = "IRNode["s;
for (auto it = l.begin(); it != l.end(); ++it)
{
str += *it;
if (&*it != &l.back())
str += ';';
} }
str += ']'; str += ']';
return str; l.push_back(str);
}
std::string str = "IRNode["s;
for (auto it = l.begin(); it != l.end(); ++it) {
str += *it;
if (&*it != &l.back())
str += ';';
}
str += ']';
return str;
} }
void Parser::check(Token::Kind expected) void Parser::check(Token::Kind expected) {
{ if (sym == expected)
if (sym == expected)
scan();
else
error("expected %s, was %s", Token::KindToStr(expected).data(), Token::KindToStr(sym).data());
}
IRNode Parser::call()
{
check(Token::Kind::Ident);
std::string name = t.str;
std::list<IRNode> args;
check(Token::Kind::Lpar);
if (sym == Token::Kind::Lpar || sym == Token::Kind::Ident ||
sym == Token::Kind::Number || sym == Token::Kind::Minus)
{
args.push_back(expr());
while (sym == Token::Kind::Comma)
{
scan();
args.push_back(expr());
}
}
if (sym != Token::Kind::Rpar)
error("expected expr|rpar, was %s", Token::KindToStr(sym).data());
else
scan();
return IRNode(IRNode::Kind::Call, std::move(name), std::move(args), t.loc);
}
bool Parser::imm(const IRNode& a, const IRNode& b)
{
return a.kind == IRNode::Kind::Imm && b.kind == IRNode::Kind::Imm;
}
IRNode Parser::expr()
{
IRNode node = sum();
while (sym == Token::Kind::Plus || sym == Token::Kind::Minus)
{
scan();
Token::Kind op = t.kind;
IRNode right = sum();
switch (op)
{
case Token::Kind::Plus:
if (imm(node, right)) // constant folding
return IRNode(IRNode::Kind::Imm, node.val + right.val, t.loc);
else
node = IRNode(IRNode::Op::Add, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Minus:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val - right.val, t.loc);
else
node = IRNode(IRNode::Op::Sub, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::sum()
{
IRNode node = factor();
while (sym == Token::Kind::Times || sym == Token::Kind::Div)
{
scan();
Token::Kind op = t.kind;
IRNode right = factor();
switch (op)
{
case Token::Kind::Times:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val * right.val, t.loc);
else
node = IRNode(IRNode::Op::Mul, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Div:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val / right.val, t.loc);
else
node = IRNode(IRNode::Op::Div, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::factor()
{
if (sym == Token::Kind::Lpar)
{
scan();
IRNode node = expr();
check(Token::Kind::Rpar);
return node;
} else
return value();
}
IRNode Parser::value()
{
if (sym == Token::Kind::Number || sym == Token::Kind::Minus)
{
scan();
bool neg = false;
if (t.kind == Token::Kind::Minus)
{
neg = true;
check(Token::Kind::Number);
}
float val = strtof(((neg ? "-"s : ""s) + t.str).c_str(), nullptr);
return IRNode(IRNode::Kind::Imm, val, t.loc);
}
else if (sym == Token::Kind::Ident)
{
IRNode call = Parser::call();
if (sym == Token::Kind::Period)
{
scan();
check(Token::Kind::Ident);
return IRNode(IRNode::Kind::Swizzle, std::string(t.str), std::move(call), t.loc);
}
return call;
}
else
{
error("expected number|call, was %s", Token::KindToStr(sym).data());
return IRNode();
}
}
std::list<IRNode> Parser::parse()
{
std::list<IRNode> result;
scan(); scan();
while (sym == Token::Kind::Lf) else
scan(); error("expected %s, was %s", Token::KindToStr(expected).data(), Token::KindToStr(sym).data());
result.push_back(call()); }
while (sym == Token::Kind::Lf)
{ IRNode Parser::call() {
while (sym == Token::Kind::Lf) check(Token::Kind::Ident);
scan(); std::string name = t.str;
if (sym != Token::Kind::Eof)
result.push_back(call()); std::list<IRNode> args;
check(Token::Kind::Lpar);
if (sym == Token::Kind::Lpar || sym == Token::Kind::Ident || sym == Token::Kind::Number ||
sym == Token::Kind::Minus) {
args.push_back(expr());
while (sym == Token::Kind::Comma) {
scan();
args.push_back(expr());
} }
}
if (sym != Token::Kind::Rpar)
error("expected expr|rpar, was %s", Token::KindToStr(sym).data());
else
scan();
return IRNode(IRNode::Kind::Call, std::move(name), std::move(args), t.loc);
}
bool Parser::imm(const IRNode& a, const IRNode& b) {
return a.kind == IRNode::Kind::Imm && b.kind == IRNode::Kind::Imm;
}
IRNode Parser::expr() {
IRNode node = sum();
while (sym == Token::Kind::Plus || sym == Token::Kind::Minus) {
scan();
Token::Kind op = t.kind;
IRNode right = sum();
switch (op) {
case Token::Kind::Plus:
if (imm(node, right)) // constant folding
return IRNode(IRNode::Kind::Imm, node.val + right.val, t.loc);
else
node = IRNode(IRNode::Op::Add, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Minus:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val - right.val, t.loc);
else
node = IRNode(IRNode::Op::Sub, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::sum() {
IRNode node = factor();
while (sym == Token::Kind::Times || sym == Token::Kind::Div) {
scan();
Token::Kind op = t.kind;
IRNode right = factor();
switch (op) {
case Token::Kind::Times:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val * right.val, t.loc);
else
node = IRNode(IRNode::Op::Mul, std::move(node), std::move(right), t.loc);
break;
case Token::Kind::Div:
if (imm(node, right)) // constant folding
node = IRNode(IRNode::Kind::Imm, node.val / right.val, t.loc);
else
node = IRNode(IRNode::Op::Div, std::move(node), std::move(right), t.loc);
break;
default:
break;
}
}
return node;
}
IRNode Parser::factor() {
if (sym == Token::Kind::Lpar) {
scan();
IRNode node = expr();
check(Token::Kind::Rpar);
return node;
} else
return value();
}
IRNode Parser::value() {
if (sym == Token::Kind::Number || sym == Token::Kind::Minus) {
scan();
bool neg = false;
if (t.kind == Token::Kind::Minus) {
neg = true;
check(Token::Kind::Number);
}
float val = strtof(((neg ? "-"s : ""s) + t.str).c_str(), nullptr);
return IRNode(IRNode::Kind::Imm, val, t.loc);
} else if (sym == Token::Kind::Ident) {
IRNode call = Parser::call();
if (sym == Token::Kind::Period) {
scan();
check(Token::Kind::Ident);
return IRNode(IRNode::Kind::Swizzle, std::string(t.str), std::move(call), t.loc);
}
return call;
} else {
error("expected number|call, was %s", Token::KindToStr(sym).data());
return IRNode();
}
}
std::list<IRNode> Parser::parse() {
std::list<IRNode> result;
scan();
while (sym == Token::Kind::Lf)
scan();
result.push_back(call());
while (sym == Token::Kind::Lf) {
while (sym == Token::Kind::Lf) while (sym == Token::Kind::Lf)
scan(); scan();
check(Token::Kind::Eof); if (sym != Token::Kind::Eof)
result.push_back(call());
}
while (sym == Token::Kind::Lf)
scan();
check(Token::Kind::Eof);
if (hecl::VerbosityLevel > 1) if (hecl::VerbosityLevel > 1)
for (auto& res : result) for (auto& res : result)
printf("%s\n", res.toString().c_str()); printf("%s\n", res.toString().c_str());
return result; return result;
} }
} } // namespace hecl::Frontend

View File

@ -1,122 +1,104 @@
#include "hecl/Frontend.hpp" #include "hecl/Frontend.hpp"
namespace hecl::Frontend namespace hecl::Frontend {
{
int Scanner::_read() int Scanner::_read() {
{ if (m_sourceIt == m_source.end())
if (m_sourceIt == m_source.end()) return -1;
return -1; return *m_sourceIt++;
return *m_sourceIt++;
} }
bool Scanner::read() bool Scanner::read() {
{ if (ch == EOF)
if (ch == EOF) return false;
return false; if (ch == LF) {
if (ch == LF) lastLine = std::move(currentLine);
{ currentLine = std::string();
lastLine = std::move(currentLine); }
currentLine = std::string(); int c = _read();
} ch = char(c);
int c = _read(); if (ch == LF) {
ch = char(c); loc.line++;
if (ch == LF) lfcol = loc.col;
{ loc.col = 0;
loc.line++; } else if (c != EOF) {
lfcol = loc.col; currentLine += ch;
loc.col = 0; loc.col++;
} }
else if (c != EOF) return c != EOF;
{
currentLine += ch;
loc.col++;
}
return c != EOF;
} }
Token Scanner::next() Token Scanner::next() {
{ if (ch == EOF)
if (ch == EOF) return Token(Token::Kind::Eof, loc);
return Token(Token::Kind::Eof, loc);
char c = ch; char c = ch;
int tline = loc.line; int tline = loc.line;
int tcol = loc.col; int tcol = loc.col;
int tlfcol = lfcol; int tlfcol = lfcol;
read(); read();
// skip comments and newlines // skip comments and newlines
while (c != EOF && (c == COMMENT || isspace(c))) while (c != EOF && (c == COMMENT || isspace(c))) {
{ if (c == COMMENT) {
if (c == COMMENT) while (c != LF && c != EOF) {
{ tline = loc.line;
while (c != LF && c != EOF) tcol = loc.col;
{ tlfcol = lfcol;
tline = loc.line; c = ch;
tcol = loc.col; read();
tlfcol = lfcol; }
c = ch;
read();
}
}
while (c != EOF && isspace(c))
{
if (c == LF)
return Token(Token::Kind::Lf, {tline - 1, tlfcol + 1});
tline = loc.line;
tcol = loc.col;
tlfcol = lfcol;
c = ch;
read();
}
} }
while (c != EOF && isspace(c)) {
Token::Kind kind = CharToTokenKind(c); if (c == LF)
if (kind != Token::Kind::None) return Token(Token::Kind::Lf, {tline - 1, tlfcol + 1});
return Token(kind, {tline, tcol}); tline = loc.line;
tcol = loc.col;
if (ch == EOF) tlfcol = lfcol;
return Token(Token::Kind::Eof, {tline, tcol}); c = ch;
read();
// ident or number
if (isDigit(c))
{
std::string buf;
buf += c;
while (isDigit(ch))
{
buf += ch;
read();
}
if (ch == '.')
{ // float
buf += ch;
read();
while (isDigit(ch))
{
buf += ch;
read();
}
}
return Token(Token::Kind::Number, std::move(buf), {tline, tcol});
} }
}
if (isStartIdent(c)) Token::Kind kind = CharToTokenKind(c);
{ if (kind != Token::Kind::None)
std::string buf; return Token(kind, {tline, tcol});
buf += c;
while (isMidIdent(ch)) if (ch == EOF)
{ return Token(Token::Kind::Eof, {tline, tcol});
buf += ch;
read(); // ident or number
} if (isDigit(c)) {
return Token(Token::Kind::Ident, std::move(buf), {tline, tcol}); std::string buf;
buf += c;
while (isDigit(ch)) {
buf += ch;
read();
} }
if (ch == '.') { // float
buf += ch;
read();
while (isDigit(ch)) {
buf += ch;
read();
}
}
return Token(Token::Kind::Number, std::move(buf), {tline, tcol});
}
error({tline, tcol}, "unexpected character '%c' (X'%02X')", chr(c), int(c)); if (isStartIdent(c)) {
std::string buf;
buf += c;
while (isMidIdent(ch)) {
buf += ch;
read();
}
return Token(Token::Kind::Ident, std::move(buf), {tline, tcol});
}
return Token(Token::Kind::None, {tline, tcol}); error({tline, tcol}, "unexpected character '%c' (X'%02X')", chr(c), int(c));
return Token(Token::Kind::None, {tline, tcol});
} }
} } // namespace hecl::Frontend

View File

@ -32,133 +32,124 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
namespace hecl namespace hecl {
{
static logvisor::Module Log("hecl::HumanizeNumber"); static logvisor::Module Log("hecl::HumanizeNumber");
static const int maxscale = 7; static const int maxscale = 7;
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags) std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags) {
{ const char *prefixes, *sep;
const char *prefixes, *sep; int i, r, remainder, s1, s2, sign;
int i, r, remainder, s1, s2, sign; int divisordeccut;
int divisordeccut; int64_t divisor, max;
int64_t divisor, max; size_t baselen;
size_t baselen;
/* validate args */ /* validate args */
if (suffix == nullptr) if (suffix == nullptr)
suffix = ""; suffix = "";
if ((flags & HNFlags::Divisor1000) != HNFlags::None && (flags & HNFlags::IECPrefixes) != HNFlags::None) if ((flags & HNFlags::Divisor1000) != HNFlags::None && (flags & HNFlags::IECPrefixes) != HNFlags::None)
Log.report(logvisor::Fatal, "invalid flags combo"); Log.report(logvisor::Fatal, "invalid flags combo");
/* setup parameters */ /* setup parameters */
remainder = 0; remainder = 0;
if ((flags & HNFlags::IECPrefixes) != HNFlags::None) { if ((flags & HNFlags::IECPrefixes) != HNFlags::None) {
baselen = 2; baselen = 2;
/*
* Use the prefixes for power of two recommended by
* the International Electrotechnical Commission
* (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
*
* HN_IEC_PREFIXES implies a divisor of 1024 here
* (use of HN_DIVISOR_1000 would have triggered
* an assertion earlier).
*/
divisor = 1024;
divisordeccut = 973; /* ceil(.95 * 1024) */
if ((flags & HNFlags::B) != HNFlags::None)
prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
else
prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
} else {
baselen = 1;
if ((flags & HNFlags::Divisor1000) != HNFlags::None) {
divisor = 1000;
divisordeccut = 950;
if ((flags & HNFlags::B) != HNFlags::None)
prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
} else {
divisor = 1024;
divisordeccut = 973; /* ceil(.95 * 1024) */
if ((flags & HNFlags::B) != HNFlags::None)
prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
}
}
#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
if (quotient < 0) {
sign = -1;
quotient = -quotient;
baselen += 2; /* sign, digit */
} else {
sign = 1;
baselen += 1; /* digit */
}
if ((flags & HNFlags::NoSpace) != HNFlags::None)
sep = "";
else {
sep = " ";
baselen++;
}
baselen += strlen(suffix);
/* Check if enough room for `x y' + suffix */
if (len < baselen)
Log.report(logvisor::Fatal,
"buffer size %" PRISize "insufficient for minimum size %" PRISize,
len, baselen);
std::string ret(len, '\0');
len += 1;
if ((scale & int(HNScale::AutoScale)) != 0) {
/* See if there is additional columns can be used. */
for (max = 1, i = len - baselen; i-- > 0;)
max *= 10;
/*
* Divide the number until it fits the given column.
* If there will be an overflow by the rounding below,
* divide once more.
*/
for (i = 0;
(quotient >= max || (quotient == max - 1 &&
remainder >= divisordeccut)) && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
} else {
for (i = 0; i < scale && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
}
/* If a value <= 9.9 after rounding and ... */
/* /*
* XXX - should we make sure there is enough space for the decimal * Use the prefixes for power of two recommended by
* place and if not, don't do HN_DECIMAL? * the International Electrotechnical Commission
* (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
*
* HN_IEC_PREFIXES implies a divisor of 1024 here
* (use of HN_DIVISOR_1000 would have triggered
* an assertion earlier).
*/ */
if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) && divisor = 1024;
i > 0 && (flags & HNFlags::Decimal) != HNFlags::None) { divisordeccut = 973; /* ceil(.95 * 1024) */
s1 = (int)quotient + ((remainder * 10 + divisor / 2) / if ((flags & HNFlags::B) != HNFlags::None)
divisor / 10); prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
s2 = ((remainder * 10 + divisor / 2) / divisor) % 10; else
r = snprintf(&ret[0], len, "%d%s%d%s%s%s", prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
sign * s1, localeconv()->decimal_point, s2, } else {
sep, SCALE2PREFIX(i), suffix); baselen = 1;
} else if ((flags & HNFlags::Divisor1000) != HNFlags::None) {
r = snprintf(&ret[0], len, "%" PRId64 "%s%s%s", divisor = 1000;
sign * (quotient + (remainder + divisor / 2) / divisor), divisordeccut = 950;
sep, SCALE2PREFIX(i), suffix); if ((flags & HNFlags::B) != HNFlags::None)
prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
} else {
divisor = 1024;
divisordeccut = 973; /* ceil(.95 * 1024) */
if ((flags & HNFlags::B) != HNFlags::None)
prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
}
}
return ret; #define SCALE2PREFIX(scale) (&prefixes[(scale)*3])
if (quotient < 0) {
sign = -1;
quotient = -quotient;
baselen += 2; /* sign, digit */
} else {
sign = 1;
baselen += 1; /* digit */
}
if ((flags & HNFlags::NoSpace) != HNFlags::None)
sep = "";
else {
sep = " ";
baselen++;
}
baselen += strlen(suffix);
/* Check if enough room for `x y' + suffix */
if (len < baselen)
Log.report(logvisor::Fatal, "buffer size %" PRISize "insufficient for minimum size %" PRISize, len, baselen);
std::string ret(len, '\0');
len += 1;
if ((scale & int(HNScale::AutoScale)) != 0) {
/* See if there is additional columns can be used. */
for (max = 1, i = len - baselen; i-- > 0;)
max *= 10;
/*
* Divide the number until it fits the given column.
* If there will be an overflow by the rounding below,
* divide once more.
*/
for (i = 0; (quotient >= max || (quotient == max - 1 && remainder >= divisordeccut)) && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
} else {
for (i = 0; i < scale && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
}
/* If a value <= 9.9 after rounding and ... */
/*
* XXX - should we make sure there is enough space for the decimal
* place and if not, don't do HN_DECIMAL?
*/
if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) && i > 0 &&
(flags & HNFlags::Decimal) != HNFlags::None) {
s1 = (int)quotient + ((remainder * 10 + divisor / 2) / divisor / 10);
s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
r = snprintf(&ret[0], len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i),
suffix);
} else
r = snprintf(&ret[0], len, "%" PRId64 "%s%s%s", sign * (quotient + (remainder + divisor / 2) / divisor), sep,
SCALE2PREFIX(i), suffix);
return ret;
} }
} } // namespace hecl

View File

@ -7,386 +7,336 @@
#define SHOW_CURSOR "\033[?25h" #define SHOW_CURSOR "\033[?25h"
#if _WIN32 #if _WIN32
#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE #define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#endif #endif
namespace hecl namespace hecl {
{
void MultiProgressPrinter::ThreadStat::print(const TermInfo& tinfo) const void MultiProgressPrinter::ThreadStat::print(const TermInfo& tinfo) const {
{ bool blocks = m_factor >= 0.f;
bool blocks = m_factor >= 0.f; float factor = std::max(0.f, std::min(1.f, m_factor));
float factor = std::max(0.f, std::min(1.f, m_factor)); int iFactor = factor * 100.f;
int iFactor = factor * 100.f;
int messageLen = m_message.size(); int messageLen = m_message.size();
int submessageLen = m_submessage.size(); int submessageLen = m_submessage.size();
int half; int half;
if (blocks) if (blocks)
half = (tinfo.width + 1) / 2 - 2; half = (tinfo.width + 1) / 2 - 2;
else if (tinfo.truncate) else if (tinfo.truncate)
half = tinfo.width - 4; half = tinfo.width - 4;
else else
half = messageLen; half = messageLen;
if (half - messageLen < submessageLen-2) if (half - messageLen < submessageLen - 2)
submessageLen = 0; submessageLen = 0;
if (submessageLen) if (submessageLen) {
{ if (messageLen > half - submessageLen - 1)
if (messageLen > half-submessageLen-1) hecl::Printf(_SYS_STR(" %.*s... %s "), half - submessageLen - 4, m_message.c_str(), m_submessage.c_str());
hecl::Printf(_SYS_STR(" %.*s... %s "), half-submessageLen-4, m_message.c_str(), m_submessage.c_str()); else {
else hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
{ for (int i = half - messageLen - submessageLen - 1; i >= 0; --i)
hecl::Printf(_SYS_STR(" %s"), m_message.c_str()); hecl::Printf(_SYS_STR(" "));
for (int i=half-messageLen-submessageLen-1 ; i>=0 ; --i) hecl::Printf(_SYS_STR("%s "), m_submessage.c_str());
hecl::Printf(_SYS_STR(" "));
hecl::Printf(_SYS_STR("%s "), m_submessage.c_str());
}
} }
else } else {
{ if (messageLen > half)
if (messageLen > half) hecl::Printf(_SYS_STR(" %.*s... "), half - 3, m_message.c_str());
hecl::Printf(_SYS_STR(" %.*s... "), half-3, m_message.c_str()); else {
else hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
{ for (int i = half - messageLen; i >= 0; --i)
hecl::Printf(_SYS_STR(" %s"), m_message.c_str()); hecl::Printf(_SYS_STR(" "));
for (int i=half-messageLen ; i>=0 ; --i)
hecl::Printf(_SYS_STR(" "));
}
} }
}
if (blocks) if (blocks) {
{ int rightHalf = tinfo.width - half - 4;
int rightHalf = tinfo.width - half - 4; int blocks = rightHalf - 7;
int blocks = rightHalf - 7; int filled = blocks * factor;
int filled = blocks * factor; int rem = blocks - filled;
int rem = blocks - filled;
if (tinfo.xtermColor) if (tinfo.xtermColor) {
{ hecl::Printf(_SYS_STR("" BOLD "%3d%% ["), iFactor);
hecl::Printf(_SYS_STR("" BOLD "%3d%% ["), iFactor); for (int b = 0; b < filled; ++b)
for (int b=0 ; b<filled ; ++b)
hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL ""));
}
else
{
#if _WIN32
SetConsoleTextAttribute(tinfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#endif
hecl::Printf(_SYS_STR("%3d%% ["), iFactor);
for (int b=0 ; b<filled ; ++b)
hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]"));
#if _WIN32
SetConsoleTextAttribute(tinfo.console, FOREGROUND_WHITE);
#endif
}
}
}
void MultiProgressPrinter::DrawIndeterminateBar()
{
int half = m_termInfo.width - 2;
int blocks = half - 2;
++m_indeterminateCounter;
if (m_indeterminateCounter <= -blocks)
m_indeterminateCounter = -blocks + 1;
else if (m_indeterminateCounter >= blocks)
m_indeterminateCounter = -blocks + 2;
int absCounter = std::abs(m_indeterminateCounter);
int pre = absCounter;
int rem = blocks - pre - 1;
if (m_termInfo.xtermColor)
{
hecl::Printf(_SYS_STR("" BOLD " ["));
for (int b=0 ; b<pre ; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("#")); hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b) for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-")); hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL "")); hecl::Printf(_SYS_STR("]" NORMAL ""));
} } else {
else
{
#if _WIN32 #if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE); SetConsoleTextAttribute(tinfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#endif #endif
hecl::Printf(_SYS_STR(" [")); hecl::Printf(_SYS_STR("%3d%% ["), iFactor);
for (int b=0 ; b<pre ; ++b) for (int b = 0; b < filled; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("#")); hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b) for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-")); hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]")); hecl::Printf(_SYS_STR("]"));
#if _WIN32 #if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE); SetConsoleTextAttribute(tinfo.console, FOREGROUND_WHITE);
#endif #endif
} }
}
} }
void MultiProgressPrinter::MoveCursorUp(int n) void MultiProgressPrinter::DrawIndeterminateBar() {
{ int half = m_termInfo.width - 2;
if (n) int blocks = half - 2;
{
if (m_termInfo.xtermColor) ++m_indeterminateCounter;
{ if (m_indeterminateCounter <= -blocks)
hecl::Printf(_SYS_STR("" PREV_LINE ""), n); m_indeterminateCounter = -blocks + 1;
} else if (m_indeterminateCounter >= blocks)
m_indeterminateCounter = -blocks + 2;
int absCounter = std::abs(m_indeterminateCounter);
int pre = absCounter;
int rem = blocks - pre - 1;
if (m_termInfo.xtermColor) {
hecl::Printf(_SYS_STR("" BOLD " ["));
for (int b = 0; b < pre; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("#"));
for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL ""));
} else {
#if _WIN32 #if _WIN32
else SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
{
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
GetConsoleScreenBufferInfo(m_termInfo.console, &consoleInfo);
consoleInfo.dwCursorPosition.X = 0;
consoleInfo.dwCursorPosition.Y -= n;
SetConsoleCursorPosition(m_termInfo.console, consoleInfo.dwCursorPosition);
}
#endif #endif
} hecl::Printf(_SYS_STR(" ["));
else for (int b = 0; b < pre; ++b)
{ hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("\r")); hecl::Printf(_SYS_STR("#"));
} for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]"));
#if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE);
#endif
}
} }
void MultiProgressPrinter::DoPrint() void MultiProgressPrinter::MoveCursorUp(int n) {
{ if (n) {
auto logLk = logvisor::LockLog(); if (m_termInfo.xtermColor) {
uint64_t logCounter = logvisor::GetLogCounter(); hecl::Printf(_SYS_STR("" PREV_LINE ""), n);
if (logCounter != m_lastLogCounter)
{
m_curThreadLines = 0;
m_lastLogCounter = logCounter;
} }
#if _WIN32 #if _WIN32
CONSOLE_CURSOR_INFO cursorInfo; else {
GetConsoleCursorInfo(m_termInfo.console, &cursorInfo); CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
cursorInfo.bVisible = FALSE; GetConsoleScreenBufferInfo(m_termInfo.console, &consoleInfo);
SetConsoleCursorInfo(m_termInfo.console, &cursorInfo); consoleInfo.dwCursorPosition.X = 0;
#endif consoleInfo.dwCursorPosition.Y -= n;
if (m_termInfo.xtermColor) SetConsoleCursorPosition(m_termInfo.console, consoleInfo.dwCursorPosition);
hecl::Printf(_SYS_STR("" HIDE_CURSOR ""));
if (m_dirty)
{
m_termInfo.width = (hecl::GuiMode ? 120 : std::max(80, hecl::ConsoleWidth(&m_termInfo.truncate)));
MoveCursorUp(m_curThreadLines + m_curProgLines);
m_curThreadLines = m_curProgLines = 0;
if (m_newLineAfter)
{
for (const ThreadStat& stat : m_threadStats)
{
if (stat.m_active)
{
stat.print(m_termInfo);
hecl::Printf(_SYS_STR("\n"));
++m_curThreadLines;
}
}
if (m_mainIndeterminate
#ifndef _WIN32
&& m_termInfo.xtermColor
#endif
)
{
DrawIndeterminateBar();
hecl::Printf(_SYS_STR("\n"));
++m_curProgLines;
}
else if (m_mainFactor >= 0.f)
{
float factor = std::max(0.0f, std::min(1.0f, m_mainFactor));
int iFactor = factor * 100.0;
int half = m_termInfo.width - 2;
int blocks = half - 8;
int filled = blocks * factor;
int rem = blocks - filled;
if (m_termInfo.xtermColor)
{
hecl::Printf(_SYS_STR("" BOLD " %3d%% ["), iFactor);
for (int b=0 ; b<filled ; ++b)
hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL ""));
}
else
{
#if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#endif
hecl::Printf(_SYS_STR(" %3d%% ["), iFactor);
for (int b=0 ; b<filled ; ++b)
hecl::Printf(_SYS_STR("#"));
for (int b=0 ; b<rem ; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]"));
#if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE);
#endif
}
hecl::Printf(_SYS_STR("\n"));
++m_curProgLines;
}
}
else if (m_latestThread != -1)
{
const ThreadStat& stat = m_threadStats[m_latestThread];
stat.print(m_termInfo);
hecl::Printf(_SYS_STR("\r"));
}
m_dirty = false;
} }
else if (m_mainIndeterminate
#ifndef _WIN32
&& m_termInfo.xtermColor
#endif #endif
) } else {
{ hecl::Printf(_SYS_STR("\r"));
m_termInfo.width = (hecl::GuiMode ? 120 : std::max(80, hecl::ConsoleWidth())); }
MoveCursorUp(m_curProgLines); }
m_curProgLines = 0;
void MultiProgressPrinter::DoPrint() {
auto logLk = logvisor::LockLog();
uint64_t logCounter = logvisor::GetLogCounter();
if (logCounter != m_lastLogCounter) {
m_curThreadLines = 0;
m_lastLogCounter = logCounter;
}
#if _WIN32
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(m_termInfo.console, &cursorInfo);
cursorInfo.bVisible = FALSE;
SetConsoleCursorInfo(m_termInfo.console, &cursorInfo);
#endif
if (m_termInfo.xtermColor)
hecl::Printf(_SYS_STR("" HIDE_CURSOR ""));
if (m_dirty) {
m_termInfo.width = (hecl::GuiMode ? 120 : std::max(80, hecl::ConsoleWidth(&m_termInfo.truncate)));
MoveCursorUp(m_curThreadLines + m_curProgLines);
m_curThreadLines = m_curProgLines = 0;
if (m_newLineAfter) {
for (const ThreadStat& stat : m_threadStats) {
if (stat.m_active) {
stat.print(m_termInfo);
hecl::Printf(_SYS_STR("\n"));
++m_curThreadLines;
}
}
if (m_mainIndeterminate
#ifndef _WIN32
&& m_termInfo.xtermColor
#endif
) {
DrawIndeterminateBar(); DrawIndeterminateBar();
hecl::Printf(_SYS_STR("\n")); hecl::Printf(_SYS_STR("\n"));
++m_curProgLines; ++m_curProgLines;
} } else if (m_mainFactor >= 0.f) {
float factor = std::max(0.0f, std::min(1.0f, m_mainFactor));
int iFactor = factor * 100.0;
int half = m_termInfo.width - 2;
if (m_termInfo.xtermColor) int blocks = half - 8;
hecl::Printf(_SYS_STR("" SHOW_CURSOR "")); int filled = blocks * factor;
fflush(stdout); int rem = blocks - filled;
if (m_termInfo.xtermColor) {
hecl::Printf(_SYS_STR("" BOLD " %3d%% ["), iFactor);
for (int b = 0; b < filled; ++b)
hecl::Printf(_SYS_STR("#"));
for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL ""));
} else {
#if _WIN32 #if _WIN32
cursorInfo.bVisible = TRUE; SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
SetConsoleCursorInfo(m_termInfo.console, &cursorInfo);
#endif #endif
} hecl::Printf(_SYS_STR(" %3d%% ["), iFactor);
for (int b = 0; b < filled; ++b)
void MultiProgressPrinter::LogProc() hecl::Printf(_SYS_STR("#"));
{ for (int b = 0; b < rem; ++b)
while (m_running) hecl::Printf(_SYS_STR("-"));
{ hecl::Printf(_SYS_STR("]"));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (!m_dirty && !m_mainIndeterminate)
continue;
std::lock_guard<std::mutex> lk(m_logLock);
DoPrint();
}
}
MultiProgressPrinter::MultiProgressPrinter(bool activate)
{
if (activate)
{
/* Xterm check */
#if _WIN32 #if _WIN32
m_newLineAfter = true; SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE);
m_termInfo.console = GetStdHandle(STD_OUTPUT_HANDLE); #endif
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
m_termInfo.xtermColor = true;
#else
m_newLineAfter = false;
const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5))
{
m_termInfo.xtermColor = true;
m_newLineAfter = true;
} }
hecl::Printf(_SYS_STR("\n"));
++m_curProgLines;
}
} else if (m_latestThread != -1) {
const ThreadStat& stat = m_threadStats[m_latestThread];
stat.print(m_termInfo);
hecl::Printf(_SYS_STR("\r"));
}
m_dirty = false;
} else if (m_mainIndeterminate
#ifndef _WIN32
&& m_termInfo.xtermColor
#endif
) {
m_termInfo.width = (hecl::GuiMode ? 120 : std::max(80, hecl::ConsoleWidth()));
MoveCursorUp(m_curProgLines);
m_curProgLines = 0;
DrawIndeterminateBar();
hecl::Printf(_SYS_STR("\n"));
++m_curProgLines;
}
if (m_termInfo.xtermColor)
hecl::Printf(_SYS_STR("" SHOW_CURSOR ""));
fflush(stdout);
#if _WIN32
cursorInfo.bVisible = TRUE;
SetConsoleCursorInfo(m_termInfo.console, &cursorInfo);
#endif
}
void MultiProgressPrinter::LogProc() {
while (m_running) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (!m_dirty && !m_mainIndeterminate)
continue;
std::lock_guard<std::mutex> lk(m_logLock);
DoPrint();
}
}
MultiProgressPrinter::MultiProgressPrinter(bool activate) {
if (activate) {
/* Xterm check */
#if _WIN32
m_newLineAfter = true;
m_termInfo.console = GetStdHandle(STD_OUTPUT_HANDLE);
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
m_termInfo.xtermColor = true;
#else
m_newLineAfter = false;
const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5)) {
m_termInfo.xtermColor = true;
m_newLineAfter = true;
}
#endif #endif
m_running = true; m_running = true;
m_logThread = std::thread(std::bind(&MultiProgressPrinter::LogProc, this)); m_logThread = std::thread(std::bind(&MultiProgressPrinter::LogProc, this));
} }
} }
MultiProgressPrinter::~MultiProgressPrinter() MultiProgressPrinter::~MultiProgressPrinter() {
{ m_running = false;
m_running = false; if (m_logThread.joinable())
if (m_logThread.joinable()) m_logThread.join();
m_logThread.join();
} }
void MultiProgressPrinter::print(const hecl::SystemChar* message, void MultiProgressPrinter::print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, float factor,
const hecl::SystemChar* submessage, int threadIdx) const {
float factor, int threadIdx) const if (!m_running)
{ return;
if (!m_running) std::lock_guard<std::mutex> lk(m_logLock);
return; if (threadIdx < 0)
std::lock_guard<std::mutex> lk(m_logLock); threadIdx = 0;
if (threadIdx < 0) if (threadIdx >= m_threadStats.size())
threadIdx = 0; m_threadStats.resize(threadIdx + 1);
if (threadIdx >= m_threadStats.size()) ThreadStat& stat = m_threadStats[threadIdx];
m_threadStats.resize(threadIdx + 1); if (message)
ThreadStat& stat = m_threadStats[threadIdx]; stat.m_message = message;
if (message) else
stat.m_message = message; stat.m_message.clear();
else if (submessage)
stat.m_message.clear(); stat.m_submessage = submessage;
if (submessage) else
stat.m_submessage = submessage; stat.m_submessage.clear();
else stat.m_factor = factor;
stat.m_submessage.clear(); stat.m_active = true;
stat.m_factor = factor; m_latestThread = threadIdx;
stat.m_active = true; m_dirty = true;
m_latestThread = threadIdx; }
void MultiProgressPrinter::setMainFactor(float factor) const {
if (!m_running)
return;
std::lock_guard<std::mutex> lk(m_logLock);
if (!m_mainIndeterminate)
m_dirty = true; m_dirty = true;
m_mainFactor = factor;
} }
void MultiProgressPrinter::setMainFactor(float factor) const void MultiProgressPrinter::setMainIndeterminate(bool indeterminate) const {
{ if (!m_running)
if (!m_running) return;
return; std::lock_guard<std::mutex> lk(m_logLock);
std::lock_guard<std::mutex> lk(m_logLock); if (m_mainIndeterminate != indeterminate) {
if (!m_mainIndeterminate) m_mainIndeterminate = indeterminate;
m_dirty = true; m_dirty = true;
m_mainFactor = factor; }
} }
void MultiProgressPrinter::setMainIndeterminate(bool indeterminate) const void MultiProgressPrinter::startNewLine() const {
{ if (!m_running)
if (!m_running) return;
return; std::lock_guard<std::mutex> lk(m_logLock);
std::lock_guard<std::mutex> lk(m_logLock); const_cast<MultiProgressPrinter&>(*this).DoPrint();
if (m_mainIndeterminate != indeterminate) m_threadStats.clear();
{ m_latestThread = -1;
m_mainIndeterminate = indeterminate; m_curThreadLines = 0;
m_dirty = true; m_mainFactor = -1.f;
} auto logLk = logvisor::LockLog();
hecl::Printf(_SYS_STR("\n"));
} }
void MultiProgressPrinter::startNewLine() const void MultiProgressPrinter::flush() const {
{ std::lock_guard<std::mutex> lk(m_logLock);
if (!m_running) const_cast<MultiProgressPrinter&>(*this).DoPrint();
return;
std::lock_guard<std::mutex> lk(m_logLock);
const_cast<MultiProgressPrinter&>(*this).DoPrint();
m_threadStats.clear();
m_latestThread = -1;
m_curThreadLines = 0;
m_mainFactor = -1.f;
auto logLk = logvisor::LockLog();
hecl::Printf(_SYS_STR("\n"));
} }
void MultiProgressPrinter::flush() const } // namespace hecl
{
std::lock_guard<std::mutex> lk(m_logLock);
const_cast<MultiProgressPrinter&>(*this).DoPrint();
}
}

View File

@ -2,159 +2,143 @@
#include "athena/FileReader.hpp" #include "athena/FileReader.hpp"
#include <zlib.h> #include <zlib.h>
namespace hecl namespace hecl {
{
#if HECL_RUNTIME #if HECL_RUNTIME
PipelineConverterBase* conv = nullptr; PipelineConverterBase* conv = nullptr;
class ShaderCacheZipStream : public athena::io::IStreamReader class ShaderCacheZipStream : public athena::io::IStreamReader {
{ std::unique_ptr<uint8_t[]> m_compBuf;
std::unique_ptr<uint8_t[]> m_compBuf; athena::io::FileReader m_reader;
athena::io::FileReader m_reader; z_stream m_zstrm = {};
z_stream m_zstrm = {};
public: public:
explicit ShaderCacheZipStream(const hecl::SystemChar* path) explicit ShaderCacheZipStream(const hecl::SystemChar* path) : m_reader(path) {
: m_reader(path) if (m_reader.hasError())
{ return;
if (m_reader.hasError()) if (m_reader.readUint32Big() != SBIG('SHAD'))
return; return;
if (m_reader.readUint32Big() != SBIG('SHAD')) m_compBuf.reset(new uint8_t[4096]);
return; m_zstrm.next_in = m_compBuf.get();
m_compBuf.reset(new uint8_t[4096]); m_zstrm.avail_in = 0;
inflateInit(&m_zstrm);
}
~ShaderCacheZipStream() { inflateEnd(&m_zstrm); }
operator bool() const { return m_compBuf.operator bool(); }
atUint64 readUBytesToBuf(void* buf, atUint64 len) {
m_zstrm.next_out = (Bytef*)buf;
m_zstrm.avail_out = len;
m_zstrm.total_out = 0;
while (m_zstrm.avail_out != 0) {
if (m_zstrm.avail_in == 0) {
atUint64 readSz = m_reader.readUBytesToBuf(m_compBuf.get(), 4096);
m_zstrm.avail_in = readSz;
m_zstrm.next_in = m_compBuf.get(); m_zstrm.next_in = m_compBuf.get();
m_zstrm.avail_in = 0; }
inflateInit(&m_zstrm); int inflateRet = inflate(&m_zstrm, Z_NO_FLUSH);
if (inflateRet != Z_OK)
break;
} }
~ShaderCacheZipStream() return m_zstrm.total_out;
{ }
inflateEnd(&m_zstrm); void seek(atInt64, athena::SeekOrigin) {}
} atUint64 position() const { return 0; }
operator bool() const { return m_compBuf.operator bool(); } atUint64 length() const { return 0; }
atUint64 readUBytesToBuf(void *buf, atUint64 len)
{
m_zstrm.next_out = (Bytef*)buf;
m_zstrm.avail_out = len;
m_zstrm.total_out = 0;
while (m_zstrm.avail_out != 0)
{
if (m_zstrm.avail_in == 0)
{
atUint64 readSz = m_reader.readUBytesToBuf(m_compBuf.get(), 4096);
m_zstrm.avail_in = readSz;
m_zstrm.next_in = m_compBuf.get();
}
int inflateRet = inflate(&m_zstrm, Z_NO_FLUSH);
if (inflateRet != Z_OK)
break;
}
return m_zstrm.total_out;
}
void seek(atInt64, athena::SeekOrigin) {}
atUint64 position() const {return 0;}
atUint64 length() const {return 0;}
}; };
template<typename P, typename S> template <typename P, typename S>
void StageConverter<P, S>::loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r) void StageConverter<P, S>::loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r) {
{ uint32_t count = r.readUint32Big();
uint32_t count = r.readUint32Big(); for (uint32_t i = 0; i < count; ++i) {
for (uint32_t i = 0; i < count; ++i) uint64_t hash = r.readUint64Big();
{ uint32_t size = r.readUint32Big();
uint64_t hash = r.readUint64Big(); StageBinaryData data = MakeStageBinaryData(size);
uint32_t size = r.readUint32Big(); r.readUBytesToBuf(data.get(), size);
StageBinaryData data = MakeStageBinaryData(size); m_stageCache.insert(std::make_pair(hash, Do<StageTargetTp>(ctx, StageBinary<P, S>(data, size))));
r.readUBytesToBuf(data.get(), size); }
m_stageCache.insert(std::make_pair(hash,
Do<StageTargetTp>(ctx, StageBinary<P, S>(data, size))));
}
} }
static boo::AdditionalPipelineInfo ReadAdditionalInfo(ShaderCacheZipStream& r) static boo::AdditionalPipelineInfo ReadAdditionalInfo(ShaderCacheZipStream& r) {
{ boo::AdditionalPipelineInfo additionalInfo;
boo::AdditionalPipelineInfo additionalInfo; additionalInfo.srcFac = boo::BlendFactor(r.readUint32Big());
additionalInfo.srcFac = boo::BlendFactor(r.readUint32Big()); additionalInfo.dstFac = boo::BlendFactor(r.readUint32Big());
additionalInfo.dstFac = boo::BlendFactor(r.readUint32Big()); additionalInfo.prim = boo::Primitive(r.readUint32Big());
additionalInfo.prim = boo::Primitive(r.readUint32Big()); additionalInfo.depthTest = boo::ZTest(r.readUint32Big());
additionalInfo.depthTest = boo::ZTest(r.readUint32Big()); additionalInfo.depthWrite = r.readBool();
additionalInfo.depthWrite = r.readBool(); additionalInfo.colorWrite = r.readBool();
additionalInfo.colorWrite = r.readBool(); additionalInfo.alphaWrite = r.readBool();
additionalInfo.alphaWrite = r.readBool(); additionalInfo.culling = boo::CullMode(r.readUint32Big());
additionalInfo.culling = boo::CullMode(r.readUint32Big()); additionalInfo.patchSize = r.readUint32Big();
additionalInfo.patchSize = r.readUint32Big(); additionalInfo.overwriteAlpha = r.readBool();
additionalInfo.overwriteAlpha = r.readBool(); additionalInfo.depthAttachment = r.readBool();
additionalInfo.depthAttachment = r.readBool(); return additionalInfo;
return additionalInfo;
} }
static std::vector<boo::VertexElementDescriptor> ReadVertexFormat(ShaderCacheZipStream& r) static std::vector<boo::VertexElementDescriptor> ReadVertexFormat(ShaderCacheZipStream& r) {
{ std::vector<boo::VertexElementDescriptor> ret;
std::vector<boo::VertexElementDescriptor> ret; uint32_t count = r.readUint32Big();
uint32_t count = r.readUint32Big(); ret.reserve(count);
ret.reserve(count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i) {
{ ret.emplace_back();
ret.emplace_back(); ret.back().semantic = boo::VertexSemantic(r.readUint32Big());
ret.back().semantic = boo::VertexSemantic(r.readUint32Big()); ret.back().semanticIdx = int(r.readUint32Big());
ret.back().semanticIdx = int(r.readUint32Big()); }
}
return ret; return ret;
} }
template<typename P> template <typename P>
bool PipelineConverter<P>::loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path) bool PipelineConverter<P>::loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path) {
{ ShaderCacheZipStream r(path);
ShaderCacheZipStream r(path); if (!r)
if (!r) return false;
return false;
m_vertexConverter.loadFromStream(ctx, r); m_vertexConverter.loadFromStream(ctx, r);
m_fragmentConverter.loadFromStream(ctx, r); m_fragmentConverter.loadFromStream(ctx, r);
m_geometryConverter.loadFromStream(ctx, r); m_geometryConverter.loadFromStream(ctx, r);
m_controlConverter.loadFromStream(ctx, r); m_controlConverter.loadFromStream(ctx, r);
m_evaluationConverter.loadFromStream(ctx, r); m_evaluationConverter.loadFromStream(ctx, r);
uint32_t count = r.readUint32Big(); uint32_t count = r.readUint32Big();
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i) {
{ uint64_t hash = r.readUint64Big();
uint64_t hash = r.readUint64Big(); StageRuntimeObject<P, PipelineStage::Vertex> vertex;
StageRuntimeObject<P, PipelineStage::Vertex> vertex; StageRuntimeObject<P, PipelineStage::Fragment> fragment;
StageRuntimeObject<P, PipelineStage::Fragment> fragment; StageRuntimeObject<P, PipelineStage::Geometry> geometry;
StageRuntimeObject<P, PipelineStage::Geometry> geometry; StageRuntimeObject<P, PipelineStage::Control> control;
StageRuntimeObject<P, PipelineStage::Control> control; StageRuntimeObject<P, PipelineStage::Evaluation> evaluation;
StageRuntimeObject<P, PipelineStage::Evaluation> evaluation; if (uint64_t vhash = r.readUint64Big())
if (uint64_t vhash = r.readUint64Big()) vertex = m_vertexConverter.m_stageCache.find(vhash)->second;
vertex = m_vertexConverter.m_stageCache.find(vhash)->second; if (uint64_t fhash = r.readUint64Big())
if (uint64_t fhash = r.readUint64Big()) fragment = m_fragmentConverter.m_stageCache.find(fhash)->second;
fragment = m_fragmentConverter.m_stageCache.find(fhash)->second; if (uint64_t ghash = r.readUint64Big())
if (uint64_t ghash = r.readUint64Big()) geometry = m_geometryConverter.m_stageCache.find(ghash)->second;
geometry = m_geometryConverter.m_stageCache.find(ghash)->second; if (uint64_t chash = r.readUint64Big())
if (uint64_t chash = r.readUint64Big()) control = m_controlConverter.m_stageCache.find(chash)->second;
control = m_controlConverter.m_stageCache.find(chash)->second; if (uint64_t ehash = r.readUint64Big())
if (uint64_t ehash = r.readUint64Big()) evaluation = m_evaluationConverter.m_stageCache.find(ehash)->second;
evaluation = m_evaluationConverter.m_stageCache.find(ehash)->second;
boo::AdditionalPipelineInfo additionalInfo = ReadAdditionalInfo(r); boo::AdditionalPipelineInfo additionalInfo = ReadAdditionalInfo(r);
std::vector<boo::VertexElementDescriptor> vtxFmt = ReadVertexFormat(r); std::vector<boo::VertexElementDescriptor> vtxFmt = ReadVertexFormat(r);
m_pipelineCache.insert(std::make_pair(hash, FinalPipeline<P>(*this, ctx, m_pipelineCache.insert(
StageCollection<StageRuntimeObject<P, PipelineStage::Null>> std::make_pair(hash, FinalPipeline<P>(*this, ctx,
(vertex, fragment, geometry, control, evaluation, additionalInfo, StageCollection<StageRuntimeObject<P, PipelineStage::Null>>(
boo::VertexFormatInfo(vtxFmt.size(), vtxFmt.data()))))); vertex, fragment, geometry, control, evaluation, additionalInfo,
} boo::VertexFormatInfo(vtxFmt.size(), vtxFmt.data())))));
}
return true; return true;
} }
#define SPECIALIZE_STAGE_CONVERTER(P) \ #define SPECIALIZE_STAGE_CONVERTER(P) \
template class StageConverter<P, PipelineStage::Vertex>; \ template class StageConverter<P, PipelineStage::Vertex>; \
template class StageConverter<P, PipelineStage::Fragment>; \ template class StageConverter<P, PipelineStage::Fragment>; \
template class StageConverter<P, PipelineStage::Geometry>; \ template class StageConverter<P, PipelineStage::Geometry>; \
template class StageConverter<P, PipelineStage::Control>; \ template class StageConverter<P, PipelineStage::Control>; \
template class StageConverter<P, PipelineStage::Evaluation>; template class StageConverter<P, PipelineStage::Evaluation>;
#if BOO_HAS_GL #if BOO_HAS_GL
template class PipelineConverter<PlatformType::OpenGL>; template class PipelineConverter<PlatformType::OpenGL>;
@ -179,4 +163,4 @@ SPECIALIZE_STAGE_CONVERTER(PlatformType::NX)
#endif #endif
} } // namespace hecl

View File

@ -14,8 +14,7 @@
#include "hecl/ClientProcess.hpp" #include "hecl/ClientProcess.hpp"
#include "hecl/MultiProgressPrinter.hpp" #include "hecl/MultiProgressPrinter.hpp"
namespace hecl::Database namespace hecl::Database {
{
logvisor::Module LogModule("hecl::Database"); logvisor::Module LogModule("hecl::Database");
static const hecl::FourCC HECLfcc("HECL"); static const hecl::FourCC HECLfcc("HECL");
@ -24,174 +23,142 @@ static const hecl::FourCC HECLfcc("HECL");
* Project::ConfigFile * Project::ConfigFile
**********************************************/ **********************************************/
static inline bool CheckNewLineAdvance(std::string::const_iterator& it) static inline bool CheckNewLineAdvance(std::string::const_iterator& it) {
{ if (*it == '\n') {
if (*it == '\n') it += 1;
{ return true;
it += 1; } else if (*it == '\r') {
return true; if (*(it + 1) == '\n') {
it += 2;
return true;
} }
else if (*it == '\r') it += 1;
{ return true;
if (*(it+1) == '\n') }
{ return false;
it += 2;
return true;
}
it += 1;
return true;
}
return false;
} }
Project::ConfigFile::ConfigFile(const Project& project, SystemStringView name, Project::ConfigFile::ConfigFile(const Project& project, SystemStringView name, SystemStringView subdir) {
SystemStringView subdir) m_filepath = SystemString(project.m_rootPath.getAbsolutePath()) + subdir.data() + name.data();
{
m_filepath = SystemString(project.m_rootPath.getAbsolutePath()) + subdir.data() + name.data();
} }
std::vector<std::string>& Project::ConfigFile::lockAndRead() std::vector<std::string>& Project::ConfigFile::lockAndRead() {
{ if (m_lockedFile)
if (m_lockedFile)
return m_lines;
m_lockedFile = hecl::Fopen(m_filepath.c_str(), _SYS_STR("a+"), FileLockType::Write);
hecl::FSeek(m_lockedFile, 0, SEEK_SET);
std::string mainString;
char readBuf[1024];
size_t readSz;
while ((readSz = fread(readBuf, 1, 1024, m_lockedFile)))
mainString += std::string(readBuf, readSz);
std::string::const_iterator begin = mainString.begin();
std::string::const_iterator end = mainString.begin();
m_lines.clear();
while (end != mainString.end())
{
std::string::const_iterator origEnd = end;
if (*end == '\0')
break;
else if (CheckNewLineAdvance(end))
{
if (begin != origEnd)
m_lines.push_back(std::string(begin, origEnd));
begin = end;
continue;
}
++end;
}
if (begin != end)
m_lines.push_back(std::string(begin, end));
return m_lines; return m_lines;
m_lockedFile = hecl::Fopen(m_filepath.c_str(), _SYS_STR("a+"), FileLockType::Write);
hecl::FSeek(m_lockedFile, 0, SEEK_SET);
std::string mainString;
char readBuf[1024];
size_t readSz;
while ((readSz = fread(readBuf, 1, 1024, m_lockedFile)))
mainString += std::string(readBuf, readSz);
std::string::const_iterator begin = mainString.begin();
std::string::const_iterator end = mainString.begin();
m_lines.clear();
while (end != mainString.end()) {
std::string::const_iterator origEnd = end;
if (*end == '\0')
break;
else if (CheckNewLineAdvance(end)) {
if (begin != origEnd)
m_lines.push_back(std::string(begin, origEnd));
begin = end;
continue;
}
++end;
}
if (begin != end)
m_lines.push_back(std::string(begin, end));
return m_lines;
} }
void Project::ConfigFile::addLine(std::string_view line) void Project::ConfigFile::addLine(std::string_view line) {
{ if (!checkForLine(line))
if (!checkForLine(line)) m_lines.emplace_back(line);
m_lines.emplace_back(line);
} }
void Project::ConfigFile::removeLine(std::string_view refLine) void Project::ConfigFile::removeLine(std::string_view refLine) {
{ if (!m_lockedFile) {
if (!m_lockedFile) LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called");
{ return;
LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, }
"Project::ConfigFile::lockAndRead not yet called");
return;
}
for (auto it = m_lines.begin(); for (auto it = m_lines.begin(); it != m_lines.end();) {
it != m_lines.end();) if (!(*it).compare(refLine)) {
{ it = m_lines.erase(it);
if (!(*it).compare(refLine)) continue;
{
it = m_lines.erase(it);
continue;
}
++it;
} }
++it;
}
} }
bool Project::ConfigFile::checkForLine(std::string_view refLine) bool Project::ConfigFile::checkForLine(std::string_view refLine) {
{ if (!m_lockedFile) {
if (!m_lockedFile) LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called");
{
LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__,
"Project::ConfigFile::lockAndRead not yet called");
return false;
}
for (const std::string& line : m_lines)
if (!line.compare(refLine))
return true;
return false; return false;
}
for (const std::string& line : m_lines)
if (!line.compare(refLine))
return true;
return false;
} }
void Project::ConfigFile::unlockAndDiscard() void Project::ConfigFile::unlockAndDiscard() {
{ if (!m_lockedFile) {
if (!m_lockedFile) LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called");
{ return;
LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, }
"Project::ConfigFile::lockAndRead not yet called");
return;
}
m_lines.clear(); m_lines.clear();
fclose(m_lockedFile); fclose(m_lockedFile);
m_lockedFile = NULL; m_lockedFile = NULL;
} }
bool Project::ConfigFile::unlockAndCommit() bool Project::ConfigFile::unlockAndCommit() {
{ if (!m_lockedFile) {
if (!m_lockedFile) LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, "Project::ConfigFile::lockAndRead not yet called");
{ return false;
LogModule.reportSource(logvisor::Fatal, __FILE__, __LINE__, }
"Project::ConfigFile::lockAndRead not yet called");
return false;
}
SystemString newPath = m_filepath + _SYS_STR(".part"); SystemString newPath = m_filepath + _SYS_STR(".part");
FILE* newFile = hecl::Fopen(newPath.c_str(), _SYS_STR("w"), FileLockType::Write); FILE* newFile = hecl::Fopen(newPath.c_str(), _SYS_STR("w"), FileLockType::Write);
bool fail = false; bool fail = false;
for (const std::string& line : m_lines) for (const std::string& line : m_lines) {
{ if (fwrite(line.c_str(), 1, line.size(), newFile) != line.size()) {
if (fwrite(line.c_str(), 1, line.size(), newFile) != line.size()) fail = true;
{ break;
fail = true;
break;
}
if (fwrite("\n", 1, 1, newFile) != 1)
{
fail = true;
break;
}
} }
m_lines.clear(); if (fwrite("\n", 1, 1, newFile) != 1) {
fclose(newFile); fail = true;
fclose(m_lockedFile); break;
m_lockedFile = NULL; }
if (fail) }
{ m_lines.clear();
fclose(newFile);
fclose(m_lockedFile);
m_lockedFile = NULL;
if (fail) {
#if HECL_UCS2 #if HECL_UCS2
_wunlink(newPath.c_str()); _wunlink(newPath.c_str());
#else #else
unlink(newPath.c_str()); unlink(newPath.c_str());
#endif #endif
return false; return false;
} } else {
else
{
#if HECL_UCS2 #if HECL_UCS2
//_wrename(newPath.c_str(), m_filepath.c_str()); //_wrename(newPath.c_str(), m_filepath.c_str());
MoveFileExW(newPath.c_str(), m_filepath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); MoveFileExW(newPath.c_str(), m_filepath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
#else #else
rename(newPath.c_str(), m_filepath.c_str()); rename(newPath.c_str(), m_filepath.c_str());
#endif #endif
return true; return true;
} }
} }
/********************************************** /**********************************************
@ -199,407 +166,333 @@ bool Project::ConfigFile::unlockAndCommit()
**********************************************/ **********************************************/
Project::Project(const ProjectRootPath& rootPath) Project::Project(const ProjectRootPath& rootPath)
: m_rootPath(rootPath), : m_rootPath(rootPath)
m_workRoot(*this, _SYS_STR("")), , m_workRoot(*this, _SYS_STR(""))
m_dotPath(m_workRoot, _SYS_STR(".hecl")), , m_dotPath(m_workRoot, _SYS_STR(".hecl"))
m_cookedRoot(m_dotPath, _SYS_STR("cooked")), , m_cookedRoot(m_dotPath, _SYS_STR("cooked"))
m_specs(*this, _SYS_STR("specs")), , m_specs(*this, _SYS_STR("specs"))
m_paths(*this, _SYS_STR("paths")), , m_paths(*this, _SYS_STR("paths"))
m_groups(*this, _SYS_STR("groups")) , m_groups(*this, _SYS_STR("groups")) {
{ /* Stat for existing project directory (must already exist) */
/* Stat for existing project directory (must already exist) */ Sstat myStat;
Sstat myStat; if (hecl::Stat(m_rootPath.getAbsolutePath().data(), &myStat)) {
if (hecl::Stat(m_rootPath.getAbsolutePath().data(), &myStat)) LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), m_rootPath.getAbsolutePath().data());
{ return;
LogModule.report(logvisor::Error, _SYS_STR("unable to stat %s"), m_rootPath.getAbsolutePath().data()); }
return;
}
if (!S_ISDIR(myStat.st_mode)) if (!S_ISDIR(myStat.st_mode)) {
{ LogModule.report(logvisor::Error, _SYS_STR("provided path must be a directory; '%s' isn't"),
LogModule.report(logvisor::Error, _SYS_STR("provided path must be a directory; '%s' isn't"), m_rootPath.getAbsolutePath().data());
m_rootPath.getAbsolutePath().data()); return;
return; }
}
/* Create project directory structure */ /* Create project directory structure */
m_dotPath.makeDir(); m_dotPath.makeDir();
m_cookedRoot.makeDir(); m_cookedRoot.makeDir();
/* Ensure beacon is valid or created */ /* Ensure beacon is valid or created */
ProjectPath beaconPath(m_dotPath, _SYS_STR("beacon")); ProjectPath beaconPath(m_dotPath, _SYS_STR("beacon"));
FILE* bf = hecl::Fopen(beaconPath.getAbsolutePath().data(), _SYS_STR("a+b")); FILE* bf = hecl::Fopen(beaconPath.getAbsolutePath().data(), _SYS_STR("a+b"));
struct BeaconStruct struct BeaconStruct {
{ hecl::FourCC magic;
hecl::FourCC magic; uint32_t version;
uint32_t version; } beacon;
} beacon;
#define DATA_VERSION 1 #define DATA_VERSION 1
if (fread(&beacon, 1, sizeof(beacon), bf) != sizeof(beacon)) if (fread(&beacon, 1, sizeof(beacon), bf) != sizeof(beacon)) {
{ fseek(bf, 0, SEEK_SET);
fseek(bf, 0, SEEK_SET); beacon.magic = HECLfcc;
beacon.magic = HECLfcc; beacon.version = SBig(DATA_VERSION);
beacon.version = SBig(DATA_VERSION); fwrite(&beacon, 1, sizeof(beacon), bf);
fwrite(&beacon, 1, sizeof(beacon), bf); }
} fclose(bf);
fclose(bf); if (beacon.magic != HECLfcc || SBig(beacon.version) != DATA_VERSION) {
if (beacon.magic != HECLfcc || LogModule.report(logvisor::Fatal, "incompatible project version");
SBig(beacon.version) != DATA_VERSION) return;
{ }
LogModule.report(logvisor::Fatal, "incompatible project version");
return;
}
/* Compile current dataspec */ /* Compile current dataspec */
rescanDataSpecs(); rescanDataSpecs();
m_valid = true; m_valid = true;
} }
const ProjectPath& Project::getProjectCookedPath(const DataSpecEntry& spec) const const ProjectPath& Project::getProjectCookedPath(const DataSpecEntry& spec) const {
{ for (const ProjectDataSpec& sp : m_compiledSpecs)
for (const ProjectDataSpec& sp : m_compiledSpecs) if (&sp.spec == &spec)
if (&sp.spec == &spec) return sp.cookedPath;
return sp.cookedPath; LogModule.report(logvisor::Fatal, "Unable to find spec '%s'", spec.m_name.data());
LogModule.report(logvisor::Fatal, "Unable to find spec '%s'", spec.m_name.data()); return m_cookedRoot;
return m_cookedRoot;
} }
bool Project::addPaths(const std::vector<ProjectPath>& paths) bool Project::addPaths(const std::vector<ProjectPath>& paths) {
{ m_paths.lockAndRead();
m_paths.lockAndRead(); for (const ProjectPath& path : paths)
for (const ProjectPath& path : paths) m_paths.addLine(path.getRelativePathUTF8());
m_paths.addLine(path.getRelativePathUTF8()); return m_paths.unlockAndCommit();
return m_paths.unlockAndCommit();
} }
bool Project::removePaths(const std::vector<ProjectPath>& paths, bool recursive) bool Project::removePaths(const std::vector<ProjectPath>& paths, bool recursive) {
{ std::vector<std::string>& existingPaths = m_paths.lockAndRead();
std::vector<std::string>& existingPaths = m_paths.lockAndRead(); if (recursive) {
if (recursive) for (const ProjectPath& path : paths) {
{ auto recursiveBase = path.getRelativePathUTF8();
for (const ProjectPath& path : paths) for (auto it = existingPaths.begin(); it != existingPaths.end();) {
{ if (!(*it).compare(0, recursiveBase.size(), recursiveBase)) {
auto recursiveBase = path.getRelativePathUTF8(); it = existingPaths.erase(it);
for (auto it = existingPaths.begin(); continue;
it != existingPaths.end();)
{
if (!(*it).compare(0, recursiveBase.size(), recursiveBase))
{
it = existingPaths.erase(it);
continue;
}
++it;
}
} }
++it;
}
} }
else } else
for (const ProjectPath& path : paths) for (const ProjectPath& path : paths)
m_paths.removeLine(path.getRelativePathUTF8()); m_paths.removeLine(path.getRelativePathUTF8());
return m_paths.unlockAndCommit(); return m_paths.unlockAndCommit();
} }
bool Project::addGroup(const hecl::ProjectPath& path) bool Project::addGroup(const hecl::ProjectPath& path) {
{ m_groups.lockAndRead();
m_groups.lockAndRead(); m_groups.addLine(path.getRelativePathUTF8());
m_groups.addLine(path.getRelativePathUTF8()); return m_groups.unlockAndCommit();
return m_groups.unlockAndCommit();
} }
bool Project::removeGroup(const ProjectPath& path) bool Project::removeGroup(const ProjectPath& path) {
{ m_groups.lockAndRead();
m_groups.lockAndRead(); m_groups.removeLine(path.getRelativePathUTF8());
m_groups.removeLine(path.getRelativePathUTF8()); return m_groups.unlockAndCommit();
return m_groups.unlockAndCommit();
} }
void Project::rescanDataSpecs() void Project::rescanDataSpecs() {
{ m_compiledSpecs.clear();
m_compiledSpecs.clear(); m_specs.lockAndRead();
m_specs.lockAndRead(); for (const DataSpecEntry* spec : DATA_SPEC_REGISTRY) {
for (const DataSpecEntry* spec : DATA_SPEC_REGISTRY) hecl::SystemString specStr(spec->m_name);
{ SystemUTF8Conv specUTF8(specStr);
hecl::SystemString specStr(spec->m_name); m_compiledSpecs.push_back({*spec, ProjectPath(m_cookedRoot, hecl::SystemString(spec->m_name) + _SYS_STR(".spec")),
SystemUTF8Conv specUTF8(specStr); m_specs.checkForLine(specUTF8.str())});
m_compiledSpecs.push_back({*spec, ProjectPath(m_cookedRoot, hecl::SystemString(spec->m_name) + _SYS_STR(".spec")), }
m_specs.checkForLine(specUTF8.str())}); m_specs.unlockAndDiscard();
}
m_specs.unlockAndDiscard();
} }
bool Project::enableDataSpecs(const std::vector<SystemString>& specs) bool Project::enableDataSpecs(const std::vector<SystemString>& specs) {
{ m_specs.lockAndRead();
m_specs.lockAndRead(); for (const SystemString& spec : specs) {
for (const SystemString& spec : specs) SystemUTF8Conv specView(spec);
{ m_specs.addLine(specView.str());
SystemUTF8Conv specView(spec); }
m_specs.addLine(specView.str()); bool result = m_specs.unlockAndCommit();
} rescanDataSpecs();
bool result = m_specs.unlockAndCommit(); return result;
rescanDataSpecs();
return result;
} }
bool Project::disableDataSpecs(const std::vector<SystemString>& specs) bool Project::disableDataSpecs(const std::vector<SystemString>& specs) {
{ m_specs.lockAndRead();
m_specs.lockAndRead(); for (const SystemString& spec : specs) {
for (const SystemString& spec : specs) SystemUTF8Conv specView(spec);
{ m_specs.removeLine(specView.str());
SystemUTF8Conv specView(spec); }
m_specs.removeLine(specView.str()); bool result = m_specs.unlockAndCommit();
} rescanDataSpecs();
bool result = m_specs.unlockAndCommit(); return result;
rescanDataSpecs();
return result;
} }
class CookProgress class CookProgress {
{ const hecl::MultiProgressPrinter& m_progPrinter;
const hecl::MultiProgressPrinter& m_progPrinter; const SystemChar* m_dir = nullptr;
const SystemChar* m_dir = nullptr; const SystemChar* m_file = nullptr;
const SystemChar* m_file = nullptr; float m_prog = 0.f;
float m_prog = 0.f;
public: public:
CookProgress(const hecl::MultiProgressPrinter& progPrinter) : m_progPrinter(progPrinter) {} CookProgress(const hecl::MultiProgressPrinter& progPrinter) : m_progPrinter(progPrinter) {}
void changeDir(const SystemChar* dir) {m_dir = dir; m_progPrinter.startNewLine();} void changeDir(const SystemChar* dir) {
void changeFile(const SystemChar* file, float prog) {m_file = file; m_prog = prog;} m_dir = dir;
void reportFile(const DataSpecEntry* specEnt) m_progPrinter.startNewLine();
{ }
SystemString submsg(m_file); void changeFile(const SystemChar* file, float prog) {
submsg += _SYS_STR(" ("); m_file = file;
submsg += specEnt->m_name.data(); m_prog = prog;
submsg += _SYS_STR(')'); }
m_progPrinter.print(m_dir, submsg.c_str(), m_prog); void reportFile(const DataSpecEntry* specEnt) {
} SystemString submsg(m_file);
void reportFile(const DataSpecEntry* specEnt, const SystemChar* extra) submsg += _SYS_STR(" (");
{ submsg += specEnt->m_name.data();
SystemString submsg(m_file); submsg += _SYS_STR(')');
submsg += _SYS_STR(" ("); m_progPrinter.print(m_dir, submsg.c_str(), m_prog);
submsg += specEnt->m_name.data(); }
submsg += _SYS_STR(", "); void reportFile(const DataSpecEntry* specEnt, const SystemChar* extra) {
submsg += extra; SystemString submsg(m_file);
submsg += _SYS_STR(')'); submsg += _SYS_STR(" (");
m_progPrinter.print(m_dir, submsg.c_str(), m_prog); submsg += specEnt->m_name.data();
} submsg += _SYS_STR(", ");
void reportDirComplete() submsg += extra;
{ submsg += _SYS_STR(')');
m_progPrinter.print(m_dir, nullptr, 1.f); m_progPrinter.print(m_dir, submsg.c_str(), m_prog);
} }
void reportDirComplete() { m_progPrinter.print(m_dir, nullptr, 1.f); }
}; };
static void VisitFile(const ProjectPath& path, bool force, bool fast, static void VisitFile(const ProjectPath& path, bool force, bool fast,
std::vector<std::unique_ptr<IDataSpec>>& specInsts, std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress, ClientProcess* cp,
CookProgress& progress, ClientProcess* cp, int cookPass) int cookPass) {
{ for (auto& spec : specInsts) {
for (auto& spec : specInsts) if (spec->canCook(path, hecl::blender::SharedBlenderToken, cookPass)) {
{ if (cp) {
if (spec->canCook(path, hecl::blender::SharedBlenderToken, cookPass)) cp->addCookTransaction(path, force, fast, spec.get());
{ } else {
if (cp) const DataSpecEntry* override =
{ spec->overrideDataSpec(path, spec->getDataSpecEntry(), hecl::blender::SharedBlenderToken);
cp->addCookTransaction(path, force, fast, spec.get()); if (!override)
} continue;
else ProjectPath cooked = path.getCookedPath(*override);
{ if (fast)
const DataSpecEntry* override = spec->overrideDataSpec(path, spec->getDataSpecEntry(), cooked = cooked.getWithExtension(_SYS_STR(".fast"));
hecl::blender::SharedBlenderToken); if (force || cooked.getPathType() == ProjectPath::Type::None || path.getModtime() > cooked.getModtime()) {
if (!override) progress.reportFile(override);
continue; spec->doCook(path, cooked, fast, hecl::blender::SharedBlenderToken,
ProjectPath cooked = path.getCookedPath(*override); [&](const SystemChar* extra) { progress.reportFile(override, extra); });
if (fast)
cooked = cooked.getWithExtension(_SYS_STR(".fast"));
if (force || cooked.getPathType() == ProjectPath::Type::None ||
path.getModtime() > cooked.getModtime())
{
progress.reportFile(override);
spec->doCook(path, cooked, fast, hecl::blender::SharedBlenderToken,
[&](const SystemChar* extra)
{
progress.reportFile(override, extra);
});
}
}
} }
}
} }
}
} }
static void VisitDirectory(const ProjectPath& dir, static void VisitDirectory(const ProjectPath& dir, bool recursive, bool force, bool fast,
bool recursive, bool force, bool fast, std::vector<std::unique_ptr<IDataSpec>>& specInsts, CookProgress& progress,
std::vector<std::unique_ptr<IDataSpec>>& specInsts, ClientProcess* cp, int cookPass) {
CookProgress& progress, ClientProcess* cp, int cookPass) if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.'))
{ return;
if (dir.getLastComponent().size() > 1 && dir.getLastComponent()[0] == _SYS_STR('.'))
return;
if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() && if (hecl::ProjectPath(dir, _SYS_STR("!project.yaml")).isFile() &&
hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) hecl::ProjectPath(dir, _SYS_STR("!pool.yaml")).isFile()) {
{ /* Handle AudioGroup case */
/* Handle AudioGroup case */ VisitFile(dir, force, fast, specInsts, progress, cp, cookPass);
VisitFile(dir, force, fast, specInsts, progress, cp, cookPass); return;
return; }
std::map<SystemString, ProjectPath> children;
dir.getDirChildren(children);
/* Pass 1: child file count */
int childFileCount = 0;
for (auto& child : children)
if (child.second.getPathType() == ProjectPath::Type::File)
++childFileCount;
/* Pass 2: child files */
int progNum = 0;
float progDenom = childFileCount;
progress.changeDir(dir.getLastComponent().data());
for (auto& child : children) {
if (child.second.getPathType() == ProjectPath::Type::File) {
progress.changeFile(child.first.c_str(), progNum++ / progDenom);
VisitFile(child.second, force, fast, specInsts, progress, cp, cookPass);
} }
}
progress.reportDirComplete();
std::map<SystemString, ProjectPath> children; /* Pass 3: child directories */
dir.getDirChildren(children); if (recursive) {
for (auto& child : children) {
/* Pass 1: child file count */ switch (child.second.getPathType()) {
int childFileCount = 0; case ProjectPath::Type::Directory: {
for (auto& child : children) VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp, cookPass);
if (child.second.getPathType() == ProjectPath::Type::File) break;
++childFileCount; }
default:
/* Pass 2: child files */ break;
int progNum = 0; }
float progDenom = childFileCount;
progress.changeDir(dir.getLastComponent().data());
for (auto& child : children)
{
if (child.second.getPathType() == ProjectPath::Type::File)
{
progress.changeFile(child.first.c_str(), progNum++/progDenom);
VisitFile(child.second, force, fast, specInsts, progress, cp, cookPass);
}
}
progress.reportDirComplete();
/* Pass 3: child directories */
if (recursive)
{
for (auto& child : children)
{
switch (child.second.getPathType())
{
case ProjectPath::Type::Directory:
{
VisitDirectory(child.second, recursive, force, fast, specInsts, progress, cp, cookPass);
break;
}
default: break;
}
}
} }
}
} }
bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool Project::cookPath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool recursive, bool force,
bool recursive, bool force, bool fast, const DataSpecEntry* spec, bool fast, const DataSpecEntry* spec, ClientProcess* cp, int cookPass) {
ClientProcess* cp, int cookPass) /* Construct DataSpec instances for cooking */
{ if (spec) {
/* Construct DataSpec instances for cooking */ if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) {
if (spec) m_cookSpecs.clear();
{ if (spec->m_factory)
if (m_cookSpecs.size() != 1 || m_cookSpecs[0]->getDataSpecEntry() != spec) m_cookSpecs.push_back(spec->m_factory(*this, DataSpecTool::Cook));
{ }
m_cookSpecs.clear(); } else if (m_cookSpecs.empty()) {
if (spec->m_factory) m_cookSpecs.reserve(m_compiledSpecs.size());
m_cookSpecs.push_back(spec->m_factory(*this, DataSpecTool::Cook)); for (const ProjectDataSpec& spec : m_compiledSpecs)
if (spec.active && spec.spec.m_factory)
m_cookSpecs.push_back(spec.spec.m_factory(*this, DataSpecTool::Cook));
}
/* Iterate complete directory/file/glob list */
CookProgress cookProg(progress);
switch (path.getPathType()) {
case ProjectPath::Type::File:
case ProjectPath::Type::Glob: {
cookProg.changeFile(path.getLastComponent().data(), 0.f);
VisitFile(path, force, fast, m_cookSpecs, cookProg, cp, cookPass);
break;
}
case ProjectPath::Type::Directory: {
VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp, cookPass);
break;
}
default:
break;
}
return true;
}
bool Project::packagePath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, bool fast,
const DataSpecEntry* spec, ClientProcess* cp) {
/* Construct DataSpec instance for packaging */
const DataSpecEntry* specEntry = nullptr;
if (spec) {
if (spec->m_factory)
specEntry = spec;
} else {
bool foundPC = false;
for (const ProjectDataSpec& spec : m_compiledSpecs) {
if (spec.active && spec.spec.m_factory) {
if (hecl::StringUtils::EndsWith(spec.spec.m_name, _SYS_STR("-PC"))) {
foundPC = true;
specEntry = &spec.spec;
} else if (!foundPC) {
specEntry = &spec.spec;
} }
}
} }
else if (m_cookSpecs.empty()) }
{
m_cookSpecs.reserve(m_compiledSpecs.size());
for (const ProjectDataSpec& spec : m_compiledSpecs)
if (spec.active && spec.spec.m_factory)
m_cookSpecs.push_back(spec.spec.m_factory(*this, DataSpecTool::Cook));
}
/* Iterate complete directory/file/glob list */ if (!specEntry)
CookProgress cookProg(progress); LogModule.report(logvisor::Fatal, "No matching DataSpec");
switch (path.getPathType())
{
case ProjectPath::Type::File:
case ProjectPath::Type::Glob:
{
cookProg.changeFile(path.getLastComponent().data(), 0.f);
VisitFile(path, force, fast, m_cookSpecs, cookProg, cp, cookPass);
break;
}
case ProjectPath::Type::Directory:
{
VisitDirectory(path, recursive, force, fast, m_cookSpecs, cookProg, cp, cookPass);
break;
}
default: break;
}
if (!m_lastPackageSpec || m_lastPackageSpec->getDataSpecEntry() != specEntry)
m_lastPackageSpec = specEntry->m_factory(*this, DataSpecTool::Package);
if (m_lastPackageSpec->canPackage(path)) {
m_lastPackageSpec->doPackage(path, specEntry, fast, hecl::blender::SharedBlenderToken, progress, cp);
return true; return true;
}
return false;
} }
bool Project::packagePath(const ProjectPath& path, const hecl::MultiProgressPrinter& progress, void Project::interruptCook() {
bool fast, const DataSpecEntry* spec, ClientProcess* cp) if (m_lastPackageSpec)
{ m_lastPackageSpec->interruptCook();
/* Construct DataSpec instance for packaging */
const DataSpecEntry* specEntry = nullptr;
if (spec)
{
if (spec->m_factory)
specEntry = spec;
}
else
{
bool foundPC = false;
for (const ProjectDataSpec& spec : m_compiledSpecs)
{
if (spec.active && spec.spec.m_factory)
{
if (hecl::StringUtils::EndsWith(spec.spec.m_name, _SYS_STR("-PC")))
{
foundPC = true;
specEntry = &spec.spec;
}
else if (!foundPC)
{
specEntry = &spec.spec;
}
}
}
}
if (!specEntry)
LogModule.report(logvisor::Fatal, "No matching DataSpec");
if (!m_lastPackageSpec || m_lastPackageSpec->getDataSpecEntry() != specEntry)
m_lastPackageSpec = specEntry->m_factory(*this, DataSpecTool::Package);
if (m_lastPackageSpec->canPackage(path))
{
m_lastPackageSpec->doPackage(path, specEntry, fast, hecl::blender::SharedBlenderToken, progress, cp);
return true;
}
return false;
} }
void Project::interruptCook() bool Project::cleanPath(const ProjectPath& path, bool recursive) { return false; }
{
if (m_lastPackageSpec) PackageDepsgraph Project::buildPackageDepsgraph(const ProjectPath& path) { return PackageDepsgraph(); }
m_lastPackageSpec->interruptCook();
void Project::addBridgePathToCache(uint64_t id, const ProjectPath& path) { m_bridgePathCache[id] = path; }
void Project::clearBridgePathCache() { m_bridgePathCache.clear(); }
const ProjectPath* Project::lookupBridgePath(uint64_t id) const {
auto search = m_bridgePathCache.find(id);
if (search == m_bridgePathCache.cend())
return nullptr;
return &search->second;
} }
bool Project::cleanPath(const ProjectPath& path, bool recursive) } // namespace hecl::Database
{
return false;
}
PackageDepsgraph Project::buildPackageDepsgraph(const ProjectPath& path)
{
return PackageDepsgraph();
}
void Project::addBridgePathToCache(uint64_t id, const ProjectPath& path)
{
m_bridgePathCache[id] = path;
}
void Project::clearBridgePathCache()
{
m_bridgePathCache.clear();
}
const ProjectPath* Project::lookupBridgePath(uint64_t id) const
{
auto search = m_bridgePathCache.find(id);
if (search == m_bridgePathCache.cend())
return nullptr;
return &search->second;
}
}

View File

@ -2,376 +2,320 @@
#include "hecl/Database.hpp" #include "hecl/Database.hpp"
#include <regex> #include <regex>
namespace hecl namespace hecl {
{ static const SystemRegex regPATHCOMP(_SYS_STR("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript | SystemRegex::optimize);
static const SystemRegex regPATHCOMP(_SYS_STR("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript|SystemRegex::optimize); static const SystemRegex regDRIVELETTER(_SYS_STR("^([^/]*)/"), SystemRegex::ECMAScript | SystemRegex::optimize);
static const SystemRegex regDRIVELETTER(_SYS_STR("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize);
static SystemString CanonRelPath(SystemStringView path) static SystemString CanonRelPath(SystemStringView path) {
{ /* Tokenize Path */
/* Tokenize Path */ std::vector<SystemString> comps;
std::vector<SystemString> comps; hecl::SystemRegexMatch matches;
hecl::SystemRegexMatch matches; SystemString in(path);
SystemString in(path); SanitizePath(in);
SanitizePath(in); for (; std::regex_search(in, matches, regPATHCOMP); in = matches.suffix().str()) {
for (; std::regex_search(in, matches, regPATHCOMP) ; in = matches.suffix().str()) hecl::SystemRegexMatch::const_reference match = matches[1];
{ if (!match.compare(_SYS_STR(".")))
hecl::SystemRegexMatch::const_reference match = matches[1]; continue;
if (!match.compare(_SYS_STR("."))) else if (!match.compare(_SYS_STR(".."))) {
continue; if (comps.empty()) {
else if (!match.compare(_SYS_STR(".."))) /* Unable to resolve outside project */
{ LogModule.report(logvisor::Fatal, _SYS_STR("Unable to resolve outside project root in %s"), path.data());
if (comps.empty()) return _SYS_STR(".");
{ }
/* Unable to resolve outside project */ comps.pop_back();
LogModule.report(logvisor::Fatal, _SYS_STR("Unable to resolve outside project root in %s"), path.data()); continue;
return _SYS_STR(".");
}
comps.pop_back();
continue;
}
comps.push_back(match.str());
} }
comps.push_back(match.str());
}
/* Emit relative path */ /* Emit relative path */
if (comps.size()) if (comps.size()) {
{ auto it = comps.begin();
auto it = comps.begin(); SystemString retval = *it;
SystemString retval = *it; for (++it; it != comps.end(); ++it) {
for (++it ; it != comps.end() ; ++it) if ((*it).size()) {
{ retval += _SYS_STR('/');
if ((*it).size()) retval += *it;
{ }
retval += _SYS_STR('/');
retval += *it;
}
}
return retval;
} }
return _SYS_STR("."); return retval;
}
return _SYS_STR(".");
} }
static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot) static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot) {
{ /* Absolute paths not allowed; attempt to make project-relative */
/* Absolute paths not allowed; attempt to make project-relative */ if (IsAbsolute(path))
if (IsAbsolute(path)) return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path)); return CanonRelPath(path);
return CanonRelPath(path);
} }
void ProjectPath::assign(Database::Project& project, SystemStringView path) void ProjectPath::assign(Database::Project& project, SystemStringView path) {
{ m_proj = &project;
m_proj = &project;
SystemString usePath; SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|')); size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) if (pipeFind != SystemString::npos) {
{ m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend()); usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
usePath.assign(path.cbegin(), path.cbegin() + pipeFind); } else
} usePath = path;
else
usePath = path;
m_relPath = CanonRelPath(usePath, project.getProjectRootPath()); m_relPath = CanonRelPath(usePath, project.getProjectRootPath());
m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath; m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath); SanitizePath(m_relPath);
SanitizePath(m_absPath); SanitizePath(m_absPath);
ComputeHash(); ComputeHash();
} }
#if HECL_UCS2 #if HECL_UCS2
void ProjectPath::assign(Database::Project& project, std::string_view path) void ProjectPath::assign(Database::Project& project, std::string_view path) {
{ std::wstring wpath = UTF8ToWide(path);
std::wstring wpath = UTF8ToWide(path); assign(project, wpath);
assign(project, wpath);
} }
#endif #endif
void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path) void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path) {
{ m_proj = parentPath.m_proj;
m_proj = parentPath.m_proj;
SystemString usePath; SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|')); size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) if (pipeFind != SystemString::npos) {
{ m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend()); usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
usePath.assign(path.cbegin(), path.cbegin() + pipeFind); } else
} usePath = path;
else
usePath = path;
m_relPath = CanonRelPath(parentPath.m_relPath + _SYS_STR('/') + usePath); m_relPath = CanonRelPath(parentPath.m_relPath + _SYS_STR('/') + usePath);
m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath; m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath); SanitizePath(m_relPath);
SanitizePath(m_absPath); SanitizePath(m_absPath);
ComputeHash(); ComputeHash();
} }
#if HECL_UCS2 #if HECL_UCS2
void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path) void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path) {
{ std::wstring wpath = UTF8ToWide(path);
std::wstring wpath = UTF8ToWide(path); assign(parentPath, wpath);
assign(parentPath, wpath);
} }
#endif #endif
ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const {
{ ProjectPath pp(*this);
ProjectPath pp(*this); if (replace) {
if (replace) auto relIt = pp.m_relPath.end();
{ if (relIt != pp.m_relPath.begin())
auto relIt = pp.m_relPath.end(); --relIt;
if (relIt != pp.m_relPath.begin()) auto absIt = pp.m_absPath.end();
--relIt; if (absIt != pp.m_absPath.begin())
auto absIt = pp.m_absPath.end(); --absIt;
if (absIt != pp.m_absPath.begin()) while (relIt != pp.m_relPath.begin() && *relIt != _SYS_STR('.') && *relIt != _SYS_STR('/')) {
--absIt; --relIt;
while (relIt != pp.m_relPath.begin() && *relIt != _SYS_STR('.') && *relIt != _SYS_STR('/')) --absIt;
{
--relIt;
--absIt;
}
if (*relIt == _SYS_STR('.') && relIt != pp.m_relPath.begin())
{
pp.m_relPath.resize(relIt - pp.m_relPath.begin());
pp.m_absPath.resize(absIt - pp.m_absPath.begin());
}
} }
if (ext) if (*relIt == _SYS_STR('.') && relIt != pp.m_relPath.begin()) {
{ pp.m_relPath.resize(relIt - pp.m_relPath.begin());
pp.m_relPath += ext; pp.m_absPath.resize(absIt - pp.m_absPath.begin());
pp.m_absPath += ext;
} }
}
if (ext) {
pp.m_relPath += ext;
pp.m_absPath += ext;
}
pp.ComputeHash(); pp.ComputeHash();
return pp; return pp;
} }
ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const {
{ ProjectPath woExt = getWithExtension(nullptr, true);
ProjectPath woExt = getWithExtension(nullptr, true); ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
if (getAuxInfo().size()) if (getAuxInfo().size())
return ret.getWithExtension((SystemString(_SYS_STR(".")) + getAuxInfo().data()).c_str()); return ret.getWithExtension((SystemString(_SYS_STR(".")) + getAuxInfo().data()).c_str());
else else
return ret; return ret;
} }
ProjectPath::Type ProjectPath::getPathType() const ProjectPath::Type ProjectPath::getPathType() const {
{ if (m_absPath.find(_SYS_STR('*')) != SystemString::npos)
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos) return Type::Glob;
return Type::Glob; Sstat theStat;
Sstat theStat; if (hecl::Stat(m_absPath.c_str(), &theStat))
if (hecl::Stat(m_absPath.c_str(), &theStat))
return Type::None;
if (S_ISDIR(theStat.st_mode))
return Type::Directory;
if (S_ISREG(theStat.st_mode))
return Type::File;
return Type::None; return Type::None;
if (S_ISDIR(theStat.st_mode))
return Type::Directory;
if (S_ISREG(theStat.st_mode))
return Type::File;
return Type::None;
} }
Time ProjectPath::getModtime() const Time ProjectPath::getModtime() const {
{ Sstat theStat;
time_t latestTime = 0;
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos) {
std::vector<ProjectPath> globResults;
getGlobResults(globResults);
for (ProjectPath& path : globResults) {
if (!hecl::Stat(path.getAbsolutePath().data(), &theStat)) {
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
if (!hecl::Stat(m_absPath.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode)) {
return Time(theStat.st_mtime);
} else if (S_ISDIR(theStat.st_mode)) {
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de) {
if (!hecl::Stat(ent.m_path.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
}
LogModule.report(logvisor::Fatal, _SYS_STR("invalid path type for computing modtime in '%s'"), m_absPath.c_str());
return Time();
}
static void _recursiveGlob(Database::Project& proj, std::vector<ProjectPath>& outPaths, const SystemString& remPath,
const SystemString& itStr, bool needSlash) {
SystemRegexMatch matches;
if (!std::regex_search(remPath, matches, regPATHCOMP))
return;
const SystemString& comp = matches[1];
if (comp.find(_SYS_STR('*')) == SystemString::npos) {
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += _SYS_STR('/');
nextItStr += comp;
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
return;
if (S_ISDIR(theStat.st_mode))
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
return;
}
/* Compile component into regex */
SystemRegex regComp(comp, SystemRegex::ECMAScript);
hecl::DirectoryEnumerator de(itStr, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de) {
if (std::regex_match(ent.m_name, regComp)) {
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += '/';
nextItStr += ent.m_name;
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
continue;
if (ent.m_isDir)
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
}
}
}
void ProjectPath::getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const {
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de)
outPaths[ent.m_name] = ProjectPath(*this, ent.m_name);
}
hecl::DirectoryEnumerator ProjectPath::enumerateDir() const {
return hecl::DirectoryEnumerator(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
}
void ProjectPath::getGlobResults(std::vector<ProjectPath>& outPaths) const {
auto rootPath = m_proj->getProjectRootPath().getAbsolutePath();
_recursiveGlob(*m_proj, outPaths, m_relPath, rootPath.data(), rootPath.back() != _SYS_STR('/'));
}
ProjectRootPath SearchForProject(SystemStringView path) {
ProjectRootPath testRoot(path);
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
while (begin != end) {
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
Sstat theStat; Sstat theStat;
time_t latestTime = 0; if (!hecl::Stat(testIndexPath.c_str(), &theStat)) {
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos) if (S_ISREG(theStat.st_mode)) {
{ FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb"));
std::vector<ProjectPath> globResults; if (!fp)
getGlobResults(globResults); continue;
for (ProjectPath& path : globResults) char magic[4];
{ size_t readSize = fread(magic, 1, 4, fp);
if (!hecl::Stat(path.getAbsolutePath().data(), &theStat)) fclose(fp);
{ if (readSize != 4)
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime) continue;
latestTime = theStat.st_mtime; static const hecl::FourCC hecl("HECL");
} if (hecl::FourCC(magic) != hecl)
} continue;
return Time(latestTime); return ProjectRootPath(testPath);
} }
if (!hecl::Stat(m_absPath.c_str(), &theStat))
{
if (S_ISREG(theStat.st_mode))
{
return Time(theStat.st_mtime);
}
else if (S_ISDIR(theStat.st_mode))
{
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de)
{
if (!hecl::Stat(ent.m_path.c_str(), &theStat))
{
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
}
LogModule.report(logvisor::Fatal, _SYS_STR("invalid path type for computing modtime in '%s'"), m_absPath.c_str());
return Time();
}
static void _recursiveGlob(Database::Project& proj,
std::vector<ProjectPath>& outPaths,
const SystemString& remPath,
const SystemString& itStr,
bool needSlash)
{
SystemRegexMatch matches;
if (!std::regex_search(remPath, matches, regPATHCOMP))
return;
const SystemString& comp = matches[1];
if (comp.find(_SYS_STR('*')) == SystemString::npos)
{
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += _SYS_STR('/');
nextItStr += comp;
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
return;
if (S_ISDIR(theStat.st_mode))
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
return;
} }
/* Compile component into regex */ while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\'))
SystemRegex regComp(comp, SystemRegex::ECMAScript); --end;
if (begin != end)
--end;
}
return ProjectRootPath();
}
hecl::DirectoryEnumerator de(itStr, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut) {
false, false, true); ProjectRootPath testRoot(path);
for (const hecl::DirectoryEnumerator::Entry& ent : de) auto begin = testRoot.getAbsolutePath().begin();
{ auto end = testRoot.getAbsolutePath().end();
if (std::regex_match(ent.m_name, regComp)) while (begin != end) {
{ SystemString testPath(begin, end);
SystemString nextItStr = itStr; SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
if (needSlash) Sstat theStat;
nextItStr += '/'; if (!hecl::Stat(testIndexPath.c_str(), &theStat)) {
nextItStr += ent.m_name; if (S_ISREG(theStat.st_mode)) {
FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb"));
hecl::Sstat theStat; if (!fp)
if (Stat(nextItStr.c_str(), &theStat)) continue;
continue; char magic[4];
size_t readSize = fread(magic, 1, 4, fp);
if (ent.m_isDir) fclose(fp);
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true); if (readSize != 4)
else continue;
outPaths.emplace_back(proj, nextItStr); if (hecl::FourCC(magic) != FOURCC('HECL'))
} continue;
ProjectRootPath newRootPath = ProjectRootPath(testPath);
auto origEnd = testRoot.getAbsolutePath().end();
while (end != origEnd && *end != _SYS_STR('/') && *end != _SYS_STR('\\'))
++end;
if (end != origEnd && (*end == _SYS_STR('/') || *end == _SYS_STR('\\')))
++end;
subpathOut.assign(end, origEnd);
return newRootPath;
}
} }
while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
} }
void ProjectPath::getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const } // namespace hecl
{
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de)
outPaths[ent.m_name] = ProjectPath(*this, ent.m_name);
}
hecl::DirectoryEnumerator ProjectPath::enumerateDir() const
{
return hecl::DirectoryEnumerator(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
}
void ProjectPath::getGlobResults(std::vector<ProjectPath>& outPaths) const
{
auto rootPath = m_proj->getProjectRootPath().getAbsolutePath();
_recursiveGlob(*m_proj, outPaths, m_relPath, rootPath.data(), rootPath.back() != _SYS_STR('/'));
}
ProjectRootPath SearchForProject(SystemStringView path)
{
ProjectRootPath testRoot(path);
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
while (begin != end)
{
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
Sstat theStat;
if (!hecl::Stat(testIndexPath.c_str(), &theStat))
{
if (S_ISREG(theStat.st_mode))
{
FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb"));
if (!fp)
continue;
char magic[4];
size_t readSize = fread(magic, 1, 4, fp);
fclose(fp);
if (readSize != 4)
continue;
static const hecl::FourCC hecl("HECL");
if (hecl::FourCC(magic) != hecl)
continue;
return ProjectRootPath(testPath);
}
}
while (begin != end && *(end-1) != _SYS_STR('/') && *(end-1) != _SYS_STR('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
}
ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut)
{
ProjectRootPath testRoot(path);
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
while (begin != end)
{
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
Sstat theStat;
if (!hecl::Stat(testIndexPath.c_str(), &theStat))
{
if (S_ISREG(theStat.st_mode))
{
FILE* fp = hecl::Fopen(testIndexPath.c_str(), _SYS_STR("rb"));
if (!fp)
continue;
char magic[4];
size_t readSize = fread(magic, 1, 4, fp);
fclose(fp);
if (readSize != 4)
continue;
if (hecl::FourCC(magic) != FOURCC('HECL'))
continue;
ProjectRootPath newRootPath = ProjectRootPath(testPath);
auto origEnd = testRoot.getAbsolutePath().end();
while (end != origEnd && *end != _SYS_STR('/') && *end != _SYS_STR('\\'))
++end;
if (end != origEnd && (*end == _SYS_STR('/') || *end == _SYS_STR('\\')))
++end;
subpathOut.assign(end, origEnd);
return newRootPath;
}
}
while (begin != end && *(end-1) != _SYS_STR('/') && *(end-1) != _SYS_STR('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
}
}

View File

@ -8,46 +8,43 @@
using namespace Windows::Storage; using namespace Windows::Storage;
#endif #endif
namespace hecl::Runtime namespace hecl::Runtime {
{
static logvisor::Module Log("FileStoreManager"); static logvisor::Module Log("FileStoreManager");
FileStoreManager::FileStoreManager(SystemStringView domain) FileStoreManager::FileStoreManager(SystemStringView domain) : m_domain(domain) {
: m_domain(domain)
{
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
WCHAR home[MAX_PATH]; WCHAR home[MAX_PATH];
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, home))) if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, home)))
Log.report(logvisor::Fatal, _SYS_STR("unable to locate profile for file store")); Log.report(logvisor::Fatal, _SYS_STR("unable to locate profile for file store"));
SystemString path(home); SystemString path(home);
#else #else
StorageFolder^ cacheFolder = ApplicationData::Current->LocalCacheFolder; StorageFolder ^ cacheFolder = ApplicationData::Current->LocalCacheFolder;
SystemString path(cacheFolder->Path->Data()); SystemString path(cacheFolder->Path->Data());
#endif #endif
path += _SYS_STR("/.heclrun"); path += _SYS_STR("/.heclrun");
hecl::MakeDir(path.c_str()); hecl::MakeDir(path.c_str());
path += _SYS_STR('/'); path += _SYS_STR('/');
path += domain.data(); path += domain.data();
hecl::MakeDir(path.c_str()); hecl::MakeDir(path.c_str());
m_storeRoot = path; m_storeRoot = path;
#else #else
const char* home = getenv("HOME"); const char* home = getenv("HOME");
if (!home) if (!home)
Log.report(logvisor::Fatal, "unable to locate $HOME for file store"); Log.report(logvisor::Fatal, "unable to locate $HOME for file store");
std::string path(home); std::string path(home);
path += "/.heclrun"; path += "/.heclrun";
if (mkdir(path.c_str(), 0755) && errno != EEXIST) if (mkdir(path.c_str(), 0755) && errno != EEXIST)
Log.report(logvisor::Fatal, "unable to mkdir at %s", path.c_str()); Log.report(logvisor::Fatal, "unable to mkdir at %s", path.c_str());
path += '/'; path += '/';
path += domain.data(); path += domain.data();
if (mkdir(path.c_str(), 0755) && errno != EEXIST) if (mkdir(path.c_str(), 0755) && errno != EEXIST)
Log.report(logvisor::Fatal, "unable to mkdir at %s", path.c_str()); Log.report(logvisor::Fatal, "unable to mkdir at %s", path.c_str());
m_storeRoot = path; m_storeRoot = path;
#endif #endif
} }
} } // namespace hecl::Runtime

View File

@ -2,50 +2,44 @@
#include "hecl/Runtime.hpp" #include "hecl/Runtime.hpp"
#include <athena/MemoryReader.hpp> #include <athena/MemoryReader.hpp>
namespace hecl::Runtime namespace hecl::Runtime {
{
static logvisor::Module HMDL_Log("HMDL"); static logvisor::Module HMDL_Log("HMDL");
HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx, HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo) {
const void* metaData, const void* vbo, const void* ibo) HMDLMeta meta;
{ {
HMDLMeta meta; athena::io::MemoryReader r((atUint8*)metaData, HECL_HMDL_META_SZ);
{ meta.read(r);
athena::io::MemoryReader r((atUint8*)metaData, HECL_HMDL_META_SZ); }
meta.read(r); if (meta.magic != 'TACO')
} HMDL_Log.report(logvisor::Fatal, "invalid HMDL magic");
if (meta.magic != 'TACO')
HMDL_Log.report(logvisor::Fatal, "invalid HMDL magic");
m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount); m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount);
m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount); m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount);
size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount; size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount;
m_vtxFmtData.reset(new boo::VertexElementDescriptor[elemCount]); m_vtxFmtData.reset(new boo::VertexElementDescriptor[elemCount]);
m_vtxFmtData[0].semantic = boo::VertexSemantic::Position3; m_vtxFmtData[0].semantic = boo::VertexSemantic::Position3;
m_vtxFmtData[1].semantic = boo::VertexSemantic::Normal3; m_vtxFmtData[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2; size_t e = 2;
for (size_t i=0 ; i<meta.colorCount ; ++i, ++e) for (size_t i = 0; i < meta.colorCount; ++i, ++e) {
{ m_vtxFmtData[e].semantic = boo::VertexSemantic::ColorUNorm;
m_vtxFmtData[e].semantic = boo::VertexSemantic::ColorUNorm; m_vtxFmtData[e].semanticIdx = i;
m_vtxFmtData[e].semanticIdx = i; }
}
for (size_t i=0 ; i<meta.uvCount ; ++i, ++e) for (size_t i = 0; i < meta.uvCount; ++i, ++e) {
{ m_vtxFmtData[e].semantic = boo::VertexSemantic::UV2;
m_vtxFmtData[e].semantic = boo::VertexSemantic::UV2; m_vtxFmtData[e].semanticIdx = i;
m_vtxFmtData[e].semanticIdx = i; }
}
for (size_t i=0 ; i<meta.weightCount ; ++i, ++e) for (size_t i = 0; i < meta.weightCount; ++i, ++e) {
{ m_vtxFmtData[e].semantic = boo::VertexSemantic::Weight;
m_vtxFmtData[e].semantic = boo::VertexSemantic::Weight; m_vtxFmtData[e].semanticIdx = i;
m_vtxFmtData[e].semanticIdx = i; }
}
m_vtxFmt = boo::VertexFormatInfo(elemCount, m_vtxFmtData.get()); m_vtxFmt = boo::VertexFormatInfo(elemCount, m_vtxFmtData.get());
} }
} } // namespace hecl::Runtime

View File

@ -7,106 +7,98 @@
#define PATH_SEP '/' #define PATH_SEP '/'
#endif #endif
namespace hecl namespace hecl {
{
/* Used to extract alternate steam install directories from libraryfolders.vdf */ /* Used to extract alternate steam install directories from libraryfolders.vdf */
static const std::regex regSteamPath(R"(^\s+\"[0-9]+\"\s+\"(.*)\")", static const std::regex regSteamPath(R"(^\s+\"[0-9]+\"\s+\"(.*)\")", std::regex::ECMAScript | std::regex::optimize);
std::regex::ECMAScript|std::regex::optimize);
hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name) hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name) {
{ hecl::SystemString steamInstallDir;
hecl::SystemString steamInstallDir; hecl::Sstat theStat;
hecl::Sstat theStat;
#ifdef WIN32 #ifdef WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
HKEY hkey; HKEY hkey;
hecl::SystemChar _steamInstallDir[MAX_PATH] = {0}; hecl::SystemChar _steamInstallDir[MAX_PATH] = {0};
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE, &hkey) !=
0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) ERROR_SUCCESS) {
{ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY,
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), &hkey) != ERROR_SUCCESS)
0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hkey) != ERROR_SUCCESS) return {};
return {}; }
}
DWORD size = MAX_PATH; DWORD size = MAX_PATH;
if (RegQueryValueEx(hkey, _SYS_STR("InstallPath"), nullptr, nullptr, if (RegQueryValueEx(hkey, _SYS_STR("InstallPath"), nullptr, nullptr, (LPBYTE)_steamInstallDir, &size) ==
(LPBYTE)_steamInstallDir, &size) == ERROR_SUCCESS) ERROR_SUCCESS)
steamInstallDir = _steamInstallDir; steamInstallDir = _steamInstallDir;
RegCloseKey(hkey); RegCloseKey(hkey);
if (steamInstallDir.empty()) if (steamInstallDir.empty())
return {};
#else
return {}; return {};
#else
return {};
#endif #endif
#elif defined(__APPLE__) #elif defined(__APPLE__)
steamInstallDir = getenv("HOME"); steamInstallDir = getenv("HOME");
steamInstallDir += "/Library/Application Support/Steam"; steamInstallDir += "/Library/Application Support/Steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
return {}; return {};
#else #else
steamInstallDir = getenv("HOME");
steamInstallDir += "/.local/share/Steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
steamInstallDir = getenv("HOME"); steamInstallDir = getenv("HOME");
steamInstallDir += "/.local/share/Steam"; steamInstallDir += "/.steam/steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
{ return {};
steamInstallDir = getenv("HOME"); }
steamInstallDir += "/.steam/steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
return {};
}
#endif #endif
hecl::SystemString appPath = hecl::SystemString(_SYS_STR("common")) + PATH_SEP + name; hecl::SystemString appPath = hecl::SystemString(_SYS_STR("common")) + PATH_SEP + name;
/* Try main steam install directory first */ /* Try main steam install directory first */
hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps"); hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps");
hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath; hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath;
if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode)) if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode))
return mainAppPath; return mainAppPath;
/* Iterate alternate steam install dirs */
hecl::SystemString libraryFoldersVdfPath = steamAppsMain + PATH_SEP + _SYS_STR("libraryfolders.vdf");
FILE* fp = hecl::Fopen(libraryFoldersVdfPath.c_str(), _SYS_STR("r"));
if (!fp)
return {};
hecl::FSeek(fp, 0, SEEK_END);
int64_t fileLen = hecl::FTell(fp);
if (fileLen <= 0)
{
fclose(fp);
return {};
}
hecl::FSeek(fp, 0, SEEK_SET);
std::string fileBuf;
fileBuf.resize(fileLen);
if (fread(&fileBuf[0], 1, fileLen, fp) != fileLen)
{
fclose(fp);
return {};
}
fclose(fp);
std::smatch dirMatch;
auto begin = fileBuf.cbegin();
auto end = fileBuf.cend();
while (std::regex_search(begin, end, dirMatch, regSteamPath))
{
std::string match = dirMatch[1].str();
hecl::SystemStringConv otherInstallDir(match);
hecl::SystemString otherAppPath = hecl::SystemString(otherInstallDir.sys_str()) + PATH_SEP +
_SYS_STR("steamapps") + PATH_SEP + appPath;
if (!hecl::Stat(otherAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode))
return otherAppPath;
begin = dirMatch.suffix().first;
}
/* Iterate alternate steam install dirs */
hecl::SystemString libraryFoldersVdfPath = steamAppsMain + PATH_SEP + _SYS_STR("libraryfolders.vdf");
FILE* fp = hecl::Fopen(libraryFoldersVdfPath.c_str(), _SYS_STR("r"));
if (!fp)
return {}; return {};
hecl::FSeek(fp, 0, SEEK_END);
int64_t fileLen = hecl::FTell(fp);
if (fileLen <= 0) {
fclose(fp);
return {};
}
hecl::FSeek(fp, 0, SEEK_SET);
std::string fileBuf;
fileBuf.resize(fileLen);
if (fread(&fileBuf[0], 1, fileLen, fp) != fileLen) {
fclose(fp);
return {};
}
fclose(fp);
std::smatch dirMatch;
auto begin = fileBuf.cbegin();
auto end = fileBuf.cend();
while (std::regex_search(begin, end, dirMatch, regSteamPath)) {
std::string match = dirMatch[1].str();
hecl::SystemStringConv otherInstallDir(match);
hecl::SystemString otherAppPath =
hecl::SystemString(otherInstallDir.sys_str()) + PATH_SEP + _SYS_STR("steamapps") + PATH_SEP + appPath;
if (!hecl::Stat(otherAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode))
return otherAppPath;
begin = dirMatch.suffix().first;
}
return {};
} }
} } // namespace hecl

View File

@ -1,84 +1,71 @@
#include <utf8proc.h> #include <utf8proc.h>
#include "hecl/hecl.hpp" #include "hecl/hecl.hpp"
namespace hecl namespace hecl {
{
static logvisor::Module Log("hecl-wsconv"); static logvisor::Module Log("hecl-wsconv");
std::string WideToUTF8(std::wstring_view src) std::string WideToUTF8(std::wstring_view src) {
{ std::string retval;
std::string retval; retval.reserve(src.length());
retval.reserve(src.length()); for (wchar_t ch : src) {
for (wchar_t ch : src) utf8proc_uint8_t mb[4];
{ utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
utf8proc_uint8_t mb[4]; if (c < 0) {
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb); Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
if (c < 0) return retval;
{
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
retval.append(reinterpret_cast<char*>(mb), c);
} }
return retval; retval.append(reinterpret_cast<char*>(mb), c);
}
return retval;
} }
std::string Char16ToUTF8(std::u16string_view src) std::string Char16ToUTF8(std::u16string_view src) {
{ std::string retval;
std::string retval; retval.reserve(src.length());
retval.reserve(src.length()); for (char16_t ch : src) {
for (char16_t ch : src) utf8proc_uint8_t mb[4];
{ utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
utf8proc_uint8_t mb[4]; if (c < 0) {
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb); Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
if (c < 0) return retval;
{
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
retval.append(reinterpret_cast<char*>(mb), c);
} }
return retval; retval.append(reinterpret_cast<char*>(mb), c);
}
return retval;
} }
std::wstring UTF8ToWide(std::string_view src) std::wstring UTF8ToWide(std::string_view src) {
{ std::wstring retval;
std::wstring retval; retval.reserve(src.length());
retval.reserve(src.length()); const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data()); while (*buf) {
while (*buf) utf8proc_int32_t wc;
{ utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
utf8proc_int32_t wc; if (len < 0) {
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc); Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
if (len < 0) return retval;
{
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
buf += len;
retval += wchar_t(wc);
} }
return retval; buf += len;
retval += wchar_t(wc);
}
return retval;
} }
std::u16string UTF8ToChar16(std::string_view src) std::u16string UTF8ToChar16(std::string_view src) {
{ std::u16string retval;
std::u16string retval; retval.reserve(src.length());
retval.reserve(src.length()); const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data()); while (*buf) {
while (*buf) utf8proc_int32_t wc;
{ utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
utf8proc_int32_t wc; if (len < 0) {
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc); Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
if (len < 0) return retval;
{
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
buf += len;
retval += char16_t(wc);
} }
return retval; buf += len;
retval += char16_t(wc);
}
return retval;
} }
} } // namespace hecl

File diff suppressed because it is too large Load Diff

View File

@ -5,45 +5,42 @@
#include "hecl/winsupport.hpp" #include "hecl/winsupport.hpp"
/* /*
* The memmem() function finds the start of the first occurrence of the * The memmem() function finds the start of the first occurrence of the
* substring 'needle' of length 'nlen' in the memory area 'haystack' of * substring 'needle' of length 'nlen' in the memory area 'haystack' of
* length 'hlen'. * length 'hlen'.
* *
* The return value is a pointer to the beginning of the sub-string, or * The return value is a pointer to the beginning of the sub-string, or
* NULL if the substring is not found. * NULL if the substring is not found.
*/ */
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen) void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) {
{ int needle_first;
int needle_first; const uint8_t* p = static_cast<const uint8_t*>(haystack);
const uint8_t *p = static_cast<const uint8_t*>(haystack); size_t plen = hlen;
size_t plen = hlen;
if (!nlen)
return NULL;
needle_first = *(unsigned char *)needle;
while (plen >= nlen && (p = static_cast<const uint8_t*>(memchr(p, needle_first, plen - nlen + 1))))
{
if (!memcmp(p, needle, nlen))
return (void *)p;
p++;
plen = hlen - (p - static_cast<const uint8_t*>(haystack));
}
if (!nlen)
return NULL; return NULL;
needle_first = *(unsigned char*)needle;
while (plen >= nlen && (p = static_cast<const uint8_t*>(memchr(p, needle_first, plen - nlen + 1)))) {
if (!memcmp(p, needle, nlen))
return (void*)p;
p++;
plen = hlen - (p - static_cast<const uint8_t*>(haystack));
}
return NULL;
} }
int asprintf(char** buf, const char* format, ...) int asprintf(char** buf, const char* format, ...) {
{ va_list ap;
va_list ap; va_start(ap, format);
va_start(ap, format); int len = vsnprintf(nullptr, 0, format, ap);
int len = vsnprintf(nullptr, 0, format, ap); va_end(ap);
va_end(ap); *buf = (char*)malloc(len + 1);
*buf = (char*)malloc(len + 1); va_start(ap, format);
va_start(ap, format); vsnprintf(*buf, len + 1, format, ap);
vsnprintf(*buf, len + 1, format, ap); va_end(ap);
va_end(ap); return len;
return len;
} }

View File

@ -11,31 +11,25 @@ static logvisor::Module Log("shaderc");
extern pD3DCompile D3DCompilePROC; extern pD3DCompile D3DCompilePROC;
pD3DCompile D3DCompilePROC = nullptr; pD3DCompile D3DCompilePROC = nullptr;
static bool FindBestD3DCompile() static bool FindBestD3DCompile() {
{ HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll");
HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll"); if (!d3dCompilelib) {
if (!d3dCompilelib) d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll");
{ if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll"); d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll");
if (!d3dCompilelib) if (!d3dCompilelib) {
{ d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll"); if (!d3dCompilelib) {
if (!d3dCompilelib) d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
}
}
} }
}
} }
if (d3dCompilelib) }
{ if (d3dCompilelib) {
D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile"); D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile");
return D3DCompilePROC != nullptr; return D3DCompilePROC != nullptr;
} }
return false; return false;
} }
int wmain(int argc, const hecl::SystemChar** argv) int wmain(int argc, const hecl::SystemChar** argv)
@ -43,127 +37,102 @@ int wmain(int argc, const hecl::SystemChar** argv)
int main(int argc, const hecl::SystemChar** argv) int main(int argc, const hecl::SystemChar** argv)
#endif #endif
{ {
logvisor::RegisterConsoleLogger(); logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions(); logvisor::RegisterStandardExceptions();
#if _WIN32 #if _WIN32
if (!FindBestD3DCompile()) if (!FindBestD3DCompile()) {
{ Log.report(logvisor::Info, "Unable to find D3DCompiler dll");
Log.report(logvisor::Info, "Unable to find D3DCompiler dll"); return 1;
return 1; }
}
#endif #endif
if (argc == 1) if (argc == 1) {
{ Log.report(logvisor::Info, "Usage: shaderc -o <out-base> [-D definevar=defineval]... <in-files>...");
Log.report(logvisor::Info, "Usage: shaderc -o <out-base> [-D definevar=defineval]... <in-files>...");
return 0;
}
hecl::SystemString outPath;
hecl::shaderc::Compiler c;
for (int i = 1; i < argc; ++i)
{
if (argv[i][0] == '-')
{
if (argv[i][1] == 'o')
{
if (argv[i][2])
{
outPath = &argv[i][2];
}
else if (i + 1 < argc)
{
++i;
outPath = argv[i];
}
else
{
Log.report(logvisor::Error, "Invalid -o argument");
return 1;
}
}
else if (argv[i][1] == 'D')
{
const hecl::SystemChar* define;
if (argv[i][2])
{
define = &argv[i][2];
}
else if (i + 1 < argc)
{
++i;
define = argv[i];
}
else
{
Log.report(logvisor::Error, "Invalid -D argument");
return 1;
}
hecl::SystemUTF8Conv conv(define);
const char* defineU8 = conv.c_str();
if (const char* equals = strchr(defineU8, '='))
c.addDefine(std::string(defineU8, equals - defineU8), equals + 1);
else
c.addDefine(defineU8, "");
}
else
{
Log.report(logvisor::Error, "Unrecognized flag option '%c'", argv[i][1]);
return 1;
}
}
else
{
c.addInputFile(argv[i]);
}
}
if (outPath.empty())
{
Log.report(logvisor::Error, "-o option is required");
return 1;
}
hecl::SystemStringView baseName;
auto slashPos = outPath.find_last_of(_SYS_STR("/\\"));
if (slashPos != hecl::SystemString::npos)
baseName = outPath.data() + slashPos + 1;
else
baseName = outPath;
if (!glslang::InitializeProcess())
{
Log.report(logvisor::Error, "Unable to initialize glslang");
return 1;
}
hecl::SystemUTF8Conv conv(baseName);
std::pair<std::string, std::string> ret;
if (!c.compile(conv.str(), ret))
return 1;
{
hecl::SystemString headerPath = outPath + _SYS_STR(".hpp");
athena::io::FileWriter w(headerPath);
if (w.hasError())
{
Log.report(logvisor::Error, _SYS_STR("Error opening '%s' for writing"), headerPath.c_str());
return 1;
}
w.writeBytes(ret.first.data(), ret.first.size());
}
{
hecl::SystemString impPath = outPath + _SYS_STR(".cpp");
athena::io::FileWriter w(impPath);
if (w.hasError())
{
Log.report(logvisor::Error, _SYS_STR("Error opening '%s' for writing"), impPath.c_str());
return 1;
}
w.writeBytes(ret.second.data(), ret.second.size());
}
return 0; return 0;
}
hecl::SystemString outPath;
hecl::shaderc::Compiler c;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
if (argv[i][1] == 'o') {
if (argv[i][2]) {
outPath = &argv[i][2];
} else if (i + 1 < argc) {
++i;
outPath = argv[i];
} else {
Log.report(logvisor::Error, "Invalid -o argument");
return 1;
}
} else if (argv[i][1] == 'D') {
const hecl::SystemChar* define;
if (argv[i][2]) {
define = &argv[i][2];
} else if (i + 1 < argc) {
++i;
define = argv[i];
} else {
Log.report(logvisor::Error, "Invalid -D argument");
return 1;
}
hecl::SystemUTF8Conv conv(define);
const char* defineU8 = conv.c_str();
if (const char* equals = strchr(defineU8, '='))
c.addDefine(std::string(defineU8, equals - defineU8), equals + 1);
else
c.addDefine(defineU8, "");
} else {
Log.report(logvisor::Error, "Unrecognized flag option '%c'", argv[i][1]);
return 1;
}
} else {
c.addInputFile(argv[i]);
}
}
if (outPath.empty()) {
Log.report(logvisor::Error, "-o option is required");
return 1;
}
hecl::SystemStringView baseName;
auto slashPos = outPath.find_last_of(_SYS_STR("/\\"));
if (slashPos != hecl::SystemString::npos)
baseName = outPath.data() + slashPos + 1;
else
baseName = outPath;
if (!glslang::InitializeProcess()) {
Log.report(logvisor::Error, "Unable to initialize glslang");
return 1;
}
hecl::SystemUTF8Conv conv(baseName);
std::pair<std::string, std::string> ret;
if (!c.compile(conv.str(), ret))
return 1;
{
hecl::SystemString headerPath = outPath + _SYS_STR(".hpp");
athena::io::FileWriter w(headerPath);
if (w.hasError()) {
Log.report(logvisor::Error, _SYS_STR("Error opening '%s' for writing"), headerPath.c_str());
return 1;
}
w.writeBytes(ret.first.data(), ret.first.size());
}
{
hecl::SystemString impPath = outPath + _SYS_STR(".cpp");
athena::io::FileWriter w(impPath);
if (w.hasError()) {
Log.report(logvisor::Error, _SYS_STR("Error opening '%s' for writing"), impPath.c_str());
return 1;
}
w.writeBytes(ret.second.data(), ret.second.size());
}
return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,38 +4,28 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
namespace hecl::shaderc namespace hecl::shaderc {
{
class Compiler class Compiler {
{ enum class StageType { Vertex, Fragment, Geometry, Control, Evaluation };
enum class StageType
{ std::vector<SystemString> m_inputFiles;
Vertex, std::unordered_map<SystemString, std::string> m_fileContents;
Fragment, const std::string* getFileContents(SystemStringView path);
Geometry, std::unordered_map<std::string, std::string> m_defines;
Control, template <typename Action, typename P>
Evaluation static bool StageAction(StageType type, const std::string& name, const std::string& basename,
}; const std::string& stage, std::string& implOut);
template <typename Action>
static bool StageAction(const std::string& platforms, StageType type, const std::string& name,
const std::string& basename, const std::string& stage, std::string& implOut);
bool includeFile(SystemStringView file, std::string& out, int depth = 0);
bool compileFile(SystemStringView file, std::string_view baseName, std::pair<std::string, std::string>& out);
std::vector<SystemString> m_inputFiles;
std::unordered_map<SystemString, std::string> m_fileContents;
const std::string* getFileContents(SystemStringView path);
std::unordered_map<std::string, std::string> m_defines;
template<typename Action, typename P>
static bool StageAction(StageType type,
const std::string& name, const std::string& basename, const std::string& stage,
std::string& implOut);
template<typename Action>
static bool StageAction(const std::string& platforms, StageType type,
const std::string& name, const std::string& basename, const std::string& stage,
std::string& implOut);
bool includeFile(SystemStringView file, std::string& out, int depth = 0);
bool compileFile(SystemStringView file, std::string_view baseName, std::pair<std::string, std::string>& out);
public: public:
void addInputFile(SystemStringView file); void addInputFile(SystemStringView file);
void addDefine(std::string_view var, std::string_view val); void addDefine(std::string_view var, std::string_view val);
bool compile(std::string_view baseName, std::pair<std::string, std::string>& out); bool compile(std::string_view baseName, std::pair<std::string, std::string>& out);
}; };
} } // namespace hecl::shaderc

View File

@ -14,256 +14,221 @@
using namespace std::literals; using namespace std::literals;
struct HECLWindowCallback : boo::IWindowCallback struct HECLWindowCallback : boo::IWindowCallback {
{ bool m_sizeDirty = false;
bool m_sizeDirty = false; boo::SWindowRect m_latestSize;
boo::SWindowRect m_latestSize; virtual ~HECLWindowCallback();
virtual ~HECLWindowCallback(); void resized(const boo::SWindowRect& rect, bool /*sync*/) {
void resized(const boo::SWindowRect& rect, bool /*sync*/) m_sizeDirty = true;
{ m_latestSize = rect;
m_sizeDirty = true; }
m_latestSize = rect;
}
bool m_destroyed = false; bool m_destroyed = false;
void destroyed() void destroyed() { m_destroyed = true; }
{
m_destroyed = true;
}
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) {
{ hecl::Console::instance()->handleCharCode(charCode, mods, isRepeat);
hecl::Console::instance()->handleCharCode(charCode, mods, isRepeat); }
} void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) {
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) hecl::Console::instance()->handleSpecialKeyDown(key, mods, isRepeat);
{ }
hecl::Console::instance()->handleSpecialKeyDown(key, mods, isRepeat); void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) {
} hecl::Console::instance()->hecl::Console::handleSpecialKeyUp(key, mods);
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) }
{
hecl::Console::instance()->hecl::Console::handleSpecialKeyUp(key, mods);
}
}; };
HECLWindowCallback::~HECLWindowCallback() HECLWindowCallback::~HECLWindowCallback() {}
{
} struct HECLApplicationCallback : boo::IApplicationCallback {
HECLWindowCallback m_windowCb;
hecl::Runtime::FileStoreManager m_fileStoreMgr;
hecl::CVarManager m_cvarManager;
hecl::Console m_console;
std::shared_ptr<boo::IWindow> m_mainWindow;
bool m_running = true;
struct HECLApplicationCallback : boo::IApplicationCallback HECLApplicationCallback()
{ : m_fileStoreMgr(_SYS_STR("heclTest")), m_cvarManager(m_fileStoreMgr), m_console(&m_cvarManager) {
HECLWindowCallback m_windowCb; m_console.registerCommand(
hecl::Runtime::FileStoreManager m_fileStoreMgr; "quit"sv, "Quits application"sv, "",
hecl::CVarManager m_cvarManager; std::bind(&HECLApplicationCallback::quit, this, std::placeholders::_1, std::placeholders::_2));
hecl::Console m_console; }
std::shared_ptr<boo::IWindow> m_mainWindow;
bool m_running = true;
HECLApplicationCallback() virtual ~HECLApplicationCallback();
: m_fileStoreMgr(_SYS_STR("heclTest")),
m_cvarManager(m_fileStoreMgr),
m_console(&m_cvarManager)
{
m_console.registerCommand("quit"sv, "Quits application"sv, "",
std::bind(&HECLApplicationCallback::quit, this, std::placeholders::_1, std::placeholders::_2));
}
virtual ~HECLApplicationCallback(); int appMain(boo::IApplication* app) {
hecl::VerbosityLevel = 2;
int appMain(boo::IApplication* app) /* Setup boo window */
{ m_mainWindow = app->newWindow(_SYS_STR("HECL Test"));
hecl::VerbosityLevel = 2; m_mainWindow->setCallback(&m_windowCb);
/* Setup boo window */ boo::ObjToken<boo::ITextureR> renderTex;
m_mainWindow = app->newWindow(_SYS_STR("HECL Test")); boo::ObjToken<boo::IGraphicsBuffer> vubo;
m_mainWindow->setCallback(&m_windowCb); boo::ObjToken<boo::IShaderPipeline> pipeline, pipeline2;
boo::ObjToken<boo::IShaderDataBinding> binding, binding2;
boo::ObjToken<boo::ITextureR> renderTex; struct VertexUBO {
boo::ObjToken<boo::IGraphicsBuffer> vubo; float modelview[4][4] = {};
boo::ObjToken<boo::IShaderPipeline> pipeline, pipeline2; float modelviewInv[4][4] = {};
boo::ObjToken<boo::IShaderDataBinding> binding, binding2; float projection[4][4] = {};
VertexUBO() {
modelview[0][0] = 1.0;
modelview[1][1] = 1.0;
modelview[2][2] = 1.0;
modelview[3][3] = 1.0;
modelviewInv[0][0] = 1.0;
modelviewInv[1][1] = 1.0;
modelviewInv[2][2] = 1.0;
modelviewInv[3][3] = 1.0;
projection[0][0] = 1.0;
projection[1][1] = 1.0;
projection[2][2] = 1.0;
projection[3][3] = 1.0;
}
} vuboData;
struct VertexUBO /* Make ramp texture */
{ using Pixel = uint8_t[4];
float modelview[4][4] = {}; static Pixel tex[256][256];
float modelviewInv[4][4] = {}; for (int i = 0; i < 256; ++i)
float projection[4][4] = {}; for (int j = 0; j < 256; ++j) {
VertexUBO() tex[i][j][0] = uint8_t(i);
{ tex[i][j][1] = uint8_t(j);
modelview[0][0] = 1.0; tex[i][j][2] = 0;
modelview[1][1] = 1.0; tex[i][j][3] = 0xff;
modelview[2][2] = 1.0; }
modelview[3][3] = 1.0;
modelviewInv[0][0] = 1.0;
modelviewInv[1][1] = 1.0;
modelviewInv[2][2] = 1.0;
modelviewInv[3][3] = 1.0;
projection[0][0] = 1.0;
projection[1][1] = 1.0;
projection[2][2] = 1.0;
projection[3][3] = 1.0;
}
} vuboData;
/* Make ramp texture */ boo::IGraphicsDataFactory* gfxF = m_mainWindow->getMainContextDataFactory();
using Pixel = uint8_t[4]; if (gfxF->platform() == boo::IGraphicsDataFactory::Platform::Vulkan)
static Pixel tex[256][256]; vuboData.modelview[1][1] = -1.f;
for (int i=0 ; i<256 ; ++i)
for (int j=0 ; j<256 ; ++j)
{
tex[i][j][0] = uint8_t(i);
tex[i][j][1] = uint8_t(j);
tex[i][j][2] = 0;
tex[i][j][3] = 0xff;
}
boo::IGraphicsDataFactory* gfxF = m_mainWindow->getMainContextDataFactory(); /* Pipeline converter */
if (gfxF->platform() == boo::IGraphicsDataFactory::Platform::Vulkan) std::unique_ptr<hecl::PipelineConverterBase> conv = hecl::NewPipelineConverter(gfxF);
vuboData.modelview[1][1] = -1.f;
/* Pipeline converter */ /* Compile HECL shader */
std::unique_ptr<hecl::PipelineConverterBase> conv = hecl::NewPipelineConverter(gfxF); static std::string testShader = "HECLOpaque(Texture(0, UV(0)))";
// static std::string testShader = "HECLOpaque(vec3(1.0,1.0,1.0),1.0)";
hecl::Backend::ShaderTag testShaderTag(testShader, 0, 1, 0, 0, boo::Primitive::TriStrips,
hecl::Backend::ReflectionType::None, false, false, false, false);
hecl::Frontend::Frontend FE;
hecl::Frontend::IR ir = FE.compileSource(testShader, "booTest");
hecl::HECLIR irObj(ir, testShaderTag, 0);
/* Compile HECL shader */ gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
static std::string testShader = "HECLOpaque(Texture(0, UV(0)))"; pipeline = conv->convert(ctx, irObj);
//static std::string testShader = "HECLOpaque(vec3(1.0,1.0,1.0),1.0)"; pipeline2 = conv->convert(ctx, Shader_test{});
hecl::Backend::ShaderTag testShaderTag(testShader, 0, 1, 0, 0, boo::Primitive::TriStrips,
hecl::Backend::ReflectionType::None, false, false, false, false);
hecl::Frontend::Frontend FE;
hecl::Frontend::IR ir = FE.compileSource(testShader, "booTest");
hecl::HECLIR irObj(ir, testShaderTag, 0);
gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame();
{ renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]),
pipeline = conv->convert(ctx, irObj); boo::TextureClampMode::Repeat, 1, 0);
pipeline2 = conv->convert(ctx, Shader_test{});
boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame(); /* Generate meta structure (usually statically serialized) */
renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]), hecl::HMDLMeta testMeta;
boo::TextureClampMode::Repeat, 1, 0); testMeta.topology = hecl::HMDLTopology::TriStrips;
testMeta.vertStride = 32;
testMeta.vertCount = 4;
testMeta.indexCount = 4;
testMeta.colorCount = 0;
testMeta.uvCount = 1;
testMeta.weightCount = 0;
testMeta.bankCount = 0;
/* Generate meta structure (usually statically serialized) */ /* Binary form of meta structure */
hecl::HMDLMeta testMeta; atUint8 testMetaBuf[HECL_HMDL_META_SZ];
testMeta.topology = hecl::HMDLTopology::TriStrips; athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ);
testMeta.vertStride = 32; testMeta.write(testMetaWriter);
testMeta.vertCount = 4;
testMeta.indexCount = 4;
testMeta.colorCount = 0;
testMeta.uvCount = 1;
testMeta.weightCount = 0;
testMeta.bankCount = 0;
/* Binary form of meta structure */ /* Make Tri-strip VBO */
atUint8 testMetaBuf[HECL_HMDL_META_SZ]; struct Vert {
athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ); float pos[3];
testMeta.write(testMetaWriter); float norm[3];
float uv[2];
};
static const Vert quad[4] = {{{0.5, 0.5}, {}, {1.0, 1.0}},
{{-0.5, 0.5}, {}, {0.0, 1.0}},
{{0.5, -0.5}, {}, {1.0, 0.0}},
{{-0.5, -0.5}, {}, {0.0, 0.0}}};
/* Make Tri-strip VBO */ /* Now simple IBO */
struct Vert static const uint32_t ibo[4] = {0, 1, 2, 3};
{
float pos[3];
float norm[3];
float uv[2];
};
static const Vert quad[4] =
{
{{0.5,0.5},{},{1.0,1.0}},
{{-0.5,0.5},{},{0.0,1.0}},
{{0.5,-0.5},{},{1.0,0.0}},
{{-0.5,-0.5},{},{0.0,0.0}}
};
/* Now simple IBO */ /* Construct quad mesh against boo factory */
static const uint32_t ibo[4] = {0,1,2,3}; hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo);
/* Construct quad mesh against boo factory */ boo::ObjToken<boo::ITexture> texture = ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8,
hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo); boo::TextureClampMode::Repeat, tex, 256 * 256 * 4)
.get();
boo::ObjToken<boo::ITexture> texture = /* Make vertex uniform buffer */
ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat, tex, 256*256*4).get(); vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get();
/* Make vertex uniform buffer */ /* Assemble data binding */
vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get(); binding = testData.newShaderDataBindng(ctx, pipeline, 1, &vubo, nullptr, 1, &texture);
binding2 = testData.newShaderDataBindng(ctx, pipeline2, 1, &vubo, nullptr, 1, &texture);
return true;
} BooTrace);
/* Assemble data binding */ m_mainWindow->showWindow();
binding = testData.newShaderDataBindng(ctx, pipeline, 1, &vubo, nullptr, 1, &texture); m_windowCb.m_latestSize = m_mainWindow->getWindowFrame();
binding2 = testData.newShaderDataBindng(ctx, pipeline2, 1, &vubo, nullptr, 1, &texture); boo::IGraphicsCommandQueue* gfxQ = m_mainWindow->getCommandQueue();
return true;
} BooTrace);
size_t frameIdx = 0;
while (m_running) {
m_mainWindow->waitForRetrace();
m_mainWindow->showWindow(); if (m_windowCb.m_destroyed) {
m_windowCb.m_latestSize = m_mainWindow->getWindowFrame();
boo::IGraphicsCommandQueue* gfxQ = m_mainWindow->getCommandQueue();
size_t frameIdx = 0;
while (m_running)
{
m_mainWindow->waitForRetrace();
if (m_windowCb.m_destroyed)
{
m_running = false;
break;
}
if (m_windowCb.m_sizeDirty)
{
gfxQ->resizeRenderTexture(renderTex,
size_t(m_windowCb.m_latestSize.size[0]),
size_t(m_windowCb.m_latestSize.size[1]));
m_windowCb.m_sizeDirty = false;
}
m_console.proc();
gfxQ->setRenderTarget(renderTex);
boo::SWindowRect r = m_windowCb.m_latestSize;
r.location[0] = 0;
r.location[1] = 0;
gfxQ->setViewport(r);
gfxQ->setScissor(r);
float rgba[] = {sinf(frameIdx / 60.0f), cosf(frameIdx / 60.0f), 0.0f, 1.0f};
gfxQ->setClearColor(rgba);
gfxQ->clearTarget();
vuboData.modelview[3][0] = sinf(frameIdx / 60.0f) * 0.5f;
vuboData.modelview[3][1] = cosf(frameIdx / 60.0f) * 0.5f;
vubo.cast<boo::IGraphicsBufferD>()->load(&vuboData, sizeof(vuboData));
gfxQ->setShaderDataBinding(binding2);
gfxQ->drawIndexed(0, 4);
gfxQ->resolveDisplay(renderTex);
m_console.draw(gfxQ);
gfxQ->execute();
++frameIdx;
}
m_cvarManager.serialize();
gfxQ->stopRenderer();
return 0;
}
void appQuitting(boo::IApplication* /*app*/)
{
m_running = false; m_running = false;
break;
}
if (m_windowCb.m_sizeDirty) {
gfxQ->resizeRenderTexture(renderTex, size_t(m_windowCb.m_latestSize.size[0]),
size_t(m_windowCb.m_latestSize.size[1]));
m_windowCb.m_sizeDirty = false;
}
m_console.proc();
gfxQ->setRenderTarget(renderTex);
boo::SWindowRect r = m_windowCb.m_latestSize;
r.location[0] = 0;
r.location[1] = 0;
gfxQ->setViewport(r);
gfxQ->setScissor(r);
float rgba[] = {sinf(frameIdx / 60.0f), cosf(frameIdx / 60.0f), 0.0f, 1.0f};
gfxQ->setClearColor(rgba);
gfxQ->clearTarget();
vuboData.modelview[3][0] = sinf(frameIdx / 60.0f) * 0.5f;
vuboData.modelview[3][1] = cosf(frameIdx / 60.0f) * 0.5f;
vubo.cast<boo::IGraphicsBufferD>()->load(&vuboData, sizeof(vuboData));
gfxQ->setShaderDataBinding(binding2);
gfxQ->drawIndexed(0, 4);
gfxQ->resolveDisplay(renderTex);
m_console.draw(gfxQ);
gfxQ->execute();
++frameIdx;
} }
void quit(hecl::Console* /*con*/, const std::vector<std::string>& /*args*/) m_cvarManager.serialize();
{ gfxQ->stopRenderer();
m_running = false; return 0;
} }
void appQuitting(boo::IApplication* /*app*/) { m_running = false; }
void quit(hecl::Console* /*con*/, const std::vector<std::string>& /*args*/) { m_running = false; }
}; };
void AthenaExcHandler(athena::error::Level level, void AthenaExcHandler(athena::error::Level level, const char* file, const char* /*function*/, int line, const char* fmt,
const char* file, const char* /*function*/, ...) {
int line, const char* fmt, ...) static logvisor::Module Log("heclTest::AthenaExcHandler");
{ va_list ap;
static logvisor::Module Log("heclTest::AthenaExcHandler"); va_start(ap, fmt);
va_list ap; Log.reportSource(logvisor::Level(level), file, uint32_t(line), fmt, ap);
va_start(ap, fmt); va_end(ap);
Log.reportSource(logvisor::Level(level), file, uint32_t(line), fmt, ap);
va_end(ap);
} }
#if !WINDOWS_STORE #if !WINDOWS_STORE
@ -273,51 +238,45 @@ int wmain(int argc, const boo::SystemChar** argv)
int main(int argc, const boo::SystemChar** argv) int main(int argc, const boo::SystemChar** argv)
#endif #endif
{ {
atSetExceptionHandler(AthenaExcHandler); atSetExceptionHandler(AthenaExcHandler);
logvisor::RegisterStandardExceptions(); logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger(); logvisor::RegisterConsoleLogger();
HECLApplicationCallback appCb; HECLApplicationCallback appCb;
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, appCb, _SYS_STR("heclTest"),
appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"), argc, argv); _SYS_STR("HECL Test"), argc, argv);
printf("IM DYING!!\n"); printf("IM DYING!!\n");
return ret; return ret;
} }
#else #else
using namespace Windows::ApplicationModel::Core; using namespace Windows::ApplicationModel::Core;
[Platform::MTAThread] [Platform::MTAThread] int WINAPIV main(Platform::Array<Platform::String ^> ^ params) {
int WINAPIV main(Platform::Array<Platform::String^>^ params) logvisor::RegisterStandardExceptions();
{ logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions(); HECLApplicationCallback appCb;
logvisor::RegisterConsoleLogger(); boo::ViewProvider ^ viewProvider = ref new boo::ViewProvider(appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"),
HECLApplicationCallback appCb; _SYS_STR("heclTest"), params, false);
boo::ViewProvider^ viewProvider = CoreApplication::Run(viewProvider);
ref new boo::ViewProvider(appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"), _SYS_STR("heclTest"), params, false); return 0;
CoreApplication::Run(viewProvider);
return 0;
} }
#endif #endif
#if _WIN32 && !WINDOWS_STORE #if _WIN32 && !WINDOWS_STORE
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
{ int argc = 0;
int argc = 0; const boo::SystemChar** argv;
const boo::SystemChar** argv; if (lpCmdLine[0])
if (lpCmdLine[0]) argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc)); static boo::SystemChar selfPath[1024];
static boo::SystemChar selfPath[1024]; GetModuleFileNameW(nullptr, selfPath, 1024);
GetModuleFileNameW(nullptr, selfPath, 1024); static const boo::SystemChar* booArgv[32] = {};
static const boo::SystemChar* booArgv[32] = {}; booArgv[0] = selfPath;
booArgv[0] = selfPath; for (int i = 0; i < argc; ++i)
for (int i=0 ; i<argc ; ++i) booArgv[i + 1] = argv[i];
booArgv[i+1] = argv[i];
logvisor::CreateWin32Console(); logvisor::CreateWin32Console();
return wmain(argc + 1, booArgv); return wmain(argc + 1, booArgv);
} }
#endif #endif
HECLApplicationCallback::~HECLApplicationCallback() HECLApplicationCallback::~HECLApplicationCallback() {}
{
}