dolphin/Source/Core/VideoCommon/Src/Fifo.cpp

259 lines
7.0 KiB
C++
Raw Normal View History

// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "VideoConfig.h"
#include "MemoryUtil.h"
#include "Thread.h"
#include "Atomic.h"
#include "OpcodeDecoding.h"
#include "CommandProcessor.h"
#include "PixelEngine.h"
#include "ChunkFile.h"
#include "Fifo.h"
#include "HW/Memmap.h"
#include "Core.h"
#include "CoreTiming.h"
volatile bool g_bSkipCurrentFrame = false;
extern u8* g_pVideoData;
namespace
{
static volatile bool GpuRunningState = false;
static volatile bool EmuRunningState = false;
static std::mutex m_csHWVidOccupied;
// STATE_TO_SAVE
static u8 *videoBuffer;
static int size = 0;
} // namespace
void Fifo_DoState(PointerWrap &p)
{
p.DoArray(videoBuffer, FIFO_SIZE);
p.Do(size);
p.DoPointer(g_pVideoData, videoBuffer);
p.Do(g_bSkipCurrentFrame);
}
void Fifo_PauseAndLock(bool doLock, bool unpauseOnUnlock)
{
if (doLock)
{
EmulatorState(false);
if (!Core::IsGPUThread())
m_csHWVidOccupied.lock();
_dbg_assert_(COMMON, !CommandProcessor::fifo.isGpuReadingData);
}
else
{
if (unpauseOnUnlock)
EmulatorState(true);
if (!Core::IsGPUThread())
m_csHWVidOccupied.unlock();
}
}
void Fifo_Init()
{
videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE);
size = 0;
GpuRunningState = false;
Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);
}
void Fifo_Shutdown()
{
if (GpuRunningState) PanicAlert("Fifo shutting down while active");
FreeMemoryPages(videoBuffer, FIFO_SIZE);
}
u8* GetVideoBufferStartPtr()
{
return videoBuffer;
}
u8* GetVideoBufferEndPtr()
{
return &videoBuffer[size];
}
void Fifo_SetRendering(bool enabled)
{
g_bSkipCurrentFrame = !enabled;
}
// May be executed from any thread, even the graphics thread.
// Created to allow for self shutdown.
void ExitGpuLoop()
{
// This should break the wait loop in CPU thread
CommandProcessor::fifo.bFF_GPReadEnable = false;
SCPFifoStruct &fifo = CommandProcessor::fifo;
while(fifo.isGpuReadingData) Common::YieldCPU();
// Terminate GPU thread loop
GpuRunningState = false;
EmuRunningState = true;
}
void EmulatorState(bool running)
{
EmuRunningState = running;
}
// Description: RunGpuLoop() sends data through this function.
void ReadDataFromFifo(u8* _uData, u32 len)
{
if (size + len >= FIFO_SIZE)
{
int pos = (int)(g_pVideoData - videoBuffer);
size -= pos;
if (size + len > FIFO_SIZE)
{
PanicAlert("FIFO out of bounds (sz = %i, len = %i at %08x)", size, len, pos);
}
memmove(&videoBuffer[0], &videoBuffer[pos], size);
g_pVideoData = videoBuffer;
}
// Copy new video instructions to videoBuffer for future use in rendering the new picture
memcpy(videoBuffer + size, _uData, len);
size += len;
}
void ResetVideoBuffer()
{
g_pVideoData = videoBuffer;
size = 0;
}
BIG FIFO Commit PART 1! Sometimes you need to write everything from 0, so 10 days later Ive rewrited some parts of the FIFO in Dual Core mode. Is pending use the same code for SC mode. - Improved the GP Register Status: now this is all the time from the fifo loop. - Improved the Interrupts manage: 1) Removed All UpdateInturrupts from CommandProcessor Writes and Read. 2) now the CP interrupts are schedule from the video thread and the fifo loop waiting until this happens 3) considering Inmediate mode for the CP interrupts 3) Implemented Interrupt CP Cache State 4) Implemented only Overflow interrupt in GatherPipeCheck because this need to be detected quickly. - Implemented Overflow handling like a real HW, when Hiwatermark interrupt happens this write ClearRegister with True en HI and False in LO (FifoIntReset) after that a Control Register is writed and the FIFO is processed to LO Watermark. - Removed all ugly code from LO and HI watermark manage - Removed all ugly code from BP manage - Change >= by == in the BP clauses - Removed speed hack (1024 chunk) for better GP Status Control. - Commented GXSetGPFifo very soon hack - Commented FackWatchDog hack - Commented FIFO_RW_DISTANCE = WritePointer hack This is the beginning and the base for the future., If this broke your favorite game my apologize, only report this and will try solve it. If you have a Overflown don't worry, I've implemented the real solution code using the Overflow Interruption only need continue working for a perfect protection. Why I did it? Because is preferable a accurate and clean fifo instead hack y fifo for improve that. Thanks to DONKO for you awesome Video Plug in and skid for the chatting. PD: I have 7-10 fps more in the star fox video. bye :P git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6554 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-12-11 12:42:55 +00:00
// Description: Main FIFO update loop
// Purpose: Keep the Core HW updated about the CPU-GPU distance
void RunGpuLoop()
{
std::lock_guard<std::mutex> lk(m_csHWVidOccupied);
GpuRunningState = true;
SCPFifoStruct &fifo = CommandProcessor::fifo;
u32 cyclesExecuted = 0;
while (GpuRunningState)
{
g_video_backend->PeekMessages();
VideoFifo_CheckAsyncRequest();
CommandProcessor::SetCpStatus();
Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);
// check if we are able to run this buffer
while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint())
{
if (!GpuRunningState) break;
fifo.isGpuReadingData = true;
CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false;
if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU)
{
u32 readPtr = fifo.CPReadPointer;
u8 *uData = Memory::GetPointer(readPtr);
if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase;
else readPtr += 32;
_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 ,
"Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);
ReadDataFromFifo(uData, 32);
cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame);
if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU)
Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted);
Common::AtomicStore(fifo.CPReadPointer, readPtr);
Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
}
CommandProcessor::SetCpStatus();
// This call is pretty important in DualCore mode and must be called in the FIFO Loop.
// If we don't, s_swapRequested or s_efbAccessRequested won't be set to false
// leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing things down.
VideoFifo_CheckAsyncRequest();
CommandProcessor::isPossibleWaitingSetDrawDone = false;
}
fifo.isGpuReadingData = false;
if (EmuRunningState)
{
// NOTE(jsd): Calling SwitchToThread() on Windows 7 x64 is a hot spot, according to profiler.
// See https://docs.google.com/spreadsheet/ccc?key=0Ah4nh0yGtjrgdFpDeF9pS3V6RUotRVE3S3J4TGM1NlE#gid=0
// for benchmark details.
#if 0
Common::YieldCPU();
#endif
}
else
{
// While the emu is paused, we still handle async requests then sleep.
while (!EmuRunningState)
{
g_video_backend->PeekMessages();
m_csHWVidOccupied.unlock();
Common::SleepCurrentThread(1);
m_csHWVidOccupied.lock();
}
}
}
}
BIG FIFO Commit PART 1! Sometimes you need to write everything from 0, so 10 days later Ive rewrited some parts of the FIFO in Dual Core mode. Is pending use the same code for SC mode. - Improved the GP Register Status: now this is all the time from the fifo loop. - Improved the Interrupts manage: 1) Removed All UpdateInturrupts from CommandProcessor Writes and Read. 2) now the CP interrupts are schedule from the video thread and the fifo loop waiting until this happens 3) considering Inmediate mode for the CP interrupts 3) Implemented Interrupt CP Cache State 4) Implemented only Overflow interrupt in GatherPipeCheck because this need to be detected quickly. - Implemented Overflow handling like a real HW, when Hiwatermark interrupt happens this write ClearRegister with True en HI and False in LO (FifoIntReset) after that a Control Register is writed and the FIFO is processed to LO Watermark. - Removed all ugly code from LO and HI watermark manage - Removed all ugly code from BP manage - Change >= by == in the BP clauses - Removed speed hack (1024 chunk) for better GP Status Control. - Commented GXSetGPFifo very soon hack - Commented FackWatchDog hack - Commented FIFO_RW_DISTANCE = WritePointer hack This is the beginning and the base for the future., If this broke your favorite game my apologize, only report this and will try solve it. If you have a Overflown don't worry, I've implemented the real solution code using the Overflow Interruption only need continue working for a perfect protection. Why I did it? Because is preferable a accurate and clean fifo instead hack y fifo for improve that. Thanks to DONKO for you awesome Video Plug in and skid for the chatting. PD: I have 7-10 fps more in the star fox video. bye :P git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6554 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-12-11 12:42:55 +00:00
bool AtBreakpoint()
{
SCPFifoStruct &fifo = CommandProcessor::fifo;
return fifo.bFF_BPEnable && (fifo.CPReadPointer == fifo.CPBreakpoint);
BIG FIFO Commit PART 1! Sometimes you need to write everything from 0, so 10 days later Ive rewrited some parts of the FIFO in Dual Core mode. Is pending use the same code for SC mode. - Improved the GP Register Status: now this is all the time from the fifo loop. - Improved the Interrupts manage: 1) Removed All UpdateInturrupts from CommandProcessor Writes and Read. 2) now the CP interrupts are schedule from the video thread and the fifo loop waiting until this happens 3) considering Inmediate mode for the CP interrupts 3) Implemented Interrupt CP Cache State 4) Implemented only Overflow interrupt in GatherPipeCheck because this need to be detected quickly. - Implemented Overflow handling like a real HW, when Hiwatermark interrupt happens this write ClearRegister with True en HI and False in LO (FifoIntReset) after that a Control Register is writed and the FIFO is processed to LO Watermark. - Removed all ugly code from LO and HI watermark manage - Removed all ugly code from BP manage - Change >= by == in the BP clauses - Removed speed hack (1024 chunk) for better GP Status Control. - Commented GXSetGPFifo very soon hack - Commented FackWatchDog hack - Commented FIFO_RW_DISTANCE = WritePointer hack This is the beginning and the base for the future., If this broke your favorite game my apologize, only report this and will try solve it. If you have a Overflown don't worry, I've implemented the real solution code using the Overflow Interruption only need continue working for a perfect protection. Why I did it? Because is preferable a accurate and clean fifo instead hack y fifo for improve that. Thanks to DONKO for you awesome Video Plug in and skid for the chatting. PD: I have 7-10 fps more in the star fox video. bye :P git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6554 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-12-11 12:42:55 +00:00
}
void RunGpu()
{
SCPFifoStruct &fifo = CommandProcessor::fifo;
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() )
{
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);
2013-03-15 08:32:47 +00:00
FPURoundMode::SaveSIMDState();
FPURoundMode::LoadDefaultSIMDState();
ReadDataFromFifo(uData, 32);
u32 count = OpcodeDecoder_Run(g_bSkipCurrentFrame);
FPURoundMode::LoadSIMDState();
//DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");
if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase;
else fifo.CPReadPointer += 32;
fifo.CPReadWriteDistance -= 32;
}
CommandProcessor::SetCpStatus();
}