openMSX
V9990SDLRasterizer.cc
Go to the documentation of this file.
2#include "V9990.hh"
3#include "RawFrame.hh"
4#include "PostProcessor.hh"
5#include "Display.hh"
6#include "OutputSurface.hh"
7#include "RenderSettings.hh"
8#include "enumerate.hh"
9#include "narrow.hh"
10#include "one_of.hh"
11#include "ranges.hh"
12#include "xrange.hh"
13#include <algorithm>
14#include <array>
15#include <cstdint>
16#include <memory>
17
18namespace openmsx {
19
21 V9990& vdp_, Display& display, OutputSurface& screen_,
22 std::unique_ptr<PostProcessor> postProcessor_)
23 : vdp(vdp_), vram(vdp.getVRAM())
24 , screen(screen_)
25 , workFrame(std::make_unique<RawFrame>(1280, 240))
26 , renderSettings(display.getRenderSettings())
27 , postProcessor(std::move(postProcessor_))
28 , bitmapConverter(vdp, palette64, palette64_32768, palette256, palette256_32768, palette32768)
29 , p1Converter(vdp, palette64)
30 , p2Converter(vdp, palette64)
31{
32 // Fill palettes
34
35 renderSettings.getGammaSetting() .attach(*this);
36 renderSettings.getBrightnessSetting() .attach(*this);
37 renderSettings.getContrastSetting() .attach(*this);
38 renderSettings.getColorMatrixSetting().attach(*this);
39}
40
42{
43 renderSettings.getColorMatrixSetting().detach(*this);
44 renderSettings.getGammaSetting() .detach(*this);
45 renderSettings.getBrightnessSetting() .detach(*this);
46 renderSettings.getContrastSetting() .detach(*this);
47}
48
50{
51 return postProcessor.get();
52}
53
55{
56 return postProcessor->needRender() &&
57 vdp.getMotherBoard().isActive() &&
59}
60
67
69{
70 const V9990DisplayPeriod& horTiming = vdp.getHorizontalTiming();
71 const V9990DisplayPeriod& verTiming = vdp.getVerticalTiming();
72
73 // Center image on the window.
74
75 // In SDLLo, one window pixel represents 8 UC clock ticks, so the
76 // window = 320 * 8 UC ticks wide. In SDLHi, one pixel is 4 clock-
77 // ticks and the window 640 pixels wide -- same amount of UC ticks.
78 colZero = horTiming.blank + horTiming.border1 +
79 (horTiming.display - SCREEN_WIDTH * 8) / 2;
80
81 // 240 display lines can be shown. In SDLHi, we can do interlace,
82 // but still 240 lines per frame.
83 lineRenderTop = verTiming.blank + verTiming.border1 +
84 (verTiming.display - SCREEN_HEIGHT) / 2;
85}
86
87void V9990SDLRasterizer::frameEnd(EmuTime::param time)
88{
89 workFrame = postProcessor->rotateFrames(std::move(workFrame), time);
90 workFrame->init(
94}
95
97{
98 displayMode = mode;
99 bitmapConverter.setColorMode(colorMode, displayMode);
100}
101
103{
104 colorMode = mode;
105 bitmapConverter.setColorMode(colorMode, displayMode);
106}
107
109 int fromX, int fromY, int limitX, int limitY)
110{
111 Pixel bgColor = vdp.isOverScan()
112 ? 0
113 : palette64[vdp.getBackDropColor() & 63];
114
115 int startY = std::max(fromY - lineRenderTop, 0);
116 int endY = std::min(limitY - lineRenderTop, 240);
117 if (startY >= endY) return;
118
119 if ((fromX == 0) && (limitX == V9990DisplayTiming::UC_TICKS_PER_LINE)) {
120 // optimization
121 for (auto y : xrange(startY, endY)) {
122 workFrame->setBlank(y, bgColor);
123 }
124 return;
125 }
126
127 static int const screenW = SCREEN_WIDTH * 8; // in ticks
128 int startX = std::max(0, V9990::UCtoX(fromX - colZero, displayMode));
129 int endX = V9990::UCtoX(
131 ? screenW : std::min(screenW, limitX - colZero), displayMode);
132 if (startX >= endX) return;
133
134 unsigned lineWidth = vdp.getLineWidth();
135 for (auto y : xrange(startY, endY)) {
136 ranges::fill(workFrame->getLineDirect(y).subspan(startX, size_t(endX - startX)),
137 bgColor);
138 workFrame->setLineWidth(y, lineWidth);
139 }
140}
141
143 int fromX, int fromY, int toX, int toY,
144 int displayX, int displayY, int displayYA, int displayYB)
145{
146 static int const screenW = SCREEN_WIDTH * 8;
147 static int const screenH = SCREEN_HEIGHT;
148
149 // from VDP coordinates to screen coordinates
150 fromX -= colZero;
151 toX -= colZero;
152 fromY -= lineRenderTop;
153 toY -= lineRenderTop;
154
155 // Clip to screen
156 if (fromX < 0) {
157 displayX -= fromX;
158 fromX = 0;
159 }
160 if (fromY < 0) {
161 displayY -= fromY;
162 displayYA -= fromY;
163 displayYB -= fromY;
164 fromY = 0;
165 }
166 if (toX > screenW) {
167 toX = screenW;
168 }
169 if (toY > screenH) {
170 toY = screenH;
171 }
172 fromX = V9990::UCtoX(fromX, displayMode);
173 toX = V9990::UCtoX(toX, displayMode);
174
175 if ((toX > fromX) && (toY > fromY)) {
176 bool drawSprites = vdp.spritesEnabled() &&
177 !renderSettings.getDisableSprites();
178
179 displayX = V9990::UCtoX(displayX, displayMode);
180 int displayWidth = toX - fromX;
181 int displayHeight = toY - fromY;
182
183 if (displayMode == V9990DisplayMode::P1) {
184 drawP1Mode(fromX, fromY, displayX,
185 displayY, displayYA, displayYB,
186 displayWidth, displayHeight,
187 drawSprites);
188 } else if (displayMode == V9990DisplayMode::P2) {
189 drawP2Mode(fromX, fromY, displayX,
190 displayY, displayYA,
191 displayWidth, displayHeight,
192 drawSprites);
193 } else {
194 drawBxMode(fromX, fromY, displayX,
195 displayY, displayYA,
196 displayWidth, displayHeight,
197 drawSprites);
198 }
199 }
200}
201
203 int fromX, int fromY, int displayX,
204 int displayY, int displayYA, int displayYB,
205 int displayWidth, int displayHeight, bool drawSprites)
206{
207 while (displayHeight--) {
208 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
209 p1Converter.convertLine(dst, displayX, displayY,
210 displayYA, displayYB, drawSprites);
211 workFrame->setLineWidth(fromY, 320);
212 ++fromY;
213 ++displayY;
214 ++displayYA;
215 ++displayYB;
216 }
217}
218
220 int fromX, int fromY, int displayX, int displayY, int displayYA,
221 int displayWidth, int displayHeight, bool drawSprites)
222{
223 while (displayHeight--) {
224 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
225 p2Converter.convertLine(dst, displayX, displayY, displayYA, drawSprites);
226 workFrame->setLineWidth(fromY, 640);
227 ++fromY;
228 ++displayY;
229 ++displayYA;
230 }
231}
232
234 int fromX, int fromY, int displayX, int displayY, int displayYA,
235 int displayWidth, int displayHeight, bool drawSprites)
236{
237 unsigned scrollX = vdp.getScrollAX();
238 unsigned x = displayX + scrollX;
239
240 int lineStep = 1;
241 if (vdp.isEvenOddEnabled()) {
242 displayYA *= 2;
243 if (vdp.getEvenOdd()) {
244 ++displayY;
245 ++displayYA;
246 }
247 lineStep = 2;
248 }
249
250 unsigned scrollY = vdp.getScrollAY();
251 unsigned rollMask = vdp.getRollMask(0x1FFF);
252 unsigned scrollYBase = scrollY & ~rollMask & 0x1FFF;
253 int cursorY = displayY - vdp.getCursorYOffset();
254 while (displayHeight--) {
255 // Note: convertLine() can draw up to 3 pixels too many. But
256 // that's ok, the buffer is big enough: buffer can hold 1280
257 // pixels, max displayWidth is 1024 pixels. When taking the
258 // position of the borders into account, the display area
259 // plus 3 pixels cannot go beyond the end of the buffer.
260 unsigned y = scrollYBase + ((displayYA + scrollY) & rollMask);
261 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
262 bitmapConverter.convertLine(dst, x, y, cursorY, drawSprites);
263 workFrame->setLineWidth(fromY, vdp.getLineWidth());
264 ++fromY;
265 displayYA += lineStep;
266 cursorY += lineStep;
267 }
268}
269
270
272{
273 // the 32768 color palette
274 if (renderSettings.isColorMatrixIdentity()) {
275 // Most users use the "normal" monitor type; making this a
276 // special case speeds up palette precalculation a lot.
277 std::array<int, 32> intensity;
278 for (auto [i, r] : enumerate(intensity)) {
279 r = narrow_cast<int>(255.0f * renderSettings.transformComponent(narrow<float>(i) * (1.0f / 31.0f)));
280 }
281 for (auto [grb, col] : enumerate(palette32768)) {
282 col = screen.mapRGB255(gl::ivec3(
283 intensity[(grb >> 5) & 31],
284 intensity[(grb >> 10) & 31],
285 intensity[(grb >> 0) & 31]));
286 }
287 } else {
288 for (auto g : xrange(32)) {
289 for (auto r : xrange(32)) {
290 for (auto b : xrange(32)) {
291 gl::vec3 rgb{narrow<float>(r),
292 narrow<float>(g),
293 narrow<float>(b)};
294 palette32768[(g << 10) + (r << 5) + b] = Pixel(
295 screen.mapRGB(renderSettings.transformRGB(rgb * (1.0f / 31.0f))));
296 }
297 }
298 }
299 }
300
301 // the 256 color palette
302 std::array<int, 8> mapRG = {0, 4, 9, 13, 18, 22, 27, 31};
303 std::array<int, 4> mapB = {0, 11, 21, 31};
304 for (auto g : xrange(8)) {
305 for (auto r : xrange(8)) {
306 for (auto b : xrange(4)) {
307 auto idx256 = (g << 5) | (r << 2) | b;
308 auto idx32768 = (mapRG[g] << 10) | (mapRG[r] << 5) | mapB[b];
309 palette256_32768[idx256] = narrow<int16_t>(idx32768);
310 palette256[idx256] = palette32768[idx32768];
311 }
312 }
313 }
314
315 resetPalette();
316}
317
319 byte r, byte g, byte b, bool ys)
320{
321 auto idx32768 = ((g & 31) << 10) | ((r & 31) << 5) | ((b & 31) << 0);
322 palette64_32768[index & 63] = narrow<int16_t>(idx32768); // TODO what with ys?
323 palette64[index & 63] = ys ? screen.getKeyColor()
324 : palette32768[idx32768];
325}
326
328{
329 // get 64 color palette from VDP
330 for (auto i : xrange(64)) {
331 auto [r, g, b, ys] = vdp.getPalette(i);
332 setPalette(i, r, g, b, ys);
333 }
334 palette256[0] = vdp.isSuperimposing() ? screen.getKeyColor()
335 : palette32768[0];
336 // TODO what with palette256_32768[0]?
337}
338
340{
341 resetPalette();
342}
343
345{
346 return postProcessor->isRecording();
347}
348
350{
351 if (&setting == one_of(&renderSettings.getGammaSetting(),
352 &renderSettings.getBrightnessSetting(),
353 &renderSettings.getContrastSetting(),
354 &renderSettings.getColorMatrixSetting())) {
355 preCalcPalettes();
356 }
357}
358
359} // namespace openmsx
BaseSetting * setting
int g
Represents the output window/screen of openMSX.
Definition Display.hh:31
@ ODD
Interlacing is on and this is an odd frame.
@ EVEN
Interlacing is on and this is an even frame.
@ NONINTERLACED
Interlacing is off for this frame.
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition MSXDevice.cc:70
A frame buffer where pixels can be written to.
uint32_t mapRGB255(gl::ivec3 rgb) const
Same as mapRGB, but RGB components are in range [0..255].
Pixel getKeyColor() const
Returns the color key for this output surface.
uint32_t mapRGB(gl::vec3 rgb) const
Returns the pixel value for the given RGB color.
A post processor builds the frame that is displayed from the MSX frame, while applying effects such a...
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition RawFrame.hh:16
float transformComponent(float c) const
Apply brightness, contrast and gamma transformation on the input color component.
gl::vec3 transformRGB(gl::vec3 rgb) const
Apply brightness, contrast and gamma transformation on the input color.
bool isColorMatrixIdentity() const
Returns true iff the current color matrix is the identity matrix.
FloatSetting & getContrastSetting()
Contrast video setting.
FloatSetting & getBrightnessSetting()
Brightness video setting.
FloatSetting & getGammaSetting()
The amount of gamma correction.
StringSetting & getColorMatrixSetting()
Color matrix setting.
void detach(Observer< T > &observer)
Definition Subject.hh:60
void attach(Observer< T > &observer)
Definition Subject.hh:54
void convertLine(std::span< Pixel > dst, unsigned x, unsigned y, int cursorY, bool drawCursors) const
Convert a line of VRAM into host pixels.
void setColorMode(V9990ColorMode colorMode_, V9990DisplayMode display)
Set a different rendering mode.
static constexpr int UC_TICKS_PER_LINE
The number of clock ticks per line is independent of the crystal used or the display mode (NTSC/PAL)
void convertLine(std::span< Pixel > buf, unsigned displayX, unsigned displayY, unsigned displayYA, unsigned displayYB, bool drawSprites)
void convertLine(std::span< Pixel > buf, unsigned displayX, unsigned displayY, unsigned displayYA, bool drawSprites)
void setPalette(int index, byte r, byte g, byte b, bool ys) override
Set RGB values for a palette entry.
void setDisplayMode(V9990DisplayMode displayMode) override
The display mode determines the screens geometry and how V9990 pixels are mapped to pixels on screen.
void drawBorder(int fromX, int fromY, int limitX, int limitY) override
Render a rectangle of border pixels on the host screen.
void drawP1Mode(int fromX, int fromY, int displayX, int displayY, int displayYA, int displayYB, int displayWidth, int displayHeight, bool drawSprites)
void preCalcPalettes()
Fill the palettes.
PostProcessor * getPostProcessor() const override
See V9990::getPostProcessor().
bool isActive() override
Will the output of this Rasterizer be displayed? There is no point in producing a frame that will not...
void setColorMode(V9990ColorMode colorMode) override
The color mode determines how the V9990 VRAM data gets converted to pixel colors.
void frameStart() override
Indicates the start of a new frame.
void update(const Setting &setting) noexcept override
void drawBxMode(int fromX, int fromY, int displayX, int displayY, int displayYA, int displayWidth, int displayHeight, bool drawSprites)
void drawP2Mode(int fromX, int fromY, int displayX, int displayY, int displayYA, int displayWidth, int displayHeight, bool drawSprites)
void setSuperimpose(bool enabled) override
Is superimpose enabled?
bool isRecording() const override
Is video recording active?
void drawDisplay(int fromX, int fromY, int toX, int toY, int displayX, int displayY, int displayYA, int displayYB) override
Render a rectangle of display pixels on the host screen.
void frameEnd(EmuTime::param time) override
Indicates the end of the current frame.
V9990SDLRasterizer(V9990 &vdp, Display &display, OutputSurface &screen, std::unique_ptr< PostProcessor > postProcessor)
void reset() override
Resynchronize with VDP - flush caches etc.
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition V9990.hh:35
unsigned getCursorYOffset() const
In overscan mode the cursor position is still specified with 'normal' (non-overscan) y-coordinates.
Definition V9990.hh:159
bool isSuperimposing() const
Should this frame be superimposed? This is a combination of bit 5 (YSE) in R#8 and the presence of an...
Definition V9990.hh:146
byte getBackDropColor() const
Return the current back drop color.
Definition V9990.hh:218
bool isInterlaced() const
Get interlace status.
Definition V9990.hh:62
unsigned getLineWidth() const
Return the display width.
Definition V9990.hh:274
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition V9990.hh:69
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition V9990.hh:196
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition V9990.hh:92
GetPaletteResult getPalette(int index) const
Definition V9990.cc:685
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition V9990.hh:174
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition V9990.hh:224
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition V9990.hh:230
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition V9990.hh:248
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition V9990.hh:336
V9990ColorMode getColorMode() const
Return the current color mode.
Definition V9990.cc:804
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition V9990.hh:316
bool isOverScan() const
Returns true iff in overscan mode.
Definition V9990.hh:136
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition V9990.hh:76
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:315
STL namespace.
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
constexpr auto xrange(T e)
Definition xrange.hh:132