ConfigSet.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Core/ConfigSet.h>
  6. #include <AnKi/Util/Xml.h>
  7. #include <AnKi/Util/Logger.h>
  8. #include <AnKi/Util/File.h>
  9. // Used by the config options
  10. #include <AnKi/Util/System.h>
  11. #include <AnKi/Math.h>
  12. #include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
  13. namespace anki
  14. {
  15. class ConfigSet::Option
  16. {
  17. public:
  18. enum Type
  19. {
  20. NONE,
  21. FLOAT,
  22. UNSIGNED,
  23. STRING
  24. };
  25. String m_name;
  26. String m_helpMsg;
  27. String m_str;
  28. F64 m_float = 0.0;
  29. F64 m_minFloat = 0.0;
  30. F64 m_maxFloat = 0.0;
  31. U64 m_unsigned = 0;
  32. U64 m_minUnsigned = 0;
  33. U64 m_maxUnsigned = 0;
  34. Type m_type = NONE;
  35. Option() = default;
  36. Option(const Option&) = delete; // Non-copyable
  37. Option(Option&& b)
  38. : m_name(std::move(b.m_name))
  39. , m_helpMsg(std::move(b.m_helpMsg))
  40. , m_str(std::move(b.m_str))
  41. , m_float(b.m_float)
  42. , m_minFloat(b.m_minFloat)
  43. , m_maxFloat(b.m_maxFloat)
  44. , m_unsigned(b.m_unsigned)
  45. , m_minUnsigned(b.m_minUnsigned)
  46. , m_maxUnsigned(b.m_maxUnsigned)
  47. , m_type(b.m_type)
  48. {
  49. }
  50. ~Option() = default;
  51. Option& operator=(const Option&) = delete; // Non-copyable
  52. Option& operator=(Option&& b) = delete;
  53. };
  54. ConfigSet::ConfigSet()
  55. {
  56. m_alloc = HeapAllocator<U8>(allocAligned, nullptr);
  57. #define ANKI_CONFIG_OPTION(name, ...) newOption(ANKI_STRINGIZE(name), __VA_ARGS__);
  58. #include <AnKi/Core/ConfigDefs.h>
  59. #include <AnKi/Resource/ConfigDefs.h>
  60. #include <AnKi/Renderer/ConfigDefs.h>
  61. #include <AnKi/Scene/ConfigDefs.h>
  62. #include <AnKi/Gr/ConfigDefs.h>
  63. #undef ANKI_CONFIG_OPTION
  64. }
  65. ConfigSet::~ConfigSet()
  66. {
  67. for(Option& o : m_options)
  68. {
  69. o.m_name.destroy(m_alloc);
  70. o.m_str.destroy(m_alloc);
  71. o.m_helpMsg.destroy(m_alloc);
  72. }
  73. m_options.destroy(m_alloc);
  74. }
  75. ConfigSet& ConfigSet::operator=(const ConfigSet& b)
  76. {
  77. m_alloc = b.m_alloc; // Not a copy but we are fine
  78. for(const Option& o : b.m_options)
  79. {
  80. Option newO;
  81. newO.m_name.create(m_alloc, o.m_name.toCString());
  82. if(o.m_type == Option::STRING)
  83. {
  84. newO.m_str.create(m_alloc, o.m_str.toCString());
  85. }
  86. newO.m_float = o.m_float;
  87. newO.m_minFloat = o.m_minFloat;
  88. newO.m_maxFloat = o.m_maxFloat;
  89. newO.m_unsigned = o.m_unsigned;
  90. newO.m_minUnsigned = o.m_minUnsigned;
  91. newO.m_maxUnsigned = o.m_maxUnsigned;
  92. newO.m_type = o.m_type;
  93. m_options.emplaceBack(m_alloc, std::move(newO));
  94. }
  95. return *this;
  96. }
  97. ConfigSet::Option* ConfigSet::tryFind(CString optionName)
  98. {
  99. for(List<Option>::Iterator it = m_options.getBegin(); it != m_options.getEnd(); ++it)
  100. {
  101. if((*it).m_name == optionName)
  102. {
  103. return &(*it);
  104. }
  105. }
  106. return nullptr;
  107. }
  108. const ConfigSet::Option* ConfigSet::tryFind(CString optionName) const
  109. {
  110. for(List<Option>::ConstIterator it = m_options.getBegin(); it != m_options.getEnd(); ++it)
  111. {
  112. if((*it).m_name == optionName)
  113. {
  114. return &(*it);
  115. }
  116. }
  117. return nullptr;
  118. }
  119. void ConfigSet::newOption(CString optionName, CString value, CString helpMsg)
  120. {
  121. ANKI_ASSERT(!tryFind(optionName));
  122. Option o;
  123. o.m_name.create(m_alloc, optionName);
  124. o.m_str.create(m_alloc, value);
  125. o.m_type = Option::STRING;
  126. if(!helpMsg.isEmpty())
  127. {
  128. o.m_helpMsg.create(m_alloc, helpMsg);
  129. }
  130. m_options.emplaceBack(m_alloc, std::move(o));
  131. }
  132. void ConfigSet::newOptionInternal(CString optionName, F64 value, F64 minValue, F64 maxValue, CString helpMsg)
  133. {
  134. ANKI_ASSERT(!tryFind(optionName));
  135. ANKI_ASSERT(value >= minValue && value <= maxValue && minValue <= maxValue);
  136. Option o;
  137. o.m_name.create(m_alloc, optionName);
  138. o.m_float = value;
  139. o.m_minFloat = minValue;
  140. o.m_maxFloat = maxValue;
  141. o.m_type = Option::FLOAT;
  142. if(!helpMsg.isEmpty())
  143. {
  144. o.m_helpMsg.create(m_alloc, helpMsg);
  145. }
  146. m_options.emplaceBack(m_alloc, std::move(o));
  147. }
  148. void ConfigSet::newOptionInternal(CString optionName, U64 value, U64 minValue, U64 maxValue, CString helpMsg)
  149. {
  150. ANKI_ASSERT(!tryFind(optionName));
  151. ANKI_ASSERT(value >= minValue && value <= maxValue && minValue <= maxValue);
  152. Option o;
  153. o.m_name.create(m_alloc, optionName);
  154. o.m_unsigned = value;
  155. o.m_minUnsigned = minValue;
  156. o.m_maxUnsigned = maxValue;
  157. o.m_type = Option::UNSIGNED;
  158. if(!helpMsg.isEmpty())
  159. {
  160. o.m_helpMsg.create(m_alloc, helpMsg);
  161. }
  162. m_options.emplaceBack(m_alloc, std::move(o));
  163. }
  164. void ConfigSet::set(CString optionName, CString value)
  165. {
  166. Option& o = find(optionName);
  167. ANKI_ASSERT(o.m_type == Option::STRING);
  168. o.m_str.destroy(m_alloc);
  169. o.m_str.create(m_alloc, value);
  170. }
  171. void ConfigSet::setInternal(CString optionName, F64 value)
  172. {
  173. Option& o = find(optionName);
  174. ANKI_ASSERT(o.m_type == Option::FLOAT);
  175. ANKI_ASSERT(value >= o.m_minFloat);
  176. ANKI_ASSERT(value <= o.m_maxFloat);
  177. o.m_float = value;
  178. }
  179. void ConfigSet::setInternal(CString optionName, U64 value)
  180. {
  181. Option& o = find(optionName);
  182. ANKI_ASSERT(o.m_type == Option::UNSIGNED);
  183. ANKI_ASSERT(value >= o.m_minUnsigned);
  184. ANKI_ASSERT(value <= o.m_maxUnsigned);
  185. o.m_unsigned = value;
  186. }
  187. F64 ConfigSet::getNumberF64(CString optionName) const
  188. {
  189. const Option& option = find(optionName);
  190. ANKI_ASSERT(option.m_type == Option::FLOAT);
  191. return option.m_float;
  192. }
  193. F32 ConfigSet::getNumberF32(CString optionName) const
  194. {
  195. return F32(getNumberF64(optionName));
  196. }
  197. U64 ConfigSet::getNumberU64(CString optionName) const
  198. {
  199. const Option& option = find(optionName);
  200. ANKI_ASSERT(option.m_type == Option::UNSIGNED);
  201. return option.m_unsigned;
  202. }
  203. U32 ConfigSet::getNumberU32(CString optionName) const
  204. {
  205. const U64 out = getNumberU64(optionName);
  206. if(out > MAX_U32)
  207. {
  208. ANKI_CORE_LOGW("Option is out of U32 range: %s", optionName.cstr());
  209. }
  210. return U32(out);
  211. }
  212. U16 ConfigSet::getNumberU16(CString optionName) const
  213. {
  214. const U64 out = getNumberU64(optionName);
  215. if(out > MAX_U16)
  216. {
  217. ANKI_CORE_LOGW("Option is out of U16 range: %s", optionName.cstr());
  218. }
  219. return U16(out);
  220. }
  221. U8 ConfigSet::getNumberU8(CString optionName) const
  222. {
  223. const U64 out = getNumberU64(optionName);
  224. if(out > MAX_U8)
  225. {
  226. ANKI_CORE_LOGW("Option is out of U8 range: %s", optionName.cstr());
  227. }
  228. return U8(out);
  229. }
  230. Bool ConfigSet::getBool(CString optionName) const
  231. {
  232. const U64 val = getNumberU64(optionName);
  233. if((val & ~U64(1)) != 0)
  234. {
  235. ANKI_CORE_LOGW("Expecting 0 or 1 for the config option \"%s\". Will mask out extra bits", optionName.cstr());
  236. }
  237. return val & 1;
  238. }
  239. CString ConfigSet::getString(CString optionName) const
  240. {
  241. const Option& o = find(optionName);
  242. ANKI_ASSERT(o.m_type == Option::STRING);
  243. return o.m_str.toCString();
  244. }
  245. Error ConfigSet::loadFromFile(CString filename)
  246. {
  247. ANKI_CORE_LOGI("Loading config file %s", filename.cstr());
  248. XmlDocument xml;
  249. ANKI_CHECK(xml.loadFile(filename, m_alloc));
  250. XmlElement rootel;
  251. ANKI_CHECK(xml.getChildElement("config", rootel));
  252. for(Option& option : m_options)
  253. {
  254. XmlElement el;
  255. ANKI_CHECK(rootel.getChildElementOptional(option.m_name.toCString(), el));
  256. if(el)
  257. {
  258. if(option.m_type == Option::FLOAT)
  259. {
  260. ANKI_CHECK(el.getNumber(option.m_float));
  261. }
  262. else if(option.m_type == Option::UNSIGNED)
  263. {
  264. ANKI_CHECK(el.getNumber(option.m_unsigned));
  265. }
  266. else
  267. {
  268. ANKI_ASSERT(option.m_type == Option::STRING);
  269. CString txt;
  270. ANKI_CHECK(el.getText(txt));
  271. option.m_str.destroy(m_alloc);
  272. option.m_str.create(m_alloc, txt);
  273. }
  274. }
  275. else
  276. {
  277. if(option.m_type == Option::FLOAT)
  278. {
  279. ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %f", &option.m_name[0],
  280. option.m_float);
  281. }
  282. else if(option.m_type == Option::UNSIGNED)
  283. {
  284. ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %" PRIu64, &option.m_name[0],
  285. option.m_unsigned);
  286. }
  287. else
  288. {
  289. ANKI_ASSERT(option.m_type == Option::STRING);
  290. ANKI_CORE_LOGW("Missing option for \"%s\". Will use the default value: %s", option.m_name.cstr(),
  291. option.m_str.cstr());
  292. }
  293. }
  294. }
  295. return Error::NONE;
  296. }
  297. Error ConfigSet::saveToFile(CString filename) const
  298. {
  299. ANKI_CORE_LOGI("Saving config file %s", &filename[0]);
  300. File file;
  301. ANKI_CHECK(file.open(filename, FileOpenFlag::WRITE));
  302. ANKI_CHECK(file.writeText("%s\n<config>\n", XmlDocument::XML_HEADER.cstr()));
  303. for(const Option& option : m_options)
  304. {
  305. if(!option.m_helpMsg.isEmpty())
  306. {
  307. ANKI_CHECK(file.writeText("\t<!-- %s -->\n", option.m_helpMsg.cstr()));
  308. }
  309. if(option.m_type == Option::FLOAT)
  310. {
  311. ANKI_CHECK(file.writeText("\t<%s>%f</%s>\n", option.m_name.cstr(), option.m_float, option.m_name.cstr()));
  312. }
  313. else if(option.m_type == Option::UNSIGNED)
  314. {
  315. ANKI_CHECK(file.writeText("\t<%s>%" PRIu64 "</%s>\n", option.m_name.cstr(), option.m_unsigned,
  316. option.m_name.cstr()));
  317. }
  318. else
  319. {
  320. ANKI_ASSERT(option.m_type == Option::STRING);
  321. ANKI_CHECK(file.writeText("\t<%s><![CDATA[%s]]></%s>\n", option.m_name.cstr(), option.m_str.cstr(),
  322. option.m_name.cstr()));
  323. }
  324. }
  325. ANKI_CHECK(file.writeText("</config>\n"));
  326. return Error::NONE;
  327. }
  328. Error ConfigSet::setFromCommandLineArguments(U32 cmdLineArgsCount, char* cmdLineArgs[])
  329. {
  330. for(U i = 0; i < cmdLineArgsCount; ++i)
  331. {
  332. const char* arg = cmdLineArgs[i];
  333. ANKI_ASSERT(arg);
  334. Option* option = tryFind(arg);
  335. if(option == nullptr)
  336. {
  337. ANKI_CORE_LOGW("Can't recognize command line argument: %s", arg);
  338. continue;
  339. }
  340. // Set the value
  341. ++i;
  342. if(i >= cmdLineArgsCount)
  343. {
  344. ANKI_CORE_LOGE("Expecting a command line argument after %s", arg);
  345. return Error::USER_DATA;
  346. }
  347. arg = cmdLineArgs[i];
  348. ANKI_ASSERT(arg);
  349. if(option->m_type == Option::STRING)
  350. {
  351. option->m_str.destroy(m_alloc);
  352. option->m_str.create(m_alloc, arg);
  353. }
  354. else if(option->m_type == Option::FLOAT)
  355. {
  356. CString val(arg);
  357. ANKI_CHECK(val.toNumber(option->m_float));
  358. }
  359. else
  360. {
  361. ANKI_ASSERT(option->m_type == Option::UNSIGNED);
  362. CString val(arg);
  363. ANKI_CHECK(val.toNumber(option->m_unsigned));
  364. }
  365. }
  366. return Error::NONE;
  367. }
  368. } // end namespace anki