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 "MemoryOps.hh"
9#include "enumerate.hh"
10#include "narrow.hh"
11#include "one_of.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();
136 for (auto y : xrange(startY, endY)) {
137 memset(workFrame->getLineDirect(y).subspan(startX, size_t(endX - startX)),
138 bgColor);
139 workFrame->setLineWidth(y, lineWidth);
140 }
141}
142
144 int fromX, int fromY, int toX, int toY,
145 int displayX, int displayY, int displayYA, int displayYB)
146{
147 static int const screenW = SCREEN_WIDTH * 8;
148 static int const screenH = SCREEN_HEIGHT;
149
150 // from VDP coordinates to screen coordinates
151 fromX -= colZero;
152 toX -= colZero;
153 fromY -= lineRenderTop;
154 toY -= lineRenderTop;
155
156 // Clip to screen
157 if (fromX < 0) {
158 displayX -= fromX;
159 fromX = 0;
160 }
161 if (fromY < 0) {
162 displayY -= fromY;
163 displayYA -= fromY;
164 displayYB -= fromY;
165 fromY = 0;
166 }
167 if (toX > screenW) {
168 toX = screenW;
169 }
170 if (toY > screenH) {
171 toY = screenH;
172 }
173 fromX = V9990::UCtoX(fromX, displayMode);
174 toX = V9990::UCtoX(toX, displayMode);
175
176 if ((toX > fromX) && (toY > fromY)) {
177 bool drawSprites = vdp.spritesEnabled() &&
178 !renderSettings.getDisableSprites();
179
180 displayX = V9990::UCtoX(displayX, displayMode);
181 int displayWidth = toX - fromX;
182 int displayHeight = toY - fromY;
183
184 if (displayMode == P1) {
185 drawP1Mode(fromX, fromY, displayX,
186 displayY, displayYA, displayYB,
187 displayWidth, displayHeight,
188 drawSprites);
189 } else if (displayMode == P2) {
190 drawP2Mode(fromX, fromY, displayX,
191 displayY, displayYA,
192 displayWidth, displayHeight,
193 drawSprites);
194 } else {
195 drawBxMode(fromX, fromY, displayX,
196 displayY, displayYA,
197 displayWidth, displayHeight,
198 drawSprites);
199 }
200 }
201}
202
204 int fromX, int fromY, int displayX,
205 int displayY, int displayYA, int displayYB,
206 int displayWidth, int displayHeight, bool drawSprites)
207{
208 while (displayHeight--) {
209 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
210 p1Converter.convertLine(dst, displayX, displayY,
211 displayYA, displayYB, drawSprites);
212 workFrame->setLineWidth(fromY, 320);
213 ++fromY;
214 ++displayY;
215 ++displayYA;
216 ++displayYB;
217 }
218}
219
221 int fromX, int fromY, int displayX, int displayY, int displayYA,
222 int displayWidth, int displayHeight, bool drawSprites)
223{
224 while (displayHeight--) {
225 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
226 p2Converter.convertLine(dst, displayX, displayY, displayYA, drawSprites);
227 workFrame->setLineWidth(fromY, 640);
228 ++fromY;
229 ++displayY;
230 ++displayYA;
231 }
232}
233
235 int fromX, int fromY, int displayX, int displayY, int displayYA,
236 int displayWidth, int displayHeight, bool drawSprites)
237{
238 unsigned scrollX = vdp.getScrollAX();
239 unsigned x = displayX + scrollX;
240
241 int lineStep = 1;
242 if (vdp.isEvenOddEnabled()) {
243 displayYA *= 2;
244 if (vdp.getEvenOdd()) {
245 ++displayY;
246 ++displayYA;
247 }
248 lineStep = 2;
249 }
250
251 unsigned scrollY = vdp.getScrollAY();
252 unsigned rollMask = vdp.getRollMask(0x1FFF);
253 unsigned scrollYBase = scrollY & ~rollMask & 0x1FFF;
254 int cursorY = displayY - vdp.getCursorYOffset();
255 while (displayHeight--) {
256 // Note: convertLine() can draw up to 3 pixels too many. But
257 // that's ok, the buffer is big enough: buffer can hold 1280
258 // pixels, max displayWidth is 1024 pixels. When taking the
259 // position of the borders into account, the display area
260 // plus 3 pixels cannot go beyond the end of the buffer.
261 unsigned y = scrollYBase + ((displayYA + scrollY) & rollMask);
262 auto dst = workFrame->getLineDirect(fromY).subspan(fromX, displayWidth);
263 bitmapConverter.convertLine(dst, x, y, cursorY, drawSprites);
264 workFrame->setLineWidth(fromY, vdp.getLineWidth());
265 ++fromY;
266 displayYA += lineStep;
267 cursorY += lineStep;
268 }
269}
270
271
273{
274 // the 32768 color palette
275 if (renderSettings.isColorMatrixIdentity()) {
276 // Most users use the "normal" monitor type; making this a
277 // special case speeds up palette precalculation a lot.
278 std::array<int, 32> intensity;
279 for (auto [i, r] : enumerate(intensity)) {
280 r = narrow_cast<int>(255.0f * renderSettings.transformComponent(narrow<float>(i) * (1.0f / 31.0f)));
281 }
282 for (auto [grb, col] : enumerate(palette32768)) {
283 col = screen.mapRGB255(gl::ivec3(
284 intensity[(grb >> 5) & 31],
285 intensity[(grb >> 10) & 31],
286 intensity[(grb >> 0) & 31]));
287 }
288 } else {
289 for (auto g : xrange(32)) {
290 for (auto r : xrange(32)) {
291 for (auto b : xrange(32)) {
292 gl::vec3 rgb{narrow<float>(r),
293 narrow<float>(g),
294 narrow<float>(b)};
295 palette32768[(g << 10) + (r << 5) + b] = Pixel(
296 screen.mapRGB(renderSettings.transformRGB(rgb * (1.0f / 31.0f))));
297 }
298 }
299 }
300 }
301
302 // the 256 color palette
303 std::array<int, 8> mapRG = {0, 4, 9, 13, 18, 22, 27, 31};
304 std::array<int, 4> mapB = {0, 11, 21, 31};
305 for (auto g : xrange(8)) {
306 for (auto r : xrange(8)) {
307 for (auto b : xrange(4)) {
308 auto idx256 = (g << 5) | (r << 2) | b;
309 auto idx32768 = (mapRG[g] << 10) | (mapRG[r] << 5) | mapB[b];
310 palette256_32768[idx256] = narrow<int16_t>(idx32768);
311 palette256[idx256] = palette32768[idx32768];
312 }
313 }
314 }
315
316 resetPalette();
317}
318
320 byte r, byte g, byte b, bool ys)
321{
322 auto idx32768 = ((g & 31) << 10) | ((r & 31) << 5) | ((b & 31) << 0);
323 palette64_32768[index & 63] = narrow<int16_t>(idx32768); // TODO what with ys?
324 palette64[index & 63] = ys ? screen.getKeyColor()
325 : palette32768[idx32768];
326}
327
329{
330 // get 64 color palette from VDP
331 for (auto i : xrange(64)) {
332 auto [r, g, b, ys] = vdp.getPalette(i);
333 setPalette(i, r, g, b, ys);
334 }
335 palette256[0] = vdp.isSuperimposing() ? screen.getKeyColor()
336 : palette32768[0];
337 // TODO what with palette256_32768[0]?
338}
339
341{
342 resetPalette();
343}
344
346{
347 return postProcessor->isRecording();
348}
349
351{
352 if (&setting == one_of(&renderSettings.getGammaSetting(),
353 &renderSettings.getBrightnessSetting(),
354 &renderSettings.getContrastSetting(),
355 &renderSettings.getColorMatrixSetting())) {
356 preCalcPalettes();
357 }
358}
359
360} // namespace openmsx
BaseSetting * setting
int g
Represents the output window/screen of openMSX.
Definition Display.hh:32
@ FIELD_NONINTERLACED
Interlacing is off for this frame.
@ FIELD_EVEN
Interlacing is on and this is an even frame.
@ FIELD_ODD
Interlacing is on and this is an odd 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:217
bool isInterlaced() const
Get interlace status.
Definition V9990.hh:62
unsigned getLineWidth() const
Return the display width.
Definition V9990.hh:273
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition V9990.hh:69
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition V9990.hh:195
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:223
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition V9990.hh:229
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition V9990.hh:247
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition V9990.hh:334
V9990ColorMode getColorMode() const
Return the current color mode.
Definition V9990.cc:801
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition V9990.hh:314
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
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