void VideoBackendHardware::Video_ExitLoop() { ExitGpuLoop(); s_FifoShuttingDown.Set(); s_efbAccessReadyEvent.Set(); s_perfQueryReadyEvent.Set(); }
void DSPLLE::DSP_StopSoundStream() { if (m_bDSPThread) { m_bIsRunning.Clear(); ppcEvent.Set(); dspEvent.Set(); m_hDSPThread.join(); } }
void DSPLLE::DSP_StopSoundStream() { DSPInterpreter::Stop(); m_bIsRunning = false; if (m_bDSPThread) { ppcEvent.Set(); dspEvent.Set(); m_hDSPThread.join(); } }
static void DVDThread() { Common::SetCurrentThreadName("DVD thread"); while (true) { s_request_queue_expanded.Wait(); if (s_dvd_thread_exiting.IsSet()) return; ReadRequest request; while (s_request_queue.Pop(request)) { FileMonitor::Log(*s_disc, request.partition, request.dvd_offset); std::vector<u8> buffer(request.length); if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) buffer.resize(0); request.realtime_done_us = Common::Timer::GetTimeUs(); s_result_queue.Push(ReadResult(std::move(request), std::move(buffer))); s_result_queue_expanded.Set(); if (s_dvd_thread_exiting.IsSet()) return; } } }
static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { ASSERT(Core::IsCPUThread()); ReadRequest request; request.copy_to_ram = copy_to_ram; request.output_address = output_address; request.dvd_offset = dvd_offset; request.length = length; request.partition = partition; request.reply_type = reply_type; u64 id = s_next_id++; request.id = id; request.time_started_ticks = CoreTiming::GetTicks(); request.realtime_started_us = Common::Timer::GetTimeUs(); s_request_queue.Push(std::move(request)); s_request_queue_expanded.Set(); CoreTiming::ScheduleEvent(ticks_until_completion, s_finish_read, id); }
TEST(BusyLoopTest, MultiThreaded) { Common::BlockingLoop loop; Common::Event e; for (int i = 0; i < 100; i++) { loop.Prepare(); std::thread loop_thread([&]() { loop.Run([&]() { e.Set(); }); }); // Ping - Pong for (int j = 0; j < 10; j++) { loop.Wakeup(); e.Wait(); // Just waste some time. So the main loop did fall back to the sleep state much more likely. Common::SleepCurrentThread(1); } for (int j = 0; j < 100; j++) { // We normally have to call Wakeup to assure the Event is triggered. // But this check is for an internal feature of the BlockingLoop. // It's implemented to fall back to a busy loop regulary. // If we're in the busy loop, the payload (and so the Event) is called all the time. // loop.Wakeup(); e.Wait(); } loop.Stop(); loop_thread.join(); } }
// Regular thread void DSPLLE::DSPThread(DSPLLE* dsp_lle) { Common::SetCurrentThreadName("DSP thread"); while (dsp_lle->m_bIsRunning.IsSet()) { const int cycles = static_cast<int>(dsp_lle->m_cycle_count.load()); if (cycles > 0) { std::lock_guard<std::mutex> dsp_thread_lock(dsp_lle->m_csDSPThreadActive); if (g_dsp_jit) { DSPCore_RunCycles(cycles); } else { DSPInterpreter::RunCyclesThread(cycles); } dsp_lle->m_cycle_count.store(0); } else { ppcEvent.Set(); dspEvent.Wait(); } } }
// Regular thread void DSPLLE::dsp_thread(DSPLLE *dsp_lle) { Common::SetCurrentThreadName("DSP thread"); while (dsp_lle->m_bIsRunning) { int cycles = (int)dsp_lle->m_cycle_count; if (cycles > 0) { std::lock_guard<std::mutex> lk(dsp_lle->m_csDSPThreadActive); if (dspjit) { DSPCore_RunCycles(cycles); } else { DSPInterpreter::RunCyclesThread(cycles); } Common::AtomicStore(dsp_lle->m_cycle_count, 0); } else { ppcEvent.Set(); dspEvent.Wait(); } } }
void Host_Message(int Id) { if (Id == WM_USER_STOP) { s_running.Clear(); updateMainFrameEvent.Set(); } }
void DSPCore_SetState(DSPCoreState new_state) { core_state = new_state; // kick the event, in case we are waiting if (new_state == DSPCORE_RUNNING) step_event.Set(); // Sleep(10); DSPHost::UpdateDebugger(); }
static void VideoFifo_CheckPerfQueryRequest() { if (s_perfQueryRequested.IsSet()) { g_perf_query->FlushResults(); s_perfQueryRequested.Clear(); s_perfQueryReadyEvent.Set(); } }
void VideoFifo_CheckEFBAccess() { if (s_efbAccessRequested.IsSet()) { s_AccessEFBResult = g_renderer->AccessEFB(s_accessEFBArgs.type, s_accessEFBArgs.x, s_accessEFBArgs.y, s_accessEFBArgs.Data); s_efbAccessRequested.Clear(); s_efbAccessReadyEvent.Set(); } }
void DSPCore_SetState(State new_state) { core_state = new_state; // kick the event, in case we are waiting if (new_state == State::Running) step_event.Set(); Host::UpdateDebugger(); }
static void StopDVDThread() { ASSERT(s_dvd_thread.joinable()); // By setting s_DVD_thread_exiting, we ask the DVD thread to cleanly exit. // In case the request queue is empty, we need to set s_request_queue_expanded // so that the DVD thread will wake up and check s_DVD_thread_exiting. s_dvd_thread_exiting.Set(); s_request_queue_expanded.Set(); s_dvd_thread.join(); }
void DSPLLE::DSP_Update(int cycles) { int dsp_cycles = cycles / 6; if (dsp_cycles <= 0) return; // Sound stream update job has been handled by AudioDMA routine, which is more efficient /* // This gets called VERY OFTEN. The soundstream update might be expensive so only do it 200 times per second or something. int cycles_between_ss_update; if (g_dspInitialize.bWii) cycles_between_ss_update = 121500000 / 200; else cycles_between_ss_update = 81000000 / 200; m_cycle_count += cycles; if (m_cycle_count > cycles_between_ss_update) { while (m_cycle_count > cycles_between_ss_update) m_cycle_count -= cycles_between_ss_update; soundStream->Update(); } */ if (m_bDSPThread) { if (requestDisableThread || NetPlay::IsNetPlayRunning() || Movie::IsMovieActive() || Core::g_want_determinism) { DSP_StopSoundStream(); m_bDSPThread = false; requestDisableThread = false; SConfig::GetInstance().bDSPThread = false; } } // If we're not on a thread, run cycles here. if (!m_bDSPThread) { // ~1/6th as many cycles as the period PPC-side. DSPCore_RunCycles(dsp_cycles); } else { // Wait for DSP thread to complete its cycle. Note: this logic should be thought through. ppcEvent.Wait(); m_cycle_count.fetch_add(dsp_cycles); dspEvent.Set(); } }
void DSPLLE::DSP_Update(int cycles) { int dsp_cycles = cycles / 6; if (dsp_cycles <= 0) return; // Sound stream update job has been handled by AudioDMA routine, which is more efficient /* // This gets called VERY OFTEN. The soundstream update might be expensive so only do it 200 times per second or something. int cycles_between_ss_update; if (g_dspInitialize.bWii) cycles_between_ss_update = 121500000 / 200; else cycles_between_ss_update = 81000000 / 200; m_cycle_count += cycles; if (m_cycle_count > cycles_between_ss_update) { while (m_cycle_count > cycles_between_ss_update) m_cycle_count -= cycles_between_ss_update; soundStream->Update(); } */ // If we're not on a thread, run cycles here. if (!m_bDSPThread) { // ~1/6th as many cycles as the period PPC-side. DSPCore_RunCycles(dsp_cycles); } else { // Wait for dsp thread to complete its cycle. Note: this logic should be thought through. ppcEvent.Wait(); Common::AtomicStore(m_cycle_count, dsp_cycles); dspEvent.Set(); } }
static void CompressAndDumpState(CompressAndDumpState_args save_args) { std::lock_guard<std::mutex> lk(*save_args.buffer_mutex); // ScopeGuard is used here to ensure that g_compressAndDumpStateSyncEvent.Set() // will be called and that it will happen after the IOFile is closed. // Both ScopeGuard's and IOFile's finalization occur at respective object destruction time. // As Local (stack) objects are destructed in the reverse order of construction and "ScopeGuard // on_exit" // is created before the "IOFile f", it is guaranteed that the file will be finalized before // the ScopeGuard's finalization (i.e. "g_compressAndDumpStateSyncEvent.Set()" call). Common::ScopeGuard on_exit([]() { g_compressAndDumpStateSyncEvent.Set(); }); // If it is not required to wait, we call finalizer early (and it won't be called again at // destruction). if (!save_args.wait) on_exit.Exit(); const u8* const buffer_data = &(*(save_args.buffer_vector))[0]; const size_t buffer_size = (save_args.buffer_vector)->size(); std::string& filename = save_args.filename; // For easy debugging Common::SetCurrentThreadName("SaveState thread"); // Moving to last overwritten save-state if (File::Exists(filename)) { if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")); if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm")) File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm")); if (!File::Rename(filename, File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) Core::DisplayMessage("Failed to move previous state to state undo backup", 1000); else File::Rename(filename + ".dtm", File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav.dtm"); } if ((Movie::IsMovieActive()) && !Movie::IsJustStartingRecordingInputFromSaveState()) Movie::SaveRecording(filename + ".dtm"); else if (!Movie::IsMovieActive()) File::Delete(filename + ".dtm"); File::IOFile f(filename, "wb"); if (!f) { Core::DisplayMessage("Could not save state", 2000); return; } // Setting up the header StateHeader header; strncpy(header.gameID, SConfig::GetInstance().GetUniqueID().c_str(), 6); header.size = g_use_compression ? (u32)buffer_size : 0; header.time = Common::Timer::GetDoubleTime(); f.WriteArray(&header, 1); if (header.size != 0) // non-zero header size means the state is compressed { lzo_uint i = 0; while (true) { lzo_uint32 cur_len = 0; lzo_uint out_len = 0; if ((i + IN_LEN) >= buffer_size) { cur_len = (lzo_uint32)(buffer_size - i); } else { cur_len = IN_LEN; } if (lzo1x_1_compress(buffer_data + i, cur_len, out, &out_len, wrkmem) != LZO_E_OK) PanicAlertT("Internal LZO Error - compression failed"); // The size of the data to write is 'out_len' f.WriteArray((lzo_uint32*)&out_len, 1); f.WriteBytes(out, out_len); if (cur_len != IN_LEN) break; i += cur_len; } } else // uncompressed { f.WriteBytes(buffer_data, buffer_size); } Core::DisplayMessage(StringFromFormat("Saved State to %s", filename.c_str()), 2000); Host_UpdateMainFrame(); }
void CompressAndDumpState(CompressAndDumpState_args save_args) { std::lock_guard<std::mutex> lk(*save_args.buffer_mutex); g_compressAndDumpStateSyncEvent.Set(); const u8* const buffer_data = &(*(save_args.buffer_vector))[0]; const size_t buffer_size = (save_args.buffer_vector)->size(); std::string& filename = save_args.filename; // For easy debugging Common::SetCurrentThreadName("SaveState thread"); // Moving to last overwritten save-state if (File::Exists(filename)) { if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")); if (!File::Rename(filename, File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) Core::DisplayMessage("Failed to move previous state to state undo backup", 1000); } File::IOFile f(filename, "wb"); if (!f) { Core::DisplayMessage("Could not save state", 2000); return; } // Setting up the header StateHeader header; memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6); header.size = g_use_compression ? buffer_size : 0; f.WriteArray(&header, 1); if (0 != header.size) // non-zero header size means the state is compressed { lzo_uint i = 0; while (true) { lzo_uint cur_len = 0; lzo_uint out_len = 0; if ((i + IN_LEN) >= buffer_size) cur_len = buffer_size - i; else cur_len = IN_LEN; if (lzo1x_1_compress(buffer_data + i, cur_len, out, &out_len, wrkmem) != LZO_E_OK) PanicAlertT("Internal LZO Error - compression failed"); // The size of the data to write is 'out_len' f.WriteArray(&out_len, 1); f.WriteBytes(out, out_len); if (cur_len != IN_LEN) break; i += cur_len; } } else // uncompressed { f.WriteBytes(buffer_data, buffer_size); } Core::DisplayMessage(StringFromFormat("Saved State to %s", filename.c_str()).c_str(), 2000); }
// Description: Main FIFO update loop // Purpose: Keep the Core HW updated about the CPU-GPU distance void RunGpuLoop() { AsyncRequests::GetInstance()->SetEnable(true); AsyncRequests::GetInstance()->SetPassthrough(false); s_gpu_mainloop.Run( [] { const SConfig& param = SConfig::GetInstance(); g_video_backend->PeekMessages(); // Do nothing while paused if (!s_emu_running_state.load()) return; if (g_use_deterministic_gpu_thread) { AsyncRequests::GetInstance()->PullEvents(); // All the fifo/CP stuff is on the CPU. We just need to run the opcode decoder. u8* seen_ptr = s_video_buffer_seen_ptr; u8* write_ptr = s_video_buffer_write_ptr; // See comment in SyncGPU if (write_ptr > seen_ptr) { s_video_buffer_read_ptr = OpcodeDecoder_Run(DataReader(s_video_buffer_read_ptr, write_ptr), nullptr, false); s_video_buffer_seen_ptr = write_ptr; } } else { SCPFifoStruct &fifo = CommandProcessor::fifo; AsyncRequests::GetInstance()->PullEvents(); CommandProcessor::SetCPStatusFromGPU(); // check if we are able to run this buffer while (!CommandProcessor::IsInterruptWaiting() && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) { if (param.bSyncGPU && s_sync_ticks.load() < param.iSyncGpuMinDistance) break; u32 cyclesExecuted = 0; u32 readPtr = fifo.CPReadPointer; ReadDataFromFifo(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); u8* write_ptr = s_video_buffer_write_ptr; s_video_buffer_read_ptr = OpcodeDecoder_Run(DataReader(s_video_buffer_read_ptr, write_ptr), &cyclesExecuted, false); Common::AtomicStore(fifo.CPReadPointer, readPtr); Common::AtomicAdd(fifo.CPReadWriteDistance, -32); if ((write_ptr - s_video_buffer_read_ptr) == 0) Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); CommandProcessor::SetCPStatusFromGPU(); if (param.bSyncGPU) { cyclesExecuted = (int)(cyclesExecuted / param.fSyncGpuOverclock); int old = s_sync_ticks.fetch_sub(cyclesExecuted); if (old > 0 && old - (int)cyclesExecuted <= 0) s_sync_wakeup_event.Set(); } // 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. AsyncRequests::GetInstance()->PullEvents(); } // fast skip remaining GPU time if fifo is empty if (s_sync_ticks.load() > 0) { int old = s_sync_ticks.exchange(0); if (old > 0) s_sync_wakeup_event.Set(); } // The fifo is empty and it's unlikely we will get any more work in the near future. // Make sure VertexManager finishes drawing any primitives it has stored in it's buffer. VertexManager::Flush(); } }, 100); AsyncRequests::GetInstance()->SetEnable(false); AsyncRequests::GetInstance()->SetPassthrough(true); }
void Host_UpdateMainFrame() { updateMainFrameEvent.Set(); }
void DSPCore_Step() { if (core_state == State::Stepping) step_event.Set(); }
void DSPCore_Step() { if (core_state == DSPCORE_STEPPING) step_event.Set(); }
void Host_UpdateMainFrame() { s_update_main_frame_event.Set(); }