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  , finishFrameDuration(0)
125  , frameSkipCounter(999) // force drawing of frame
126  , prevRenderFrame(false)
127 {
128  // In case of loadstate we can't yet query any state from the VDP
129  // (because that object is not yet fully deserialized). But
130  // VDP::serialize() will call Renderer::reInit() again when it is
131  // safe to query.
132  reInit();
133 
134  renderSettings.getMaxFrameSkipSetting().attach(*this);
135  renderSettings.getMinFrameSkipSetting().attach(*this);
136 }
137 
139 {
140  renderSettings.getMinFrameSkipSetting().detach(*this);
141  renderSettings.getMaxFrameSkipSetting().detach(*this);
142 }
143 
145 {
146  return rasterizer->getPostProcessor();
147 }
148 
150 {
151  // Don't draw before frameStart() is called.
152  // This for example can happen after a loadstate or after switching
153  // renderer in the middle of a frame.
154  renderFrame = false;
155  paintFrame = false;
156 
157  rasterizer->reset();
158  displayEnabled = vdp.isDisplayEnabled();
159 }
160 
161 void PixelRenderer::updateDisplayEnabled(bool enabled, EmuTime::param time)
162 {
163  sync(time, true);
164  displayEnabled = enabled;
165 }
166 
167 void PixelRenderer::frameStart(EmuTime::param time)
168 {
169  if (!rasterizer->isActive()) {
170  frameSkipCounter = 999;
171  renderFrame = false;
172  prevRenderFrame = false;
173  paintFrame = false;
174  return;
175  }
176 
177  prevRenderFrame = renderFrame;
178  if (vdp.isInterlaced() && renderSettings.getDeinterlace()
179  && vdp.getEvenOdd() && vdp.isEvenOddEnabled()) {
180  // Deinterlaced odd frame: do same as even frame.
181  paintFrame = prevRenderFrame;
182  } else if (throttleManager.isThrottled()) {
183  // Note: min/maxFrameSkip control the number of skipped frames, but
184  // for every series of skipped frames there is also one painted
185  // frame, so our boundary checks are offset by one.
186  int counter = int(frameSkipCounter);
187  if (counter < renderSettings.getMinFrameSkip()) {
188  paintFrame = false;
189  } else if (counter > renderSettings.getMaxFrameSkip()) {
190  paintFrame = true;
191  } else {
192  paintFrame = realTime.timeLeft(
193  unsigned(finishFrameDuration), time);
194  }
195  frameSkipCounter += 1.0f / float(speedManager.getSpeed());
196  } else {
197  // We need to render a frame every now and then,
198  // to show the user what is happening.
199  paintFrame = (Timer::getTime() - lastPaintTime) >= 100000; // 10 fps
200  }
201 
202  if (paintFrame) {
203  frameSkipCounter = std::remainder(frameSkipCounter, 1.0f);
204  } else if (!rasterizer->isRecording()) {
205  renderFrame = false;
206  return;
207  }
208  renderFrame = true;
209 
210  rasterizer->frameStart(time);
211 
212  accuracy = renderSettings.getAccuracy();
213 
214  nextX = 0;
215  nextY = 0;
216  // This is not what the real VDP does, but it is good enough
217  // for the "Boring scroll" demo part of ANMA's "Relax" demo.
218  textModeCounter = 0;
219 }
220 
221 void PixelRenderer::frameEnd(EmuTime::param time)
222 {
223  if (renderFrame) {
224  // Render changes from this last frame.
225  sync(time, true);
226 
227  // Let underlying graphics system finish rendering this frame.
228  auto time1 = Timer::getTime();
229  rasterizer->frameEnd();
230  auto time2 = Timer::getTime();
231  auto current = time2 - time1;
232  const float ALPHA = 0.2f;
233  finishFrameDuration = finishFrameDuration * (1 - ALPHA) +
234  current * ALPHA;
235 
236  if (vdp.isInterlaced() && vdp.isEvenOddEnabled()
237  && renderSettings.getDeinterlace() && !prevRenderFrame) {
238  // Don't paint in deinterlace mode when previous frame
239  // was not rendered.
240  paintFrame = false;
241  }
242  if (paintFrame) {
243  lastPaintTime = time2;
244  }
245  }
246  if (vdp.getMotherBoard().isActive() &&
247  !vdp.getMotherBoard().isFastForwarding()) {
248  eventDistributor.distributeEvent(
249  Event::create<FinishFrameEvent>(
250  rasterizer->getPostProcessor()->getVideoSource(),
251  videoSourceSetting.getSource(),
252  !paintFrame));
253  }
254 }
255 
257  byte scroll, EmuTime::param time)
258 {
259  if (displayEnabled) sync(time);
260  rasterizer->setHorizontalScrollLow(scroll);
261 }
262 
264  byte /*scroll*/, EmuTime::param time)
265 {
266  if (displayEnabled) sync(time);
267 }
268 
270  bool masked, EmuTime::param time)
271 {
272  if (displayEnabled) sync(time);
273  rasterizer->setBorderMask(masked);
274 }
275 
277  bool /*multiPage*/, EmuTime::param time)
278 {
279  if (displayEnabled) sync(time);
280 }
281 
283  bool enabled, EmuTime::param time)
284 {
285  if (displayEnabled) sync(time);
286  rasterizer->setTransparency(enabled);
287 }
288 
290  const RawFrame* videoSource, EmuTime::param time)
291 {
292  if (displayEnabled) sync(time);
293  rasterizer->setSuperimposeVideoFrame(videoSource);
294 }
295 
297  int /*color*/, EmuTime::param time)
298 {
299  if (displayEnabled) sync(time);
300 }
301 
303  int color, EmuTime::param time)
304 {
305  sync(time);
306  rasterizer->setBackgroundColor(color);
307 }
308 
310  int /*color*/, EmuTime::param time)
311 {
312  if (displayEnabled) sync(time);
313 }
314 
316  int /*color*/, EmuTime::param time)
317 {
318  if (displayEnabled) sync(time);
319 }
320 
322  bool /*enabled*/, EmuTime::param /*time*/)
323 {
324  // TODO: When the sync call is enabled, the screen flashes on
325  // every call to this method.
326  // I don't know why exactly, but it's probably related to
327  // being called at frame start.
328  //sync(time);
329 }
330 
332  int index, int grb, EmuTime::param time)
333 {
334  if (displayEnabled) {
335  sync(time);
336  } else {
337  // Only sync if border color changed.
338  DisplayMode mode = vdp.getDisplayMode();
339  if (mode.getBase() == DisplayMode::GRAPHIC5) {
340  int bgColor = vdp.getBackgroundColor();
341  if (index == one_of(bgColor & 3, bgColor >> 2)) {
342  sync(time);
343  }
344  } else if (mode.getByte() != DisplayMode::GRAPHIC7) {
345  if (index == vdp.getBackgroundColor()) {
346  sync(time);
347  }
348  }
349  }
350  rasterizer->setPalette(index, grb);
351 }
352 
354  int /*scroll*/, EmuTime::param time)
355 {
356  if (displayEnabled) sync(time);
357 }
358 
360  int adjust, EmuTime::param time)
361 {
362  if (displayEnabled) sync(time);
363  rasterizer->setHorizontalAdjust(adjust);
364 }
365 
367  DisplayMode mode, EmuTime::param time)
368 {
369  // Sync if in display area or if border drawing process changes.
370  DisplayMode oldMode = vdp.getDisplayMode();
371  if (displayEnabled
372  || oldMode.getByte() == DisplayMode::GRAPHIC5
373  || oldMode.getByte() == DisplayMode::GRAPHIC7
374  || mode.getByte() == DisplayMode::GRAPHIC5
375  || mode.getByte() == DisplayMode::GRAPHIC7) {
376  sync(time, true);
377  }
378  rasterizer->setDisplayMode(mode);
379 }
380 
382  int /*addr*/, EmuTime::param time)
383 {
384  if (displayEnabled) sync(time);
385 }
386 
388  int /*addr*/, EmuTime::param time)
389 {
390  if (displayEnabled) sync(time);
391 }
392 
394  int /*addr*/, EmuTime::param time)
395 {
396  if (displayEnabled) sync(time);
397 }
398 
400  bool /*enabled*/, EmuTime::param time
401 ) {
402  if (displayEnabled) sync(time);
403 }
404 
405 static constexpr bool overlap(
406  int displayY0, // start of display region, inclusive
407  int displayY1, // end of display region, exclusive
408  int vramLine0, // start of VRAM region, inclusive
409  int vramLine1 // end of VRAM region, exclusive
410  // Note: Display region can wrap around: 256 -> 0.
411  // VRAM region cannot wrap around.
412 ) {
413  if (displayY0 <= displayY1) {
414  if (vramLine1 > displayY0) {
415  if (vramLine0 <= displayY1) return true;
416  }
417  } else {
418  if (vramLine1 > displayY0) return true;
419  if (vramLine0 <= displayY1) return true;
420  }
421  return false;
422 }
423 
424 inline bool PixelRenderer::checkSync(int offset, EmuTime::param time)
425 {
426  // TODO: Because range is entire VRAM, offset == address.
427 
428  // If display is disabled, VRAM changes will not affect the
429  // renderer output, therefore sync is not necessary.
430  // TODO: Have bitmapVisibleWindow disabled in this case.
431  if (!displayEnabled) return false;
432  //if (frameSkipCounter != 0) return false; // TODO
433  if (accuracy == RenderSettings::ACC_SCREEN) return false;
434 
435  // Calculate what display lines are scanned between current
436  // renderer time and update-to time.
437  // Note: displayY1 is inclusive.
438  int deltaY = vdp.getVerticalScroll() - vdp.getLineZero();
439  int limitY = vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE;
440  int displayY0 = (nextY + deltaY) & 255;
441  int displayY1 = (limitY + deltaY) & 255;
442 
443  switch(vdp.getDisplayMode().getBase()) {
446  if (vram.colorTable.isInside(offset)) {
447  int vramQuarter = (offset & 0x1800) >> 11;
448  int mask = (vram.colorTable.getMask() & 0x1800) >> 11;
449  for (auto i : xrange(4)) {
450  if ((i & mask) == vramQuarter
451  && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
452  /*fprintf(stderr,
453  "color table: %05X %04X - quarter %d\n",
454  offset, offset & 0x1FFF, i
455  );*/
456  return true;
457  }
458  }
459  }
460  if (vram.patternTable.isInside(offset)) {
461  int vramQuarter = (offset & 0x1800) >> 11;
462  int mask = (vram.patternTable.getMask() & 0x1800) >> 11;
463  for (auto i : xrange(4)) {
464  if ((i & mask) == vramQuarter
465  && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
466  /*fprintf(stderr,
467  "pattern table: %05X %04X - quarter %d\n",
468  offset, offset & 0x1FFF, i
469  );*/
470  return true;
471  }
472  }
473  }
474  if (vram.nameTable.isInside(offset)) {
475  int vramLine = ((offset & 0x3FF) / 32) * 8;
476  if (overlap(displayY0, displayY1, vramLine, vramLine + 8)) {
477  /*fprintf(stderr,
478  "name table: %05X %03X - line %d\n",
479  offset, offset & 0x3FF, vramLine
480  );*/
481  return true;
482  }
483  }
484  return false;
486  case DisplayMode::GRAPHIC5: {
487  if (vdp.isFastBlinkEnabled()) {
488  // TODO could be improved
489  return true;
490  }
491  // Is the address inside the visual page(s)?
492  // TODO: Also look at which lines are touched inside pages.
493  int visiblePage = vram.nameTable.getMask()
494  & (0x10000 | (vdp.getEvenOddMask() << 7));
495  if (vdp.isMultiPageScrolling()) {
496  return (offset & 0x18000) == visiblePage
497  || (offset & 0x18000) == (visiblePage & 0x10000);
498  } else {
499  return (offset & 0x18000) == visiblePage;
500  }
501  }
504  return true; // TODO: Implement better detection.
505  default:
506  // Range unknown; assume full range.
507  return vram.nameTable.isInside(offset)
508  || vram.colorTable.isInside(offset)
509  || vram.patternTable.isInside(offset);
510  }
511 }
512 
513 void PixelRenderer::updateVRAM(unsigned offset, EmuTime::param time)
514 {
515  // Note: No need to sync if display is disabled, because then the
516  // output does not depend on VRAM (only on background color).
517  if (renderFrame && displayEnabled && checkSync(offset, time)) {
518  //fprintf(stderr, "vram sync @ line %d\n",
519  // vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE);
520  renderUntil(time);
521  }
522 }
523 
524 void PixelRenderer::updateWindow(bool /*enabled*/, EmuTime::param /*time*/)
525 {
526  // The bitmapVisibleWindow has moved to a different area.
527  // This update is redundant: Renderer will be notified in another way
528  // as well (updateDisplayEnabled or updateNameBase, for example).
529  // TODO: Can this be used as the main update method instead?
530 }
531 
532 void PixelRenderer::sync(EmuTime::param time, bool force)
533 {
534  if (!renderFrame) return;
535 
536  // Synchronisation is done in two phases:
537  // 1. update VRAM
538  // 2. update other subsystems
539  // Note that as part of step 1, type 2 updates can be triggered.
540  // Executing step 2 takes care of the subsystem changes that occur
541  // after the last VRAM update.
542  // This scheme makes sure type 2 routines such as renderUntil and
543  // checkUntil are not re-entered, which was causing major pain in
544  // the past.
545  // TODO: I wonder if it's possible to enforce this synchronisation
546  // scheme at a higher level. Probably. But how...
547  //if ((frameSkipCounter == 0) && TODO
548  if (accuracy != RenderSettings::ACC_SCREEN || force) {
549  vram.sync(time);
550  renderUntil(time);
551  }
552 }
553 
554 void PixelRenderer::renderUntil(EmuTime::param time)
555 {
556  // Translate from time to pixel position.
557  int limitTicks = vdp.getTicksThisFrame(time);
558  assert(limitTicks <= vdp.getTicksPerFrame());
559  auto [limitX, limitY] = [&]() -> std::pair<int, int> {
560  switch (accuracy) {
562  return {limitTicks % VDP::TICKS_PER_LINE,
563  limitTicks / VDP::TICKS_PER_LINE};
564  }
567  // Note: I'm not sure the rounding point is optimal.
568  // It used to be based on the left margin, but that doesn't work
569  // because the margin can change which leads to a line being
570  // rendered even though the time doesn't advance.
571  return {0,
572  (limitTicks + VDP::TICKS_PER_LINE - 400) / VDP::TICKS_PER_LINE};
573  default:
574  UNREACHABLE;
575  return {0, 0}; // avoid warning
576  }
577  }();
578 
579  // Stop here if there is nothing to render.
580  // This ensures that no pixels are rendered in a series of updates that
581  // happen at exactly the same time; the VDP subsystem states may be
582  // inconsistent until all updates are performed.
583  // Also it is a small performance optimisation.
584  if (limitX == nextX && limitY == nextY) return;
585 
586  if (displayEnabled) {
587  if (vdp.spritesEnabled()) {
588  // Update sprite checking, so that rasterizer can call getSprites.
589  spriteChecker.checkUntil(time);
590  }
591 
592  // Calculate start and end of borders in ticks since start of line.
593  // The 0..7 extra horizontal scroll low pixels should be drawn in
594  // border color. These will be drawn together with the border,
595  // but sprites above these pixels are clipped at the actual border
596  // rather than the end of the border colored area.
597  // TODO: Move these calculations and getDisplayLeft() to VDP.
598  int borderL = vdp.getLeftBorder();
599  int displayL =
600  vdp.isBorderMasked() ? borderL : vdp.getLeftBackground();
601  int borderR = vdp.getRightBorder();
602 
603  // It's important that right border is drawn last (after left
604  // border and display area). See comment in SDLRasterizer::drawBorder().
605  // Left border.
606  subdivide(nextX, nextY, limitX, limitY,
607  0, displayL, DRAW_BORDER);
608  // Display area.
609  subdivide(nextX, nextY, limitX, limitY,
610  displayL, borderR, DRAW_DISPLAY);
611  // Right border.
612  subdivide(nextX, nextY, limitX, limitY,
613  borderR, VDP::TICKS_PER_LINE, DRAW_BORDER);
614  } else {
615  subdivide(nextX, nextY, limitX, limitY,
616  0, VDP::TICKS_PER_LINE, DRAW_BORDER);
617  }
618 
619  nextX = limitX;
620  nextY = limitY;
621 }
622 
623 void PixelRenderer::update(const Setting& setting) noexcept
624 {
625  assert(&setting == one_of(&renderSettings.getMinFrameSkipSetting(),
626  &renderSettings.getMaxFrameSkipSetting()));
627  (void)setting;
628  // Force drawing of frame.
629  frameSkipCounter = 999;
630 }
631 
632 } // 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:107
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
Definition: DisplayMode.hh:122
constexpr byte getByte() const
Get the display mode as a byte: YAE YJK M5..M1 combined.
Definition: DisplayMode.hh:93
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:15
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:669
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition: VDPVRAM.hh:401
VRAMWindow patternTable
Definition: VDPVRAM.hh:670
VRAMWindow nameTable
Definition: VDPVRAM.hh:668
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:145
bool isInside(unsigned address) const
Test whether an address is inside this window.
Definition: VDPVRAM.hh:296
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:276
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:133