|
|
@@ -8,116 +8,72 @@
|
|
|
#include <AnKi/Util/Logger.h>
|
|
|
#include <AnKi/Util/File.h>
|
|
|
|
|
|
-// Used by the config options
|
|
|
+// Because some cvars set their default values
|
|
|
#include <AnKi/Util/System.h>
|
|
|
-#include <AnKi/Math.h>
|
|
|
#include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
|
|
|
|
|
|
namespace anki {
|
|
|
|
|
|
-class ConfigSet::Option
|
|
|
+ConfigSet::~ConfigSet()
|
|
|
{
|
|
|
-public:
|
|
|
- enum Type
|
|
|
- {
|
|
|
- NONE,
|
|
|
- FLOAT,
|
|
|
- UNSIGNED,
|
|
|
- STRING
|
|
|
- };
|
|
|
-
|
|
|
- String m_name;
|
|
|
- String m_helpMsg;
|
|
|
-
|
|
|
- String m_str;
|
|
|
- F64 m_float = 0.0;
|
|
|
- F64 m_minFloat = 0.0;
|
|
|
- F64 m_maxFloat = 0.0;
|
|
|
- U64 m_unsigned = 0;
|
|
|
- U64 m_minUnsigned = 0;
|
|
|
- U64 m_maxUnsigned = 0;
|
|
|
-
|
|
|
- Type m_type = NONE;
|
|
|
-
|
|
|
- Option() = default;
|
|
|
-
|
|
|
- Option(const Option&) = delete; // Non-copyable
|
|
|
-
|
|
|
- Option(Option&& b)
|
|
|
- : m_name(std::move(b.m_name))
|
|
|
- , m_helpMsg(std::move(b.m_helpMsg))
|
|
|
- , m_str(std::move(b.m_str))
|
|
|
- , m_float(b.m_float)
|
|
|
- , m_minFloat(b.m_minFloat)
|
|
|
- , m_maxFloat(b.m_maxFloat)
|
|
|
- , m_unsigned(b.m_unsigned)
|
|
|
- , m_minUnsigned(b.m_minUnsigned)
|
|
|
- , m_maxUnsigned(b.m_maxUnsigned)
|
|
|
- , m_type(b.m_type)
|
|
|
+#define ANKI_CONFIG_VAR_STRING(name, defaultValue, description) m_alloc.getMemoryPool().free(m_##name.m_value);
|
|
|
+#include <AnKi/Core/AllConfigVars.defs.h>
|
|
|
+
|
|
|
+#if ANKI_EXTRA_CHECKS
|
|
|
+ for(const Var& var : m_vars)
|
|
|
{
|
|
|
+ if(var.m_timesAccessed.load() == 0)
|
|
|
+ {
|
|
|
+ ANKI_CORE_LOGW("Config var doesn't appear to have been accessed. Maybe unused: %s", var.m_name.cstr());
|
|
|
+ }
|
|
|
}
|
|
|
+#endif
|
|
|
+}
|
|
|
|
|
|
- ~Option() = default;
|
|
|
+void ConfigSet::init(AllocAlignedCallback allocCb, void* allocCbUserData)
|
|
|
+{
|
|
|
+ ANKI_ASSERT(!isInitialized());
|
|
|
+ m_alloc = HeapAllocator<U8>(allocCb, allocCbUserData);
|
|
|
|
|
|
- Option& operator=(const Option&) = delete; // Non-copyable
|
|
|
+#define ANKI_CONFIG_VAR_NUMERIC(name, defaultValue, minValue, maxValue, description) \
|
|
|
+ ANKI_ASSERT(minValue <= maxValue && defaultValue >= minValue && defaultValue <= maxValue); \
|
|
|
+ m_##name.m_name = #name; \
|
|
|
+ m_##name.m_description = description; \
|
|
|
+ m_##name.m_value = defaultValue; \
|
|
|
+ m_##name.m_min = minValue; \
|
|
|
+ m_##name.m_max = minValue; \
|
|
|
+ m_vars.pushBack(&m_##name);
|
|
|
|
|
|
- Option& operator=(Option&& b) = delete;
|
|
|
-};
|
|
|
+#define ANKI_CONFIG_VAR_U8(name, defaultValue, minValue, maxValue, description) \
|
|
|
+ ANKI_CONFIG_VAR_NUMERIC(name, defaultValue, minValue, maxValue, description)
|
|
|
|
|
|
-ConfigSet::ConfigSet()
|
|
|
-{
|
|
|
- m_alloc = HeapAllocator<U8>(allocAligned, nullptr, "ConfigSet");
|
|
|
-
|
|
|
-#define ANKI_CONFIG_OPTION(name, ...) newOption(ANKI_STRINGIZE(name), __VA_ARGS__);
|
|
|
-#include <AnKi/Core/ConfigDefs.h>
|
|
|
-#include <AnKi/Resource/ConfigDefs.h>
|
|
|
-#include <AnKi/Renderer/ConfigDefs.h>
|
|
|
-#include <AnKi/Scene/ConfigDefs.h>
|
|
|
-#include <AnKi/Gr/ConfigDefs.h>
|
|
|
-#undef ANKI_CONFIG_OPTION
|
|
|
-}
|
|
|
+#define ANKI_CONFIG_VAR_U32(name, defaultValue, minValue, maxValue, description) \
|
|
|
+ ANKI_CONFIG_VAR_NUMERIC(name, defaultValue, minValue, maxValue, description)
|
|
|
|
|
|
-ConfigSet::~ConfigSet()
|
|
|
-{
|
|
|
- for(Option& o : m_options)
|
|
|
- {
|
|
|
- o.m_name.destroy(m_alloc);
|
|
|
- o.m_str.destroy(m_alloc);
|
|
|
- o.m_helpMsg.destroy(m_alloc);
|
|
|
- }
|
|
|
+#define ANKI_CONFIG_VAR_PTR_SIZE(name, defaultValue, minValue, maxValue, description) \
|
|
|
+ ANKI_CONFIG_VAR_NUMERIC(name, defaultValue, minValue, maxValue, description)
|
|
|
|
|
|
- m_options.destroy(m_alloc);
|
|
|
-}
|
|
|
+#define ANKI_CONFIG_VAR_F32(name, defaultValue, minValue, maxValue, description) \
|
|
|
+ ANKI_CONFIG_VAR_NUMERIC(name, defaultValue, minValue, maxValue, description)
|
|
|
|
|
|
-ConfigSet& ConfigSet::operator=(const ConfigSet& b)
|
|
|
-{
|
|
|
- m_alloc = b.m_alloc; // Not a copy but we are fine
|
|
|
+#define ANKI_CONFIG_VAR_BOOL(name, defaultValue, description) \
|
|
|
+ m_##name.m_name = #name; \
|
|
|
+ m_##name.m_description = description; \
|
|
|
+ m_##name.m_value = defaultValue; \
|
|
|
+ m_vars.pushBack(&m_##name);
|
|
|
|
|
|
- for(const Option& o : b.m_options)
|
|
|
- {
|
|
|
- Option newO;
|
|
|
- newO.m_name.create(m_alloc, o.m_name.toCString());
|
|
|
- if(o.m_type == Option::STRING)
|
|
|
- {
|
|
|
- newO.m_str.create(m_alloc, o.m_str.toCString());
|
|
|
- }
|
|
|
- newO.m_float = o.m_float;
|
|
|
- newO.m_minFloat = o.m_minFloat;
|
|
|
- newO.m_maxFloat = o.m_maxFloat;
|
|
|
- newO.m_unsigned = o.m_unsigned;
|
|
|
- newO.m_minUnsigned = o.m_minUnsigned;
|
|
|
- newO.m_maxUnsigned = o.m_maxUnsigned;
|
|
|
- newO.m_type = o.m_type;
|
|
|
-
|
|
|
- m_options.emplaceBack(m_alloc, std::move(newO));
|
|
|
- }
|
|
|
+#define ANKI_CONFIG_VAR_STRING(name, defaultValue, description) \
|
|
|
+ m_##name.m_name = #name; \
|
|
|
+ m_##name.m_description = description; \
|
|
|
+ setStringVar(defaultValue, m_##name); \
|
|
|
+ m_vars.pushBack(&m_##name);
|
|
|
|
|
|
- return *this;
|
|
|
+#include <AnKi/Core/AllConfigVars.defs.h>
|
|
|
}
|
|
|
|
|
|
-ConfigSet::Option* ConfigSet::tryFind(CString optionName)
|
|
|
+ConfigSet::Var* ConfigSet::tryFind(CString optionName)
|
|
|
{
|
|
|
- for(List<Option>::Iterator it = m_options.getBegin(); it != m_options.getEnd(); ++it)
|
|
|
+ for(auto it = m_vars.getBegin(); it != m_vars.getEnd(); ++it)
|
|
|
{
|
|
|
if((*it).m_name == optionName)
|
|
|
{
|
|
|
@@ -128,9 +84,9 @@ ConfigSet::Option* ConfigSet::tryFind(CString optionName)
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
-const ConfigSet::Option* ConfigSet::tryFind(CString optionName) const
|
|
|
+const ConfigSet::Var* ConfigSet::tryFind(CString optionName) const
|
|
|
{
|
|
|
- for(List<Option>::ConstIterator it = m_options.getBegin(); it != m_options.getEnd(); ++it)
|
|
|
+ for(auto it = m_vars.getBegin(); it != m_vars.getEnd(); ++it)
|
|
|
{
|
|
|
if((*it).m_name == optionName)
|
|
|
{
|
|
|
@@ -141,154 +97,10 @@ const ConfigSet::Option* ConfigSet::tryFind(CString optionName) const
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
-void ConfigSet::newOption(CString optionName, CString value, CString helpMsg)
|
|
|
-{
|
|
|
- ANKI_ASSERT(!tryFind(optionName));
|
|
|
-
|
|
|
- Option o;
|
|
|
- o.m_name.create(m_alloc, optionName);
|
|
|
- o.m_str.create(m_alloc, value);
|
|
|
- o.m_type = Option::STRING;
|
|
|
- if(!helpMsg.isEmpty())
|
|
|
- {
|
|
|
- o.m_helpMsg.create(m_alloc, helpMsg);
|
|
|
- }
|
|
|
-
|
|
|
- m_options.emplaceBack(m_alloc, std::move(o));
|
|
|
-}
|
|
|
-
|
|
|
-void ConfigSet::newOptionInternal(CString optionName, F64 value, F64 minValue, F64 maxValue, CString helpMsg)
|
|
|
-{
|
|
|
- ANKI_ASSERT(!tryFind(optionName));
|
|
|
- ANKI_ASSERT(value >= minValue && value <= maxValue && minValue <= maxValue);
|
|
|
-
|
|
|
- Option o;
|
|
|
- o.m_name.create(m_alloc, optionName);
|
|
|
- o.m_float = value;
|
|
|
- o.m_minFloat = minValue;
|
|
|
- o.m_maxFloat = maxValue;
|
|
|
- o.m_type = Option::FLOAT;
|
|
|
- if(!helpMsg.isEmpty())
|
|
|
- {
|
|
|
- o.m_helpMsg.create(m_alloc, helpMsg);
|
|
|
- }
|
|
|
-
|
|
|
- m_options.emplaceBack(m_alloc, std::move(o));
|
|
|
-}
|
|
|
-
|
|
|
-void ConfigSet::newOptionInternal(CString optionName, U64 value, U64 minValue, U64 maxValue, CString helpMsg)
|
|
|
-{
|
|
|
- ANKI_ASSERT(!tryFind(optionName));
|
|
|
- ANKI_ASSERT(value >= minValue && value <= maxValue && minValue <= maxValue);
|
|
|
-
|
|
|
- Option o;
|
|
|
- o.m_name.create(m_alloc, optionName);
|
|
|
- o.m_unsigned = value;
|
|
|
- o.m_minUnsigned = minValue;
|
|
|
- o.m_maxUnsigned = maxValue;
|
|
|
- o.m_type = Option::UNSIGNED;
|
|
|
- if(!helpMsg.isEmpty())
|
|
|
- {
|
|
|
- o.m_helpMsg.create(m_alloc, helpMsg);
|
|
|
- }
|
|
|
-
|
|
|
- m_options.emplaceBack(m_alloc, std::move(o));
|
|
|
-}
|
|
|
-
|
|
|
-void ConfigSet::set(CString optionName, CString value)
|
|
|
-{
|
|
|
- Option& o = find(optionName);
|
|
|
- ANKI_ASSERT(o.m_type == Option::STRING);
|
|
|
- o.m_str.destroy(m_alloc);
|
|
|
- o.m_str.create(m_alloc, value);
|
|
|
-}
|
|
|
-
|
|
|
-void ConfigSet::setInternal(CString optionName, F64 value)
|
|
|
-{
|
|
|
- Option& o = find(optionName);
|
|
|
- ANKI_ASSERT(o.m_type == Option::FLOAT);
|
|
|
- ANKI_ASSERT(value >= o.m_minFloat);
|
|
|
- ANKI_ASSERT(value <= o.m_maxFloat);
|
|
|
- o.m_float = value;
|
|
|
-}
|
|
|
-
|
|
|
-void ConfigSet::setInternal(CString optionName, U64 value)
|
|
|
-{
|
|
|
- Option& o = find(optionName);
|
|
|
- ANKI_ASSERT(o.m_type == Option::UNSIGNED);
|
|
|
- ANKI_ASSERT(value >= o.m_minUnsigned);
|
|
|
- ANKI_ASSERT(value <= o.m_maxUnsigned);
|
|
|
- o.m_unsigned = value;
|
|
|
-}
|
|
|
-
|
|
|
-F64 ConfigSet::getNumberF64(CString optionName) const
|
|
|
-{
|
|
|
- const Option& option = find(optionName);
|
|
|
- ANKI_ASSERT(option.m_type == Option::FLOAT);
|
|
|
- return option.m_float;
|
|
|
-}
|
|
|
-
|
|
|
-F32 ConfigSet::getNumberF32(CString optionName) const
|
|
|
-{
|
|
|
- return F32(getNumberF64(optionName));
|
|
|
-}
|
|
|
-
|
|
|
-U64 ConfigSet::getNumberU64(CString optionName) const
|
|
|
-{
|
|
|
- const Option& option = find(optionName);
|
|
|
- ANKI_ASSERT(option.m_type == Option::UNSIGNED);
|
|
|
- return option.m_unsigned;
|
|
|
-}
|
|
|
-
|
|
|
-U32 ConfigSet::getNumberU32(CString optionName) const
|
|
|
-{
|
|
|
- const U64 out = getNumberU64(optionName);
|
|
|
- if(out > MAX_U32)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Option is out of U32 range: %s", optionName.cstr());
|
|
|
- }
|
|
|
- return U32(out);
|
|
|
-}
|
|
|
-
|
|
|
-U16 ConfigSet::getNumberU16(CString optionName) const
|
|
|
-{
|
|
|
- const U64 out = getNumberU64(optionName);
|
|
|
- if(out > MAX_U16)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Option is out of U16 range: %s", optionName.cstr());
|
|
|
- }
|
|
|
- return U16(out);
|
|
|
-}
|
|
|
-
|
|
|
-U8 ConfigSet::getNumberU8(CString optionName) const
|
|
|
-{
|
|
|
- const U64 out = getNumberU64(optionName);
|
|
|
- if(out > MAX_U8)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Option is out of U8 range: %s", optionName.cstr());
|
|
|
- }
|
|
|
- return U8(out);
|
|
|
-}
|
|
|
-
|
|
|
-Bool ConfigSet::getBool(CString optionName) const
|
|
|
-{
|
|
|
- const U64 val = getNumberU64(optionName);
|
|
|
- if((val & ~U64(1)) != 0)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Expecting 0 or 1 for the config option \"%s\". Will mask out extra bits", optionName.cstr());
|
|
|
- }
|
|
|
- return val & 1;
|
|
|
-}
|
|
|
-
|
|
|
-CString ConfigSet::getString(CString optionName) const
|
|
|
-{
|
|
|
- const Option& o = find(optionName);
|
|
|
- ANKI_ASSERT(o.m_type == Option::STRING);
|
|
|
- return o.m_str.toCString();
|
|
|
-}
|
|
|
-
|
|
|
Error ConfigSet::loadFromFile(CString filename)
|
|
|
{
|
|
|
+ ANKI_ASSERT(isInitialized());
|
|
|
+
|
|
|
ANKI_CORE_LOGI("Loading config file %s", filename.cstr());
|
|
|
XmlDocument xml;
|
|
|
ANKI_CHECK(xml.loadFile(filename, m_alloc));
|
|
|
@@ -296,56 +108,59 @@ Error ConfigSet::loadFromFile(CString filename)
|
|
|
XmlElement rootel;
|
|
|
ANKI_CHECK(xml.getChildElement("config", rootel));
|
|
|
|
|
|
- for(Option& option : m_options)
|
|
|
- {
|
|
|
- XmlElement el;
|
|
|
- ANKI_CHECK(rootel.getChildElementOptional(option.m_name.toCString(), el));
|
|
|
+ XmlElement el;
|
|
|
|
|
|
- if(el)
|
|
|
- {
|
|
|
- if(option.m_type == Option::FLOAT)
|
|
|
- {
|
|
|
- ANKI_CHECK(el.getNumber(option.m_float));
|
|
|
- }
|
|
|
- else if(option.m_type == Option::UNSIGNED)
|
|
|
- {
|
|
|
- ANKI_CHECK(el.getNumber(option.m_unsigned));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ANKI_ASSERT(option.m_type == Option::STRING);
|
|
|
- CString txt;
|
|
|
- ANKI_CHECK(el.getText(txt));
|
|
|
- option.m_str.destroy(m_alloc);
|
|
|
- option.m_str.create(m_alloc, txt);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if(option.m_type == Option::FLOAT)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %f", &option.m_name[0],
|
|
|
- option.m_float);
|
|
|
- }
|
|
|
- else if(option.m_type == Option::UNSIGNED)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %" PRIu64, &option.m_name[0],
|
|
|
- option.m_unsigned);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ANKI_ASSERT(option.m_type == Option::STRING);
|
|
|
- ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %s", option.m_name.cstr(),
|
|
|
- option.m_str.cstr());
|
|
|
- }
|
|
|
- }
|
|
|
+#define ANKI_NUMERIC(name) \
|
|
|
+ ANKI_CHECK(rootel.getChildElementOptional(m_##name.m_name, el)); \
|
|
|
+ if(el) \
|
|
|
+ { \
|
|
|
+ ANKI_CHECK(el.getNumber(m_##name.m_value)); \
|
|
|
+ }
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_U8(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_U32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_PTR_SIZE(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_F32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_BOOL(name, defaultValue, description) \
|
|
|
+ ANKI_CHECK(rootel.getChildElementOptional(m_##name.m_name, el)); \
|
|
|
+ if(el) \
|
|
|
+ { \
|
|
|
+ CString txt; \
|
|
|
+ ANKI_CHECK(el.getText(txt)); \
|
|
|
+ if(txt == "true") \
|
|
|
+ { \
|
|
|
+ m_##name.m_value = true; \
|
|
|
+ } \
|
|
|
+ else if(txt == "false") \
|
|
|
+ { \
|
|
|
+ m_##name.m_value = false; \
|
|
|
+ } \
|
|
|
+ else \
|
|
|
+ { \
|
|
|
+ ANKI_CORE_LOGE("Wrong value for %s", m_##name.m_name.cstr()); \
|
|
|
+ return Error::USER_DATA; \
|
|
|
+ } \
|
|
|
}
|
|
|
|
|
|
+#define ANKI_CONFIG_VAR_STRING(name, defaultValue, description) \
|
|
|
+ ANKI_CHECK(rootel.getChildElementOptional(m_##name.m_name, el)); \
|
|
|
+ if(el) \
|
|
|
+ { \
|
|
|
+ CString txt; \
|
|
|
+ ANKI_CHECK(el.getText(txt)); \
|
|
|
+ setStringVar(txt, m_##name); \
|
|
|
+ }
|
|
|
+
|
|
|
+#include <AnKi/Core/AllConfigVars.defs.h>
|
|
|
+#undef ANKI_NUMERIC
|
|
|
+
|
|
|
return Error::NONE;
|
|
|
}
|
|
|
|
|
|
Error ConfigSet::saveToFile(CString filename) const
|
|
|
{
|
|
|
+ ANKI_ASSERT(isInitialized());
|
|
|
ANKI_CORE_LOGI("Saving config file %s", &filename[0]);
|
|
|
|
|
|
File file;
|
|
|
@@ -353,29 +168,24 @@ Error ConfigSet::saveToFile(CString filename) const
|
|
|
|
|
|
ANKI_CHECK(file.writeText("%s\n<config>\n", XmlDocument::XML_HEADER.cstr()));
|
|
|
|
|
|
- for(const Option& option : m_options)
|
|
|
- {
|
|
|
- if(!option.m_helpMsg.isEmpty())
|
|
|
- {
|
|
|
- ANKI_CHECK(file.writeText("\t<!-- %s -->\n", option.m_helpMsg.cstr()));
|
|
|
- }
|
|
|
-
|
|
|
- if(option.m_type == Option::FLOAT)
|
|
|
- {
|
|
|
- ANKI_CHECK(file.writeText("\t<%s>%f</%s>\n", option.m_name.cstr(), option.m_float, option.m_name.cstr()));
|
|
|
- }
|
|
|
- else if(option.m_type == Option::UNSIGNED)
|
|
|
- {
|
|
|
- ANKI_CHECK(file.writeText("\t<%s>%" PRIu64 "</%s>\n", option.m_name.cstr(), option.m_unsigned,
|
|
|
- option.m_name.cstr()));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ANKI_ASSERT(option.m_type == Option::STRING);
|
|
|
- ANKI_CHECK(file.writeText("\t<%s><![CDATA[%s]]></%s>\n", option.m_name.cstr(), option.m_str.cstr(),
|
|
|
- option.m_name.cstr()));
|
|
|
- }
|
|
|
- }
|
|
|
+#define ANKI_NUMERIC_UINT(name) \
|
|
|
+ ANKI_CHECK(file.writeText("\t<!-- %s -->\n", m_##name.m_description.cstr())); \
|
|
|
+ ANKI_CHECK( \
|
|
|
+ file.writeText("\t<%s>%llu</%s>\n", m_##name.m_name.cstr(), U64(m_##name.m_value), m_##name.m_name.cstr()));
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_U8(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC_UINT(name)
|
|
|
+#define ANKI_CONFIG_VAR_U32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC_UINT(name)
|
|
|
+#define ANKI_CONFIG_VAR_PTR_SIZE(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC_UINT(name)
|
|
|
+#define ANKI_CONFIG_VAR_F32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC_UINT(name)
|
|
|
+#define ANKI_CONFIG_VAR_BOOL(name, defaultValue, description) \
|
|
|
+ ANKI_CHECK(file.writeText("\t<!-- %s -->\n", m_##name.m_description.cstr())); \
|
|
|
+ ANKI_CHECK(file.writeText("\t<%s>%s</%s>\n", m_##name.m_name.cstr(), (m_##name.m_value) ? "true" : "false", \
|
|
|
+ m_##name.m_name.cstr()));
|
|
|
+#define ANKI_CONFIG_VAR_STRING(name, defaultValue, description) \
|
|
|
+ ANKI_CHECK(file.writeText("\t<!-- %s -->\n", m_##name.m_description.cstr())); \
|
|
|
+ ANKI_CHECK(file.writeText("\t<%s>%s</%s>\n", m_##name.m_name.cstr(), m_##name.m_value, m_##name.m_name.cstr()));
|
|
|
+#include <AnKi/Core/AllConfigVars.defs.h>
|
|
|
+#undef ANKI_NUMERIC_UINT
|
|
|
|
|
|
ANKI_CHECK(file.writeText("</config>\n"));
|
|
|
return Error::NONE;
|
|
|
@@ -383,43 +193,58 @@ Error ConfigSet::saveToFile(CString filename) const
|
|
|
|
|
|
Error ConfigSet::setFromCommandLineArguments(U32 cmdLineArgsCount, char* cmdLineArgs[])
|
|
|
{
|
|
|
+ ANKI_ASSERT(isInitialized());
|
|
|
+
|
|
|
for(U i = 0; i < cmdLineArgsCount; ++i)
|
|
|
{
|
|
|
- const char* arg = cmdLineArgs[i];
|
|
|
- ANKI_ASSERT(arg);
|
|
|
-
|
|
|
- Option* option = tryFind(arg);
|
|
|
- if(option == nullptr)
|
|
|
- {
|
|
|
- ANKI_CORE_LOGW("Can't recognize command line argument: %s", arg);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ ANKI_ASSERT(cmdLineArgs[i]);
|
|
|
+ const CString varName = cmdLineArgs[i];
|
|
|
|
|
|
// Set the value
|
|
|
++i;
|
|
|
if(i >= cmdLineArgsCount)
|
|
|
{
|
|
|
- ANKI_CORE_LOGE("Expecting a command line argument after %s", arg);
|
|
|
+ ANKI_CORE_LOGE("Expecting a command line argument after %s", varName.cstr());
|
|
|
return Error::USER_DATA;
|
|
|
}
|
|
|
|
|
|
- arg = cmdLineArgs[i];
|
|
|
- ANKI_ASSERT(arg);
|
|
|
- if(option->m_type == Option::STRING)
|
|
|
- {
|
|
|
- option->m_str.destroy(m_alloc);
|
|
|
- option->m_str.create(m_alloc, arg);
|
|
|
- }
|
|
|
- else if(option->m_type == Option::FLOAT)
|
|
|
+ ANKI_ASSERT(cmdLineArgs[i]);
|
|
|
+ const CString value = cmdLineArgs[i];
|
|
|
+
|
|
|
+ if(false)
|
|
|
{
|
|
|
- CString val(arg);
|
|
|
- ANKI_CHECK(val.toNumber(option->m_float));
|
|
|
}
|
|
|
+
|
|
|
+#define ANKI_NUMERIC(name) \
|
|
|
+ else if(varName == m_##name.m_name) \
|
|
|
+ { \
|
|
|
+ ANKI_CHECK(value.toNumber(m_##name.m_value)); \
|
|
|
+ }
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_U8(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_U32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_PTR_SIZE(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+#define ANKI_CONFIG_VAR_F32(name, defaultValue, minValue, maxValue, description) ANKI_NUMERIC(name)
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_BOOL(name, defaultValue, description) \
|
|
|
+ else if(varName == m_##name.m_name) \
|
|
|
+ { \
|
|
|
+ U32 v; \
|
|
|
+ ANKI_CHECK(value.toNumber(v)); \
|
|
|
+ m_##name.m_value = v != 0; \
|
|
|
+ }
|
|
|
+
|
|
|
+#define ANKI_CONFIG_VAR_STRING(name, defaultValue, description) \
|
|
|
+ else if(varName == m_##name.m_name) \
|
|
|
+ { \
|
|
|
+ setStringVar(value, m_##name); \
|
|
|
+ }
|
|
|
+
|
|
|
+#include <AnKi/Core/AllConfigVars.defs.h>
|
|
|
+#undef ANKI_NUMERIC
|
|
|
else
|
|
|
{
|
|
|
- ANKI_ASSERT(option->m_type == Option::UNSIGNED);
|
|
|
- CString val(arg);
|
|
|
- ANKI_CHECK(val.toNumber(option->m_unsigned));
|
|
|
+ ANKI_CORE_LOGW("Can't recognize command line argument: %s. Skipping", varName.cstr());
|
|
|
}
|
|
|
}
|
|
|
|