GraphicsTarget.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. // Copyright 2022 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h"
  4. #include "Common/Logging/Log.h"
  5. #include "Common/StringUtil.h"
  6. #include "Common/VariantUtil.h"
  7. #include "VideoCommon/TextureCacheBase.h"
  8. namespace
  9. {
  10. template <typename T, std::enable_if_t<std::is_base_of_v<FBTarget, T>, int> = 0>
  11. std::optional<T> DeserializeFBTargetFromConfig(const picojson::object& obj, std::string_view prefix)
  12. {
  13. T fb;
  14. const auto texture_filename_iter = obj.find("texture_filename");
  15. if (texture_filename_iter == obj.end())
  16. {
  17. ERROR_LOG_FMT(VIDEO,
  18. "Failed to load mod configuration file, option 'texture_filename' not found");
  19. return std::nullopt;
  20. }
  21. if (!texture_filename_iter->second.is<std::string>())
  22. {
  23. ERROR_LOG_FMT(
  24. VIDEO,
  25. "Failed to load mod configuration file, option 'texture_filename' is not a string type");
  26. return std::nullopt;
  27. }
  28. const auto texture_filename = texture_filename_iter->second.get<std::string>();
  29. const auto texture_filename_without_prefix = texture_filename.substr(prefix.size() + 1);
  30. const auto split_str_values = SplitString(texture_filename_without_prefix, '_');
  31. if (split_str_values.size() == 1)
  32. {
  33. ERROR_LOG_FMT(
  34. VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is not valid");
  35. return std::nullopt;
  36. }
  37. const auto split_width_height_values = SplitString(texture_filename_without_prefix, 'x');
  38. if (split_width_height_values.size() != 2)
  39. {
  40. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  41. "not valid, width and height separator found more matches than expected");
  42. return std::nullopt;
  43. }
  44. const std::size_t width_underscore_pos = split_width_height_values[0].find_last_of('_');
  45. std::string width_str;
  46. if (width_underscore_pos == std::string::npos)
  47. {
  48. width_str = split_width_height_values[0];
  49. }
  50. else
  51. {
  52. width_str = split_width_height_values[0].substr(width_underscore_pos + 1);
  53. }
  54. if (!TryParse(width_str, &fb.m_width))
  55. {
  56. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  57. "not valid, width not a number");
  58. return std::nullopt;
  59. }
  60. const std::size_t height_underscore_pos = split_width_height_values[1].find_first_of('_');
  61. if (height_underscore_pos == std::string::npos ||
  62. height_underscore_pos == split_width_height_values[1].size() - 1)
  63. {
  64. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  65. "not valid, underscore after height is missing or incomplete");
  66. return std::nullopt;
  67. }
  68. const std::string height_str = split_width_height_values[1].substr(0, height_underscore_pos);
  69. if (!TryParse(height_str, &fb.m_height))
  70. {
  71. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  72. "not valid, height not a number");
  73. return std::nullopt;
  74. }
  75. const std::size_t format_underscore_pos =
  76. split_width_height_values[1].find_first_of('_', height_underscore_pos + 1);
  77. std::string format_str;
  78. if (format_underscore_pos == std::string::npos)
  79. {
  80. format_str = split_width_height_values[1].substr(height_underscore_pos + 1);
  81. }
  82. else
  83. {
  84. format_str = split_width_height_values[1].substr(
  85. height_underscore_pos + 1, (format_underscore_pos - height_underscore_pos) - 1);
  86. }
  87. u32 format;
  88. if (!TryParse(format_str, &format))
  89. {
  90. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  91. "not valid, texture format is not a number");
  92. return std::nullopt;
  93. }
  94. if (!IsValidTextureFormat(static_cast<TextureFormat>(format)))
  95. {
  96. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
  97. "not valid, texture format is not valid");
  98. return std::nullopt;
  99. }
  100. fb.m_texture_format = static_cast<TextureFormat>(format);
  101. return fb;
  102. }
  103. std::optional<std::string> ExtractTextureFilenameForConfig(const picojson::object& obj)
  104. {
  105. const auto texture_filename_iter = obj.find("texture_filename");
  106. if (texture_filename_iter == obj.end())
  107. {
  108. ERROR_LOG_FMT(VIDEO,
  109. "Failed to load mod configuration file, option 'texture_filename' not found");
  110. return std::nullopt;
  111. }
  112. if (!texture_filename_iter->second.is<std::string>())
  113. {
  114. ERROR_LOG_FMT(
  115. VIDEO,
  116. "Failed to load mod configuration file, option 'texture_filename' is not a string type");
  117. return std::nullopt;
  118. }
  119. std::string texture_info = texture_filename_iter->second.get<std::string>();
  120. const auto handle_fb_texture =
  121. [&texture_info](std::string_view type) -> std::optional<std::string> {
  122. const auto letter_n_pos = texture_info.find("_n");
  123. if (letter_n_pos == std::string::npos)
  124. {
  125. ERROR_LOG_FMT(VIDEO,
  126. "Failed to load mod configuration file, value in 'texture_filename' "
  127. "is {} without a count",
  128. type);
  129. return std::nullopt;
  130. }
  131. const auto post_underscore = texture_info.find_first_of('_', letter_n_pos + 2);
  132. if (post_underscore == std::string::npos)
  133. return texture_info.erase(letter_n_pos, texture_info.size() - letter_n_pos);
  134. else
  135. return texture_info.erase(letter_n_pos, post_underscore - letter_n_pos);
  136. };
  137. if (texture_info.starts_with(EFB_DUMP_PREFIX))
  138. return handle_fb_texture("an efb");
  139. else if (texture_info.starts_with(XFB_DUMP_PREFIX))
  140. return handle_fb_texture("a xfb");
  141. return texture_info;
  142. }
  143. } // namespace
  144. void SerializeTargetToConfig(picojson::object& json_obj, const GraphicsTargetConfig& target)
  145. {
  146. std::visit(overloaded{
  147. [&](const DrawStartedTextureTarget& the_target) {
  148. json_obj.emplace("type", "draw_started");
  149. json_obj.emplace("texture_filename", the_target.m_texture_info_string);
  150. },
  151. [&](const LoadTextureTarget& the_target) {
  152. json_obj.emplace("type", "load_texture");
  153. json_obj.emplace("texture_filename", the_target.m_texture_info_string);
  154. },
  155. [&](const CreateTextureTarget& the_target) {
  156. json_obj.emplace("type", "create_texture");
  157. json_obj.emplace("texture_filename", the_target.m_texture_info_string);
  158. },
  159. [&](const EFBTarget& the_target) {
  160. json_obj.emplace("type", "efb");
  161. json_obj.emplace("texture_filename",
  162. fmt::format("{}_{}x{}_{}", EFB_DUMP_PREFIX, the_target.m_width,
  163. the_target.m_height,
  164. static_cast<int>(the_target.m_texture_format)));
  165. },
  166. [&](const XFBTarget& the_target) {
  167. json_obj.emplace("type", "xfb");
  168. json_obj.emplace("texture_filename",
  169. fmt::format("{}_{}x{}_{}", XFB_DUMP_PREFIX, the_target.m_width,
  170. the_target.m_height,
  171. static_cast<int>(the_target.m_texture_format)));
  172. },
  173. [&](const ProjectionTarget& the_target) {
  174. const char* type_name = "3d";
  175. if (the_target.m_projection_type == ProjectionType::Orthographic)
  176. type_name = "2d";
  177. json_obj.emplace("type", type_name);
  178. if (the_target.m_texture_info_string)
  179. {
  180. json_obj.emplace("texture_filename", *the_target.m_texture_info_string);
  181. }
  182. },
  183. },
  184. target);
  185. }
  186. std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj)
  187. {
  188. const auto type_iter = obj.find("type");
  189. if (type_iter == obj.end())
  190. {
  191. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'type' not found");
  192. return std::nullopt;
  193. }
  194. if (!type_iter->second.is<std::string>())
  195. {
  196. ERROR_LOG_FMT(VIDEO,
  197. "Failed to load mod configuration file, option 'type' is not a string type");
  198. return std::nullopt;
  199. }
  200. const std::string& type = type_iter->second.get<std::string>();
  201. if (type == "draw_started")
  202. {
  203. std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
  204. if (!texture_info.has_value())
  205. return std::nullopt;
  206. DrawStartedTextureTarget target;
  207. target.m_texture_info_string = texture_info.value();
  208. return target;
  209. }
  210. else if (type == "load_texture")
  211. {
  212. std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
  213. if (!texture_info.has_value())
  214. return std::nullopt;
  215. LoadTextureTarget target;
  216. target.m_texture_info_string = texture_info.value();
  217. return target;
  218. }
  219. else if (type == "create_texture")
  220. {
  221. std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
  222. if (!texture_info.has_value())
  223. return std::nullopt;
  224. CreateTextureTarget target;
  225. target.m_texture_info_string = texture_info.value();
  226. return target;
  227. }
  228. else if (type == "efb")
  229. {
  230. return DeserializeFBTargetFromConfig<EFBTarget>(obj, EFB_DUMP_PREFIX);
  231. }
  232. else if (type == "xfb")
  233. {
  234. return DeserializeFBTargetFromConfig<XFBTarget>(obj, EFB_DUMP_PREFIX);
  235. }
  236. else if (type == "projection")
  237. {
  238. ProjectionTarget target;
  239. const auto texture_iter = obj.find("texture_filename");
  240. if (texture_iter != obj.end())
  241. {
  242. std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
  243. if (!texture_info.has_value())
  244. return std::nullopt;
  245. target.m_texture_info_string = texture_info;
  246. }
  247. const auto value_iter = obj.find("value");
  248. if (value_iter == obj.end())
  249. {
  250. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' not found");
  251. return std::nullopt;
  252. }
  253. if (!value_iter->second.is<std::string>())
  254. {
  255. ERROR_LOG_FMT(VIDEO,
  256. "Failed to load mod configuration file, option 'value' is not a string type");
  257. return std::nullopt;
  258. }
  259. const auto& value_str = value_iter->second.get<std::string>();
  260. if (value_str == "2d")
  261. {
  262. target.m_projection_type = ProjectionType::Orthographic;
  263. }
  264. else if (value_str == "3d")
  265. {
  266. target.m_projection_type = ProjectionType::Perspective;
  267. }
  268. else
  269. {
  270. ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' is not a valid "
  271. "value, valid values are: 2d, 3d");
  272. return std::nullopt;
  273. }
  274. return target;
  275. }
  276. else
  277. {
  278. ERROR_LOG_FMT(VIDEO,
  279. "Failed to load mod configuration file, option 'type' is not a valid value");
  280. }
  281. return std::nullopt;
  282. }
  283. void SerializeTargetToProfile(picojson::object*, const GraphicsTargetConfig&)
  284. {
  285. // Added for consistency, no functionality as of now
  286. }
  287. void DeserializeTargetFromProfile(const picojson::object&, GraphicsTargetConfig*)
  288. {
  289. // Added for consistency, no functionality as of now
  290. }