From 1195c46ac00eb7f0da057e8c4f6cd1d4b2651c6e Mon Sep 17 00:00:00 2001 From: byuu <2107894+byuu@users.noreply.github.com> Date: Tue, 3 Sep 2019 18:50:46 +0900 Subject: [PATCH] v108.17 Enhanced perspective correction support [DerKoun] --- bsnes/emulator/emulator.hpp | 2 +- bsnes/sfc/ppu-fast/line.cpp | 1 + bsnes/sfc/ppu-fast/mode7.cpp | 2 - bsnes/sfc/ppu-fast/mode7hd.cpp | 121 +++++++++++++++++++++++++++++++-- bsnes/sfc/ppu-fast/ppu.hpp | 15 +++- 5 files changed, 129 insertions(+), 12 deletions(-) diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index d38282c1..218ac639 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "108.16"; + static const string Version = "108.17"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/sfc/ppu-fast/line.cpp b/bsnes/sfc/ppu-fast/line.cpp index 7fa41de0..24dc0175 100644 --- a/bsnes/sfc/ppu-fast/line.cpp +++ b/bsnes/sfc/ppu-fast/line.cpp @@ -3,6 +3,7 @@ uint PPU::Line::count = 0; auto PPU::Line::flush() -> void { if(Line::count) { + if(ppu.hdScale() > 1) cacheMode7HD(); #pragma omp parallel for if(Line::count >= 8) for(uint y = 0; y < Line::count; y++) { if(ppu.deinterlace()) { diff --git a/bsnes/sfc/ppu-fast/mode7.cpp b/bsnes/sfc/ppu-fast/mode7.cpp index 45f53cde..3ccf9e09 100644 --- a/bsnes/sfc/ppu-fast/mode7.cpp +++ b/bsnes/sfc/ppu-fast/mode7.cpp @@ -16,8 +16,6 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { int hoffset = (int13)io.mode7.hoffset; int voffset = (int13)io.mode7.voffset; -//if(source==Source::BG1&&y==1) print(a, " ", b, " ", c, " ", d, " ", hcenter, " ", vcenter, " ", hoffset, " ", voffset, "\n"); - uint mosaicCounter = 1; uint mosaicPalette = 0; uint mosaicPriority = 0; diff --git a/bsnes/sfc/ppu-fast/mode7hd.cpp b/bsnes/sfc/ppu-fast/mode7hd.cpp index 0a721720..947d2146 100644 --- a/bsnes/sfc/ppu-fast/mode7hd.cpp +++ b/bsnes/sfc/ppu-fast/mode7hd.cpp @@ -1,3 +1,100 @@ +//determine mode 7 line groups for perspective correction +auto PPU::Line::cacheMode7HD() -> void { + ppu.mode7LineGroups.count = 0; + if(ppu.hdPerspective()) { + #define isLineMode7(line) (line.io.bg1.tileMode == TileMode::Mode7 && !line.io.displayDisable && ( \ + (line.io.bg1.aboveEnable || line.io.bg1.belowEnable) \ + )) + bool state = false; + uint y; + //find the moe 7 groups + for(y = 0; y < Line::count; y++) { + if(state != isLineMode7(ppu.lines[Line::start + y])) { + state = !state; + if(state) { + ppu.mode7LineGroups.startLine[ppu.mode7LineGroups.count] = ppu.lines[Line::start + y].y; + } else { + ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] = ppu.lines[Line::start + y].y - 1; + //the lines at the edges of mode 7 groups may be erroneous, so start and end lines for interpolation are moved inside + int offset = (ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] - ppu.mode7LineGroups.startLine[ppu.mode7LineGroups.count]) / 8; + ppu.mode7LineGroups.startLerpLine[ppu.mode7LineGroups.count] = ppu.mode7LineGroups.startLine[ppu.mode7LineGroups.count] + offset; + ppu.mode7LineGroups.endLerpLine[ppu.mode7LineGroups.count] = ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] - offset; + ppu.mode7LineGroups.count++; + } + } + } + #undef isLineMode7 + if(state) { + //close the last group if necessary + ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] = ppu.lines[Line::start + y].y - 1; + int offset = (ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] - ppu.mode7LineGroups.startLine[ppu.mode7LineGroups.count]) / 8; + ppu.mode7LineGroups.startLerpLine[ppu.mode7LineGroups.count] = ppu.mode7LineGroups.startLine[ppu.mode7LineGroups.count] + offset; + ppu.mode7LineGroups.endLerpLine[ppu.mode7LineGroups.count] = ppu.mode7LineGroups.endLine[ppu.mode7LineGroups.count] - offset; + ppu.mode7LineGroups.count++; + } + + //detect groups that do not have perspective + for(int i : range(ppu.mode7LineGroups.count)) { + int a = -1, b = -1, c = -1, d = -1; //the mode 7 scale factors of the current line + int aPrev = -1, bPrev = -1, cPrev = -1, dPrev = -1; //the mode 7 scale factors of the previous line + bool aVar = false, bVar = false, cVar = false, dVar = false; //has a varying value been found for the factors? + bool aInc = false, bInc = false, cInc = false, dInc = false; //has the variation been an increase or decrease? + for(y = ppu.mode7LineGroups.startLerpLine[i]; y <= ppu.mode7LineGroups.endLerpLine[i]; y++) { + a = ((int)((int16)(ppu.lines[y].io.mode7.a))); + b = ((int)((int16)(ppu.lines[y].io.mode7.b))); + c = ((int)((int16)(ppu.lines[y].io.mode7.c))); + d = ((int)((int16)(ppu.lines[y].io.mode7.d))); + //has the value of 'a' changed compared to the last line? + //(and is the factor larger than zero, which happens sometimes and seems to be game-specific, mostly at the edges of the screen) + if(aPrev > 0 && a > 0 && a != aPrev) { + if(!aVar) { + //if there has been no variation yet, store that there is one and store if it is an increase or decrease + aVar = true; + aInc = a > aPrev; + } else if(aInc != a > aPrev) { + //if there has been an increase and now we have a decrease, or vice versa, set the interpolation lines to -1 + //to deactivate perspective correction for this group and stop analyzing it further + ppu.mode7LineGroups.startLerpLine[i] = -1; + ppu.mode7LineGroups.endLerpLine[i] = -1; + break; + } + } + if(bPrev > 0 && b > 0 && b != bPrev) { + if(!bVar) { + bVar = true; + bInc = b > bPrev; + } else if(bInc != b > bPrev) { + ppu.mode7LineGroups.startLerpLine[i] = -1; + ppu.mode7LineGroups.endLerpLine[i] = -1; + break; + } + } + if(cPrev > 0 && c > 0 && c != cPrev) { + if(!cVar) { + cVar = true; + cInc = c > cPrev; + } else if(cInc != c > cPrev) { + ppu.mode7LineGroups.startLerpLine[i] = -1; + ppu.mode7LineGroups.endLerpLine[i] = -1; + break; + } + } + if(dPrev > 0 && d > 0 && d != bPrev) { + if(!dVar) { + dVar = true; + dInc = d > dPrev; + } else if(dInc != d > dPrev) { + ppu.mode7LineGroups.startLerpLine[i] = -1; + ppu.mode7LineGroups.endLerpLine[i] = -1; + break; + } + } + aPrev = a, bPrev = b, cPrev = c, dPrev = d; + } + } + } +} + auto PPU::Line::renderMode7HD(PPU::IO::Background& self, uint source) -> void { const bool extbg = source == Source::BG2; const uint scale = ppu.hdScale(); @@ -7,15 +104,25 @@ auto PPU::Line::renderMode7HD(PPU::IO::Background& self, uint source) -> void { Pixel* below = &this->below[-1]; //find the first and last scanline for interpolation - int y_a = y; - int y_b = y; - #define isLineMode7(n) (ppu.lines[n].io.bg1.tileMode == TileMode::Mode7 && ( \ + int y_a = -1; + int y_b = -1; + #define isLineMode7(n) (ppu.lines[n].io.bg1.tileMode == TileMode::Mode7 && !ppu.lines[n].io.displayDisable && ( \ (ppu.lines[n].io.bg1.aboveEnable || ppu.lines[n].io.bg1.belowEnable) \ )) - if(ppu.hdPerspective()) { - while(y_a > 1 && isLineMode7(y_a)) y_a--; y_a += 1; - while(y_b < 239 && isLineMode7(y_b)) y_b++; y_b -= 8; - } else { + if(ppu.hdPerspective()) { + //find the mode 7 line group this line is in and use its interpolation lines + for(int i : range(ppu.mode7LineGroups.count)) { + if(y >= ppu.mode7LineGroups.startLine[i] && y <= ppu.mode7LineGroups.endLine[i]) { + y_a = ppu.mode7LineGroups.startLerpLine[i]; + y_b = ppu.mode7LineGroups.endLerpLine[i]; + break; + } + } + } + if(y_a == -1 || y_b == -1) { + //if perspective correction is disabled or the group was detected as non-perspective, use the neighboring lines + y_a = y; + y_b = y; if(y_a > 1 && isLineMode7(y_a)) y_a--; if(y_b < 239 && isLineMode7(y_b)) y_b++; } diff --git a/bsnes/sfc/ppu-fast/ppu.hpp b/bsnes/sfc/ppu-fast/ppu.hpp index 65ffa6dd..c12382b3 100644 --- a/bsnes/sfc/ppu-fast/ppu.hpp +++ b/bsnes/sfc/ppu-fast/ppu.hpp @@ -265,7 +265,7 @@ public: auto readObject(uint10 address) -> uint8; auto writeObject(uint10 address, uint8 data) -> void; - //[serialized] +//serialized: Latch latch; IO io; @@ -303,6 +303,7 @@ public: auto renderMode7(PPU::IO::Background&, uint source) -> void; //mode7hd.cpp + static auto cacheMode7HD() -> void; auto renderMode7HD(PPU::IO::Background&, uint source) -> void; alwaysinline auto lerp(float pa, float va, float pb, float vb, float pr) -> float; @@ -313,7 +314,7 @@ public: auto renderWindow(PPU::IO::WindowLayer&, bool enable, bool output[256]) -> void; auto renderWindow(PPU::IO::WindowColor&, uint mask, bool output[256]) -> void; - //[unserialized] + //unserialized: uint y; //constant bool fieldID; @@ -333,6 +334,8 @@ public: static uint start; static uint count; }; + +//unserialized: Line lines[240]; //used to help detect when the video output size changes between frames to clear overscan area. @@ -341,6 +344,14 @@ public: uint width = 0; uint height = 0; } frame; + + struct Mode7LineGroups { + int count = -1; + int startLine[32]; + int endLine[32]; + int startLerpLine[32]; + int endLerpLine[32]; + } mode7LineGroups; }; extern PPU ppufast;