Fix flickering and other graphics

This commit is contained in:
izzy2lost
2026-01-17 22:44:38 -05:00
parent a36cd5980d
commit 516e61a960
4 changed files with 117 additions and 52 deletions
+32 -21
View File
@@ -1772,7 +1772,10 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
}
else if ((addr>=0xF1180000) && (addr<0xF1180100))
{
TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data));
UINT32 flipped = FLIPENDIAN32(data);
TileGen.WriteRegister(addr&0xFF, flipped);
if (addr == 0xF118000C)
GPU.TilegenDrawFrame(flipped);
break;
}
@@ -2048,19 +2051,9 @@ void CModel3::RunMainBoardFrame(void)
// Compute display and VBlank timings
unsigned ppcCycles = m_config["PowerPCFrequency"].ValueAs<unsigned>() * 1000000;
unsigned frameCycles = (unsigned)((float)ppcCycles / 57.524160f);
unsigned gapCycles = (unsigned)((float)frameCycles * 2.5f / 100.0f); // we need a gap between asserting irq2 & irq 0x40
unsigned offsetCycles = (unsigned)((float)frameCycles * 33.f / 100.0f);
unsigned dispCycles = frameCycles - gapCycles - offsetCycles;
unsigned statusCycles = (unsigned)((float)frameCycles * (0.005f));
// we think a frame looks like this on the model 2
// 66% of frame
// [irq2------------------ping_pong_flips------]
//
// Games will start writing a new frame at the ping_pong time. It could be the buffer swaps here.
// Need more h/w testing to confirm.
// What we are doing here is asserting IRQ2 at 33% of the frame, and treating the ping_pong flip as the front/back buffer swap
// This way the data for the correct frames, ends up in the right frames!
unsigned lineCycles = frameCycles / 424;
unsigned vBlankCycles = lineCycles * 40;
unsigned dispCycles = lineCycles * 384;
// Scale PPC timer ratio according to speed at which the PowerPC is being emulated so that the observed running frequency of the PPC timer
// registers is more or less correct. This is needed to get the Virtua Striker 2 series of games running at the right speed (they are
@@ -2072,11 +2065,8 @@ void CModel3::RunMainBoardFrame(void)
if (gpusReady)
{
TileGen.BeginVBlank();
GPU.BeginVBlank(statusCycles); // Games poll the ping_pong at startup. Values aren't 100% accurate so we stretch the frame a bit to ensure writes happen in the correct frame
ppc_execute(offsetCycles);
IRQ.Assert(0x02); // start at 33% of the frame
ppc_execute(gapCycles); // need a gap between asserting irqs
GPU.BeginVBlank();
ppc_execute(vBlankCycles);
/*
* Sound:
@@ -2100,7 +2090,10 @@ void CModel3::RunMainBoardFrame(void)
// Process MIDI interrupt
IRQ.Assert(0x40);
ppc_execute(500); // give PowerPC time to acknowledge IR
dispCycles -= 500;
if (dispCycles >= 500)
dispCycles -= 500;
else
dispCycles = 0;
++irqCount;
if (irqCount > 128)
@@ -2117,7 +2110,25 @@ void CModel3::RunMainBoardFrame(void)
}
// Run the PowerPC for the active display part of the frame
ppc_execute(dispCycles);
unsigned pingPongFlipLine = TileGen.ReadRegister(0x08);
for (unsigned i = 0; i < 384; i++)
{
if (i == pingPongFlipLine)
GPU.FlipPingPongBit();
if (i == 383)
IRQ.Assert(0x02);
unsigned cycles = lineCycles;
if (dispCycles < cycles)
cycles = dispCycles;
if (cycles > 0)
ppc_execute(cycles);
if (dispCycles >= cycles)
dispCycles -= cycles;
}
timings.ppcTicks = CThread::GetTicks() - start;
}
+63 -27
View File
@@ -140,6 +140,8 @@ void CReal3D::LoadState(CBlockFile *SaveState)
UpdateRenderConfig(Render3D, m_internalRenderConfig);
SaveState->Read(&commandPortWritten);
SaveState->Read(&m_pingPong, sizeof(m_pingPong));
m_pingPongCopy = m_pingPong;
m_tilegenDrawFrame = false;
for (int i = 0; i < 39; i++)
{
uint8_t nul;
@@ -162,13 +164,15 @@ static void UpdateRenderConfig(IRender3D *Render3D, uint64_t internalRenderConfi
Render3D->SetSignedShade(shadeIsSigned);
}
void CReal3D::BeginVBlank(int statusCycles)
void CReal3D::BeginVBlank(void)
{
// Calculate point at which status bit should change value. Currently the same timing is used for both the status bit in ReadRegister
// and in WriteDMARegister32/ReadDMARegister32, however it may be that they are completely unrelated. It appears that step 1.x games
// access just the former while step 2.x access the latter. It is not known yet what this bit/these bits actually represent.
statusChange = ppc_total_cycles() + statusCycles;
m_evenFrame = !m_evenFrame;
m_pingPongCopy = m_pingPong;
if (commandPortWritten)
FlushTextures();
if (m_tilegenDrawFrame && commandPortWritten)
DrawFrame();
}
void CReal3D::EndVBlank(void)
@@ -176,12 +180,15 @@ void CReal3D::EndVBlank(void)
error = false; // clear error (just needs to be done once per frame)
}
void CReal3D::FlipPingPongBit(void)
{
m_pingPong = !m_pingPong;
m_tilegenDrawFrame = false;
commandPortWritten = false;
}
uint32_t CReal3D::SyncSnapshots(void)
{
// Update read-only copy of command port flag
commandPortWrittenRO = commandPortWritten;
commandPortWritten = false;
if (!m_gpuMultiThreaded)
return 0;
@@ -268,8 +275,7 @@ void CReal3D::BeginFrame(void)
void CReal3D::RenderFrame(void)
{
//if (commandPortWrittenRO)
Render3D->RenderFrame();
Render3D->RenderFrame();
}
void CReal3D::EndFrame(void)
@@ -641,11 +647,8 @@ void CReal3D::WriteDMARegister32(unsigned reg, uint32_t data)
Basic Emulation Functions, Registers, Memory, and Texture FIFO
******************************************************************************/
void CReal3D::Flush(void)
void CReal3D::FlushTextures()
{
commandPortWritten = true;
DebugLog("Real3D 88000000 written @ PC=%08X\n", ppc_get_pc());
// Upload textures (if any)
if (fifoIdx > 0)
{
@@ -673,6 +676,42 @@ void CReal3D::Flush(void)
fifoIdx = 0;
}
bool CReal3D::PollPingPong()
{
return m_pingPong != m_pingPongCopy;
}
void CReal3D::DrawFrame()
{
m_tilegenDrawFrame = false;
commandPortWritten = false;
}
void CReal3D::TilegenDrawFrame(uint32_t flags)
{
(void)flags;
m_tilegenDrawFrame = true;
if (!PollPingPong())
{
if (commandPortWritten)
DrawFrame();
}
}
void CReal3D::Flush(void)
{
commandPortWritten = true;
DebugLog("Real3D 88000000 written @ PC=%08X\n", ppc_get_pc());
if (!PollPingPong())
{
FlushTextures();
if (m_tilegenDrawFrame)
DrawFrame();
}
}
void CReal3D::WriteTextureFIFO(uint32_t data)
{
if (fifoIdx >= (0x100000/4))
@@ -789,16 +828,8 @@ uint32_t CReal3D::ReadRegister(unsigned reg)
DebugLog("Real3D: Read reg %X\n", reg);
if (reg == 0)
{
uint32_t ping_pong;
if (m_evenFrame) {
ping_pong = (ppc_total_cycles() >= statusChange ? 0x0 : 0x02000000);
}
else {
ping_pong = (ppc_total_cycles() >= statusChange ? 0x02000000 : 0x0);
}
return 0xfdffffff | ping_pong;
uint32_t ping_pong = (m_pingPong ? 0x02000000 : 0x0);
return 0xfdffffff | ping_pong;
}
else if (reg >= 20 && reg<=32) { // line of sight registers
@@ -867,8 +898,9 @@ void CReal3D::Reset(void)
error = false;
m_pingPong = 0;
m_pingPongCopy = 0;
commandPortWritten = false;
commandPortWrittenRO = false;
m_tilegenDrawFrame = false;
queuedUploadTextures.clear();
queuedUploadTexturesRO.clear();
@@ -1029,6 +1061,10 @@ CReal3D::CReal3D(const Util::Config::Node &config)
vrom = NULL;
error = false;
fifoIdx = 0;
commandPortWritten = false;
m_tilegenDrawFrame = false;
m_pingPong = 0;
m_pingPongCopy = 0;
m_vromTextureFIFO[0] = 0;
m_vromTextureFIFO[1] = 0;
m_vromTextureFIFOIdx = 0;
+21 -4
View File
@@ -121,7 +121,7 @@ public:
*
* Must be called before the VBlank starts.
*/
void BeginVBlank(int statusCycles);
void BeginVBlank(void);
/*
* EndVBlank(void)
@@ -130,6 +130,13 @@ public:
*/
void EndVBlank(void);
/*
* FlipPingPongBit(void)
*
* Any writes that happen after the ping_pong bit flips but before VBlank are buffered.
*/
void FlipPingPongBit(void);
/*
* SyncSnapshots(void):
*
@@ -338,6 +345,14 @@ public:
* data Data.
*/
void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, uint32_t data);
/*
* TilegenDrawFrame(flags):
*
* The tilegen controls the frame timing. A write to reg 0x0C appears to
* trigger the 3D hardware to draw the frame.
*/
void TilegenDrawFrame(uint32_t flags);
/*
* Reset(void):
@@ -436,6 +451,9 @@ private:
void UploadTexture(uint32_t header, const uint16_t *texData);
uint32_t UpdateSnapshots(bool copyWhole);
uint32_t UpdateSnapshot(bool copyWhole, uint8_t *src, uint8_t *dst, unsigned size, uint8_t *dirty);
void FlushTextures();
bool PollPingPong();
void DrawFrame();
// Config
const Util::Config::Node &m_config;
@@ -497,12 +515,11 @@ private:
// Command port
bool commandPortWritten;
bool commandPortWrittenRO; // Read-only copy of flag
bool m_tilegenDrawFrame;
// Status and command registers
uint32_t m_pingPong;
uint64_t statusChange = 0;
bool m_evenFrame = false;
uint32_t m_pingPongCopy;
// Internal ASIC state
std::unordered_map<ASIC, uint32_t> m_asicID;
+1
View File
@@ -326,6 +326,7 @@ struct Super3Host {
// Keep unsupported rendering knobs clamped on Android.
config.Set("QuadRendering", false);
config.Set("New3DEngine", true);
config.Set("New3DAccurate", false);
// Allow user-specified framebuffer sizes. Clamp to sane bounds so we don't