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