void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) { sMutex.lock(); delete m_File; delete []m_Ram; delete []m_ExRam; m_File = new FifoDataFile; m_Ram = new u8[Memory::RAM_SIZE]; m_ExRam = new u8[Memory::EXRAM_SIZE]; memset(m_Ram, 0, Memory::RAM_SIZE); memset(m_ExRam, 0, Memory::EXRAM_SIZE); m_File->SetIsWii(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii); if (!m_IsRecording) { m_WasRecording = false; m_IsRecording = true; m_RecordFramesRemaining = numFrames; } m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; sMutex.unlock(); }
// numFrames is number of stereo frames. // This is called from *outside* the emulator thread. int __AudioMix(short *outstereo, int numFrames) { // TODO: if mixFrequency != the actual output frequency, resample! section.lock(); int underrun = -1; s16 sampleL = 0; s16 sampleR = 0; bool anythingToPlay = false; for (int i = 0; i < numFrames; i++) { if (outAudioQueue.size() >= 2) { sampleL = outAudioQueue.pop_front(); sampleR = outAudioQueue.pop_front(); outstereo[i * 2 + 0] = sampleL; outstereo[i * 2 + 1] = sampleR; anythingToPlay = true; } else { if (underrun == -1) underrun = i; outstereo[i * 2 + 0] = sampleL; // repeat last sample, can reduce clicking outstereo[i * 2 + 1] = sampleR; // repeat last sample, can reduce clicking } } if (anythingToPlay && underrun >= 0) { DEBUG_LOG(HLE, "Audio out buffer UNDERRUN at %i of %i", underrun, numFrames); } else { // DEBUG_LOG(HLE, "No underrun, mixed %i samples fine", numFrames); } section.unlock(); return underrun >= 0 ? underrun : numFrames; }
void __AudioDoState(PointerWrap &p) { section.lock(); p.Do(eventAudioUpdate); CoreTiming::RestoreRegisterEvent(eventAudioUpdate, "AudioUpdate", &hleAudioUpdate); p.Do(eventHostAudioUpdate); CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleHostAudioUpdate); p.Do(mixFrequency); outAudioQueue.DoState(p); int chanCount = ARRAY_SIZE(chans); p.Do(chanCount); if (chanCount != ARRAY_SIZE(chans)) { ERROR_LOG(HLE, "Savestate failure: different number of audio channels."); section.unlock(); return; } for (int i = 0; i < chanCount; ++i) chans[i].DoState(p); section.unlock(); p.DoMarker("sceAudio"); }
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking) { u32 ret = 0; section.lock(); if (chan.sampleAddress == 0) return SCE_ERROR_AUDIO_NOT_OUTPUT; if (chan.sampleQueue.size() > chan.sampleCount*2*chanQueueMaxSizeFactor) { // Block! if (blocking) { chan.waitingThread = __KernelGetCurThread(); // WARNING: This changes currentThread so must grab waitingThread before (line above). __KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum, 0, 0, false, "blocking audio waited"); // Fall through to the sample queueing, don't want to lose the samples even though // we're getting full. } else { chan.waitingThread = 0; return SCE_ERROR_AUDIO_CHANNEL_BUSY; } } if (chan.format == PSP_AUDIO_FORMAT_STEREO) { const u32 totalSamples = chan.sampleCount * 2; if (IS_LITTLE_ENDIAN) { s16 *sampleData = (s16 *) Memory::GetPointer(chan.sampleAddress); // Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr. if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16))) { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push(*sampleData++); } } else { for (u32 i = 0; i < totalSamples; i++) chan.sampleQueue.push((s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i)); } ret = chan.sampleCount; } else if (chan.format == PSP_AUDIO_FORMAT_MONO) { for (u32 i = 0; i < chan.sampleCount; i++) { // Expand to stereo s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i); chan.sampleQueue.push(sample); chan.sampleQueue.push(sample); } ret = chan.sampleCount; } section.unlock(); return ret; }
int main() { m.lock(); std::thread t(f); std::this_thread::sleep_for(ms(250)); m.unlock(); t.join(); }
void processFdbEntriesForAging() { SWSS_LOG_ENTER(); if (!g_recursive_mutex.try_lock()) { return; } SWSS_LOG_INFO("fdb infos to process: %zu", g_fdb_info_set.size()); uint32_t current = (uint32_t)time(NULL); // find aged fdb entries for (auto it = g_fdb_info_set.begin(); it != g_fdb_info_set.end();) { sai_attribute_t attr; attr.id = SAI_SWITCH_ATTR_FDB_AGING_TIME; sai_status_t status = vs_generic_get(SAI_OBJECT_TYPE_SWITCH, it->fdb_entry.switch_id, 1, &attr); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_WARN("failed to get FDB aging time for switch %s", sai_serialize_object_id(it->fdb_entry.switch_id).c_str()); ++it; continue; } uint32_t aging_time = attr.value.u32; if (aging_time == 0) { // aging is disabled ++it; continue; } if ((current - it->timestamp) >= aging_time) { fdb_info_t fi = *it; processFdbInfo(fi, SAI_FDB_EVENT_AGED); it = g_fdb_info_set.erase(it); } else { ++it; } } g_recursive_mutex.unlock(); }
// Mix samples from the various audio channels into a single sample queue. // This single sample queue is where __AudioMix should read from. If the sample queue is full, we should // just sleep the main emulator thread a little. void __AudioUpdate() { // Audio throttle doesn't really work on the PSP since the mixing intervals are so closely tied // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. s32 mixBuffer[hwBlockSize * 2]; memset(mixBuffer, 0, sizeof(mixBuffer)); for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) { if (!chans[i].reserved) continue; __AudioWakeThreads(chans[i], hwBlockSize); if (!chans[i].sampleQueue.size()) { // ERROR_LOG(HLE, "No queued samples, skipping channel %i", i); continue; } for (int s = 0; s < hwBlockSize; s++) { if (chans[i].sampleQueue.size() >= 2) { s16 sampleL = chans[i].sampleQueue.pop_front(); s16 sampleR = chans[i].sampleQueue.pop_front(); mixBuffer[s * 2 + 0] += sampleL; mixBuffer[s * 2 + 1] += sampleR; } else { ERROR_LOG(HLE, "Channel %i buffer underrun at %i of %i", i, s, hwBlockSize); break; } } } if (g_Config.bEnableSound) { section.lock(); if (outAudioQueue.room() >= hwBlockSize * 2) { // Push the mixed samples onto the output audio queue. for (int i = 0; i < hwBlockSize; i++) { s16 sampleL = clamp_s16(mixBuffer[i * 2 + 0]); s16 sampleR = clamp_s16(mixBuffer[i * 2 + 1]); outAudioQueue.push((s16)sampleL); outAudioQueue.push((s16)sampleR); } } else { // This happens quite a lot. There's still something slightly off // about the amount of audio we produce. DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity()); } section.unlock(); } }
void f() { time_point t0 = Clock::now(); m.lock(); time_point t1 = Clock::now(); m.lock(); m.unlock(); m.unlock(); ns d = t1 - t0 - ms(250); assert(d < ns(2500000)); // within 2.5ms }
void FifoPlayerDlg::RecordingFinished() { sMutex.lock(); if (m_EvtHandler) { wxCommandEvent event(RECORDING_FINISHED_EVENT); m_EvtHandler->AddPendingEvent(event); } sMutex.unlock(); }
void FifoPlayerDlg::FileLoaded() { sMutex.lock(); if (m_EvtHandler) { wxPaintEvent event; m_EvtHandler->AddPendingEvent(event); } sMutex.unlock(); }
void FifoPlayerDlg::FrameWritten() { sMutex.lock(); if (m_EvtHandler) { wxCommandEvent event(FRAME_WRITTEN_EVENT); m_EvtHandler->AddPendingEvent(event); } sMutex.unlock(); }
void attemp_10k_increasesR() { int i; mrtx.lock(); mrtx.lock(); // 递归锁,多次调用lock都不会死锁 for (i=0; i<10; ++i) { ++counter; std::this_thread::sleep_for(std::chrono::microseconds(1*1000000)); std::cout << "thread Reverse[" << std::this_thread::get_id() <<"]" << "add the counter" << endl; } mrtx.unlock(); }
FifoPlayerDlg::FifoPlayerDlg(wxWindow * const parent) : wxDialog(parent, wxID_ANY, _("FIFO Player"), wxDefaultPosition, wxDefaultSize), m_FramesToRecord(1) { CreateGUIControls(); sMutex.lock(); m_EvtHandler = GetEventHandler(); sMutex.unlock(); FifoPlayer::GetInstance().SetFileLoadedCallback(FileLoaded); FifoPlayer::GetInstance().SetFrameWrittenCallback(FrameWritten); }
// Read the Wiimote once void Update(int _WiimoteNumber) { // Try to get a lock and return without doing anything if we fail // This avoids deadlocks when adding a Wiimote during continuous scan if(!g_refresh_lock.try_lock()) return; if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->Update(); // Wiimote::Update() may remove the Wiimote if it was disconnected. if (!g_wiimotes[_WiimoteNumber]) { Host_ConnectWiimote(_WiimoteNumber, false); } g_refresh_lock.unlock(); }
// Mix samples from the various audio channels into a single sample queue. // This single sample queue is where __AudioMix should read from. If the sample queue is full, we should // just sleep the main emulator thread a little. void __AudioUpdate() { // Audio throttle doesn't really work on the PSP since the mixing intervals are so closely tied // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. s32 mixBuffer[hwBlockSize * 2]; memset(mixBuffer, 0, sizeof(mixBuffer)); for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX; i++) { if (!chans[i].reserved) continue; if (!chans[i].sampleQueue.size()) { // ERROR_LOG(HLE, "No queued samples, skipping channel %i", i); continue; } for (int s = 0; s < hwBlockSize; s++) { if (chans[i].sampleQueue.size() >= 2) { s16 sampleL = chans[i].sampleQueue.pop_front(); s16 sampleR = chans[i].sampleQueue.pop_front(); mixBuffer[s * 2 + 0] += sampleL; mixBuffer[s * 2 + 1] += sampleR; } else { ERROR_LOG(HLE, "Channel %i buffer underrun at %i of %i", i, s, hwBlockSize); break; } } if (chans[i].sampleQueue.size() < chans[i].sampleCount * 2 * chanQueueMinSizeFactor) { // Ask the thread to send more samples until next time, queue is being drained. if (chans[i].waitingThread) { SceUID waitingThread = chans[i].waitingThread; chans[i].waitingThread = 0; // DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread); __KernelResumeThreadFromWait(waitingThread, chans[i].sampleCount); } } } if (g_Config.bEnableSound) { section.lock(); if (outAudioQueue.room() >= hwBlockSize * 2) { // Push the mixed samples onto the output audio queue. for (int i = 0; i < hwBlockSize; i++) { s32 sampleL = mixBuffer[i * 2 + 0] >> 2; // TODO - what factor? s32 sampleR = mixBuffer[i * 2 + 1] >> 2; outAudioQueue.push((s16)sampleL); outAudioQueue.push((s16)sampleR); } } else {
void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd) { // m_IsRecording is assumed to be true at this point, otherwise this function would not be called sMutex.lock(); m_FrameEnded = true; m_CurrentFrame.fifoStart = fifoStart; m_CurrentFrame.fifoEnd = fifoEnd; if (m_WasRecording) { // If recording a fixed number of frames then check if the end of the recording was reached if (m_RecordFramesRemaining > 0) { --m_RecordFramesRemaining; if (m_RecordFramesRemaining == 0) m_RequestedRecordingEnd = true; } } else { m_WasRecording = true; // Skip the first data which will be the frame copy command m_SkipNextData = true; m_SkipFutureData = false; m_FrameEnded = false; m_FifoData.reserve(1024 * 1024 * 4); m_FifoData.clear(); } if (m_RequestedRecordingEnd) { // Skip data after the next time WriteFifoData is called m_SkipFutureData = true; // Signal video backend that it should not call this function when the next frame ends m_IsRecording = false; } sMutex.unlock(); }
void FifoRecorder::SetVideoMemory(u32 *bpMem, u32 *cpMem, u32 *xfMem, u32 *xfRegs, u32 xfRegsSize) { sMutex.lock(); if (m_File) { memcpy(m_File->GetBPMem(), bpMem, FifoDataFile::BP_MEM_SIZE * 4); memcpy(m_File->GetCPMem(), cpMem, FifoDataFile::CP_MEM_SIZE * 4); memcpy(m_File->GetXFMem(), xfMem, FifoDataFile::XF_MEM_SIZE * 4); u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize); memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4); } m_RecordAnalyzer.Initialize(bpMem, cpMem); sMutex.unlock(); }
static int AVCodecMTLockCallback(void** mutex, AVLockOp op) { switch (op) { case AV_LOCK_CREATE: *mutex = &kAVCodecMTLock; break; case AV_LOCK_OBTAIN: kAVCodecMTLock.lock(); break; case AV_LOCK_RELEASE: kAVCodecMTLock.unlock(); break; case AV_LOCK_DESTROY: *mutex = NULL; break; } return 0; }
FifoPlayerDlg::~FifoPlayerDlg() { Disconnect(RECORDING_FINISHED_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnRecordingFinished), NULL, this); Disconnect(FRAME_WRITTEN_EVENT, wxCommandEventHandler(FifoPlayerDlg::OnFrameWritten), NULL, this); // Disconnect Events Disconnect(wxEVT_PAINT, wxPaintEventHandler(FifoPlayerDlg::OnPaint), NULL, this); m_FrameFromCtrl->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(FifoPlayerDlg::OnFrameFrom), NULL, this); m_FrameToCtrl->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(FifoPlayerDlg::OnFrameTo), NULL, this); m_ObjectFromCtrl->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(FifoPlayerDlg::OnObjectFrom), NULL, this); m_ObjectToCtrl->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(FifoPlayerDlg::OnObjectTo), NULL, this); m_EarlyMemoryUpdates->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(FifoPlayerDlg::OnCheckEarlyMemoryUpdates), NULL, this); m_RecordStop->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FifoPlayerDlg::OnRecordStop), NULL, this); m_Save->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FifoPlayerDlg::OnSaveFile), NULL, this); m_FramesToRecordCtrl->Disconnect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(FifoPlayerDlg::OnNumFramesToRecord), NULL, this); m_Close->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FifoPlayerDlg::OnCloseClick), NULL, this); FifoPlayer::GetInstance().SetFrameWrittenCallback(NULL); sMutex.lock(); m_EvtHandler = NULL; sMutex.unlock(); }
void FifoRecorder::WriteGPCommand(const u8 *data, u32 size) { if (!m_SkipNextData) { m_RecordAnalyzer.AnalyzeGPCommand(data); // Copy data to buffer size_t currentSize = m_FifoData.size(); m_FifoData.resize(currentSize + size); memcpy(&m_FifoData[currentSize], data, size); } if (m_FrameEnded && m_FifoData.size() > 0) { size_t dataSize = m_FifoData.size(); m_CurrentFrame.fifoDataSize = (u32)dataSize; m_CurrentFrame.fifoData = new u8[dataSize]; memcpy(m_CurrentFrame.fifoData, m_FifoData.data(), dataSize); sMutex.lock(); // Copy frame to file // The file will be responsible for freeing the memory allocated for each frame's fifoData m_File->AddFrame(m_CurrentFrame); if (m_FinishedCb && m_RequestedRecordingEnd) m_FinishedCb(); sMutex.unlock(); m_CurrentFrame.memoryUpdates.clear(); m_FifoData.clear(); m_FrameEnded = false; } m_SkipNextData = m_SkipFutureData; }
void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb) { sMutex.lock(); delete m_File; m_File = new FifoDataFile; std::fill(m_Ram.begin(), m_Ram.end(), 0); std::fill(m_ExRam.begin(), m_ExRam.end(), 0); m_File->SetIsWii(SConfig::GetInstance().bWii); if (!m_IsRecording) { m_WasRecording = false; m_IsRecording = true; m_RecordFramesRemaining = numFrames; } m_RequestedRecordingEnd = false; m_FinishedCb = finishedCb; sMutex.unlock(); }
LongRunningOperation::~LongRunningOperation() { _operationMutex.unlock(); }
void StartBulkLog(){ log_mutex.lock(); add_timestamp = false; }
void EndBulkLog(){ add_timestamp = true; log_mutex.unlock(); }
// We will inject the stereo here void sys_glAttachShader(GLuint program, GLuint shader) { m_opengl32Mutex.lock(); orig_glCompileShader = (func_glCompileShader)orig_wglGetProcAddress("glCompileShader"); if (orig_glCompileShader == 0x0) add_log("glCompileShader not found !!!!"); orig_glGetShaderiv = (func_glGetShaderiv_t)orig_wglGetProcAddress("glGetShaderiv"); if (orig_glGetShaderiv == 0x0) add_log("glGetShaderiv not found !!!!"); // Add the CRC of the program std::string programSring = std::to_string(program); DWORD progCRC32 = crc32buf(programSring.c_str(), programSring.length()); // Get the correct shader type // Get the instance ShaderManager *shaderManager = ShaderManager::getInstance(); // Depends on what type of shader it is we do stuff if (shaderManager->isShaderType(shader, GL_VERTEX_SHADER)) { //get the original shader Source std::string shaderSource = shaderManager->getShaderSource(shader); // Calculate the CRC32 before we do any injection // Otherwise the CRC32 will change DWORD crc32 = crc32buf(shaderSource.c_str(), shaderSource.length()); crc32 += progCRC32; if (shaderManager->GetVertexInjectionState()) { //Apply the custom shaders // If we failed apply normal injection if (shaderManager->ApplyExceptionShaders(shaderSource, GL_VERTEX_SHADER, crc32) == false) { //Insert Stereo shaderSource = shaderManager->injectStereoScopy(shaderSource, program); } } // Swap the Source shaderManager->applyShaderSource(shader, shaderSource, NULL); // Store it as an existing shader EXISTING_SHADER_T currentShader; currentShader.m_CRC32 = crc32; currentShader.m_programId = program; currentShader.m_shaderId = shader; currentShader.m_shaderSourceCode = shaderSource; currentShader.m_shaderType = GL_VERTEX_SHADER; // Push it back shaderManager->addExistingShaderInfo(currentShader); // Export the Source ExportShader("Vertex", currentShader.m_programId, currentShader.m_CRC32, currentShader.m_shaderSourceCode); // We also need to compile the shader before attaching it //Compile shader (*orig_glCompileShader)(shader); //Test compile GLint statusOk = 0; (*orig_glGetShaderiv)(shader, GL_COMPILE_STATUS, &statusOk); if (statusOk == GL_FALSE) { CheckCompileState(shader); } } else if (shaderManager->isShaderType(shader, GL_FRAGMENT_SHADER)) { //get the original shader Source std::string shaderSource = shaderManager->getShaderSource(shader); // Calculate the CRC32 DWORD crc32 = crc32buf(shaderSource.c_str(), shaderSource.length()); crc32 += progCRC32; if (shaderManager->GetVertexInjectionState()) { //Apply the custom shaders // If we failed apply normal injection if (shaderManager->ApplyExceptionShaders(shaderSource, GL_FRAGMENT_SHADER, crc32) == false) { // This only happens in development mode. #ifdef DEBUG_WRAPPER //Insert Stereo shaderSource = shaderManager->injectFragmentModification(shaderSource, program); #endif } } // Swap the Source shaderManager->applyShaderSource(shader, shaderSource, NULL); // Store it as an existing shader EXISTING_SHADER_T currentShader; currentShader.m_CRC32 = crc32; currentShader.m_programId = program; currentShader.m_shaderId = shader; currentShader.m_shaderSourceCode = shaderSource; currentShader.m_shaderType = GL_FRAGMENT_SHADER; // Push it back shaderManager->addExistingShaderInfo(currentShader); // Export the Source ExportShader("Pixel", currentShader.m_programId, currentShader.m_CRC32, currentShader.m_shaderSourceCode); //Compile shader (*orig_glCompileShader)(shader); // We also need to compile the shader before attaching it //Test compile GLint statusOk = 0; (*orig_glGetShaderiv)(shader, GL_COMPILE_STATUS, &statusOk); if (statusOk == GL_FALSE) { CheckCompileState(shader); } } // We attach after we swapped the sources (*orig_glAttachShader)(program, shader); m_opengl32Mutex.unlock(); }
// add the stereo values void sys_glUseProgram(GLuint program) { m_opengl32Mutex.lock(); // Get the shaderManager ShaderManager * shaderManager = ShaderManager::getInstance(); #ifdef DEBUG_WRAPPER if (shaderManager->GetVertexInjectionState()) { shaderManager->ApplyDebugExceptionShaders(); } #endif // Use the original program (*orig_glUseProgram)(program); if (shaderManager->GetVertexInjectionState()) { // Get the locations of the uniforms GLfloat value; const GLchar *uniform_eye = (GLchar*)"g_eye"; const GLchar *uniform_eye_spearation = (GLchar*)"g_eye_separation"; const GLchar *uniform_convergence = (GLchar*)"g_convergence"; const GLchar *uniform_vertexEnabled = (GLchar*)"g_vertexEnabled"; const GLchar *uniform_pixelEnabled = (GLchar*)"g_pixelEnabled"; // Get our functions from the driver. Be sure to only take them once if (orig_glGetUniformLocation == NULL) { orig_glGetUniformLocation = (func_glGetUniformLocation_t)(orig_wglGetProcAddress)("glGetUniformLocation"); } //glUniform1f if (orig_glUniform1f == NULL) { orig_glUniform1f = (func_glUniform1f_t)(orig_wglGetProcAddress)("glUniform1f"); } if ((GLint)program >= 0) { GLint location_eye = (*orig_glGetUniformLocation)(program, uniform_eye); GLint location_eye_separation = (*orig_glGetUniformLocation)(program, uniform_eye_spearation); GLint location_convergence = (*orig_glGetUniformLocation)(program, uniform_convergence); GLint location_vertexEnabled = (*orig_glGetUniformLocation)(program, uniform_vertexEnabled); GLint location_pixelEnabled = (*orig_glGetUniformLocation)(program, uniform_pixelEnabled); //set the uniform inside the shaders if (NV3DVisionGetCurrentFrame() == 1) { //left eye value = -1.0f; (*orig_glUniform1f)(location_eye, value); } else { //right eye value = +1.0f; (*orig_glUniform1f)(location_eye, value); } //Set the Separation and convergence (*orig_glUniform1f)(location_eye_separation, Get3DEyeSeparation()); (*orig_glUniform1f)(location_convergence, Get3DConvergence()); (*orig_glUniform1f)(location_vertexEnabled, 1.0f); (*orig_glUniform1f)(location_pixelEnabled, 1.0f); } ///////////////////////////////////// // APPLY CUSTOM SHADER PARAMS ///////////////////////////////////// shaderManager->ApplyCustomShaderParams(program); ///////////////////////////////////// // USED TO DISABLE ALOT OF SHADERS ///////////////////////////////////// #ifdef DEBUG_WRAPPER if (shaderManager->VertexShadersExceptionsEnabled()) { if (((GLint)program >= (GLint)shaderManager->GetExceptionShaderStart() && (GLint)program <= (GLint)shaderManager->GetExceptionShaderEnd())) { GLint location_eye_separation = (*orig_glGetUniformLocation)(program, uniform_eye_spearation); GLint location_convergence = (*orig_glGetUniformLocation)(program, uniform_convergence); //Set the Separation and convergence (*orig_glUniform1f)(location_eye_separation, 0); (*orig_glUniform1f)(location_convergence, 0); // Do we disable the vertex shaders ??? if (shaderManager->VertexShaderExceptionsDisableShader()) { GLint location_enabled = (*orig_glGetUniformLocation)(program, uniform_vertexEnabled); (*orig_glUniform1f)(location_enabled, 0.0f); } else { GLint location_enabled = (*orig_glGetUniformLocation)(program, uniform_vertexEnabled); (*orig_glUniform1f)(location_enabled, 1.0f); } } if (shaderManager->DisableCurrentShader()) { // If we are navigating vertexes if (isCurrentShaderVertex) { // Disable the current Shader if (debugVertexIndex == (int)program) { GLint location_enabled = (*orig_glGetUniformLocation)(program, uniform_vertexEnabled); (*orig_glUniform1f)(location_enabled, 0.0f); } } else // Pixel Shader { // Disable the current Shader if (debugPixelIndex == (int)program) { GLint location_enabled = (*orig_glGetUniformLocation)(program, uniform_pixelEnabled); (*orig_glUniform1f)(location_enabled, 0.0f); } } } } #endif } m_opengl32Mutex.unlock(); }
// Wanting to avoid include pollution, MemMap.h is included a lot. MemoryInitedLock::MemoryInitedLock() { g_shutdownLock.lock(); }
MemoryInitedLock::~MemoryInitedLock() { g_shutdownLock.unlock(); }
namespace Memory { // The base pointer to the auto-mirrored arena. u8* base = NULL; // The MemArena class MemArena g_arena; // ============== u8 *m_pPhysicalScratchPad; u8 *m_pUncachedScratchPad; // 64-bit: Pointers to high-mem mirrors // 32-bit: Same as above u8 *m_pPhysicalRAM; u8 *m_pUncachedRAM; u8 *m_pKernelRAM; // RAM mirrored up to "kernel space". Fully accessible at all times currently. u8 *m_pPhysicalRAM2; u8 *m_pUncachedRAM2; u8 *m_pKernelRAM2; u8 *m_pPhysicalRAM3; u8 *m_pUncachedRAM3; u8 *m_pKernelRAM3; // VRAM is mirrored 4 times. The second and fourth mirrors are swizzled. // In practice, a game accessing the mirrors most likely is deswizzling the depth buffer. u8 *m_pPhysicalVRAM1; u8 *m_pPhysicalVRAM2; u8 *m_pPhysicalVRAM3; u8 *m_pPhysicalVRAM4; u8 *m_pUncachedVRAM1; u8 *m_pUncachedVRAM2; u8 *m_pUncachedVRAM3; u8 *m_pUncachedVRAM4; // Holds the ending address of the PSP's user space. // Required for HD Remasters to work properly. // This replaces RAM_NORMAL_SIZE at runtime. u32 g_MemorySize; // Used to store the PSP model on game startup. u32 g_PSPModel; std::recursive_mutex g_shutdownLock; // We don't declare the IO region in here since its handled by other means. static MemoryView views[] = { {&m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0}, {&m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS}, {&m_pPhysicalVRAM1, 0x04000000, 0x00200000, 0}, {&m_pPhysicalVRAM2, 0x04200000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pPhysicalVRAM3, 0x04400000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pPhysicalVRAM4, 0x04600000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pUncachedVRAM1, 0x44000000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pUncachedVRAM2, 0x44200000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pUncachedVRAM3, 0x44400000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pUncachedVRAM4, 0x44600000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pPhysicalRAM, 0x08000000, g_MemorySize, MV_IS_PRIMARY_RAM}, // only from 0x08800000 is it usable (last 24 megs) {&m_pUncachedRAM, 0x48000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, {&m_pKernelRAM, 0x88000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM | MV_KERNEL}, // Starts at memory + 31 MB. {&m_pPhysicalRAM2, 0x09F00000, g_MemorySize, MV_IS_EXTRA1_RAM}, {&m_pUncachedRAM2, 0x49F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM}, {&m_pKernelRAM2, 0x89F00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA1_RAM | MV_KERNEL}, // Starts at memory + 31 * 2 MB. {&m_pPhysicalRAM3, 0x0BE00000, g_MemorySize, MV_IS_EXTRA2_RAM}, {&m_pUncachedRAM3, 0x4BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM}, {&m_pKernelRAM3, 0x8BE00000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_EXTRA2_RAM | MV_KERNEL}, // TODO: There are a few swizzled mirrors of VRAM, not sure about the best way to // implement those. }; static const int num_views = sizeof(views) / sizeof(MemoryView); inline static bool CanIgnoreView(const MemoryView &view) { #if PPSSPP_ARCH(32BIT) // Basically, 32-bit platforms can ignore views that are masked out anyway. return (view.flags & MV_MIRROR_PREVIOUS) && (view.virtual_address & ~MEMVIEW32_MASK) != 0; #else return false; #endif } #if defined(IOS) && PPSSPP_ARCH(64BIT) #define SKIP(a_flags, b_flags) \ if ((b_flags) & MV_KERNEL) \ continue; #else #define SKIP(a_flags, b_flags) \ ; #endif static bool Memory_TryBase(u32 flags) { // OK, we know where to find free space. Now grab it! // We just mimic the popular BAT setup. size_t position = 0; size_t last_position = 0; // Zero all the pointers to be sure. for (int i = 0; i < num_views; i++) { if (views[i].out_ptr) *views[i].out_ptr = 0; } int i; for (i = 0; i < num_views; i++) { const MemoryView &view = views[i]; if (view.size == 0) continue; SKIP(flags, view.flags); if (view.flags & MV_MIRROR_PREVIOUS) { position = last_position; } #ifndef MASKED_PSP_MEMORY *view.out_ptr = (u8*)g_arena.CreateView( position, view.size, base + view.virtual_address); if (!*view.out_ptr) { goto bail; DEBUG_LOG(MEMMAP, "Failed at view %d", i); } #else if (CanIgnoreView(view)) { // This is handled by address masking in 32-bit, no view needs to be created. *view.out_ptr = *views[i - 1].out_ptr; } else { *view.out_ptr = (u8*)g_arena.CreateView( position, view.size, base + (view.virtual_address & MEMVIEW32_MASK)); if (!*view.out_ptr) { DEBUG_LOG(MEMMAP, "Failed at view %d", i); goto bail; } } #endif last_position = position; position += g_arena.roundup(view.size); } return true; bail: // Argh! ERROR! Free what we grabbed so far so we can try again. for (int j = 0; j <= i; j++) { if (views[i].size == 0) continue; SKIP(flags, views[i].flags); if (*views[j].out_ptr) { if (!CanIgnoreView(views[j])) { g_arena.ReleaseView(*views[j].out_ptr, views[j].size); } *views[j].out_ptr = NULL; } } return false; } bool MemoryMap_Setup(u32 flags) { #if PPSSPP_PLATFORM(UWP) // We reserve the memory, then simply commit in TryBase. base = (u8*)VirtualAllocFromApp(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE); #else // Figure out how much memory we need to allocate in total. size_t total_mem = 0; for (int i = 0; i < num_views; i++) { if (views[i].size == 0) continue; SKIP(flags, views[i].flags); if (!CanIgnoreView(views[i])) total_mem += g_arena.roundup(views[i].size); } // Grab some pagefile backed memory out of the void ... g_arena.GrabLowMemSpace(total_mem); #endif #if !PPSSPP_PLATFORM(ANDROID) if (g_arena.NeedsProbing()) { int base_attempts = 0; #if defined(_WIN32) && PPSSPP_ARCH(32BIT) // Try a whole range of possible bases. Return once we got a valid one. uintptr_t max_base_addr = 0x7FFF0000 - 0x10000000; uintptr_t min_base_addr = 0x01000000; uintptr_t stride = 0x400000; #else // iOS uintptr_t max_base_addr = 0x1FFFF0000ULL - 0x80000000ULL; uintptr_t min_base_addr = 0x100000000ULL; uintptr_t stride = 0x800000; #endif for (uintptr_t base_addr = min_base_addr; base_addr < max_base_addr; base_addr += stride) { base_attempts++; base = (u8 *)base_addr; if (Memory_TryBase(flags)) { INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); return true; } } ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base."); PanicAlert("MemoryMap_Setup: Failed finding a memory base."); return false; } else #endif { #if !PPSSPP_PLATFORM(UWP) base = g_arena.Find4GBBase(); #endif } // Should return true... return Memory_TryBase(flags); } void MemoryMap_Shutdown(u32 flags) { for (int i = 0; i < num_views; i++) { if (views[i].size == 0) continue; SKIP(flags, views[i].flags); if (*views[i].out_ptr) g_arena.ReleaseView(*views[i].out_ptr, views[i].size); *views[i].out_ptr = nullptr; } g_arena.ReleaseSpace(); #if PPSSPP_PLATFORM(UWP) VirtualFree(base, 0, MEM_RELEASE); #endif } void Init() { // On some 32 bit platforms, you can only map < 32 megs at a time. // TODO: Wait, wtf? What platforms are those? This seems bad. const static int MAX_MMAP_SIZE = 31 * 1024 * 1024; _dbg_assert_msg_(MEMMAP, g_MemorySize < MAX_MMAP_SIZE * 3, "ACK - too much memory for three mmap views."); for (size_t i = 0; i < ARRAY_SIZE(views); i++) { if (views[i].flags & MV_IS_PRIMARY_RAM) views[i].size = std::min((int)g_MemorySize, MAX_MMAP_SIZE); if (views[i].flags & MV_IS_EXTRA1_RAM) views[i].size = std::min(std::max((int)g_MemorySize - MAX_MMAP_SIZE, 0), MAX_MMAP_SIZE); if (views[i].flags & MV_IS_EXTRA2_RAM) views[i].size = std::min(std::max((int)g_MemorySize - MAX_MMAP_SIZE * 2, 0), MAX_MMAP_SIZE); } int flags = 0; MemoryMap_Setup(flags); INFO_LOG(MEMMAP, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)", base, m_pPhysicalRAM, m_pUncachedRAM); } void DoState(PointerWrap &p) { auto s = p.Section("Memory", 1, 3); if (!s) return; if (s < 2) { if (!g_RemasterMode) g_MemorySize = RAM_NORMAL_SIZE; g_PSPModel = PSP_MODEL_FAT; } else if (s == 2) { // In version 2, we determine memory size based on PSP model. u32 oldMemorySize = g_MemorySize; p.Do(g_PSPModel); p.DoMarker("PSPModel"); if (!g_RemasterMode) { g_MemorySize = g_PSPModel == PSP_MODEL_FAT ? RAM_NORMAL_SIZE : RAM_DOUBLE_SIZE; if (oldMemorySize < g_MemorySize) { Shutdown(); Init(); } } } else { // In version 3, we started just saving the memory size directly. // It's no longer based strictly on the PSP model. u32 oldMemorySize = g_MemorySize; p.Do(g_PSPModel); p.DoMarker("PSPModel"); p.Do(g_MemorySize); if (oldMemorySize != g_MemorySize) { Shutdown(); Init(); } } p.DoArray(GetPointer(PSP_GetKernelMemoryBase()), g_MemorySize); p.DoMarker("RAM"); p.DoArray(m_pPhysicalVRAM1, VRAM_SIZE); p.DoMarker("VRAM"); p.DoArray(m_pPhysicalScratchPad, SCRATCHPAD_SIZE); p.DoMarker("ScratchPad"); } void Shutdown() { std::lock_guard<std::recursive_mutex> guard(g_shutdownLock); u32 flags = 0; MemoryMap_Shutdown(flags); base = nullptr; DEBUG_LOG(MEMMAP, "Memory system shut down."); } void Clear() { if (m_pPhysicalRAM) memset(GetPointerUnchecked(PSP_GetKernelMemoryBase()), 0, g_MemorySize); if (m_pPhysicalScratchPad) memset(m_pPhysicalScratchPad, 0, SCRATCHPAD_SIZE); if (m_pPhysicalVRAM1) memset(m_pPhysicalVRAM1, 0, VRAM_SIZE); } bool IsActive() { return base != nullptr; } // Wanting to avoid include pollution, MemMap.h is included a lot. MemoryInitedLock::MemoryInitedLock() { g_shutdownLock.lock(); } MemoryInitedLock::~MemoryInitedLock() { g_shutdownLock.unlock(); } MemoryInitedLock Lock() { return MemoryInitedLock(); } __forceinline static Opcode Read_Instruction(u32 address, bool resolveReplacements, Opcode inst) { if (!MIPS_IS_EMUHACK(inst.encoding)) { return inst; } if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { inst = MIPSComp::jit->GetOriginalOp(inst); if (resolveReplacements && MIPS_IS_REPLACEMENT(inst)) { u32 op; if (GetReplacedOpAt(address, &op)) { if (MIPS_IS_EMUHACK(op)) { ERROR_LOG(MEMMAP, "WTF 1"); return Opcode(op); } else { return Opcode(op); } } else { ERROR_LOG(MEMMAP, "Replacement, but no replacement op? %08x", inst.encoding); } } return inst; } else if (resolveReplacements && MIPS_IS_REPLACEMENT(inst.encoding)) { u32 op; if (GetReplacedOpAt(address, &op)) { if (MIPS_IS_EMUHACK(op)) { ERROR_LOG(MEMMAP, "WTF 2"); return Opcode(op); } else { return Opcode(op); } } else { return inst; } } else { return inst; } } Opcode Read_Instruction(u32 address, bool resolveReplacements) { Opcode inst = Opcode(Read_U32(address)); return Read_Instruction(address, resolveReplacements, inst); } Opcode ReadUnchecked_Instruction(u32 address, bool resolveReplacements) { Opcode inst = Opcode(ReadUnchecked_U32(address)); return Read_Instruction(address, resolveReplacements, inst); } Opcode Read_Opcode_JIT(u32 address) { Opcode inst = Opcode(Read_U32(address)); if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { return MIPSComp::jit->GetOriginalOp(inst); } else { return inst; } } // WARNING! No checks! // We assume that _Address is cached void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value) { Memory::WriteUnchecked_U32(_Value.encoding, _Address); } void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { u8 *ptr = GetPointer(_Address); if (ptr != NULL) { memset(ptr, _iValue, _iLength); } else { for (size_t i = 0; i < _iLength; i++) Write_U8(_iValue, (u32)(_Address + i)); } #ifndef MOBILE_DEVICE CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc); #endif } } // namespace
namespace WiimoteReal { void HandleFoundWiimotes(const std::vector<Wiimote*>&); void TryToConnectBalanceBoard(Wiimote*); void TryToConnectWiimote(Wiimote*); void HandleWiimoteDisconnect(int index); void DoneWithWiimote(int index); static bool g_real_wiimotes_initialized = false; std::recursive_mutex g_refresh_lock; Wiimote* g_wiimotes[MAX_BBMOTES]; WiimoteScanner g_wiimote_scanner; Wiimote::Wiimote() : m_index() , m_last_input_report() , m_channel(0) , m_rumble_state() , m_need_prepare() {} void Wiimote::Shutdown() { StopThread(); ClearReadQueue(); m_write_reports.Clear(); } // to be called from CPU thread void Wiimote::WriteReport(Report rpt) { if (rpt.size() >= 3) { bool const new_rumble_state = (rpt[2] & 0x1) != 0; // If this is a rumble report and the rumble state didn't change, ignore. if (WM_RUMBLE == rpt[1] && new_rumble_state == m_rumble_state) return; m_rumble_state = new_rumble_state; } m_write_reports.Push(std::move(rpt)); IOWakeup(); } // to be called from CPU thread void Wiimote::QueueReport(u8 rpt_id, const void* _data, unsigned int size) { auto const data = static_cast<const u8*>(_data); Report rpt(size + 2); rpt[0] = WM_SET_REPORT | WM_BT_OUTPUT; rpt[1] = rpt_id; std::copy_n(data, size, rpt.begin() + 2); WriteReport(std::move(rpt)); } void Wiimote::DisableDataReporting() { m_last_input_report.clear(); // This probably accomplishes nothing. wm_report_mode rpt = {}; rpt.mode = WM_REPORT_CORE; rpt.all_the_time = 0; rpt.continuous = 0; rpt.rumble = 0; QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt)); } void Wiimote::EnableDataReporting(u8 mode) { m_last_input_report.clear(); wm_report_mode rpt = {}; rpt.mode = mode; rpt.all_the_time = 1; rpt.continuous = 1; QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt)); } void Wiimote::SetChannel(u16 channel) { m_channel = channel; } void Wiimote::ClearReadQueue() { Report rpt; // The "Clear" function isn't thread-safe :/ while (m_read_reports.Pop(rpt)) {} } void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size) { // Check for custom communication if (99 == channel) { EmuStop(); } else { InterruptChannel(channel, data, size); const hid_packet* const hidp = (hid_packet*)data; if (hidp->type == HID_TYPE_SET_REPORT) { u8 handshake_ok = HID_HANDSHAKE_SUCCESS; Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake_ok, sizeof(handshake_ok)); } } } void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const u32 size) { // first interrupt/control channel sent if (channel != m_channel) { m_channel = channel; ClearReadQueue(); EmuStart(); } auto const data = static_cast<const u8*>(_data); Report rpt(data, data + size); WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[m_index]; // Convert output DATA packets to SET_REPORT packets. // Nintendo Wiimotes work without this translation, but 3rd // party ones don't. if (rpt[0] == 0xa2) { rpt[0] = WM_SET_REPORT | WM_BT_OUTPUT; } // Disallow games from turning off all of the LEDs. // It makes Wiimote connection status confusing. if (rpt[1] == WM_LEDS) { auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt[2]); if (0 == leds_rpt.leds) { // Turn on ALL of the LEDs. leds_rpt.leds = 0xf; } } else if (rpt[1] == WM_WRITE_SPEAKER_DATA && (!SConfig::GetInstance().m_WiimoteEnableSpeaker || (!wm->m_status.speaker || wm->m_speaker_mute))) { // Translate speaker data reports into rumble reports. rpt[1] = WM_RUMBLE; // Keep only the rumble bit. rpt[2] &= 0x1; rpt.resize(3); } WriteReport(std::move(rpt)); } bool Wiimote::Read() { Report rpt(MAX_PAYLOAD); auto const result = IORead(rpt.data()); if (result > 0 && m_channel > 0) { if (SConfig::GetInstance().iBBDumpPort > 0 && m_index == WIIMOTE_BALANCE_BOARD) { static sf::UdpSocket Socket; Socket.send((char*)rpt.data(), rpt.size(), sf::IpAddress::LocalHost, SConfig::GetInstance().iBBDumpPort); } // Add it to queue rpt.resize(result); m_read_reports.Push(std::move(rpt)); return true; } else if (0 == result) { ERROR_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", m_index + 1); DisconnectInternal(); } return false; } bool Wiimote::Write() { if (!m_write_reports.Empty()) { Report const& rpt = m_write_reports.Front(); bool const is_speaker_data = rpt[1] == WM_WRITE_SPEAKER_DATA; if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5) { if (SConfig::GetInstance().iBBDumpPort > 0 && m_index == WIIMOTE_BALANCE_BOARD) { static sf::UdpSocket Socket; Socket.send((char*)rpt.data(), rpt.size(), sf::IpAddress::LocalHost, SConfig::GetInstance().iBBDumpPort); } IOWrite(rpt.data(), rpt.size()); if (is_speaker_data) { m_last_audio_report.Update(); } m_write_reports.Pop(); return true; } } return false; } static bool IsDataReport(const Report& rpt) { return rpt.size() >= 2 && rpt[1] >= WM_REPORT_CORE; } // Returns the next report that should be sent const Report& Wiimote::ProcessReadQueue() { // Pop through the queued reports while (m_read_reports.Pop(m_last_input_report)) { if (!IsDataReport(m_last_input_report)) { // A non-data report, use it. return m_last_input_report; // Forget the last data report as it may be of the wrong type // or contain outdated button data // or it's not supposed to be sent at this time // It's just easier to be correct this way and it's probably not horrible. } } // If the last report wasn't a data report it's irrelevant. if (!IsDataReport(m_last_input_report)) m_last_input_report.clear(); // If it was a data report, we repeat that until something else comes in. return m_last_input_report; } void Wiimote::Update() { if (!IsConnected()) { HandleWiimoteDisconnect(m_index); return; } // Pop through the queued reports const Report& rpt = ProcessReadQueue(); // Send the report if (!rpt.empty() && m_channel > 0) { Core::Callback_WiimoteInterruptChannel(m_index, m_channel, rpt.data(), (u32)rpt.size()); } } void Wiimote::Prepare(int _index) { m_index = _index; m_need_prepare.store(true); } bool Wiimote::PrepareOnThread() { // core buttons, no continuous reporting u8 static const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE}; // Set the active LEDs and turn on rumble. u8 static const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << (m_index%WIIMOTE_BALANCE_BOARD) | 0x1)}; // Turn off rumble u8 static const rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0}; // Request status report u8 static const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0}; // TODO: check for sane response? return (IOWrite(mode_report, sizeof(mode_report)) && IOWrite(led_report, sizeof(led_report)) && (SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))) && IOWrite(req_status_report, sizeof(req_status_report))); } void Wiimote::EmuStart() { DisableDataReporting(); EnablePowerAssertionInternal(); } void Wiimote::EmuStop() { m_channel = 0; DisableDataReporting(); NOTICE_LOG(WIIMOTE, "Stopping Wiimote data reporting."); DisablePowerAssertionInternal(); } void Wiimote::EmuResume() { WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[m_index]; m_last_input_report.clear(); wm_report_mode rpt = {}; rpt.mode = wm->m_reporting_mode; rpt.all_the_time = 1; rpt.continuous = 1; QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Resuming Wiimote data reporting."); EnablePowerAssertionInternal(); } void Wiimote::EmuPause() { m_last_input_report.clear(); wm_report_mode rpt = {}; rpt.mode = WM_REPORT_CORE; rpt.all_the_time = 0; rpt.continuous = 0; QueueReport(WM_REPORT_MODE, &rpt, sizeof(rpt)); NOTICE_LOG(WIIMOTE, "Pausing Wiimote data reporting."); DisablePowerAssertionInternal(); } static unsigned int CalculateConnectedWiimotes() { unsigned int connected_wiimotes = 0; for (unsigned int i = 0; i < MAX_WIIMOTES; ++i) if (g_wiimotes[i]) ++connected_wiimotes; return connected_wiimotes; } static unsigned int CalculateWantedWiimotes() { // Figure out how many real Wiimotes are required unsigned int wanted_wiimotes = 0; for (unsigned int i = 0; i < MAX_WIIMOTES; ++i) if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && !g_wiimotes[i]) ++wanted_wiimotes; return wanted_wiimotes; } static unsigned int CalculateWantedBB() { unsigned int wanted_bb = 0; if (WIIMOTE_SRC_REAL & g_wiimote_sources[WIIMOTE_BALANCE_BOARD] && !g_wiimotes[WIIMOTE_BALANCE_BOARD]) ++wanted_bb; return wanted_bb; } void WiimoteScanner::WantWiimotes(bool do_want) { m_want_wiimotes.store(do_want); } void WiimoteScanner::WantBB(bool do_want) { m_want_bb.store(do_want); } void WiimoteScanner::StartScanning() { if (!m_run_thread.load()) { m_run_thread.store(true); m_scan_thread = std::thread(&WiimoteScanner::ThreadFunc, this); } } void WiimoteScanner::StopScanning() { m_run_thread.store(false); if (m_scan_thread.joinable()) { m_scan_thread.join(); } } static void CheckForDisconnectedWiimotes() { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); for (unsigned int i = 0; i < MAX_BBMOTES; ++i) if (g_wiimotes[i] && !g_wiimotes[i]->IsConnected()) HandleWiimoteDisconnect(i); } void WiimoteScanner::ThreadFunc() { Common::SetCurrentThreadName("Wiimote Scanning Thread"); NOTICE_LOG(WIIMOTE, "Wiimote scanning has started."); while (m_run_thread.load()) { std::vector<Wiimote*> found_wiimotes; Wiimote* found_board = nullptr; //NOTICE_LOG(WIIMOTE, "In loop"); if (m_want_wiimotes.load() || m_want_bb.load()) { FindWiimotes(found_wiimotes, found_board); } else { // Does stuff needed to detect disconnects on Windows Update(); } //NOTICE_LOG(WIIMOTE, "After update"); // TODO: this is a fairly lame place for this CheckForDisconnectedWiimotes(); if (m_want_wiimotes.load()) HandleFoundWiimotes(found_wiimotes); if (m_want_bb.load() && found_board) TryToConnectBalanceBoard(found_board); //std::this_thread::yield(); Common::SleepCurrentThread(500); } NOTICE_LOG(WIIMOTE, "Wiimote scanning has stopped."); } bool Wiimote::Connect() { if (!m_run_thread.load()) { m_thread_ready.store(false); StartThread(); WaitReady(); } return IsConnected(); } void Wiimote::StartThread() { m_run_thread.store(true); m_wiimote_thread = std::thread(&Wiimote::ThreadFunc, this); } void Wiimote::StopThread() { m_run_thread.store(false); IOWakeup(); if (m_wiimote_thread.joinable()) m_wiimote_thread.join(); } void Wiimote::SetReady() { if (!m_thread_ready.load()) { m_thread_ready.store(true); m_thread_ready_cond.notify_all(); } } void Wiimote::WaitReady() { std::unique_lock<std::mutex> lock(m_thread_ready_mutex); while (!m_thread_ready.load()) { m_thread_ready_cond.wait(lock); } } void Wiimote::ThreadFunc() { Common::SetCurrentThreadName("Wiimote Device Thread"); bool ok = ConnectInternal(); if (!ok) { // try again, it might take a moment to settle Common::SleepCurrentThread(100); ok = ConnectInternal(); } SetReady(); if (!ok) { return; } // main loop while (IsConnected() && m_run_thread.load()) { if (m_need_prepare.load()) { m_need_prepare.store(false); if (!PrepareOnThread()) { ERROR_LOG(WIIMOTE, "Wiimote::PrepareOnThread failed. Disconnecting Wiimote %d.", m_index + 1); break; } } Write(); Read(); } DisconnectInternal(); } void LoadSettings() { std::string ini_filename = File::GetUserPath(D_CONFIG_IDX) + WIIMOTE_INI_NAME ".ini"; IniFile inifile; inifile.Load(ini_filename); for (unsigned int i=0; i<MAX_WIIMOTES; ++i) { std::string secname("Wiimote"); secname += (char)('1' + i); IniFile::Section& sec = *inifile.GetOrCreateSection(secname); sec.Get("Source", &g_wiimote_sources[i], i ? WIIMOTE_SRC_NONE : WIIMOTE_SRC_EMU); } std::string secname("BalanceBoard"); IniFile::Section& sec = *inifile.GetOrCreateSection(secname); sec.Get("Source", &g_wiimote_sources[WIIMOTE_BALANCE_BOARD], WIIMOTE_SRC_NONE); } // config dialog calls this when some settings change void Initialize(bool wait) { if (SConfig::GetInstance().m_WiimoteContinuousScanning) g_wiimote_scanner.StartScanning(); else g_wiimote_scanner.StopScanning(); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes()); g_wiimote_scanner.WantBB(0 != CalculateWantedBB()); // wait for connection because it should exist before state load if (wait) { int timeout = 100; std::vector<Wiimote*> found_wiimotes; Wiimote* found_board = nullptr; g_wiimote_scanner.FindWiimotes(found_wiimotes, found_board); if (SConfig::GetInstance().m_WiimoteContinuousScanning) { while (CalculateWantedWiimotes() && CalculateConnectedWiimotes() < found_wiimotes.size() && timeout) { Common::SleepCurrentThread(100); timeout--; } } } if (g_real_wiimotes_initialized) return; NOTICE_LOG(WIIMOTE, "WiimoteReal::Initialize"); g_real_wiimotes_initialized = true; } // called on emulation shutdown void Stop() { for (auto& wiimote : g_wiimotes) if (wiimote && wiimote->IsConnected()) wiimote->EmuStop(); } // called when the Dolphin app exits void Shutdown() { g_wiimote_scanner.StopScanning(); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); if (!g_real_wiimotes_initialized) return; NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown"); g_real_wiimotes_initialized = false; for (unsigned int i = 0; i < MAX_BBMOTES; ++i) HandleWiimoteDisconnect(i); } void Resume() { for (auto& wiimote : g_wiimotes) if (wiimote && wiimote->IsConnected()) wiimote->EmuResume(); } void Pause() { for (auto& wiimote : g_wiimotes) if (wiimote && wiimote->IsConnected()) wiimote->EmuPause(); } void ChangeWiimoteSource(unsigned int index, int source) { { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); g_wiimote_sources[index] = source; g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes()); g_wiimote_scanner.WantBB(0 != CalculateWantedBB()); // kill real connection (or swap to different slot) DoneWithWiimote(index); } // reconnect to the emulator Host_ConnectWiimote(index, false); if (WIIMOTE_SRC_EMU & source) Host_ConnectWiimote(index, true); } static bool TryToConnectWiimoteN(Wiimote* wm, unsigned int i) { if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && !g_wiimotes[i]) { if (wm->Connect()) { wm->Prepare(i); NOTICE_LOG(WIIMOTE, "Connected to Wiimote %i.", i + 1); g_wiimotes[i] = wm; Host_ConnectWiimote(i, true); } return true; } return false; } void TryToConnectWiimote(Wiimote* wm) { std::unique_lock<std::recursive_mutex> lk(g_refresh_lock); for (unsigned int i = 0; i < MAX_WIIMOTES; ++i) { if (TryToConnectWiimoteN(wm, i)) { wm = nullptr; break; } } g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes()); lk.unlock(); delete wm; } void TryToConnectBalanceBoard(Wiimote* wm) { std::unique_lock<std::recursive_mutex> lk(g_refresh_lock); if (TryToConnectWiimoteN(wm, WIIMOTE_BALANCE_BOARD)) { wm = nullptr; } g_wiimote_scanner.WantBB(0 != CalculateWantedBB()); lk.unlock(); delete wm; } void DoneWithWiimote(int index) { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); Wiimote* wm = g_wiimotes[index]; if (wm) { g_wiimotes[index] = nullptr; // First see if we can use this real Wiimote in another slot. TryToConnectWiimote(wm); } // else, just disconnect the Wiimote HandleWiimoteDisconnect(index); } void HandleWiimoteDisconnect(int index) { Wiimote* wm = nullptr; { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::swap(wm, g_wiimotes[index]); g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes()); g_wiimote_scanner.WantBB(0 != CalculateWantedBB()); } if (wm) { delete wm; NOTICE_LOG(WIIMOTE, "Disconnected Wiimote %i.", index + 1); } } void HandleFoundWiimotes(const std::vector<Wiimote*>& wiimotes) { std::for_each(wiimotes.begin(), wiimotes.end(), TryToConnectWiimote); } // This is called from the GUI thread void Refresh() { g_wiimote_scanner.StopScanning(); { std::unique_lock<std::recursive_mutex> lk(g_refresh_lock); std::vector<Wiimote*> found_wiimotes; Wiimote* found_board = nullptr; if (0 != CalculateWantedWiimotes() || 0 != CalculateWantedBB()) { // Don't hang Dolphin when searching lk.unlock(); g_wiimote_scanner.FindWiimotes(found_wiimotes, found_board); lk.lock(); } CheckForDisconnectedWiimotes(); // Brief rumble for already connected Wiimotes. // Don't do this for Balance Board as it doesn't have rumble anyway. for (int i = 0; i < MAX_WIIMOTES; ++i) { if (g_wiimotes[i]) { g_wiimotes[i]->Prepare(i); } } HandleFoundWiimotes(found_wiimotes); if (found_board) TryToConnectBalanceBoard(found_board); } Initialize(); } void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size) { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, _pData, _Size); } void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size) { std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->ControlChannel(_channelID, _pData, _Size); } // Read the Wiimote once void Update(int _WiimoteNumber) { // Try to get a lock and return without doing anything if we fail // This avoids deadlocks when adding a Wiimote during continuous scan if(!g_refresh_lock.try_lock()) return; if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->Update(); // Wiimote::Update() may remove the Wiimote if it was disconnected. if (!g_wiimotes[_WiimoteNumber]) { Host_ConnectWiimote(_WiimoteNumber, false); } g_refresh_lock.unlock(); } void StateChange(EMUSTATE_CHANGE newState) { //std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); // TODO: disable/enable auto reporting, maybe } bool IsValidBluetoothName(const std::string& name) { return "Nintendo RVL-CNT-01" == name || "Nintendo RVL-CNT-01-TR" == name || IsBalanceBoardName(name); } bool IsBalanceBoardName(const std::string& name) { return "Nintendo RVL-WBC-01" == name; } }; // end of namespace