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