#pragma once #include "VFS.h" #include "util/atomic.hpp" #include "Utilities/Config.h" #include #include #include u64 get_system_time(); u64 get_guest_system_time(); enum class system_state { running, paused, stopped, ready, }; enum class ppu_decoder_type { precise, fast, llvm, }; enum class spu_decoder_type { precise, fast, asmjit, llvm, }; enum class spu_block_size_type { safe, mega, giga, }; enum class lib_loading_type { manual, hybrid, liblv2only, liblv2both, liblv2list, }; enum sleep_timers_accuracy_level : u32 { _as_host = 0, _usleep, _all_timers, }; enum class keyboard_handler { null, basic, }; enum class mouse_handler { null, basic, }; enum class pad_handler { null, keyboard, ds3, ds4, #ifdef _WIN32 xinput, mm, #endif #ifdef HAVE_LIBEVDEV evdev, #endif }; enum class video_renderer { null, opengl, vulkan, #ifdef _MSC_VER dx12, #endif }; enum class audio_renderer { null, #ifdef _WIN32 xaudio, #endif #ifdef HAVE_ALSA alsa, #endif openal, #ifdef HAVE_PULSE pulse, #endif }; enum class camera_handler { null, fake, }; enum class fake_camera_type { unknown, eyetoy, eyetoy2, uvc1_1, }; enum class move_handler { null, fake, }; enum class microphone_handler { null, standard, singstar, real_singstar, rocksmith, }; enum class video_resolution { _1080, _720, _480, _576, _1600x1080, _1440x1080, _1280x1080, _960x1080, }; enum class video_aspect { _auto, _4_3, _16_9, }; enum class frame_limit_type { none, _59_94, _50, _60, _30, _auto, }; enum class msaa_level { none, _auto }; enum class detail_level { minimal, low, medium, high, }; enum class screen_quadrant { top_left, top_right, bottom_left, bottom_right }; enum class tsx_usage { disabled, enabled, forced, }; enum enter_button_assign { circle = 0, // CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CIRCLE cross = 1 // CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CROSS }; enum CellNetCtlState : s32; enum CellSysutilLang : s32; enum CellKbMappingType : s32; struct EmuCallbacks { std::function)> call_after; std::function on_run; std::function on_pause; std::function on_resume; std::function on_stop; std::function on_ready; std::function exit; // (force_quit) close RPCS3 std::function reset_pads; std::function enable_pads; std::function handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit std::function init_kb_handler; std::function init_mouse_handler; std::function init_pad_handler; std::function()> get_gs_frame; std::function init_gs_render; std::function()> get_audio; std::function()> get_msg_dialog; std::function()> get_osk_dialog; std::function()> get_save_dialog; std::function()> get_trophy_notification_dialog; }; class Emulator final { atomic_t m_state{system_state::stopped}; EmuCallbacks m_cb; atomic_t m_pause_start_time; // set when paused atomic_t m_pause_amend_time; // increased when resumed std::string m_path; std::string m_path_old; std::string m_title_id; std::string m_title; std::string m_cat; std::string m_dir; std::string m_sfo_dir; std::string m_game_dir{"PS3_GAME"}; std::string m_usr{"00000001"}; u32 m_usrid{1}; bool m_force_boot = false; public: Emulator() = default; void SetCallbacks(EmuCallbacks&& cb) { m_cb = std::move(cb); } const auto& GetCallbacks() const { return m_cb; } // Call from the GUI thread void CallAfter(std::function&& func) const { return m_cb.call_after(std::move(func)); } /** Set emulator mode to running unconditionnaly. * Required to execute various part (PPUInterpreter, memory manager...) outside of rpcs3. */ void SetTestMode() { m_state = system_state::running; } void Init(); std::vector argv; std::vector envp; std::vector data; std::vector klic; std::string disc; const std::string& GetBoot() const { return m_path; } const std::string& GetTitleID() const { return m_title_id; } const std::string& GetTitle() const { return m_title; } const std::string& GetCat() const { return m_cat; } const std::string& GetDir() const { return m_dir; } const std::string& GetSfoDir() const { return m_sfo_dir; } // String for GUI dialogs. const std::string& GetUsr() const { return m_usr; } // u32 for cell. const u32 GetUsrId() const { return m_usrid; } const bool SetUsr(const std::string& user); const std::string GetBackgroundPicturePath() const; u64 GetPauseTime() { return m_pause_amend_time; } std::string PPUCache() const; bool BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, bool force_global_config = false); bool BootRsxCapture(const std::string& path); bool InstallPkg(const std::string& path); private: static std::string GetEmuDir(); void LimitCacheSize(); public: static std::string GetHddDir(); static std::string GetHdd1Dir(); static std::string GetSfoDirFromGamePath(const std::string& game_path, const std::string& user, const std::string& title_id = ""); static std::string GetCustomConfigDir(); static std::string GetCustomConfigPath(const std::string& title_id, bool get_deprecated_path = false); static std::string GetCustomInputConfigDir(const std::string& title_id); static std::string GetCustomInputConfigPath(const std::string& title_id); void SetForceBoot(bool force_boot); void Load(const std::string& title_id = "", bool add_only = false, bool force_global_config = false); void Run(); bool Pause(); void Resume(); void Stop(bool restart = false); void Restart() { Stop(true); } bool IsRunning() const { return m_state == system_state::running; } bool IsPaused() const { return m_state == system_state::paused; } bool IsStopped() const { return m_state == system_state::stopped; } bool IsReady() const { return m_state == system_state::ready; } auto GetStatus() const { return m_state.load(); } }; extern Emulator Emu; struct cfg_root : cfg::node { struct node_core : cfg::node { static constexpr bool thread_scheduler_enabled_def = #ifdef _WIN32 true; #else false; #endif node_core(cfg::node* _this) : cfg::node(_this, "Core") {} cfg::_enum ppu_decoder{this, "PPU Decoder", ppu_decoder_type::llvm}; cfg::_int<1, 8> ppu_threads{this, "PPU Threads", 2}; // Amount of PPU threads running simultaneously (must be 2) cfg::_bool ppu_debug{this, "PPU Debug"}; cfg::_bool llvm_logs{this, "Save LLVM logs"}; cfg::string llvm_cpu{this, "Use LLVM CPU"}; cfg::_int<0, INT32_MAX> llvm_threads{this, "Max LLVM Compile Threads", 0}; cfg::_bool thread_scheduler_enabled{this, "Enable thread scheduler", thread_scheduler_enabled_def}; cfg::_bool set_daz_and_ftz{this, "Set DAZ and FTZ", false}; cfg::_enum spu_decoder{this, "SPU Decoder", spu_decoder_type::llvm}; cfg::_bool lower_spu_priority{this, "Lower SPU thread priority"}; cfg::_bool spu_debug{this, "SPU Debug"}; cfg::_int<0, 6> preferred_spu_threads{this, "Preferred SPU Threads", 0}; //Numnber of hardware threads dedicated to heavy simultaneous spu tasks cfg::_int<0, 16> spu_delay_penalty{this, "SPU delay penalty", 3}; //Number of milliseconds to block a thread if a virtual 'core' isn't free cfg::_bool spu_loop_detection{this, "SPU loop detection", true}; //Try to detect wait loops and trigger thread yield cfg::_int<0, 6> max_spurs_threads{this, "Max SPURS Threads", 6}; // HACK. If less then 6, max number of running SPURS threads in each thread group. cfg::_enum spu_block_size{this, "SPU Block Size", spu_block_size_type::safe}; cfg::_bool spu_accurate_getllar{this, "Accurate GETLLAR", false}; cfg::_bool spu_accurate_putlluc{this, "Accurate PUTLLUC", false}; cfg::_bool spu_verification{this, "SPU Verification", true}; // Should be enabled cfg::_bool spu_cache{this, "SPU Cache", true}; cfg::_bool spu_prof{this, "SPU Profiler", false}; cfg::_enum enable_TSX{this, "Enable TSX", tsx_usage::enabled}; // Enable TSX. Forcing this on Haswell/Broadwell CPUs should be used carefully cfg::_bool spu_accurate_xfloat{this, "Accurate xfloat", false}; cfg::_bool spu_approx_xfloat{this, "Approximate xfloat", true}; cfg::_bool debug_console_mode{this, "Debug Console Mode", false}; // Debug console emulation, not recommended cfg::_enum lib_loading{this, "Lib Loader", lib_loading_type::liblv2only}; cfg::_bool hook_functions{this, "Hook static functions"}; cfg::set_entry load_libraries{this, "Load libraries"}; cfg::_bool hle_lwmutex{this, "HLE lwmutex"}; // Force alternative lwmutex/lwcond implementation cfg::_int<10, 1000> clocks_scale{this, "Clocks scale", 100}; // Changing this from 100 (percentage) may affect game speed in unexpected ways cfg::_enum sleep_timers_accuracy{this, "Sleep Timers Accuracy", #ifdef __linux__ sleep_timers_accuracy_level::_as_host}; #else sleep_timers_accuracy_level::_usleep}; #endif } core{this}; struct node_vfs : cfg::node { node_vfs(cfg::node* _this) : cfg::node(_this, "VFS") {} std::string get(const cfg::string&, const char*) const; cfg::string emulator_dir{this, "$(EmulatorDir)"}; // Default (empty): taken from fs::get_config_dir() cfg::string dev_hdd0{this, "/dev_hdd0/", "$(EmulatorDir)dev_hdd0/"}; cfg::string dev_hdd1{this, "/dev_hdd1/", "$(EmulatorDir)dev_hdd1/"}; cfg::string dev_flash{this, "/dev_flash/"}; cfg::string dev_usb000{this, "/dev_usb000/", "$(EmulatorDir)dev_usb000/"}; cfg::string dev_bdvd{this, "/dev_bdvd/"}; // Not mounted cfg::string app_home{this, "/app_home/"}; // Not mounted std::string get_dev_flash() const { return get(dev_flash, "dev_flash/"); } cfg::_bool host_root{this, "Enable /host_root/"}; cfg::_bool init_dirs{this, "Initialize Directories", true}; cfg::_bool limit_cache_size{this, "Limit disk cache size", false}; cfg::_int<0, 10240> cache_max_size{this, "Disk cache maximum size (MB)", 5120}; } vfs{this}; struct node_video : cfg::node { node_video(cfg::node* _this) : cfg::node(_this, "Video") {} cfg::_enum renderer{this, "Renderer", video_renderer::opengl}; cfg::_enum resolution{this, "Resolution", video_resolution::_720}; cfg::_enum aspect_ratio{this, "Aspect ratio", video_aspect::_16_9}; cfg::_enum frame_limit{this, "Frame limit", frame_limit_type::none}; cfg::_enum antialiasing_level{this, "MSAA", msaa_level::_auto}; cfg::_bool write_color_buffers{this, "Write Color Buffers"}; cfg::_bool write_depth_buffer{this, "Write Depth Buffer"}; cfg::_bool read_color_buffers{this, "Read Color Buffers"}; cfg::_bool read_depth_buffer{this, "Read Depth Buffer"}; cfg::_bool log_programs{this, "Log shader programs"}; cfg::_bool vsync{this, "VSync"}; cfg::_bool debug_output{this, "Debug output"}; cfg::_bool overlay{this, "Debug overlay"}; cfg::_bool gl_legacy_buffers{this, "Use Legacy OpenGL Buffers"}; cfg::_bool use_gpu_texture_scaling{this, "Use GPU texture scaling", false}; cfg::_bool stretch_to_display_area{this, "Stretch To Display Area"}; cfg::_bool force_high_precision_z_buffer{this, "Force High Precision Z buffer"}; cfg::_bool strict_rendering_mode{this, "Strict Rendering Mode"}; cfg::_bool disable_zcull_queries{this, "Disable ZCull Occlusion Queries", false}; cfg::_bool disable_vertex_cache{this, "Disable Vertex Cache", false}; cfg::_bool disable_FIFO_reordering{this, "Disable FIFO Reordering", false}; cfg::_bool frame_skip_enabled{this, "Enable Frame Skip", false}; cfg::_bool force_cpu_blit_processing{this, "Force CPU Blit", false}; // Debugging option cfg::_bool disable_on_disk_shader_cache{this, "Disable On-Disk Shader Cache", false}; cfg::_bool disable_vulkan_mem_allocator{this, "Disable Vulkan Memory Allocator", false}; cfg::_bool full_rgb_range_output{this, "Use full RGB output range", true}; // Video out dynamic range cfg::_bool disable_asynchronous_shader_compiler{this, "Disable Asynchronous Shader Compiler", false}; cfg::_bool strict_texture_flushing{this, "Strict Texture Flushing", false}; cfg::_bool disable_native_float16{this, "Disable native float16 support", false}; cfg::_bool multithreaded_rsx{this, "Multithreaded RSX", false}; cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consecutive Frames To Draw", 1}; cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consecutive Frames To Skip", 1}; cfg::_int<50, 800> resolution_scale_percent{this, "Resolution Scale", 100}; cfg::_int<0, 16> anisotropic_level_override{this, "Anisotropic Filter Override", 0}; cfg::_int<1, 1024> min_scalable_dimension{this, "Minimum Scalable Dimension", 16}; cfg::_int<0, 30000000> driver_recovery_timeout{this, "Driver Recovery Timeout", 1000000}; cfg::_int<1, 500> vblank_rate{this, "Vblank Rate", 60}; // Changing this from 60 may affect game speed in unexpected ways struct node_d3d12 : cfg::node { node_d3d12(cfg::node* _this) : cfg::node(_this, "D3D12") {} cfg::string adapter{this, "Adapter"}; } d3d12{this}; struct node_vk : cfg::node { node_vk(cfg::node* _this) : cfg::node(_this, "Vulkan") {} cfg::string adapter{this, "Adapter"}; cfg::_bool force_fifo{this, "Force FIFO present mode"}; cfg::_bool force_primitive_restart{this, "Force primitive restart flag"}; } vk{this}; struct node_perf_overlay : cfg::node { node_perf_overlay(cfg::node* _this) : cfg::node(_this, "Performance Overlay") {} cfg::_bool perf_overlay_enabled{this, "Enabled", false}; cfg::_enum level{this, "Detail level", detail_level::medium}; cfg::_int<30, 5000> update_interval{ this, "Metrics update interval (ms)", 350 }; cfg::_int<4, 36> font_size{ this, "Font size (px)", 10 }; cfg::_enum position{this, "Position", screen_quadrant::top_left}; cfg::string font{this, "Font", "n023055ms.ttf"}; cfg::_int<0, 1280> margin_x{this, "Horizontal Margin (px)", 50}; // horizontal distance to the screen border relative to the screen_quadrant in px cfg::_int<0, 720> margin_y{this, "Vertical Margin (px)", 50}; // vertical distance to the screen border relative to the screen_quadrant in px cfg::_bool center_x{ this, "Center Horizontally", false }; cfg::_bool center_y{ this, "Center Vertically", false }; cfg::_int<0, 100> opacity{this, "Opacity (%)", 70}; cfg::string color_body{ this, "Body Color (hex)", "#FFE138FF" }; cfg::string background_body{ this, "Body Background (hex)", "#002339FF" }; cfg::string color_title{ this, "Title Color (hex)", "#F26C24FF" }; cfg::string background_title{ this, "Title Background (hex)", "#00000000" }; } perf_overlay{this}; struct node_shader_compilation_hint : cfg::node { node_shader_compilation_hint(cfg::node* _this) : cfg::node(_this, "Shader Compilation Hint") {} cfg::_int<0, 1280> pos_x{this, "Position X (px)", 20}; // horizontal position starting from the upper border in px cfg::_int<0, 720> pos_y{this, "Position Y (px)", 690}; // vertical position starting from the left border in px } shader_compilation_hint{this}; struct node_shader_preloading_dialog : cfg::node { node_shader_preloading_dialog(cfg::node* _this) : cfg::node(_this, "Shader Loading Dialog"){} cfg::_bool use_custom_background{this, "Allow custom background", true}; cfg::_int<0, 100> darkening_strength{this, "Darkening effect strength", 30}; cfg::_int<0, 100> blur_strength{this, "Blur effect strength", 0}; } shader_preloading_dialog{this}; } video{this}; struct node_audio : cfg::node { node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {} cfg::_enum renderer{this, "Renderer", static_cast(1)}; cfg::_bool dump_to_file{this, "Dump to file"}; cfg::_bool convert_to_u16{this, "Convert to 16 bit"}; cfg::_bool downmix_to_2ch{this, "Downmix to Stereo", true}; cfg::_int<1, 128> startt{this, "Start Threshold", 1}; // TODO: used only by ALSA, should probably be removed once ALSA is upgraded cfg::_int<0, 200> volume{this, "Master Volume", 100}; cfg::_bool enable_buffering{this, "Enable Buffering", true}; cfg::_int <20, 250> desired_buffer_duration{this, "Desired Audio Buffer Duration", 100}; cfg::_int<1, 1000> sampling_period_multiplier{this, "Sampling Period Multiplier", 100}; cfg::_bool enable_time_stretching{this, "Enable Time Stretching", false}; cfg::_int<0, 100> time_stretching_threshold{this, "Time Stretching Threshold", 75}; cfg::_enum microphone_type{ this, "Microphone Type", microphone_handler::null }; cfg::string microphone_devices{ this, "Microphone Devices", ";;;;" }; } audio{this}; struct node_io : cfg::node { node_io(cfg::node* _this) : cfg::node(_this, "Input/Output") {} cfg::_enum keyboard{this, "Keyboard", keyboard_handler::null}; cfg::_enum mouse{this, "Mouse", mouse_handler::basic}; cfg::_enum pad{this, "Pad", pad_handler::keyboard}; cfg::_enum camera{this, "Camera", camera_handler::null}; cfg::_enum camera_type{this, "Camera type", fake_camera_type::unknown}; cfg::_enum move{this, "Move", move_handler::null}; } io{this}; struct node_sys : cfg::node { node_sys(cfg::node* _this) : cfg::node(_this, "System") {} cfg::_enum language{this, "Language", (CellSysutilLang)1}; // CELL_SYSUTIL_LANG_ENGLISH_US cfg::_enum keyboard_type{this, "Keyboard Type", (CellKbMappingType)0}; // CELL_KB_MAPPING_101 = US cfg::_enum enter_button_assignment{this, "Enter button assignment", enter_button_assign::cross}; } sys{this}; struct node_net : cfg::node { node_net(cfg::node* _this) : cfg::node(_this, "Net") {} cfg::_enum net_status{this, "Connection status"}; cfg::string ip_address{this, "IP address", "192.168.1.1"}; } net{this}; struct node_misc : cfg::node { node_misc(cfg::node* _this) : cfg::node(_this, "Miscellaneous") {} cfg::_bool autostart{this, "Automatically start games after boot", true}; cfg::_bool autoexit{this, "Exit RPCS3 when process finishes"}; cfg::_bool start_fullscreen{ this, "Start games in fullscreen mode" }; cfg::_bool prevent_display_sleep{ this, "Prevent display sleep while running games", true}; cfg::_bool show_fps_in_title{ this, "Show FPS counter in window title", true}; cfg::_bool show_trophy_popups{ this, "Show trophy popups", true}; cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true }; cfg::_bool use_native_interface{ this, "Use native user interface", true }; cfg::string gdb_server{this, "GDB Server", "127.0.0.1:2345"}; } misc{this}; cfg::log_entry log{this, "Log"}; }; extern cfg_root g_cfg; extern bool g_use_rtm;