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

View File

@ -4,178 +4,157 @@
#include <cstdio>
#include "hecl/ClientProcess.hpp"
class ToolCook final : public ToolBase
{
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_recursive = false;
bool m_fast = false;
class ToolCook final : public ToolBase {
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_recursive = false;
bool m_fast = false;
public:
ToolCook(const ToolPassInfo& info)
: ToolBase(info), m_useProj(info.project)
{
/* Check for recursive flag */
for (hecl::SystemChar arg : info.flags)
if (arg == _SYS_STR('r'))
m_recursive = true;
ToolCook(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
/* Check for recursive flag */
for (hecl::SystemChar arg : info.flags)
if (arg == _SYS_STR('r'))
m_recursive = true;
/* Scan args */
if (info.args.size())
{
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args)
{
if (arg.empty())
continue;
else if (!arg.compare(_SYS_STR("--fast")))
{
m_fast = true;
continue;
}
else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec=")))
{
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);
}
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
else if (!arg.compare(_SYS_STR("--fast"))) {
m_fast = true;
continue;
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
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_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,
"hecl cook must be ran within a project directory or "
"provided a path within a project");
/* Default case: recursive at root */
if (m_selectedItems.empty())
{
m_selectedItems.reserve(1);
m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
m_recursive = true;
_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)
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)
{
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\n"));
help.endWrap();
/* Default case: recursive at root */
if (m_selectedItems.empty()) {
m_selectedItems.reserve(1);
m_selectedItems.push_back({hecl::ProjectPath(*m_useProj, _SYS_STR(""))});
m_recursive = true;
}
}
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl cook [-rf] [--fast] [--spec=<spec>] [<pathspec>...]\n"));
help.endWrap();
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-cook - Cook objects within the project database\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 ")
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
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("are cached within the project database. HECL performs the following ")
_SYS_STR("tasks for each object during the cook process:\n\n"));
help.wrapBold(_SYS_STR("- Object Gather: "));
help.wrap(_SYS_STR("Files added with "));
help.wrapBold(_SYS_STR("hecl add"));
help.wrap(_SYS_STR(" are queried for their dependent files (e.g. "));
help.wrapBold(_SYS_STR(".blend"));
help.wrap(_SYS_STR(" files return any linked "));
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.wrapBold(_SYS_STR("- Modtime Comparison: "));
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("process is skipped. If the file has been moved or deleted, the object is automatically ")
_SYS_STR("removed from the project database.\n\n"));
help.wrapBold(_SYS_STR("- Cook: "));
help.wrap(_SYS_STR("A type-specific procedure compiles the file's contents into an efficient format ")
_SYS_STR("are cached within the project database. HECL performs the following ")
_SYS_STR("tasks for each object during the cook process:\n\n"));
help.wrapBold(_SYS_STR("- Object Gather: "));
help.wrap(_SYS_STR("Files added with "));
help.wrapBold(_SYS_STR("hecl add"));
help.wrap(_SYS_STR(" are queried for their dependent files (e.g. "));
help.wrapBold(_SYS_STR(".blend"));
help.wrap(_SYS_STR(" files return any linked "));
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.wrapBold(_SYS_STR("- Modtime Comparison: "));
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(
"process is skipped. If the file has been moved or deleted, the object is automatically ")
_SYS_STR("removed from the project database.\n\n"));
help.wrapBold(_SYS_STR("- Cook: "));
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"));
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.endWrap();
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.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies working file(s) containing production data to be cooked by HECL. ")
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<pathspec>..."), _SYS_STR("input file(s)"));
help.beginWrap();
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. "));
help.wrapBold(_SYS_STR("*.blend"));
help.wrap(_SYS_STR(") to automatically cook all matching current-directory files in the project database. ")
help.wrapBold(_SYS_STR("*.blend"));
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"));
help.endWrap();
help.endWrap();
help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion"));
help.beginWrap();
help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n"));
help.endWrap();
help.optionHead(_SYS_STR("-f"), _SYS_STR("force"));
help.beginWrap();
help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n"));
help.endWrap();
help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook"));
help.beginWrap();
help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n"));
help.endWrap();
help.optionHead(_SYS_STR("-r"), _SYS_STR("recursion"));
help.beginWrap();
help.wrap(_SYS_STR("Enables recursive file-matching for cooking entire directories of working files.\n"));
help.endWrap();
help.optionHead(_SYS_STR("-f"), _SYS_STR("force"));
help.beginWrap();
help.wrap(_SYS_STR("Forces cooking of all matched files, ignoring timestamp differences.\n"));
help.endWrap();
help.optionHead(_SYS_STR("--fast"), _SYS_STR("fast cook"));
help.beginWrap();
help.wrap(_SYS_STR("Performs draft-optimization cooking for supported data types.\n"));
help.endWrap();
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ")
help.optionHead(_SYS_STR("--spec=<spec>"), _SYS_STR("data specification"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a DataSpec to use when cooking. ")
_SYS_STR("This build of hecl supports the following values of <spec>:\n"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY)
{
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
}
hecl::SystemString toolName() const {return _SYS_STR("cook");}
hecl::SystemString toolName() const { return _SYS_STR("cook"); }
int run()
{
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems)
m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp);
cp.waitUntilComplete();
return 0;
}
int run() {
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems)
m_useProj->cookPath(path, printer, m_recursive, m_info.force, m_fast, m_spec, &cp);
cp.waitUntilComplete();
return 0;
}
void cancel()
{
m_useProj->interruptCook();
}
void cancel() { m_useProj->interruptCook(); }
};

View File

@ -11,175 +11,156 @@
#include "hecl/MultiProgressPrinter.hpp"
class ToolExtract final : public ToolBase
{
hecl::Database::IDataSpec::ExtractPassInfo m_einfo;
struct SpecExtractPass
{
const hecl::Database::DataSpecEntry* m_entry;
std::unique_ptr<hecl::Database::IDataSpec> m_instance;
SpecExtractPass(const hecl::Database::DataSpecEntry* entry,
std::unique_ptr<hecl::Database::IDataSpec>&& instance)
: m_entry(entry), m_instance(std::move(instance)) {}
SpecExtractPass(const SpecExtractPass& other) = delete;
SpecExtractPass(SpecExtractPass&& other) = default;
};
std::vector<SpecExtractPass> m_specPasses;
std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj = nullptr;
class ToolExtract final : public ToolBase {
hecl::Database::IDataSpec::ExtractPassInfo m_einfo;
struct SpecExtractPass {
const hecl::Database::DataSpecEntry* m_entry;
std::unique_ptr<hecl::Database::IDataSpec> m_instance;
SpecExtractPass(const hecl::Database::DataSpecEntry* entry, std::unique_ptr<hecl::Database::IDataSpec>&& instance)
: m_entry(entry), m_instance(std::move(instance)) {}
SpecExtractPass(const SpecExtractPass& other) = delete;
SpecExtractPass(SpecExtractPass&& other) = default;
};
std::vector<SpecExtractPass> m_specPasses;
std::vector<hecl::Database::IDataSpec::ExtractReport> m_reps;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj = nullptr;
public:
ToolExtract(const ToolPassInfo& info)
: ToolBase(info)
{
if (!m_info.args.size())
LogModule.report(logvisor::Fatal, "hecl extract needs a source path as its first argument");
ToolExtract(const ToolPassInfo& info) : ToolBase(info) {
if (!m_info.args.size())
LogModule.report(logvisor::Fatal, "hecl extract needs a source path as its first argument");
if (!info.project)
{
hecl::SystemString rootDir;
if (!info.project) {
hecl::SystemString rootDir;
if (info.output.empty())
{
/* Get name from input file and init project there */
hecl::SystemString baseFile = info.args.front();
size_t slashPos = baseFile.rfind(_SYS_STR('/'));
if (slashPos == hecl::SystemString::npos)
slashPos = baseFile.rfind(_SYS_STR('\\'));
if (slashPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
size_t dotPos = baseFile.rfind(_SYS_STR('.'));
if (dotPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
if (info.output.empty()) {
/* Get name from input file and init project there */
hecl::SystemString baseFile = info.args.front();
size_t slashPos = baseFile.rfind(_SYS_STR('/'));
if (slashPos == hecl::SystemString::npos)
slashPos = baseFile.rfind(_SYS_STR('\\'));
if (slashPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin() + slashPos + 1, baseFile.end());
size_t dotPos = baseFile.rfind(_SYS_STR('.'));
if (dotPos != hecl::SystemString::npos)
baseFile.assign(baseFile.begin(), baseFile.begin() + dotPos);
if (baseFile.empty())
LogModule.report(logvisor::Fatal, "hecl extract must be ran within a project directory");
if (baseFile.empty())
LogModule.report(logvisor::Fatal, "hecl extract must be ran within a project directory");
rootDir = info.cwd + baseFile;
}
else
{
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();
}
rootDir = info.cwd + baseFile;
} else {
if (hecl::PathRelative(info.output.c_str()))
rootDir = info.cwd + info.output;
else
m_useProj = info.project;
rootDir = info.output;
}
m_einfo.srcpath = m_info.args.front();
m_einfo.force = info.force;
m_einfo.extractArgs.reserve(info.args.size());
auto it=info.args.cbegin();
++it;
for (; it != info.args.cend(); ++it)
m_einfo.extractArgs.push_back(*it);
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
m_useProj = info.project;
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));
}
}
m_einfo.srcpath = m_info.args.front();
m_einfo.force = info.force;
m_einfo.extractArgs.reserve(info.args.size());
auto it = info.args.cbegin();
++it;
for (; it != info.args.cend(); ++it)
m_einfo.extractArgs.push_back(*it);
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)
{
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n"));
help.endWrap();
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-extract - Extract objects from supported package/image formats\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl extract <packagefile> [<subnode>...]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command recursively extracts all or part of a dataspec-supported ")
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
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"));
help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies the package file or disc image to source data from. ")
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<packagefile>[/<subnode>...]"), _SYS_STR("input file"));
help.beginWrap();
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("to the game architecture (levels/areas)."));
help.endWrap();
_SYS_STR("to the game architecture (levels/areas)."));
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 (int l=0 ; l<level ; ++l)
hecl::Printf(_SYS_STR(" "));
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("" 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
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())
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);
ds.m_instance->doExtract(m_einfo, {true});
hecl::Printf(_SYS_STR("\n\n"));
}
}
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;
}
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;
}
return 0;
}
};

View File

@ -4,89 +4,78 @@
#include <cstdio>
#include <functional>
class ToolHelp final : public ToolBase
{
class ToolHelp final : public ToolBase {
public:
ToolHelp(const ToolPassInfo& info)
: ToolBase(info)
{
if (m_info.args.empty())
{
LogModule.report(logvisor::Error, "help requires a tool name argument");
return;
}
m_good = true;
ToolHelp(const ToolPassInfo& info) : ToolBase(info) {
if (m_info.args.empty()) {
LogModule.report(logvisor::Error, "help requires a tool name argument");
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"); }
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;
}
HelpOutput ho(helpFunc);
ho.go();
}
hecl::SystemString toolName() const {return _SYS_STR("help");}
int run()
{
ShowHelp(m_info.args.front());
return 0;
}
int run() {
ShowHelp(m_info.args.front());
return 0;
}
};

View File

@ -10,148 +10,128 @@
#include "nod/DiscWii.hpp"
#include "athena/FileReader.hpp"
class ToolImage final : public ToolBase
{
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
class ToolImage final : public ToolBase {
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
public:
ToolImage(const ToolPassInfo& info)
: ToolBase(info), m_useProj(info.project)
{
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl image must be ran within a project directory");
ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl image must be ran within a project directory");
/* Scan args */
if (info.args.size())
{
/* See if project path is supplied via args and use that over the getcwd one */
for (const hecl::SystemString& arg : info.args)
{
if (arg.empty())
continue;
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
hecl::SystemString subPath;
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
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();
break;
}
}
}
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
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)
{
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n"));
help.endWrap();
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-image - Generate GameCube/Wii disc image from packaged files\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl image [<input-dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl image [<input-dir>]\n"));
help.endWrap();
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command uses the current contents of `out` to generate a GameCube or ")
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
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"));
help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting image from. ")
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
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"));
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");}
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;
}
return 0;
}
};
#endif

View File

@ -3,84 +3,75 @@
#include "ToolBase.hpp"
#include <cstdio>
class ToolInit final : public ToolBase
{
const hecl::SystemString* m_dir = NULL;
class ToolInit final : public ToolBase {
const hecl::SystemString* m_dir = NULL;
public:
ToolInit(const ToolPassInfo& info)
: ToolBase(info)
{
hecl::Sstat theStat;
const hecl::SystemString* dir;
if (info.args.size())
dir = &info.args.front();
else
dir = &info.cwd;
ToolInit(const ToolPassInfo& info) : ToolBase(info) {
hecl::Sstat theStat;
const hecl::SystemString* dir;
if (info.args.size())
dir = &info.args.front();
else
dir = &info.cwd;
if (hecl::Stat(dir->c_str(), &theStat))
{
hecl::MakeDir(dir->c_str());
if (hecl::Stat(dir->c_str(), &theStat))
{
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());
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;
if (hecl::Stat(dir->c_str(), &theStat)) {
hecl::MakeDir(dir->c_str());
if (hecl::Stat(dir->c_str(), &theStat)) {
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());
return;
}
int run()
{
if (!m_dir)
return 1;
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;
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;
}
static void Help(HelpOutput& help)
{
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-init - Initialize a brand-new project database\n"));
help.endWrap();
m_dir = dir;
}
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl init [<dir>]\n"));
help.endWrap();
int run() {
if (!m_dir)
return 1;
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"));
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. ")
static void Help(HelpOutput& help) {
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"));
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"));
help.endWrap();
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path"));
help.beginWrap();
help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n"));
help.endWrap();
}
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<dir>"), _SYS_STR("group directory path"));
help.beginWrap();
help.wrap(_SYS_STR("Directory to create new project database in. If not specified, current directory is used.\n"));
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 <cstdio>
class ToolPackage final : public ToolBase
{
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_fast = false;
class ToolPackage final : public ToolBase {
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_fast = false;
void AddSelectedItem(const hecl::ProjectPath& path)
{
for (const hecl::ProjectPath& item : m_selectedItems)
if (item == path)
return;
m_selectedItems.push_back(path);
}
void AddSelectedItem(const hecl::ProjectPath& path) {
for (const hecl::ProjectPath& item : m_selectedItems)
if (item == path)
return;
m_selectedItems.push_back(path);
}
void CheckFile(const hecl::ProjectPath& path)
{
if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!world.blend")))
AddSelectedItem(path);
void CheckFile(const hecl::ProjectPath& path) {
if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!world.blend")))
AddSelectedItem(path);
#if RUNTIME_ORIGINAL_IDS
else if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!original_ids.yaml")))
{
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out"))
AddSelectedItem(path);
}
else if (!hecl::StrCmp(path.getLastComponent().data(), _SYS_STR("!original_ids.yaml"))) {
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != _SYS_STR("out"))
AddSelectedItem(path);
}
#endif
}
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) {
if (path.isFile()) {
CheckFile(path);
return;
}
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral)
{
if (path.isFile())
{
CheckFile(path);
return;
}
size_t origSize = m_selectedItems.size();
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);
}
size_t origSize = m_selectedItems.size();
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);
}
}
public:
ToolPackage(const ToolPassInfo& info)
: ToolBase(info), m_useProj(info.project)
{
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl package must be ran within a project directory");
ToolPackage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl package must be ran within a project directory");
/* Scan args */
if (info.args.size())
{
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args)
{
if (arg.empty())
continue;
else if (!arg.compare(_SYS_STR("--fast")))
{
m_fast = true;
continue;
}
else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec=")))
{
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);
}
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const hecl::SystemString& arg : info.args) {
if (arg.empty())
continue;
else if (!arg.compare(_SYS_STR("--fast"))) {
m_fast = true;
continue;
} else if (arg.size() >= 8 && !arg.compare(0, 7, _SYS_STR("--spec="))) {
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_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,
"hecl package must be ran within a project directory or "
"provided a path within a project");
_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());
/* Default case: recursive at root */
if (m_selectedItems.empty())
FindSelectedItems({*m_useProj, _SYS_STR("")}, true);
FindSelectedItems({*m_useProj, subPath}, 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)
{
help.secHead(_SYS_STR("NAME"));
help.beginWrap();
help.wrap(_SYS_STR("hecl-pack\n")
_SYS_STR("hecl-package - Package objects within the project database\n"));
help.endWrap();
/* Default case: recursive at root */
if (m_selectedItems.empty())
FindSelectedItems({*m_useProj, _SYS_STR("")}, true);
}
help.secHead(_SYS_STR("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\n"));
help.endWrap();
static void Help(HelpOutput& help) {
help.secHead(_SYS_STR("NAME"));
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("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ")
_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("SYNOPSIS"));
help.beginWrap();
help.wrap(_SYS_STR("hecl package [--spec=<spec>] [<input-dir>]\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. ")
help.secHead(_SYS_STR("DESCRIPTION"));
help.beginWrap();
help.wrap(_SYS_STR("This command initiates a packaging pass on the project database. Packaging ")
_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"));
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY)
{
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
help.endWrap();
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!spec->m_factory)
continue;
help.wrap(_SYS_STR(" "));
help.wrapBold(spec->m_name.data());
help.wrap(_SYS_STR("\n"));
}
help.endWrap();
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
help.wrap(_SYS_STR("Specifies a project subdirectory to root the resulting package from. ")
help.secHead(_SYS_STR("OPTIONS"));
help.optionHead(_SYS_STR("<input-dir>"), _SYS_STR("input directory"));
help.beginWrap();
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("gathered and packaged.\n"));
help.endWrap();
_SYS_STR("gathered and packaged.\n"));
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()
{
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();
}
void cancel() { m_useProj->interruptCook(); }
};

View File

@ -4,153 +4,128 @@
#include <cstdio>
#include <map>
class ToolSpec final : public ToolBase
{
enum Mode
{
MLIST = 0,
MENABLE,
MDISABLE
} mode = MLIST;
class ToolSpec final : public ToolBase {
enum Mode { MLIST = 0, MENABLE, MDISABLE } mode = MLIST;
public:
ToolSpec(const ToolPassInfo& info)
: ToolBase(info)
{
if (info.args.empty())
return;
ToolSpec(const ToolPassInfo& info) : ToolBase(info) {
if (info.args.empty())
return;
if (!info.project)
LogModule.report(logvisor::Fatal,
"hecl spec must be ran within a project directory");
if (!info.project)
LogModule.report(logvisor::Fatal, "hecl spec must be ran within a project directory");
const auto& specs = info.project->getDataSpecs();
hecl::SystemString firstArg = info.args.front();
hecl::ToLower(firstArg);
const auto& specs = info.project->getDataSpecs();
hecl::SystemString firstArg = info.args.front();
hecl::ToLower(firstArg);
if (!firstArg.compare(_SYS_STR("enable")))
mode = MENABLE;
else if (!firstArg.compare(_SYS_STR("disable")))
mode = MDISABLE;
if (!firstArg.compare(_SYS_STR("enable")))
mode = MENABLE;
else if (!firstArg.compare(_SYS_STR("disable")))
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
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());
}
hecl::Printf(_SYS_STR("%s\n"), spec->m_name.data());
hecl::Printf(_SYS_STR(" %s\n"), spec->m_desc.data());
}
return 0;
}
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();
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;
}
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
hecl::Printf(_SYS_STR("%s\n"), spec->m_name.data());
hecl::Printf(_SYS_STR(" %s\n"), 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;
}
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;
/*
#define HECL_GIT 1234567
#define HECL_GIT_S "1234567"
@ -44,49 +43,45 @@ bool XTERM_COLOR = false;
*/
/* Main usage message */
static void printHelp(const hecl::SystemChar* pname)
{
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD "HECL" NORMAL ""));
else
hecl::Printf(_SYS_STR("HECL"));
static void printHelp(const hecl::SystemChar* pname) {
if (XTERM_COLOR)
hecl::Printf(_SYS_STR("" BOLD "HECL" NORMAL ""));
else
hecl::Printf(_SYS_STR("HECL"));
#if HECL_HAS_NOD
# define TOOL_LIST "extract|init|cook|package|image|help"
#define TOOL_LIST "extract|init|cook|package|image|help"
#else
# define TOOL_LIST "extract|init|cook|package|help"
#define TOOL_LIST "extract|init|cook|package|help"
#endif
#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
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
hecl::Printf(_SYS_STR("\nUsage: %s " TOOL_LIST "\n"), pname);
hecl::Printf(_SYS_STR("\nUsage: %s " TOOL_LIST "\n"), pname);
#endif
}
/* 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;
/* SIGINT will gracefully close blender connections and delete blends in progress */
static void SIGINTHandler(int sig)
{
if (ToolPtr)
ToolPtr->cancel();
hecl::blender::Connection::Shutdown();
logvisor::KillProcessTree();
exit(1);
static void SIGINTHandler(int sig) {
if (ToolPtr)
ToolPtr->cancel();
hecl::blender::Connection::Shutdown();
logvisor::KillProcessTree();
exit(1);
}
static logvisor::Module AthenaLog("Athena");
static void AthenaExc(athena::error::Level level, const char* file,
const char*, int line, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
AthenaLog.report(logvisor::Level(level), fmt, ap);
va_end(ap);
static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
AthenaLog.report(logvisor::Level(level), fmt, ap);
va_end(ap);
}
static hecl::SystemChar cwdbuf[1024];
@ -100,232 +95,210 @@ static void SIGWINCHHandler(int sig) {}
int main(int argc, const char** argv)
#endif
{
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage")))
{
printf("%s\n", HECL_DLPACKAGE);
return 100;
}
if (argc > 1 && !hecl::StrCmp(argv[1], _SYS_STR("--dlpackage"))) {
printf("%s\n", HECL_DLPACKAGE);
return 100;
}
#if _WIN32
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
#else
std::setlocale(LC_ALL, "en-US.UTF-8");
std::setlocale(LC_ALL, "en-US.UTF-8");
#endif
/* Xterm check */
/* Xterm check */
#if _WIN32
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
XTERM_COLOR = true;
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
XTERM_COLOR = true;
#else
const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5))
XTERM_COLOR = true;
signal(SIGWINCH, SIGWINCHHandler);
const char* term = getenv("TERM");
if (term && !strncmp(term, "xterm", 5))
XTERM_COLOR = true;
signal(SIGWINCH, SIGWINCHHandler);
#endif
signal(SIGINT, SIGINTHandler);
signal(SIGINT, SIGINTHandler);
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc);
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
atSetExceptionHandler(AthenaExc);
/* Basic usage check */
if (argc == 1)
{
printHelp(argv[0]);
/* Basic usage check */
if (argc == 1) {
printHelp(argv[0]);
#if WIN_PAUSE
system("PAUSE");
system("PAUSE");
#endif
return 0;
}
else if (argc == 0)
{
printHelp(_SYS_STR("hecl"));
return 0;
} else if (argc == 0) {
printHelp(_SYS_STR("hecl"));
#if WIN_PAUSE
system("PAUSE");
system("PAUSE");
#endif
return 0;
}
return 0;
}
/* Prepare DataSpecs */
HECLRegisterDataSpecs();
/* Prepare DataSpecs */
HECLRegisterDataSpecs();
/* Assemble common tool pass info */
ToolPassInfo info;
info.pname = argv[0];
if (hecl::Getcwd(cwdbuf, 1024))
{
info.cwd = cwdbuf;
if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\'))
/* Assemble common tool pass info */
ToolPassInfo info;
info.pname = argv[0];
if (hecl::Getcwd(cwdbuf, 1024)) {
info.cwd = cwdbuf;
if (info.cwd.size() && info.cwd.back() != _SYS_STR('/') && info.cwd.back() != _SYS_STR('\\'))
#if _WIN32
info.cwd += _SYS_STR('\\');
info.cwd += _SYS_STR('\\');
#else
info.cwd += _SYS_STR('/');
info.cwd += _SYS_STR('/');
#endif
if (hecl::PathRelative(argv[0]))
ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos)
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
if (hecl::PathRelative(argv[0]))
ExeDir = hecl::SystemString(cwdbuf) + _SYS_STR('/');
hecl::SystemString Argv0(argv[0]);
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_SYS_STR("/\\"));
if (lastIdx != hecl::SystemString::npos)
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 */
std::vector<hecl::SystemString> args;
args.reserve(argc-2);
for (int i=2 ; i<argc ; ++i)
args.push_back(hecl::SystemString(argv[i]));
/* 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;
}
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;
}
/* 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());
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
{
/* Shortcut-case: implicit extract */
fclose(fp);
info.args.insert(info.args.begin(), argv[1]);
tool.reset(new ToolExtract(info));
}
info.flags.push_back(*chit);
}
it = args.erase(it);
}
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
system("PAUSE");
system("PAUSE");
#endif
return 1;
delete newProj;
return 1;
}
project.reset(newProj);
info.project = newProj;
}
if (info.verbosityLevel)
LogModule.report(logvisor::Info, _SYS_STR("Constructed tool '%s' %d\n"),
tool->toolName().c_str(), info.verbosityLevel);
/* Construct selected tool */
hecl::SystemString toolName(argv[1]);
hecl::ToLower(toolName);
std::unique_ptr<ToolBase> tool;
/* Run tool */
ErrorRef = logvisor::ErrorCount;
ToolPtr = tool.get();
int retval = tool->run();
ToolPtr = nullptr;
if (logvisor::ErrorCount > ErrorRef)
{
hecl::blender::Connection::Shutdown();
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 {
/* 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
system("PAUSE");
system("PAUSE");
#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();
#if WIN_PAUSE
system("PAUSE");
#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"
namespace hecl::Backend
{
namespace hecl::Backend {
struct ExtensionSlot;
using IR = Frontend::IR;
@ -11,286 +10,271 @@ using Diagnostics = Frontend::Diagnostics;
using SourceLocation = Frontend::SourceLocation;
using ArithmeticOp = IR::Instruction::ArithmeticOpType;
enum class TexGenSrc : uint8_t
{
Position,
Normal,
UV
enum class TexGenSrc : uint8_t { Position, Normal, UV };
enum class BlendFactor : uint8_t {
Zero,
One,
SrcColor,
InvSrcColor,
DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
Original = 0xff
};
enum class BlendFactor : uint8_t
{
Zero,
One,
SrcColor,
InvSrcColor,
DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
Original = 0xff
enum class ZTest : uint8_t { None, LEqual, Greater, Equal, GEqual, Original = 0xff };
enum class CullMode : uint8_t { None, Backface, Frontface, Original = 0xff };
struct TextureInfo {
TexGenSrc src;
int mapIdx;
int uvIdx;
int mtxIdx;
bool normalize;
};
enum class ZTest : uint8_t
{
None,
LEqual,
Greater,
Equal,
GEqual,
Original = 0xff
};
enum class ReflectionType { None, Simple, Indirect };
enum class CullMode : uint8_t
{
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
{
class IBackend {
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
*/
class ShaderTag : public Hash
{
union
{
uint64_t m_meta = 0;
struct
{
uint8_t m_colorCount;
uint8_t m_uvCount;
uint8_t m_weightCount;
uint8_t m_skinSlotCount;
uint8_t m_primitiveType;
uint8_t m_reflectionType;
bool m_depthTest:1;
bool m_depthWrite:1;
bool m_backfaceCulling:1;
bool m_alphaTest:1;
};
class ShaderTag : public Hash {
union {
uint64_t m_meta = 0;
struct {
uint8_t m_colorCount;
uint8_t m_uvCount;
uint8_t m_weightCount;
uint8_t m_skinSlotCount;
uint8_t m_primitiveType;
uint8_t m_reflectionType;
bool m_depthTest : 1;
bool m_depthWrite : 1;
bool m_backfaceCulling : 1;
bool m_alphaTest : 1;
};
};
public:
ShaderTag() = default;
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,
bool alphaTest)
: Hash(source), m_colorCount(c), 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(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,
bool alphaTest)
: Hash(ir.m_hash), m_colorCount(c), 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 hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest)
: Hash(hashin), m_colorCount(c), 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;}
ShaderTag() = default;
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,
bool alphaTest)
: Hash(source)
, m_colorCount(c)
, 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(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,
bool alphaTest)
: Hash(ir.m_hash)
, m_colorCount(c)
, 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 hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt,
Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling,
bool alphaTest)
: Hash(hashin)
, m_colorCount(c)
, 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> ret;
size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount;
ret.resize(elemCount);
std::vector<boo::VertexElementDescriptor> vertexFormat() const {
std::vector<boo::VertexElementDescriptor> ret;
size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount;
ret.resize(elemCount);
ret[0].semantic = boo::VertexSemantic::Position3;
ret[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2;
ret[0].semantic = boo::VertexSemantic::Position3;
ret[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2;
for (size_t i=0 ; i<m_colorCount ; ++i, ++e)
{
ret[e].semantic = boo::VertexSemantic::ColorUNorm;
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;
for (size_t i = 0; i < m_colorCount; ++i, ++e) {
ret[e].semantic = boo::VertexSemantic::ColorUNorm;
ret[e].semanticIdx = i;
}
boo::AdditionalPipelineInfo additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const;
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,
std::pair<BlendFactor, BlendFactor> blendFactors) const;
};
struct Function
{
std::string_view m_source;
std::string_view m_entry;
Function() = default;
Function(std::string_view source, std::string_view entry)
: m_source(source), m_entry(entry) {}
struct Function {
std::string_view m_source;
std::string_view m_entry;
Function() = default;
Function(std::string_view source, std::string_view entry) : m_source(source), m_entry(entry) {}
};
struct ExtensionSlot
{
Function lighting;
Function post;
size_t blockCount = 0;
const char** blockNames = nullptr;
size_t texCount = 0;
const Backend::TextureInfo* texs = nullptr;
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original;
Backend::ZTest depthTest = Backend::ZTest::Original;
Backend::CullMode cullMode = Backend::CullMode::Backface;
bool noDepthWrite = false;
bool noColorWrite = false;
bool noAlphaWrite = false;
bool noAlphaOverwrite = false;
bool noReflection = false;
bool forceAlphaTest = false;
struct ExtensionSlot {
Function lighting;
Function post;
size_t blockCount = 0;
const char** blockNames = nullptr;
size_t texCount = 0;
const Backend::TextureInfo* texs = nullptr;
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original;
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original;
Backend::ZTest depthTest = Backend::ZTest::Original;
Backend::CullMode cullMode = Backend::CullMode::Backface;
bool noDepthWrite = false;
bool noColorWrite = false;
bool noAlphaWrite = false;
bool noAlphaOverwrite = false;
bool noReflection = false;
bool forceAlphaTest = false;
ExtensionSlot(size_t blockCount = 0,
const char** blockNames = nullptr,
size_t texCount = 0,
const Backend::TextureInfo* texs = nullptr,
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
Backend::ZTest depthTest = Backend::ZTest::Original,
Backend::CullMode cullMode = Backend::CullMode::Backface,
bool noDepthWrite = false,
bool noColorWrite = false,
bool noAlphaWrite = false,
bool noAlphaOverwrite = false,
bool noReflection = false,
bool forceAlphaTest = false)
: blockCount(blockCount), blockNames(blockNames), texCount(texCount), texs(texs),
srcFactor(srcFactor), dstFactor(dstFactor), depthTest(depthTest), cullMode(cullMode),
noDepthWrite(noDepthWrite), noColorWrite(noColorWrite), noAlphaWrite(noAlphaWrite),
noAlphaOverwrite(noAlphaOverwrite), noReflection(noReflection), forceAlphaTest(forceAlphaTest) {}
ExtensionSlot(size_t blockCount = 0, const char** blockNames = nullptr, size_t texCount = 0,
const Backend::TextureInfo* texs = nullptr,
Backend::BlendFactor srcFactor = Backend::BlendFactor::Original,
Backend::BlendFactor dstFactor = Backend::BlendFactor::Original,
Backend::ZTest depthTest = Backend::ZTest::Original,
Backend::CullMode cullMode = Backend::CullMode::Backface, bool noDepthWrite = false,
bool noColorWrite = false, bool noAlphaWrite = false, bool noAlphaOverwrite = false,
bool noReflection = false, bool forceAlphaTest = false)
: blockCount(blockCount)
, blockNames(blockNames)
, texCount(texCount)
, texs(texs)
, srcFactor(srcFactor)
, dstFactor(dstFactor)
, depthTest(depthTest)
, cullMode(cullMode)
, noDepthWrite(noDepthWrite)
, noColorWrite(noColorWrite)
, noAlphaWrite(noAlphaWrite)
, noAlphaOverwrite(noAlphaOverwrite)
, noReflection(noReflection)
, forceAlphaTest(forceAlphaTest) {}
mutable uint64_t m_hash = 0;
void calculateHash() const
{
XXH64_state_t st;
XXH64_reset(&st, 0);
if (!lighting.m_source.empty())
XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size());
if (!post.m_source.empty())
XXH64_update(&st, post.m_source.data(), post.m_source.size());
for (size_t i = 0; i < texCount; ++i)
{
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;
mutable uint64_t m_hash = 0;
void calculateHash() const {
XXH64_state_t st;
XXH64_reset(&st, 0);
if (!lighting.m_source.empty())
XXH64_update(&st, lighting.m_source.data(), lighting.m_source.size());
if (!post.m_source.empty())
XXH64_update(&st, post.m_source.data(), post.m_source.size());
for (size_t i = 0; i < texCount; ++i) {
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;
}
};
inline boo::AdditionalPipelineInfo ShaderTag::additionalInfo(const ExtensionSlot& ext,
std::pair<BlendFactor, BlendFactor> blendFactors) const
{
boo::ZTest zTest;
switch (ext.depthTest)
{
case hecl::Backend::ZTest::Original:
default:
zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None;
break;
case hecl::Backend::ZTest::None:
zTest = boo::ZTest::None;
break;
case hecl::Backend::ZTest::LEqual:
zTest = boo::ZTest::LEqual;
break;
case hecl::Backend::ZTest::Greater:
zTest = boo::ZTest::Greater;
break;
case hecl::Backend::ZTest::Equal:
zTest = boo::ZTest::Equal;
break;
case hecl::Backend::ZTest::GEqual:
zTest = boo::ZTest::GEqual;
break;
}
std::pair<BlendFactor, BlendFactor> blendFactors) const {
boo::ZTest zTest;
switch (ext.depthTest) {
case hecl::Backend::ZTest::Original:
default:
zTest = getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None;
break;
case hecl::Backend::ZTest::None:
zTest = boo::ZTest::None;
break;
case hecl::Backend::ZTest::LEqual:
zTest = boo::ZTest::LEqual;
break;
case hecl::Backend::ZTest::Greater:
zTest = boo::ZTest::Greater;
break;
case hecl::Backend::ZTest::Equal:
zTest = boo::ZTest::Equal;
break;
case hecl::Backend::ZTest::GEqual:
zTest = boo::ZTest::GEqual;
break;
}
return {
boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? blendFactors.first : ext.srcFactor),
boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? blendFactors.second : ext.dstFactor),
getPrimType(), zTest, ext.noDepthWrite ? false : getDepthWrite(),
!ext.noColorWrite, !ext.noAlphaWrite,
(ext.cullMode == hecl::Backend::CullMode::Original) ?
(getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) :
boo::CullMode(ext.cullMode), !ext.noAlphaOverwrite
};
return {boo::BlendFactor((ext.srcFactor == BlendFactor::Original) ? blendFactors.first : ext.srcFactor),
boo::BlendFactor((ext.dstFactor == BlendFactor::Original) ? blendFactors.second : ext.dstFactor),
getPrimType(),
zTest,
ext.noDepthWrite ? false : getDepthWrite(),
!ext.noColorWrite,
!ext.noAlphaWrite,
(ext.cullMode == hecl::Backend::CullMode::Original)
? (getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)
: boo::CullMode(ext.cullMode),
!ext.noAlphaOverwrite};
}
}
} // namespace hecl::Backend
namespace std
{
template <> struct hash<hecl::Backend::ShaderTag>
{
size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept
{return val.valSizeT();}
namespace std {
template <>
struct hash<hecl::Backend::ShaderTag> {
size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept { return val.valSizeT(); }
};
}
} // namespace std

View File

@ -2,51 +2,39 @@
#include "ProgrammableCommon.hpp"
namespace hecl::Backend
{
namespace hecl::Backend {
#define HECL_GLSL_VERT_UNIFORM_BLOCK_NAME "HECLVertUniform"
#define HECL_GLSL_TEXMTX_UNIFORM_BLOCK_NAME "HECLTexMtxUniform"
struct GLSL : ProgrammableCommon
{
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w,
unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames,
bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
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;
struct GLSL : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, 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:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const
{
athena::simd_floats f(vec.simd);
return hecl::Format("vec3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
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
{
return hecl::Format("vec3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
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());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource2(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"
namespace hecl::Backend
{
namespace hecl::Backend {
struct HLSL : ProgrammableCommon
{
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w,
unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames,
bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
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;
struct HLSL : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, 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:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const
{
athena::simd_floats f(vec.simd);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
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
{
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
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());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource2(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"
namespace hecl::Backend
{
namespace hecl::Backend {
struct Metal : ProgrammableCommon
{
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w,
unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest,
ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
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;
struct Metal : ProgrammableCommon {
void reset(const IR& ir, Diagnostics& diag);
std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount,
const TextureInfo* extTexs, ReflectionType reflectionType) const;
std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, 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:
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots) const;
std::string GenerateFragOutStruct() const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const;
std::string GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const;
std::string GenerateVertUniformStruct(unsigned skinSlots) const;
std::string GenerateFragOutStruct() const;
std::string GenerateAlphaTest() const;
std::string GenerateReflectionExpr(ReflectionType type) const;
std::string EmitVec3(const atVec4f& vec) const
{
athena::simd_floats f(vec.simd);
return hecl::Format("float3(%g,%g,%g)", f[0], f[1], f[2]);
}
std::string EmitVec3(const atVec4f& vec) const {
athena::simd_floats f(vec.simd);
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
{
return hecl::Format("float3(%s,%s,%s)", a.c_str(), b.c_str(), c.c_str());
}
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());
}
std::string EmitTexGenSource2(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource4(TexGenSrc src, int uvIdx) const;
std::string EmitTexGenSource2(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 <algorithm>
namespace hecl::Backend
{
namespace hecl::Backend {
struct ProgrammableCommon : IBackend
{
std::string m_colorExpr;
std::string m_alphaExpr;
BlendFactor m_blendSrc;
BlendFactor m_blendDst;
bool m_lighting = false;
struct ProgrammableCommon : IBackend {
std::string m_colorExpr;
std::string m_alphaExpr;
BlendFactor m_blendSrc;
BlendFactor m_blendDst;
bool m_lighting = false;
struct TexSampling
{
int mapIdx = -1;
int tcgIdx = -1;
};
std::vector<TexSampling> m_texSamplings;
unsigned m_texMapEnd = 0;
unsigned m_extMapStart = 8;
struct TexSampling {
int mapIdx = -1;
int tcgIdx = -1;
};
std::vector<TexSampling> m_texSamplings;
unsigned m_texMapEnd = 0;
unsigned m_extMapStart = 8;
struct TexCoordGen
{
TexGenSrc m_src;
int m_uvIdx = 0;
int m_mtx = -1;
bool m_norm = false;
std::string m_gameFunction;
std::vector<atVec4f> m_gameArgs;
};
std::vector<TexCoordGen> m_tcgs;
std::vector<size_t> m_texMtxRefs;
struct TexCoordGen {
TexGenSrc m_src;
int m_uvIdx = 0;
int m_mtx = -1;
bool m_norm = false;
std::string m_gameFunction;
std::vector<atVec4f> m_gameArgs;
};
std::vector<TexCoordGen> m_tcgs;
std::vector<size_t> m_texMtxRefs;
using IBackend::reset;
void reset(const IR& ir, Diagnostics& diag, const char* backendName);
using IBackend::reset;
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:
unsigned addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize);
unsigned addTexSampling(unsigned mapIdx, unsigned tcgIdx);
std::string RecursiveTraceColor(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst, bool toSwizzle);
std::string RecursiveTraceAlpha(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst, bool toSwizzle);
unsigned RecursiveTraceTexGen(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst,
int mtx, bool normalize);
unsigned addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize);
unsigned addTexSampling(unsigned mapIdx, unsigned tcgIdx);
std::string RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, bool toSwizzle);
std::string RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, 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
{
return hecl::Format("sampling%u", samplingIdx);
}
std::string EmitSamplingUseRaw(unsigned samplingIdx) const { return hecl::Format("sampling%u", samplingIdx); }
std::string EmitSamplingUseRGB(unsigned samplingIdx) const
{
return hecl::Format("sampling%u.rgb", samplingIdx);
}
std::string EmitSamplingUseRGB(unsigned samplingIdx) const { return hecl::Format("sampling%u.rgb", samplingIdx); }
std::string EmitSamplingUseAlpha(unsigned samplingIdx) const
{
return hecl::Format("sampling%u.a", samplingIdx);
}
std::string EmitSamplingUseAlpha(unsigned samplingIdx) const { return hecl::Format("sampling%u.a", samplingIdx); }
std::string EmitColorRegUseRaw(unsigned idx) const
{
return hecl::Format("colorReg%u", idx);
}
std::string EmitColorRegUseRaw(unsigned idx) const { return hecl::Format("colorReg%u", idx); }
std::string EmitColorRegUseRGB(unsigned idx) const
{
return hecl::Format("colorReg%u.rgb", idx);
}
std::string EmitColorRegUseRGB(unsigned idx) const { return hecl::Format("colorReg%u.rgb", idx); }
std::string EmitColorRegUseAlpha(unsigned idx) const
{
return hecl::Format("colorReg%u.a", idx);
}
std::string EmitColorRegUseAlpha(unsigned idx) const { return hecl::Format("colorReg%u.a", idx); }
std::string EmitLightingRaw() const
{
return std::string("lighting");
}
std::string EmitLightingRaw() const { return std::string("lighting"); }
std::string EmitLightingRGB() const
{
return std::string("lighting.rgb");
}
std::string EmitLightingRGB() const { return std::string("lighting.rgb"); }
std::string EmitLightingAlpha() const
{
return std::string("lighting.a");
}
std::string EmitLightingAlpha() const { return std::string("lighting.a"); }
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 atVec4f& vec) 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
{
return hecl::Format("%g", val);
}
std::string EmitVal(float val) const { return hecl::Format("%g", val); }
std::string EmitAdd(const std::string& a, const std::string& b) const
{
return '(' + a + '+' + b + ')';
}
std::string EmitAdd(const std::string& a, const std::string& b) const { return '(' + a + '+' + b + ')'; }
std::string EmitSub(const std::string& a, const std::string& b) const
{
return '(' + a + '-' + b + ')';
}
std::string EmitSub(const std::string& a, const std::string& b) const { return '(' + a + '-' + b + ')'; }
std::string EmitMult(const std::string& a, const std::string& b) const
{
return '(' + a + '*' + b + ')';
}
std::string EmitMult(const std::string& a, const std::string& b) const { return '(' + a + '*' + b + ')'; }
std::string EmitDiv(const std::string& a, const std::string& b) const
{
return '(' + a + '/' + b + ')';
}
std::string EmitDiv(const std::string& a, const std::string& b) const { return '(' + a + '/' + b + ')'; }
std::string EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc,
const std::string& a, const atInt8 swiz[4]) const;
std::string EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const;
std::string EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc,
const std::string& a, const atInt8 swiz[4]) const;
std::string EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const;
};
}
} // namespace hecl::Backend

View File

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

View File

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

View File

@ -5,8 +5,7 @@
#undef min
#undef max
namespace hecl
{
namespace hecl {
using namespace std::literals;
@ -18,75 +17,45 @@ using namespace std::literals;
#define DEFAULT_GRAPHICS_API "OpenGL"sv
#endif
struct CVarCommons
{
CVarManager& m_mgr;
CVar* m_graphicsApi = nullptr;
CVar* m_drawSamples = nullptr;
CVar* m_texAnisotropy = nullptr;
CVar* m_deepColor = nullptr;
struct CVarCommons {
CVarManager& m_mgr;
CVar* m_graphicsApi = nullptr;
CVar* m_drawSamples = nullptr;
CVar* m_texAnisotropy = nullptr;
CVar* m_deepColor = nullptr;
CVarCommons(CVarManager& manager) : m_mgr(manager)
{
m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv,
"API to use for rendering graphics"sv,
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, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
m_texAnisotropy = m_mgr.findOrMakeCVar("texAnisotropy"sv,
"Number of anisotropic samples to use for sampling textures"sv,
1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
m_deepColor = m_mgr.findOrMakeCVar("deepColor"sv,
"Allow framebuffer with color depth greater-then 24-bits"sv,
false, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
}
CVarCommons(CVarManager& manager) : m_mgr(manager) {
m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv, "API to use for rendering graphics"sv, 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,
hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive |
hecl::CVar::EFlags::ModifyRestart);
m_texAnisotropy = m_mgr.findOrMakeCVar(
"texAnisotropy"sv, "Number of anisotropic samples to use for sampling textures"sv, 1,
hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
m_deepColor = m_mgr.findOrMakeCVar(
"deepColor"sv, "Allow framebuffer with color depth greater-then 24-bits"sv, false,
hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart);
}
std::string getGraphicsApi() const
{
return m_graphicsApi->toLiteral();
}
std::string getGraphicsApi() const { return m_graphicsApi->toLiteral(); }
void setGraphicsApi(std::string_view api)
{
m_graphicsApi->fromLiteral(api);
}
void setGraphicsApi(std::string_view api) { m_graphicsApi->fromLiteral(api); }
uint32_t getSamples() const
{
return std::max(uint32_t(1), uint32_t(m_drawSamples->toInteger()));
}
uint32_t getSamples() const { return std::max(uint32_t(1), uint32_t(m_drawSamples->toInteger())); }
void setSamples(uint32_t v)
{
m_drawSamples->fromInteger(std::max(uint32_t(1), v));
}
void setSamples(uint32_t v) { m_drawSamples->fromInteger(std::max(uint32_t(1), v)); }
uint32_t getAnisotropy() const
{
return std::max(uint32_t(1), uint32_t(m_texAnisotropy->toInteger()));
}
uint32_t getAnisotropy() const { return std::max(uint32_t(1), uint32_t(m_texAnisotropy->toInteger())); }
void setAnisotropy(uint32_t v)
{
m_texAnisotropy->fromInteger(std::max(uint32_t(1), v));
}
void setAnisotropy(uint32_t v) { m_texAnisotropy->fromInteger(std::max(uint32_t(1), v)); }
bool getDeepColor() const
{
return m_deepColor->toBoolean();
}
bool getDeepColor() const { return m_deepColor->toBoolean(); }
void setDeepColor(bool b)
{
m_deepColor->fromBoolean(b);
}
void setDeepColor(bool b) { m_deepColor->fromBoolean(b); }
void serialize()
{
m_mgr.serialize();
}
void 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 "hecl/SystemChar.hpp"
namespace hecl
{
namespace Runtime
{
namespace hecl {
namespace Runtime {
class FileStoreManager;
}
extern CVar* com_developer;
extern CVar* com_configfile;
extern CVar* com_enableCheats;
class CVarManager final
{
using CVarContainer = DNACVAR::CVarContainer;
template <typename T>
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);
return ret;
}
return nullptr;
class CVarManager final {
using CVarContainer = DNACVAR::CVarContainer;
template <typename T>
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);
return ret;
}
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:
CVarManager() = delete;
CVarManager(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&&) = delete;
CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false);
~CVarManager();
CVarManager() = delete;
CVarManager(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&) = delete;
CVarManager& operator=(const CVarManager&&) = delete;
CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary = false);
~CVarManager();
CVar* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags 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, bool value, CVar::EFlags flags)
{ return _newCVar<bool>(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* newCVar(std::string_view name, std::string_view help, const atVec4f& value, CVar::EFlags 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, bool value, CVar::EFlags flags) {
return _newCVar<bool>(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);
template<class... _Args>
CVar* findOrMakeCVar(std::string_view name, _Args&&... args)
{
if (CVar* cv = findCVar(name))
return cv;
return newCVar(name, std::forward<_Args>(args)...);
}
CVar* findCVar(std::string_view name);
template <class... _Args>
CVar* findOrMakeCVar(std::string_view name, _Args&&... args) {
if (CVar* cv = findCVar(name))
return cv;
return newCVar(name, std::forward<_Args>(args)...);
}
std::vector<CVar*> archivedCVars() const;
std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::None) const;
std::vector<CVar*> archivedCVars() const;
std::vector<CVar*> cvars(CVar::EFlags filter = CVar::EFlags::None) const;
void deserialize(CVar* cvar);
void serialize();
void deserialize(CVar* cvar);
void serialize();
static CVarManager* instance();
static CVarManager* instance();
void list(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 list(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 setDeveloperMode(bool v, bool setDeserialized = false);
bool restartRequired() const;
void setDeveloperMode(bool v, bool setDeserialized=false);
bool restartRequired() const;
void parseCommandLine(const std::vector<SystemString>& args);
void parseCommandLine(const std::vector<SystemString>& args);
private:
bool suppressDeveloper();
void restoreDeveloper(bool oldDeveloper);
bool suppressDeveloper();
void restoreDeveloper(bool oldDeveloper);
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::unique_ptr<CVar>> m_cvars;
std::unordered_map<std::string, std::string> m_deferedCVars;
};
}
} // namespace hecl

View File

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

View File

@ -5,66 +5,87 @@
#include "boo/graphicsdev/D3D.hpp"
#include "boo/graphicsdev/Metal.hpp"
namespace hecl
{
namespace hecl {
namespace PlatformType
{
namespace PlatformType {
using PlatformEnum = boo::IGraphicsDataFactory::Platform;
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
using Context = boo::GLDataFactory::Context;
using Context = boo::GLDataFactory::Context;
#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
using Context = boo::D3D11DataFactory::Context;
using Context = boo::D3D11DataFactory::Context;
#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
using Context = boo::MetalDataFactory::Context;
using Context = boo::MetalDataFactory::Context;
#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
using Context = boo::VulkanDataFactory::Context;
using Context = boo::VulkanDataFactory::Context;
#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
using Context = boo::NXDataFactory::Context;
using Context = boo::NXDataFactory::Context;
#endif
};
}
} // namespace PlatformType
namespace PipelineStage
{
namespace PipelineStage {
using StageEnum = boo::PipelineStage;
struct Null { static constexpr StageEnum Enum = StageEnum::Null; static const char* Name; };
struct Vertex { 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; };
}
struct Null {
static constexpr StageEnum Enum = StageEnum::Null;
static const char* Name;
};
struct Vertex {
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__
using StageBinaryData = std::shared_ptr<uint8_t>;
static inline StageBinaryData MakeStageBinaryData(size_t sz)
{
return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
static inline StageBinaryData MakeStageBinaryData(size_t sz) {
return StageBinaryData(new uint8_t[sz], std::default_delete<uint8_t[]>{});
}
#else
using StageBinaryData = std::shared_ptr<uint8_t[]>;
static inline StageBinaryData MakeStageBinaryData(size_t sz)
{
return StageBinaryData(new uint8_t[sz]);
}
static inline StageBinaryData MakeStageBinaryData(size_t sz) { return StageBinaryData(new uint8_t[sz]); }
#endif
template<typename P, typename S>
template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text);
}
} // namespace hecl

View File

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

View File

@ -21,12 +21,10 @@
#define RUNTIME_ORIGINAL_IDS 0
namespace hecl
{
namespace hecl {
class ClientProcess;
namespace Database
{
namespace Database {
class Project;
extern logvisor::Module LogModule;
@ -34,27 +32,23 @@ extern logvisor::Module LogModule;
/**
* @brief Nodegraph class for gathering dependency-resolved objects for packaging
*/
class PackageDepsgraph
{
class PackageDepsgraph {
public:
struct Node
{
enum class Type
{
Data,
Group
} type;
ProjectPath path;
ProjectPath cookedPath;
class ObjectBase* projectObj;
Node* sub;
Node* next;
};
struct Node {
enum class Type { Data, Group } type;
ProjectPath path;
ProjectPath cookedPath;
class ObjectBase* projectObj;
Node* sub;
Node* next;
};
private:
friend class Project;
std::vector<Node> m_nodes;
friend class Project;
std::vector<Node> m_nodes;
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
* of data for interacting with a specific system/game-engine.
*/
class IDataSpec
{
const DataSpecEntry* m_specEntry;
class IDataSpec {
const DataSpecEntry* m_specEntry;
public:
IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {}
virtual ~IDataSpec() {}
using FCookProgress = std::function<void(const SystemChar*)>;
IDataSpec(const DataSpecEntry* specEntry) : m_specEntry(specEntry) {}
virtual ~IDataSpec() {}
using FCookProgress = std::function<void(const SystemChar*)>;
/**
* @brief Extract Pass Info
*
* An extract pass iterates through a source package or image and
* reverses the cooking process by emitting editable resources
*/
struct ExtractPassInfo
{
SystemString srcpath;
std::vector<SystemString> extractArgs;
bool force;
};
/**
* @brief Extract Pass Info
*
* An extract pass iterates through a source package or image and
* reverses the cooking process by emitting editable resources
*/
struct ExtractPassInfo {
SystemString srcpath;
std::vector<SystemString> extractArgs;
bool force;
};
/**
* @brief Extract Report Representation
*
* Constructed by canExtract() to advise the user of the content about
* to be extracted
*/
struct ExtractReport
{
SystemString name;
SystemString desc;
std::vector<ExtractReport> childOpts;
};
/**
* @brief Extract Report Representation
*
* Constructed by canExtract() to advise the user of the content about
* to be extracted
*/
struct ExtractReport {
SystemString name;
SystemString desc;
std::vector<ExtractReport> childOpts;
};
virtual void setThreadProject() {}
virtual void setThreadProject() {}
virtual bool canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps)
{(void)info;(void)reps;LogModule.report(logvisor::Error, "not implemented");return false;}
virtual void doExtract(const ExtractPassInfo& info, const MultiProgressPrinter& progress)
{(void)info;(void)progress;}
virtual bool canExtract(const ExtractPassInfo& info, std::vector<ExtractReport>& reps) {
(void)info;
(void)reps;
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)
{(void)path;LogModule.report(logvisor::Error, "not implemented");(void)cookPass;return false;}
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path,
const Database::DataSpecEntry* oldEntry,
blender::Token& btok) const
{(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 canCook(const ProjectPath& path, blender::Token& btok, int cookPass = -1) {
(void)path;
LogModule.report(logvisor::Error, "not implemented");
(void)cookPass;
return false;
}
virtual const DataSpecEntry* overrideDataSpec(const ProjectPath& path, const Database::DataSpecEntry* oldEntry,
blender::Token& btok) const {
(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)
{(void)path;return false;}
virtual void doPackage(const ProjectPath& path, const Database::DataSpecEntry* entry,
bool fast, blender::Token& btok, const MultiProgressPrinter& progress,
ClientProcess* cp=nullptr)
{(void)path;}
virtual bool canPackage(const ProjectPath& path) {
(void)path;
return false;
}
virtual void doPackage(const ProjectPath& path, const Database::DataSpecEntry* entry, bool fast, blender::Token& btok,
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
*/
enum class DataSpecTool
{
Extract,
Cook,
Package
};
enum class DataSpecTool { Extract, Cook, Package };
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
*/
struct DataSpecEntry
{
SystemStringView m_name;
SystemStringView m_desc;
SystemStringView m_pakExt;
int m_numCookPasses;
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
struct DataSpecEntry {
SystemStringView m_name;
SystemStringView m_desc;
SystemStringView m_pakExt;
int m_numCookPasses;
std::function<std::unique_ptr<IDataSpec>(Project&, DataSpecTool)> m_factory;
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses),
m_factory(std::move(factory)) {}
DataSpecEntry(SystemStringView name, SystemStringView desc, SystemStringView pakExt, int numCookPasses,
std::function<std::unique_ptr<IDataSpec>(Project& project, DataSpecTool)>&& factory)
: m_name(name), m_desc(desc), m_pakExt(pakExt), m_numCookPasses(numCookPasses), m_factory(std::move(factory)) {}
};
/**
@ -166,79 +168,74 @@ struct DataSpecEntry
*
* DO NOT CONSTRUCT THIS OR SUBCLASSES DIRECTLY!!
*/
class ObjectBase
{
friend class Project;
SystemString m_path;
class ObjectBase {
friend class Project;
SystemString m_path;
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
*/
enum class DataEndianness
{
None,
Big, /**< Big-endian (PowerPC) */
Little /**< Little-endian (Intel) */
};
/**
* @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 */
};
/**
* @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;
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;
}
/**
* @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;
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
* @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 Get a packagable FourCC representation of the object's type
* @return FourCC of the type
*/
virtual FourCC getType() const
{return FourCC("NULL");}
/**
* @brief Get a packagable FourCC representation of the object's type
* @return FourCC of the type
*/
virtual FourCC getType() const { return FourCC("NULL"); }
public:
ObjectBase(SystemStringView path)
: m_path(path) {}
SystemStringView getPath() const {return m_path;}
ObjectBase(SystemStringView path) : m_path(path) {}
SystemStringView getPath() const { return m_path; }
};
/**
* @brief Main project interface
*
@ -246,237 +243,226 @@ public:
* resources in their ideal editor-formats. This interface exposes all
* primary operations to perform on a given project.
*/
class Project
{
class Project {
public:
struct ProjectDataSpec
{
const DataSpecEntry& spec;
ProjectPath cookedPath;
bool active;
};
struct ProjectDataSpec {
const DataSpecEntry& spec;
ProjectPath cookedPath;
bool active;
};
private:
ProjectRootPath m_rootPath;
ProjectPath m_workRoot;
ProjectPath m_dotPath;
ProjectPath m_cookedRoot;
std::vector<ProjectDataSpec> m_compiledSpecs;
std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache;
std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs;
std::unique_ptr<IDataSpec> m_lastPackageSpec;
bool m_valid = false;
ProjectRootPath m_rootPath;
ProjectPath m_workRoot;
ProjectPath m_dotPath;
ProjectPath m_cookedRoot;
std::vector<ProjectDataSpec> m_compiledSpecs;
std::unordered_map<uint64_t, ProjectPath> m_bridgePathCache;
std::vector<std::unique_ptr<IDataSpec>> m_cookSpecs;
std::unique_ptr<IDataSpec> m_lastPackageSpec;
bool m_valid = false;
public:
Project(const ProjectRootPath& rootPath);
operator bool() const {return m_valid;}
Project(const ProjectRootPath& rootPath);
operator bool() const { return m_valid; }
/**
* @brief Configuration file handle
*
* Holds a path to a line-delimited textual configuration file;
* opening a locked handle for read/write transactions
*/
class ConfigFile
{
SystemString m_filepath;
std::vector<std::string> m_lines;
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;
/**
* @brief Configuration file handle
*
* Holds a path to a line-delimited textual configuration file;
* opening a locked handle for read/write transactions
*/
class ConfigFile {
SystemString m_filepath;
std::vector<std::string> m_lines;
FILE* m_lockedFile = NULL;
/**
* @brief A rough description of how 'expensive' a given cook operation is
*
* This is used to provide pretty colors during the cook operation
*/
enum class Cost
{
None,
Light,
Medium,
Heavy
};
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;
/**
* @brief Get the path of the project's root-directory
* @return project root path
*
* Self explanatory
*/
const ProjectRootPath& getProjectRootPath() const {return m_rootPath;}
/**
* @brief A rough description of how 'expensive' a given cook operation is
*
* This is used to provide pretty colors during the cook operation
*/
enum class Cost { None, Light, Medium, Heavy };
/**
* @brief Get the path of project's working directory
* @return project working path
*/
const ProjectPath& getProjectWorkingPath() const {return m_workRoot;}
/**
* @brief Get the path of the project's root-directory
* @return project root path
*
* Self explanatory
*/
const ProjectRootPath& getProjectRootPath() const { return m_rootPath; }
/**
* @brief Get the path of project's cooked directory for a specific DataSpec
* @param DataSpec to retrieve path for
* @return project cooked path
*
* The cooked path matches the directory layout of the working directory
*/
const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
/**
* @brief Get the path of project's working directory
* @return project working path
*/
const ProjectPath& getProjectWorkingPath() const { return m_workRoot; }
/**
* @brief Add given file(s) to the database
* @param path file or pattern within project
* @return true on success
*
* This method blocks while object hashing takes place
*/
bool addPaths(const std::vector<ProjectPath>& paths);
/**
* @brief Get the path of project's cooked directory for a specific DataSpec
* @param DataSpec to retrieve path for
* @return project cooked path
*
* The cooked path matches the directory layout of the working directory
*/
const ProjectPath& getProjectCookedPath(const DataSpecEntry& spec) const;
/**
* @brief Remove a given file or file-pattern from the database
* @param paths file(s) or pattern(s) within project
* @param recursive traverse into matched subdirectories
* @return true on success
*
* This method will not delete actual working files from the project
* directory. It will delete associated cooked objects though.
*/
bool removePaths(const std::vector<ProjectPath>& paths, bool recursive=false);
/**
* @brief Add given file(s) to the database
* @param path file or pattern within project
* @return true on success
*
* This method blocks while object hashing takes place
*/
bool addPaths(const std::vector<ProjectPath>& paths);
/**
* @brief Register a working sub-directory as a Dependency Group
* @param path directory to register as Dependency Group
* @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
* 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 Remove a given file or file-pattern from the database
* @param paths file(s) or pattern(s) within project
* @param recursive traverse into matched subdirectories
* @return true on success
*
* This method will not delete actual working files from the project
* directory. It will delete associated cooked objects though.
*/
bool removePaths(const std::vector<ProjectPath>& paths, bool recursive = false);
/**
* @brief Unregister a working sub-directory as a dependency group
* @param path directory to unregister as Dependency Group
* @return true on success
*/
bool removeGroup(const ProjectPath& path);
/**
* @brief Register a working sub-directory as a Dependency Group
* @param path directory to register as Dependency Group
* @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
* 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
*
* Call periodically in a long-term use of the hecl::Database::Project class.
* Install filesystem event-hooks if possible.
*/
void rescanDataSpecs();
/**
* @brief Unregister a working sub-directory as a dependency group
* @param path directory to unregister as Dependency Group
* @return true on success
*/
bool removeGroup(const ProjectPath& path);
/**
* @brief Return map populated with dataspecs targetable by this project interface
* @return Platform map with name-string keys and enable-status values
*/
const std::vector<ProjectDataSpec>& getDataSpecs() const {return m_compiledSpecs;}
/**
* @brief Re-reads the data store holding user's spec preferences
*
* Call periodically in a long-term use of the hecl::Database::Project class.
* Install filesystem event-hooks if possible.
*/
void rescanDataSpecs();
/**
* @brief Enable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool enableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Return map populated with dataspecs targetable by this project interface
* @return Platform map with name-string keys and enable-status values
*/
const std::vector<ProjectDataSpec>& getDataSpecs() const { return m_compiledSpecs; }
/**
* @brief Disable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool disableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Enable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool enableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Begin cook process for specified directory
* @param path directory of intermediates to cook
* @param feedbackCb a callback to run reporting cook-progress
* @param recursive traverse subdirectories to cook as well
* @param fast enables faster (draft) extraction for supported data types
* @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 Disable persistent user preference for particular spec string(s)
* @param specs String(s) representing unique spec(s) from getDataSpecs
* @return true on success
*/
bool disableDataSpecs(const std::vector<SystemString>& specs);
/**
* @brief Begin package process for specified !world.blend or directory
* @param path Path to !world.blend or directory
* @param feedbackCb a callback to run reporting cook-progress
* @param fast enables faster (draft) extraction for supported data types
* @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,
bool fast=false, const DataSpecEntry* spec=nullptr,
ClientProcess* cp=nullptr);
/**
* @brief Begin cook process for specified directory
* @param path directory of intermediates to cook
* @param feedbackCb a callback to run reporting cook-progress
* @param recursive traverse subdirectories to cook as well
* @param fast enables faster (draft) extraction for supported data types
* @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 Interrupts a cook in progress (call from SIGINT handler)
*
* Database corruption is bad! HECL spreads its data objects through
* the filesystem; this ensures that open objects are cleanly
* finalized or discarded before stopping.
*
* Note that this method returns immediately; the resumed cookPath()
* call will return as quickly as possible.
*/
void interruptCook();
/**
* @brief Begin package process for specified !world.blend or directory
* @param path Path to !world.blend or directory
* @param feedbackCb a callback to run reporting cook-progress
* @param fast enables faster (draft) extraction for supported data types
* @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, bool fast = false,
const DataSpecEntry* spec = nullptr, ClientProcess* cp = nullptr);
/**
* @brief Delete cooked objects for directory
* @param path directory of intermediates to clean
* @param recursive traverse subdirectories to clean as well
* @return true on success
*
* 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);
/**
* @brief Interrupts a cook in progress (call from SIGINT handler)
*
* Database corruption is bad! HECL spreads its data objects through
* the filesystem; this ensures that open objects are cleanly
* finalized or discarded before stopping.
*
* Note that this method returns immediately; the resumed cookPath()
* call will return as quickly as possible.
*/
void interruptCook();
/**
* @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);
/**
* @brief Delete cooked objects for directory
* @param path directory of intermediates to clean
* @param recursive traverse subdirectories to clean as well
* @return true on success
*
* 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 */
void clearBridgePathCache();
/** Add ProjectPath to bridge cache */
void addBridgePathToCache(uint64_t id, const ProjectPath& path);
/** Lookup ProjectPath from bridge cache */
const ProjectPath* lookupBridgePath(uint64_t id) const;
/** Clear all ProjectPaths in bridge cache */
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 "athena/DNA.hpp"
namespace hecl
{
namespace hecl {
/**
* @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
* to remain endian-independent.
*/
class FourCC
{
class FourCC {
protected:
union
{
char fcc[4];
uint32_t num;
};
union {
char fcc[4];
uint32_t num;
};
public:
FourCC() /* Sentinel FourCC */
: num(0) {}
FourCC(const FourCC& other)
{num = other.num;}
FourCC(const char* name)
: num(*(uint32_t*)name) {}
FourCC(uint32_t n)
: num(n) {}
bool operator==(const FourCC& other) const {return num == other.num;}
bool operator!=(const FourCC& other) const {return num != other.num;}
bool operator==(const char* other) const {return num == *(uint32_t*)other;}
bool operator!=(const char* other) const {return num != *(uint32_t*)other;}
bool operator==(int32_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!=(uint32_t other) const {return num != other;}
std::string toString() const {return std::string(fcc, 4);}
uint32_t toUint32() const {return num;}
operator uint32_t() const {return num;}
const char* getChars() const {return fcc;}
char* getChars() {return fcc;}
FourCC() /* Sentinel FourCC */
: num(0) {}
FourCC(const FourCC& other) { num = other.num; }
FourCC(const char* name) : num(*(uint32_t*)name) {}
FourCC(uint32_t n) : num(n) {}
bool operator==(const FourCC& other) const { return num == other.num; }
bool operator!=(const FourCC& other) const { return num != other.num; }
bool operator==(const char* other) const { return num == *(uint32_t*)other; }
bool operator!=(const char* other) const { return num != *(uint32_t*)other; }
bool operator==(int32_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!=(uint32_t other) const { return num != other; }
std::string toString() const { return std::string(fcc, 4); }
uint32_t toUint32() const { return num; }
operator uint32_t() const { return num; }
const char* getChars() const { return fcc; }
char* getChars() { return fcc; }
};
#define FOURCC(chars) FourCC(SBIG(chars))
using BigDNA = athena::io::DNA<athena::Big>;
/** FourCC with DNA read/write */
class DNAFourCC final : public BigDNA, public FourCC
{
class DNAFourCC final : public BigDNA, public FourCC {
public:
DNAFourCC() : FourCC() {}
DNAFourCC(const FourCC& other)
: FourCC() {num = other.toUint32();}
DNAFourCC(const char* name)
: FourCC(name) {}
DNAFourCC(uint32_t n)
: FourCC(n) {}
AT_DECL_EXPLICIT_DNA_YAML
DNAFourCC() : FourCC() {}
DNAFourCC(const FourCC& other) : FourCC() { num = other.toUint32(); }
DNAFourCC(const char* name) : FourCC(name) {}
DNAFourCC(uint32_t n) : FourCC(n) {}
AT_DECL_EXPLICIT_DNA_YAML
};
template <> inline void DNAFourCC::Enumerate<BigDNA::Read>(typename Read::StreamT& r)
{ r.readUBytesToBuf(fcc, 4); }
template <> inline void DNAFourCC::Enumerate<BigDNA::Write>(typename Write::StreamT& w)
{ w.writeUBytes((atUint8*)fcc, 4); }
template <> 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; }
template <>
inline void DNAFourCC::Enumerate<BigDNA::Read>(typename Read::StreamT& r) {
r.readUBytesToBuf(fcc, 4);
}
template <>
inline void DNAFourCC::Enumerate<BigDNA::Write>(typename Write::StreamT& w) {
w.writeUBytes((atUint8*)fcc, 4);
}
template <>
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
{
template <> struct hash<hecl::FourCC>
{
size_t operator()(const hecl::FourCC& val) const noexcept
{return val.toUint32();}
};
}
} // namespace hecl
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 <boo/graphicsdev/IGraphicsDataFactory.hpp>
namespace hecl::Frontend
{
namespace hecl::Frontend {
using namespace std::literals;
struct SourceLocation
{
int line = -1;
int col = -1;
SourceLocation() = default;
SourceLocation(int l, int c) : line(l), col(c) {}
struct SourceLocation {
int line = -1;
int col = -1;
SourceLocation() = default;
SourceLocation(int l, int c) : line(l), col(c) {}
};
class Diagnostics
{
std::string m_name;
std::string m_source;
std::string m_backend = "Backend";
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; }
class Diagnostics {
std::string m_name;
std::string m_source;
std::string m_backend = "Backend";
std::string sourceDiagString(const SourceLocation& l, bool ansi = false) const;
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)
{
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();
std::string_view getName() const { return m_name; }
std::string_view getSource() const { return m_source; }
};
struct IRNode
{
friend struct IR;
struct Token {
enum class Kind { None, Eof, Lf, Plus, Minus, Times, Div, Lpar, Rpar, Comma, Period, Ident, Number };
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::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;
}
}
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;
}
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;
}
}
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) {}
template <class... Args>
void error(const SourceLocation& loc, const char* s, Args&&... args) {
m_diag.reportScannerErr(loc, s, args...);
}
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:
static std::string rep(int n, std::string_view s);
std::string fmt(int level, bool stripUVAnims) const;
std::string describe() const;
static std::string rep(int n, std::string_view s);
std::string fmt(int level, bool stripUVAnims) const;
std::string describe() const;
};
class Parser
{
Scanner m_scanner;
class Parser {
Scanner m_scanner;
Token t;
Token la;
Token::Kind sym;
Token t;
Token la;
Token::Kind sym;
void scan()
{
t = la;
la = m_scanner.next();
sym = la.kind;
}
void scan() {
t = la;
la = m_scanner.next();
sym = la.kind;
}
template <class... Args>
void error(const char* s, Args&&... args)
{
m_scanner.m_diag.reportParserErr(la.loc, s, args...);
}
template <class... Args>
void error(const char* s, Args&&... args) {
m_scanner.m_diag.reportParserErr(la.loc, s, args...);
}
void check(Token::Kind expected);
IRNode call();
static bool imm(const IRNode& a, const IRNode& b);
IRNode expr();
IRNode sum();
IRNode factor();
IRNode value();
void check(Token::Kind expected);
IRNode call();
static bool imm(const IRNode& a, const IRNode& b);
IRNode expr();
IRNode sum();
IRNode factor();
IRNode value();
public:
Parser(Diagnostics& diag)
: m_scanner(diag) {}
Parser(Diagnostics& diag) : m_scanner(diag) {}
void reset(std::string_view in) { la = Token(); m_scanner.reset(in); }
std::list<IRNode> parse();
void reset(std::string_view in) {
la = Token();
m_scanner.reset(in);
}
std::list<IRNode> parse();
};
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
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 */
};
OpType m_op = OpType::None;
RegID m_target = RegID(-1);
SourceLocation m_loc;
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
{
AT_DECL_EXPLICIT_DNA
struct LoadImm : BigDNA {
AT_DECL_DNA
Value<atVec4f> m_immVec = {};
} m_loadImm;
OpType m_op = OpType::None;
RegID m_target = RegID(-1);
SourceLocation m_loc;
enum ArithmeticOpType : uint8_t { None, Add, Subtract, Multiply, Divide };
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 Arithmetic : BigDNA {
AT_DECL_DNA
Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
Value<atUint16> m_instIdxs[2];
} m_arithmetic;
struct LoadImm : BigDNA
{
AT_DECL_DNA
Value<atVec4f> m_immVec = {};
} m_loadImm;
struct Swizzle : BigDNA {
AT_DECL_DNA
Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
Value<atUint16> m_instIdx;
} m_swizzle;
enum ArithmeticOpType : uint8_t
{
None,
Add,
Subtract,
Multiply,
Divide
};
Instruction(OpType type, RegID target, const SourceLocation& loc) : m_op(type), m_target(target), m_loc(loc) {}
int getChildCount() const;
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
const atVec4f& getImmVec() const;
struct Arithmetic : BigDNA
{
AT_DECL_DNA
Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
Value<atUint16> m_instIdxs[2];
} m_arithmetic;
Instruction(athena::io::IStreamReader& reader) { read(reader); }
};
struct Swizzle : BigDNA
{
AT_DECL_DNA
Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
Value<atUint16> m_instIdx;
} m_swizzle;
atUint64 m_hash = 0;
atUint16 m_regCount = 0;
std::vector<Instruction> m_instructions;
Instruction(OpType type, RegID target, const SourceLocation& loc)
: m_op(type), m_target(target), m_loc(loc) {}
int getChildCount() const;
const IR::Instruction& getChildInst(const IR& ir, size_t idx) const;
const atVec4f& getImmVec() const;
boo::BlendFactor m_blendSrc = boo::BlendFactor::One;
boo::BlendFactor m_blendDst = boo::BlendFactor::Zero;
bool m_doAlpha = false;
Instruction(athena::io::IStreamReader& reader) {read(reader);}
};
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);
static atInt8 swizzleCompIdx(char aChar);
int addInstruction(const IRNode& n, IR::RegID target);
};
class Frontend
{
Diagnostics m_diag;
Parser m_parser;
class Frontend {
Diagnostics m_diag;
Parser m_parser;
public:
IR compileSource(std::string_view source, std::string_view diagName);
Diagnostics& getDiagnostics() { return m_diag; }
Frontend() : m_parser(m_diag) {}
IR compileSource(std::string_view source, std::string_view diagName);
Diagnostics& getDiagnostics() { return m_diag; }
Frontend() : m_parser(m_diag) {}
};
}
} // namespace hecl::Frontend

View File

@ -3,30 +3,26 @@
#include "hecl/hecl.hpp"
#include "athena/DNA.hpp"
namespace hecl
{
namespace hecl {
enum class HMDLTopology : atUint32
{
Triangles,
TriStrips,
enum class HMDLTopology : atUint32 {
Triangles,
TriStrips,
};
#define HECL_HMDL_META_SZ 32
struct HMDLMeta : athena::io::DNA<athena::Big>
{
AT_DECL_DNA
Value<atUint32> magic = 'TACO';
Value<HMDLTopology> topology;
Value<atUint32> vertStride;
Value<atUint32> vertCount;
Value<atUint32> indexCount;
Value<atUint32> colorCount;
Value<atUint32> uvCount;
Value<atUint16> weightCount;
Value<atUint16> bankCount;
struct HMDLMeta : athena::io::DNA<athena::Big> {
AT_DECL_DNA
Value<atUint32> magic = 'TACO';
Value<HMDLTopology> topology;
Value<atUint32> vertStride;
Value<atUint32> vertCount;
Value<atUint32> indexCount;
Value<atUint32> colorCount;
Value<atUint32> uvCount;
Value<atUint16> weightCount;
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
/// available.
#ifndef LLVM_GNUC_PREREQ
# if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
# define LLVM_GNUC_PREREQ(maj, min, patch) \
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \
((maj) << 20) + ((min) << 10) + (patch))
# elif defined(__GNUC__) && defined(__GNUC_MINOR__)
# define LLVM_GNUC_PREREQ(maj, min, patch) \
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
# else
# define LLVM_GNUC_PREREQ(maj, min, patch) 0
# endif
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
#define LLVM_GNUC_PREREQ(maj, min, patch) \
((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= ((maj) << 20) + ((min) << 10) + (patch))
#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
#define LLVM_GNUC_PREREQ(maj, min, patch) ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
#else
#define LLVM_GNUC_PREREQ(maj, min, patch) 0
#endif
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#define __has_builtin(x) 0
#endif
#include "hecl.hpp"
@ -61,7 +59,8 @@ enum ZeroBehavior {
};
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) {
if (!Val)
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)
template <typename T> struct TrailingZerosCounter<T, 4> {
template <typename T>
struct TrailingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
@ -101,7 +101,8 @@ template <typename T> struct TrailingZerosCounter<T, 4> {
};
#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) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
@ -128,14 +129,14 @@ template <typename T> struct TrailingZerosCounter<T, 8> {
/// valid arguments.
template <typename T>
std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return detail::TrailingZerosCounter<T, sizeof(T)>::count(Val, ZB);
}
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) {
if (!Val)
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)
template <typename T> struct LeadingZerosCounter<T, 4> {
template <typename T>
struct LeadingZerosCounter<T, 4> {
static std::size_t count(T Val, ZeroBehavior ZB) {
if (ZB != ZB_Undefined && Val == 0)
return 32;
@ -170,7 +172,8 @@ template <typename T> struct LeadingZerosCounter<T, 4> {
};
#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) {
if (ZB != ZB_Undefined && Val == 0)
return 64;
@ -197,8 +200,7 @@ template <typename T> struct LeadingZerosCounter<T, 8> {
/// valid arguments.
template <typename T>
std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
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
/// 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)
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
/// 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)
return std::numeric_limits<T>::max();
// Use ^ instead of - because both gcc and llvm can remove the associated ^
// in the __builtin_clz intrinsic on x86.
return countLeadingZeros(Val, ZB_Undefined) ^
(std::numeric_limits<T>::digits - 1);
return countLeadingZeros(Val, ZB_Undefined) ^ (std::numeric_limits<T>::digits - 1);
}
/// \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 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)
R6(0), R6(2), R6(1), R6(3)
R6(0), R6(2), R6(1), R6(3)
#undef R2
#undef R4
#undef R6
@ -264,33 +267,31 @@ T reverseBits(T Val) {
// ambiguity.
/// Hi_32 - This function returns the high 32 bits of a 64 bit value.
constexpr inline uint32_t Hi_32(uint64_t Value) {
return static_cast<uint32_t>(Value >> 32);
}
constexpr inline uint32_t Hi_32(uint64_t Value) { return static_cast<uint32_t>(Value >> 32); }
/// Lo_32 - This function returns the low 32 bits of a 64 bit value.
constexpr inline uint32_t Lo_32(uint64_t Value) {
return static_cast<uint32_t>(Value);
}
constexpr inline uint32_t Lo_32(uint64_t Value) { return static_cast<uint32_t>(Value); }
/// Make_64 - This functions makes a 64-bit integer from a high / low pair of
/// 32-bit integers.
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
return ((uint64_t)High << 32) | (uint64_t)Low;
}
constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) { return ((uint64_t)High << 32) | (uint64_t)Low; }
/// isInt - Checks if an integer fits into the given bit width.
template <unsigned N> constexpr inline bool isInt(int64_t x) {
return N >= 64 || (-(INT64_C(1)<<(N-1)) <= x && x < (INT64_C(1)<<(N-1)));
template <unsigned N>
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 <> constexpr inline bool isInt<8>(int64_t x) {
template <>
constexpr inline bool isInt<8>(int64_t 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;
}
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;
}
@ -298,8 +299,7 @@ template <> constexpr inline bool isInt<32>(int64_t x) {
/// left by S.
template <unsigned N, unsigned S>
constexpr inline bool isShiftedInt(int64_t x) {
static_assert(
N > 0, "isShiftedInt<0> doesn't make sense (refers to a 0-bit number.");
static_assert(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.");
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
/// left too many places.
template <unsigned N>
constexpr inline typename std::enable_if<(N < 64), bool>::type
isUInt(uint64_t X) {
constexpr inline typename std::enable_if<(N < 64), bool>::type isUInt(uint64_t X) {
static_assert(N > 0, "isUInt<0> doesn't make sense");
return X < (UINT64_C(1) << (N));
}
template <unsigned N>
constexpr inline typename std::enable_if<N >= 64, bool>::type
isUInt(uint64_t X) {
constexpr inline typename std::enable_if<N >= 64, bool>::type isUInt(uint64_t X) {
return true;
}
// 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;
}
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;
}
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;
}
/// Checks if a unsigned integer is an N bit number shifted left by S.
template <unsigned N, unsigned S>
constexpr inline bool isShiftedUInt(uint64_t x) {
static_assert(
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 > 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.");
// Per the two static_asserts above, S must be strictly less than 64. So
// 1 << S is not undefined behavior.
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) {
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.
@ -376,72 +375,50 @@ inline int64_t maxIntN(int64_t N) {
/// isUIntN - Checks if an unsigned integer fits into the given (dynamic)
/// bit width.
inline bool isUIntN(unsigned N, uint64_t x) {
return N >= 64 || x <= maxUIntN(N);
}
inline bool isUIntN(unsigned N, uint64_t x) { return N >= 64 || x <= maxUIntN(N); }
/// isIntN - Checks if an signed integer fits into the given (dynamic)
/// bit width.
inline bool isIntN(unsigned N, int64_t x) {
return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N));
}
inline bool isIntN(unsigned N, int64_t x) { return N >= 64 || (minIntN(N) <= x && x <= maxIntN(N)); }
/// 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
/// zero (32 bit version). Ex. isMask_32(0x0000FFFFU) == true.
constexpr inline bool isMask_32(uint32_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
constexpr inline bool isMask_32(uint32_t Value) { return Value && ((Value + 1) & Value) == 0; }
/// 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
/// zero (64 bit version).
constexpr inline bool isMask_64(uint64_t Value) {
return Value && ((Value + 1) & Value) == 0;
}
constexpr inline bool isMask_64(uint64_t Value) { return Value && ((Value + 1) & Value) == 0; }
/// isShiftedMask_32 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (32 bit version.)
/// Ex. isShiftedMask_32(0x0000FF00U) == true.
constexpr inline bool isShiftedMask_32(uint32_t Value) {
return Value && isMask_32((Value - 1) | Value);
}
constexpr inline bool isShiftedMask_32(uint32_t Value) { return Value && isMask_32((Value - 1) | Value); }
/// isShiftedMask_64 - This function returns true if the argument contains a
/// non-empty sequence of ones with the remainder zero (64 bit version.)
constexpr inline bool isShiftedMask_64(uint64_t Value) {
return Value && isMask_64((Value - 1) | Value);
}
constexpr inline bool isShiftedMask_64(uint64_t Value) { return Value && isMask_64((Value - 1) | Value); }
/// isPowerOf2_32 - This function returns true if the argument is a power of
/// two > 0. Ex. isPowerOf2_32(0x00100000U) == true (32 bit edition.)
constexpr inline bool isPowerOf2_32(uint32_t Value) {
return Value && !(Value & (Value - 1));
}
constexpr inline bool isPowerOf2_32(uint32_t Value) { return Value && !(Value & (Value - 1)); }
/// isPowerOf2_64 - This function returns true if the argument is a power of two
/// > 0 (64 bit edition.)
constexpr inline bool isPowerOf2_64(uint64_t Value) {
return Value && !(Value & (Value - int64_t(1L)));
}
constexpr inline bool isPowerOf2_64(uint64_t Value) { return Value && !(Value & (Value - int64_t(1L))); }
/// ByteSwap_16 - This function returns a byte-swapped representation of the
/// 16-bit argument, Value.
inline uint16_t ByteSwap_16(uint16_t Value) {
return hecl::bswap16(Value);
}
inline uint16_t ByteSwap_16(uint16_t Value) { return hecl::bswap16(Value); }
/// ByteSwap_32 - This function returns a byte-swapped representation of the
/// 32-bit argument, Value.
inline uint32_t ByteSwap_32(uint32_t Value) {
return hecl::bswap32(Value);
}
inline uint32_t ByteSwap_32(uint32_t Value) { return hecl::bswap32(Value); }
/// ByteSwap_64 - This function returns a byte-swapped representation of the
/// 64-bit argument, Value.
inline uint64_t ByteSwap_64(uint64_t Value) {
return hecl::bswap64(Value);
}
inline uint64_t ByteSwap_64(uint64_t Value) { return hecl::bswap64(Value); }
/// \brief Count the number of ones from the most significant bit to the first
/// zero bit.
@ -453,8 +430,7 @@ inline uint64_t ByteSwap_64(uint64_t Value) {
/// ZB_Undefined are valid arguments.
template <typename T>
std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return countLeadingZeros(~Value, ZB);
}
@ -469,14 +445,14 @@ std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
/// ZB_Undefined are valid arguments.
template <typename T>
std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
return countTrailingZeros(~Value, ZB);
}
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) {
// Generic version, forward to 32 bits.
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) {
#if __GNUC__ >= 4
return __builtin_popcountll(Value);
@ -511,8 +488,7 @@ template <typename T> struct PopulationCounter<T, 8> {
/// Returns 0 if the word is zero.
template <typename T>
inline unsigned countPopulation(T Value) {
static_assert(std::numeric_limits<T>::is_integer &&
!std::numeric_limits<T>::is_signed,
static_assert(std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
"Only unsigned integral types are allowed.");
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,
/// -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
inline unsigned Log2_32(uint32_t Value) {
return 31 - countLeadingZeros(Value);
}
inline unsigned Log2_32(uint32_t Value) { return 31 - countLeadingZeros(Value); }
/// Log2_64 - This function returns the floor log base 2 of the specified value,
/// -1 if the value is zero. (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) {
return 63 - countLeadingZeros(Value);
}
inline unsigned Log2_64(uint64_t Value) { return 63 - countLeadingZeros(Value); }
/// Log2_32_Ceil - This function returns the ceil log base 2 of the specified
/// 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
inline unsigned Log2_32_Ceil(uint32_t Value) {
return 32 - countLeadingZeros(Value - 1);
}
inline unsigned Log2_32_Ceil(uint32_t Value) { return 32 - countLeadingZeros(Value - 1); }
/// Log2_64_Ceil - This function returns the ceil log base 2 of the specified
/// value, 64 if the value is zero. (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) {
return 64 - countLeadingZeros(Value - 1);
}
inline unsigned Log2_64_Ceil(uint64_t Value) { return 64 - countLeadingZeros(Value - 1); }
/// GreatestCommonDivisor64 - Return the greatest common divisor of the two
/// 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
/// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8.
inline uintptr_t alignAddr(const void *Addr, size_t Alignment) {
assert(Alignment && isPowerOf2_64((uint64_t)Alignment) &&
"Alignment is not a power of two!");
inline uintptr_t alignAddr(const void* Addr, size_t Alignment) {
assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && "Alignment is not a power of two!");
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
/// 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;
}
@ -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.
/// Essentially, it is a floor operation across the domain of powers of two.
inline uint64_t PowerOf2Floor(uint64_t A) {
if (!A) return 0;
if (!A)
return 0;
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
/// \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");
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
/// or equal to \p Value and is a multiple of \p Align. \p Align must be
/// non-zero.
inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) {
return alignTo(Value, Align) - Value;
}
inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { return alignTo(Value, Align) - Value; }
/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// 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 <= 32, "Bit width out of range.");
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.
/// 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 <= 64, "Bit width out of range.");
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
/// value of the result.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type
AbsoluteDifference(T X, T Y) {
typename std::enable_if<std::is_unsigned<T>::value, T>::type AbsoluteDifference(T X, T 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
/// the result is larger than the maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingAdd(T X, T Y, bool* ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29
T Z = X + 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
/// the result is larger than the maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiply(T X, T Y,
bool* ResultOverflowed = nullptr) {
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
// 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
/// maximum representable value of type T.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, T>::type
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
typename std::enable_if<std::is_unsigned<T>::value, T>::type SaturatingMultiplyAdd(T X, T Y, T A,
bool* ResultOverflowed = nullptr) {
bool Dummy;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
bool& Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
T Product = SaturatingMultiply(X, Y, &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.
extern const float huge_valf;
} // End llvm namespace
} // End hecl namespace
} // namespace llvm
} // namespace hecl

View File

@ -3,57 +3,53 @@
#include "hecl.hpp"
#include <thread>
namespace hecl
{
namespace hecl {
class MultiProgressPrinter
{
std::thread m_logThread;
mutable std::mutex m_logLock;
bool m_newLineAfter;
class MultiProgressPrinter {
std::thread m_logThread;
mutable std::mutex m_logLock;
bool m_newLineAfter;
struct TermInfo
{
struct TermInfo {
#if _WIN32
HANDLE console;
HANDLE console;
#endif
int width;
bool xtermColor = false;
bool truncate = false;
} m_termInfo;
int width;
bool xtermColor = false;
bool truncate = false;
} m_termInfo;
struct ThreadStat
{
hecl::SystemString m_message, m_submessage;
float m_factor = 0.f;
bool m_active = false;
void print(const TermInfo& tinfo) const;
};
mutable std::vector<ThreadStat> m_threadStats;
struct ThreadStat {
hecl::SystemString m_message, m_submessage;
float m_factor = 0.f;
bool m_active = false;
void print(const TermInfo& tinfo) const;
};
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:
MultiProgressPrinter(bool activate = false);
~MultiProgressPrinter();
void print(const hecl::SystemChar* message, const hecl::SystemChar* submessage,
float factor = -1.f, int threadIdx = 0) const;
void setMainFactor(float factor) const;
void setMainIndeterminate(bool indeterminate) const;
void startNewLine() const;
void flush() const;
MultiProgressPrinter(bool activate = false);
~MultiProgressPrinter();
void print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, float factor = -1.f,
int threadIdx = 0) const;
void setMainFactor(float factor) const;
void setMainIndeterminate(bool indeterminate) const;
void startNewLine() const;
void flush() const;
};
}
} // namespace hecl

View File

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

View File

@ -1,33 +1,26 @@
#pragma once
#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
namespace hecl
{
namespace hecl {
using AdditionalPipelineInfo = boo::AdditionalPipelineInfo;
enum class StageTargetType
{
SourceText,
Binary,
Runtime
enum class StageTargetType { SourceText, Binary, Runtime };
enum class PipelineTargetType {
StageSourceTextCollection,
StageBinaryCollection,
StageRuntimeCollection,
FinalPipeline
};
enum class PipelineTargetType
{
StageSourceTextCollection,
StageBinaryCollection,
StageRuntimeCollection,
FinalPipeline
};
template<typename P, typename S>
template <typename P, typename S>
class StageConverter;
template<typename P>
template <typename P>
class PipelineConverter;
#if HECL_RUNTIME
@ -36,200 +29,189 @@ using FactoryCtx = boo::IGraphicsDataFactory::Context;
struct FactoryCtx {};
#endif
template<typename P, typename S>
class StageRep
{
template <typename P, typename S>
class StageRep {
public:
using Platform = P;
using Stage = S;
using Platform = P;
using Stage = S;
};
template<typename P>
class PipelineRep
{
template <typename P>
class PipelineRep {
public:
using Platform = P;
using Platform = P;
};
class GeneralShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
class TessellationShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
template<typename P, typename S>
class StageSourceText : public StageRep<P, S>
{
std::string_view m_text;
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; }
template <typename P, typename S>
class StageSourceText : public StageRep<P, S> {
std::string_view m_text;
uint64_t 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; }
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) : 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>
class StageBinary : public StageRep<P, S>
{
StageBinaryData m_ownedData;
const uint8_t* m_data = nullptr;
size_t m_size = 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; }
template <typename P, typename S>
class StageBinary : public StageRep<P, S> {
StageBinaryData m_ownedData;
const uint8_t* m_data = nullptr;
size_t m_size = 0;
uint64_t m_hash = 0;
StageBinary(const uint8_t* data, size_t size)
: m_data(data), m_size(size)
{ m_hash = XXH64(m_data, m_size, 0); }
StageBinary(StageBinaryData data, size_t size)
: m_ownedData(std::move(data)),
m_data(m_ownedData.get()), m_size(size)
{ m_hash = XXH64(m_data, m_size, 0); }
explicit StageBinary(std::pair<StageBinaryData, size_t> data)
: StageBinary(data.first, data.second) {}
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; }
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) : m_data(data), m_size(size) { m_hash = XXH64(m_data, m_size, 0); }
StageBinary(StageBinaryData data, size_t size)
: m_ownedData(std::move(data)), m_data(m_ownedData.get()), m_size(size) {
m_hash = XXH64(m_data, m_size, 0);
}
explicit StageBinary(std::pair<StageBinaryData, size_t> data) : StageBinary(data.first, data.second) {}
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;
template<typename P>
template <typename P>
class HECLBackend;
template <class T>
using __IsStageSubclass = 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::Geometry>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Control>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Evaluation>, T>>;
using __IsStageSubclass =
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::Geometry>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Control>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Evaluation>, T>>;
template <class T>
inline constexpr bool __IsStageSubclass_v = __IsStageSubclass<T>::value;
template<typename T> class StageCollection;
template<template<typename, typename> class T, typename P, typename... Rest>
class StageCollection<T<P, Rest...>> : public PipelineRep<P>
{
using base = PipelineRep<P>;
friend class FinalPipeline<P>;
static_assert(__IsStageSubclass_v<T<P, PipelineStage::Vertex>>,
"Stage Collection may only be specialized with StageRep subclasses");
T<P, PipelineStage::Vertex> m_vertex;
T<P, PipelineStage::Fragment> m_fragment;
T<P, PipelineStage::Geometry> m_geometry;
T<P, PipelineStage::Control> m_control;
T<P, PipelineStage::Evaluation> m_evaluation;
AdditionalPipelineInfo m_additionalInfo;
std::vector<boo::VertexElementDescriptor> m_vtxFmtData;
boo::VertexFormatInfo m_vtxFmt;
uint64_t m_hash;
template <typename T>
class StageCollection;
template <template <typename, typename> class T, typename P, typename... Rest>
class StageCollection<T<P, Rest...>> : public PipelineRep<P> {
using base = PipelineRep<P>;
friend class FinalPipeline<P>;
static_assert(__IsStageSubclass_v<T<P, PipelineStage::Vertex>>,
"Stage Collection may only be specialized with StageRep subclasses");
T<P, PipelineStage::Vertex> m_vertex;
T<P, PipelineStage::Fragment> m_fragment;
T<P, PipelineStage::Geometry> m_geometry;
T<P, PipelineStage::Control> m_control;
T<P, PipelineStage::Evaluation> m_evaluation;
AdditionalPipelineInfo m_additionalInfo;
std::vector<boo::VertexElementDescriptor> m_vtxFmtData;
boo::VertexFormatInfo m_vtxFmt;
uint64_t m_hash;
public:
static constexpr PipelineTargetType TargetType = T<P, PipelineStage::Vertex>::PipelineTarget;
static constexpr bool HasHash = T<P, PipelineStage::Vertex>::HasHash;
template<typename U = StageCollection<T<P, Rest...>>>
std::enable_if_t<U::HasHash, uint64_t> Hash() const { return m_hash; }
template<typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<!U::HasHash>* = 0) {}
template<typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<U::HasHash>* = 0)
{
m_hash = 0;
m_hash ^= m_vertex.Hash();
m_hash ^= m_fragment.Hash();
m_hash ^= m_geometry.Hash();
m_hash ^= m_control.Hash();
m_hash ^= m_evaluation.Hash();
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
static constexpr PipelineTargetType TargetType = T<P, PipelineStage::Vertex>::PipelineTarget;
static constexpr bool HasHash = T<P, PipelineStage::Vertex>::HasHash;
template <typename U = StageCollection<T<P, Rest...>>>
std::enable_if_t<U::HasHash, uint64_t> Hash() const {
return m_hash;
}
template <typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<!U::HasHash>* = 0) {}
template <typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<U::HasHash>* = 0) {
m_hash = 0;
m_hash ^= m_vertex.Hash();
m_hash ^= m_fragment.Hash();
m_hash ^= m_geometry.Hash();
m_hash ^= m_control.Hash();
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);
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();
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::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);
}
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);
}
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::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) {}
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
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Vertex> \
T<P, hecl::PipelineStage::Vertex>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Fragment> \
T<P, hecl::PipelineStage::Fragment>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Geometry> \
T<P, hecl::PipelineStage::Geometry>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Control> \
T<P, hecl::PipelineStage::Control>::Prototype; \
template <> const hecl::StageBinary<P, hecl::PipelineStage::Evaluation> \
T<P, hecl::PipelineStage::Evaluation>::Prototype;
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Vertex> T<P, hecl::PipelineStage::Vertex>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Fragment> T<P, hecl::PipelineStage::Fragment>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Geometry> T<P, hecl::PipelineStage::Geometry>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Control> T<P, hecl::PipelineStage::Control>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Evaluation> T<P, hecl::PipelineStage::Evaluation>::Prototype;
#else
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P)
#endif
#define STAGEOBJECT_PROTOTYPE_DECLARATIONS(T) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::OpenGL) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Vulkan) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::D3D11) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Metal) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::NX)
#define STAGEOBJECT_PROTOTYPE_DECLARATIONS(T) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::OpenGL) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Vulkan) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::D3D11) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Metal) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::NX)

View File

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

View File

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

View File

@ -18,8 +18,7 @@
#include <cstring>
#include <algorithm>
namespace hecl
{
namespace hecl {
#if _WIN32 && UNICODE
#define HECL_UCS2 1
@ -27,15 +26,13 @@ namespace hecl
#if HECL_UCS2
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_view SystemStringView;
static inline void ToLower(SystemString& str)
{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 ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towlower); }
static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towupper); }
#ifndef _SYS_STR
#define _SYS_STR(val) L ## val
#define _SYS_STR(val) L##val
#endif
#ifndef FMT_CSTR_SYS
#define FMT_CSTR_SYS "S"
@ -43,13 +40,11 @@ static inline void ToUpper(SystemString& str)
typedef struct _stat Sstat;
#else
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_view SystemStringView;
static inline void ToLower(SystemString& str)
{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 ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), tolower); }
static inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), toupper); }
#ifndef _SYS_STR
#define _SYS_STR(val) val
#endif
@ -59,5 +54,4 @@ static inline void ToUpper(SystemString& str)
typedef struct stat Sstat;
#endif
}
} // namespace hecl

View File

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

View File

@ -6,389 +6,356 @@
static logvisor::Module Log("hecl::Backend::GLSL");
namespace hecl::Backend
{
namespace hecl::Backend {
std::string GLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "objPos.xy";
case TexGenSrc::Normal:
return "objNorm.xy";
case TexGenSrc::UV:
return hecl::Format("uvIn[%u]", uvIdx);
default: break;
}
return std::string();
std::string GLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "objPos.xy";
case TexGenSrc::Normal:
return "objNorm.xy";
case TexGenSrc::UV:
return hecl::Format("uvIn[%u]", uvIdx);
default:
break;
}
return std::string();
}
std::string GLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "vec4(objPos.xyz, 1.0)";
case TexGenSrc::Normal:
return "vec4(objNorm.xyz, 1.0)";
case TexGenSrc::UV:
return hecl::Format("vec4(uvIn[%u], 0.0, 1.0)", uvIdx);
default: break;
}
return std::string();
std::string GLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "vec4(objPos.xyz, 1.0)";
case TexGenSrc::Normal:
return "vec4(objNorm.xyz, 1.0)";
case TexGenSrc::UV:
return hecl::Format("vec4(uvIn[%u], 0.0, 1.0)", uvIdx);
default:
break;
}
return std::string();
}
std::string GLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const
{
std::string retval =
"layout(location=0) in vec3 posIn;\n"
"layout(location=1) in vec3 normIn;\n";
std::string GLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
std::string retval =
"layout(location=0) in vec3 posIn;\n"
"layout(location=1) in vec3 normIn;\n";
unsigned idx = 2;
if (col)
{
retval += hecl::Format("layout(location=%u) in vec4 colIn[%u];\n", idx, col);
idx += col;
}
unsigned idx = 2;
if (col) {
retval += hecl::Format("layout(location=%u) in vec4 colIn[%u];\n", idx, col);
idx += col;
}
if (uv)
{
retval += hecl::Format("layout(location=%u) in vec2 uvIn[%u];\n", idx, uv);
idx += uv;
}
if (uv) {
retval += hecl::Format("layout(location=%u) in vec2 uvIn[%u];\n", idx, uv);
idx += uv;
}
if (w)
{
retval += hecl::Format("layout(location=%u) in vec4 weightIn[%u];\n", idx, w);
}
if (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 retval =
"struct VertToFrag\n"
"{\n"
" vec4 mvPos;\n"
" vec4 mvNorm;\n";
std::string GLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
std::string retval =
"struct VertToFrag\n"
"{\n"
" vec4 mvPos;\n"
" vec4 mvNorm;\n";
if (m_tcgs.size())
retval += hecl::Format(" vec2 tcgs[%u];\n", unsigned(m_tcgs.size()));
if (extTexCount)
retval += hecl::Format(" vec2 extTcgs[%u];\n", unsigned(extTexCount));
if (m_tcgs.size())
retval += hecl::Format(" vec2 tcgs[%u];\n", unsigned(m_tcgs.size()));
if (extTexCount)
retval += hecl::Format(" vec2 extTcgs[%u];\n", unsigned(extTexCount));
if (reflectionCoords)
retval += " vec2 reflectTcgs[2];\n"
" float reflectAlpha;\n";
if (reflectionCoords)
retval +=
" vec2 reflectTcgs[2];\n"
" float reflectAlpha;\n";
return retval + "};\n";
return retval + "};\n";
}
std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const
{
std::string retval;
if (skinSlots == 0)
{
retval = "UBINDING0 uniform HECLVertUniform\n"
"{\n"
" mat4 mv;\n"
" mat4 mvInv;\n"
" mat4 proj;\n"
"};\n";
}
std::string GLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const {
std::string retval;
if (skinSlots == 0) {
retval =
"UBINDING0 uniform HECLVertUniform\n"
"{\n"
" mat4 mv;\n"
" mat4 mvInv;\n"
" mat4 proj;\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
{
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 += 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;
}
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";
}
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
{
/* 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 += 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);
}
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;
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";
return retval + "}\n";
}
std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest,
ReflectionType reflectionType, BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting) const
{
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string GLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
else
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
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";
retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\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);
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 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";
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";
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
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";
return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
}
std::string GLSL::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;
else
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 GLSL::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;
else
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 postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
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 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);
uint32_t extTexBits = 0;
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (!(extTexBits & (1 << extTex.mapIdx)))
{
texMapDecl += hecl::Format("TBINDING%u uniform sampler2D extTex%u;\n",
extTex.mapIdx, extTex.mapIdx);
extTexBits |= (1 << extTex.mapIdx);
}
uint32_t extTexBits = 0;
for (int i = 0; i < extTexCount; ++i) {
const TextureInfo& extTex = extTexs[i];
if (!(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("#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(extTexCount, reflectionType != ReflectionType::None) +
(!alphaTest ?
"\n#ifdef GL_ARB_shader_image_load_store\n"
"layout(early_fragment_tests) in;\n"
"#endif\n" : "") +
"\nlayout(location=0) out vec4 colorOut;\n" +
texMapDecl +
"SBINDING(0) in VertToFrag vtf;\n\n" +
lightingSrc + "\n" +
postSrc +
"\nvoid main()\n{\n";
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(extTexCount, reflectionType != ReflectionType::None) +
(!alphaTest ? "\n#ifdef GL_ARB_shader_image_load_store\n"
"layout(early_fragment_tests) in;\n"
"#endif\n"
: "") +
"\nlayout(location=0) out vec4 colorOut;\n" + texMapDecl + "SBINDING(0) in VertToFrag vtf;\n\n" +
lightingSrc + "\n" + postSrc + "\nvoid 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
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";
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
retval += " colorOut = " + postEntry + "(vec4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n";
return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n";
}
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
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");
namespace hecl::Backend
{
namespace hecl::Backend {
std::string HLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "objPos.xy\n";
case TexGenSrc::Normal:
return "objNorm.xy\n";
case TexGenSrc::UV:
return hecl::Format("v.uvIn[%u]", uvIdx);
default: break;
}
return std::string();
std::string HLSL::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "objPos.xy\n";
case TexGenSrc::Normal:
return "objNorm.xy\n";
case TexGenSrc::UV:
return hecl::Format("v.uvIn[%u]", uvIdx);
default:
break;
}
return std::string();
}
std::string HLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Normal:
return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::UV:
return hecl::Format("float4(v.uvIn[%u], 0.0, 1.0)", uvIdx);
default: break;
}
return std::string();
std::string HLSL::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Normal:
return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::UV:
return hecl::Format("float4(v.uvIn[%u], 0.0, 1.0)", uvIdx);
default:
break;
}
return std::string();
}
std::string HLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const
{
std::string retval =
"struct VertData\n"
"{\n"
" float3 posIn : POSITION;\n"
" float3 normIn : NORMAL;\n";
std::string HLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
std::string retval =
"struct VertData\n"
"{\n"
" float3 posIn : POSITION;\n"
" float3 normIn : NORMAL;\n";
if (col)
retval += hecl::Format(" float4 colIn[%u] : COLOR;\n", col);
if (col)
retval += hecl::Format(" float4 colIn[%u] : COLOR;\n", col);
if (uv)
retval += hecl::Format(" float2 uvIn[%u] : UV;\n", uv);
if (uv)
retval += hecl::Format(" float2 uvIn[%u] : UV;\n", uv);
if (w)
retval += hecl::Format(" float4 weightIn[%u] : WEIGHT;\n", w);
if (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 retval =
"struct VertToFrag\n"
"{\n"
" float4 mvpPos : SV_Position;\n"
" float4 mvPos : POSITION;\n"
" float4 mvNorm : NORMAL;\n";
std::string HLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
std::string retval =
"struct VertToFrag\n"
"{\n"
" float4 mvpPos : SV_Position;\n"
" float4 mvPos : POSITION;\n"
" float4 mvNorm : NORMAL;\n";
if (m_tcgs.size())
retval += hecl::Format(" float2 tcgs[%u] : UV;\n", unsigned(m_tcgs.size()));
if (extTexCount)
retval += hecl::Format(" float2 extTcgs[%u] : EXTUV;\n", unsigned(extTexCount));
if (m_tcgs.size())
retval += hecl::Format(" float2 tcgs[%u] : UV;\n", unsigned(m_tcgs.size()));
if (extTexCount)
retval += hecl::Format(" float2 extTcgs[%u] : EXTUV;\n", unsigned(extTexCount));
if (reflectionCoords)
retval += " float2 reflectTcgs[2] : REFLECTUV;\n"
" float reflectAlpha : REFLECTALPHA;\n";
if (reflectionCoords)
retval +=
" 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 retval;
if (skinSlots == 0)
{
retval = "cbuffer HECLVertUniform : register(b0)\n"
"{\n"
" float4x4 mv;\n"
" float4x4 mvInv;\n"
" float4x4 proj;\n"
"};\n";
}
std::string HLSL::GenerateVertUniformStruct(unsigned skinSlots, bool reflectionCoords) const {
std::string retval;
if (skinSlots == 0) {
retval =
"cbuffer HECLVertUniform : register(b0)\n"
"{\n"
" float4x4 mv;\n"
" float4x4 mvInv;\n"
" float4x4 proj;\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
{
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";
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;
}
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";
}
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
{
/* 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 += 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);
}
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;
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 = 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";
return retval +
" return vtf;\n"
"}\n";
}
std::string HLSL::makeFrag(size_t blockCount, const char** blockNames,
bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting) const
{
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string HLSL::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
else
lightingSrc =
"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
lightingSrc = "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";
retval += " float4 lighting = 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";
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);
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
retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n";
}
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";
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 = 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";
return retval + (alphaTest ? GenerateAlphaTest() : "") + " return colorOut;\n}\n";
}
std::string HLSL::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;
else
lightingSrc = "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 HLSL::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;
else
lightingSrc =
"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 postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string postSrc;
if (!post.m_source.empty())
postSrc = post.m_source;
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
std::string postEntry;
if (!post.m_entry.empty())
postEntry = post.m_entry;
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 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);
uint32_t extTexBits = 0;
for (int i=0 ; i<extTexCount ; ++i)
{
const TextureInfo& extTex = extTexs[i];
if (!(extTexBits & (1 << extTex.mapIdx)))
{
texMapDecl += hecl::Format("Texture2D extTex%u : register(t%u);\n",
extTex.mapIdx, extTex.mapIdx);
extTexBits |= (1 << extTex.mapIdx);
}
uint32_t extTexBits = 0;
for (int i = 0; i < extTexCount; ++i) {
const TextureInfo& extTex = extTexs[i];
if (!(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("#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(extTexCount, reflectionType != ReflectionType::None) +
texMapDecl + "\n" +
lightingSrc + "\n" +
postSrc +
(!alphaTest ? "\n[earlydepthstencil]\n" : "\n") +
"float4 main(in VertToFrag vtf) : SV_Target0\n{\n";
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(extTexCount, reflectionType != ReflectionType::None) + texMapDecl +
"\n" + lightingSrc + "\n" + postSrc + (!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
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";
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
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");
namespace hecl::Backend
{
namespace hecl::Backend {
std::string Metal::EmitTexGenSource2(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "objPos.xy\n";
case TexGenSrc::Normal:
return "objNorm.xy\n";
case TexGenSrc::UV:
return hecl::Format("v.uvIn%u", uvIdx);
default: break;
}
return std::string();
std::string Metal::EmitTexGenSource2(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "objPos.xy\n";
case TexGenSrc::Normal:
return "objNorm.xy\n";
case TexGenSrc::UV:
return hecl::Format("v.uvIn%u", uvIdx);
default:
break;
}
return std::string();
}
std::string Metal::EmitTexGenSource4(TexGenSrc src, int uvIdx) const
{
switch (src)
{
case TexGenSrc::Position:
return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Normal:
return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::UV:
return hecl::Format("float4(v.uvIn%u, 0.0, 1.0)", uvIdx);
default: break;
}
return std::string();
std::string Metal::EmitTexGenSource4(TexGenSrc src, int uvIdx) const {
switch (src) {
case TexGenSrc::Position:
return "float4(objPos.xyz, 1.0)\n";
case TexGenSrc::Normal:
return "float4(objNorm.xyz, 1.0)\n";
case TexGenSrc::UV:
return hecl::Format("float4(v.uvIn%u, 0.0, 1.0)", uvIdx);
default:
break;
}
return std::string();
}
std::string Metal::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const
{
std::string retval =
"struct VertData\n"
"{\n"
" float3 posIn [[ attribute(0) ]];\n"
" float3 normIn [[ attribute(1) ]];\n";
std::string Metal::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const {
std::string retval =
"struct VertData\n"
"{\n"
" float3 posIn [[ attribute(0) ]];\n"
" float3 normIn [[ attribute(1) ]];\n";
unsigned idx = 2;
if (col)
{
for (unsigned i=0 ; i<col ; ++i, ++idx)
retval += hecl::Format(" float4 colIn%u [[ attribute(%u) ]];\n", i, idx);
}
unsigned idx = 2;
if (col) {
for (unsigned i = 0; i < col; ++i, ++idx)
retval += hecl::Format(" float4 colIn%u [[ attribute(%u) ]];\n", i, idx);
}
if (uv)
{
for (unsigned i=0 ; i<uv ; ++i, ++idx)
retval += hecl::Format(" float2 uvIn%u [[ attribute(%u) ]];\n", i, idx);
}
if (uv) {
for (unsigned i = 0; i < uv; ++i, ++idx)
retval += hecl::Format(" float2 uvIn%u [[ attribute(%u) ]];\n", i, idx);
}
if (w)
{
for (unsigned i=0 ; i<w ; ++i, ++idx)
retval += hecl::Format(" float4 weightIn%u [[ attribute(%u) ]];\n", i, idx);
}
if (w) {
for (unsigned i = 0; i < w; ++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 retval =
"struct VertToFrag\n"
"{\n"
" float4 mvpPos [[ position ]];\n"
" float4 mvPos;\n"
" float4 mvNorm;\n";
std::string Metal::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const {
std::string retval =
"struct VertToFrag\n"
"{\n"
" float4 mvpPos [[ position ]];\n"
" float4 mvPos;\n"
" float4 mvNorm;\n";
if (m_tcgs.size())
for (size_t i=0 ; i<m_tcgs.size() ; ++i)
retval += hecl::Format(" float2 tcgs%" PRISize ";\n", i);
if (extTexCount)
for (size_t i=0 ; i<extTexCount ; ++i)
retval += hecl::Format(" float2 extTcgs%" PRISize ";\n", i);
if (m_tcgs.size())
for (size_t i = 0; i < m_tcgs.size(); ++i)
retval += hecl::Format(" float2 tcgs%" PRISize ";\n", i);
if (extTexCount)
for (size_t i = 0; i < extTexCount; ++i)
retval += hecl::Format(" float2 extTcgs%" PRISize ";\n", i);
if (reflectionCoords)
retval += " float2 reflectTcgs0;\n"
" float2 reflectTcgs1;\n"
" float reflectAlpha;\n";
if (reflectionCoords)
retval +=
" float2 reflectTcgs0;\n"
" float2 reflectTcgs1;\n"
" float reflectAlpha;\n";
return retval + "};\n";
return retval + "};\n";
}
std::string Metal::GenerateVertUniformStruct(unsigned skinSlots) const
{
std::string retval;
if (skinSlots == 0)
{
retval = "struct HECLVertUniform\n"
"{\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";
}
std::string Metal::GenerateVertUniformStruct(unsigned skinSlots) const {
std::string retval;
if (skinSlots == 0) {
retval =
"struct HECLVertUniform\n"
"{\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";
} 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
{
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;
}
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;
}
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";
}
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
{
/* 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 += 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);
}
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;
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";
return retval + " return vtf;\n}\n";
}
std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest,
ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor,
const Function& lighting) const
{
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType,
BlendFactor srcFactor, BlendFactor dstFactor, const Function& lighting) const {
std::string lightingSrc;
if (!lighting.m_source.empty())
lightingSrc = lighting.m_source;
std::string texMapDecl;
if (m_texMapEnd)
for (int i=0 ; i<m_texMapEnd ; ++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 texMapDecl;
if (m_texMapEnd)
for (int i = 0; i < m_texMapEnd; ++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 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 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(0, reflectionType != ReflectionType::None) + "\n" +
GenerateFragOutStruct() + "\n" +
lightingSrc + "\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";
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(0, reflectionType != ReflectionType::None) + "\n" + GenerateFragOutStruct() + "\n" +
lightingSrc + "\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 (!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 += 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 (m_lighting) {
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";
}
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 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";
}
retval += " float4 lighting = 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);
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);
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())
{
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";
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 <map>
namespace hecl::Backend
{
namespace hecl::Backend {
const char* ProgrammableCommon::BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor)
{
switch (factor)
{
case BlendFactor::Zero:
return "ZERO";
case BlendFactor::One:
return "ONE";
case BlendFactor::SrcColor:
return "SRCCOLOR";
case BlendFactor::InvSrcColor:
return "INVSRCCOLOR";
case BlendFactor::DstColor:
return "DSTCOLOR";
case BlendFactor::InvDstColor:
return "INVDSTCOLOR";
case BlendFactor::SrcAlpha:
return "SRCALPHA";
case BlendFactor::InvSrcAlpha:
return "INVSRCALPHA";
case BlendFactor::DstAlpha:
return "DSTALPHA";
case BlendFactor::InvDstAlpha:
return "INVDSTALPHA";
case BlendFactor::SrcColor1:
return "SRCCOLOR1";
case BlendFactor::InvSrcColor1:
return "INVSRCCOLOR1";
default:
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
const char* ProgrammableCommon::BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
switch (factor) {
case BlendFactor::Zero:
return "ZERO";
case BlendFactor::One:
return "ONE";
case BlendFactor::SrcColor:
return "SRCCOLOR";
case BlendFactor::InvSrcColor:
return "INVSRCCOLOR";
case BlendFactor::DstColor:
return "DSTCOLOR";
case BlendFactor::InvDstColor:
return "INVDSTCOLOR";
case BlendFactor::SrcAlpha:
return "SRCALPHA";
case BlendFactor::InvSrcAlpha:
return "INVSRCALPHA";
case BlendFactor::DstAlpha:
return "DSTALPHA";
case BlendFactor::InvDstAlpha:
return "INVDSTALPHA";
case BlendFactor::SrcColor1:
return "SRCCOLOR1";
case BlendFactor::InvSrcColor1:
return "INVSRCCOLOR1";
default:
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
}
unsigned ProgrammableCommon::addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize)
{
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)
return i;
}
m_tcgs.emplace_back();
TexCoordGen& newTcg = m_tcgs.back();
newTcg.m_src = src;
newTcg.m_uvIdx = uvIdx;
newTcg.m_mtx = mtx;
newTcg.m_norm = normalize;
return m_tcgs.size() - 1;
unsigned ProgrammableCommon::addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize) {
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)
return i;
}
m_tcgs.emplace_back();
TexCoordGen& newTcg = m_tcgs.back();
newTcg.m_src = src;
newTcg.m_uvIdx = uvIdx;
newTcg.m_mtx = mtx;
newTcg.m_norm = normalize;
return m_tcgs.size() - 1;
}
unsigned ProgrammableCommon::addTexSampling(unsigned mapIdx, unsigned tcgIdx)
{
for (unsigned i=0 ; i<m_texSamplings.size() ; ++i)
{
TexSampling& samp = m_texSamplings[i];
if (samp.mapIdx == mapIdx && samp.tcgIdx == tcgIdx)
return i;
}
m_texSamplings.emplace_back();
TexSampling& samp = m_texSamplings.back();
samp.mapIdx = mapIdx;
samp.tcgIdx = tcgIdx;
if (m_texMapEnd < mapIdx + 1)
m_texMapEnd = mapIdx + 1;
return m_texSamplings.size() - 1;
unsigned ProgrammableCommon::addTexSampling(unsigned mapIdx, unsigned tcgIdx) {
for (unsigned i = 0; i < m_texSamplings.size(); ++i) {
TexSampling& samp = m_texSamplings[i];
if (samp.mapIdx == mapIdx && samp.tcgIdx == tcgIdx)
return i;
}
m_texSamplings.emplace_back();
TexSampling& samp = m_texSamplings.back();
samp.mapIdx = mapIdx;
samp.tcgIdx = tcgIdx;
if (m_texMapEnd < mapIdx + 1)
m_texMapEnd = mapIdx + 1;
return m_texSamplings.size() - 1;
}
unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst, int mtx, bool normalize)
{
if (inst.m_op != IR::OpType::Call)
diag.reportBackendErr(inst.m_loc, "TexCoordGen resolution requires function");
unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, int mtx,
bool normalize) {
if (inst.m_op != IR::OpType::Call)
diag.reportBackendErr(inst.m_loc, "TexCoordGen resolution requires function");
const std::string& tcgName = inst.m_call.m_name;
if (!tcgName.compare("UV"))
{
if (inst.getChildCount() < 1)
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
auto& idxImm = idxInst.getImmVec();
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("View"))
return addTexCoordGen(TexGenSrc::Position, -1, mtx, normalize);
const std::string& tcgName = inst.m_call.m_name;
if (!tcgName.compare("UV")) {
if (inst.getChildCount() < 1)
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
auto& idxImm = idxInst.getImmVec();
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("View"))
return addTexCoordGen(TexGenSrc::Position, -1, mtx, normalize);
/* Otherwise treat as game-specific function */
const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0);
unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, m_texMtxRefs.size(),
normalize || tcgName.back() == 'N');
TexCoordGen& tcg = m_tcgs[idx];
m_texMtxRefs.push_back(idx);
tcg.m_gameFunction = tcgName;
tcg.m_gameArgs.clear();
for (int i=1 ; i<inst.getChildCount() ; ++i)
{
const IR::Instruction& ci = inst.getChildInst(ir, i);
tcg.m_gameArgs.push_back(ci.getImmVec());
}
return idx;
/* Otherwise treat as game-specific function */
const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0);
unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, m_texMtxRefs.size(), normalize || tcgName.back() == 'N');
TexCoordGen& tcg = m_tcgs[idx];
m_texMtxRefs.push_back(idx);
tcg.m_gameFunction = tcgName;
tcg.m_gameArgs.clear();
for (int i = 1; i < inst.getChildCount(); ++i) {
const IR::Instruction& ci = inst.getChildInst(ir, i);
tcg.m_gameArgs.push_back(ci.getImmVec());
}
return idx;
}
std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& diag,
const IR::Instruction& inst, bool toSwizzle)
{
switch (inst.m_op)
{
case IR::OpType::Call:
{
const std::string& name = inst.m_call.m_name;
bool normalize = false;
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN")))
{
if (inst.getChildCount() < 2)
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool toSwizzle) {
switch (inst.m_op) {
case IR::OpType::Call: {
const std::string& name = inst.m_call.m_name;
bool normalize = false;
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);
auto& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
auto& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx)) :
EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx));
}
else if (!name.compare("ColorReg"))
{
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseRGB(idx);
}
else if (!name.compare("Lighting"))
{
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingRGB();
}
else if (!name.compare("vec3"))
{
if (inst.getChildCount() < 3)
diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments");
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
const IR::Instruction& cInst = inst.getChildInst(ir, 2);
return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false),
RecursiveTraceAlpha(ir, diag, bInst, false),
RecursiveTraceAlpha(ir, diag, cInst, false));
}
else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
: EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx));
} else if (!name.compare("ColorReg")) {
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseRGB(idx);
} else if (!name.compare("Lighting")) {
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingRGB();
} else if (!name.compare("vec3")) {
if (inst.getChildCount() < 3)
diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments");
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
const IR::Instruction& cInst = inst.getChildInst(ir, 2);
return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false), RecursiveTraceAlpha(ir, diag, bInst, false),
RecursiveTraceAlpha(ir, diag, cInst, false));
} else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
}
case IR::OpType::LoadImm: {
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVec3(vec);
}
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 = 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:
{
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVec3(vec);
case ArithmeticOp::Subtract: {
return EmitSub(aTrace, bTrace);
}
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 = 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 ArithmeticOp::Multiply: {
return EmitMult(aTrace, bTrace);
}
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);
case ArithmeticOp::Divide: {
return EmitDiv(aTrace, bTrace);
}
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,
const IR::Instruction& inst, bool toSwizzle)
{
switch (inst.m_op)
{
case IR::OpType::Call:
{
const std::string& name = inst.m_call.m_name;
bool normalize = false;
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN")))
{
if (inst.getChildCount() < 2)
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
std::string ProgrammableCommon::RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool toSwizzle) {
switch (inst.m_op) {
case IR::OpType::Call: {
const std::string& name = inst.m_call.m_name;
bool normalize = false;
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 atVec4f& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
const atVec4f& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx)) :
EmitSamplingUseAlpha(addTexSampling(mapIdx, texGenIdx));
}
else if (!name.compare("ColorReg"))
{
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseAlpha(idx);
}
else if (!name.compare("Lighting"))
{
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingAlpha();
}
else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
: EmitSamplingUseAlpha(addTexSampling(mapIdx, texGenIdx));
} else if (!name.compare("ColorReg")) {
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseAlpha(idx);
} else if (!name.compare("Lighting")) {
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingAlpha();
} else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
}
case IR::OpType::LoadImm: {
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVal(vec.simd[0]);
}
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:
{
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVal(vec.simd[0]);
case ArithmeticOp::Subtract: {
return EmitSub(aTrace, bTrace);
}
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 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 ArithmeticOp::Multiply: {
return EmitMult(aTrace, bTrace);
}
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);
case ArithmeticOp::Divide: {
return EmitDiv(aTrace, bTrace);
}
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)
{
m_lighting = false;
m_texSamplings.clear();
m_texMapEnd = 0;
m_tcgs.clear();
m_texMtxRefs.clear();
m_colorExpr.clear();
m_alphaExpr.clear();
void ProgrammableCommon::reset(const IR& ir, Diagnostics& diag, const char* backendName) {
m_lighting = false;
m_texSamplings.clear();
m_texMapEnd = 0;
m_tcgs.clear();
m_texMtxRefs.clear();
m_colorExpr.clear();
m_alphaExpr.clear();
diag.setBackend(backendName);
diag.setBackend(backendName);
/* Final instruction is the root call by hecl convention */
const IR::Instruction& rootCall = ir.m_instructions.back();
if (!rootCall.m_call.m_name.compare("HECLOpaque"))
{
m_blendSrc = BlendFactor::One;
m_blendDst = BlendFactor::Zero;
}
else if (!rootCall.m_call.m_name.compare("HECLAlpha"))
{
m_blendSrc = BlendFactor::SrcAlpha;
m_blendDst = BlendFactor::InvSrcAlpha;
}
else if (!rootCall.m_call.m_name.compare("HECLAdditive"))
{
m_blendSrc = BlendFactor::SrcAlpha;
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;
}
/* Final instruction is the root call by hecl convention */
const IR::Instruction& rootCall = ir.m_instructions.back();
if (!rootCall.m_call.m_name.compare("HECLOpaque")) {
m_blendSrc = BlendFactor::One;
m_blendDst = BlendFactor::Zero;
} else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
m_blendSrc = BlendFactor::SrcAlpha;
m_blendDst = BlendFactor::InvSrcAlpha;
} else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
m_blendSrc = BlendFactor::SrcAlpha;
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 */
const IR::Instruction& colorRoot =
ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(0));
m_colorExpr = RecursiveTraceColor(ir, diag, colorRoot, false);
/* Follow Color Chain */
const IR::Instruction& colorRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(0));
m_colorExpr = RecursiveTraceColor(ir, diag, colorRoot, false);
/* Follow Alpha Chain */
if (rootCall.m_call.m_argInstIdxs.size() > 1)
{
const IR::Instruction& alphaRoot =
ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(1));
m_alphaExpr = RecursiveTraceAlpha(ir, diag, alphaRoot, false);
}
/* Follow Alpha Chain */
if (rootCall.m_call.m_argInstIdxs.size() > 1) {
const IR::Instruction& alphaRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(1));
m_alphaExpr = RecursiveTraceAlpha(ir, diag, alphaRoot, false);
}
}
static const char SWIZZLE_CHARS[] = "rgba";
std::string ProgrammableCommon::EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc,
const std::string& a, const atInt8 swiz[4]) const
{
std::string retval = a + '.';
for (int i=0 ; i<3 ; ++i)
{
if (swiz[i] < 0 || swiz[i] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as RGB value");
retval += SWIZZLE_CHARS[swiz[i]];
}
return retval;
std::string ProgrammableCommon::EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const {
std::string retval = a + '.';
for (int i = 0; i < 3; ++i) {
if (swiz[i] < 0 || swiz[i] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as RGB value");
retval += SWIZZLE_CHARS[swiz[i]];
}
return retval;
}
std::string ProgrammableCommon::EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc,
const std::string& a, const atInt8 swiz[4]) const
{
std::string retval = a + '.';
if (swiz[0] < 0 || swiz[0] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as Alpha value");
retval += SWIZZLE_CHARS[swiz[0]];
return retval;
std::string ProgrammableCommon::EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const {
std::string retval = a + '.';
if (swiz[0] < 0 || swiz[0] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as Alpha value");
retval += SWIZZLE_CHARS[swiz[0]];
return retval;
}
}
} // namespace hecl::Backend

File diff suppressed because it is too large Load Diff

View File

@ -5,183 +5,164 @@
#undef min
#undef max
namespace hecl::blender
{
namespace hecl::blender {
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec)
{
atVec3f res;
athena::simd_floats resf;
athena::simd_floats mtxf[3];
for (int i = 0; i < 3; ++i)
mtx[i].simd.copy_to(mtxf[i]);
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[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];
res.simd.copy_from(resf);
return res;
atVec3f MtxVecMul4RM(const Matrix4f& mtx, const Vector3f& vec) {
atVec3f res;
athena::simd_floats resf;
athena::simd_floats mtxf[3];
for (int i = 0; i < 3; ++i)
mtx[i].simd.copy_to(mtxf[i]);
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[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];
res.simd.copy_from(resf);
return res;
}
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec)
{
atVec3f res;
athena::simd_floats resf;
athena::simd_floats mtxf[3];
for (int i = 0; i < 3; ++i)
mtx[i].simd.copy_to(mtxf[i]);
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[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];
res.simd.copy_from(resf);
return res;
atVec3f MtxVecMul3RM(const Matrix4f& mtx, const Vector3f& vec) {
atVec3f res;
athena::simd_floats resf;
athena::simd_floats mtxf[3];
for (int i = 0; i < 3; ++i)
mtx[i].simd.copy_to(mtxf[i]);
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[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];
res.simd.copy_from(resf);
return res;
}
HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const
{
/* If skinned, compute max weight vec count */
size_t weightCount = 0;
for (const SkinBanks::Bank& bank : skinBanks.banks)
weightCount = std::max(weightCount, bank.m_boneIdxs.size());
size_t weightVecCount = weightCount / 4;
if (weightCount % 4)
++weightVecCount;
HMDLBuffers Mesh::getHMDLBuffers(bool absoluteCoords, PoolSkinIndex& poolSkinIndex) const {
/* If skinned, compute max weight vec count */
size_t weightCount = 0;
for (const SkinBanks::Bank& bank : skinBanks.banks)
weightCount = std::max(weightCount, bank.m_boneIdxs.size());
size_t weightVecCount = weightCount / 4;
if (weightCount % 4)
++weightVecCount;
/* Prepare HMDL meta */
HMDLMeta metaOut;
metaOut.topology = topology;
metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4;
metaOut.colorCount = colorLayerCount;
metaOut.uvCount = uvLayerCount;
metaOut.weightCount = weightVecCount;
metaOut.bankCount = skinBanks.banks.size();
/* Prepare HMDL meta */
HMDLMeta metaOut;
metaOut.topology = topology;
metaOut.vertStride = (3 + 3 + colorLayerCount + uvLayerCount * 2 + weightVecCount * 4) * 4;
metaOut.colorCount = colorLayerCount;
metaOut.uvCount = uvLayerCount;
metaOut.weightCount = weightVecCount;
metaOut.bankCount = skinBanks.banks.size();
/* Total all verts from all surfaces (for ibo length) */
size_t boundVerts = 0;
for (const Surface& surf : surfaces)
boundVerts += surf.verts.size();
/* Total all verts from all surfaces (for ibo length) */
size_t boundVerts = 0;
for (const Surface& surf : surfaces)
boundVerts += surf.verts.size();
/* Maintain unique vert pool for VBO */
std::vector<std::pair<const Surface*, const Surface::Vert*>> vertPool;
vertPool.reserve(boundVerts);
/* Maintain unique vert pool for VBO */
std::vector<std::pair<const Surface*, const Surface::Vert*>> vertPool;
vertPool.reserve(boundVerts);
/* Target surfaces representation */
std::vector<HMDLBuffers::Surface> outSurfaces;
outSurfaces.reserve(surfaces.size());
/* Target surfaces representation */
std::vector<HMDLBuffers::Surface> outSurfaces;
outSurfaces.reserve(surfaces.size());
/* Index buffer */
std::vector<atUint32> iboData;
iboData.reserve(boundVerts);
/* Index buffer */
std::vector<atUint32> iboData;
iboData.reserve(boundVerts);
for (const Surface& surf : surfaces)
{
size_t iboStart = iboData.size();
for (const Surface::Vert& v : surf.verts)
{
if (v.iPos == 0xffffffff)
{
iboData.push_back(0xffffffff);
continue;
}
size_t ti = 0;
bool found = false;
for (const std::pair<const Surface*, const Surface::Vert*>& tv : vertPool)
{
if (v == *tv.second && surf.skinBankIdx == tv.first->skinBankIdx)
{
iboData.push_back(ti);
found = true;
break;
}
++ti;
}
if (!found)
{
iboData.push_back(vertPool.size());
vertPool.emplace_back(&surf, &v);
}
for (const Surface& surf : surfaces) {
size_t iboStart = iboData.size();
for (const Surface::Vert& v : surf.verts) {
if (v.iPos == 0xffffffff) {
iboData.push_back(0xffffffff);
continue;
}
size_t ti = 0;
bool found = false;
for (const std::pair<const Surface*, const Surface::Vert*>& tv : vertPool) {
if (v == *tv.second && surf.skinBankIdx == tv.first->skinBankIdx) {
iboData.push_back(ti);
found = true;
break;
}
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();
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]);
}
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;
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);
}
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 <zlib.h>
namespace hecl::blender
{
namespace hecl::blender {
void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block)
{
atUint32 offset = 0;
for (SDNAField& f : fields)
{
const auto& name = block.names[f.name];
f.offset = offset;
if (name.front() == '*')
{
offset += 8;
}
else
{
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;
}
void SDNABlock::SDNAStruct::computeOffsets(const SDNABlock& block) {
atUint32 offset = 0;
for (SDNAField& f : fields) {
const auto& name = block.names[f.name];
f.offset = offset;
if (name.front() == '*') {
offset += 8;
} else {
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
{
for (const SDNAField& field : fields)
{
const auto& name = block.names[field.name];
auto bracket = name.find('[');
if (bracket != std::string::npos)
{
if (!name.compare(0, bracket, n))
return &field;
}
else if (!name.compare(n))
return &field;
}
return nullptr;
const SDNABlock::SDNAStruct::SDNAField* SDNABlock::SDNAStruct::lookupField(const SDNABlock& block,
const char* n) const {
for (const SDNAField& field : fields) {
const auto& name = block.names[field.name];
auto bracket = name.find('[');
if (bracket != std::string::npos) {
if (!name.compare(0, bracket, n))
return &field;
} else if (!name.compare(n))
return &field;
}
return nullptr;
}
const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, int& idx) const
{
idx = 0;
for (const SDNAStruct& strc : strcs)
{
const auto& name = types[strc.type];
if (!name.compare(n))
return &strc;
++idx;
}
return nullptr;
const SDNABlock::SDNAStruct* SDNABlock::lookupStruct(const char* n, int& idx) const {
idx = 0;
for (const SDNAStruct& strc : strcs) {
const auto& name = types[strc.type];
if (!name.compare(n))
return &strc;
++idx;
}
return nullptr;
}
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());
r.seek(12);
while (r.position() < r.length())
{
FileBlock block;
block.read(r);
if (block.type == FOURCC('ENDB'))
break;
athena::io::MemoryReader r2(m_data.data() + r.position(), block.size);
if (!func(block, r2))
break;
r.seek(block.size);
}
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());
r.seek(12);
while (r.position() < r.length()) {
FileBlock block;
block.read(r);
if (block.type == FOURCC('ENDB'))
break;
athena::io::MemoryReader r2(m_data.data() + r.position(), block.size);
if (!func(block, r2))
break;
r.seek(block.size);
}
}
SDNARead::SDNARead(SystemStringView path)
{
athena::io::FileReader r(path);
if (r.hasError())
return;
SDNARead::SDNARead(SystemStringView path) {
athena::io::FileReader r(path);
if (r.hasError())
return;
atUint64 length = r.length();
char magicBuf[7];
r.readUBytesToBuf(magicBuf, 7);
r.seek(0, athena::Begin);
if (strncmp(magicBuf, "BLENDER", 7))
{
/* Try gzip decompression */
std::unique_ptr<uint8_t[]> compBuf(new uint8_t[4096]);
m_data.resize((length * 2 + 4095) & ~4095);
z_stream zstrm = {};
inflateInit2(&zstrm, 16 + MAX_WBITS);
zstrm.next_out = (Bytef*)m_data.data();
zstrm.avail_out = m_data.size();
zstrm.total_out = 0;
atUint64 length = r.length();
char magicBuf[7];
r.readUBytesToBuf(magicBuf, 7);
r.seek(0, athena::Begin);
if (strncmp(magicBuf, "BLENDER", 7)) {
/* Try gzip decompression */
std::unique_ptr<uint8_t[]> compBuf(new uint8_t[4096]);
m_data.resize((length * 2 + 4095) & ~4095);
z_stream zstrm = {};
inflateInit2(&zstrm, 16 + MAX_WBITS);
zstrm.next_out = (Bytef*)m_data.data();
zstrm.avail_out = m_data.size();
zstrm.total_out = 0;
atUint64 rs;
while ((rs = r.readUBytesToBuf(compBuf.get(), 4096)))
{
int inflateRet;
zstrm.next_in = compBuf.get();
zstrm.avail_in = rs;
while (zstrm.avail_in)
{
if (!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;
atUint64 rs;
while ((rs = r.readUBytesToBuf(compBuf.get(), 4096))) {
int inflateRet;
zstrm.next_in = compBuf.get();
zstrm.avail_in = rs;
while (zstrm.avail_in) {
if (!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;
}
inflateEnd(&zstrm);
if (strncmp((char*)m_data.data(), "BLENDER", 7))
{
m_data = std::vector<uint8_t>();
return;
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;
}
}
else
{
m_data.resize(length);
r.readUBytesToBuf(m_data.data(), length);
}
if (inflateRet == Z_STREAM_END)
break;
}
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;
inflateEnd(&zstrm);
if (strncmp((char*)m_data.data(), "BLENDER", 7)) {
m_data = std::vector<uint8_t>();
return;
}
} else {
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 ret;
}
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 ret;
}
}
} // namespace hecl::blender

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

@ -6,436 +6,397 @@
#include <algorithm>
#include <sstream>
namespace hecl
{
namespace hecl {
extern CVar* com_developer;
extern CVar* com_enableCheats;
using namespace std::literals;
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_name = std::string(name);
m_value = std::string(value);
m_defaultValue = std::string(value);
m_help = help;
m_type = type;
m_flags = flags;
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, EType type, EFlags flags,
CVarManager& parent)
: m_mgr(parent) {
m_name = std::string(name);
m_value = std::string(value);
m_defaultValue = std::string(value);
m_help = help;
m_type = type;
m_flags = flags;
}
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, CVar::EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_flags = flags;
m_name = std::string(name);
m_help = help;
m_type = EType::Literal;
CVar::CVar(std::string_view name, std::string_view value, std::string_view help, CVar::EFlags flags,
CVarManager& parent)
: m_mgr(parent) {
m_flags = flags;
m_name = std::string(name);
m_help = help;
m_type = EType::Literal;
// Unlock the cvar for writing if readonly
unlock();
// Unlock the cvar for writing if readonly
unlock();
fromLiteral(value);
m_defaultValue = m_value;
fromLiteral(value);
m_defaultValue = m_value;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
}
CVar::CVar(std::string_view name, const atVec4f& value, std::string_view help, EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_name = std::string(name);
m_help = help;
m_type = EType::Vec4f;
m_flags = flags;
: m_mgr(parent) {
m_name = std::string(name);
m_help = help;
m_type = EType::Vec4f;
m_flags = flags;
// Unlock the cvar for writing if readonly
unlock();
// Unlock the cvar for writing if readonly
unlock();
fromVec4f(value);
m_defaultValue = m_value;
fromVec4f(value);
m_defaultValue = m_value;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
}
CVar::CVar(std::string_view name, float value, std::string_view help, EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_name = std::string(name);
m_help = help;
m_type = EType::Float;
m_flags = flags;
: m_mgr(parent) {
m_name = std::string(name);
m_help = help;
m_type = EType::Float;
m_flags = flags;
// Unlock the cvar for writing if readonly
unlock();
// Unlock the cvar for writing if readonly
unlock();
fromFloat(value);
m_defaultValue = m_value;
fromFloat(value);
m_defaultValue = m_value;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
}
CVar::CVar(std::string_view name, bool value, std::string_view help, CVar::EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_name = std::string(name);
m_help = help;
m_type = EType::Boolean;
m_flags = flags;
: m_mgr(parent) {
m_name = std::string(name);
m_help = help;
m_type = EType::Boolean;
m_flags = flags;
// Unlock the cvar for writing if readonly
unlock();
// Unlock the cvar for writing if readonly
unlock();
fromBoolean(value);
m_defaultValue = m_value;
fromBoolean(value);
m_defaultValue = m_value;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
}
CVar::CVar(std::string_view name, int value, std::string_view help, CVar::EFlags flags, CVarManager& parent)
: m_mgr(parent)
{
m_name = std::string(name);
m_help = help;
m_type = EType::Integer;
m_flags = flags;
: m_mgr(parent) {
m_name = std::string(name);
m_help = help;
m_type = EType::Integer;
m_flags = flags;
// Unlock the cvar for writing if readonly
unlock();
// Unlock the cvar for writing if readonly
unlock();
fromInteger(value);
m_defaultValue = m_value;
fromInteger(value);
m_defaultValue = m_value;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
// Lock the cvar
lock();
// Clear the modified flag, just incase lock didn't do it.
m_flags = flags;
}
std::string CVar::help() const
{
return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") +
(isReadOnly() ? " [ReadOnly]" : ""));
std::string CVar::help() const {
return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") +
(isReadOnly() ? " [ReadOnly]" : ""));
}
atVec4f CVar::toVec4f(bool* isValid) const
{
if (m_type != EType::Vec4f)
{
if (isValid != nullptr)
*isValid = false;
return atVec4f{};
}
atVec4f CVar::toVec4f(bool* isValid) const {
if (m_type != EType::Vec4f) {
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;
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);
return vec;
return fromVec4f(vec);
}
}
if (setDefault)
m_value = m_defaultValue;
return false;
}
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::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::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;
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::isModified() const { return int(m_flags & EFlags::Modified) != 0; }
bool CVar::modificationRequiresRestart() const { return int(m_flags & EFlags::ModifyRestart) != 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; }
void CVar::clearModified()
{
if (!modificationRequiresRestart())
m_flags &= ~EFlags::Modified;
void CVar::clearModified() {
if (!modificationRequiresRestart())
m_flags &= ~EFlags::Modified;
}
void CVar::setModified() { m_flags |= EFlags::Modified; }
void CVar::unlock()
{
if (isReadOnly() && !m_unlocked)
{
m_oldFlags = m_flags;
m_flags &= ~EFlags::ReadOnly;
m_unlocked = true;
}
void CVar::unlock() {
if (isReadOnly() && !m_unlocked) {
m_oldFlags = m_flags;
m_flags &= ~EFlags::ReadOnly;
m_unlocked = true;
}
}
void CVar::lock()
{
if (!isReadOnly() && m_unlocked)
{
m_flags = m_oldFlags;
m_unlocked = false;
}
void CVar::lock() {
if (!isReadOnly() && m_unlocked) {
m_flags = m_oldFlags;
m_unlocked = false;
}
}
void CVar::dispatch()
{
for (const ListenerFunc& listen : m_listeners)
listen(this);
void CVar::dispatch() {
for (const ListenerFunc& listen : m_listeners)
listen(this);
}
}
} // namespace hecl

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

@ -7,8 +7,7 @@
#include <memory>
#include <regex>
namespace hecl
{
namespace hecl {
CVar* com_developer = nullptr;
CVar* com_configfile = nullptr;
@ -19,333 +18,289 @@ CVarManager* CVarManager::m_instance = nullptr;
static logvisor::Module CVarLog("CVarManager");
CVarManager::CVarManager(hecl::Runtime::FileStoreManager& store, bool useBinary)
: m_store(store),
m_useBinary(useBinary)
{
m_instance = this;
com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System);
com_developer = newCVar("developer", "Enables developer mode", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::InternalArchivable));
com_enableCheats = newCVar("cheats", "Enable cheats", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::Hidden));
: m_store(store), m_useBinary(useBinary) {
m_instance = this;
com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System);
com_developer = newCVar("developer", "Enables developer mode", false,
(CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::InternalArchivable));
com_enableCheats =
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)
{
std::string tmp(cvar->name());
athena::utility::tolower(tmp);
if (m_cvars.find(tmp) != m_cvars.end())
return nullptr;
CVar* CVarManager::findCVar(std::string_view name) {
std::string lower(name);
athena::utility::tolower(lower);
auto search = m_cvars.find(lower);
if (search == m_cvars.end())
return nullptr;
CVar* ret = cvar.get();
m_cvars[tmp] = std::move(cvar);
return ret;
return search->second.get();
}
CVar* CVarManager::findCVar(std::string_view name)
{
std::string lower(name);
athena::utility::tolower(lower);
auto search = m_cvars.find(lower);
if (search == m_cvars.end())
return nullptr;
std::vector<CVar*> CVarManager::archivedCVars() const {
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (pair.second->isArchive())
ret.push_back(pair.second.get());
return search->second.get();
return ret;
}
std::vector<CVar*> CVarManager::archivedCVars() const
{
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (pair.second->isArchive())
ret.push_back(pair.second.get());
std::vector<CVar*> CVarManager::cvars(CVar::EFlags filter) const {
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (filter == CVar::EFlags::None || (pair.second->flags() & filter) != 0)
ret.push_back(pair.second.get());
return ret;
return ret;
}
std::vector<CVar*> CVarManager::cvars(CVar::EFlags filter) const
{
std::vector<CVar*> ret;
for (const auto& pair : m_cvars)
if (filter == CVar::EFlags::None || (pair.second->flags() & filter) != 0)
ret.push_back(pair.second.get());
void CVarManager::deserialize(CVar* cvar) {
if (!cvar)
return;
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)
{
if (!cvar)
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;
/* We were either unable to find a deferred value or got an invalid value */
if (!cvar->isArchive() && !cvar->isInternalArchivable())
return;
#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
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
hecl::Sstat st;
hecl::Sstat st;
if (m_useBinary)
{
CVarContainer container;
filename += _SYS_STR(".bin");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen())
container.read(reader);
if (m_useBinary) {
CVarContainer container;
filename += _SYS_STR(".bin");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen())
container.read(reader);
if (container.cvars.size() > 0)
{
auto serialized = std::find_if(container.cvars.begin(),
container.cvars.end(),
[&cvar](const DNACVAR::CVar& c) { return c.m_name == cvar->name(); });
if (container.cvars.size() > 0) {
auto serialized = std::find_if(container.cvars.begin(), container.cvars.end(),
[&cvar](const DNACVAR::CVar& c) { return c.m_name == cvar->name(); });
if (serialized != container.cvars.end())
{
DNACVAR::CVar& tmp = *serialized;
if (serialized != container.cvars.end()) {
DNACVAR::CVar& tmp = *serialized;
if (cvar->m_value != tmp.m_value)
{
cvar->unlock();
cvar->fromLiteralToType(tmp.m_value, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
}
if (cvar->m_value != tmp.m_value) {
cvar->unlock();
cvar->fromLiteralToType(tmp.m_value, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
}
}
else
{
filename += _SYS_STR(".yaml");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen())
{
athena::io::YAMLDocReader docReader;
if (docReader.parse(&reader))
{
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(); });
} else {
filename += _SYS_STR(".yaml");
if (hecl::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode))
return;
athena::io::FileReader reader(filename);
if (reader.isOpen()) {
athena::io::YAMLDocReader docReader;
if (docReader.parse(&reader)) {
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())
{
const std::unique_ptr<athena::io::YAMLNode>& tmp = serialized->second;
if (serialized != root->m_mapChildren.end()) {
const std::unique_ptr<athena::io::YAMLNode>& tmp = serialized->second;
if (cvar->m_value != tmp->m_scalarString)
{
cvar->unlock();
cvar->fromLiteralToType(tmp->m_scalarString, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
}
}
if (cvar->m_value != tmp->m_scalarString) {
cvar->unlock();
cvar->fromLiteralToType(tmp->m_scalarString, true);
cvar->m_wasDeserialized = true;
cvar->lock();
}
}
}
}
}
}
void CVarManager::serialize()
{
void CVarManager::serialize() {
#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
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
if (m_useBinary)
{
CVarContainer container;
for (const auto& pair : m_cvars)
if (pair.second->isArchive() || (pair.second->isInternalArchivable() &&
pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
container.cvars.push_back(*pair.second);
container.cvarCount = atUint32(container.cvars.size());
if (m_useBinary) {
CVarContainer container;
for (const auto& pair : m_cvars)
if (pair.second->isArchive() ||
(pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
container.cvars.push_back(*pair.second);
container.cvarCount = atUint32(container.cvars.size());
filename += _SYS_STR(".bin");
athena::io::FileWriter writer(filename);
if (writer.isOpen())
container.write(writer);
}
else
{
filename += _SYS_STR(".yaml");
filename += _SYS_STR(".bin");
athena::io::FileWriter writer(filename);
if (writer.isOpen())
container.write(writer);
} else {
filename += _SYS_STR(".yaml");
athena::io::FileReader r(filename);
athena::io::YAMLDocWriter docWriter(nullptr, r.isOpen() ? &r : nullptr);
r.close();
athena::io::FileReader r(filename);
athena::io::YAMLDocWriter docWriter(nullptr, r.isOpen() ? &r : nullptr);
r.close();
docWriter.setStyle(athena::io::YAMLNodeStyle::Block);
for (const auto& pair : m_cvars)
if (pair.second->isArchive() || (pair.second->isInternalArchivable() &&
pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
docWriter.writeString(pair.second->name().data(), pair.second->toLiteral());
docWriter.setStyle(athena::io::YAMLNodeStyle::Block);
for (const auto& pair : m_cvars)
if (pair.second->isArchive() ||
(pair.second->isInternalArchivable() && pair.second->wasDeserialized() && !pair.second->hasDefaultValue()))
docWriter.writeString(pair.second->name().data(), pair.second->toLiteral());
athena::io::FileWriter w(filename);
if (w.isOpen())
docWriter.finish(&w);
}
athena::io::FileWriter w(filename);
if (w.isOpen())
docWriter.finish(&w);
}
}
CVarManager* CVarManager::instance()
{
return m_instance;
CVarManager* CVarManager::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*/)
{
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::setCVar(Console* con, const std::vector<std::string>& args) {
if (args.size() < 2) {
con->report(Console::Level::Info, "Usage setCvar <cvar> <value>");
return;
}
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)
{
if (args.size() < 2)
{
con->report(Console::Level::Info, "Usage setCvar <cvar> <value>");
return;
}
void CVarManager::getCVar(Console* con, const std::vector<std::string>& args) {
if (args.empty()) {
con->report(Console::Level::Info, "Usage getCVar <cvar>");
return;
}
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;
}
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());
const auto& cv = m_cvars[cvName];
con->report(Console::Level::Info, "'%s' = '%s'", cv->name().data(), cv->value().c_str());
}
void CVarManager::getCVar(Console* con, const std::vector<std::string> &args)
{
if (args.empty())
{
con->report(Console::Level::Info, "Usage getCVar <cvar>");
return;
}
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) {
com_developer->unlock();
com_developer->fromBoolean(v);
if (setDeserialized)
com_developer->m_wasDeserialized = true;
com_developer->lock();
com_developer->setModified();
}
void CVarManager::setDeveloperMode(bool v, bool setDeserialized)
{
com_developer->unlock();
com_developer->fromBoolean(v);
if (setDeserialized)
com_developer->m_wasDeserialized = true;
com_developer->lock();
com_developer->setModified();
bool CVarManager::restartRequired() const {
for (const auto& cv : m_cvars) {
if (cv.second->isModified() && cv.second->modificationRequiresRestart())
return true;
}
return false;
}
bool CVarManager::restartRequired() const
{
for (const auto& cv : m_cvars)
{
if (cv.second->isModified() && cv.second->modificationRequiresRestart())
return true;
}
void CVarManager::parseCommandLine(const std::vector<SystemString>& args) {
bool oldDeveloper = suppressDeveloper();
std::string developerName = com_developer->name().data();
athena::utility::tolower(developerName);
for (const SystemString& arg : args) {
if (arg[0] == _SYS_STR('+')) {
std::string tmp = SystemUTF8Conv(arg).c_str();
return false;
}
void CVarManager::parseCommandLine(const std::vector<SystemString>& args)
{
bool oldDeveloper = suppressDeveloper();
std::string developerName = com_developer->name().data();
athena::utility::tolower(developerName);
for (const SystemString& arg : args)
{
if (arg[0] == _SYS_STR('+'))
{
std::string tmp = SystemUTF8Conv(arg).c_str();
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;
}
}
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 oldDeveloper = com_developer->toBoolean();
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(true);
bool CVarManager::suppressDeveloper() {
bool oldDeveloper = com_developer->toBoolean();
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(true);
return oldDeveloper;
return oldDeveloper;
}
void CVarManager::restoreDeveloper(bool oldDeveloper)
{
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(oldDeveloper);
void CVarManager::restoreDeveloper(bool oldDeveloper) {
CVarUnlocker unlock(com_developer);
com_developer->fromBoolean(oldDeveloper);
}
}
} // namespace hecl

View File

@ -13,225 +13,192 @@
#define HECL_MULTIPROCESSOR 1
namespace hecl
{
namespace hecl {
static logvisor::Module CP_Log("hecl::ClientProcess");
ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker;
static int GetCPUCount()
{
static int GetCPUCount() {
#if _WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#else
return sysconf(_SC_NPROCESSORS_ONLN);
return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}
void ClientProcess::BufferTransaction::run(blender::Token& btok)
{
athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
if (r.hasError())
{
CP_Log.report(logvisor::Fatal, _SYS_STR("unable to background-buffer '%s'"),
m_path.getAbsolutePath().data());
return;
void ClientProcess::BufferTransaction::run(blender::Token& btok) {
athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
if (r.hasError()) {
CP_Log.report(logvisor::Fatal, _SYS_STR("unable to background-buffer '%s'"), m_path.getAbsolutePath().data());
return;
}
if (m_offset)
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)
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;
}
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);
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;
}
lk.unlock();
m_blendTok.shutdown();
m_proc.m_waitCv.notify_one();
if (!m_proc.m_running)
break;
m_proc.m_cv.wait(lk);
}
lk.unlock();
m_blendTok.shutdown();
}
ClientProcess::ClientProcess(const MultiProgressPrinter* progPrinter)
: m_progPrinter(progPrinter)
{
ClientProcess::ClientProcess(const MultiProgressPrinter* progPrinter) : m_progPrinter(progPrinter) {
#if HECL_MULTIPROCESSOR
const int cpuCount = GetCPUCount();
const int cpuCount = GetCPUCount();
#else
constexpr int cpuCount = 1;
constexpr int cpuCount = 1;
#endif
m_workers.reserve(cpuCount);
for (int i=0 ; i<cpuCount ; ++i)
{
std::unique_lock<std::mutex> lk(m_mutex);
m_workers.emplace_back(*this, m_workers.size());
m_initCv.wait(lk);
}
m_workers.reserve(cpuCount);
for (int i = 0; i < cpuCount; ++i) {
std::unique_lock<std::mutex> lk(m_mutex);
m_workers.emplace_back(*this, m_workers.size());
m_initCv.wait(lk);
}
}
std::shared_ptr<const ClientProcess::BufferTransaction>
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
size_t maxLen, size_t offset)
{
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
return ret;
std::shared_ptr<const ClientProcess::BufferTransaction> ClientProcess::addBufferTransaction(const ProjectPath& path,
void* target, size_t maxLen,
size_t offset) {
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
return ret;
}
std::shared_ptr<const ClientProcess::CookTransaction>
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, bool force,
bool fast, Database::IDataSpec* spec)
{
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<CookTransaction>(*this, path, force, fast, spec);
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
++m_addedCooks;
m_progPrinter->setMainFactor(m_completedCooks / float(m_addedCooks));
return ret;
std::shared_ptr<const ClientProcess::CookTransaction> ClientProcess::addCookTransaction(const hecl::ProjectPath& path,
bool force, bool fast,
Database::IDataSpec* spec) {
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<CookTransaction>(*this, path, force, fast, spec);
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
++m_addedCooks;
m_progPrinter->setMainFactor(m_completedCooks / float(m_addedCooks));
return ret;
}
std::shared_ptr<const ClientProcess::LambdaTransaction>
ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func)
{
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
return ret;
ClientProcess::addLambdaTransaction(std::function<void(blender::Token&)>&& func) {
std::unique_lock<std::mutex> lk(m_mutex);
auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
m_pendingQueue.emplace_back(ret);
m_cv.notify_one();
return ret;
}
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok,
bool force, bool fast)
{
if (spec->canCook(path, btok))
{
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
if (specEnt)
{
hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
if (fast)
cooked = cooked.getWithExtension(_SYS_STR(".fast"));
cooked.makeDirChain(false);
if (force || cooked.getPathType() == ProjectPath::Type::None ||
path.getModtime() > cooked.getModtime())
{
if (m_progPrinter)
{
hecl::SystemString str;
if (path.getAuxInfo().empty())
str = hecl::SysFormat(_SYS_STR("Cooking %s"), path.getRelativePath().data());
else
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());
m_progPrinter->flush();
}
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;
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, blender::Token& btok, bool force,
bool fast) {
if (spec->canCook(path, btok)) {
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
if (specEnt) {
hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
if (fast)
cooked = cooked.getWithExtension(_SYS_STR(".fast"));
cooked.makeDirChain(false);
if (force || cooked.getPathType() == ProjectPath::Type::None || path.getModtime() > cooked.getModtime()) {
if (m_progPrinter) {
hecl::SystemString str;
if (path.getAuxInfo().empty())
str = hecl::SysFormat(_SYS_STR("Cooking %s"), path.getRelativePath().data());
else
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());
m_progPrinter->flush();
} 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;
}
return false;
}
return false;
}
void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue)
{
std::unique_lock<std::mutex> lk(m_mutex);
queue.swap(m_completedQueue);
void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue) {
std::unique_lock<std::mutex> lk(m_mutex);
queue.swap(m_completedQueue);
}
void ClientProcess::waitUntilComplete()
{
std::unique_lock<std::mutex> lk(m_mutex);
while (isBusy())
m_waitCv.wait(lk);
void ClientProcess::waitUntilComplete() {
std::unique_lock<std::mutex> lk(m_mutex);
while (isBusy())
m_waitCv.wait(lk);
}
void ClientProcess::shutdown()
{
if (!m_running)
return;
std::unique_lock<std::mutex> lk(m_mutex);
m_pendingQueue.clear();
m_running = false;
m_cv.notify_all();
lk.unlock();
for (Worker& worker : m_workers)
if (worker.m_thr.joinable())
worker.m_thr.join();
void ClientProcess::shutdown() {
if (!m_running)
return;
std::unique_lock<std::mutex> lk(m_mutex);
m_pendingQueue.clear();
m_running = false;
m_cv.notify_all();
lk.unlock();
for (Worker& worker : m_workers)
if (worker.m_thr.joinable())
worker.m_thr.join();
}
}
} // namespace hecl

View File

@ -14,314 +14,276 @@ extern pD3DCompile D3DCompilePROC;
#include <memory>
#endif
namespace hecl
{
namespace hecl {
logvisor::Module Log("hecl::Compilers");
namespace PlatformType
{
namespace PlatformType {
const char* OpenGL::Name = "OpenGL";
const char* Vulkan::Name = "Vulkan";
const char* D3D11::Name = "D3D11";
const char* Metal::Name = "Metal";
const char* NX::Name = "NX";
}
} // namespace PlatformType
namespace PipelineStage
{
namespace PipelineStage {
const char* Null::Name = "Null";
const char* Vertex::Name = "Vertex";
const char* Fragment::Name = "Fragment";
const char* Geometry::Name = "Geometry";
const char* Control::Name = "Control";
const char* Evaluation::Name = "Evaluation";
}
} // namespace PipelineStage
template<typename P> struct ShaderCompiler {};
template <typename P>
struct ShaderCompiler {};
template<> struct ShaderCompiler<PlatformType::OpenGL>
{
template<typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text)
{
std::string str = "#version 330\n";
str += BOO_GLSL_BINDING_HEAD;
str += text;
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1);
memcpy(ret.first.get(), str.data(), ret.second);
return ret;
}
template <>
struct ShaderCompiler<PlatformType::OpenGL> {
template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
std::string str = "#version 330\n";
str += BOO_GLSL_BINDING_HEAD;
str += text;
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1);
memcpy(ret.first.get(), str.data(), ret.second);
return ret;
}
};
template<> struct ShaderCompiler<PlatformType::Vulkan>
{
static constexpr EShLanguage ShaderTypes[] =
{
EShLangVertex, /* Invalid */
EShLangVertex,
EShLangFragment,
EShLangGeometry,
EShLangTessControl,
EShLangTessEvaluation
};
template <>
struct ShaderCompiler<PlatformType::Vulkan> {
static constexpr EShLanguage ShaderTypes[] = {EShLangVertex, /* Invalid */
EShLangVertex, EShLangFragment, EShLangGeometry,
EShLangTessControl, EShLangTessEvaluation};
template<typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text)
{
EShLanguage lang = ShaderTypes[int(S::Enum)];
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
glslang::TShader shader(lang);
const char* strings[] = { "#version 330\n", BOO_GLSL_BINDING_HEAD, text.data() };
shader.setStrings(strings, 3);
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());
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;
template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
EShLanguage lang = ShaderTypes[int(S::Enum)];
const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
glslang::TShader shader(lang);
const char* strings[] = {"#version 330\n", BOO_GLSL_BINDING_HEAD, text.data()};
shader.setStrings(strings, 3);
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());
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;
}
};
#if _WIN32
static const char* D3DShaderTypes[] =
{
nullptr,
"vs_5_0",
"ps_5_0",
"gs_5_0",
"hs_5_0",
"ds_5_0"
};
template<> struct ShaderCompiler<PlatformType::D3D11>
{
static const char* D3DShaderTypes[] = {nullptr, "vs_5_0", "ps_5_0", "gs_5_0", "hs_5_0", "ds_5_0"};
template <>
struct ShaderCompiler<PlatformType::D3D11> {
#if _DEBUG && 0
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0
#else
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_OPTIMIZATION_LEVEL3
#endif
template<typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text)
{
ComPtr<ID3DBlob> errBlob;
ComPtr<ID3DBlob> blobOut;
if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main",
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());
return {};
}
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()),
blobOut->GetBufferSize());
memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
return ret;
template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
ComPtr<ID3DBlob> errBlob;
ComPtr<ID3DBlob> blobOut;
if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main",
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());
return {};
}
std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()), blobOut->GetBufferSize());
memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
return ret;
}
};
#endif
#if __APPLE__
template<> struct ShaderCompiler<PlatformType::Metal>
{
static bool m_didCompilerSearch;
static bool m_hasCompiler;
template <>
struct ShaderCompiler<PlatformType::Metal> {
static bool m_didCompilerSearch;
static bool m_hasCompiler;
static bool SearchForCompiler()
{
m_didCompilerSearch = true;
const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER");
if (no_metal_compiler && atoi(no_metal_compiler))
return false;
static bool SearchForCompiler() {
m_didCompilerSearch = true;
const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER");
if (no_metal_compiler && atoi(no_metal_compiler))
return false;
pid_t pid = fork();
if (!pid)
{
execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL);
/* xcrun returns 72 if metal command not found;
* emulate that if xcrun not found */
exit(72);
}
int status, ret;
while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
if (ret < 0)
return false;
return WEXITSTATUS(status) == 1;
pid_t pid = fork();
if (!pid) {
execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL);
/* xcrun returns 72 if metal command not found;
* emulate that if xcrun not found */
exit(72);
}
template<typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text)
{
if (!m_didCompilerSearch)
m_hasCompiler = SearchForCompiler();
int status, ret;
while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
if (ret < 0)
return false;
return WEXITSTATUS(status) == 1;
}
std::string str = "#include <metal_stdlib>\n"
"using namespace metal;\n";
str += text;
std::pair<StageBinaryData, size_t> ret;
template <typename S>
static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
if (!m_didCompilerSearch)
m_hasCompiler = SearchForCompiler();
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);
std::string str =
"#include <metal_stdlib>\n"
"using namespace metal;\n";
str += text;
std::pair<StageBinaryData, size_t> ret;
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
{
int compilerOut[2];
int compilerIn[2];
pipe(compilerOut);
pipe(compilerIn);
inPtr += writeRes;
inRem -= writeRes;
}
close(compilerIn[1]);
pid_t pid = getpid();
const char* tmpdir = getenv("TMPDIR");
char libFile[1024];
snprintf(libFile, 1024, "%sboo_metal_shader%d.metallib", tmpdir, pid);
/* 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 {};
}
/* Pipe source write to compiler */
pid_t compilerPid = fork();
if (!compilerPid)
{
dup2(compilerIn[0], STDIN_FILENO);
dup2(compilerOut[1], STDOUT_FILENO);
if (WEXITSTATUS(compilerStat)) {
Log.report(logvisor::Fatal, "compile fail");
return {};
}
close(compilerOut[0]);
close(compilerOut[1]);
close(compilerIn[0]);
close(compilerIn[1]);
while (waitpid(linkerPid, &linkerStat, 0) < 0) {
if (errno == EINTR)
continue;
Log.report(logvisor::Fatal, "waitpid fail %s", strerror(errno));
return {};
}
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]);
if (WEXITSTATUS(linkerStat)) {
Log.report(logvisor::Fatal, "link fail");
return {};
}
/* 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;
}
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;
/* 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;
}
};
bool ShaderCompiler<PlatformType::Metal>::m_didCompilerSearch = false;
bool ShaderCompiler<PlatformType::Metal>::m_hasCompiler = false;
#endif
#if HECL_NOUVEAU_NX
template<> struct ShaderCompiler<PlatformType::NX>
{
template<typename S>
static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text)
{
std::string str = "#version 330\n";
str += BOO_GLSL_BINDING_HEAD;
str += text;
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);
return ret;
}
template <>
struct ShaderCompiler<PlatformType::NX> {
template <typename S>
static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text) {
std::string str = "#version 330\n";
str += BOO_GLSL_BINDING_HEAD;
str += text;
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);
return ret;
}
};
#endif
template<typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text)
{
return ShaderCompiler<P>::template Compile<S>(text);
template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text) {
return ShaderCompiler<P>::template Compile<S>(text);
}
#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::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::Control>(std::string_view text); \
template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Evaluation>(std::string_view text);
#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::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::Control>(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::Vulkan)
#if _WIN32
@ -334,4 +296,4 @@ SPECIALIZE_COMPILE_SHADER(PlatformType::Metal)
SPECIALIZE_COMPILE_SHADER(PlatformType::NX)
#endif
}
} // namespace hecl

View File

@ -4,497 +4,424 @@
#include "hecl/hecl.hpp"
#include "athena/Utility.hpp"
namespace hecl
{
namespace hecl {
Console* Console::m_instance = nullptr;
Console::Console(CVarManager* cvarMgr)
: m_cvarMgr(cvarMgr)
, m_overwrite(false)
, m_cursorAtEnd(false)
{
m_instance = this;
registerCommand("help", "Prints information about a given function", "<command>", std::bind(&Console::help, this, std::placeholders::_1, std::placeholders::_2));
registerCommand("listCommands", "Prints a list of all available Commands", "", std::bind(&Console::listCommands, this, 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>", 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));
m_conSpeed = cvarMgr->findOrMakeCVar("con_speed", "Speed at which the console opens and closes, calculated as pixels per second", 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);
Console::Console(CVarManager* cvarMgr) : m_cvarMgr(cvarMgr), m_overwrite(false), m_cursorAtEnd(false) {
m_instance = this;
registerCommand("help", "Prints information about a given function", "<command>",
std::bind(&Console::help, this, std::placeholders::_1, std::placeholders::_2));
registerCommand("listCommands", "Prints a list of all available Commands", "",
std::bind(&Console::listCommands, this, 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>",
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));
m_conSpeed = cvarMgr->findOrMakeCVar("con_speed",
"Speed at which the console opens and closes, calculated as pixels per second",
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)
{
std::string lowName = name.data();
athena::utility::tolower(lowName);
if (m_commands.find(lowName) == m_commands.end())
m_commands[lowName] = SConsoleCommand{name.data(), helpText.data(), usage.data(), std::move(func), 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,
SConsoleCommand::ECommandFlags cmdFlags) {
std::string lowName = name.data();
athena::utility::tolower(lowName);
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)
{
std::string lowName = name.data();
athena::utility::tolower(lowName);
if (m_commands.find(lowName) != m_commands.end())
m_commands.erase(m_commands.find(lowName));
void Console::unregisterCommand(std::string_view name) {
std::string lowName = name.data();
athena::utility::tolower(lowName);
if (m_commands.find(lowName) != m_commands.end())
m_commands.erase(m_commands.find(lowName));
}
void Console::executeString(const std::string& str)
{
if (str.empty())
void Console::executeString(const std::string& str) {
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;
}
args.push_back(curLiteral);
}
/* First let's split semi-colon delimited commands */
std::vector<std::string> commands = athena::utility::split(str, ';');
std::string commandName = args[0];
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;
}
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 (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
report(Level::Error, "Command '%s' is not valid!", commandName.c_str());
}
}
void Console::help(Console* /*con*/, const std::vector<std::string>& args) {
if (args.empty()) {
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());
if (!it->second.m_usage.empty())
report(Level::Info, "Usage: %s %s", it->second.m_displayName.c_str(), it->second.m_usage.c_str());
}
void Console::listCommands(Console* /*con*/, const std::vector<std::string>& /*args*/) {
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)
{
if ((curLiteral.back() != '\'' && curLiteral.back() != '"') || depth > 1)
{
report(Level::Warning, "Unterminated string literal");
return;
}
args.push_back(curLiteral);
}
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;
std::string commandName = args[0];
args.erase(args.begin());
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;
}
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);
}
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
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 (args.empty())
{
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;
}
if (int(mod & boo::EModifierKey::Ctrl) != 0)
m_cursorPosition = int(m_commandString.rfind(' ', size_t(m_cursorPosition) - 1));
else
m_cursorPosition--;
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());
}
m_showCursor = true;
m_cursorTime = 0.f;
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*/)
{
for (const auto& comPair : m_commands)
report(Level::Info, "'%s': %s", comPair.second.m_displayName.c_str(), comPair.second.m_helpString.c_str());
}
if (int(mod & boo::EModifierKey::Ctrl) != 0) {
if (m_commandString[size_t(m_cursorPosition)] == ' ')
m_cursorPosition++;
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))
size_t tmpPos = m_commandString.find(' ', size_t(m_cursorPosition));
if (tmpPos == std::string::npos)
m_cursorPosition = int(m_commandString.size() - 1);
if (m_cursorPosition < -1)
m_cursorPosition = -1;
else
m_cursorPosition = int(tmpPos);
} else
m_cursorPosition++;
if (m_logOffset > int(m_log.size() - 1))
m_logOffset = int(m_log.size() - 1);
if (m_logOffset < 0)
m_logOffset = 0;
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::draw(boo::IGraphicsCommandQueue* /*gfxQ*/)
{
}
void Console::handleSpecialKeyUp(boo::ESpecialKey /*sp*/, boo::EModifierKey /*mod*/) {}
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;
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);
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::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::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));
}
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()
{
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) { logvisor::MainLoggers.emplace_back(new LogVisorAdapter(con)); }
void Console::RegisterLogger(Console* con)
{
logvisor::MainLoggers.emplace_back(new LogVisorAdapter(con));
}
Console* Console::instance()
{
return m_instance;
}
}
Console* Console::instance() { return m_instance; }
} // namespace hecl

View File

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

View File

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

View File

@ -3,8 +3,7 @@
/* Syntatical token parsing system */
namespace hecl::Frontend
{
namespace hecl::Frontend {
/*
* hecl = { lf } call { lf { lf } call } { lf } .
@ -15,281 +14,244 @@ namespace hecl::Frontend
* value = ( call [ "." ident ] )
* | [ "-" ] number
* .
*/
*/
std::string IRNode::rep(int n, std::string_view s)
{
std::string buf;
buf.reserve(n * s.size());
for (int i = 0; i < n; i++)
buf.append(s);
return buf;
std::string IRNode::rep(int n, std::string_view s) {
std::string buf;
buf.reserve(n * s.size());
for (int i = 0; i < n; i++)
buf.append(s);
return buf;
}
std::string IRNode::fmt(int level, bool stripUVAnims) const
{
std::string buf;
auto indent = rep(level, "\t"sv);
switch (kind)
{
case Kind::Call:
if (stripUVAnims && (str == "Texture" || str == "TextureN") && children.size() >= 2)
{
auto it = children.begin();
IRNode& uvNode = const_cast<IRNode&>(*++it);
if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View")
{
std::string replacementName(str);
if (uvNode.str.back() == 'N' && replacementName.back() != 'N')
replacementName += 'N';
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;
std::string IRNode::fmt(int level, bool stripUVAnims) const {
std::string buf;
auto indent = rep(level, "\t"sv);
switch (kind) {
case Kind::Call:
if (stripUVAnims && (str == "Texture" || str == "TextureN") && children.size() >= 2) {
auto it = children.begin();
IRNode& uvNode = const_cast<IRNode&>(*++it);
if (uvNode.str != "UV" && uvNode.str != "Normal" && uvNode.str != "View") {
std::string replacementName(str);
if (uvNode.str.back() == 'N' && replacementName.back() != 'N')
replacementName += 'N';
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;
}
}
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::vector<std::string> l;
l.push_back("kind="s + KindToStr(kind).data());
if (!str.empty())
l.push_back("str="s + str);
if (kind == Kind::Binop)
{
l.push_back("op="s + OpToStr(op).data());
l.push_back("left="s + left->toString());
l.push_back("right="s + right->toString());
}
if (kind == Kind::Swizzle)
l.push_back("node="s + left->toString());
if (kind == Kind::Call)
{
std::string str = "children=["s;
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 += ';';
std::string IRNode::describe() const {
std::vector<std::string> l;
l.push_back("kind="s + KindToStr(kind).data());
if (!str.empty())
l.push_back("str="s + str);
if (kind == Kind::Binop) {
l.push_back("op="s + OpToStr(op).data());
l.push_back("left="s + left->toString());
l.push_back("right="s + right->toString());
}
if (kind == Kind::Swizzle)
l.push_back("node="s + left->toString());
if (kind == Kind::Call) {
std::string str = "children=["s;
for (auto it = children.begin(); it != children.end(); ++it) {
str += it->toString();
if (&*it != &children.back())
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)
{
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;
void Parser::check(Token::Kind expected) {
if (sym == expected)
scan();
while (sym == Token::Kind::Lf)
scan();
result.push_back(call());
while (sym == Token::Kind::Lf)
{
while (sym == Token::Kind::Lf)
scan();
if (sym != Token::Kind::Eof)
result.push_back(call());
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();
while (sym == Token::Kind::Lf)
scan();
result.push_back(call());
while (sym == Token::Kind::Lf) {
while (sym == Token::Kind::Lf)
scan();
check(Token::Kind::Eof);
scan();
if (sym != Token::Kind::Eof)
result.push_back(call());
}
while (sym == Token::Kind::Lf)
scan();
check(Token::Kind::Eof);
if (hecl::VerbosityLevel > 1)
for (auto& res : result)
printf("%s\n", res.toString().c_str());
if (hecl::VerbosityLevel > 1)
for (auto& res : result)
printf("%s\n", res.toString().c_str());
return result;
return result;
}
}
} // namespace hecl::Frontend

View File

@ -1,122 +1,104 @@
#include "hecl/Frontend.hpp"
namespace hecl::Frontend
{
namespace hecl::Frontend {
int Scanner::_read()
{
if (m_sourceIt == m_source.end())
return -1;
return *m_sourceIt++;
int Scanner::_read() {
if (m_sourceIt == m_source.end())
return -1;
return *m_sourceIt++;
}
bool Scanner::read()
{
if (ch == EOF)
return false;
if (ch == LF)
{
lastLine = std::move(currentLine);
currentLine = std::string();
}
int c = _read();
ch = char(c);
if (ch == LF)
{
loc.line++;
lfcol = loc.col;
loc.col = 0;
}
else if (c != EOF)
{
currentLine += ch;
loc.col++;
}
return c != EOF;
bool Scanner::read() {
if (ch == EOF)
return false;
if (ch == LF) {
lastLine = std::move(currentLine);
currentLine = std::string();
}
int c = _read();
ch = char(c);
if (ch == LF) {
loc.line++;
lfcol = loc.col;
loc.col = 0;
} else if (c != EOF) {
currentLine += ch;
loc.col++;
}
return c != EOF;
}
Token Scanner::next()
{
if (ch == EOF)
return Token(Token::Kind::Eof, loc);
Token Scanner::next() {
if (ch == EOF)
return Token(Token::Kind::Eof, loc);
char c = ch;
int tline = loc.line;
int tcol = loc.col;
int tlfcol = lfcol;
read();
char c = ch;
int tline = loc.line;
int tcol = loc.col;
int tlfcol = lfcol;
read();
// skip comments and newlines
while (c != EOF && (c == COMMENT || isspace(c)))
{
if (c == COMMENT)
{
while (c != LF && c != EOF)
{
tline = loc.line;
tcol = loc.col;
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();
}
// skip comments and newlines
while (c != EOF && (c == COMMENT || isspace(c))) {
if (c == COMMENT) {
while (c != LF && c != EOF) {
tline = loc.line;
tcol = loc.col;
tlfcol = lfcol;
c = ch;
read();
}
}
Token::Kind kind = CharToTokenKind(c);
if (kind != Token::Kind::None)
return Token(kind, {tline, tcol});
if (ch == EOF)
return Token(Token::Kind::Eof, {tline, tcol});
// 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});
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();
}
}
if (isStartIdent(c))
{
std::string buf;
buf += c;
while (isMidIdent(ch))
{
buf += ch;
read();
}
return Token(Token::Kind::Ident, std::move(buf), {tline, tcol});
Token::Kind kind = CharToTokenKind(c);
if (kind != Token::Kind::None)
return Token(kind, {tline, tcol});
if (ch == EOF)
return Token(Token::Kind::Eof, {tline, tcol});
// 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});
}
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.
*/
namespace hecl
{
namespace hecl {
static logvisor::Module Log("hecl::HumanizeNumber");
static const int maxscale = 7;
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags)
{
const char *prefixes, *sep;
int i, r, remainder, s1, s2, sign;
int divisordeccut;
int64_t divisor, max;
size_t baselen;
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags) {
const char *prefixes, *sep;
int i, r, remainder, s1, s2, sign;
int divisordeccut;
int64_t divisor, max;
size_t baselen;
/* validate args */
if (suffix == nullptr)
suffix = "";
if ((flags & HNFlags::Divisor1000) != HNFlags::None && (flags & HNFlags::IECPrefixes) != HNFlags::None)
Log.report(logvisor::Fatal, "invalid flags combo");
/* validate args */
if (suffix == nullptr)
suffix = "";
if ((flags & HNFlags::Divisor1000) != HNFlags::None && (flags & HNFlags::IECPrefixes) != HNFlags::None)
Log.report(logvisor::Fatal, "invalid flags combo");
/* setup parameters */
remainder = 0;
/* setup parameters */
remainder = 0;
if ((flags & HNFlags::IECPrefixes) != HNFlags::None) {
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 ... */
if ((flags & HNFlags::IECPrefixes) != HNFlags::None) {
baselen = 2;
/*
* XXX - should we make sure there is enough space for the decimal
* place and if not, don't do HN_DECIMAL?
* 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).
*/
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);
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";
}
}
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"
#if _WIN32
#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
#define FOREGROUND_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#endif
namespace hecl
{
namespace hecl {
void MultiProgressPrinter::ThreadStat::print(const TermInfo& tinfo) const
{
bool blocks = m_factor >= 0.f;
float factor = std::max(0.f, std::min(1.f, m_factor));
int iFactor = factor * 100.f;
void MultiProgressPrinter::ThreadStat::print(const TermInfo& tinfo) const {
bool blocks = m_factor >= 0.f;
float factor = std::max(0.f, std::min(1.f, m_factor));
int iFactor = factor * 100.f;
int messageLen = m_message.size();
int submessageLen = m_submessage.size();
int messageLen = m_message.size();
int submessageLen = m_submessage.size();
int half;
if (blocks)
half = (tinfo.width + 1) / 2 - 2;
else if (tinfo.truncate)
half = tinfo.width - 4;
else
half = messageLen;
int half;
if (blocks)
half = (tinfo.width + 1) / 2 - 2;
else if (tinfo.truncate)
half = tinfo.width - 4;
else
half = messageLen;
if (half - messageLen < submessageLen-2)
submessageLen = 0;
if (half - messageLen < submessageLen - 2)
submessageLen = 0;
if (submessageLen)
{
if (messageLen > half-submessageLen-1)
hecl::Printf(_SYS_STR(" %.*s... %s "), half-submessageLen-4, m_message.c_str(), m_submessage.c_str());
else
{
hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
for (int i=half-messageLen-submessageLen-1 ; i>=0 ; --i)
hecl::Printf(_SYS_STR(" "));
hecl::Printf(_SYS_STR("%s "), m_submessage.c_str());
}
if (submessageLen) {
if (messageLen > half - submessageLen - 1)
hecl::Printf(_SYS_STR(" %.*s... %s "), half - submessageLen - 4, m_message.c_str(), m_submessage.c_str());
else {
hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
for (int i = half - messageLen - submessageLen - 1; i >= 0; --i)
hecl::Printf(_SYS_STR(" "));
hecl::Printf(_SYS_STR("%s "), m_submessage.c_str());
}
else
{
if (messageLen > half)
hecl::Printf(_SYS_STR(" %.*s... "), half-3, m_message.c_str());
else
{
hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
for (int i=half-messageLen ; i>=0 ; --i)
hecl::Printf(_SYS_STR(" "));
}
} else {
if (messageLen > half)
hecl::Printf(_SYS_STR(" %.*s... "), half - 3, m_message.c_str());
else {
hecl::Printf(_SYS_STR(" %s"), m_message.c_str());
for (int i = half - messageLen; i >= 0; --i)
hecl::Printf(_SYS_STR(" "));
}
}
if (blocks)
{
int rightHalf = tinfo.width - half - 4;
int blocks = rightHalf - 7;
int filled = blocks * factor;
int rem = blocks - filled;
if (blocks) {
int rightHalf = tinfo.width - half - 4;
int blocks = rightHalf - 7;
int filled = blocks * factor;
int rem = blocks - filled;
if (tinfo.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(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("-"));
if (tinfo.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
{
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);
SetConsoleTextAttribute(tinfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#endif
hecl::Printf(_SYS_STR(" ["));
for (int b=0 ; b<pre ; ++b)
hecl::Printf(_SYS_STR("-"));
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("]"));
for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]"));
#if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE);
SetConsoleTextAttribute(tinfo.console, FOREGROUND_WHITE);
#endif
}
}
}
void MultiProgressPrinter::MoveCursorUp(int n)
{
if (n)
{
if (m_termInfo.xtermColor)
{
hecl::Printf(_SYS_STR("" PREV_LINE ""), n);
}
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("#"));
for (int b = 0; b < rem; ++b)
hecl::Printf(_SYS_STR("-"));
hecl::Printf(_SYS_STR("]" NORMAL ""));
} else {
#if _WIN32
else
{
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
GetConsoleScreenBufferInfo(m_termInfo.console, &consoleInfo);
consoleInfo.dwCursorPosition.X = 0;
consoleInfo.dwCursorPosition.Y -= n;
SetConsoleCursorPosition(m_termInfo.console, consoleInfo.dwCursorPosition);
}
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#endif
}
else
{
hecl::Printf(_SYS_STR("\r"));
}
hecl::Printf(_SYS_STR(" ["));
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("]"));
#if _WIN32
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_WHITE);
#endif
}
}
void MultiProgressPrinter::DoPrint()
{
auto logLk = logvisor::LockLog();
uint64_t logCounter = logvisor::GetLogCounter();
if (logCounter != m_lastLogCounter)
{
m_curThreadLines = 0;
m_lastLogCounter = logCounter;
void MultiProgressPrinter::MoveCursorUp(int n) {
if (n) {
if (m_termInfo.xtermColor) {
hecl::Printf(_SYS_STR("" PREV_LINE ""), n);
}
#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();
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 {
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
GetConsoleScreenBufferInfo(m_termInfo.console, &consoleInfo);
consoleInfo.dwCursorPosition.X = 0;
consoleInfo.dwCursorPosition.Y -= n;
SetConsoleCursorPosition(m_termInfo.console, consoleInfo.dwCursorPosition);
}
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;
} else {
hecl::Printf(_SYS_STR("\r"));
}
}
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();
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;
if (m_termInfo.xtermColor)
hecl::Printf(_SYS_STR("" SHOW_CURSOR ""));
fflush(stdout);
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
cursorInfo.bVisible = TRUE;
SetConsoleCursorInfo(m_termInfo.console, &cursorInfo);
SetConsoleTextAttribute(m_termInfo.console, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
#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 */
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
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;
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
) {
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
m_running = true;
m_logThread = std::thread(std::bind(&MultiProgressPrinter::LogProc, this));
}
m_running = true;
m_logThread = std::thread(std::bind(&MultiProgressPrinter::LogProc, this));
}
}
MultiProgressPrinter::~MultiProgressPrinter()
{
m_running = false;
if (m_logThread.joinable())
m_logThread.join();
MultiProgressPrinter::~MultiProgressPrinter() {
m_running = false;
if (m_logThread.joinable())
m_logThread.join();
}
void MultiProgressPrinter::print(const hecl::SystemChar* message,
const hecl::SystemChar* submessage,
float factor, int threadIdx) const
{
if (!m_running)
return;
std::lock_guard<std::mutex> lk(m_logLock);
if (threadIdx < 0)
threadIdx = 0;
if (threadIdx >= m_threadStats.size())
m_threadStats.resize(threadIdx + 1);
ThreadStat& stat = m_threadStats[threadIdx];
if (message)
stat.m_message = message;
else
stat.m_message.clear();
if (submessage)
stat.m_submessage = submessage;
else
stat.m_submessage.clear();
stat.m_factor = factor;
stat.m_active = true;
m_latestThread = threadIdx;
void MultiProgressPrinter::print(const hecl::SystemChar* message, const hecl::SystemChar* submessage, float factor,
int threadIdx) const {
if (!m_running)
return;
std::lock_guard<std::mutex> lk(m_logLock);
if (threadIdx < 0)
threadIdx = 0;
if (threadIdx >= m_threadStats.size())
m_threadStats.resize(threadIdx + 1);
ThreadStat& stat = m_threadStats[threadIdx];
if (message)
stat.m_message = message;
else
stat.m_message.clear();
if (submessage)
stat.m_submessage = submessage;
else
stat.m_submessage.clear();
stat.m_factor = factor;
stat.m_active = true;
m_latestThread = threadIdx;
m_dirty = true;
}
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_mainFactor = factor;
}
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_mainFactor = factor;
void MultiProgressPrinter::setMainIndeterminate(bool indeterminate) const {
if (!m_running)
return;
std::lock_guard<std::mutex> lk(m_logLock);
if (m_mainIndeterminate != indeterminate) {
m_mainIndeterminate = indeterminate;
m_dirty = true;
}
}
void MultiProgressPrinter::setMainIndeterminate(bool indeterminate) const
{
if (!m_running)
return;
std::lock_guard<std::mutex> lk(m_logLock);
if (m_mainIndeterminate != indeterminate)
{
m_mainIndeterminate = indeterminate;
m_dirty = true;
}
void MultiProgressPrinter::startNewLine() const {
if (!m_running)
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::startNewLine() const
{
if (!m_running)
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 {
std::lock_guard<std::mutex> lk(m_logLock);
const_cast<MultiProgressPrinter&>(*this).DoPrint();
}
void MultiProgressPrinter::flush() const
{
std::lock_guard<std::mutex> lk(m_logLock);
const_cast<MultiProgressPrinter&>(*this).DoPrint();
}
}
} // namespace hecl

View File

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

View File

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

View File

@ -2,376 +2,320 @@
#include "hecl/Database.hpp"
#include <regex>
namespace hecl
{
static const SystemRegex regPATHCOMP(_SYS_STR("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript|SystemRegex::optimize);
static const SystemRegex regDRIVELETTER(_SYS_STR("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize);
namespace hecl {
static const SystemRegex regPATHCOMP(_SYS_STR("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript | SystemRegex::optimize);
static const SystemRegex regDRIVELETTER(_SYS_STR("^([^/]*)/"), SystemRegex::ECMAScript | SystemRegex::optimize);
static SystemString CanonRelPath(SystemStringView path)
{
/* Tokenize Path */
std::vector<SystemString> comps;
hecl::SystemRegexMatch matches;
SystemString in(path);
SanitizePath(in);
for (; std::regex_search(in, matches, regPATHCOMP) ; in = matches.suffix().str())
{
hecl::SystemRegexMatch::const_reference match = matches[1];
if (!match.compare(_SYS_STR(".")))
continue;
else if (!match.compare(_SYS_STR("..")))
{
if (comps.empty())
{
/* Unable to resolve outside project */
LogModule.report(logvisor::Fatal, _SYS_STR("Unable to resolve outside project root in %s"), path.data());
return _SYS_STR(".");
}
comps.pop_back();
continue;
}
comps.push_back(match.str());
static SystemString CanonRelPath(SystemStringView path) {
/* Tokenize Path */
std::vector<SystemString> comps;
hecl::SystemRegexMatch matches;
SystemString in(path);
SanitizePath(in);
for (; std::regex_search(in, matches, regPATHCOMP); in = matches.suffix().str()) {
hecl::SystemRegexMatch::const_reference match = matches[1];
if (!match.compare(_SYS_STR(".")))
continue;
else if (!match.compare(_SYS_STR(".."))) {
if (comps.empty()) {
/* Unable to resolve outside project */
LogModule.report(logvisor::Fatal, _SYS_STR("Unable to resolve outside project root in %s"), path.data());
return _SYS_STR(".");
}
comps.pop_back();
continue;
}
comps.push_back(match.str());
}
/* Emit relative path */
if (comps.size())
{
auto it = comps.begin();
SystemString retval = *it;
for (++it ; it != comps.end() ; ++it)
{
if ((*it).size())
{
retval += _SYS_STR('/');
retval += *it;
}
}
return retval;
/* Emit relative path */
if (comps.size()) {
auto it = comps.begin();
SystemString retval = *it;
for (++it; it != comps.end(); ++it) {
if ((*it).size()) {
retval += _SYS_STR('/');
retval += *it;
}
}
return _SYS_STR(".");
return retval;
}
return _SYS_STR(".");
}
static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot)
{
/* Absolute paths not allowed; attempt to make project-relative */
if (IsAbsolute(path))
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(path);
static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot) {
/* Absolute paths not allowed; attempt to make project-relative */
if (IsAbsolute(path))
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(path);
}
void ProjectPath::assign(Database::Project& project, SystemStringView path)
{
m_proj = &project;
void ProjectPath::assign(Database::Project& project, SystemStringView path) {
m_proj = &project;
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos)
{
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
}
else
usePath = path;
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) {
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
} else
usePath = path;
m_relPath = CanonRelPath(usePath, project.getProjectRootPath());
m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
m_relPath = CanonRelPath(usePath, project.getProjectRootPath());
m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
ComputeHash();
ComputeHash();
}
#if HECL_UCS2
void ProjectPath::assign(Database::Project& project, std::string_view path)
{
std::wstring wpath = UTF8ToWide(path);
assign(project, wpath);
void ProjectPath::assign(Database::Project& project, std::string_view path) {
std::wstring wpath = UTF8ToWide(path);
assign(project, wpath);
}
#endif
void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path)
{
m_proj = parentPath.m_proj;
void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path) {
m_proj = parentPath.m_proj;
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos)
{
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
}
else
usePath = path;
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) {
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
} else
usePath = path;
m_relPath = CanonRelPath(parentPath.m_relPath + _SYS_STR('/') + usePath);
m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
m_relPath = CanonRelPath(parentPath.m_relPath + _SYS_STR('/') + usePath);
m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
ComputeHash();
ComputeHash();
}
#if HECL_UCS2
void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path)
{
std::wstring wpath = UTF8ToWide(path);
assign(parentPath, wpath);
void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path) {
std::wstring wpath = UTF8ToWide(path);
assign(parentPath, wpath);
}
#endif
ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const
{
ProjectPath pp(*this);
if (replace)
{
auto relIt = pp.m_relPath.end();
if (relIt != pp.m_relPath.begin())
--relIt;
auto absIt = pp.m_absPath.end();
if (absIt != pp.m_absPath.begin())
--absIt;
while (relIt != pp.m_relPath.begin() && *relIt != _SYS_STR('.') && *relIt != _SYS_STR('/'))
{
--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());
}
ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const {
ProjectPath pp(*this);
if (replace) {
auto relIt = pp.m_relPath.end();
if (relIt != pp.m_relPath.begin())
--relIt;
auto absIt = pp.m_absPath.end();
if (absIt != pp.m_absPath.begin())
--absIt;
while (relIt != pp.m_relPath.begin() && *relIt != _SYS_STR('.') && *relIt != _SYS_STR('/')) {
--relIt;
--absIt;
}
if (ext)
{
pp.m_relPath += ext;
pp.m_absPath += ext;
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) {
pp.m_relPath += ext;
pp.m_absPath += ext;
}
pp.ComputeHash();
return pp;
pp.ComputeHash();
return pp;
}
ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const
{
ProjectPath woExt = getWithExtension(nullptr, true);
ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const {
ProjectPath woExt = getWithExtension(nullptr, true);
ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
if (getAuxInfo().size())
return ret.getWithExtension((SystemString(_SYS_STR(".")) + getAuxInfo().data()).c_str());
else
return ret;
if (getAuxInfo().size())
return ret.getWithExtension((SystemString(_SYS_STR(".")) + getAuxInfo().data()).c_str());
else
return ret;
}
ProjectPath::Type ProjectPath::getPathType() const
{
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos)
return Type::Glob;
Sstat 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;
ProjectPath::Type ProjectPath::getPathType() const {
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos)
return Type::Glob;
Sstat 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;
}
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;
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;
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);
}
}
/* Compile component into regex */
SystemRegex regComp(comp, SystemRegex::ECMAScript);
while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
}
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);
}
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();
}
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;
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();
}
}
} // namespace hecl

View File

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

View File

@ -2,50 +2,44 @@
#include "hecl/Runtime.hpp"
#include <athena/MemoryReader.hpp>
namespace hecl::Runtime
{
namespace hecl::Runtime {
static logvisor::Module HMDL_Log("HMDL");
HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx,
const void* metaData, const void* vbo, const void* ibo)
{
HMDLMeta meta;
{
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");
HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo) {
HMDLMeta meta;
{
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");
m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount);
m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount);
m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount);
m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount);
size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount;
m_vtxFmtData.reset(new boo::VertexElementDescriptor[elemCount]);
size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount;
m_vtxFmtData.reset(new boo::VertexElementDescriptor[elemCount]);
m_vtxFmtData[0].semantic = boo::VertexSemantic::Position3;
m_vtxFmtData[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2;
m_vtxFmtData[0].semantic = boo::VertexSemantic::Position3;
m_vtxFmtData[1].semantic = boo::VertexSemantic::Normal3;
size_t e = 2;
for (size_t i=0 ; i<meta.colorCount ; ++i, ++e)
{
m_vtxFmtData[e].semantic = boo::VertexSemantic::ColorUNorm;
m_vtxFmtData[e].semanticIdx = i;
}
for (size_t i = 0; i < meta.colorCount; ++i, ++e) {
m_vtxFmtData[e].semantic = boo::VertexSemantic::ColorUNorm;
m_vtxFmtData[e].semanticIdx = i;
}
for (size_t i=0 ; i<meta.uvCount ; ++i, ++e)
{
m_vtxFmtData[e].semantic = boo::VertexSemantic::UV2;
m_vtxFmtData[e].semanticIdx = i;
}
for (size_t i = 0; i < meta.uvCount; ++i, ++e) {
m_vtxFmtData[e].semantic = boo::VertexSemantic::UV2;
m_vtxFmtData[e].semanticIdx = i;
}
for (size_t i=0 ; i<meta.weightCount ; ++i, ++e)
{
m_vtxFmtData[e].semantic = boo::VertexSemantic::Weight;
m_vtxFmtData[e].semanticIdx = i;
}
for (size_t i = 0; i < meta.weightCount; ++i, ++e) {
m_vtxFmtData[e].semantic = boo::VertexSemantic::Weight;
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 '/'
#endif
namespace hecl
{
namespace hecl {
/* Used to extract alternate steam install directories from libraryfolders.vdf */
static const std::regex regSteamPath(R"(^\s+\"[0-9]+\"\s+\"(.*)\")",
std::regex::ECMAScript|std::regex::optimize);
static const std::regex regSteamPath(R"(^\s+\"[0-9]+\"\s+\"(.*)\")", std::regex::ECMAScript | std::regex::optimize);
hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name)
{
hecl::SystemString steamInstallDir;
hecl::Sstat theStat;
hecl::SystemString FindCommonSteamApp(const hecl::SystemChar* name) {
hecl::SystemString steamInstallDir;
hecl::Sstat theStat;
#ifdef WIN32
#if !WINDOWS_STORE
HKEY hkey;
hecl::SystemChar _steamInstallDir[MAX_PATH] = {0};
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"),
0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
{
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"),
0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hkey) != ERROR_SUCCESS)
return {};
}
HKEY hkey;
hecl::SystemChar _steamInstallDir[MAX_PATH] = {0};
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE, &hkey) !=
ERROR_SUCCESS) {
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _SYS_STR("Software\\Valve\\Steam"), 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY,
&hkey) != ERROR_SUCCESS)
return {};
}
DWORD size = MAX_PATH;
if (RegQueryValueEx(hkey, _SYS_STR("InstallPath"), nullptr, nullptr,
(LPBYTE)_steamInstallDir, &size) == ERROR_SUCCESS)
steamInstallDir = _steamInstallDir;
RegCloseKey(hkey);
DWORD size = MAX_PATH;
if (RegQueryValueEx(hkey, _SYS_STR("InstallPath"), nullptr, nullptr, (LPBYTE)_steamInstallDir, &size) ==
ERROR_SUCCESS)
steamInstallDir = _steamInstallDir;
RegCloseKey(hkey);
if (steamInstallDir.empty())
return {};
#else
if (steamInstallDir.empty())
return {};
#else
return {};
#endif
#elif defined(__APPLE__)
steamInstallDir = getenv("HOME");
steamInstallDir += "/Library/Application Support/Steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
return {};
steamInstallDir = getenv("HOME");
steamInstallDir += "/Library/Application Support/Steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
return {};
#else
steamInstallDir = getenv("HOME");
steamInstallDir += "/.local/share/Steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
steamInstallDir = getenv("HOME");
steamInstallDir += "/.local/share/Steam";
steamInstallDir += "/.steam/steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
{
steamInstallDir = getenv("HOME");
steamInstallDir += "/.steam/steam";
if (hecl::Stat(steamInstallDir.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
return {};
}
return {};
}
#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 */
hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps");
hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath;
if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode))
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;
}
/* Try main steam install directory first */
hecl::SystemString steamAppsMain = steamInstallDir + PATH_SEP + _SYS_STR("steamapps");
hecl::SystemString mainAppPath = steamAppsMain + PATH_SEP + appPath;
if (!hecl::Stat(mainAppPath.c_str(), &theStat) && S_ISDIR(theStat.st_mode))
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;
}
return {};
}
}
} // namespace hecl

View File

@ -1,84 +1,71 @@
#include <utf8proc.h>
#include "hecl/hecl.hpp"
namespace hecl
{
namespace hecl {
static logvisor::Module Log("hecl-wsconv");
std::string WideToUTF8(std::wstring_view src)
{
std::string retval;
retval.reserve(src.length());
for (wchar_t ch : src)
{
utf8proc_uint8_t mb[4];
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
if (c < 0)
{
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
retval.append(reinterpret_cast<char*>(mb), c);
std::string WideToUTF8(std::wstring_view src) {
std::string retval;
retval.reserve(src.length());
for (wchar_t ch : src) {
utf8proc_uint8_t mb[4];
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
if (c < 0) {
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
return retval;
retval.append(reinterpret_cast<char*>(mb), c);
}
return retval;
}
std::string Char16ToUTF8(std::u16string_view src)
{
std::string retval;
retval.reserve(src.length());
for (char16_t ch : src)
{
utf8proc_uint8_t mb[4];
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
if (c < 0)
{
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
retval.append(reinterpret_cast<char*>(mb), c);
std::string Char16ToUTF8(std::u16string_view src) {
std::string retval;
retval.reserve(src.length());
for (char16_t ch : src) {
utf8proc_uint8_t mb[4];
utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb);
if (c < 0) {
Log.report(logvisor::Warning, "invalid UTF-8 character while encoding");
return retval;
}
return retval;
retval.append(reinterpret_cast<char*>(mb), c);
}
return retval;
}
std::wstring UTF8ToWide(std::string_view src)
{
std::wstring retval;
retval.reserve(src.length());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
while (*buf)
{
utf8proc_int32_t wc;
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
if (len < 0)
{
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
buf += len;
retval += wchar_t(wc);
std::wstring UTF8ToWide(std::string_view src) {
std::wstring retval;
retval.reserve(src.length());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
while (*buf) {
utf8proc_int32_t wc;
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
if (len < 0) {
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
return retval;
buf += len;
retval += wchar_t(wc);
}
return retval;
}
std::u16string UTF8ToChar16(std::string_view src)
{
std::u16string retval;
retval.reserve(src.length());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
while (*buf)
{
utf8proc_int32_t wc;
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
if (len < 0)
{
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
buf += len;
retval += char16_t(wc);
std::u16string UTF8ToChar16(std::string_view src) {
std::u16string retval;
retval.reserve(src.length());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
while (*buf) {
utf8proc_int32_t wc;
utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc);
if (len < 0) {
Log.report(logvisor::Warning, "invalid UTF-8 character while decoding");
return retval;
}
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"
/*
* The memmem() function finds the start of the first occurrence of the
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
* length 'hlen'.
*
* The return value is a pointer to the beginning of the sub-string, or
* NULL if the substring is not found.
*/
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
{
int needle_first;
const uint8_t *p = static_cast<const uint8_t*>(haystack);
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));
}
* The memmem() function finds the start of the first occurrence of the
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
* length 'hlen'.
*
* The return value is a pointer to the beginning of the sub-string, or
* NULL if the substring is not found.
*/
void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) {
int needle_first;
const uint8_t* p = static_cast<const uint8_t*>(haystack);
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));
}
return NULL;
}
int asprintf(char** buf, const char* format, ...)
{
va_list ap;
va_start(ap, format);
int len = vsnprintf(nullptr, 0, format, ap);
va_end(ap);
*buf = (char*)malloc(len + 1);
va_start(ap, format);
vsnprintf(*buf, len + 1, format, ap);
va_end(ap);
return len;
int asprintf(char** buf, const char* format, ...) {
va_list ap;
va_start(ap, format);
int len = vsnprintf(nullptr, 0, format, ap);
va_end(ap);
*buf = (char*)malloc(len + 1);
va_start(ap, format);
vsnprintf(*buf, len + 1, format, ap);
va_end(ap);
return len;
}

View File

@ -11,31 +11,25 @@ static logvisor::Module Log("shaderc");
extern pD3DCompile D3DCompilePROC;
pD3DCompile D3DCompilePROC = nullptr;
static bool FindBestD3DCompile()
{
HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
}
}
static bool FindBestD3DCompile() {
HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll");
if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll");
if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll");
if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
}
}
}
if (d3dCompilelib)
{
D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile");
return D3DCompilePROC != nullptr;
}
return false;
}
if (d3dCompilelib) {
D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile");
return D3DCompilePROC != nullptr;
}
return false;
}
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)
#endif
{
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
#if _WIN32
if (!FindBestD3DCompile())
{
Log.report(logvisor::Info, "Unable to find D3DCompiler dll");
return 1;
}
if (!FindBestD3DCompile()) {
Log.report(logvisor::Info, "Unable to find D3DCompiler dll");
return 1;
}
#endif
if (argc == 1)
{
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());
}
if (argc == 1) {
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,38 +4,28 @@
#include <vector>
#include <unordered_map>
namespace hecl::shaderc
{
namespace hecl::shaderc {
class Compiler
{
enum class StageType
{
Vertex,
Fragment,
Geometry,
Control,
Evaluation
};
class Compiler {
enum class StageType { Vertex, Fragment, Geometry, Control, Evaluation };
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);
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:
void addInputFile(SystemStringView file);
void addDefine(std::string_view var, std::string_view val);
bool compile(std::string_view baseName, std::pair<std::string, std::string>& out);
void addInputFile(SystemStringView file);
void addDefine(std::string_view var, std::string_view val);
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;
struct HECLWindowCallback : boo::IWindowCallback
{
bool m_sizeDirty = false;
boo::SWindowRect m_latestSize;
virtual ~HECLWindowCallback();
void resized(const boo::SWindowRect& rect, bool /*sync*/)
{
m_sizeDirty = true;
m_latestSize = rect;
}
struct HECLWindowCallback : boo::IWindowCallback {
bool m_sizeDirty = false;
boo::SWindowRect m_latestSize;
virtual ~HECLWindowCallback();
void resized(const boo::SWindowRect& rect, bool /*sync*/) {
m_sizeDirty = true;
m_latestSize = rect;
}
bool m_destroyed = false;
void destroyed()
{
m_destroyed = true;
}
bool m_destroyed = false;
void destroyed() { m_destroyed = true; }
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
{
hecl::Console::instance()->handleCharCode(charCode, mods, isRepeat);
}
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool 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 charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) {
hecl::Console::instance()->handleCharCode(charCode, mods, isRepeat);
}
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) {
hecl::Console::instance()->handleSpecialKeyDown(key, mods, isRepeat);
}
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
{
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;
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));
}
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();
virtual ~HECLApplicationCallback();
int appMain(boo::IApplication* app) {
hecl::VerbosityLevel = 2;
int appMain(boo::IApplication* app)
{
hecl::VerbosityLevel = 2;
/* Setup boo window */
m_mainWindow = app->newWindow(_SYS_STR("HECL Test"));
m_mainWindow->setCallback(&m_windowCb);
/* Setup boo window */
m_mainWindow = app->newWindow(_SYS_STR("HECL Test"));
m_mainWindow->setCallback(&m_windowCb);
boo::ObjToken<boo::ITextureR> renderTex;
boo::ObjToken<boo::IGraphicsBuffer> vubo;
boo::ObjToken<boo::IShaderPipeline> pipeline, pipeline2;
boo::ObjToken<boo::IShaderDataBinding> binding, binding2;
boo::ObjToken<boo::ITextureR> renderTex;
boo::ObjToken<boo::IGraphicsBuffer> vubo;
boo::ObjToken<boo::IShaderPipeline> pipeline, pipeline2;
boo::ObjToken<boo::IShaderDataBinding> binding, binding2;
struct VertexUBO {
float modelview[4][4] = {};
float modelviewInv[4][4] = {};
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
{
float modelview[4][4] = {};
float modelviewInv[4][4] = {};
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;
/* Make ramp texture */
using Pixel = uint8_t[4];
static Pixel tex[256][256];
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;
}
/* Make ramp texture */
using Pixel = uint8_t[4];
static Pixel tex[256][256];
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();
if (gfxF->platform() == boo::IGraphicsDataFactory::Platform::Vulkan)
vuboData.modelview[1][1] = -1.f;
boo::IGraphicsDataFactory* gfxF = m_mainWindow->getMainContextDataFactory();
if (gfxF->platform() == boo::IGraphicsDataFactory::Platform::Vulkan)
vuboData.modelview[1][1] = -1.f;
/* Pipeline converter */
std::unique_ptr<hecl::PipelineConverterBase> conv = hecl::NewPipelineConverter(gfxF);
/* Pipeline converter */
std::unique_ptr<hecl::PipelineConverterBase> conv = hecl::NewPipelineConverter(gfxF);
/* Compile HECL shader */
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 */
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);
gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
pipeline = conv->convert(ctx, irObj);
pipeline2 = conv->convert(ctx, Shader_test{});
gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx)
{
pipeline = conv->convert(ctx, irObj);
pipeline2 = conv->convert(ctx, Shader_test{});
boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame();
renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]),
boo::TextureClampMode::Repeat, 1, 0);
boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame();
renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]),
boo::TextureClampMode::Repeat, 1, 0);
/* Generate meta structure (usually statically serialized) */
hecl::HMDLMeta testMeta;
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) */
hecl::HMDLMeta testMeta;
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;
/* Binary form of meta structure */
atUint8 testMetaBuf[HECL_HMDL_META_SZ];
athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ);
testMeta.write(testMetaWriter);
/* Binary form of meta structure */
atUint8 testMetaBuf[HECL_HMDL_META_SZ];
athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ);
testMeta.write(testMetaWriter);
/* Make Tri-strip VBO */
struct Vert {
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}}};
/* Make Tri-strip VBO */
struct Vert
{
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 */
static const uint32_t ibo[4] = {0, 1, 2, 3};
/* Now simple IBO */
static const uint32_t ibo[4] = {0,1,2,3};
/* Construct quad mesh against boo factory */
hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo);
/* Construct quad mesh against boo factory */
hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo);
boo::ObjToken<boo::ITexture> texture = ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8,
boo::TextureClampMode::Repeat, tex, 256 * 256 * 4)
.get();
boo::ObjToken<boo::ITexture> texture =
ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat, tex, 256*256*4).get();
/* Make vertex uniform buffer */
vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get();
/* Make vertex uniform buffer */
vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get();
/* Assemble data binding */
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 */
binding = testData.newShaderDataBindng(ctx, pipeline, 1, &vubo, nullptr, 1, &texture);
binding2 = testData.newShaderDataBindng(ctx, pipeline2, 1, &vubo, nullptr, 1, &texture);
return true;
} BooTrace);
m_mainWindow->showWindow();
m_windowCb.m_latestSize = m_mainWindow->getWindowFrame();
boo::IGraphicsCommandQueue* gfxQ = m_mainWindow->getCommandQueue();
size_t frameIdx = 0;
while (m_running) {
m_mainWindow->waitForRetrace();
m_mainWindow->showWindow();
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*/)
{
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;
}
void quit(hecl::Console* /*con*/, const std::vector<std::string>& /*args*/)
{
m_running = false;
}
m_cvarManager.serialize();
gfxQ->stopRenderer();
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,
const char* file, const char* /*function*/,
int line, const char* fmt, ...)
{
static logvisor::Module Log("heclTest::AthenaExcHandler");
va_list ap;
va_start(ap, fmt);
Log.reportSource(logvisor::Level(level), file, uint32_t(line), fmt, ap);
va_end(ap);
void AthenaExcHandler(athena::error::Level level, const char* file, const char* /*function*/, int line, const char* fmt,
...) {
static logvisor::Module Log("heclTest::AthenaExcHandler");
va_list ap;
va_start(ap, fmt);
Log.reportSource(logvisor::Level(level), file, uint32_t(line), fmt, ap);
va_end(ap);
}
#if !WINDOWS_STORE
@ -273,51 +238,45 @@ int wmain(int argc, const boo::SystemChar** argv)
int main(int argc, const boo::SystemChar** argv)
#endif
{
atSetExceptionHandler(AthenaExcHandler);
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
HECLApplicationCallback appCb;
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"), argc, argv);
printf("IM DYING!!\n");
return ret;
atSetExceptionHandler(AthenaExcHandler);
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
HECLApplicationCallback appCb;
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, appCb, _SYS_STR("heclTest"),
_SYS_STR("HECL Test"), argc, argv);
printf("IM DYING!!\n");
return ret;
}
#else
using namespace Windows::ApplicationModel::Core;
[Platform::MTAThread]
int WINAPIV main(Platform::Array<Platform::String^>^ params)
{
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
HECLApplicationCallback appCb;
boo::ViewProvider^ viewProvider =
ref new boo::ViewProvider(appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"), _SYS_STR("heclTest"), params, false);
CoreApplication::Run(viewProvider);
return 0;
[Platform::MTAThread] int WINAPIV main(Platform::Array<Platform::String ^> ^ params) {
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
HECLApplicationCallback appCb;
boo::ViewProvider ^ viewProvider = ref new boo::ViewProvider(appCb, _SYS_STR("heclTest"), _SYS_STR("HECL Test"),
_SYS_STR("heclTest"), params, false);
CoreApplication::Run(viewProvider);
return 0;
}
#endif
#if _WIN32 && !WINDOWS_STORE
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i=0 ; i<argc ; ++i)
booArgv[i+1] = argv[i];
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i = 0; i < argc; ++i)
booArgv[i + 1] = argv[i];
logvisor::CreateWin32Console();
return wmain(argc + 1, booArgv);
logvisor::CreateWin32Console();
return wmain(argc + 1, booArgv);
}
#endif
HECLApplicationCallback::~HECLApplicationCallback()
{
}
HECLApplicationCallback::~HECLApplicationCallback() {}