bool FifoCommandRunnable() { u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData); if (buffer_size == 0) return false; // can't peek u8 cmd_byte = DataPeek8(0); u32 command_size = 0; switch (cmd_byte) { case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly... case GX_CMD_INVL_VC: // Invalidate Vertex Cache - no parameters case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that command_size = 1; break; case GX_LOAD_BP_REG: command_size = 5; break; case GX_LOAD_CP_REG: command_size = 6; break; case GX_LOAD_INDX_A: case GX_LOAD_INDX_B: case GX_LOAD_INDX_C: case GX_LOAD_INDX_D: command_size = 5; break; case GX_CMD_CALL_DL: command_size = 9; break; case GX_LOAD_XF_REG: { // check if we can read the header if (buffer_size >= 5) { command_size = 1 + 4; u32 Cmd2 = DataPeek32(1); int transfer_size = ((Cmd2 >> 16) & 15) + 1; command_size += transfer_size * 4; } else { return false; } }
// 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()) { fifo.isGpuReadingData = true; CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false; if (!Core::g_CoreStartupParameter.bSyncGPU || Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin) { 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 instability in the game. Please report it.", fifo.CPReadWriteDistance - 32); ReadDataFromFifo(uData, 32); cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame); if (Core::g_CoreStartupParameter.bSyncGPU && Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted) 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(); } } } }
u32 FifoCommandRunnable(u32 &command_size) { u32 cycleTime = 0; u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData); if (buffer_size == 0) return 0; // can't peek u8 cmd_byte = DataPeek8(0); switch (cmd_byte) { case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly... command_size = 1; cycleTime = 6; break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache - no parameters command_size = 1; cycleTime = 6; break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that command_size = 1; cycleTime = 6; break; case GX_LOAD_BP_REG: command_size = 5; cycleTime = 12; break; case GX_LOAD_CP_REG: command_size = 6; cycleTime = 12; break; case GX_LOAD_INDX_A: case GX_LOAD_INDX_B: case GX_LOAD_INDX_C: case GX_LOAD_INDX_D: command_size = 5; cycleTime = 6; // TODO break; case GX_CMD_CALL_DL: { // FIXME: Calculate the cycle time of the display list. //u32 address = DataPeek32(1); //u32 size = DataPeek32(5); //u8* old_pVideoData = g_pVideoData; //u8* startAddress = Memory::GetPointer(address); //// Avoid the crash if Memory::GetPointer failed .. //if (startAddress != 0) //{ // g_pVideoData = startAddress; // u8 *end = g_pVideoData + size; // u32 step = 0; // while (g_pVideoData < end) // { // cycleTime += FifoCommandRunnable(step); // g_pVideoData += step; // } //} //else //{ // cycleTime = 45; //} //// reset to the old pointer //g_pVideoData = old_pVideoData; command_size = 9; cycleTime = 45; // This is unverified } break; case GX_LOAD_XF_REG: { // check if we can read the header if (buffer_size >= 5) { command_size = 1 + 4; u32 Cmd2 = DataPeek32(1); int transfer_size = ((Cmd2 >> 16) & 15) + 1; command_size += transfer_size * 4; cycleTime = 18 + 6 * transfer_size; } else { return 0; } }