OXYGENHUB
◆ main openmsx / src / video / RenderSettings.cc
8.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "RenderSettings.hh"
2
3
#include "CommandController.hh"
4
#include "CommandException.hh"
5
#include "Version.hh"
6
7
#include "stl.hh"
8
#include "unreachable.hh"
9
10
#include "build-info.hh"
11
12
#include <algorithm>
13
#include <cmath>
14
#include <iostream>
15
16
using namespace gl;
17
18
namespace openmsx {
19
20
EnumSetting<RenderSettings::ScaleAlgorithm>::Map RenderSettings::getScalerMap()
21
{
22
	using enum ScaleAlgorithm;
23
	EnumSetting<ScaleAlgorithm>::Map scalerMap = {
24
		{"simple",     SIMPLE},
25
		{"ScaleNx",    SCALE},
26
		{"hq",         HQ},
27
		{"RGBtriplet", RGBTRIPLET},
28
		{"TV",         TV}
29
	};
30
	return scalerMap;
31
}
32
33
EnumSetting<RenderSettings::RendererID>::Map RenderSettings::getRendererMap()
34
{
35
	using enum RendererID;
36
	EnumSetting<RendererID>::Map rendererMap = {
37
		{"uninitialized", UNINITIALIZED},
38
		{"none",          DUMMY},
39
		{"SDLGL-PP",      SDLGL_PP}
40
	};
41
	return rendererMap;
42
}
43
44
RenderSettings::RenderSettings(CommandController& commandController)
45
	: accuracySetting(commandController,
46
		"accuracy", "rendering accuracy", Accuracy::PIXEL,
47
		EnumSetting<Accuracy>::Map{
48
			{"screen", Accuracy::SCREEN},
49
			{"line",   Accuracy::LINE},
50
			{"pixel",  Accuracy::PIXEL}})
51
52
	, deinterlaceSetting(commandController,
53
		"deinterlace", "deinterlacing on/off", true)
54
55
	, deflickerSetting(commandController,
56
		"deflicker", "deflicker on/off", false)
57
58
	, maxFrameSkipSetting(commandController,
59
		"maxframeskip", "set the max amount of frameskip", 3, 0, 100)
60
61
	, minFrameSkipSetting(commandController,
62
		"minframeskip", "set the min amount of frameskip", 0, 0, 100)
63
64
	, fullScreenSetting(commandController,
65
		"fullscreen", "full screen display on/off", false)
66
67
	, gammaSetting(commandController, "gamma",
68
		"amount of gamma correction: low is dark, high is bright",
69
		1.1, 0.1, 5.0)
70
71
	, brightnessSetting(commandController, "brightness",
72
		"brightness video setting: "
73
		"0 is normal, lower is darker, higher is brighter",
74
		0.0, -100.0, 100.0)
75
	, contrastSetting(commandController, "contrast",
76
		"contrast video setting: "
77
		"0 is normal, lower is less contrast, higher is more contrast",
78
		0.0, -100.0, 100.0)
79
80
	, colorMatrixSetting(commandController,
81
		"color_matrix",
82
		"3x3 matrix to transform MSX RGB to host RGB, see manual for details",
83
		"{ 1 0 0 } { 0 1 0 } { 0 0 1 }")
84
85
	, glowSetting(commandController,
86
		"glow", "amount of afterglow effect: 0 = none, 100 = lots",
87
		0, 0, 100)
88
89
	, noiseSetting(commandController,
90
		"noise", "amount of noise to add to the frame",
91
		0.0, 0.0, 100.0)
92
93
	, rendererSetting(commandController,
94
		"renderer", "rendering back-end used to display the MSX screen",
95
		RendererID::SDLGL_PP, getRendererMap(), Setting::Save::NO)
96
97
	, horizontalBlurSetting(commandController,
98
		"blur", "amount of horizontal blur effect: 0 = none, 100 = full",
99
		50, 0, 100)
100
101
	, scaleAlgorithmSetting(
102
		commandController, "scale_algorithm", "scale algorithm",
103
		ScaleAlgorithm::SIMPLE, getScalerMap())
104
105
	, scaleFactorSetting(commandController,
106
		"scale_factor", "scale factor",
107
		std::clamp(2, MIN_SCALE_FACTOR, MAX_SCALE_FACTOR), MIN_SCALE_FACTOR, MAX_SCALE_FACTOR)
108
109
	, scanlineAlphaSetting(commandController,
110
		"scanline", "amount of scanline effect: 0 = none, 100 = full",
111
		20, 0, 100)
112
113
	, limitSpritesSetting(commandController,
114
		"limitsprites", "limit number of sprites per line "
115
		"(on for realism, off to reduce sprite flashing)", true)
116
117
	, disableSpritesSetting(commandController,
118
		"disablesprites", "disable sprite rendering",
119
		false, Setting::Save::NO)
120
121
	, cmdTimingSetting(commandController,
122
		"cmdtiming", "VDP command timing", false,
123
		EnumSetting<bool>::Map{{"real", false}, {"broken", true}},
124
		Setting::Save::NO)
125
126
	, tooFastAccessSetting(commandController,
127
		"too_fast_vram_access",
128
		"Should too fast VDP VRAM access be correctly emulated.\n"
129
		"Possible values are:\n"
130
		" real -> too fast accesses are dropped\n"
131
		" ignore -> access speed is ignored, all accesses are executed",
132
		false,
133
		EnumSetting<bool>::Map{{"real", false }, {"ignore", true}},
134
		Setting::Save::NO)
135
136
	, displayDeformSetting(
137
		commandController,
138
		"display_deform", "Display deform", DisplayDeform::NORMAL,
139
		EnumSetting<DisplayDeform>::Map{
140
			{"normal", DisplayDeform::NORMAL},
141
			{"3d",     DisplayDeform::_3D}})
142
143
	, vSyncSetting(commandController,
144
		"vsync", "Synchronize page flip with the host screen vertical sync:\n"
145
		" on -> flip on host vsync: avoids tearing\n"
146
		" off -> immediate flip: might be more fluent when host framerate"
147
		" (typically 60Hz) differs from MSX framerate (50 or 60Hz).\n",
148
		true)
149
150
	, fullStretchSetting(commandController,
151
		"full_stretch", "Stretch the image to fill the entire screen in fullscreen mode", false)
152
153
	// Many android devices are relatively low powered. Therefore use
154
	// no stretch (value 320) as default for Android because it gives
155
	// better performance
156
	, horizontalStretchSetting(commandController,
157
		"horizontal_stretch",
158
		"Amount of horizontal stretch: this many MSX pixels will be "
159
		"stretched over the complete width of the output screen.\n"
160
		"  320 = no stretch (large borders)\n"
161
		"  288 = a bit more than all border pixels\n"
162
		"  284 = all border pixels\n"
163
		"  280 = a bit less than all border pixels\n"
164
		"  272 = realistic\n"
165
		"  256 = max stretch (no border visible at all)\n"
166
		"  good values are 272 or 280\n",
167
		PLATFORM_ANDROID ? 320.0 : 280.0, 256.0, 320.0)
168
169
	, pointerHideDelaySetting(commandController,
170
		"pointer_hide_delay",
171
		"number of seconds after which the mouse pointer is hidden in the openMSX "
172
		"window; negative = no hiding, 0 = immediately",
173
		2.0, -1.0, 60.0)
174
175
	, interleaveBlackFrameSetting(commandController,
176
		"interleave_black_frame",
177
		"Insert a black frame in between each normal MSX frame. "
178
		"Useful on (100Hz+) lightboost enabled monitors to reduce "
179
		"motion blur and double frame artifacts.",
180
		false)
181
{
182
	brightnessSetting.attach(*this);
183
	contrastSetting  .attach(*this);
184
	updateBrightnessAndContrast();
185
186
	auto& interp = commandController.getInterpreter();
187
	colorMatrixSetting.setChecker([this, &interp](const TclObject& newValue) {
188
		try {
189
			parseColorMatrix(interp, newValue);
190
		} catch (CommandException& e) {
191
			throw CommandException(
192
				"Invalid color matrix: ", e.getMessage());
193
		}
194
	});
195
	try {
196
		parseColorMatrix(interp, colorMatrixSetting.getValue());
197
	} catch (MSXException& e) {
198
		std::cerr << e.getMessage() << '\n';
199
		cmIdentity = true;
200
	}
201
202
	rendererSetting.setEnum(RendererID::UNINITIALIZED); // always start hidden
203
	rendererSetting.setChecker([this](const TclObject& newValue) {
204
		if (newValue.getString() == "uninitialized") {
205
			throw CommandException("can't set special value 'uninitialized'");
206
		}
207
		// the default enum check
208
		(void)rendererSetting.fromStringBase(newValue.getString());
209
	});
210
}
211
212
RenderSettings::~RenderSettings()
213
{
214
	brightnessSetting.detach(*this);
215
	contrastSetting  .detach(*this);
216
}
217
218
void RenderSettings::update(const Setting& setting) noexcept
219
{
220
	if (&setting == &brightnessSetting) {
221
		updateBrightnessAndContrast();
222
	} else if (&setting == &contrastSetting) {
223
		updateBrightnessAndContrast();
224
	} else {
225
		UNREACHABLE;
226
	}
227
}
228
229
void RenderSettings::updateBrightnessAndContrast()
230
{
231
	float contrastValue = getContrast();
232
	contrast = (contrastValue >= 0.0f) ? (1.0f + contrastValue * (1.0f /  25.0f))
233
	                                   : (1.0f + contrastValue * (1.0f / 125.0f));
234
	brightness = (getBrightness() * (1.0f / 100.0f) - 0.5f) * contrast + 0.5f;
235
}
236
237
static float conv2(float x, float gamma)
238
{
239
	return ::powf(std::clamp(x, 0.0f, 1.0f), gamma);
240
}
241
242
float RenderSettings::transformComponent(float c) const
243
{
244
	float c2 = c * contrast + brightness;
245
	return conv2(c2, 1.0f / getGamma());
246
}
247
248
vec3 RenderSettings::transformRGB(vec3 rgb) const
249
{
250
	auto [r, g, b] = colorMatrix * (rgb * contrast + vec3(brightness));
251
	float gamma = 1.0f / getGamma();
252
	return {conv2(r, gamma),
253
	        conv2(g, gamma),
254
	        conv2(b, gamma)};
255
}
256
257
void RenderSettings::parseColorMatrix(Interpreter& interp, const TclObject& value)
258
{
259
	if (value.getListLength(interp) != 3) {
260
		throw CommandException("must have 3 rows");
261
	}
262
	bool identity = true;
263
	for (auto i : xrange(3)) {
264
		TclObject row = value.getListIndex(interp, i);
265
		if (row.getListLength(interp) != 3) {
266
			throw CommandException("each row must have 3 elements");
267
		}
268
		for (auto j : xrange(3)) {
269
			TclObject element = row.getListIndex(interp, j);
270
			float val = element.getFloat(interp);
271
			colorMatrix[j][i] = val;
272
			identity &= (val == (i == j ? 1.0f : 0.0f));
273
		}
274
	}
275
	cmIdentity = identity;
276
}
277
278
} // namespace openmsx