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