28static constexpr int TICKS_LEFT_BORDER = 100 + 102;
34static constexpr int TICKS_VISIBLE_MIDDLE =
43static constexpr int translateX(
int absoluteX,
bool narrow)
45 int maxX =
narrow ? 640 : 320;
50 const int ROUND_MASK =
narrow ? ~1 : ~3;
52 ((absoluteX - (TICKS_VISIBLE_MIDDLE & ROUND_MASK))
58template<std::
unsigned_
integral Pixel>
59inline void SDLRasterizer<Pixel>::renderBitmapLine(std::span<Pixel> buf,
unsigned vramLine)
61 if (vdp.getDisplayMode().isPlanar()) {
62 auto [vramPtr0, vramPtr1] =
63 vram.bitmapCacheWindow.getReadAreaPlanar<256>(vramLine * 256);
64 bitmapConverter.convertLinePlanar(buf, vramPtr0, vramPtr1);
67 vram.bitmapCacheWindow.getReadArea<128>(vramLine * 128);
68 bitmapConverter.convertLine(buf, vramPtr);
72template<std::
unsigned_
integral Pixel>
75 std::unique_ptr<PostProcessor> postProcessor_)
76 : vdp(vdp_), vram(vdp.getVRAM())
78 , postProcessor(
std::move(postProcessor_))
79 , workFrame(
std::make_unique<
RawFrame>(screen.getPixelFormat(), 640, 240))
80 , renderSettings(display.getRenderSettings())
81 , characterConverter(vdp,
subspan<16>(palFg), palBg)
82 , bitmapConverter(palFg, PALETTE256, V9958_COLORS)
83 , spriteConverter(vdp.getSpriteChecker(), palBg)
90 for (
auto i :
xrange(16)) {
91 palFg[i] = palFg[i + 16] = palBg[i] =
92 V9938_COLORS[0][0][0];
102template<std::
unsigned_
integral Pixel>
105 renderSettings.getColorMatrixSetting().detach(*
this);
106 renderSettings.getGammaSetting() .detach(*
this);
107 renderSettings.getBrightnessSetting() .detach(*
this);
108 renderSettings.getContrastSetting() .detach(*
this);
111template<std::
unsigned_
integral Pixel>
114 return postProcessor.get();
117template<std::
unsigned_
integral Pixel>
120 return postProcessor->needRender() &&
121 vdp.getMotherBoard().isActive() &&
122 !vdp.getMotherBoard().isFastForwarding();
125template<std::
unsigned_
integral Pixel>
129 setDisplayMode(vdp.getDisplayMode());
130 spriteConverter.setTransparency(vdp.getTransparency());
135template<std::
unsigned_
integral Pixel>
138 if (!vdp.isMSX1VDP()) {
140 for (
auto i :
xrange(16)) {
141 setPalette(i, vdp.getPalette(i));
146template<std::
unsigned_
integral Pixel>
149 postProcessor->setSuperimposeVideoFrame(videoSource);
150 precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
151 videoSource, vdp.getBackgroundColor());
154template<std::
unsigned_
integral Pixel>
157 workFrame = postProcessor->rotateFrames(std::move(workFrame), time);
168 lineRenderTop = vdp.isPalTiming() ? 59 - 14 : 32 - 14;
171template<std::
unsigned_
integral Pixel>
176template<std::
unsigned_
integral Pixel>
180 bitmapConverter.setDisplayMode(mode);
182 characterConverter.setDisplayMode(mode);
184 precalcColorIndex0(mode, vdp.getTransparency(),
185 vdp.isSuperimposing(), vdp.getBackgroundColor());
186 spriteConverter.setDisplayMode(mode);
188 ? palGraphic7Sprites : palBg);
192template<std::
unsigned_
integral Pixel>
196 Pixel newColor = V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7];
197 palFg[index ] = newColor;
198 palFg[index + 16] = newColor;
199 palBg[index ] = newColor;
200 bitmapConverter.palette16Changed();
202 precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
203 vdp.isSuperimposing(), vdp.getBackgroundColor());
206template<std::
unsigned_
integral Pixel>
210 precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(),
211 vdp.isSuperimposing(), index);
215template<std::
unsigned_
integral Pixel>
220template<std::
unsigned_
integral Pixel>
225template<std::
unsigned_
integral Pixel>
230template<std::
unsigned_
integral Pixel>
233 spriteConverter.setTransparency(enabled);
234 precalcColorIndex0(vdp.getDisplayMode(), enabled,
235 vdp.isSuperimposing(), vdp.getBackgroundColor());
238template<std::
unsigned_
integral Pixel>
241 if (vdp.isMSX1VDP()) {
243 const auto palette = vdp.getMSX1Palette();
244 for (
auto i :
xrange(16)) {
245 const auto rgb = palette[i];
246 palFg[i] = palFg[i + 16] = palBg[i] =
247 screen.mapKeyedRGB<
Pixel>(
248 renderSettings.transformRGB(
249 vec3(rgb[0], rgb[1], rgb[2]) / 255.0f));
254 if (renderSettings.isColorMatrixIdentity()) {
257 std::array<int, 32> intensity;
258 for (
auto [i, r] :
enumerate(intensity)) {
259 r = narrow_cast<int>(255.0f * renderSettings.transformComponent(narrow<float>(i) / 31.0f));
261 for (
auto [rgb, col] :
enumerate(V9958_COLORS)) {
263 intensity[(rgb >> 10) & 31],
264 intensity[(rgb >> 5) & 31],
265 intensity[(rgb >> 0) & 31]));
268 for (
auto r :
xrange(32)) {
270 for (
auto b :
xrange(32)) {
271 vec3 rgb{narrow<float>(r),
274 V9958_COLORS[(r << 10) + (
g << 5) + b] =
275 screen.mapKeyedRGB<
Pixel>(
276 renderSettings.transformRGB(rgb / 31.0f));
284 for (
auto r3 :
xrange(8)) {
285 int r5 = (r3 << 2) | (r3 >> 1);
286 for (
auto g3 :
xrange(8)) {
287 int g5 = (g3 << 2) | (g3 >> 1);
288 for (
auto b3 :
xrange(8)) {
289 int b5 = (b3 << 2) | (b3 >> 1);
290 V9938_COLORS[r3][g3][b3] =
291 V9958_COLORS[(r5 << 10) + (g5 << 5) + b5];
297 if (renderSettings.isColorMatrixIdentity()) {
298 std::array<int, 8> intensity;
299 for (
auto [i, r] :
enumerate(intensity)) {
300 r = narrow_cast<int>(255.0f * renderSettings.transformComponent(narrow<float>(i) / 7.0f));
302 for (
auto r :
xrange(8)) {
304 for (
auto b :
xrange(8)) {
305 V9938_COLORS[r][
g][b] =
314 for (
auto r :
xrange(8)) {
316 for (
auto b :
xrange(8)) {
317 vec3 rgb{narrow<float>(r),
320 V9938_COLORS[r][
g][b] =
321 screen.mapKeyedRGB<
Pixel>(
322 renderSettings.transformRGB(rgb / 7.0f));
329 for (
auto i :
xrange(256)) {
330 PALETTE256[i] = V9938_COLORS
333 [(i & 0x03) == 3 ? 7 : (i & 0x03) * 2];
336 for (
auto i :
xrange(16)) {
338 palGraphic7Sprites[i] =
339 V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7];
344template<std::
unsigned_
integral Pixel>
345void SDLRasterizer<Pixel>::precalcColorIndex0(DisplayMode mode,
346 bool transparency,
const RawFrame* superimposing,
byte bgColorIndex)
350 transparency =
false;
353 int tpIndex = transparency ? bgColorIndex : 0;
355 Pixel c = (superimposing && (bgColorIndex == 0))
356 ? screen.getKeyColor<
Pixel>()
361 bitmapConverter.palette16Changed();
365 if ((palFg[ 0] != palBg[tpIndex >> 2]) ||
366 (palFg[16] != palBg[tpIndex & 3])) {
367 palFg[ 0] = palBg[tpIndex >> 2];
368 palFg[16] = palBg[tpIndex & 3];
369 bitmapConverter.palette16Changed();
374template<std::
unsigned_
integral Pixel>
375std::pair<Pixel, Pixel> SDLRasterizer<Pixel>::getBorderColors()
377 DisplayMode mode = vdp.getDisplayMode();
378 int bgColor = vdp.getBackgroundColor();
382 return {palBg[(bgColor & 0x0C) >> 2],
383 palBg[(bgColor & 0x03) >> 0]};
387 return PALETTE256[bgColor];
389 if (!bgColor && vdp.isSuperimposing()) {
390 return screen.getKeyColor<
Pixel>();
392 return palBg[bgColor];
399template<std::
unsigned_
integral Pixel>
401 int fromX,
int fromY,
int limitX,
int limitY)
403 auto [border0, border1] = getBorderColors();
405 int startY =
std::max(fromY - lineRenderTop, 0);
406 int endY =
std::min(limitY - lineRenderTop, 240);
408 (border0 == border1)) {
410 for (
auto y :
xrange(startY, endY)) {
411 workFrame->setBlank(y, border0);
417 unsigned lineWidth = vdp.getDisplayMode().getLineWidth();
418 unsigned x = translateX(fromX, (lineWidth == 512));
419 unsigned num = translateX(limitX, (lineWidth == 512)) - x;
420 unsigned width = (lineWidth == 512) ? 640 : 320;
422 for (
auto y :
xrange(startY, endY)) {
423 memset(workFrame->getLineDirect<
Pixel>(y).subspan(x, num),
431 workFrame->setLineWidth(y, width);
437template<std::
unsigned_
integral Pixel>
440 int displayX,
int displayY,
441 int displayWidth,
int displayHeight)
448 if (lineWidth == 256) {
449 int endX = displayX + displayWidth;
451 displayWidth = endX / 2 - displayX;
456 fromY + displayHeight - lineRenderTop,
458 int screenY = fromY - lineRenderTop;
461 fromY = lineRenderTop;
464 displayHeight = screenLimitY - screenY;
465 if (displayHeight <= 0)
return;
468 translateX(vdp.getLeftBackground(), lineWidth == 512);
474 : 8 * (lineWidth / 256) * (vdp.getHorizontalScrollHigh() & 0x1F);
481 int pageBorder = displayX + displayWidth;
482 auto [scrollPage1, scrollPage2] = [&]() -> std::pair<int, int> {
483 if (vdp.isMultiPageScrolling()) {
484 int p1 = vdp.getHorizontalScrollHigh() >> 5;
493 int pageSplit = narrow<int>(lineWidth - hScroll);
494 if (pageSplit < pageBorder) {
495 pageBorder = pageSplit;
499 for (
auto y :
xrange(screenY, screenLimitY)) {
507 unsigned pageMaskOdd = (mode.
isPlanar() ? 0x000 : 0x200) |
508 vdp.getEvenOddMask(y);
509 unsigned pageMaskEven = vdp.isMultiPageScrolling()
510 ? (pageMaskOdd & ~0x100)
512 const std::array<unsigned, 2> vramLine = {
513 (vram.nameTable.getMask() >> 7) & (pageMaskEven | displayY),
514 (vram.nameTable.getMask() >> 7) & (pageMaskOdd | displayY)
517 std::array<Pixel, 512> buf;
518 auto lineInBuf = unsigned(-1);
519 auto dst = workFrame->getLineDirect<
Pixel>(y).
subspan(leftBackground + displayX);
520 int firstPageWidth = pageBorder - displayX;
521 if (firstPageWidth > 0) {
522 if (((displayX + hScroll) == 0) &&
523 (firstPageWidth == narrow<int>(lineWidth))) {
525 renderBitmapLine(dst, vramLine[scrollPage1]);
527 lineInBuf = vramLine[scrollPage1];
528 renderBitmapLine(buf, vramLine[scrollPage1]);
529 auto src =
subspan(buf, displayX + hScroll, firstPageWidth);
535 if (firstPageWidth < displayWidth) {
536 if (lineInBuf != vramLine[scrollPage2]) {
537 renderBitmapLine(buf, vramLine[scrollPage2]);
539 unsigned x = displayX < pageBorder
540 ? 0 : displayX + hScroll - lineWidth;
545 displayY = (displayY + 1) & 255;
549 for (
auto y :
xrange(screenY, screenLimitY)) {
550 assert(!vdp.isMSX1VDP() || displayY < 192);
552 auto dst = workFrame->getLineDirect<
Pixel>(y).
subspan(leftBackground + displayX);
553 if ((displayX == 0) && (displayWidth == narrow<int>(lineWidth))){
554 characterConverter.convertLine(dst, displayY);
556 std::array<Pixel, 512> buf;
557 characterConverter.convertLine(buf, displayY);
558 auto src =
subspan(buf, displayX, displayWidth);
562 displayY = (displayY + 1) & 255;
567template<std::
unsigned_
integral Pixel>
571 int displayWidth,
int displayHeight)
576 fromY + displayHeight - lineRenderTop,
578 int screenY = fromY - lineRenderTop;
580 fromY = lineRenderTop;
583 displayHeight = screenLimitY - screenY;
584 if (displayHeight <= 0)
return;
589 int spriteMode = vdp.getDisplayMode().getSpriteMode(vdp.isMSX1VDP());
590 int displayLimitX = displayX + displayWidth;
591 int limitY = fromY + displayHeight;
592 int screenX = translateX(
593 vdp.getLeftSprites(),
594 vdp.getDisplayMode().getLineWidth() == 512);
595 if (spriteMode == 1) {
596 for (
int y = fromY; y < limitY; y++, screenY++) {
597 auto dst = workFrame->getLineDirect<
Pixel>(screenY).
subspan(screenX);
598 spriteConverter.drawMode1(y, displayX, displayLimitX, dst);
601 byte mode = vdp.getDisplayMode().getByte();
603 for (
int y = fromY; y < limitY; y++, screenY++) {
604 auto dst = workFrame->getLineDirect<
Pixel>(screenY).
subspan(screenX);
605 spriteConverter.template drawMode2<DisplayMode::GRAPHIC5>(
606 y, displayX, displayLimitX, dst);
609 for (
int y = fromY; y < limitY; y++, screenY++) {
610 auto dst = workFrame->getLineDirect<
Pixel>(screenY).
subspan(screenX);
611 spriteConverter.template drawMode2<DisplayMode::GRAPHIC6>(
612 y, displayX, displayLimitX, dst);
615 for (
int y = fromY; y < limitY; y++, screenY++) {
616 auto dst = workFrame->getLineDirect<
Pixel>(screenY).
subspan(screenX);
617 spriteConverter.template drawMode2<DisplayMode::GRAPHIC4>(
618 y, displayX, displayLimitX, dst);
624template<std::
unsigned_
integral Pixel>
627 return postProcessor->isRecording();
630template<std::
unsigned_
integral Pixel>
634 &renderSettings.getBrightnessSetting(),
635 &renderSettings.getContrastSetting(),
636 &renderSettings.getColorMatrixSetting())) {
645template class SDLRasterizer<uint16_t>;
647#if HAVE_32BPP || COMPONENT_GL
648template class SDLRasterizer<uint32_t>;
Represents a VDP display mode.
constexpr bool isPlanar() const
Is VRAM "planar" in the current display mode? Graphic 6 and 7 spread their bytes over two VRAM ICs,...
constexpr unsigned getLineWidth() const
Get number of pixels on a display line in this mode.
constexpr bool isBitmapMode() const
Is the current mode a bitmap mode? Graphic4 and higher are bitmap modes.
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
constexpr byte getByte() const
Get the display mode as a byte: YAE YJK M5..M1 combined.
Represents the output window/screen of openMSX.
@ FIELD_NONINTERLACED
Interlacing is off for this frame.
@ FIELD_EVEN
Interlacing is on and this is an even frame.
@ FIELD_ODD
Interlacing is on and this is an odd frame.
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...
FloatSetting & getContrastSetting()
Contrast video setting.
FloatSetting & getBrightnessSetting()
Brightness video setting.
FloatSetting & getGammaSetting()
The amount of gamma correction.
StringSetting & getColorMatrixSetting()
Color matrix setting.
static constexpr std::array< uint16_t, 16 > GRAPHIC7_SPRITE_PALETTE
Sprite palette in Graphic 7 mode.
Rasterizer using a frame buffer approach: it writes pixels to a single rectangular pixel buffer.
void setHorizontalScrollLow(byte scroll) override
void setDisplayMode(DisplayMode mode) override
Precalc several values that depend on the display mode.
void setTransparency(bool enabled) override
void setHorizontalAdjust(int adjust) override
void setBackgroundColor(byte index) override
Changes the background color.
bool isRecording() const override
Is video recording active?
void drawDisplay(int fromX, int fromY, int displayX, int displayY, int displayWidth, int displayHeight) override
Render a rectangle of display pixels on the host screen.
void reset() override
Resynchronize with VDP: all cached states are flushed.
void setPalette(unsigned index, int grb) override
Change an entry in the palette.
void drawBorder(int fromX, int fromY, int limitX, int limitY) override
Render a rectangle of border pixels on the host screen.
~SDLRasterizer() override
void setSuperimposeVideoFrame(const RawFrame *videoSource) override
void frameEnd() override
Indicates the end of the current frame.
void frameStart(EmuTime::param time) override
Indicates the start of a new frame.
void setBorderMask(bool masked) override
bool isActive() override
Will the output of this Rasterizer be displayed? There is no point in producing a frame that will not...
void drawSprites(int fromX, int fromY, int displayX, int displayY, int displayWidth, int displayHeight) override
Render a rectangle of sprite pixels on the host screen.
PostProcessor * getPostProcessor() const override
See VDP::getPostProcessor().
SDLRasterizer(const SDLRasterizer &)=delete
void attach(Observer< T > &observer)
Unified implementation of MSX Video Display Processors (VDPs).
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
bool isMSX1VDP() const
Is this an MSX1 VDP?
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
auto copy(InputRange &&range, OutputIter out)
constexpr To narrow(From from) noexcept
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
constexpr auto xrange(T e)