RenderSettings.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #include "RenderSettings.hh"
  2. #include "CommandController.hh"
  3. #include "CommandException.hh"
  4. #include "Version.hh"
  5. #include "stl.hh"
  6. #include "unreachable.hh"
  7. #include "build-info.hh"
  8. #include "components.hh"
  9. #include <algorithm>
  10. #include <iostream>
  11. #include <cmath>
  12. using namespace gl;
  13. namespace openmsx {
  14. EnumSetting<RenderSettings::ScaleAlgorithm>::Map RenderSettings::getScalerMap()
  15. {
  16. EnumSetting<ScaleAlgorithm>::Map scalerMap = { { "simple", SCALER_SIMPLE } };
  17. if (MAX_SCALE_FACTOR > 1) {
  18. append(scalerMap, {{"SaI", SCALER_SAI},
  19. {"ScaleNx", SCALER_SCALE},
  20. {"hq", SCALER_HQ},
  21. {"hqlite", SCALER_HQLITE},
  22. {"RGBtriplet", SCALER_RGBTRIPLET},
  23. {"TV", SCALER_TV}});
  24. if (!Version::RELEASE) {
  25. // This scaler is not ready yet for the upcoming 0.8.1
  26. // release, so disable it. As soon as it is ready we
  27. // can remove this test.
  28. scalerMap.emplace_back("MLAA", SCALER_MLAA);
  29. }
  30. }
  31. return scalerMap;
  32. }
  33. EnumSetting<RenderSettings::RendererID>::Map RenderSettings::getRendererMap()
  34. {
  35. EnumSetting<RendererID>::Map rendererMap = {
  36. { "none", DUMMY },// TODO: only register when in CliComm mode
  37. { "SDL", SDL } };
  38. #if COMPONENT_GL
  39. // compiled with OpenGL-2.0, still need to test whether
  40. // it's available at run time, but cannot be done here
  41. rendererMap.emplace_back("SDLGL-PP", SDLGL_PP);
  42. #endif
  43. return rendererMap;
  44. }
  45. RenderSettings::RenderSettings(CommandController& commandController)
  46. : accuracySetting(commandController,
  47. "accuracy", "rendering accuracy", ACC_PIXEL,
  48. EnumSetting<Accuracy>::Map{
  49. {"screen", ACC_SCREEN},
  50. {"line", ACC_LINE},
  51. {"pixel", ACC_PIXEL}})
  52. , deinterlaceSetting(commandController,
  53. "deinterlace", "deinterlacing on/off", true)
  54. , deflickerSetting(commandController,
  55. "deflicker", "deflicker on/off", false)
  56. , maxFrameSkipSetting(commandController,
  57. "maxframeskip", "set the max amount of frameskip", 3, 0, 100)
  58. , minFrameSkipSetting(commandController,
  59. "minframeskip", "set the min amount of frameskip", 0, 0, 100)
  60. , fullScreenSetting(commandController,
  61. "fullscreen", "full screen display on/off", false)
  62. , gammaSetting(commandController, "gamma",
  63. "amount of gamma correction: low is dark, high is bright",
  64. 1.1, 0.1, 5.0)
  65. , brightnessSetting(commandController, "brightness",
  66. "brightness video setting: "
  67. "0 is normal, lower is darker, higher is brighter",
  68. 0.0, -100.0, 100.0)
  69. , contrastSetting(commandController, "contrast",
  70. "contrast video setting: "
  71. "0 is normal, lower is less contrast, higher is more contrast",
  72. 0.0, -100.0, 100.0)
  73. , colorMatrixSetting(commandController,
  74. "color_matrix",
  75. "3x3 matrix to transform MSX RGB to host RGB, see manual for details",
  76. "{ 1 0 0 } { 0 1 0 } { 0 0 1 }")
  77. , glowSetting(commandController,
  78. "glow", "amount of afterglow effect: 0 = none, 100 = lots",
  79. 0, 0, 100)
  80. , noiseSetting(commandController,
  81. "noise", "amount of noise to add to the frame",
  82. 0.0, 0.0, 100.0)
  83. , rendererSetting(commandController,
  84. "renderer", "rendering back-end used to display the MSX screen",
  85. #if COMPONENT_GL
  86. SDLGL_PP,
  87. #else
  88. SDL,
  89. #endif
  90. getRendererMap())
  91. , horizontalBlurSetting(commandController,
  92. "blur", "amount of horizontal blur effect: 0 = none, 100 = full",
  93. 50, 0, 100)
  94. , scaleAlgorithmSetting(
  95. commandController, "scale_algorithm", "scale algorithm",
  96. SCALER_SIMPLE, getScalerMap())
  97. , scaleFactorSetting(commandController,
  98. "scale_factor", "scale factor",
  99. std::min(2, MAX_SCALE_FACTOR), MIN_SCALE_FACTOR, MAX_SCALE_FACTOR)
  100. , scanlineAlphaSetting(commandController,
  101. "scanline", "amount of scanline effect: 0 = none, 100 = full",
  102. 20, 0, 100)
  103. , limitSpritesSetting(commandController,
  104. "limitsprites", "limit number of sprites per line "
  105. "(on for realism, off to reduce sprite flashing)", true)
  106. , disableSpritesSetting(commandController,
  107. "disablesprites", "disable sprite rendering",
  108. false, Setting::DONT_SAVE)
  109. , cmdTimingSetting(commandController,
  110. "cmdtiming", "VDP command timing", false,
  111. EnumSetting<bool>::Map{{"real", false}, {"broken", true}},
  112. Setting::DONT_SAVE)
  113. , tooFastAccessSetting(commandController,
  114. "too_fast_vram_access",
  115. "Should too fast VDP VRAM access be correctly emulated.\n"
  116. "Possible values are:\n"
  117. " real -> too fast accesses are dropped\n"
  118. " ignore -> access speed is ignored, all accesses are executed",
  119. false,
  120. EnumSetting<bool>::Map{{"real", false }, {"ignore", true}},
  121. Setting::DONT_SAVE)
  122. , displayDeformSetting(
  123. commandController,
  124. "display_deform", "Display deform (for the moment this only "
  125. "works with the SDLGL-PP renderer)", DEFORM_NORMAL,
  126. EnumSetting<DisplayDeform>::Map{
  127. {"normal", DEFORM_NORMAL},
  128. {"3d", DEFORM_3D}})
  129. , vSyncSetting(commandController,
  130. "vsync", "Synchronize page flip with the host screen vertical sync:\n"
  131. " on -> flip on host vsync: avoids tearing\n"
  132. " off -> immediate flip: might be more fluent when host framerate"
  133. " (typically 60Hz) differs from MSX framerate (50 or 60Hz)\n"
  134. "Currently this only affects the SDLGL-PP renderer.",
  135. true)
  136. // Many android devices are relatively low powered. Therefore use
  137. // no stretch (value 320) as default for Android because it gives
  138. // better performance
  139. , horizontalStretchSetting(commandController,
  140. "horizontal_stretch",
  141. "Amount of horizontal stretch: this many MSX pixels will be "
  142. "stretched over the complete width of the output screen.\n"
  143. " 320 = no stretch\n"
  144. " 256 = max stretch (no border visible anymore)\n"
  145. " good values are 272 or 280\n"
  146. "This setting has only effect when using the SDLGL-PP renderer.",
  147. PLATFORM_ANDROID ? 320.0 : 280.0, 256.0, 320.0)
  148. , pointerHideDelaySetting(commandController,
  149. "pointer_hide_delay",
  150. "number of seconds after which the mouse pointer is hidden in the openMSX "
  151. "window; negative = no hiding, 0 = immediately",
  152. 2.0, -1.0, 60.0)
  153. , interleaveBlackFrameSetting(commandController,
  154. "interleave_black_frame",
  155. "Insert a black frame in between each normal MSX frame. "
  156. "Useful on (100Hz+) lightboost enabled monitors to reduce "
  157. "motion blur and double frame artifacts.",
  158. false)
  159. {
  160. brightnessSetting.attach(*this);
  161. contrastSetting .attach(*this);
  162. updateBrightnessAndContrast();
  163. auto& interp = commandController.getInterpreter();
  164. colorMatrixSetting.setChecker([this, &interp](TclObject& newValue) {
  165. try {
  166. parseColorMatrix(interp, newValue);
  167. } catch (CommandException& e) {
  168. throw CommandException(
  169. "Invalid color matrix: ", e.getMessage());
  170. }
  171. });
  172. try {
  173. parseColorMatrix(interp, colorMatrixSetting.getValue());
  174. } catch (MSXException& e) {
  175. std::cerr << e.getMessage() << '\n';
  176. cmIdentity = true;
  177. }
  178. // RendererSetting
  179. // Make sure the value 'none' never gets saved in settings.xml.
  180. // This happened in the following scenario:
  181. // - During startup, the renderer is forced to the value 'none'.
  182. // - If there's an error in the parsing of the command line (e.g.
  183. // because an invalid option is passed) then openmsx will never
  184. // get to the point where the actual renderer setting is restored
  185. // - After the error, the classes are destructed, part of that is
  186. // saving the current settings. But without extra care, this would
  187. // save renderer=none
  188. rendererSetting.setDontSaveValue(TclObject("none"));
  189. // A saved value 'none' can be very confusing. If so change it to default.
  190. if (rendererSetting.getEnum() == DUMMY) {
  191. rendererSetting.setValue(rendererSetting.getDefaultValue());
  192. }
  193. // set saved value as default
  194. rendererSetting.setRestoreValue(rendererSetting.getValue());
  195. rendererSetting.setEnum(DUMMY); // always start hidden
  196. }
  197. RenderSettings::~RenderSettings()
  198. {
  199. brightnessSetting.detach(*this);
  200. contrastSetting .detach(*this);
  201. }
  202. void RenderSettings::update(const Setting& setting)
  203. {
  204. if (&setting == &brightnessSetting) {
  205. updateBrightnessAndContrast();
  206. } else if (&setting == &contrastSetting) {
  207. updateBrightnessAndContrast();
  208. } else {
  209. UNREACHABLE;
  210. }
  211. }
  212. void RenderSettings::updateBrightnessAndContrast()
  213. {
  214. float contrastValue = getContrast();
  215. contrast = (contrastValue >= 0.0f) ? (1.0f + contrastValue / 25.0f)
  216. : (1.0f + contrastValue / 125.0f);
  217. brightness = (getBrightness() / 100.0f - 0.5f) * contrast + 0.5f;
  218. }
  219. static float conv2(float x, float gamma)
  220. {
  221. return ::powf(std::min(std::max(0.0f, x), 1.0f), gamma);
  222. }
  223. float RenderSettings::transformComponent(float c) const
  224. {
  225. float c2 = c * contrast + brightness;
  226. return conv2(c2, 1.0f / getGamma());
  227. }
  228. vec3 RenderSettings::transformRGB(vec3 rgb) const
  229. {
  230. auto [r, g, b] = colorMatrix * (rgb * contrast + vec3(brightness));
  231. float gamma = 1.0f / getGamma();
  232. return {conv2(r, gamma),
  233. conv2(g, gamma),
  234. conv2(b, gamma)};
  235. }
  236. void RenderSettings::parseColorMatrix(Interpreter& interp, const TclObject& value)
  237. {
  238. if (value.getListLength(interp) != 3) {
  239. throw CommandException("must have 3 rows");
  240. }
  241. bool identity = true;
  242. for (int i = 0; i < 3; ++i) {
  243. TclObject row = value.getListIndex(interp, i);
  244. if (row.getListLength(interp) != 3) {
  245. throw CommandException("each row must have 3 elements");
  246. }
  247. for (int j = 0; j < 3; ++j) {
  248. TclObject element = row.getListIndex(interp, j);
  249. float val = element.getDouble(interp);
  250. colorMatrix[i][j] = val;
  251. identity &= (val == (i == j ? 1.0f : 0.0f));
  252. }
  253. }
  254. cmIdentity = identity;
  255. }
  256. } // namespace openmsx