openMSX
V9990SDLRasterizer.cc
Go to the documentation of this file.
1 #include "V9990SDLRasterizer.hh"
2 #include "V9990.hh"
3 #include "RawFrame.hh"
4 #include "PostProcessor.hh"
5 #include "Display.hh"
6 #include "VisibleSurface.hh"
7 #include "RenderSettings.hh"
8 #include "MemoryOps.hh"
9 #include "build-info.hh"
10 #include "components.hh"
11 #include <algorithm>
12 #include <cstdint>
13 #include <memory>
14 
15 using std::min;
16 using std::max;
17 
18 namespace openmsx {
19 
20 template <class Pixel>
22  V9990& vdp_, Display& display, VisibleSurface& screen_,
23  std::unique_ptr<PostProcessor> postProcessor_)
24  : vdp(vdp_), vram(vdp.getVRAM())
25  , screen(screen_)
26  , workFrame(std::make_unique<RawFrame>(screen.getSDLFormat(), 1280, 240))
27  , renderSettings(display.getRenderSettings())
28  , displayMode(P1) // dummy value
29  , colorMode(PP) // avoid UMR
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 template <class Pixel>
46 {
47  renderSettings.getColorMatrixSetting().detach(*this);
48  renderSettings.getGammaSetting() .detach(*this);
49  renderSettings.getBrightnessSetting() .detach(*this);
50  renderSettings.getContrastSetting() .detach(*this);
51 }
52 
53 template <class Pixel>
55 {
56  return postProcessor.get();
57 }
58 
59 template <class Pixel>
61 {
62  return postProcessor->needRender() &&
63  vdp.getMotherBoard().isActive() &&
65 }
66 
67 template <class Pixel>
69 {
72  resetPalette();
73 }
74 
75 template <class Pixel>
77 {
78  const V9990DisplayPeriod& horTiming = vdp.getHorizontalTiming();
79  const V9990DisplayPeriod& verTiming = vdp.getVerticalTiming();
80 
81  // Center image on the window.
82 
83  // In SDLLo, one window pixel represents 8 UC clockticks, so the
84  // window = 320 * 8 UC ticks wide. In SDLHi, one pixel is 4 clock-
85  // ticks and the window 640 pixels wide -- same amount of UC ticks.
86  colZero = horTiming.blank + horTiming.border1 +
87  (horTiming.display - SCREEN_WIDTH * 8) / 2;
88 
89  // 240 display lines can be shown. In SDLHi, we can do interlace,
90  // but still 240 lines per frame.
91  lineRenderTop = verTiming.blank + verTiming.border1 +
92  (verTiming.display - SCREEN_HEIGHT) / 2;
93 }
94 
95 template <class Pixel>
96 void V9990SDLRasterizer<Pixel>::frameEnd(EmuTime::param time)
97 {
98  workFrame = postProcessor->rotateFrames(std::move(workFrame), time);
99  workFrame->init(
103 }
104 
105 template <class Pixel>
107 {
108  displayMode = mode;
109  bitmapConverter.setColorMode(colorMode, displayMode);
110 }
111 
112 template <class Pixel>
114 {
115  colorMode = mode;
116  bitmapConverter.setColorMode(colorMode, displayMode);
117 }
118 
119 template <class Pixel>
121  int fromX, int fromY, int limitX, int limitY)
122 {
123  Pixel bgColor = vdp.isOverScan()
124  ? 0
125  : palette64[vdp.getBackDropColor() & 63];
126 
127  int startY = max(fromY - lineRenderTop, 0);
128  int endY = min(limitY - lineRenderTop, 240);
129  if (startY >= endY) return;
130 
131  if ((fromX == 0) && (limitX == V9990DisplayTiming::UC_TICKS_PER_LINE)) {
132  // optimization
133  for (int y = startY; y < endY; ++y) {
134  workFrame->setBlank(y, bgColor);
135  }
136  return;
137  }
138 
139  static int const screenW = SCREEN_WIDTH * 8; // in ticks
140  int startX = max(0, V9990::UCtoX(fromX - colZero, displayMode));
141  int endX = V9990::UCtoX(
143  ? screenW : min(screenW, limitX - colZero), displayMode);
144  if (startX >= endX) return;
145 
146  unsigned lineWidth = vdp.getLineWidth();
148  for (int y = startY; y < endY; ++y) {
149  memset(workFrame->getLinePtrDirect<Pixel>(y) + startX,
150  endX - startX, bgColor);
151  workFrame->setLineWidth(y, lineWidth);
152  }
153 }
154 
155 template <class Pixel>
157  int fromX, int fromY, int toX, int toY,
158  int displayX, int displayY, int displayYA, int displayYB)
159 {
160  static int const screenW = SCREEN_WIDTH * 8;
161  static int const screenH = SCREEN_HEIGHT;
162 
163  // from VDP coordinates to screen coordinates
164  fromX -= colZero;
165  toX -= colZero;
166  fromY -= lineRenderTop;
167  toY -= lineRenderTop;
168 
169  // Clip to screen
170  if (fromX < 0) {
171  displayX -= fromX;
172  fromX = 0;
173  }
174  if (fromY < 0) {
175  displayY -= fromY;
176  displayYA -= fromY;
177  displayYB -= fromY;
178  fromY = 0;
179  }
180  if (toX > screenW) {
181  toX = screenW;
182  }
183  if (toY > screenH) {
184  toY = screenH;
185  }
186  fromX = V9990::UCtoX(fromX, displayMode);
187  toX = V9990::UCtoX(toX, displayMode);
188 
189  if ((toX > fromX) && (toY > fromY)) {
190  bool drawSprites = vdp.spritesEnabled() &&
191  !renderSettings.getDisableSprites();
192 
193  displayX = V9990::UCtoX(displayX, displayMode);
194  int displayWidth = toX - fromX;
195  int displayHeight = toY - fromY;
196 
197  if (displayMode == P1) {
198  drawP1Mode(fromX, fromY, displayX,
199  displayY, displayYA, displayYB,
200  displayWidth, displayHeight,
201  drawSprites);
202  } else if (displayMode == P2) {
203  drawP2Mode(fromX, fromY, displayX,
204  displayY, displayYA,
205  displayWidth, displayHeight,
206  drawSprites);
207  } else {
208  drawBxMode(fromX, fromY, displayX,
209  displayY, displayYA,
210  displayWidth, displayHeight,
211  drawSprites);
212  }
213  }
214 }
215 
216 template <class Pixel>
218  int fromX, int fromY, int displayX,
219  int displayY, int displayYA, int displayYB,
220  int displayWidth, int displayHeight, bool drawSprites)
221 {
222  while (displayHeight--) {
223  Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(fromY) + fromX;
224  p1Converter.convertLine(pixelPtr, displayX, displayWidth,
225  displayY, displayYA, displayYB,
226  drawSprites);
227  workFrame->setLineWidth(fromY, 320);
228  ++fromY;
229  ++displayY;
230  ++displayYA;
231  ++displayYB;
232  }
233 }
234 
235 template <class Pixel>
237  int fromX, int fromY, int displayX, int displayY, int displayYA,
238  int displayWidth, int displayHeight, bool drawSprites)
239 {
240  while (displayHeight--) {
241  Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(fromY) + fromX;
242  p2Converter.convertLine(pixelPtr, displayX, displayWidth,
243  displayY, displayYA, drawSprites);
244  workFrame->setLineWidth(fromY, 640);
245  ++fromY;
246  ++displayY;
247  ++displayYA;
248  }
249 }
250 
251 template <class Pixel>
253  int fromX, int fromY, int displayX, int displayY, int displayYA,
254  int displayWidth, int displayHeight, bool drawSprites)
255 {
256  unsigned scrollX = vdp.getScrollAX();
257  unsigned x = displayX + scrollX;
258 
259  int lineStep = 1;
260  if (vdp.isEvenOddEnabled()) {
261  if (vdp.getEvenOdd()) {
262  ++displayY;
263  ++displayYA;
264  }
265  lineStep = 2;
266  }
267 
268  unsigned scrollY = vdp.getScrollAY();
269  unsigned rollMask = vdp.getRollMask(0x1FFF);
270  unsigned scrollYBase = scrollY & ~rollMask & 0x1FFF;
271  int cursorY = displayY - vdp.getCursorYOffset();
272  while (displayHeight--) {
273  // Note: convertLine() can draw up to 3 pixels too many. But
274  // that's ok, the buffer is big enough: buffer can hold 1280
275  // pixels, max displayWidth is 1024 pixels. When taking the
276  // position of the borders into account, the display area
277  // plus 3 pixels cannot go beyond the end of the buffer.
278  unsigned y = scrollYBase + ((displayYA + scrollY) & rollMask);
279  Pixel* pixelPtr = workFrame->getLinePtrDirect<Pixel>(fromY) + fromX;
280  bitmapConverter.convertLine(pixelPtr, x, y, displayWidth,
281  cursorY, drawSprites);
282  workFrame->setLineWidth(fromY, vdp.getLineWidth());
283  ++fromY;
284  displayYA += lineStep;
285  cursorY += lineStep;
286  }
287 }
288 
289 
290 template <class Pixel>
292 {
293  // the 32768 color palette
294  if (renderSettings.isColorMatrixIdentity()) {
295  // Most users use the "normal" monitor type; making this a
296  // special case speeds up palette precalculation a lot.
297  int intensity[32];
298  for (int i = 0; i < 32; ++i) {
299  intensity[i] =
300  int(255 * renderSettings.transformComponent(i / 31.0f));
301  }
302  for (int grb = 0; grb < (1 << 15); ++grb) {
303  palette32768[grb] = screen.mapKeyedRGB255<Pixel>(gl::ivec3(
304  intensity[(grb >> 5) & 31],
305  intensity[(grb >> 10) & 31],
306  intensity[(grb >> 0) & 31]));
307  }
308  } else {
309  for (int g = 0; g < 32; ++g) {
310  for (int r = 0; r < 32; ++r) {
311  for (int b = 0; b < 32; ++b) {
312  palette32768[(g << 10) + (r << 5) + b] =
313  screen.mapRGB(renderSettings.transformRGB(
314  gl::vec3(r, g, b) / 31.0f));
315  }
316  }
317  }
318  }
319 
320  // the 256 color palette
321  int mapRG[8] = { 0, 4, 9, 13, 18, 22, 27, 31 };
322  int mapB [4] = { 0, 11, 21, 31 };
323  for (int g = 0; g < 8; ++g) {
324  for (int r = 0; r < 8; ++r) {
325  for (int b = 0; b < 4; ++b) {
326  auto idx256 = (g << 5) | (r << 2) | b;
327  auto idx32768 = (mapRG[g] << 10) | (mapRG[r] << 5) | mapB[b];
328  palette256_32768[idx256] = idx32768;
329  palette256[idx256] = palette32768[idx32768];
330  }
331  }
332  }
333 
334  resetPalette();
335 }
336 
337 template <class Pixel>
339  byte r, byte g, byte b, bool ys)
340 {
341  auto idx32768 = ((g & 31) << 10) | ((r & 31) << 5) | ((b & 31) << 0);
342  palette64_32768[index & 63] = idx32768; // TODO what with ys?
343  palette64[index & 63] = ys ? screen.getKeyColor<Pixel>()
344  : palette32768[idx32768];
345 }
346 
347 template <class Pixel>
349 {
350  // get 64 color palette from VDP
351  for (int i = 0; i < 64; ++i) {
352  byte r, g, b;
353  bool ys;
354  vdp.getPalette(i, r, g, b, ys);
355  setPalette(i, r, g, b, ys);
356  }
357  palette256[0] = vdp.isSuperimposing() ? screen.getKeyColor<Pixel>()
358  : palette32768[0];
359  // TODO what with palette256_32768[0]?
360 }
361 
362 template <class Pixel>
364 {
365  resetPalette();
366 }
367 
368 template <class Pixel>
370 {
371  return postProcessor->isRecording();
372 }
373 
374 template <class Pixel>
375 void V9990SDLRasterizer<Pixel>::update(const Setting& setting)
376 {
377  if ((&setting == &renderSettings.getGammaSetting()) ||
378  (&setting == &renderSettings.getBrightnessSetting()) ||
379  (&setting == &renderSettings.getContrastSetting()) ||
380  (&setting == &renderSettings.getColorMatrixSetting())) {
381  preCalcPalettes();
382  }
383 }
384 
385 // Force template instantiation.
386 #if HAVE_16BPP
387 template class V9990SDLRasterizer<uint16_t>;
388 #endif
389 #if HAVE_32BPP || COMPONENT_GL
390 template class V9990SDLRasterizer<uint32_t>;
391 #endif
392 
393 } // namespace openmsx
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:29
void setColorMode(V9990ColorMode colorMode) override
The color mode determines how the V9990 VRAM data gets converted to pixel colors. ...
FloatSetting & getBrightnessSetting()
Brightness video setting.
void reset() override
Resynchronize with VDP - flush caches etc.
Pixel mapKeyedRGB255(gl::ivec3 rgb)
Returns the pixel value for the given RGB color.
Represents the output window/screen of openMSX.
Definition: Display.hh:31
Interlacing is on and this is an odd frame.
Definition: FrameSource.hh:28
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition: V9990.hh:242
bool isInterlaced() const
Get interlace status.
Definition: V9990.hh:57
FloatSetting & getGammaSetting()
The amount of gamma correction.
void setSuperimpose(bool enabled) override
Is superimpose enabled?
bool isFastForwarding() const
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition: V9990.hh:329
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition: V9990.hh:190
unsigned getLineWidth() const
Return the display width.
Definition: V9990.hh:268
STL namespace.
Rasterizer using SDL.
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition: V9990.hh:169
uint32_t Pixel
V9990SDLRasterizer(V9990 &vdp, Display &display, VisibleSurface &screen, std::unique_ptr< PostProcessor > postProcessor)
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
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:141
static constexpr int UC_TICKS_PER_LINE
The number of clockticks per line is independent of the crystal used or the display mode (NTSC/PAL) ...
FloatSetting & getContrastSetting()
Contrast video setting.
void attach(Observer< T > &observer)
Definition: Subject.hh:50
vecN< 3, int > ivec3
Definition: gl_vec.hh:143
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition: V9990.hh:71
bool isRecording() const override
Is video recording active?
StringSetting & getColorMatrixSetting()
Color matrix setting.
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition: RawFrame.hh:25
void setPalette(int index, byte r, byte g, byte b, bool ys) override
Set RGB values for a palette entry.
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition: V9990.hh:64
bool isColorMatrixIdentity()
Returns true iff the current color matrix is the identity matrix.
An OutputSurface which is visible to the user, such as a window or a full screen display.
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
Interlacing is on and this is an even frame.
Definition: FrameSource.hh:25
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:73
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:812
void frameStart() override
Indicates the start of a new frame.
unsigned mapRGB(gl::vec3 rgb)
Returns the pixel value for the given RGB color.
void setDisplayMode(V9990DisplayMode displayMode) override
The display mode determines the screens geometry and how V9990 pixels are mapped to pixels on screen...
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
bool isOverScan() const
Returns true iff in overscan mode.
Definition: V9990.hh:131
int g
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:218
PostProcessor * getPostProcessor() const override
See V9990::getPostProcessor().
Interlacing is off for this frame.
Definition: FrameSource.hh:22
Abstract base class for post processors.
float transformComponent(float c) const
Apply brightness, contrast and gamma transformation on the input color component. ...
void getPalette(int index, byte &r, byte &g, byte &b, bool &ys) const
Get palette entry.
Definition: V9990.cc:696
unsigned getCursorYOffset() const
In overscan mode the cursor position is still specified with &#39;normal&#39; (non-overscan) y-coordinates...
Definition: V9990.hh:154
gl::vec3 transformRGB(gl::vec3 rgb) const
Apply brightness, contrast and gamma transformation on the input color.
void drawBorder(int fromX, int fromY, int limitX, int limitY) override
Render a rectangle of border pixels on the host screen.
void frameEnd(EmuTime::param time) override
Indicates the end of the current frame.
void detach(Observer< T > &observer)
Definition: Subject.hh:56
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition: V9990.hh:309
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1377
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:224
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition: V9990.hh:87
bool getDisableSprites() const
Disable sprite rendering?
byte getBackDropColor() const
Return the current back drop color.
Definition: V9990.hh:212
bool isActive() override
Will the output of this Rasterizer be displayed? There is no point in producing a frame that will not...
Pixel getKeyColor() const
Returns the color key for this output surface.
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.