openMSX
PixelRenderer.cc
Go to the documentation of this file.
1 /*
2 TODO:
3 - Implement blinking (of page mask) in bitmap modes.
4 */
5 
6 #include "PixelRenderer.hh"
7 #include "Rasterizer.hh"
8 #include "PostProcessor.hh"
9 #include "Display.hh"
10 #include "VideoSystem.hh"
11 #include "RenderSettings.hh"
12 #include "VideoSourceSetting.hh"
13 #include "IntegerSetting.hh"
14 #include "VDP.hh"
15 #include "VDPVRAM.hh"
16 #include "SpriteChecker.hh"
17 #include "EventDistributor.hh"
18 #include "Event.hh"
19 #include "RealTime.hh"
20 #include "SpeedManager.hh"
21 #include "ThrottleManager.hh"
22 #include "GlobalSettings.hh"
23 #include "MSXMotherBoard.hh"
24 #include "Reactor.hh"
25 #include "Timer.hh"
26 #include "one_of.hh"
27 #include "unreachable.hh"
28 #include <algorithm>
29 #include <cassert>
30 #include <cmath>
31 
32 namespace openmsx {
33 
34 void PixelRenderer::draw(
35  int startX, int startY, int endX, int endY, DrawType drawType, bool atEnd)
36 {
37  if (drawType == DRAW_BORDER) {
38  rasterizer->drawBorder(startX, startY, endX, endY);
39  } else {
40  assert(drawType == DRAW_DISPLAY);
41 
42  // Calculate display coordinates.
43  int zero = vdp.getLineZero();
44  int displayX = (startX - vdp.getLeftSprites()) / 2;
45  int displayY = startY - zero;
46  if (!vdp.getDisplayMode().isTextMode()) {
47  displayY += vdp.getVerticalScroll();
48  } else {
49  // this is not what the real VDP does, but it is good
50  // enough for "Boring scroll" demo part of "Relax"
51  displayY = (displayY & 7) | (textModeCounter * 8);
52  if (atEnd && (drawType == DRAW_DISPLAY)) {
53  int low = std::max(0, (startY - zero)) / 8;
54  int high = std::max(0, (endY - zero)) / 8;
55  textModeCounter += (high - low);
56  }
57  }
58 
59  displayY &= 255; // Page wrap.
60  int displayWidth = (endX - (startX & ~1)) / 2;
61  int displayHeight = endY - startY;
62 
63  assert(0 <= displayX);
64  assert(displayX + displayWidth <= 512);
65 
66  rasterizer->drawDisplay(
67  startX, startY,
68  displayX - vdp.getHorizontalScrollLow() * 2, displayY,
69  displayWidth, displayHeight
70  );
71  if (vdp.spritesEnabled() && !renderSettings.getDisableSprites()) {
72  rasterizer->drawSprites(
73  startX, startY,
74  displayX / 2, displayY,
75  (displayWidth + 1) / 2, displayHeight);
76  }
77  }
78 }
79 
80 void PixelRenderer::subdivide(
81  int startX, int startY, int endX, int endY, int clipL, int clipR,
82  DrawType drawType)
83 {
84  // Partial first line.
85  if (startX > clipL) {
86  bool atEnd = (startY != endY) || (endX >= clipR);
87  if (startX < clipR) {
88  draw(startX, startY, (atEnd ? clipR : endX),
89  startY + 1, drawType, atEnd);
90  }
91  if (startY == endY) return;
92  startY++;
93  }
94  // Partial last line.
95  bool drawLast = false;
96  if (endX >= clipR) {
97  endY++;
98  } else if (endX > clipL) {
99  drawLast = true;
100  }
101  // Full middle lines.
102  if (startY < endY) {
103  draw(clipL, startY, clipR, endY, drawType, true);
104  }
105  // Actually draw last line if necessary.
106  // The point of keeping top-to-bottom draw order is that it increases
107  // the locality of memory references, which generally improves cache
108  // hit rates.
109  if (drawLast) draw(clipL, endY, endX, endY + 1, drawType, false);
110 }
111 
113  : vdp(vdp_), vram(vdp.getVRAM())
114  , eventDistributor(vdp.getReactor().getEventDistributor())
115  , realTime(vdp.getMotherBoard().getRealTime())
116  , speedManager(
117  vdp.getReactor().getGlobalSettings().getSpeedManager())
118  , throttleManager(
119  vdp.getReactor().getGlobalSettings().getThrottleManager())
120  , renderSettings(display.getRenderSettings())
121  , videoSourceSetting(vdp.getMotherBoard().getVideoSource())
122  , spriteChecker(vdp.getSpriteChecker())
123  , rasterizer(display.getVideoSystem().createRasterizer(vdp))
124 {
125  // In case of loadstate we can't yet query any state from the VDP
126  // (because that object is not yet fully deserialized). But
127  // VDP::serialize() will call Renderer::reInit() again when it is
128  // safe to query.
129  reInit();
130 
131  finishFrameDuration = 0;
132  frameSkipCounter = 999; // force drawing of frame
133  prevRenderFrame = false;
134 
135  renderSettings.getMaxFrameSkipSetting().attach(*this);
136  renderSettings.getMinFrameSkipSetting().attach(*this);
137 }
138 
140 {
141  renderSettings.getMinFrameSkipSetting().detach(*this);
142  renderSettings.getMaxFrameSkipSetting().detach(*this);
143 }
144 
146 {
147  return rasterizer->getPostProcessor();
148 }
149 
151 {
152  // Don't draw before frameStart() is called.
153  // This for example can happen after a loadstate or after switching
154  // renderer in the middle of a frame.
155  renderFrame = false;
156  paintFrame = false;
157 
158  rasterizer->reset();
159  displayEnabled = vdp.isDisplayEnabled();
160 }
161 
162 void PixelRenderer::updateDisplayEnabled(bool enabled, EmuTime::param time)
163 {
164  sync(time, true);
165  displayEnabled = enabled;
166 }
167 
168 void PixelRenderer::frameStart(EmuTime::param time)
169 {
170  if (!rasterizer->isActive()) {
171  frameSkipCounter = 999;
172  renderFrame = false;
173  prevRenderFrame = false;
174  paintFrame = false;
175  return;
176  }
177 
178  prevRenderFrame = renderFrame;
179  if (vdp.isInterlaced() && renderSettings.getDeinterlace()
180  && vdp.getEvenOdd() && vdp.isEvenOddEnabled()) {
181  // Deinterlaced odd frame: do same as even frame.
182  paintFrame = prevRenderFrame;
183  } else if (throttleManager.isThrottled()) {
184  // Note: min/maxFrameSkip control the number of skipped frames, but
185  // for every series of skipped frames there is also one painted
186  // frame, so our boundary checks are offset by one.
187  int counter = int(frameSkipCounter);
188  if (counter < renderSettings.getMinFrameSkip()) {
189  paintFrame = false;
190  } else if (counter > renderSettings.getMaxFrameSkip()) {
191  paintFrame = true;
192  } else {
193  paintFrame = realTime.timeLeft(
194  unsigned(finishFrameDuration), time);
195  }
196  frameSkipCounter += 1.0f / float(speedManager.getSpeed());
197  } else {
198  // We need to render a frame every now and then,
199  // to show the user what is happening.
200  paintFrame = (Timer::getTime() - lastPaintTime) >= 100000; // 10 fps
201  }
202 
203  if (paintFrame) {
204  frameSkipCounter = std::remainder(frameSkipCounter, 1.0f);
205  } else if (!rasterizer->isRecording()) {
206  renderFrame = false;
207  return;
208  }
209  renderFrame = true;
210 
211  rasterizer->frameStart(time);
212 
213  accuracy = renderSettings.getAccuracy();
214 
215  nextX = 0;
216  nextY = 0;
217  // This is not what the real VDP does, but it is good enough
218  // for the "Boring scroll" demo part of ANMA's "Relax" demo.
219  textModeCounter = 0;
220 }
221 
222 void PixelRenderer::frameEnd(EmuTime::param time)
223 {
224  if (renderFrame) {
225  // Render changes from this last frame.
226  sync(time, true);
227 
228  // Let underlying graphics system finish rendering this frame.
229  auto time1 = Timer::getTime();
230  rasterizer->frameEnd();
231  auto time2 = Timer::getTime();
232  auto current = time2 - time1;
233  const float ALPHA = 0.2f;
234  finishFrameDuration = finishFrameDuration * (1 - ALPHA) +
235  current * ALPHA;
236 
237  if (vdp.isInterlaced() && vdp.isEvenOddEnabled()
238  && renderSettings.getDeinterlace() && !prevRenderFrame) {
239  // Don't paint in deinterlace mode when previous frame
240  // was not rendered.
241  paintFrame = false;
242  }
243  if (paintFrame) {
244  lastPaintTime = time2;
245  }
246  }
247  if (vdp.getMotherBoard().isActive() &&
248  !vdp.getMotherBoard().isFastForwarding()) {
249  eventDistributor.distributeEvent(
250  Event::create<FinishFrameEvent>(
251  rasterizer->getPostProcessor()->getVideoSource(),
252  videoSourceSetting.getSource(),
253  !paintFrame));
254  }
255 }
256 
258  byte scroll, EmuTime::param time)
259 {
260  if (displayEnabled) sync(time);
261  rasterizer->setHorizontalScrollLow(scroll);
262 }
263 
265  byte /*scroll*/, EmuTime::param time)
266 {
267  if (displayEnabled) sync(time);
268 }
269 
271  bool masked, EmuTime::param time)
272 {
273  if (displayEnabled) sync(time);
274  rasterizer->setBorderMask(masked);
275 }
276 
278  bool /*multiPage*/, EmuTime::param time)
279 {
280  if (displayEnabled) sync(time);
281 }
282 
284  bool enabled, EmuTime::param time)
285 {
286  if (displayEnabled) sync(time);
287  rasterizer->setTransparency(enabled);
288 }
289 
291  const RawFrame* videoSource, EmuTime::param time)
292 {
293  if (displayEnabled) sync(time);
294  rasterizer->setSuperimposeVideoFrame(videoSource);
295 }
296 
298  int /*color*/, EmuTime::param time)
299 {
300  if (displayEnabled) sync(time);
301 }
302 
304  int color, EmuTime::param time)
305 {
306  sync(time);
307  rasterizer->setBackgroundColor(color);
308 }
309 
311  int /*color*/, EmuTime::param time)
312 {
313  if (displayEnabled) sync(time);
314 }
315 
317  int /*color*/, EmuTime::param time)
318 {
319  if (displayEnabled) sync(time);
320 }
321 
323  bool /*enabled*/, EmuTime::param /*time*/)
324 {
325  // TODO: When the sync call is enabled, the screen flashes on
326  // every call to this method.
327  // I don't know why exactly, but it's probably related to
328  // being called at frame start.
329  //sync(time);
330 }
331 
333  int index, int grb, EmuTime::param time)
334 {
335  if (displayEnabled) {
336  sync(time);
337  } else {
338  // Only sync if border color changed.
339  DisplayMode mode = vdp.getDisplayMode();
340  if (mode.getBase() == DisplayMode::GRAPHIC5) {
341  int bgColor = vdp.getBackgroundColor();
342  if (index == one_of(bgColor & 3, bgColor >> 2)) {
343  sync(time);
344  }
345  } else if (mode.getByte() != DisplayMode::GRAPHIC7) {
346  if (index == vdp.getBackgroundColor()) {
347  sync(time);
348  }
349  }
350  }
351  rasterizer->setPalette(index, grb);
352 }
353 
355  int /*scroll*/, EmuTime::param time)
356 {
357  if (displayEnabled) sync(time);
358 }
359 
361  int adjust, EmuTime::param time)
362 {
363  if (displayEnabled) sync(time);
364  rasterizer->setHorizontalAdjust(adjust);
365 }
366 
368  DisplayMode mode, EmuTime::param time)
369 {
370  // Sync if in display area or if border drawing process changes.
371  DisplayMode oldMode = vdp.getDisplayMode();
372  if (displayEnabled
373  || oldMode.getByte() == DisplayMode::GRAPHIC5
374  || oldMode.getByte() == DisplayMode::GRAPHIC7
375  || mode.getByte() == DisplayMode::GRAPHIC5
376  || mode.getByte() == DisplayMode::GRAPHIC7) {
377  sync(time, true);
378  }
379  rasterizer->setDisplayMode(mode);
380 }
381 
383  int /*addr*/, EmuTime::param time)
384 {
385  if (displayEnabled) sync(time);
386 }
387 
389  int /*addr*/, EmuTime::param time)
390 {
391  if (displayEnabled) sync(time);
392 }
393 
395  int /*addr*/, EmuTime::param time)
396 {
397  if (displayEnabled) sync(time);
398 }
399 
401  bool /*enabled*/, EmuTime::param time
402 ) {
403  if (displayEnabled) sync(time);
404 }
405 
406 static constexpr bool overlap(
407  int displayY0, // start of display region, inclusive
408  int displayY1, // end of display region, exclusive
409  int vramLine0, // start of VRAM region, inclusive
410  int vramLine1 // end of VRAM region, exclusive
411  // Note: Display region can wrap around: 256 -> 0.
412  // VRAM region cannot wrap around.
413 ) {
414  if (displayY0 <= displayY1) {
415  if (vramLine1 > displayY0) {
416  if (vramLine0 <= displayY1) return true;
417  }
418  } else {
419  if (vramLine1 > displayY0) return true;
420  if (vramLine0 <= displayY1) return true;
421  }
422  return false;
423 }
424 
425 inline bool PixelRenderer::checkSync(int offset, EmuTime::param time)
426 {
427  // TODO: Because range is entire VRAM, offset == address.
428 
429  // If display is disabled, VRAM changes will not affect the
430  // renderer output, therefore sync is not necessary.
431  // TODO: Have bitmapVisibleWindow disabled in this case.
432  if (!displayEnabled) return false;
433  //if (frameSkipCounter != 0) return false; // TODO
434  if (accuracy == RenderSettings::ACC_SCREEN) return false;
435 
436  // Calculate what display lines are scanned between current
437  // renderer time and update-to time.
438  // Note: displayY1 is inclusive.
439  int deltaY = vdp.getVerticalScroll() - vdp.getLineZero();
440  int limitY = vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE;
441  int displayY0 = (nextY + deltaY) & 255;
442  int displayY1 = (limitY + deltaY) & 255;
443 
444  switch(vdp.getDisplayMode().getBase()) {
447  if (vram.colorTable.isInside(offset)) {
448  int vramQuarter = (offset & 0x1800) >> 11;
449  int mask = (vram.colorTable.getMask() & 0x1800) >> 11;
450  for (auto i : xrange(4)) {
451  if ((i & mask) == vramQuarter
452  && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
453  /*fprintf(stderr,
454  "color table: %05X %04X - quarter %d\n",
455  offset, offset & 0x1FFF, i
456  );*/
457  return true;
458  }
459  }
460  }
461  if (vram.patternTable.isInside(offset)) {
462  int vramQuarter = (offset & 0x1800) >> 11;
463  int mask = (vram.patternTable.getMask() & 0x1800) >> 11;
464  for (auto i : xrange(4)) {
465  if ((i & mask) == vramQuarter
466  && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
467  /*fprintf(stderr,
468  "pattern table: %05X %04X - quarter %d\n",
469  offset, offset & 0x1FFF, i
470  );*/
471  return true;
472  }
473  }
474  }
475  if (vram.nameTable.isInside(offset)) {
476  int vramLine = ((offset & 0x3FF) / 32) * 8;
477  if (overlap(displayY0, displayY1, vramLine, vramLine + 8)) {
478  /*fprintf(stderr,
479  "name table: %05X %03X - line %d\n",
480  offset, offset & 0x3FF, vramLine
481  );*/
482  return true;
483  }
484  }
485  return false;
487  case DisplayMode::GRAPHIC5: {
488  if (vdp.isFastBlinkEnabled()) {
489  // TODO could be improved
490  return true;
491  }
492  // Is the address inside the visual page(s)?
493  // TODO: Also look at which lines are touched inside pages.
494  int visiblePage = vram.nameTable.getMask()
495  & (0x10000 | (vdp.getEvenOddMask() << 7));
496  if (vdp.isMultiPageScrolling()) {
497  return (offset & 0x18000) == visiblePage
498  || (offset & 0x18000) == (visiblePage & 0x10000);
499  } else {
500  return (offset & 0x18000) == visiblePage;
501  }
502  }
505  return true; // TODO: Implement better detection.
506  default:
507  // Range unknown; assume full range.
508  return vram.nameTable.isInside(offset)
509  || vram.colorTable.isInside(offset)
510  || vram.patternTable.isInside(offset);
511  }
512 }
513 
514 void PixelRenderer::updateVRAM(unsigned offset, EmuTime::param time)
515 {
516  // Note: No need to sync if display is disabled, because then the
517  // output does not depend on VRAM (only on background color).
518  if (renderFrame && displayEnabled && checkSync(offset, time)) {
519  //fprintf(stderr, "vram sync @ line %d\n",
520  // vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE);
521  renderUntil(time);
522  }
523 }
524 
525 void PixelRenderer::updateWindow(bool /*enabled*/, EmuTime::param /*time*/)
526 {
527  // The bitmapVisibleWindow has moved to a different area.
528  // This update is redundant: Renderer will be notified in another way
529  // as well (updateDisplayEnabled or updateNameBase, for example).
530  // TODO: Can this be used as the main update method instead?
531 }
532 
533 void PixelRenderer::sync(EmuTime::param time, bool force)
534 {
535  if (!renderFrame) return;
536 
537  // Synchronisation is done in two phases:
538  // 1. update VRAM
539  // 2. update other subsystems
540  // Note that as part of step 1, type 2 updates can be triggered.
541  // Executing step 2 takes care of the subsystem changes that occur
542  // after the last VRAM update.
543  // This scheme makes sure type 2 routines such as renderUntil and
544  // checkUntil are not re-entered, which was causing major pain in
545  // the past.
546  // TODO: I wonder if it's possible to enforce this synchronisation
547  // scheme at a higher level. Probably. But how...
548  //if ((frameSkipCounter == 0) && TODO
549  if (accuracy != RenderSettings::ACC_SCREEN || force) {
550  vram.sync(time);
551  renderUntil(time);
552  }
553 }
554 
555 void PixelRenderer::renderUntil(EmuTime::param time)
556 {
557  // Translate from time to pixel position.
558  int limitTicks = vdp.getTicksThisFrame(time);
559  assert(limitTicks <= vdp.getTicksPerFrame());
560  auto [limitX, limitY] = [&]() -> std::pair<int, int> {
561  switch (accuracy) {
563  return {limitTicks % VDP::TICKS_PER_LINE,
564  limitTicks / VDP::TICKS_PER_LINE};
565  }
568  // Note: I'm not sure the rounding point is optimal.
569  // It used to be based on the left margin, but that doesn't work
570  // because the margin can change which leads to a line being
571  // rendered even though the time doesn't advance.
572  return {0,
573  (limitTicks + VDP::TICKS_PER_LINE - 400) / VDP::TICKS_PER_LINE};
574  default:
575  UNREACHABLE;
576  return {0, 0}; // avoid warning
577  }
578  }();
579 
580  // Stop here if there is nothing to render.
581  // This ensures that no pixels are rendered in a series of updates that
582  // happen at exactly the same time; the VDP subsystem states may be
583  // inconsistent until all updates are performed.
584  // Also it is a small performance optimisation.
585  if (limitX == nextX && limitY == nextY) return;
586 
587  if (displayEnabled) {
588  if (vdp.spritesEnabled()) {
589  // Update sprite checking, so that rasterizer can call getSprites.
590  spriteChecker.checkUntil(time);
591  }
592 
593  // Calculate start and end of borders in ticks since start of line.
594  // The 0..7 extra horizontal scroll low pixels should be drawn in
595  // border color. These will be drawn together with the border,
596  // but sprites above these pixels are clipped at the actual border
597  // rather than the end of the border colored area.
598  // TODO: Move these calculations and getDisplayLeft() to VDP.
599  int borderL = vdp.getLeftBorder();
600  int displayL =
601  vdp.isBorderMasked() ? borderL : vdp.getLeftBackground();
602  int borderR = vdp.getRightBorder();
603 
604  // It's important that right border is drawn last (after left
605  // border and display area). See comment in SDLRasterizer::drawBorder().
606  // Left border.
607  subdivide(nextX, nextY, limitX, limitY,
608  0, displayL, DRAW_BORDER);
609  // Display area.
610  subdivide(nextX, nextY, limitX, limitY,
611  displayL, borderR, DRAW_DISPLAY);
612  // Right border.
613  subdivide(nextX, nextY, limitX, limitY,
614  borderR, VDP::TICKS_PER_LINE, DRAW_BORDER);
615  } else {
616  subdivide(nextX, nextY, limitX, limitY,
617  0, VDP::TICKS_PER_LINE, DRAW_BORDER);
618  }
619 
620  nextX = limitX;
621  nextY = limitY;
622 }
623 
624 void PixelRenderer::update(const Setting& setting) noexcept
625 {
626  assert(&setting == one_of(&renderSettings.getMinFrameSkipSetting(),
627  &renderSettings.getMaxFrameSkipSetting()));
628  (void)setting;
629  // Force drawing of frame.
630  frameSkipCounter = 999;
631 }
632 
633 } // namespace openmsx
BaseSetting * setting
Definition: Interpreter.cc:27
Definition: one_of.hh:7
Represents a VDP display mode.
Definition: DisplayMode.hh:16
constexpr byte getBase() const
Get the base dispay mode as an integer: M5..M1 combined.
Definition: DisplayMode.hh:115
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
Definition: DisplayMode.hh:130
constexpr byte getByte() const
Get the dispay mode as a byte: YAE YJK M5..M1 combined.
Definition: DisplayMode.hh:101
Represents the output window/screen of openMSX.
Definition: Display.hh:33
void distributeEvent(Event &&event)
Schedule the given event for delivery.
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:71
bool isFastForwarding() const
void updateSuperimposing(const RawFrame *videoSource, EmuTime::param time) override
Informs the renderer of a VDP superimposing change.
void updateWindow(bool enabled, EmuTime::param time) override
Informs the observer that the entire VRAM window will change.
PostProcessor * getPostProcessor() const override
See VDP::getPostProcessor.
void updateForegroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP foreground color change.
void updateBorderMask(bool masked, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the border mask has been enabled/disabled.
void updatePatternBase(int addr, EmuTime::param time) override
Informs the renderer of a pattern table base address change.
void updateVerticalScroll(int scroll, EmuTime::param time) override
Informs the renderer of a vertical scroll change.
void updateColorBase(int addr, EmuTime::param time) override
Informs the renderer of a color table base address change.
void updateBlinkState(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP blinking state change.
void updateTransparency(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP transparency enable/disable change.
void updateNameBase(int addr, EmuTime::param time) override
Informs the renderer of a name table base address change.
void updateDisplayMode(DisplayMode mode, EmuTime::param time) override
Informs the renderer of a VDP display mode change.
void updateHorizontalAdjust(int adjust, EmuTime::param time) override
Informs the renderer of a horizontal adjust change.
PixelRenderer(VDP &vdp, Display &display)
void updateVRAM(unsigned offset, EmuTime::param time) override
Informs the observer of a change in VRAM contents.
void frameEnd(EmuTime::param time) override
Signals the end of a frame.
void updateSpritesEnabled(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP sprites enabled change.
void updateHorizontalScrollHigh(byte scroll, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the higher scroll value has changed.
void updatePalette(int index, int grb, EmuTime::param time) override
Informs the renderer of a VDP palette change.
void frameStart(EmuTime::param time) override
Signals the start of a new frame.
void updateBlinkBackgroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP blink background color change.
void updateHorizontalScrollLow(byte scroll, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the lower scroll value has changed.
void updateMultiPage(bool multiPage, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the multi page setting has changed.
void reInit() override
Reinitialize Renderer state.
void updateBackgroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP background color change.
void updateDisplayEnabled(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP display enabled change.
void updateBlinkForegroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP blink foreground color change.
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
bool timeLeft(uint64_t us, EmuTime::param time)
Check that there is enough real time left before we reach as certain point in emulated time.
Definition: RealTime.cc:65
bool getDeinterlace() const
Deinterlacing [on, off].
IntegerSetting & getMinFrameSkipSetting()
The current min frameskip.
Accuracy getAccuracy() const
Accuracy [screen, line, pixel].
bool getDisableSprites() const
Disable sprite rendering?
IntegerSetting & getMaxFrameSkipSetting()
The current max frameskip.
double getSpeed() const
Return the desired ratio between emutime and real time.
Definition: SpeedManager.hh:26
void checkUntil(EmuTime::param time)
Update sprite checking until specified line.
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
bool isThrottled() const
Ask if throttling is enabled.
VRAMWindow colorTable
Definition: VDPVRAM.hh:670
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition: VDPVRAM.hh:402
VRAMWindow patternTable
Definition: VDPVRAM.hh:671
VRAMWindow nameTable
Definition: VDPVRAM.hh:669
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:63
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition: VDP.hh:381
int getEvenOddMask() const
Expresses the state of even/odd page interchange in a mask on the line number.
Definition: VDP.hh:396
bool spritesEnabled() const
Are sprites enabled?
Definition: VDP.hh:256
int getLeftBackground() const
Gets the number of VDP clockticks between start of line and the time when the background pixel with X...
Definition: VDP.hh:561
int getBackgroundColor() const
Gets the current background color.
Definition: VDP.hh:204
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition: VDP.hh:373
bool isFastBlinkEnabled() const
Get 'fast-blink' status.
Definition: VDP.hh:358
bool isBorderMasked() const
Gets the current border mask setting.
Definition: VDP.hh:307
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
Definition: VDP.hh:72
int getTicksPerFrame() const
Gets the number of VDP clockticks (21MHz) per frame.
Definition: VDP.hh:501
int getRightBorder() const
Gets the number of VDP clockticks between start of line and the start of the right border.
Definition: VDP.hh:551
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
Definition: VDP.hh:141
int getLineZero() const
Get the absolute line number of display line zero.
Definition: VDP.hh:325
byte getVerticalScroll() const
Gets the current vertical scroll (line displayed at Y=0).
Definition: VDP.hh:280
bool isMultiPageScrolling() const
Is multi page scrolling enabled? It is considered enabled if both the multi page scrolling flag is en...
Definition: VDP.hh:317
int getLeftBorder() const
Gets the number of VDP clockticks between start of line and the end of the left border.
Definition: VDP.hh:544
bool isInterlaced() const
Get interlace status.
Definition: VDP.hh:344
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
Definition: VDP.hh:248
int getTicksThisFrame(EmuTime::param time) const
Gets the number of VDP clock ticks (21MHz) elapsed between a given time and the start of this frame.
Definition: VDP.hh:466
byte getHorizontalScrollLow() const
Gets the current horizontal scroll lower bits.
Definition: VDP.hh:289
int getLeftSprites() const
Gets the number of VDP clockticks between start of line and the start of the sprite plane.
Definition: VDP.hh:533
int getMask() const
Gets the mask for this window.
Definition: VDPVRAM.hh:146
bool isInside(unsigned address) const
Test whether an address is inside this window.
Definition: VDPVRAM.hh:297
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr nibble mask[4][13]
Definition: RP5C01.cc:34
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155