OXYGENHUB
◆ main openmsx / src / video / v9990 / V9990SDLRasterizer.cc
10.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "V9990SDLRasterizer.hh"
2
3
#include "V9990.hh"
4
5
#include "Display.hh"
6
#include "OutputSurface.hh"
7
#include "PostProcessor.hh"
8
#include "RawFrame.hh"
9
#include "RenderSettings.hh"
10
11
#include "enumerate.hh"
12
#include "narrow.hh"
13
#include "one_of.hh"
14
#include "xrange.hh"
15
16
#include <algorithm>
17
#include <array>
18
#include <cstdint>
19
#include <memory>
20
21
namespace openmsx {
22
23
V9990SDLRasterizer::V9990SDLRasterizer(
24
		V9990& vdp_, Display& display, OutputSurface& screen_,
25
		std::unique_ptr<PostProcessor> postProcessor_)
26
	: vdp(vdp_), vram(vdp.getVRAM())
27
	, screen(screen_)
28
	, workFrame(std::make_unique<RawFrame>(1280, 240))
29
	, renderSettings(display.getRenderSettings())
30
	, postProcessor(std::move(postProcessor_))
31
	, bitmapConverter(vdp, palette64, palette64_32768, palette256, palette256_32768, palette32768)
32
	, p1Converter(vdp, palette64)
33
	, p2Converter(vdp, palette64)
34
{
35
	// Fill palettes
36
	preCalcPalettes();
37
38
	renderSettings.getGammaSetting()      .attach(*this);
39
	renderSettings.getBrightnessSetting() .attach(*this);
40
	renderSettings.getContrastSetting()   .attach(*this);
41
	renderSettings.getColorMatrixSetting().attach(*this);
42
}
43
44
V9990SDLRasterizer::~V9990SDLRasterizer()
45
{
46
	renderSettings.getColorMatrixSetting().detach(*this);
47
	renderSettings.getGammaSetting()      .detach(*this);
48
	renderSettings.getBrightnessSetting() .detach(*this);
49
	renderSettings.getContrastSetting()   .detach(*this);
50
}
51
52
PostProcessor* V9990SDLRasterizer::getPostProcessor() const
53
{
54
	return postProcessor.get();
55
}
56
57
bool V9990SDLRasterizer::isActive()
58
{
59
	return postProcessor->needRender() &&
60
	       vdp.getMotherBoard().isActive() &&
61
	       !vdp.getMotherBoard().isFastForwarding();
62
}
63
64
void V9990SDLRasterizer::reset()
65
{
66
	setDisplayMode(vdp.getDisplayMode());
67
	setColorMode(vdp.getColorMode());
68
	resetPalette();
69
}
70
71
void V9990SDLRasterizer::frameStart()
72
{
73
	const V9990DisplayPeriod& horTiming = vdp.getHorizontalTiming();
74
	const V9990DisplayPeriod& verTiming = vdp.getVerticalTiming();
75
76
	// Center image on the window.
77
78
	// In SDLLo, one window pixel represents 8 UC clock ticks, so the
79
	// window = 320 * 8 UC ticks wide. In SDLHi, one pixel is 4 clock-
80
	// ticks and the window 640 pixels wide -- same amount of UC ticks.
81
	colZero = horTiming.blank + horTiming.border1 +
82
	          (horTiming.display - SCREEN_WIDTH * 8) / 2;
83
84
	// 240 display lines can be shown. In SDLHi, we can do interlace,
85
	// but still 240 lines per frame.
86
	lineRenderTop = verTiming.blank + verTiming.border1 +
87
	                (verTiming.display - SCREEN_HEIGHT) / 2;
88
}
89
90
void V9990SDLRasterizer::frameEnd(EmuTime time)
91
{
92
	workFrame = postProcessor->rotateFrames(std::move(workFrame), time);
93
	workFrame->init(
94
	    vdp.isInterlaced() ? (vdp.getEvenOdd() ? RawFrame::FieldType::EVEN
95
	                                           : RawFrame::FieldType::ODD)
96
	                       : RawFrame::FieldType::NONINTERLACED);
97
}
98
99
void V9990SDLRasterizer::setDisplayMode(V9990DisplayMode mode)
100
{
101
	displayMode = mode;
102
	bitmapConverter.setColorMode(colorMode, displayMode);
103
}
104
105
void V9990SDLRasterizer::setColorMode(V9990ColorMode mode)
106
{
107
	colorMode = mode;
108
	bitmapConverter.setColorMode(colorMode, displayMode);
109
}
110
111
void V9990SDLRasterizer::drawBorder(
112
	int fromX, int fromY, int limitX, int limitY)
113
{
114
	Pixel bgColor = vdp.isOverScan()
115
	              ? 0
116
	              : palette64[vdp.getBackDropColor() & 63];
117
118
	int startY = std::max(fromY  - lineRenderTop,   0);
119
	int endY   = std::min(limitY - lineRenderTop, 240);
120
	if (startY >= endY) return;
121
122
	if ((fromX == 0) && (limitX == V9990DisplayTiming::UC_TICKS_PER_LINE)) {
123
		// optimization
124
		for (auto y : xrange(startY, endY)) {
125
			workFrame->setBlank(y, bgColor);
126
		}
127
		return;
128
	}
129
130
	static int const screenW = SCREEN_WIDTH * 8; // in ticks
131
	int startX = std::max(0, V9990::UCtoX(fromX - colZero, displayMode));
132
	int endX = V9990::UCtoX(
133
		(limitX == V9990DisplayTiming::UC_TICKS_PER_LINE)
134
		? screenW : std::min(screenW, limitX - colZero), displayMode);
135
	if (startX >= endX) return;
136
137
	unsigned lineWidth = vdp.getLineWidth();
138
	for (auto y : xrange(startY, endY)) {
139
		std::ranges::fill(workFrame->getLineDirect(y).subspan(startX, size_t(endX - startX)),
140
		             bgColor);
141
		workFrame->setLineWidth(y, lineWidth);
142
	}
143
}
144
145
void V9990SDLRasterizer::drawDisplay(
146
	int fromX, int fromY, int toX, int toY,
147
	int displayX, int displayY, int displayYA, int displayYB)
148
{
149
	static int const screenW = SCREEN_WIDTH * 8;
150
	static int const screenH = SCREEN_HEIGHT;
151
152
	// from VDP coordinates to screen coordinates
153
	fromX -= colZero;
154
	toX   -= colZero;
155
	fromY -= lineRenderTop;
156
	toY   -= lineRenderTop;
157
158
	// Clip to screen
159
	if (fromX < 0) {
160
		displayX -= fromX;
161
		fromX = 0;
162
	}
163
	if (fromY < 0) {
164
		displayY  -= fromY;
165
		displayYA -= fromY;
166
		displayYB -= fromY;
167
		fromY = 0;
168
	}
169
	toX = std::min(toX, screenW);
170
	toY = std::min(toY, screenH);
171
	fromX = V9990::UCtoX(fromX, displayMode);
172
	toX   = V9990::UCtoX(toX,   displayMode);
173
174
	if ((toX > fromX) && (toY > fromY)) {
175
		bool drawSprites = vdp.spritesEnabled() &&
176
			!renderSettings.getDisableSprites();
177
178
		displayX = V9990::UCtoX(displayX, displayMode);
179
		int displayWidth  = toX - fromX;
180
		int displayHeight = toY - fromY;
181
182
		if (displayMode == V9990DisplayMode::P1) {
183
			drawP1Mode(fromX, fromY, displayX,
184
			           displayY, displayYA, displayYB,
185
			           displayWidth, displayHeight,
186
			           drawSprites);
187
		} else if (displayMode == V9990DisplayMode::P2) {
188
			drawP2Mode(fromX, fromY, displayX,
189
			           displayY, displayYA,
190
			           displayWidth, displayHeight,
191
			           drawSprites);
192
		} else {
193
			drawBxMode(fromX, fromY, displayX,
194
			           displayY, displayYA,
195
			           displayWidth, displayHeight,
196
			           drawSprites);
197
		}
198
	}
199
}
200
201
void V9990SDLRasterizer::drawP1Mode(
202
	int fromX, int fromY, int displayX,
203
	int displayY, int displayYA, int displayYB,
204
	int displayWidth, int displayHeight, bool drawSprites)
205
{
206
	while (displayHeight--) {
207
		auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
208
		p1Converter.convertLine(dst, displayX, displayY,
209
		                        displayYA, displayYB, drawSprites);
210
		workFrame->setLineWidth(fromY, 320);
211
		++fromY;
212
		++displayY;
213
		++displayYA;
214
		++displayYB;
215
	}
216
}
217
218
void V9990SDLRasterizer::drawP2Mode(
219
	int fromX, int fromY, int displayX, int displayY, int displayYA,
220
	int displayWidth, int displayHeight, bool drawSprites)
221
{
222
	while (displayHeight--) {
223
		auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
224
		p2Converter.convertLine(dst, displayX, displayY, displayYA, drawSprites);
225
		workFrame->setLineWidth(fromY, 640);
226
		++fromY;
227
		++displayY;
228
		++displayYA;
229
	}
230
}
231
232
void V9990SDLRasterizer::drawBxMode(
233
	int fromX, int fromY, int displayX, int displayY, int displayYA,
234
	int displayWidth, int displayHeight, bool drawSprites)
235
{
236
	unsigned scrollX = vdp.getScrollAX();
237
	unsigned x = displayX + scrollX;
238
239
	int lineStep = 1;
240
	if (vdp.isEvenOddEnabled()) {
241
		displayYA *= 2;
242
		if (vdp.getEvenOdd()) {
243
			++displayY;
244
			++displayYA;
245
		}
246
		lineStep = 2;
247
	}
248
249
	unsigned scrollY = vdp.getScrollAY();
250
	unsigned rollMask = vdp.getRollMask(0x1FFF);
251
	unsigned scrollYBase = scrollY & ~rollMask & 0x1FFF;
252
	int cursorY = displayY - vdp.getCursorYOffset();
253
	while (displayHeight--) {
254
		// Note: convertLine() can draw up to 3 pixels too many. But
255
		// that's ok, the buffer is big enough: buffer can hold 1280
256
		// pixels, max displayWidth is 1024 pixels. When taking the
257
		// position of the borders into account, the display area
258
		// plus 3 pixels cannot go beyond the end of the buffer.
259
		unsigned y = scrollYBase + ((displayYA + scrollY) & rollMask);
260
		auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
261
		bitmapConverter.convertLine(dst, x, y, cursorY, drawSprites);
262
		workFrame->setLineWidth(fromY, vdp.getLineWidth());
263
		++fromY;
264
		displayYA += lineStep;
265
		cursorY   += lineStep;
266
	}
267
}
268
269
270
void V9990SDLRasterizer::preCalcPalettes()
271
{
272
	// the 32768 color palette
273
	if (renderSettings.isColorMatrixIdentity()) {
274
		// Most users use the "normal" monitor type; making this a
275
		// special case speeds up palette precalculation a lot.
276
		std::array<int, 32> intensity;
277
		for (auto [i, r] : enumerate(intensity)) {
278
			r = narrow_cast<int>(255.0f * renderSettings.transformComponent(narrow<float>(i) * (1.0f / 31.0f)));
279
		}
280
		for (auto [grb, col] : enumerate(palette32768)) {
281
			col = screen.mapRGB255(gl::ivec3(
282
				intensity[(grb >>  5) & 31],
283
				intensity[(grb >> 10) & 31],
284
				intensity[(grb >>  0) & 31]));
285
		}
286
	} else {
287
		for (auto g : xrange(32)) {
288
			for (auto r : xrange(32)) {
289
				for (auto b : xrange(32)) {
290
					gl::vec3 rgb{narrow<float>(r),
291
					             narrow<float>(g),
292
					             narrow<float>(b)};
293
					palette32768[(g << 10) + (r << 5) + b] = Pixel(
294
						screen.mapRGB(renderSettings.transformRGB(rgb * (1.0f / 31.0f))));
295
				}
296
			}
297
		}
298
	}
299
300
	// the 256 color palette
301
	std::array<int, 8> mapRG = {0, 4, 9, 13, 18, 22, 27, 31};
302
	std::array<int, 4> mapB = {0, 11, 21, 31};
303
	for (auto g : xrange(8)) {
304
		for (auto r : xrange(8)) {
305
			for (auto b : xrange(4)) {
306
				auto idx256 = (g << 5) | (r << 2) | b;
307
				auto idx32768 = (mapRG[g] << 10) | (mapRG[r] << 5) | mapB[b];
308
				palette256_32768[idx256] = narrow<int16_t>(idx32768);
309
				palette256[idx256] = palette32768[idx32768];
310
			}
311
		}
312
	}
313
314
	resetPalette();
315
}
316
317
void V9990SDLRasterizer::setPalette(int index, uint8_t r, uint8_t g, uint8_t b, bool ys)
318
{
319
	auto idx32768 = ((g & 31) << 10) | ((r & 31) << 5) | ((b & 31) << 0);
320
	palette64_32768[index & 63] = narrow<int16_t>(idx32768); // TODO what with ys?
321
	palette64[index & 63] = ys ? screen.getKeyColor()
322
	                           : palette32768[idx32768];
323
}
324
325
void V9990SDLRasterizer::resetPalette()
326
{
327
	// get 64 color palette from VDP
328
	for (auto i : xrange(64)) {
329
		auto [r, g, b, ys] = vdp.getPalette(i);
330
		setPalette(i, r, g, b, ys);
331
	}
332
	palette256[0] = vdp.isSuperimposing() ? screen.getKeyColor()
333
	                                      : palette32768[0];
334
	// TODO what with palette256_32768[0]?
335
}
336
337
void V9990SDLRasterizer::setSuperimpose(bool /*enabled*/)
338
{
339
	resetPalette();
340
}
341
342
bool V9990SDLRasterizer::isRecording() const
343
{
344
	return postProcessor->isRecording();
345
}
346
347
void V9990SDLRasterizer::update(const Setting& setting) noexcept
348
{
349
	if (&setting == one_of(&renderSettings.getGammaSetting(),
350
	                       &renderSettings.getBrightnessSetting(),
351
	                       &renderSettings.getContrastSetting(),
352
	                       &renderSettings.getColorMatrixSetting())) {
353
		preCalcPalettes();
354
	}
355
}
356
357
} // namespace openmsx