PPU: Support for mode 5, hires, interlace, and overscan mode

This commit is contained in:
Sour 2019-02-23 21:39:35 -05:00
parent 19a6663ed9
commit 85f1333c3d
10 changed files with 208 additions and 133 deletions

View file

@ -20,6 +20,16 @@ BaseVideoFilter::~BaseVideoFilter()
}
}
void BaseVideoFilter::SetBaseFrameInfo(FrameInfo frameInfo)
{
_baseFrameInfo = frameInfo;
}
FrameInfo BaseVideoFilter::GetFrameInfo()
{
return _baseFrameInfo;
}
void BaseVideoFilter::UpdateBufferSize()
{
uint32_t newBufferSize = GetFrameInfo().Width*GetFrameInfo().Height;

View file

@ -13,6 +13,7 @@ private:
SimpleLock _frameLock;
OverscanDimensions _overscan;
bool _isOddFrame;
FrameInfo _baseFrameInfo;
void UpdateBufferSize();
@ -33,5 +34,7 @@ public:
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr);
virtual OverscanDimensions GetOverscan();
virtual FrameInfo GetFrameInfo() = 0;
void SetBaseFrameInfo(FrameInfo frameInfo);
virtual FrameInfo GetFrameInfo();
};

View file

@ -34,12 +34,6 @@ void DefaultVideoFilter::InitConversionMatrix(double hueShift, double saturation
}
}
FrameInfo DefaultVideoFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
return { overscan.GetScreenWidth(), overscan.GetScreenHeight(), OverscanDimensions::ScreenWidth, OverscanDimensions::ScreenHeight, 4 };
}
void DefaultVideoFilter::OnBeforeApplyFilter()
{
/*PictureSettings currentSettings = _console->GetSettings()->GetPictureSettings();
@ -99,7 +93,8 @@ void DefaultVideoFilter::DecodePpuBuffer(uint16_t *ppuOutputBuffer, uint32_t* ou
void DefaultVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
{
uint32_t *out = GetOutputBuffer();
for(int i = 0; i < 256 * 224; i++) {
uint32_t pixelCount = GetFrameInfo().Width * GetFrameInfo().Height;
for(int i = 0; i < pixelCount; i++) {
uint16_t rgb555 = ppuOutputBuffer[i];
uint8_t b = (rgb555 >> 10) * 256 / 32;
uint8_t g = ((rgb555 >> 5) & 0x1F) * 256 / 32;

View file

@ -24,5 +24,4 @@ protected:
public:
DefaultVideoFilter(shared_ptr<Console> console);
void ApplyFilter(uint16_t *ppuOutputBuffer);
FrameInfo GetFrameInfo();
};

View file

@ -20,12 +20,6 @@ protected:
virtual void InternalDraw() = 0;
__forceinline void DrawPixel(uint32_t x, uint32_t y, int color)
{
if(x < _overscan.Left || x >= _overscan.Left + _overscan.GetScreenWidth() ||
y < _overscan.Top || y >= _overscan.Top + _overscan.GetScreenHeight()) {
//In overscan (out of bounds), skip drawing
return;
}
uint32_t alpha = (color & 0xFF000000);
if(alpha > 0) {
if(_yScale == 1) {
@ -82,8 +76,8 @@ public:
_argbBuffer = argbBuffer;
_overscan = overscan;
_lineWidth = lineWidth;
_yScale = lineWidth / overscan.GetScreenWidth();
_xScale = (float)lineWidth / overscan.GetScreenWidth();
_yScale = 1; //TODO
_xScale = 2; //TODO
InternalDraw();

View file

@ -14,8 +14,8 @@ Ppu::Ppu(shared_ptr<Console> console)
_console = console;
_regs = console->GetInternalRegisters();
_outputBuffers[0] = new uint16_t[256 * 224];
_outputBuffers[1] = new uint16_t[256 * 224];
_outputBuffers[0] = new uint16_t[512 * 478];
_outputBuffers[1] = new uint16_t[512 * 478];
_currentBuffer = _outputBuffers[0];
@ -64,9 +64,9 @@ void Ppu::Exec()
_rangeOver = false;
_timeOver = false;
if(_scanline < 224) {
if(_scanline < (_overscanMode ? 240 : 224)) {
RenderScanline();
} else if(_scanline == 225) {
} else if(_scanline == (_overscanMode ? 241 : 225)) {
//Reset OAM address at the start of vblank?
if(!_forcedVblank) {
_internalOamAddress = (_oamRamAddress << 1);
@ -238,8 +238,6 @@ void Ppu::RenderSprites()
}
if(forMainScreen) {
uint16_t outBaseAddress = (_scanline << 8);
for(int x = 0; x < 256; x++) {
if(!_rowPixelFlags[x] && _spritePriority[x] == priority) {
if(activeWindowCount && ProcessMaskWindow<Ppu::SpriteLayerIndex>(activeWindowCount, x)) {
@ -247,7 +245,7 @@ void Ppu::RenderSprites()
continue;
}
_currentBuffer[outBaseAddress | x] = _spritePixels[x];
_mainScreenBuffer[x] = _spritePixels[x];
_rowPixelFlags[x] |= PixelFlags::Filled | (((_colorMathEnabled & 0x10) && _spritePalette[x] > 3) ? PixelFlags::AllowColorMath : 0);
}
}
@ -347,6 +345,20 @@ void Ppu::RenderMode4()
RenderBgColor<forMainScreen>();
}
template<bool forMainScreen>
void Ppu::RenderMode5()
{
RenderSprites<3, forMainScreen>();
RenderTilemap<0, 4, true, forMainScreen>();
RenderSprites<2, forMainScreen>();
RenderTilemap<1, 2, true, forMainScreen>();
RenderSprites<1, forMainScreen>();
RenderTilemap<0, 4, false, forMainScreen>();
RenderSprites<0, forMainScreen>();
RenderTilemap<1, 2, false, forMainScreen>();
RenderBgColor<forMainScreen>();
}
template<bool forMainScreen>
void Ppu::RenderMode7()
{
@ -388,32 +400,26 @@ void Ppu::RenderScanline()
break;
case 2:
MessageManager::Log("[Debug] Using mode 2");
RenderMode2<true>();
RenderMode2<false>();
break;
case 3:
MessageManager::Log("[Debug] Using mode 3");
RenderMode3<true>();
RenderMode3<false>();
break;
case 4:
MessageManager::Log("[Debug] Using mode 4");
RenderMode4<true>();
RenderMode4<false>();
break;
case 5:
MessageManager::Log("[Debug] Using mode 5");
RenderTilemap<1, 2, false, true>();
RenderTilemap<0, 4, false, true>();
RenderBgColor<true>();
RenderMode5<true>();
RenderMode5<false>();
break;
case 6:
MessageManager::Log("[Debug] Using mode 6");
RenderTilemap<0, 8, false, true>();
RenderBgColor<true>();
break;
@ -425,7 +431,8 @@ void Ppu::RenderScanline()
}
ApplyColorMath();
ApplyBrightness();
ApplyBrightness<true>();
ApplyHiResMode();
//Process sprites for next scanline
EvaluateNextLineSprites();
@ -439,12 +446,11 @@ void Ppu::RenderBgColor()
}
uint16_t bgColor = _cgram[0] | (_cgram[1] << 8);
uint16_t outBaseAddress = (_scanline << 8);
for(int x = 0; x < 256; x++) {
if(forMainScreen) {
if(!_rowPixelFlags[x]) {
uint8_t pixelFlags = PixelFlags::Filled | ((_colorMathEnabled & 0x20) ? PixelFlags::AllowColorMath : 0);
_currentBuffer[outBaseAddress | x] = bgColor;
_mainScreenBuffer[x] = bgColor;
_rowPixelFlags[x] = pixelFlags;
}
} else {
@ -455,8 +461,8 @@ void Ppu::RenderBgColor()
}
}
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, uint16_t basePaletteOffset>
void Ppu::RenderTilemap()
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTileWidth, bool largeTileHeight, uint16_t basePaletteOffset>
void Ppu::RenderTilemapLargeTiles()
{
uint8_t activeWindowCount = 0;
if((forMainScreen && _windowMaskMain[layerIndex]) || (!forMainScreen && _windowMaskSub[layerIndex])) {
@ -465,50 +471,49 @@ void Ppu::RenderTilemap()
bool applyMosaic = forMainScreen && ((_mosaicEnabled >> layerIndex) & 0x01) != 0;
if(_layerConfig[layerIndex].LargeTiles) {
if(activeWindowCount == 0) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 0, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 0, false>();
}
} else if(activeWindowCount == 1) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 1, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 1, false>();
}
if(activeWindowCount == 0) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 0, true>();
} else {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 2, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, true, basePaletteOffset, 2, false>();
}
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 0, false>();
}
} else if(activeWindowCount == 1) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 1, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 1, false>();
}
} else {
if(activeWindowCount == 0) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 0, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 0, false>();
}
} else if(activeWindowCount == 1) {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 1, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 1, false>();
}
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 2, true>();
} else {
if(applyMosaic) {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 2, true>();
} else {
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, false, basePaletteOffset, 2, false>();
}
RenderTilemap<layerIndex, bpp, processHighPriority, forMainScreen, largeTileWidth, largeTileHeight, basePaletteOffset, 2, false>();
}
}
}
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTiles, uint16_t basePaletteOffset, uint8_t activeWindowCount, bool applyMosaic>
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, uint16_t basePaletteOffset>
void Ppu::RenderTilemap()
{
bool largeTileWidth = _layerConfig[layerIndex].LargeTiles || _bgMode == 5;
bool largeTileHeight = _layerConfig[layerIndex].LargeTiles;
if(largeTileWidth) {
if(largeTileHeight) {
RenderTilemapLargeTiles<layerIndex, bpp, processHighPriority, forMainScreen, true, true, basePaletteOffset>();
} else {
RenderTilemapLargeTiles<layerIndex, bpp, processHighPriority, forMainScreen, true, false, basePaletteOffset>();
}
} else {
if(largeTileHeight) {
RenderTilemapLargeTiles<layerIndex, bpp, processHighPriority, forMainScreen, false, true, basePaletteOffset>();
} else {
RenderTilemapLargeTiles<layerIndex, bpp, processHighPriority, forMainScreen, false, false, basePaletteOffset>();
}
}
}
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTileWidth, bool largeTileHeight, uint16_t basePaletteOffset, uint8_t activeWindowCount, bool applyMosaic>
void Ppu::RenderTilemap()
{
if(forMainScreen) {
@ -523,23 +528,49 @@ void Ppu::RenderTilemap()
}
}
bool mosaicScanline = applyMosaic && (_scanline - _mosaicStartScanline) % _mosaicSize != 0;
/* Current scanline (in interlaced mode, switches between even and odd rows every frame */
uint16_t realY = _screenInterlace ? ((_frameCount & 0x01) ? ((_scanline << 1) + 1) : (_scanline << 1)) : _scanline;
/* True when the entire scanline has to be replaced by a mosaic pattern */
bool mosaicScanline = applyMosaic && (realY - _mosaicStartScanline) % _mosaicSize != 0;
/* Keeps track of whether or not the pixel is allowed to participate in color math */
uint8_t pixelFlags = PixelFlags::Filled | (((_colorMathEnabled >> layerIndex) & 0x01) ? PixelFlags::AllowColorMath : 0);
/* The current layer's options */
LayerConfig &config = _layerConfig[layerIndex];
uint16_t tilemapAddr = config.TilemapAddress >> 1;
uint16_t chrAddr = config.ChrAddress;
uint16_t row = (_scanline + config.VScroll) >> (largeTiles ? 4 : 3);
uint8_t baseYOffset = (_scanline + config.VScroll) & 0x07;
/* Layer's tilemap start address */
uint16_t tilemapAddr = config.TilemapAddress >> 1;
/* Layer's CHR data start address */
uint16_t chrAddr = config.ChrAddress;
/* The current row of tiles (e.g scanlines 16-23 is row 2) */
uint16_t row = (realY + config.VScroll) >> (largeTileHeight ? 4 : 3);
/* The vertical offset to read in the tile we're processing */
uint8_t baseYOffset = (realY + config.VScroll) & 0x07;
/* Tilemap offset based on the current row & tilemap size options */
uint16_t addrVerticalScrollingOffset = config.VerticalMirroring ? ((row & 0x20) << (config.HorizontalMirroring ? 6 : 5)) : 0;
/* The start address for tiles on this row */
uint16_t baseOffset = tilemapAddr + addrVerticalScrollingOffset + ((row & 0x1F) << 5);
uint16_t outBaseAddress = (_scanline << 8);
for(int x = 0; x < 256; x++) {
uint16_t column = (x + config.HScroll) >> (largeTiles ? 4 : 3);
/* The current pixel x position (normally 0-255, but 0-511 in hi-res mode - even on subscreen, odd on main screen) */
uint16_t realX;
if(largeTileWidth && !largeTileHeight) {
realX = (x << 1) + (forMainScreen ? 1 : 0);
} else {
realX = x;
}
/* The current column index (in terms of 8x8 or 16x16 tiles) */
uint16_t column = (realX + config.HScroll) >> (largeTileWidth ? 4 : 3);
/* The tilemap address to read the tile data from */
uint32_t addr = (baseOffset + (column & 0x1F) + (config.HorizontalMirroring ? ((column & 0x20) << 5) : 0)) << 1;
//Skip pixels that were filled by previous layers (or that don't match the priority level currently being processed)
@ -560,7 +591,7 @@ void Ppu::RenderTilemap()
if(applyMosaic && (mosaicScanline || x % _mosaicSize != 0)) {
//If this is not the top-left pixels in the mosaic pattern, override it with the top-left pixel data
_currentBuffer[outBaseAddress | x] = _mosaicColor[x];
_mainScreenBuffer[x] = _mosaicColor[x];
_rowPixelFlags[x] = pixelFlags;
if(forMainScreen) {
_pixelsDrawn++;
@ -575,11 +606,11 @@ void Ppu::RenderTilemap()
bool hMirror = (_vram[addr + 1] & 0x40) != 0;
uint16_t tileIndex = ((_vram[addr + 1] & 0x03) << 8) | _vram[addr];
if(largeTiles) {
if(largeTileWidth || largeTileHeight) {
tileIndex = (
tileIndex +
(((_scanline + config.VScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) +
(((x + config.HScroll) & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0))
(largeTileHeight ? (((realY + config.VScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) +
(largeTileWidth ? (((realX + config.HScroll) & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0)
) & 0x3FF;
}
@ -588,8 +619,9 @@ void Ppu::RenderTilemap()
uint8_t yOffset = vMirror ? (7 - baseYOffset) : baseYOffset;
uint16_t pixelStart = tileStart + yOffset * 2;
uint8_t xOffset = (x + config.HScroll) & 0x07;
uint8_t xOffset = (realX + config.HScroll) & 0x07;
uint8_t shift = hMirror ? xOffset : (7 - xOffset);
uint16_t color = 0;
if(bpp == 2) {
color |= (((_vram[pixelStart + 0] >> shift) & 0x01) << 0);
@ -628,8 +660,6 @@ void Ppu::RenderTilemap()
template<uint8_t layerIndex, bool forMainScreen, bool applyMosaic, bool processHighPriority>
void Ppu::RenderTilemapMode7()
{
uint16_t realY = _mode7.VerticalMirroring ? (255 - _scanline) : _scanline;
if(forMainScreen) {
if(_pixelsDrawn == 256 || ((_mainScreenLayers >> layerIndex) & 0x01) == 0) {
//This screen is disabled, or we've drawn all pixels already
@ -651,6 +681,7 @@ void Ppu::RenderTilemapMode7()
int32_t vScroll = ((int32_t)_mode7.VScroll << 19) >> 19;
int32_t centerX = ((int32_t)_mode7.CenterX << 19) >> 19;
int32_t centerY = ((int32_t)_mode7.CenterY << 19) >> 19;
uint16_t realY = _mode7.VerticalMirroring ? (255 - _scanline) : _scanline;
lutX[0] = (
((_mode7.Matrix[0] * clip(hScroll - centerX)) & ~63) +
@ -731,7 +762,7 @@ void Ppu::RenderTilemapMode7()
template<bool applyMosaic>
void Ppu::DrawMainPixel(uint8_t x, uint16_t paletteRamOffset, uint8_t flags)
{
_currentBuffer[(_scanline << 8) | x] = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
_mainScreenBuffer[x] = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8);
_rowPixelFlags[x] = flags;
if(applyMosaic && x % _mosaicSize == 0) {
@ -764,7 +795,7 @@ void Ppu::ApplyColorMath()
for(int x = 0; x < 256; x++) {
if(_rowPixelFlags[x] & PixelFlags::AllowColorMath) {
uint8_t halfShift = _colorMathHalveResult ? 1 : 0;
uint16_t &mainPixel = _currentBuffer[outBaseAddress | x];
uint16_t &mainPixel = _mainScreenBuffer[x];
bool isInsideWindow = activeWindowCount && ProcessMaskWindow<Ppu::ColorWindowIndex>(activeWindowCount, x);
//Set color to black as needed based on clip mode
@ -839,13 +870,12 @@ void Ppu::ApplyColorMath()
}
}
template<bool forMainScreen>
void Ppu::ApplyBrightness()
{
if(_screenBrightness != 15) {
uint16_t outBaseAddress = (_scanline << 8);
for(int x = 0; x < 256; x++) {
uint16_t &pixel = _currentBuffer[outBaseAddress | x];
uint16_t &pixel = (forMainScreen ? _mainScreenBuffer : _subScreenBuffer)[x];
uint16_t r = (pixel & 0x1F) * _screenBrightness / 15;
uint16_t g = ((pixel >> 5) & 0x1F) * _screenBrightness / 15;
uint16_t b = ((pixel >> 10) & 0x1F) * _screenBrightness / 15;
@ -854,6 +884,21 @@ void Ppu::ApplyBrightness()
}
}
void Ppu::ApplyHiResMode()
{
uint32_t screenY = _screenInterlace ? ((_frameCount & 0x01) ? ((_scanline << 1) + 1) : (_scanline << 1)) : _scanline;
if(_hiresMode || _bgMode == 5 || _bgMode == 6) {
ApplyBrightness<false>();
for(int i = 0; i < 512; i += 2) {
_currentBuffer[(screenY << 9) + i] = _subScreenBuffer[i >> 1];
_currentBuffer[(screenY << 9) + i + 1] = _mainScreenBuffer[i >> 1];
}
} else {
memcpy(_currentBuffer + (screenY << 8), _mainScreenBuffer, sizeof(_mainScreenBuffer));
}
}
template<uint8_t layerIndex>
bool Ppu::ProcessMaskWindow(uint8_t activeWindowCount, int x)
{
@ -890,8 +935,27 @@ void Ppu::ProcessWindowMaskSettings(uint8_t value, uint8_t offset)
void Ppu::SendFrame()
{
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone);
_console->GetVideoDecoder()->UpdateFrame(_currentBuffer, _frameCount);
_currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0];
uint16_t width;
uint16_t height;
if(_hiresMode || _bgMode == 5 || _bgMode == 6) {
width = 512;
} else {
width = 256;
}
if(_screenInterlace && (_bgMode == 5 || _bgMode == 6)) {
height = _overscanMode ? 478 : 448;
} else {
height = _overscanMode ? 239 : 224;
}
if(_screenInterlace) {
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, width, height, _frameCount);
} else {
_console->GetVideoDecoder()->UpdateFrame(_currentBuffer, width, height, _frameCount);
_currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0];
}
}
uint8_t* Ppu::GetVideoRam()
@ -1080,6 +1144,9 @@ void Ppu::Write(uint32_t addr, uint8_t value)
break;
case 0x2105:
if(_bgMode != (value & 0x07)) {
MessageManager::DisplayMessage("Debug", "Entering mode: " + std::to_string(value & 0x07));
}
_bgMode = value & 0x07;
_mode1Bg3Priority = (value & 0x08) != 0;
@ -1323,10 +1390,10 @@ void Ppu::Write(uint32_t addr, uint8_t value)
//SETINI - Screen Mode/Video Select
//_externalSync = (value & 0x80) != 0; //NOT USED
_mode7.ExtBgEnabled = (value & 0x40) != 0;
//_hiresMode = (value & 0x08) != 0; //TODO
//_overscanMode = (value & 0x04) != 0; //TODO
_hiresMode = (value & 0x08) != 0;
_overscanMode = (value & 0x04) != 0;
//_objInterlace = (value & 0x02) != 0; //TODO
//_screenInterlace = (value & 0x01) != 0; //TODO
_screenInterlace = (value & 0x01) != 0;
break;
default:

View file

@ -76,6 +76,7 @@ private:
uint8_t _cgram[Ppu::CgRamSize];
uint16_t *_outputBuffers[2];
uint16_t *_currentBuffer;
SpriteInfo _sprites[32] = {};
uint8_t _spriteCount = 0;
@ -87,7 +88,7 @@ private:
uint16_t _subPixelsDrawn = 0;
uint8_t _rowPixelFlags[256];
uint16_t *_currentBuffer;
uint16_t _mainScreenBuffer[256];
bool _subScreenFilled[256];
uint16_t _subScreenBuffer[256];
@ -111,6 +112,9 @@ private:
bool _timeOver = false;
bool _rangeOver = false;
bool _hiresMode = false;
bool _screenInterlace = false;
bool _overscanMode = false;
bool _directColorMode = false;
ColorWindowMode _colorMathClipMode = ColorWindowMode::Never;
@ -150,6 +154,9 @@ private:
template<bool forMainScreen>
void RenderMode4();
template<bool forMainScreen>
void RenderMode5();
template<bool forMainScreen>
void RenderMode7();
@ -158,10 +165,13 @@ private:
template<bool forMainScreen>
void RenderBgColor();
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTileWidth, bool largeTileHeight, uint16_t basePaletteOffset>
void RenderTilemapLargeTiles();
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, uint16_t basePaletteOffset = 0>
void RenderTilemap();
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTiles, uint16_t basePaletteOffset, uint8_t activeWindowCount, bool applyMosaic>
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, bool largeTileWidth, bool largeTileHeight, uint16_t basePaletteOffset, uint8_t activeWindowCount, bool applyMosaic>
void RenderTilemap();
template<uint8_t layerIndex, bool forMainScreen, bool applyMosaic, bool processHighPriority>
@ -173,8 +183,12 @@ private:
__forceinline void DrawSubPixel(uint8_t x, uint16_t paletteRamOffset);
void ApplyColorMath();
template<bool forMainScreen>
void ApplyBrightness();
void ApplyHiResMode();
template<uint8_t layerIndex>
bool ProcessMaskWindow(uint8_t activeWindowCount, int x);
void ProcessWindowMaskSettings(uint8_t value, uint8_t offset);

View file

@ -62,28 +62,10 @@ enum class VideoAspectRatio
struct OverscanDimensions
{
static const int ScreenWidth = 256;
static const int ScreenHeight = 224;
uint32_t Left = 0;
uint32_t Right = 0;
uint32_t Top = 0;
uint32_t Bottom = 0;
uint32_t GetPixelCount()
{
return GetScreenWidth() * GetScreenHeight();
}
uint32_t GetScreenWidth()
{
return 256 - Left - Right;
}
uint32_t GetScreenHeight()
{
return 224 - Top - Bottom;
}
};
struct PictureSettings
@ -116,9 +98,6 @@ struct FrameInfo
{
uint32_t Width;
uint32_t Height;
uint32_t OriginalWidth;
uint32_t OriginalHeight;
uint32_t BitsPerPixel;
};
struct ScreenSize

View file

@ -43,15 +43,23 @@ void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
}
size.Scale = scale;*/
if(ignoreScale) {
size.Width = 256;
size.Height = 224;
} else {
size.Width = 512;
size.Height = 448;
}
size.Scale = 2;
if(ignoreScale) {
size.Width = _baseFrameInfo.Width;
size.Height = _baseFrameInfo.Height;
} else {
if(_baseFrameInfo.Width == 256) {
size.Width = (int32_t)(_baseFrameInfo.Width * size.Scale);
size.Height = (int32_t)(_baseFrameInfo.Height * size.Scale);
} else {
size.Width = (int32_t)_baseFrameInfo.Width;
size.Height = (int32_t)_baseFrameInfo.Height;
if(_baseFrameInfo.Height <= 240) {
size.Height *= 2;
}
}
}
}
}
@ -93,6 +101,7 @@ void VideoDecoder::DecodeFrame(bool synchronous)
{
UpdateVideoFilter();
_videoFilter->SetBaseFrameInfo(_baseFrameInfo);
_videoFilter->SendFrame(_ppuOutputBuffer, _frameNumber);
uint32_t* outputBuffer = _videoFilter->GetOutputBuffer();
@ -150,15 +159,17 @@ uint32_t VideoDecoder::GetFrameCount()
return _frameCount;
}
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber)
{
_baseFrameInfo.Width = width;
_baseFrameInfo.Height = height;
_frameNumber = frameNumber;
_ppuOutputBuffer = ppuOutputBuffer;
DecodeFrame(true);
_frameCount++;
}
void VideoDecoder::UpdateFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
void VideoDecoder::UpdateFrame(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber)
{
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
@ -168,6 +179,8 @@ void VideoDecoder::UpdateFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
//At this point, we are sure that the decode thread is no longer busy
}
_baseFrameInfo.Width = width;
_baseFrameInfo.Height = height;
_frameNumber = frameNumber;
_ppuOutputBuffer = ppuOutputBuffer;
_frameChanged = true;

View file

@ -28,6 +28,7 @@ private:
atomic<bool> _stopFlag;
uint32_t _frameCount = 0;
FrameInfo _baseFrameInfo = { 256, 224 };
ScreenSize _previousScreenSize = {};
double _previousScale = 0;
FrameInfo _lastFrameInfo;
@ -54,8 +55,8 @@ public:
FrameInfo GetFrameInfo();
void GetScreenSize(ScreenSize &size, bool ignoreScale);
void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
void UpdateFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber);
void UpdateFrame(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber);
bool IsRunning();
void StartThread();