void VideoBackendHardware::InitializeShared() { VideoCommon_Init(); s_swapRequested.Clear(); s_efbAccessRequested.Clear(); s_perfQueryRequested.Clear(); s_FifoShuttingDown.Clear(); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); memset(&s_accessEFBArgs, 0, sizeof(s_accessEFBArgs)); s_AccessEFBResult = 0; m_invalid = false; }
void Host_Message(int Id) { if (Id == WM_USER_STOP) { s_running.Clear(); updateMainFrameEvent.Set(); } }
void VideoBackendHardware::InitializeShared() { VideoCommon_Init(); s_FifoShuttingDown.Clear(); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); m_invalid = false; }
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(); } }
// Run from the graphics thread (from Fifo.cpp) static void VideoFifo_CheckSwapRequest() { if (g_ActiveConfig.bUseXFB) { if (s_swapRequested.IsSet()) { EFBRectangle rc; Renderer::Swap(s_beginFieldArgs.xfbAddr, s_beginFieldArgs.fbWidth, s_beginFieldArgs.fbHeight,rc); s_swapRequested.Clear(); } } }
void VideoBackendBase::InitializeShared() { memset(&g_main_cp_state, 0, sizeof(g_main_cp_state)); memset(&g_preprocess_cp_state, 0, sizeof(g_preprocess_cp_state)); memset(texMem, 0, TMEM_SIZE); // Do our OSD callbacks OSD::DoCallbacks(OSD::CallbackType::Initialization); // do not initialize again for the config window m_initialized = true; s_FifoShuttingDown.Clear(); memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); m_invalid = false; frameCount = 0; CommandProcessor::Init(); Fifo::Init(); OpcodeDecoder::Init(); PixelEngine::Init(); BPInit(); VertexLoaderManager::Init(); IndexGenerator::Init(); VertexShaderManager::Init(); GeometryShaderManager::Init(); PixelShaderManager::Init(); g_Config.Load(File::GetUserPath(D_CONFIG_IDX) + "GFX.ini"); g_Config.GameIniLoad(); g_Config.UpdateProjectionHack(); g_Config.VerifyValidity(); UpdateActiveConfig(); // Notify the core that the video backend is ready Host_Message(WM_USER_CREATE); }
void HiresTexture::Update() { bool BuildMaterialMaps = g_ActiveConfig.bHiresMaterialMapsBuild; if (s_prefetcher.joinable()) { s_textureCacheAbortLoading.Set(); s_prefetcher.join(); } if (!g_ActiveConfig.bHiresTextures) { s_textureMap.clear(); s_textureCache.clear(); size_sum.store(0); return; } if (!g_ActiveConfig.bCacheHiresTextures) { s_textureCache.clear(); size_sum.store(0); } s_textureMap.clear(); const std::string& game_id = SConfig::GetInstance().GetGameID(); const std::string texture_directory = GetTextureDirectory(game_id); std::string ddscode(".dds"); std::string cddscode(".DDS"); std::vector<std::string> Extensions; Extensions.push_back(".png"); if (!BuildMaterialMaps) { Extensions.push_back(".dds"); } std::vector<std::string> filenames = Common::DoFileSearch({texture_directory}, Extensions, /*recursive*/ true); const std::string miptag = "mip"; for (const std::string& fileitem : filenames) { std::string filename; std::string Extension; SplitPath(fileitem, nullptr, &filename, &Extension); if (filename.substr(0, s_format_prefix.length()) != s_format_prefix) { // Discard wrong files continue; } size_t map_index = 0; size_t max_type = BuildMaterialMaps ? MapType::specular : MapType::normal; bool arbitrary_mips = false; for (size_t tag = 1; tag <= MapType::specular; tag++) { if (StringEndsWith(filename, s_maps_tags[tag])) { map_index = tag; filename = filename.substr(0, filename.size() - s_maps_tags[tag].size()); break; } } if (map_index > max_type) { continue; } if (BuildMaterialMaps && map_index == MapType::material) { continue; } else if (!BuildMaterialMaps && map_index == MapType::color) { const size_t arb_index = filename.rfind("_arb"); arbitrary_mips = arb_index != std::string::npos; if (arbitrary_mips) filename.erase(arb_index, 4); } const bool is_compressed = Extension.compare(ddscode) == 0 || Extension.compare(cddscode) == 0; hires_mip_level mip_level_detail(fileitem, Extension, is_compressed); u32 level = 0; size_t idx = filename.find_last_of('_'); std::string miplevel = filename.substr(idx + 1, std::string::npos); if (miplevel.substr(0, miptag.length()) == miptag) { sscanf(miplevel.substr(3, std::string::npos).c_str(), "%i", &level); filename = filename.substr(0, idx); } HiresTextureCache::iterator iter = s_textureMap.find(filename); u32 min_item_size = level + 1; if (iter == s_textureMap.end()) { HiresTextureCacheItem item(min_item_size); if (arbitrary_mips) { item.has_arbitrary_mips = true; } item.maps[map_index].resize(min_item_size); std::vector<hires_mip_level>& dst = item.maps[map_index]; dst[level] = mip_level_detail; s_textureMap.emplace(filename, item); } else { std::vector<hires_mip_level>& dst = iter->second.maps[map_index]; if (arbitrary_mips) { iter->second.has_arbitrary_mips = true; } if (dst.size() < min_item_size) { dst.resize(min_item_size); } dst[level] = mip_level_detail; } } if (g_ActiveConfig.bCacheHiresTextures && s_textureMap.size() > 0) { // remove cached but deleted textures auto iter = s_textureCache.begin(); while (iter != s_textureCache.end()) { if (s_textureMap.find(iter->first) == s_textureMap.end()) { size_sum.fetch_sub(iter->second->m_cached_data_size); iter = s_textureCache.erase(iter); } else { iter++; } } s_textureCacheAbortLoading.Clear(); s_prefetcher = std::thread(Prefetch); if (g_ActiveConfig.bWaitForCacheHiresTextures && s_prefetcher.joinable()) { s_prefetcher.join(); } } }
int main(int argc, char* argv[]) { auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions); optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv); std::vector<std::string> args = parser->args(); std::string boot_filename; if (options.is_set("exec")) { boot_filename = static_cast<const char*>(options.get("exec")); } else if (args.size()) { boot_filename = args.front(); args.erase(args.begin()); } else { parser->print_help(); return 0; } std::string user_directory; if (options.is_set("user")) { user_directory = static_cast<const char*>(options.get("user")); } platform = GetPlatform(); if (!platform) { fprintf(stderr, "No platform found\n"); return 1; } UICommon::SetUserDirectory(user_directory); UICommon::Init(); Core::SetOnStoppedCallback([]() { s_running.Clear(); }); platform->Init(); // Shut down cleanly on SIGINT and SIGTERM struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sa, nullptr); sigaction(SIGTERM, &sa, nullptr); DolphinAnalytics::Instance()->ReportDolphinStart("nogui"); if (!BootManager::BootCore(BootParameters::GenerateFromFile(boot_filename))) { fprintf(stderr, "Could not boot %s\n", boot_filename.c_str()); return 1; } while (!Core::IsRunning() && s_running.IsSet()) { Core::HostDispatchJobs(); updateMainFrameEvent.Wait(); } if (s_running.IsSet()) platform->MainLoop(); Core::Stop(); Core::Shutdown(); platform->Shutdown(); UICommon::Shutdown(); delete platform; return 0; }
void MainLoop() override { bool fullscreen = SConfig::GetInstance().bFullscreen; int last_window_width = SConfig::GetInstance().iRenderWindowWidth; int last_window_height = SConfig::GetInstance().iRenderWindowHeight; if (fullscreen) { rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win); #if defined(HAVE_XRANDR) && HAVE_XRANDR XRRConfig->ToggleDisplayMode(True); #endif } // The actual loop while (s_running.IsSet()) { if (s_shutdown_requested.TestAndClear()) { const auto ios = IOS::HLE::GetIOS(); const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr; if (!s_tried_graceful_shutdown.IsSet() && stm && std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled()) { ProcessorInterface::PowerButton_Tap(); s_tried_graceful_shutdown.Set(); } else { s_running.Clear(); } } XEvent event; KeySym key; for (int num_events = XPending(dpy); num_events > 0; num_events--) { XNextEvent(dpy, &event); switch (event.type) { case KeyPress: key = XLookupKeysym((XKeyEvent*)&event, 0); if (key == XK_Escape) { if (Core::GetState() == Core::State::Running) { if (SConfig::GetInstance().bHideCursor) XUndefineCursor(dpy, win); Core::SetState(Core::State::Paused); } else { if (SConfig::GetInstance().bHideCursor) XDefineCursor(dpy, win, blankCursor); Core::SetState(Core::State::Running); } } else if ((key == XK_Return) && (event.xkey.state & Mod1Mask)) { fullscreen = !fullscreen; X11Utils::ToggleFullscreen(dpy, win); #if defined(HAVE_XRANDR) && HAVE_XRANDR XRRConfig->ToggleDisplayMode(fullscreen); #endif } else if (key >= XK_F1 && key <= XK_F8) { int slot_number = key - XK_F1 + 1; if (event.xkey.state & ShiftMask) State::Save(slot_number); else State::Load(slot_number); } else if (key == XK_F9) Core::SaveScreenShot(); else if (key == XK_F11) State::LoadLastSaved(); else if (key == XK_F12) { if (event.xkey.state & ShiftMask) State::UndoLoadState(); else State::UndoSaveState(); } break; case FocusIn: rendererHasFocus = true; if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused) XDefineCursor(dpy, win, blankCursor); break; case FocusOut: rendererHasFocus = false; if (SConfig::GetInstance().bHideCursor) XUndefineCursor(dpy, win); break; case ClientMessage: if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False)) s_shutdown_requested.Set(); break; case ConfigureNotify: { if (last_window_width != event.xconfigure.width || last_window_height != event.xconfigure.height) { last_window_width = event.xconfigure.width; last_window_height = event.xconfigure.height; // We call Renderer::ChangeSurface here to indicate the size has changed, // but pass the same window handle. This is needed for the Vulkan backend, // otherwise it cannot tell that the window has been resized on some drivers. if (g_renderer) g_renderer->ChangeSurface(s_window_handle); } } break; } } if (!fullscreen) { Window winDummy; unsigned int borderDummy, depthDummy; XGetGeometry(dpy, win, &winDummy, &SConfig::GetInstance().iRenderWindowXPos, &SConfig::GetInstance().iRenderWindowYPos, (unsigned int*)&SConfig::GetInstance().iRenderWindowWidth, (unsigned int*)&SConfig::GetInstance().iRenderWindowHeight, &borderDummy, &depthDummy); rendererIsFullscreen = false; } Core::HostDispatchJobs(); usleep(100000); } }
static void StartDVDThread() { ASSERT(!s_dvd_thread.joinable()); s_dvd_thread_exiting.Clear(); s_dvd_thread = std::thread(DVDThread); }
void HiresTexture::Update() { s_check_native_format = false; s_check_new_format = false; if (s_prefetcher.joinable()) { s_textureCacheAbortLoading.Set(); s_prefetcher.join(); } if (!g_ActiveConfig.bHiresTextures) { s_textureMap.clear(); s_textureCache.clear(); size_sum.store(0); return; } if (!g_ActiveConfig.bCacheHiresTextures) { s_textureCache.clear(); size_sum.store(0); } s_textureMap.clear(); const std::string& gameCode = SConfig::GetInstance().m_strUniqueID; std::string szDir = StringFromFormat("%s%s", File::GetUserPath(D_HIRESTEXTURES_IDX).c_str(), gameCode.c_str()); std::string ddscode(".dds"); std::string cddscode(".DDS"); std::vector<std::string> Extensions = { ".png", ".dds" }; auto rFilenames = DoFileSearch(Extensions, { szDir }, /*recursive*/ true); const std::string code = StringFromFormat("%s_", gameCode.c_str()); const std::string miptag = "mip"; const std::string normaltag = ".nrm"; for (u32 i = 0; i < rFilenames.size(); i++) { std::string FileName; std::string Extension; SplitPath(rFilenames[i], nullptr, &FileName, &Extension); if (FileName.substr(0, code.length()) == code) { s_check_native_format = true; } else if (FileName.substr(0, s_format_prefix.length()) == s_format_prefix) { s_check_new_format = true; } else { // Discard wrong files continue; } const bool is_compressed = Extension.compare(ddscode) == 0 || Extension.compare(cddscode) == 0; const bool is_normal_map = hasEnding(FileName, normaltag); if (is_normal_map) { FileName = FileName.substr(0, FileName.size() - normaltag.size()); } hires_mip_level mip_level_detail(rFilenames[i], Extension, is_compressed); u32 level = 0; size_t idx = FileName.find_last_of('_'); std::string miplevel = FileName.substr(idx + 1, std::string::npos); if (miplevel.substr(0, miptag.length()) == miptag) { sscanf(miplevel.substr(3, std::string::npos).c_str(), "%i", &level); FileName = FileName.substr(0, idx); } HiresTextureCache::iterator iter = s_textureMap.find(FileName); u32 min_item_size = level + 1; if (iter == s_textureMap.end()) { HiresTextureCacheItem item(min_item_size); if (is_normal_map) { item.normal_map.resize(min_item_size); } std::vector<hires_mip_level> &dst = is_normal_map ? item.normal_map : item.color_map; dst[level] = mip_level_detail; s_textureMap.emplace(FileName, item); } else { std::vector<hires_mip_level> &dst = is_normal_map ? iter->second.normal_map : iter->second.color_map; if (dst.size() < min_item_size) { dst.resize(min_item_size); } dst[level] = mip_level_detail; } } if (g_ActiveConfig.bCacheHiresTextures && s_textureMap.size() > 0) { // remove cached but deleted textures auto iter = s_textureCache.begin(); while (iter != s_textureCache.end()) { if (s_textureMap.find(iter->first) == s_textureMap.end()) { size_sum.fetch_sub(iter->second->m_cached_data_size); iter = s_textureCache.erase(iter); } else { iter++; } } s_textureCacheAbortLoading.Clear(); s_prefetcher = std::thread(Prefetch); } }
void HiresTexture::Update() { if (s_prefetcher.joinable()) { s_textureCacheAbortLoading.Set(); s_prefetcher.join(); } if (!g_ActiveConfig.bHiresTextures) { s_textureMap.clear(); s_textureCache.clear(); return; } if (!g_ActiveConfig.bCacheHiresTextures) { s_textureCache.clear(); } const std::string& game_id = SConfig::GetInstance().m_strUniqueID; const std::string texture_directory = GetTextureDirectory(game_id); std::vector<std::string> extensions { ".png", ".bmp", ".tga", ".dds", ".jpg" // Why not? Could be useful for large photo-like textures }; std::vector<std::string> filenames = DoFileSearch(extensions, {texture_directory}, /*recursive*/ true); const std::string code = game_id + "_"; for (auto& rFilename : filenames) { std::string FileName; SplitPath(rFilename, nullptr, &FileName, nullptr); if (FileName.substr(0, code.length()) == code) { s_textureMap[FileName] = rFilename; s_check_native_format = true; } if (FileName.substr(0, s_format_prefix.length()) == s_format_prefix) { s_textureMap[FileName] = rFilename; s_check_new_format = true; } } if (g_ActiveConfig.bCacheHiresTextures) { // remove cached but deleted textures auto iter = s_textureCache.begin(); while (iter != s_textureCache.end()) { if (s_textureMap.find(iter->first) == s_textureMap.end()) { iter = s_textureCache.erase(iter); } else { iter++; } } s_textureCacheAbortLoading.Clear(); s_prefetcher = std::thread(Prefetch); } }
// Initialize and create emulation thread // Call browser: Init():s_emu_thread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SConfig& core_parameter = SConfig::GetInstance(); s_is_booting.Set(); Common::SetCurrentThreadName("Emuthread - Starting"); if (SConfig::GetInstance().m_OCEnable) DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(core_parameter.m_strFilename, 3000); // For a time this acts as the CPU thread... DeclareAsCPUThread(); Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(s_window_handle)) { s_is_booting.Clear(); PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000); if (cpu_info.HTT) SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 4; else SConfig::GetInstance().bDSPThread = cpu_info.num_cores > 2; if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread)) { s_is_booting.Clear(); HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulation!"); Host_Message(WM_USER_STOP); return; } bool init_controllers = false; if (!g_controller_interface.IsInit()) { Pad::Initialize(s_window_handle); Keyboard::Initialize(s_window_handle); init_controllers = true; } else { // Update references in case controllers were refreshed Pad::LoadConfig(); Keyboard::LoadConfig(); } // Load and Init Wiimotes - only if we are booting in Wii mode if (core_parameter.bWii && !SConfig::GetInstance().m_bt_passthrough_enabled) { if (init_controllers) Wiimote::Initialize(s_window_handle, !s_state_filename.empty() ? Wiimote::InitializeMode::DO_WAIT_FOR_WIIMOTES : Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES); else Wiimote::LoadConfig(); // Activate Wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_BBMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true); } AudioCommon::InitSoundStream(); // The hardware is initialized. s_hardware_initialized = true; s_is_booting.Clear(); // Set execution state to known values (CPU/FIFO/Audio Paused) CPU::Break(); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block. Fifo::Prepare(); // Thread is no longer acting as CPU Thread UndeclareAsCPUThread(); // Setup our core, but can't use dynarec if we are compare server if (core_parameter.iCPUCore != PowerPC::CORE_INTERPRETER && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient)) { PowerPC::SetMode(PowerPC::MODE_JIT); } else { PowerPC::SetMode(PowerPC::MODE_INTERPRETER); } // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the CPU thread function void (*cpuThreadFunc)(void); if (core_parameter.m_BootType == SConfig::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (core_parameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread s_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread Fifo::RunGpuLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because no one is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread s_cpu_thread = std::thread(cpuThreadFunc); while (CPU::GetState() != CPU::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str()); // Wait for s_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); #ifdef USE_GDBSTUB INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str()); gdb_deinit(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str()); #endif s_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (core_parameter.bCPUThread) g_video_backend->Video_Cleanup(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() s_hardware_initialized = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); if (init_controllers) { Wiimote::Shutdown(); Keyboard::Shutdown(); Pad::Shutdown(); init_controllers = false; } g_video_backend->Shutdown(); AudioCommon::ShutdownSoundStream(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str()); // Clear on screen messages that haven't expired OSD::ClearMessages(); BootManager::RestoreConfig(); INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----"); Movie::Shutdown(); PatchEngine::Shutdown(); HLE::Clear(); s_is_stopping = false; if (s_on_stopped_callback) s_on_stopped_callback(); }