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 "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, OutputSurface& screen_,
23  std::unique_ptr<PostProcessor> postProcessor_)
24  : vdp(vdp_), vram(vdp.getVRAM())
25  , screen(screen_)
26  , workFrame(std::make_unique<RawFrame>(screen.getPixelFormat(), 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() &&
64  !vdp.getMotherBoard().isFastForwarding();
65 }
66 
67 template <class Pixel>
69 {
70  setDisplayMode(vdp.getDisplayMode());
71  setColorMode(vdp.getColorMode());
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(
100  vdp.isInterlaced() ? (vdp.getEvenOdd() ? RawFrame::FIELD_EVEN
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>
236 void V9990SDLRasterizer<Pixel>::drawP2Mode(
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>
252 void V9990SDLRasterizer<Pixel>::drawBxMode(
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>
291 void V9990SDLRasterizer<Pixel>::preCalcPalettes()
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
openmsx::V9990DisplayPeriod
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
Definition: V9990DisplayTiming.hh:11
openmsx::V9990SDLRasterizer::setDisplayMode
void setDisplayMode(V9990DisplayMode displayMode) override
The display mode determines the screens geometry and how V9990 pixels are mapped to pixels on screen.
Definition: V9990SDLRasterizer.cc:106
openmsx::RenderSettings::getContrastSetting
FloatSetting & getContrastSetting()
Contrast video setting.
Definition: RenderSettings.hh:79
MemoryOps.hh
gl::ivec3
vecN< 3, int > ivec3
Definition: gl_vec.hh:148
openmsx::V9990SDLRasterizer::isRecording
bool isRecording() const override
Is video recording active?
Definition: V9990SDLRasterizer.cc:369
openmsx::V9990SDLRasterizer::~V9990SDLRasterizer
~V9990SDLRasterizer() override
Definition: V9990SDLRasterizer.cc:45
gl::min
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:274
Display.hh
openmsx::RenderSettings::getColorMatrixSetting
StringSetting & getColorMatrixSetting()
Color matrix setting.
Definition: RenderSettings.hh:83
openmsx::V9990SDLRasterizer::getPostProcessor
PostProcessor * getPostProcessor() const override
See V9990::getPostProcessor().
Definition: V9990SDLRasterizer.cc:54
openmsx::P2
Definition: V9990ModeEnum.hh:8
openmsx::V9990SDLRasterizer::drawDisplay
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.
Definition: V9990SDLRasterizer.cc:156
openmsx::V9990SDLRasterizer::drawBorder
void drawBorder(int fromX, int fromY, int limitX, int limitY) override
Render a rectangle of border pixels on the host screen.
Definition: V9990SDLRasterizer.cc:120
openmsx::V9990DisplayPeriod::border1
const int border1
Definition: V9990DisplayTiming.hh:15
openmsx::V9990SDLRasterizer::frameStart
void frameStart() override
Indicates the start of a new frame.
Definition: V9990SDLRasterizer.cc:76
openmsx::V9990DisplayTiming::UC_TICKS_PER_LINE
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)
Definition: V9990DisplayTiming.hh:36
openmsx::V9990SDLRasterizer::setSuperimpose
void setSuperimpose(bool enabled) override
Is superimpose enabled?
Definition: V9990SDLRasterizer.cc:363
openmsx::V9990ColorMode
V9990ColorMode
Definition: V9990ModeEnum.hh:11
RawFrame.hh
openmsx::RenderSettings::getBrightnessSetting
FloatSetting & getBrightnessSetting()
Brightness video setting.
Definition: RenderSettings.hh:75
openmsx::Subject::attach
void attach(Observer< T > &observer)
Definition: Subject.hh:50
openmsx::Setting
Definition: Setting.hh:119
gl::vecN
Definition: gl_vec.hh:35
openmsx::V9990SDLRasterizer::V9990SDLRasterizer
V9990SDLRasterizer(V9990 &vdp, Display &display, OutputSurface &screen, std::unique_ptr< PostProcessor > postProcessor)
Definition: V9990SDLRasterizer.cc:21
openmsx::FrameSource::FIELD_EVEN
Interlacing is on and this is an even frame.
Definition: FrameSource.hh:24
openmsx::RenderSettings::getGammaSetting
FloatSetting & getGammaSetting()
The amount of gamma correction.
Definition: RenderSettings.hh:71
openmsx::V9990SDLRasterizer::setPalette
void setPalette(int index, byte r, byte g, byte b, bool ys) override
Set RGB values for a palette entry.
Definition: V9990SDLRasterizer.cc:338
openmsx::V9990SDLRasterizer::isActive
bool isActive() override
Will the output of this Rasterizer be displayed? There is no point in producing a frame that will not...
Definition: V9990SDLRasterizer.cc:60
openmsx::Pixel
uint32_t Pixel
Definition: GLHQLiteScaler.cc:93
openmsx::FrameSource::FIELD_NONINTERLACED
Interlacing is off for this frame.
Definition: FrameSource.hh:21
openmsx::MemoryOps::MemSet
Definition: MemoryOps.hh:8
V9990.hh
PostProcessor.hh
openmsx::V9990DisplayMode
V9990DisplayMode
Definition: V9990ModeEnum.hh:6
build-info.hh
openmsx::PostProcessor
Abstract base class for post processors.
Definition: PostProcessor.hh:29
openmsx::RawFrame
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition: RawFrame.hh:25
RenderSettings.hh
openmsx::V9990DisplayPeriod::display
const int display
Definition: V9990DisplayTiming.hh:16
openmsx::V9990SDLRasterizer::reset
void reset() override
Resynchronize with VDP - flush caches etc.
Definition: V9990SDLRasterizer.cc:68
g
int g
Definition: ScopedAssign_test.cc:20
openmsx::P1
Definition: V9990ModeEnum.hh:8
components.hh
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1377
OutputSurface.hh
openmsx::Display
Represents the output window/screen of openMSX.
Definition: Display.hh:31
openmsx::OutputSurface
A frame buffer where pixels can be written to.
Definition: OutputSurface.hh:19
gl::max
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:292
openmsx::V9990::UCtoX
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition: V9990.hh:169
V9990SDLRasterizer.hh
openmsx::PP
Definition: V9990ModeEnum.hh:13
openmsx::V9990SDLRasterizer::setColorMode
void setColorMode(V9990ColorMode colorMode) override
The color mode determines how the V9990 VRAM data gets converted to pixel colors.
Definition: V9990SDLRasterizer.cc:113
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::V9990DisplayPeriod::blank
const int blank
Definition: V9990DisplayTiming.hh:14
openmsx::FrameSource::FIELD_ODD
Interlacing is on and this is an odd frame.
Definition: FrameSource.hh:27
openmsx::V9990SDLRasterizer::frameEnd
void frameEnd(EmuTime::param time) override
Indicates the end of the current frame.
Definition: V9990SDLRasterizer.cc:96
openmsx::V9990SDLRasterizer
Rasterizer using SDL.
Definition: V9990SDLRasterizer.hh:24
openmsx::V9990
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:29