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