mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-09 00:37:50 +00:00
Compare commits
41 Commits
f08402b81b
...
507cd17a04
Author | SHA1 | Date | |
---|---|---|---|
![]() |
507cd17a04 | ||
![]() |
52fcdde485 | ||
![]() |
5eb61024c6 | ||
![]() |
a27b845514 | ||
![]() |
1b1ca019a4 | ||
![]() |
241834709b | ||
![]() |
185b080f03 | ||
![]() |
c3d3b81533 | ||
![]() |
774a84a953 | ||
![]() |
b3f50c969e | ||
![]() |
3b83907b88 | ||
![]() |
d940d62cae | ||
![]() |
7afa9e6c6f | ||
![]() |
12d178a8df | ||
![]() |
f910c1d934 | ||
![]() |
70abcb2030 | ||
![]() |
d8ea31ca46 | ||
![]() |
2ae43324cb | ||
![]() |
7d59c2743d | ||
![]() |
8113399b68 | ||
![]() |
bafe78203d | ||
![]() |
15f125ebee | ||
![]() |
316740daed | ||
![]() |
903eafcf65 | ||
![]() |
2a7e8a4003 | ||
![]() |
9ec69b5925 | ||
![]() |
d7de49ccf6 | ||
![]() |
5ec5db9240 | ||
![]() |
974c56d3c5 | ||
![]() |
977f2da6a7 | ||
![]() |
be3d48ec5f | ||
![]() |
ae26b38fc0 | ||
![]() |
2de9122b5f | ||
![]() |
a6a5e201b6 | ||
![]() |
81980a2807 | ||
![]() |
9fa74ac631 | ||
![]() |
e0c6b4506d | ||
![]() |
6ae12a7e02 | ||
![]() |
32f1dd359d | ||
![]() |
ec29d120b5 | ||
![]() |
d48e6e25ad |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -84,6 +84,9 @@
|
||||
[submodule "Externals/Vulkan-Headers"]
|
||||
path = Externals/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
[submodule "Externals/watcher/watcher"]
|
||||
path = Externals/watcher/watcher
|
||||
url = https://github.com/e-dant/watcher.git
|
||||
[submodule "Externals/SFML/SFML"]
|
||||
path = Externals/SFML/SFML
|
||||
url = https://github.com/SFML/SFML.git
|
||||
|
@ -784,6 +784,8 @@ if (USE_RETRO_ACHIEVEMENTS)
|
||||
add_subdirectory(Externals/rcheevos)
|
||||
endif()
|
||||
|
||||
add_subdirectory(Externals/watcher)
|
||||
|
||||
########################################
|
||||
# Pre-build events: Define configuration variables and write SCM info header
|
||||
#
|
||||
|
9
Data/Sys/GameSettings/SCYE4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYE4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYE4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019CB1C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SCYP4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYP4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYP4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019CB1C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SCYR4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYR4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYR4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019B4EC:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SCYX4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYX4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYX4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019CBBC:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SCYY4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYY4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYY4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019B55C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SCYZ4Q.ini
Normal file
9
Data/Sys/GameSettings/SCYZ4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SCYZ4Q - Cars 2
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8019B55C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
@ -1,4 +1,4 @@
|
||||
# SQIE4Q, SQIP4Q - Disney Infinity
|
||||
# SQIE4Q, SQIP4Q, SQIY4Q - Disney Infinity
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main Dolphin settings.
|
||||
|
9
Data/Sys/GameSettings/SQIE4Q.ini
Normal file
9
Data/Sys/GameSettings/SQIE4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SQIE4Q - Disney Infinity
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8008E60C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SQIP4Q.ini
Normal file
9
Data/Sys/GameSettings/SQIP4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SQIP4Q - Disney Infinity
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8008E60C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/SQIY4Q.ini
Normal file
9
Data/Sys/GameSettings/SQIY4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# SQIY4Q - Disney Infinity
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x8008E60C:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSE4Q.ini
Normal file
9
Data/Sys/GameSettings/STSE4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSE4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA2E4:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSP4Qr1.ini
Normal file
9
Data/Sys/GameSettings/STSP4Qr1.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSP4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA2E4:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSP4Qr2.ini
Normal file
9
Data/Sys/GameSettings/STSP4Qr2.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSP4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA354:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSX4Q.ini
Normal file
9
Data/Sys/GameSettings/STSX4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSX4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA354:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSY4Qr0.ini
Normal file
9
Data/Sys/GameSettings/STSY4Qr0.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSY4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA2E4:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSY4Qr1.ini
Normal file
9
Data/Sys/GameSettings/STSY4Qr1.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSY4Q - Toy Story 3
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA354:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
9
Data/Sys/GameSettings/STSZ4Q.ini
Normal file
9
Data/Sys/GameSettings/STSZ4Q.ini
Normal file
@ -0,0 +1,9 @@
|
||||
# STSZ4Q - Toy Story 3 Toy Box Special Edition
|
||||
|
||||
[OnFrame]
|
||||
#This speedhack modifies the way the game manages memory to run faster in Dolphin.
|
||||
$BAT Speedhack
|
||||
0x801FA2E4:dword:0x48000180
|
||||
|
||||
[OnFrame_Enabled]
|
||||
$BAT Speedhack
|
4
Externals/watcher/CMakeLists.txt
vendored
Normal file
4
Externals/watcher/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
add_library(watcher INTERFACE IMPORTED GLOBAL)
|
||||
set_target_properties(watcher PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/watcher/include
|
||||
)
|
1
Externals/watcher/watcher
vendored
Submodule
1
Externals/watcher/watcher
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b03bdcfc11549df595b77239cefe2643943a3e2f
|
@ -64,6 +64,8 @@ add_library(common
|
||||
FatFsUtil.h
|
||||
FileSearch.cpp
|
||||
FileSearch.h
|
||||
FilesystemWatcher.cpp
|
||||
FilesystemWatcher.h
|
||||
FileUtil.cpp
|
||||
FileUtil.h
|
||||
FixedSizeQueue.h
|
||||
@ -184,6 +186,7 @@ PRIVATE
|
||||
FatFs
|
||||
Iconv::Iconv
|
||||
spng::spng
|
||||
watcher
|
||||
${VTUNE_LIBRARIES}
|
||||
)
|
||||
|
||||
|
67
Source/Core/Common/FilesystemWatcher.cpp
Normal file
67
Source/Core/Common/FilesystemWatcher.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/FilesystemWatcher.h"
|
||||
|
||||
#include <wtr/watcher.hpp>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
FilesystemWatcher::FilesystemWatcher() = default;
|
||||
FilesystemWatcher::~FilesystemWatcher() = default;
|
||||
|
||||
void FilesystemWatcher::Watch(const std::string& path)
|
||||
{
|
||||
const auto [iter, inserted] = m_watched_paths.try_emplace(path, nullptr);
|
||||
if (inserted)
|
||||
{
|
||||
iter->second = std::make_unique<wtr::watch>(path, [this](wtr::event e) {
|
||||
const auto watched_path = PathToString(e.path_name);
|
||||
if (e.path_type == wtr::event::path_type::watcher)
|
||||
{
|
||||
if (watched_path.starts_with('e'))
|
||||
ERROR_LOG_FMT(COMMON, "Filesystem watcher: '{}'", watched_path);
|
||||
else if (watched_path.starts_with('w'))
|
||||
WARN_LOG_FMT(COMMON, "Filesystem watcher: '{}'", watched_path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.effect_type == wtr::event::effect_type::create)
|
||||
{
|
||||
const auto path = WithUnifiedPathSeparators(watched_path);
|
||||
PathAdded(path);
|
||||
}
|
||||
else if (e.effect_type == wtr::event::effect_type::modify)
|
||||
{
|
||||
const auto path = WithUnifiedPathSeparators(watched_path);
|
||||
PathModified(path);
|
||||
}
|
||||
else if (e.effect_type == wtr::event::effect_type::rename)
|
||||
{
|
||||
if (!e.associated)
|
||||
{
|
||||
WARN_LOG_FMT(COMMON, "Rename on path '{}' seen without association!", watched_path);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto old_path = WithUnifiedPathSeparators(watched_path);
|
||||
const auto new_path = WithUnifiedPathSeparators(PathToString(e.associated->path_name));
|
||||
PathRenamed(old_path, new_path);
|
||||
}
|
||||
else if (e.effect_type == wtr::event::effect_type::destroy)
|
||||
{
|
||||
const auto path = WithUnifiedPathSeparators(watched_path);
|
||||
PathDeleted(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FilesystemWatcher::Unwatch(const std::string& path)
|
||||
{
|
||||
m_watched_paths.erase(path);
|
||||
}
|
||||
} // namespace Common
|
47
Source/Core/Common/FilesystemWatcher.h
Normal file
47
Source/Core/Common/FilesystemWatcher.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace wtr
|
||||
{
|
||||
inline namespace watcher
|
||||
{
|
||||
class watch;
|
||||
}
|
||||
} // namespace wtr
|
||||
|
||||
namespace Common
|
||||
{
|
||||
// A class that can watch a path and receive callbacks
|
||||
// when files or directories underneath that path receive events
|
||||
class FilesystemWatcher
|
||||
{
|
||||
public:
|
||||
FilesystemWatcher();
|
||||
virtual ~FilesystemWatcher();
|
||||
|
||||
void Watch(const std::string& path);
|
||||
void Unwatch(const std::string& path);
|
||||
|
||||
private:
|
||||
// A new file or folder was added to one of the watched paths
|
||||
virtual void PathAdded(std::string_view path) {}
|
||||
|
||||
// A file or folder was modified in one of the watched paths
|
||||
virtual void PathModified(std::string_view path) {}
|
||||
|
||||
// A file or folder was renamed in one of the watched paths
|
||||
virtual void PathRenamed(std::string_view old_path, std::string_view new_path) {}
|
||||
|
||||
// A file or folder was deleted in one of the watched paths
|
||||
virtual void PathDeleted(std::string_view path) {}
|
||||
|
||||
std::map<std::string, std::unique_ptr<wtr::watch>> m_watched_paths;
|
||||
};
|
||||
} // namespace Common
|
@ -51,6 +51,7 @@ void SymbolDB::Clear(const char* prefix)
|
||||
{
|
||||
// TODO: honor prefix
|
||||
m_functions.clear();
|
||||
m_notes.clear();
|
||||
m_checksum_to_function.clear();
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,16 @@ struct SCall
|
||||
u32 call_address;
|
||||
};
|
||||
|
||||
struct Note
|
||||
{
|
||||
std::string name;
|
||||
u32 address = 0;
|
||||
u32 size = 0;
|
||||
int layer = 0;
|
||||
|
||||
Note() = default;
|
||||
};
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
enum class Type
|
||||
@ -71,6 +81,7 @@ class SymbolDB
|
||||
{
|
||||
public:
|
||||
using XFuncMap = std::map<u32, Symbol>;
|
||||
using XNoteMap = std::map<u32, Note>;
|
||||
using XFuncPtrMap = std::map<u32, std::set<Symbol*>>;
|
||||
|
||||
SymbolDB();
|
||||
@ -86,6 +97,7 @@ public:
|
||||
std::vector<Symbol*> GetSymbolsFromHash(u32 hash);
|
||||
|
||||
const XFuncMap& Symbols() const { return m_functions; }
|
||||
const XNoteMap& Notes() const { return m_notes; }
|
||||
XFuncMap& AccessSymbols() { return m_functions; }
|
||||
bool IsEmpty() const;
|
||||
void Clear(const char* prefix = "");
|
||||
@ -94,6 +106,7 @@ public:
|
||||
|
||||
protected:
|
||||
XFuncMap m_functions;
|
||||
XNoteMap m_notes;
|
||||
XFuncPtrMap m_checksum_to_function;
|
||||
};
|
||||
} // namespace Common
|
||||
|
@ -45,6 +45,7 @@ const Info<bool> MAIN_ACCURATE_CPU_CACHE{{System::Main, "Core", "AccurateCPUCach
|
||||
const Info<bool> MAIN_DSP_HLE{{System::Main, "Core", "DSPHLE"}, true};
|
||||
const Info<int> MAIN_MAX_FALLBACK{{System::Main, "Core", "MaxFallback"}, 100};
|
||||
const Info<int> MAIN_TIMING_VARIANCE{{System::Main, "Core", "TimingVariance"}, 40};
|
||||
const Info<bool> MAIN_CORRECT_TIME_DRIFT{{System::Main, "Core", "CorrectTimeDrift"}, false};
|
||||
const Info<bool> MAIN_CPU_THREAD{{System::Main, "Core", "CPUThread"}, true};
|
||||
const Info<bool> MAIN_SYNC_ON_SKIP_IDLE{{System::Main, "Core", "SyncOnSkipIdle"}, true};
|
||||
const Info<std::string> MAIN_DEFAULT_ISO{{System::Main, "Core", "DefaultISO"}, ""};
|
||||
|
@ -63,6 +63,7 @@ extern const Info<bool> MAIN_ACCURATE_CPU_CACHE;
|
||||
extern const Info<bool> MAIN_DSP_HLE;
|
||||
extern const Info<int> MAIN_MAX_FALLBACK;
|
||||
extern const Info<int> MAIN_TIMING_VARIANCE;
|
||||
extern const Info<bool> MAIN_CORRECT_TIME_DRIFT;
|
||||
extern const Info<bool> MAIN_CPU_THREAD;
|
||||
extern const Info<bool> MAIN_SYNC_ON_SKIP_IDLE;
|
||||
extern const Info<std::string> MAIN_DEFAULT_ISO;
|
||||
|
@ -82,7 +82,6 @@
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
|
||||
#include "VideoCommon/Assets/CustomAssetLoader.h"
|
||||
#include "VideoCommon/AsyncRequests.h"
|
||||
#include "VideoCommon/Fifo.h"
|
||||
#include "VideoCommon/FrameDumper.h"
|
||||
@ -528,9 +527,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
||||
|
||||
FreeLook::LoadInputConfig();
|
||||
|
||||
system.GetCustomAssetLoader().Init();
|
||||
Common::ScopeGuard asset_loader_guard([&system] { system.GetCustomAssetLoader().Shutdown(); });
|
||||
|
||||
system.GetMovie().Init(*boot);
|
||||
Common::ScopeGuard movie_guard([&system] { system.GetMovie().Shutdown(); });
|
||||
|
||||
|
@ -105,10 +105,20 @@ void CoreTimingManager::Init()
|
||||
|
||||
m_last_oc_factor = m_config_oc_factor;
|
||||
m_globals.last_OC_factor_inverted = m_config_oc_inv_factor;
|
||||
|
||||
m_on_state_changed_handle = Core::AddOnStateChangedCallback([this](Core::State state) {
|
||||
if (state == Core::State::Running)
|
||||
{
|
||||
// We don't want Throttle to attempt catch-up for all the time lost while paused.
|
||||
ResetThrottle(GetTicks());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CoreTimingManager::Shutdown()
|
||||
{
|
||||
Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle);
|
||||
|
||||
std::lock_guard lk(m_ts_write_lock);
|
||||
MoveEvents();
|
||||
ClearPendingEvents();
|
||||
@ -131,6 +141,8 @@ void CoreTimingManager::RefreshConfig()
|
||||
|
||||
m_max_variance = std::chrono::duration_cast<DT>(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE)));
|
||||
|
||||
m_correct_time_drift = Config::Get(Config::MAIN_CORRECT_TIME_DRIFT);
|
||||
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive() &&
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f &&
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) > 0.0f)
|
||||
@ -428,7 +440,9 @@ void CoreTimingManager::Throttle(const s64 target_cycle)
|
||||
const TimePoint time = Clock::now();
|
||||
|
||||
const TimePoint min_target = time - m_max_fallback;
|
||||
if (target_time < min_target)
|
||||
|
||||
// "Correct Time Drift" setting prevents timing relaxing.
|
||||
if (!m_correct_time_drift && target_time < min_target)
|
||||
{
|
||||
// Core is running too slow.. i.e. CPU bottleneck.
|
||||
const DT adjustment = min_target - target_time;
|
||||
|
@ -211,6 +211,7 @@ private:
|
||||
|
||||
DT m_max_fallback = {};
|
||||
DT m_max_variance = {};
|
||||
bool m_correct_time_drift = false;
|
||||
double m_emulation_speed = 1.0;
|
||||
|
||||
bool IsSpeedUnlimited() const;
|
||||
@ -225,6 +226,8 @@ private:
|
||||
std::atomic_bool m_use_precision_timer = false;
|
||||
Common::PrecisionTimer m_precision_cpu_timer;
|
||||
Common::PrecisionTimer m_precision_gpu_timer;
|
||||
|
||||
int m_on_state_changed_handle;
|
||||
};
|
||||
|
||||
} // namespace CoreTiming
|
||||
|
@ -104,6 +104,10 @@ public:
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual u32 GetNoteColor(const CPUThreadGuard* /*guard*/, u32 /*address*/) const
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual std::string_view GetDescription(u32 /*address*/) const = 0;
|
||||
virtual void Clear(const CPUThreadGuard& guard) = 0;
|
||||
};
|
||||
|
@ -437,6 +437,21 @@ u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address)
|
||||
};
|
||||
return colors[symbol->index % colors.size()];
|
||||
}
|
||||
|
||||
u32 PPCDebugInterface::GetNoteColor(const Core::CPUThreadGuard* guard, u32 address) const
|
||||
{
|
||||
if (!IsAlive())
|
||||
return 0xFFFFFF;
|
||||
if (!PowerPC::MMU::HostIsRAMAddress(*guard, address))
|
||||
return 0xeeeeee;
|
||||
static const int colors[3] = {
|
||||
0xcedeee, // light blue
|
||||
0xcceecc, // light green
|
||||
0xeeeece, // light yellow
|
||||
};
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(address);
|
||||
return colors[note->layer % 3];
|
||||
}
|
||||
// =============
|
||||
|
||||
std::string_view PPCDebugInterface::GetDescription(u32 address) const
|
||||
|
@ -102,6 +102,7 @@ public:
|
||||
void Step() override {}
|
||||
void RunTo(u32 address) override;
|
||||
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
u32 GetNoteColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
std::string_view GetDescription(u32 address) const override;
|
||||
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> NetworkLogger();
|
||||
|
@ -78,8 +78,8 @@ std::string USBHost::GetDeviceNameFromVIDPID(u16 vid, u16 pid)
|
||||
libusb_get_string_descriptor_ascii(handle, desc.iProduct, buffer, sizeof(buffer)) > 0)
|
||||
{
|
||||
device_name = reinterpret_cast<char*>(buffer);
|
||||
libusb_close(handle);
|
||||
}
|
||||
libusb_close(handle);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -91,6 +91,46 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
||||
}
|
||||
}
|
||||
|
||||
void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name)
|
||||
{
|
||||
auto iter = m_notes.find(start_addr);
|
||||
|
||||
if (iter != m_notes.end())
|
||||
{
|
||||
// already got it, let's just update name, checksum & size to be sure.
|
||||
Common::Note* tempfunc = &iter->second;
|
||||
tempfunc->name = name;
|
||||
tempfunc->size = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::Note tf;
|
||||
tf.name = name;
|
||||
tf.address = start_addr;
|
||||
tf.size = size;
|
||||
|
||||
m_notes[start_addr] = tf;
|
||||
}
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DetermineNoteLayers()
|
||||
{
|
||||
if (m_notes.empty())
|
||||
return;
|
||||
|
||||
for (auto& note : m_notes)
|
||||
note.second.layer = 0;
|
||||
|
||||
for (auto iter = m_notes.begin(); iter != m_notes.end(); ++iter)
|
||||
{
|
||||
const u32 range = iter->second.address + iter->second.size;
|
||||
auto search = m_notes.upper_bound(range);
|
||||
|
||||
while (--search != iter)
|
||||
search->second.layer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
{
|
||||
auto it = m_functions.lower_bound(addr);
|
||||
@ -112,6 +152,43 @@ Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr)
|
||||
{
|
||||
if (m_notes.empty())
|
||||
return nullptr;
|
||||
|
||||
auto itn = m_notes.lower_bound(addr);
|
||||
|
||||
// If the address is exactly the start address of a symbol, we're done.
|
||||
if (itn != m_notes.end() && itn->second.address == addr)
|
||||
return &itn->second;
|
||||
|
||||
// Otherwise, check whether the address is within the bounds of a symbol.
|
||||
if (itn == m_notes.begin())
|
||||
return nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
--itn;
|
||||
|
||||
if (addr >= itn->second.address && addr < itn->second.address + itn->second.size)
|
||||
return &itn->second;
|
||||
|
||||
} while (itn != m_notes.begin() && itn->second.layer > 0);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DeleteFunction(u32 start_address)
|
||||
{
|
||||
m_functions.erase(start_address);
|
||||
}
|
||||
|
||||
void PPCSymbolDB::DeleteNote(u32 start_address)
|
||||
{
|
||||
m_notes.erase(start_address);
|
||||
}
|
||||
|
||||
std::string_view PPCSymbolDB::GetDescription(u32 addr)
|
||||
{
|
||||
if (const Common::Symbol* const symbol = GetSymbolFromAddr(addr))
|
||||
@ -406,6 +483,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
if (strlen(name) > 0)
|
||||
{
|
||||
bool good;
|
||||
// Notes will be treated the same as Data.
|
||||
const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ?
|
||||
Common::Symbol::Type::Function :
|
||||
Common::Symbol::Type::Data;
|
||||
@ -438,7 +516,11 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
if (good)
|
||||
{
|
||||
++good_count;
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
|
||||
if (section_name == ".note")
|
||||
AddKnownNote(vaddress, size, name_string);
|
||||
else
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -448,6 +530,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
||||
}
|
||||
|
||||
Index();
|
||||
DetermineNoteLayers();
|
||||
NOTICE_LOG_FMT(SYMBOLS, "{} symbols loaded, {} symbols ignored.", good_count, bad_count);
|
||||
return true;
|
||||
}
|
||||
@ -495,6 +578,17 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
||||
file.WriteString(line);
|
||||
}
|
||||
|
||||
// Write .note section
|
||||
auto note_symbols = m_notes | std::views::transform([](auto f) { return f.second; });
|
||||
file.WriteString("\n.note section layout\n");
|
||||
for (const auto& symbol : note_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
const std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}\n", symbol.address,
|
||||
symbol.size, symbol.address, 0, symbol.name);
|
||||
file.WriteString(line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,13 @@ public:
|
||||
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type = Common::Symbol::Type::Function);
|
||||
void AddKnownNote(u32 startAddr, u32 size, const std::string& name);
|
||||
|
||||
Common::Symbol* GetSymbolFromAddr(u32 addr) override;
|
||||
Common::Note* GetNoteFromAddr(u32 addr);
|
||||
void DetermineNoteLayers();
|
||||
void DeleteFunction(u32 start_address);
|
||||
void DeleteNote(u32 start_address);
|
||||
|
||||
std::string_view GetDescription(u32 addr);
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "IOS/USB/Emulated/Infinity.h"
|
||||
#include "IOS/USB/Emulated/Skylanders/Skylander.h"
|
||||
#include "IOS/USB/USBScanner.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLoader.h"
|
||||
#include "VideoCommon/Assets/CustomResourceManager.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
#include "VideoCommon/Fifo.h"
|
||||
#include "VideoCommon/GeometryShaderManager.h"
|
||||
@ -96,7 +96,7 @@ struct System::Impl
|
||||
VideoInterface::VideoInterfaceManager m_video_interface;
|
||||
Interpreter m_interpreter;
|
||||
JitInterface m_jit_interface;
|
||||
VideoCommon::CustomAssetLoader m_custom_asset_loader;
|
||||
VideoCommon::CustomResourceManager m_custom_resource_manager;
|
||||
FifoPlayer m_fifo_player;
|
||||
FifoRecorder m_fifo_recorder;
|
||||
Movie::MovieManager m_movie;
|
||||
@ -335,8 +335,8 @@ VideoInterface::VideoInterfaceManager& System::GetVideoInterface() const
|
||||
return m_impl->m_video_interface;
|
||||
}
|
||||
|
||||
VideoCommon::CustomAssetLoader& System::GetCustomAssetLoader() const
|
||||
VideoCommon::CustomResourceManager& System::GetCustomResourceManager() const
|
||||
{
|
||||
return m_impl->m_custom_asset_loader;
|
||||
return m_impl->m_custom_resource_manager;
|
||||
}
|
||||
} // namespace Core
|
||||
|
@ -108,8 +108,8 @@ class SystemTimersManager;
|
||||
}
|
||||
namespace VideoCommon
|
||||
{
|
||||
class CustomAssetLoader;
|
||||
}
|
||||
class CustomResourceManager;
|
||||
} // namespace VideoCommon
|
||||
namespace VideoInterface
|
||||
{
|
||||
class VideoInterfaceManager;
|
||||
@ -197,7 +197,7 @@ public:
|
||||
VertexShaderManager& GetVertexShaderManager() const;
|
||||
XFStateManager& GetXFStateManager() const;
|
||||
VideoInterface::VideoInterfaceManager& GetVideoInterface() const;
|
||||
VideoCommon::CustomAssetLoader& GetCustomAssetLoader() const;
|
||||
VideoCommon::CustomResourceManager& GetCustomResourceManager() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
@ -59,6 +59,7 @@
|
||||
<ClInclude Include="Common\Event.h" />
|
||||
<ClInclude Include="Common\FatFsUtil.h" />
|
||||
<ClInclude Include="Common\FileSearch.h" />
|
||||
<ClInclude Include="Common\FilesystemWatcher.h" />
|
||||
<ClInclude Include="Common\FileUtil.h" />
|
||||
<ClInclude Include="Common\FixedSizeQueue.h" />
|
||||
<ClInclude Include="Common\Flag.h" />
|
||||
@ -669,12 +670,16 @@
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAssetLibrary.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomAssetLoader.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomResourceManager.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\CustomTextureData.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\MaterialAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\MeshAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\ShaderAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\TextureAsset.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\TextureAssetUtils.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\Types.h" />
|
||||
<ClInclude Include="VideoCommon\Assets\WatchableFilesystemAssetLibrary.h" />
|
||||
<ClInclude Include="VideoCommon\AsyncRequests.h" />
|
||||
<ClInclude Include="VideoCommon\AsyncShaderCompiler.h" />
|
||||
<ClInclude Include="VideoCommon\BoundingBox.h" />
|
||||
@ -814,6 +819,7 @@
|
||||
<ClCompile Include="Common\ENet.cpp" />
|
||||
<ClCompile Include="Common\FatFsUtil.cpp" />
|
||||
<ClCompile Include="Common\FileSearch.cpp" />
|
||||
<ClCompile Include="Common\FilesystemWatcher.cpp" />
|
||||
<ClCompile Include="Common\FileUtil.cpp" />
|
||||
<ClCompile Include="Common\FloatUtils.cpp" />
|
||||
<ClCompile Include="Common\GekkoDisassembler.cpp" />
|
||||
@ -1320,14 +1326,15 @@
|
||||
<ClCompile Include="VideoCommon\AbstractStagingTexture.cpp" />
|
||||
<ClCompile Include="VideoCommon\AbstractTexture.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAssetLibrary.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomAssetLoader.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomResourceManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\CustomTextureData.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\DirectFilesystemAssetLibrary.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\MaterialAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\MeshAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\ShaderAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\TextureAsset.cpp" />
|
||||
<ClCompile Include="VideoCommon\Assets\TextureAssetUtils.cpp" />
|
||||
<ClCompile Include="VideoCommon\AsyncRequests.cpp" />
|
||||
<ClCompile Include="VideoCommon\AsyncShaderCompiler.cpp" />
|
||||
<ClCompile Include="VideoCommon\BoundingBox.cpp" />
|
||||
|
@ -15,7 +15,6 @@
|
||||
AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("About Dolphin"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
QString branch_str = QString::fromStdString(Common::GetScmBranchStr());
|
||||
const int commits_ahead = Common::GetScmCommitsAheadMaster();
|
||||
|
@ -27,7 +27,6 @@
|
||||
AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Achievements"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateMainLayout();
|
||||
ConnectWidgets();
|
||||
|
@ -221,6 +221,8 @@ add_executable(dolphin-emu
|
||||
Debugger/CodeViewWidget.h
|
||||
Debugger/CodeWidget.cpp
|
||||
Debugger/CodeWidget.h
|
||||
Debugger/EditSymbolDialog.cpp
|
||||
Debugger/EditSymbolDialog.h
|
||||
Debugger/GekkoSyntaxHighlight.cpp
|
||||
Debugger/GekkoSyntaxHighlight.h
|
||||
Debugger/JitBlockTableModel.cpp
|
||||
|
@ -26,7 +26,6 @@ CheatsManager::CheatsManager(Core::System& system, QWidget* parent)
|
||||
: QDialog(parent), m_system(system)
|
||||
{
|
||||
setWindowTitle(tr("Cheats Manager"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&CheatsManager::OnStateChanged);
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
CheatCodeEditor::CheatCodeEditor(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("Cheat Code Editor"));
|
||||
|
||||
CreateWidgets();
|
||||
|
@ -80,6 +80,36 @@ void ConfigSlider::OnConfigChanged()
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSliderU32::ConfigSliderU32(u32 minimum, u32 maximum, const Config::Info<u32>& setting,
|
||||
u32 scale)
|
||||
: ConfigSliderU32(minimum, maximum, setting, nullptr, scale)
|
||||
{
|
||||
}
|
||||
|
||||
ConfigSliderU32::ConfigSliderU32(u32 minimum, u32 maximum, const Config::Info<u32>& setting,
|
||||
Config::Layer* layer, u32 scale)
|
||||
: ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_setting(setting),
|
||||
m_scale(scale)
|
||||
|
||||
{
|
||||
setMinimum(minimum);
|
||||
setMaximum(maximum);
|
||||
setValue(ReadValue(setting));
|
||||
OnConfigChanged();
|
||||
|
||||
connect(this, &ConfigSliderU32::valueChanged, this, &ConfigSliderU32::Update);
|
||||
}
|
||||
|
||||
void ConfigSliderU32::Update(u32 value)
|
||||
{
|
||||
SaveValue(m_setting, value * m_scale);
|
||||
}
|
||||
|
||||
void ConfigSliderU32::OnConfigChanged()
|
||||
{
|
||||
setValue(ReadValue(m_setting) / m_scale);
|
||||
}
|
||||
|
||||
ConfigSliderLabel::ConfigSliderLabel(const QString& text, ConfigSlider* slider)
|
||||
: QLabel(text), m_slider(QPointer<ConfigSlider>(slider))
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/ConfigInfo.h"
|
||||
|
||||
class ConfigSlider final : public ConfigControl<ToolTipSlider>
|
||||
@ -38,6 +39,25 @@ private:
|
||||
std::vector<int> m_tick_values;
|
||||
};
|
||||
|
||||
class ConfigSliderU32 final : public ConfigControl<ToolTipSlider>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConfigSliderU32(u32 minimum, u32 maximum, const Config::Info<u32>& setting, u32 scale = 1);
|
||||
ConfigSliderU32(u32 minimum, u32 maximum, const Config::Info<u32>& setting, Config ::Layer* layer,
|
||||
u32 scale = 1);
|
||||
|
||||
void Update(u32 value);
|
||||
|
||||
protected:
|
||||
void OnConfigChanged() override;
|
||||
|
||||
private:
|
||||
const Config::Info<u32> m_setting;
|
||||
|
||||
u32 m_scale = 1;
|
||||
};
|
||||
|
||||
class ConfigSliderLabel final : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -17,7 +17,6 @@ ControllerInterfaceWindow::ControllerInterfaceWindow(QWidget* parent) : QDialog(
|
||||
CreateMainLayout();
|
||||
|
||||
setWindowTitle(tr("Alternate Input Sources"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
void ControllerInterfaceWindow::CreateMainLayout()
|
||||
|
@ -29,7 +29,6 @@ DualShockUDPClientAddServerDialog::DualShockUDPClientAddServerDialog(QWidget* pa
|
||||
void DualShockUDPClientAddServerDialog::CreateWidgets()
|
||||
{
|
||||
setWindowTitle(tr("Add New DSU Server"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
m_main_layout = new QGridLayout;
|
||||
|
||||
|
@ -17,7 +17,6 @@ FreeLookWindow::FreeLookWindow(QWidget* parent) : QDialog(parent)
|
||||
ConnectWidgets();
|
||||
|
||||
setWindowTitle(tr("Free Look Settings"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
void FreeLookWindow::CreateMainLayout()
|
||||
|
@ -22,7 +22,6 @@
|
||||
ColorCorrectionConfigWindow::ColorCorrectionConfigWindow(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Color Correction Configuration"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
Create();
|
||||
ConnectWidgets();
|
||||
|
@ -28,7 +28,6 @@ GraphicsWindow::GraphicsWindow(MainWindow* parent) : QDialog(parent), m_main_win
|
||||
CreateMainLayout();
|
||||
|
||||
setWindowTitle(tr("Graphics"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
OnBackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
|
||||
|
||||
|
@ -42,7 +42,6 @@ PostProcessingConfigWindow::PostProcessingConfigWindow(EnhancementsWidget* paren
|
||||
}
|
||||
|
||||
setWindowTitle(tr("Post-Processing Shader Configuration"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
PopulateGroups();
|
||||
Create();
|
||||
|
@ -17,8 +17,6 @@
|
||||
GCPadWiiUConfigDialog::GCPadWiiUConfigDialog(int port, QWidget* parent)
|
||||
: QDialog(parent), m_port{port}
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateLayout();
|
||||
|
||||
LoadSettings();
|
||||
|
@ -268,7 +268,6 @@ IOWindow::IOWindow(MappingWindow* window, ControllerEmu::EmulatedController* con
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &IOWindow::ConfigChanged);
|
||||
|
||||
setWindowTitle(type == IOWindow::Type::Input ? tr("Configure Input") : tr("Configure Output"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
ConfigChanged();
|
||||
|
||||
|
@ -65,7 +65,6 @@ MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
|
||||
: QDialog(parent), m_port(port_num)
|
||||
{
|
||||
setWindowTitle(tr("Port %1").arg(port_num + 1));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateDevicesLayout();
|
||||
CreateProfilesLayout();
|
||||
|
@ -38,7 +38,6 @@ NewPatchDialog::NewPatchDialog(QWidget* parent, PatchEngine::Patch& patch)
|
||||
: QDialog(parent), m_patch(patch)
|
||||
{
|
||||
setWindowTitle(tr("Patch Editor"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "UICommon/GameFile.h"
|
||||
|
||||
PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& game)
|
||||
: StackedSettingsWindow{parent}
|
||||
: StackedSettingsWindow{parent}, m_filepath(game.GetFilePath())
|
||||
{
|
||||
setWindowTitle(QStringLiteral("%1: %2 - %3")
|
||||
.arg(QString::fromStdString(game.GetFileName()),
|
||||
|
@ -17,6 +17,7 @@ class PropertiesDialog final : public StackedSettingsWindow
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PropertiesDialog(QWidget* parent, const UICommon::GameFile& game);
|
||||
const std::string& GetFilePath() const { return m_filepath; }
|
||||
|
||||
signals:
|
||||
void OpenGeneralSettings();
|
||||
@ -24,4 +25,7 @@ signals:
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void OpenAchievementSettings();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
private:
|
||||
const std::string m_filepath;
|
||||
};
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
StackedSettingsWindow::StackedSettingsWindow(QWidget* parent) : QDialog{parent}
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
// This eliminates the ugly line between the title bar and window contents with KDE Plasma.
|
||||
setStyleSheet(QStringLiteral("QDialog { border: none; }"));
|
||||
|
||||
|
@ -42,7 +42,6 @@ ConvertDialog::ConvertDialog(QList<std::shared_ptr<const UICommon::GameFile>> fi
|
||||
ASSERT(!m_files.empty());
|
||||
|
||||
setWindowTitle(tr("Convert"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
QGridLayout* grid_layout = new QGridLayout;
|
||||
grid_layout->setColumnStretch(1, 1);
|
||||
|
@ -41,7 +41,6 @@ QString HtmlFormatErrorLine(const Common::GekkoAssembler::AssemblerError& err)
|
||||
AssembleInstructionDialog::AssembleInstructionDialog(QWidget* parent, u32 address, u32 value)
|
||||
: QDialog(parent), m_code(value), m_address(address)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setWindowTitle(tr("Instruction"));
|
||||
|
||||
|
@ -200,7 +200,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
||||
: QDialog(parent), m_system(system), m_branch_watch(branch_watch), m_code_widget(code_widget)
|
||||
{
|
||||
setWindowTitle(tr("Branch Watch Tool"));
|
||||
setWindowFlags((windowFlags() | Qt::WindowMinMaxButtonsHint) & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowFlags(windowFlags() | Qt::WindowMinMaxButtonsHint);
|
||||
|
||||
// Branch Watch Table
|
||||
m_table_view = new QTableView(nullptr);
|
||||
|
@ -24,7 +24,6 @@
|
||||
BreakpointDialog::BreakpointDialog(BreakpointWidget* parent)
|
||||
: QDialog(parent), m_parent(parent), m_open_mode(OpenMode::New)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("New Breakpoint"));
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
@ -36,7 +35,6 @@ BreakpointDialog::BreakpointDialog(BreakpointWidget* parent)
|
||||
BreakpointDialog::BreakpointDialog(BreakpointWidget* parent, const TBreakPoint* breakpoint)
|
||||
: QDialog(parent), m_parent(parent), m_open_mode(OpenMode::EditBreakPoint)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("Edit Breakpoint"));
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
@ -56,7 +54,6 @@ BreakpointDialog::BreakpointDialog(BreakpointWidget* parent, const TBreakPoint*
|
||||
BreakpointDialog::BreakpointDialog(BreakpointWidget* parent, const TMemCheck* memcheck)
|
||||
: QDialog(parent), m_parent(parent), m_open_mode(OpenMode::EditMemCheck)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("Edit Breakpoint"));
|
||||
|
||||
CreateWidgets();
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
#include "DolphinQt/Debugger/AssembleInstructionDialog.h"
|
||||
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||
#include "DolphinQt/Debugger/PatchInstructionDialog.h"
|
||||
#include "DolphinQt/Host.h"
|
||||
#include "DolphinQt/QtUtils/FromStdString.h"
|
||||
@ -322,7 +323,6 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
||||
for (int i = 0; i < rowCount(); i++)
|
||||
{
|
||||
const u32 addr = AddressForRow(i);
|
||||
const u32 color = debug_interface.GetColor(guard, addr);
|
||||
auto* bp_item = new QTableWidgetItem;
|
||||
auto* addr_item = new QTableWidgetItem(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));
|
||||
|
||||
@ -331,7 +331,19 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
|
||||
|
||||
std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
|
||||
std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
|
||||
const std::string_view desc = debug_interface.GetDescription(addr);
|
||||
std::string desc;
|
||||
int color = 0xFFFFFF;
|
||||
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
if (note == nullptr)
|
||||
{
|
||||
desc = debug_interface.GetDescription(addr);
|
||||
color = debug_interface.GetColor(guard, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = note->name;
|
||||
color = debug_interface.GetNoteColor(guard, addr);
|
||||
}
|
||||
|
||||
// Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while
|
||||
// scrolling.
|
||||
@ -566,8 +578,6 @@ void CodeViewWidget::OnContextMenu()
|
||||
|
||||
const u32 addr = GetContextAddress();
|
||||
|
||||
const bool has_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
auto* follow_branch_action =
|
||||
menu->addAction(tr("Follow &Branch"), this, &CodeViewWidget::OnFollowBranch);
|
||||
|
||||
@ -587,17 +597,15 @@ void CodeViewWidget::OnContextMenu()
|
||||
menu->addAction(tr("Copy Tar&get Address"), this, &CodeViewWidget::OnCopyTargetAddress);
|
||||
menu->addSeparator();
|
||||
|
||||
auto* symbol_rename_action =
|
||||
menu->addAction(tr("&Rename Symbol"), this, &CodeViewWidget::OnRenameSymbol);
|
||||
auto* symbol_size_action =
|
||||
menu->addAction(tr("Set Symbol &Size"), this, &CodeViewWidget::OnSetSymbolSize);
|
||||
auto* symbol_end_action =
|
||||
menu->addAction(tr("Set Symbol &End Address"), this, &CodeViewWidget::OnSetSymbolEndAddress);
|
||||
auto* symbol_edit_action =
|
||||
menu->addAction(tr("&Edit function symbol"), this, &CodeViewWidget::OnEditSymbol);
|
||||
|
||||
auto* note_add_action = menu->addAction(tr("Add Note"), this, &CodeViewWidget::OnAddNote);
|
||||
auto* note_edit_action = menu->addAction(tr("Edit Note"), this, &CodeViewWidget::OnEditNote);
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
auto* run_to_action = menu->addAction(tr("Run &to Here"), this, &CodeViewWidget::OnRunToHere);
|
||||
auto* function_action =
|
||||
menu->addAction(tr("&Add Function"), this, &CodeViewWidget::OnAddFunction);
|
||||
auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison);
|
||||
auto* insert_blr_action = menu->addAction(tr("&Insert BLR"), this, &CodeViewWidget::OnInsertBLR);
|
||||
auto* insert_nop_action = menu->addAction(tr("Insert &NOP"), this, &CodeViewWidget::OnInsertNOP);
|
||||
@ -645,21 +653,24 @@ void CodeViewWidget::OnContextMenu()
|
||||
run_until_menu->setEnabled(!target.isEmpty());
|
||||
follow_branch_action->setEnabled(follow_branch_enabled);
|
||||
|
||||
for (auto* action :
|
||||
{copy_address_action, copy_line_action, copy_hex_action, function_action, run_to_action,
|
||||
ppc_action, insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, symbol_edit_action,
|
||||
note_add_action, note_edit_action, run_to_action, ppc_action,
|
||||
insert_blr_action, insert_nop_action, replace_action, assemble_action})
|
||||
{
|
||||
action->setEnabled(running);
|
||||
}
|
||||
|
||||
for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
|
||||
action->setEnabled(has_symbol);
|
||||
|
||||
for (auto* action : {copy_target_memory, show_target_memory})
|
||||
{
|
||||
action->setEnabled(valid_load_store);
|
||||
}
|
||||
|
||||
auto* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
|
||||
note_edit_action->setEnabled(note != nullptr);
|
||||
// A note cannot be added ontop of the starting address of another note.
|
||||
if (note != nullptr && note->address == addr)
|
||||
note_add_action->setEnabled(false);
|
||||
|
||||
restore_action->setEnabled(running &&
|
||||
m_system.GetPowerPC().GetDebugInterface().HasEnabledPatch(addr));
|
||||
|
||||
@ -883,6 +894,13 @@ void CodeViewWidget::OnPPCComparison()
|
||||
void CodeViewWidget::OnAddFunction()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
int confirm =
|
||||
QMessageBox::warning(this, tr("Add Function Symbol"),
|
||||
tr("Force new function symbol to be made at %1?").arg(addr, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
|
||||
@ -919,25 +937,79 @@ void CodeViewWidget::OnFollowBranch()
|
||||
SetAddress(branch_addr, SetAddressUpdate::WithDetailedUpdate);
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnRenameSymbol()
|
||||
void CodeViewWidget::OnEditSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (symbol == nullptr)
|
||||
{
|
||||
OnAddFunction();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = symbol->name;
|
||||
u32 size = symbol->size;
|
||||
const u32 symbol_address = symbol->address;
|
||||
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, symbol_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const QString name =
|
||||
QInputDialog::getText(this, tr("Rename Symbol"), tr("Symbol Name:"), QLineEdit::Normal,
|
||||
QString::fromStdString(symbol->name), &good, Qt::WindowCloseButtonHint);
|
||||
|
||||
if (good && !name.isEmpty())
|
||||
if (name.empty())
|
||||
{
|
||||
symbol->Rename(name.toStdString());
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
OnDeleteSymbol();
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbol->name != name)
|
||||
symbol->Rename(name);
|
||||
|
||||
if (symbol->size != size)
|
||||
{
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
|
||||
}
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnDeleteSymbol()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (symbol == nullptr)
|
||||
return;
|
||||
|
||||
int confirm = QMessageBox::warning(this, tr("Delete Function Symbol"),
|
||||
tr("Delete function symbol: %1\nat %2?")
|
||||
.arg(QString::fromStdString(symbol->name))
|
||||
.arg(addr, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
m_ppc_symbol_db.DeleteFunction(symbol->address);
|
||||
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnAddNote()
|
||||
{
|
||||
const u32 note_address = GetContextAddress();
|
||||
std::string name = "";
|
||||
u32 size = 4;
|
||||
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, note_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted || name.empty())
|
||||
return;
|
||||
|
||||
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
|
||||
m_ppc_symbol_db.DetermineNoteLayers();
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSelectionChanged()
|
||||
@ -953,53 +1025,57 @@ void CodeViewWidget::OnSelectionChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSetSymbolSize()
|
||||
void CodeViewWidget::OnEditNote()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const int size = QInputDialog::getInt(
|
||||
this, tr("Rename Symbol"), tr("Symbol Size (%1):").arg(QString::fromStdString(symbol->name)),
|
||||
symbol->size, 1, 0xFFFF, 1, &good, Qt::WindowCloseButtonHint);
|
||||
std::string name = note->name;
|
||||
u32 size = note->size;
|
||||
const u32 note_address = note->address;
|
||||
|
||||
if (!good)
|
||||
EditSymbolDialog* dialog = new EditSymbolDialog(this, note_address, &size, &name);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
if (name.empty())
|
||||
{
|
||||
OnDeleteNote();
|
||||
return;
|
||||
}
|
||||
|
||||
if (note->name != name || note->size != size)
|
||||
{
|
||||
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
|
||||
m_ppc_symbol_db.DetermineNoteLayers();
|
||||
}
|
||||
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
void CodeViewWidget::OnSetSymbolEndAddress()
|
||||
void CodeViewWidget::OnDeleteNote()
|
||||
{
|
||||
const u32 addr = GetContextAddress();
|
||||
const u32 context_address = GetContextAddress();
|
||||
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
|
||||
|
||||
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
|
||||
|
||||
if (!symbol)
|
||||
if (note == nullptr)
|
||||
return;
|
||||
|
||||
bool good;
|
||||
const QString name = QInputDialog::getText(
|
||||
this, tr("Set Symbol End Address"),
|
||||
tr("Symbol End Address (%1):").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal,
|
||||
QStringLiteral("%1").arg(addr + symbol->size, 8, 16, QLatin1Char('0')), &good,
|
||||
Qt::WindowCloseButtonHint);
|
||||
int confirm = QMessageBox::warning(this, tr("Delete Note"),
|
||||
tr("Delete Note: %1\nat %2?")
|
||||
.arg(QString::fromStdString(note->name))
|
||||
.arg(context_address, 0, 16),
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
||||
const u32 address = name.toUInt(&good, 16);
|
||||
|
||||
if (!good)
|
||||
if (confirm != QMessageBox::Ok)
|
||||
return;
|
||||
|
||||
Core::CPUThreadGuard guard(m_system);
|
||||
m_ppc_symbol_db.DeleteNote(note->address);
|
||||
|
||||
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
|
||||
emit Host::GetInstance()->PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
|
@ -87,13 +87,15 @@ private:
|
||||
void OnCopyFunction();
|
||||
void OnCopyCode();
|
||||
void OnCopyHex();
|
||||
void OnRenameSymbol();
|
||||
void OnSelectionChanged();
|
||||
void OnSetSymbolSize();
|
||||
void OnSetSymbolEndAddress();
|
||||
void OnRunToHere();
|
||||
void OnAddFunction();
|
||||
void OnEditSymbol();
|
||||
void OnDeleteSymbol();
|
||||
void OnAddNote();
|
||||
void OnPPCComparison();
|
||||
void OnEditNote();
|
||||
void OnDeleteNote();
|
||||
void OnInsertBLR();
|
||||
void OnInsertNOP();
|
||||
void OnReplaceInstruction();
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QStyleHints>
|
||||
#include <QTabWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/Event.h"
|
||||
@ -120,7 +122,7 @@ void CodeWidget::CreateWidgets()
|
||||
m_box_splitter = new QSplitter(Qt::Vertical);
|
||||
m_box_splitter->setStyleSheet(BOX_SPLITTER_STYLESHEET);
|
||||
|
||||
auto add_search_line_edit = [this](const QString& name, QListWidget* list_widget) {
|
||||
auto add_search_line_edit = [this](const QString& name, QWidget* list_widget) {
|
||||
auto* widget = new QWidget;
|
||||
auto* line_layout = new QGridLayout;
|
||||
auto* label = new QLabel(name);
|
||||
@ -139,8 +141,12 @@ void CodeWidget::CreateWidgets()
|
||||
m_search_callstack = add_search_line_edit(tr("Callstack"), m_callstack_list);
|
||||
|
||||
// Symbols
|
||||
auto* symbols_tab = new QTabWidget;
|
||||
m_symbols_list = new QListWidget;
|
||||
m_search_symbols = add_search_line_edit(tr("Symbols"), m_symbols_list);
|
||||
m_note_list = new QListWidget;
|
||||
symbols_tab->addTab(m_symbols_list, tr("Symbols"));
|
||||
symbols_tab->addTab(m_note_list, tr("Notes"));
|
||||
m_search_symbols = add_search_line_edit(tr("Symbols"), symbols_tab);
|
||||
|
||||
// Function calls
|
||||
m_function_calls_list = new QListWidget;
|
||||
@ -198,7 +204,7 @@ void CodeWidget::ConnectWidgets()
|
||||
connect(m_search_callstack, &QLineEdit::textChanged, this, &CodeWidget::UpdateCallstack);
|
||||
|
||||
connect(m_branch_watch, &QPushButton::clicked, this, &CodeWidget::OnBranchWatchDialog);
|
||||
|
||||
connect(m_note_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectNote);
|
||||
connect(m_symbols_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectSymbol);
|
||||
connect(m_callstack_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectCallstack);
|
||||
connect(m_function_calls_list, &QListWidget::itemPressed, this,
|
||||
@ -236,6 +242,7 @@ void CodeWidget::OnSetCodeAddress(u32 address)
|
||||
void CodeWidget::OnPPCSymbolsChanged()
|
||||
{
|
||||
UpdateSymbols();
|
||||
UpdateNotes();
|
||||
UpdateCallstack();
|
||||
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
|
||||
{
|
||||
@ -279,6 +286,7 @@ void CodeWidget::OnSearchSymbols()
|
||||
{
|
||||
m_symbol_filter = m_search_symbols->text();
|
||||
UpdateSymbols();
|
||||
UpdateNotes();
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectSymbol()
|
||||
@ -298,6 +306,17 @@ void CodeWidget::OnSelectSymbol()
|
||||
m_code_view->setFocus();
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectNote()
|
||||
{
|
||||
const auto items = m_note_list->selectedItems();
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
const u32 address = items[0]->data(Qt::UserRole).toUInt();
|
||||
|
||||
m_code_view->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithUpdate);
|
||||
}
|
||||
|
||||
void CodeWidget::OnSelectCallstack()
|
||||
{
|
||||
const auto items = m_callstack_list->selectedItems();
|
||||
@ -426,6 +445,30 @@ void CodeWidget::UpdateSymbols()
|
||||
m_symbols_list->sortItems();
|
||||
}
|
||||
|
||||
void CodeWidget::UpdateNotes()
|
||||
{
|
||||
const QString selection = m_note_list->selectedItems().isEmpty() ?
|
||||
QStringLiteral("") :
|
||||
m_note_list->selectedItems()[0]->text();
|
||||
m_note_list->clear();
|
||||
|
||||
for (const auto& note : m_ppc_symbol_db.Notes())
|
||||
{
|
||||
const QString name = QString::fromStdString(note.second.name);
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
||||
item->setData(Qt::UserRole, note.second.address);
|
||||
|
||||
if (name.toUpper().indexOf(m_symbol_filter.toUpper()) != -1)
|
||||
m_note_list->addItem(item);
|
||||
}
|
||||
|
||||
m_note_list->sortItems();
|
||||
}
|
||||
|
||||
void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
|
||||
{
|
||||
m_function_calls_list->clear();
|
||||
|
@ -61,11 +61,13 @@ private:
|
||||
void UpdateCallstack();
|
||||
void UpdateFunctionCalls(const Common::Symbol* symbol);
|
||||
void UpdateFunctionCallers(const Common::Symbol* symbol);
|
||||
void UpdateNotes();
|
||||
|
||||
void OnPPCSymbolsChanged();
|
||||
void OnSearchAddress();
|
||||
void OnSearchSymbols();
|
||||
void OnSelectSymbol();
|
||||
void OnSelectNote();
|
||||
void OnSelectCallstack();
|
||||
void OnSelectFunctionCallers();
|
||||
void OnSelectFunctionCalls();
|
||||
@ -84,6 +86,7 @@ private:
|
||||
QListWidget* m_callstack_list;
|
||||
QLineEdit* m_search_symbols;
|
||||
QListWidget* m_symbols_list;
|
||||
QListWidget* m_note_list;
|
||||
QLineEdit* m_search_calls;
|
||||
QListWidget* m_function_calls_list;
|
||||
QLineEdit* m_search_callers;
|
||||
|
152
Source/Core/DolphinQt/Debugger/EditSymbolDialog.cpp
Normal file
152
Source/Core/DolphinQt/Debugger/EditSymbolDialog.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Debugger/EditSymbolDialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
EditSymbolDialog::EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
|
||||
std::string* symbol_name)
|
||||
: QDialog(parent), m_symbol_name(symbol_name), m_symbol_size(symbol_size),
|
||||
m_symbol_address(symbol_address)
|
||||
{
|
||||
setWindowTitle(tr("Edit Symbol"));
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::CreateWidgets()
|
||||
{
|
||||
m_reset_button = new QPushButton(tr("Reset"));
|
||||
m_delete_button = new QPushButton(tr("Delete"));
|
||||
m_buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
m_buttons->addButton(m_reset_button, QDialogButtonBox::ResetRole);
|
||||
m_buttons->addButton(m_delete_button, QDialogButtonBox::DestructiveRole);
|
||||
|
||||
QLabel* info_label =
|
||||
new QLabel(tr("Editing symbol starting at: ") + QString::number(m_symbol_address, 16));
|
||||
m_name_edit = new QLineEdit();
|
||||
m_name_edit->setPlaceholderText(tr("Symbol name"));
|
||||
|
||||
auto* size_layout = new QHBoxLayout;
|
||||
QLabel* address_end_label = new QLabel(tr("End Address"));
|
||||
QLabel* size_lines_label = new QLabel(tr("Lines"));
|
||||
QLabel* size_hex_label = new QLabel(tr("Size: 0x"));
|
||||
m_address_end_edit = new QLineEdit();
|
||||
m_size_lines_spin = new QSpinBox();
|
||||
m_size_hex_edit = new QLineEdit();
|
||||
|
||||
size_hex_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
size_lines_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
|
||||
|
||||
// Get system font and use to size boxes.
|
||||
QFont font;
|
||||
QFontMetrics fm(font);
|
||||
const int width = fm.horizontalAdvance(QLatin1Char('0')) * 2;
|
||||
m_address_end_edit->setFixedWidth(width * 6);
|
||||
m_size_hex_edit->setFixedWidth(width * 5);
|
||||
m_size_lines_spin->setFixedWidth(width * 5);
|
||||
m_size_hex_edit->setMaxLength(7);
|
||||
m_size_lines_spin->setRange(0, 99999);
|
||||
|
||||
// Accept hex input only
|
||||
QRegularExpression rx(QStringLiteral("[0-9a-fA-F]{0,8}"));
|
||||
QValidator* validator = new QRegularExpressionValidator(rx, this);
|
||||
m_address_end_edit->setValidator(validator);
|
||||
m_size_hex_edit->setValidator(validator);
|
||||
|
||||
size_layout->addWidget(address_end_label);
|
||||
size_layout->addWidget(m_address_end_edit);
|
||||
size_layout->addWidget(size_hex_label);
|
||||
size_layout->addWidget(m_size_hex_edit);
|
||||
size_layout->addWidget(size_lines_label);
|
||||
size_layout->addWidget(m_size_lines_spin);
|
||||
|
||||
auto* layout = new QVBoxLayout();
|
||||
layout->addWidget(info_label);
|
||||
layout->addWidget(m_name_edit);
|
||||
layout->addLayout(size_layout);
|
||||
layout->addWidget(m_buttons);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
FillFunctionData();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::FillFunctionData()
|
||||
{
|
||||
m_name_edit->setText(QString::fromStdString(*m_symbol_name));
|
||||
m_size_lines_spin->setValue(*m_symbol_size / 4);
|
||||
m_size_hex_edit->setText(QString::number(*m_symbol_size, 16));
|
||||
m_address_end_edit->setText(
|
||||
QStringLiteral("%1").arg(m_symbol_address + *m_symbol_size, 8, 16, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
void EditSymbolDialog::UpdateAddressData(u32 size)
|
||||
{
|
||||
// Not sure what the max size should be. Definitely not a full 8, so set to 7.
|
||||
size = size & 0xFFFFFFC;
|
||||
if (size < 4)
|
||||
size = 4;
|
||||
|
||||
m_size_lines_spin->setValue(size / 4);
|
||||
m_size_hex_edit->setText(QString::number(size, 16)); //("%1").arg(size, 4, 16));
|
||||
m_address_end_edit->setText(
|
||||
QStringLiteral("%1").arg(m_symbol_address + size, 8, 16, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
void EditSymbolDialog::ConnectWidgets()
|
||||
{
|
||||
connect(m_size_lines_spin, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
[this](int value) { UpdateAddressData(value * 4); });
|
||||
|
||||
connect(m_size_hex_edit, &QLineEdit::editingFinished, this, [this] {
|
||||
bool good;
|
||||
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
|
||||
if (good)
|
||||
UpdateAddressData(size);
|
||||
});
|
||||
|
||||
connect(m_address_end_edit, &QLineEdit::textEdited, this, [this] {
|
||||
bool good;
|
||||
const u32 end = m_address_end_edit->text().toUInt(&good, 16);
|
||||
if (good && end > m_symbol_address)
|
||||
UpdateAddressData(end - m_symbol_address);
|
||||
});
|
||||
|
||||
connect(m_buttons, &QDialogButtonBox::accepted, this, &EditSymbolDialog::Accepted);
|
||||
connect(m_buttons, &QDialogButtonBox::rejected, this, &EditSymbolDialog::reject);
|
||||
connect(m_reset_button, &QPushButton::pressed, this, &EditSymbolDialog::FillFunctionData);
|
||||
connect(m_delete_button, &QPushButton::pressed, this, &EditSymbolDialog::NotifyDelete);
|
||||
}
|
||||
|
||||
void EditSymbolDialog::Accepted()
|
||||
{
|
||||
const std::string name = m_name_edit->text().toStdString();
|
||||
|
||||
if (*m_symbol_name != name)
|
||||
*m_symbol_name = name;
|
||||
|
||||
bool good;
|
||||
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
|
||||
|
||||
if (good && *m_symbol_size != size)
|
||||
*m_symbol_size = size;
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void EditSymbolDialog::NotifyDelete()
|
||||
{
|
||||
// Returning an empty name will ask the user if they want to delete the symbol. Also applies to
|
||||
// Accepted().
|
||||
*m_symbol_name = "";
|
||||
QDialog::accept();
|
||||
}
|
42
Source/Core/DolphinQt/Debugger/EditSymbolDialog.h
Normal file
42
Source/Core/DolphinQt/Debugger/EditSymbolDialog.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
// class CodeWidget;
|
||||
class QLineEdit;
|
||||
class QDialogButtonBox;
|
||||
class QSpinBox;
|
||||
|
||||
class EditSymbolDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
|
||||
std::string* symbol_name);
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
void FillFunctionData();
|
||||
void UpdateAddressData(u32 size);
|
||||
void Accepted();
|
||||
void NotifyDelete();
|
||||
|
||||
QLineEdit* m_name_edit;
|
||||
QSpinBox* m_size_lines_spin;
|
||||
QLineEdit* m_size_hex_edit;
|
||||
QLineEdit* m_address_end_edit;
|
||||
|
||||
QPushButton* m_reset_button;
|
||||
QPushButton* m_delete_button;
|
||||
QDialogButtonBox* m_buttons;
|
||||
|
||||
std::string* m_symbol_name;
|
||||
u32* m_symbol_size;
|
||||
const u32 m_symbol_address;
|
||||
};
|
@ -14,7 +14,6 @@
|
||||
PatchInstructionDialog::PatchInstructionDialog(QWidget* parent, u32 address, u32 value)
|
||||
: QDialog(parent), m_address(address)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setWindowTitle(tr("Instruction"));
|
||||
|
||||
|
@ -21,7 +21,6 @@ DiscordJoinRequestDialog::DiscordJoinRequestDialog(QWidget* parent, const std::s
|
||||
: QDialog(parent), m_user_id(id), m_close_timestamp(std::time(nullptr) + s_max_lifetime_seconds)
|
||||
{
|
||||
setWindowTitle(tr("Request to Join Your Party"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
QPixmap avatar_pixmap;
|
||||
|
||||
|
@ -144,6 +144,7 @@
|
||||
<ClCompile Include="Debugger\BreakpointWidget.cpp" />
|
||||
<ClCompile Include="Debugger\CodeViewWidget.cpp" />
|
||||
<ClCompile Include="Debugger\CodeWidget.cpp" />
|
||||
<ClCompile Include="Debugger\EditSymbolDialog.cpp" />
|
||||
<ClCompile Include="Debugger\GekkoSyntaxHighlight.cpp" />
|
||||
<ClCompile Include="Debugger\JitBlockTableModel.cpp" />
|
||||
<ClCompile Include="Debugger\JITWidget.cpp" />
|
||||
@ -364,6 +365,7 @@
|
||||
<QtMoc Include="Debugger\BreakpointWidget.h" />
|
||||
<QtMoc Include="Debugger\CodeViewWidget.h" />
|
||||
<QtMoc Include="Debugger\CodeWidget.h" />
|
||||
<QtMoc Include="Debugger\EditSymbolDialog.h" />
|
||||
<QtMoc Include="Debugger\GekkoSyntaxHighlight.h" />
|
||||
<QtMoc Include="Debugger\JitBlockTableModel.h" />
|
||||
<QtMoc Include="Debugger\JITWidget.h" />
|
||||
|
@ -59,10 +59,18 @@ FIFOPlayerWindow::FIFOPlayerWindow(FifoPlayer& fifo_player, FifoRecorder& fifo_r
|
||||
});
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
|
||||
// We don't want to trigger OnEmulationStarted when going from Paused to Running,
|
||||
// and nothing in UpdateControls treats Paused and Running differently
|
||||
if (state == Core::State::Paused)
|
||||
state = Core::State::Running;
|
||||
|
||||
// Skip redundant updates
|
||||
if (state == m_emu_state)
|
||||
return;
|
||||
|
||||
if (state == Core::State::Running && m_emu_state != Core::State::Paused)
|
||||
UpdateControls();
|
||||
|
||||
if (state == Core::State::Running)
|
||||
OnEmulationStarted();
|
||||
else if (state == Core::State::Uninitialized)
|
||||
OnEmulationStopped();
|
||||
@ -266,8 +274,6 @@ void FIFOPlayerWindow::StopRecording()
|
||||
|
||||
void FIFOPlayerWindow::OnEmulationStarted()
|
||||
{
|
||||
UpdateControls();
|
||||
|
||||
if (m_fifo_player.GetFile())
|
||||
OnFIFOLoaded();
|
||||
}
|
||||
@ -278,7 +284,6 @@ void FIFOPlayerWindow::OnEmulationStopped()
|
||||
if (m_fifo_recorder.IsRecording())
|
||||
StopRecording();
|
||||
|
||||
UpdateControls();
|
||||
// When emulation stops, switch away from the analyzer tab, as it no longer shows anything useful
|
||||
m_tab_widget->setCurrentWidget(m_main_widget);
|
||||
m_analyzer->Update();
|
||||
|
@ -60,7 +60,6 @@ GCMemcardCreateNewDialog::GCMemcardCreateNewDialog(QWidget* parent) : QDialog(pa
|
||||
});
|
||||
|
||||
setWindowTitle(tr("Create New Memory Card"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
GCMemcardCreateNewDialog::~GCMemcardCreateNewDialog() = default;
|
||||
|
@ -98,7 +98,6 @@ GCMemcardManager::GCMemcardManager(QWidget* parent) : QDialog(parent)
|
||||
resize(650, 500);
|
||||
|
||||
setWindowTitle(tr("GameCube Memory Card Manager"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
GCMemcardManager::~GCMemcardManager() = default;
|
||||
|
@ -566,6 +566,15 @@ void GameList::OpenProperties()
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
auto property_windows = this->findChildren<PropertiesDialog*>();
|
||||
auto it =
|
||||
std::ranges::find(property_windows, game->GetFilePath(), &PropertiesDialog::GetFilePath);
|
||||
if (it != property_windows.end())
|
||||
{
|
||||
(*it)->raise();
|
||||
return;
|
||||
}
|
||||
|
||||
PropertiesDialog* properties = new PropertiesDialog(this, *game);
|
||||
|
||||
connect(properties, &PropertiesDialog::OpenGeneralSettings, this, &GameList::OpenGeneralSettings);
|
||||
|
@ -27,7 +27,6 @@ NANDRepairDialog::NANDRepairDialog(const WiiUtils::NANDCheckResult& result, QWid
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("NAND Check"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowIcon(Resources::GetAppIcon());
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout();
|
||||
|
@ -30,7 +30,6 @@ bool NKitWarningDialog::ShowUnlessDisabled(QWidget* parent)
|
||||
NKitWarningDialog::NKitWarningDialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("NKit Warning"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowIcon(Resources::GetAppIcon());
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout;
|
||||
|
@ -45,7 +45,6 @@ ChunkedProgressDialog::ChunkedProgressDialog(QWidget* parent) : QDialog(parent)
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
setWindowTitle(tr("Data Transfer"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
}
|
||||
|
||||
void ChunkedProgressDialog::CreateWidgets()
|
||||
|
@ -14,7 +14,6 @@
|
||||
GameListDialog::GameListDialog(const GameListModel& game_list_model, QWidget* parent)
|
||||
: QDialog(parent), m_game_list_model(game_list_model)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("Select a game"));
|
||||
|
||||
CreateWidgets();
|
||||
|
@ -32,7 +32,6 @@
|
||||
NetPlayBrowser::NetPlayBrowser(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("NetPlay Session Browser"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateWidgets();
|
||||
RestoreSettings();
|
||||
@ -297,7 +296,6 @@ void NetPlayBrowser::accept()
|
||||
{
|
||||
QInputDialog dialog(this);
|
||||
|
||||
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
dialog.setWindowTitle(tr("Enter password"));
|
||||
dialog.setLabelText(tr("This session requires a password:"));
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
@ -97,8 +97,6 @@ NetPlayDialog::NetPlayDialog(const GameListModel& game_list_model,
|
||||
: QDialog(parent), m_game_list_model(game_list_model),
|
||||
m_start_game_callback(std::move(start_game_callback))
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
setWindowTitle(tr("NetPlay"));
|
||||
setWindowIcon(Resources::GetAppIcon());
|
||||
|
||||
|
@ -32,7 +32,6 @@ NetPlaySetupDialog::NetPlaySetupDialog(const GameListModel& game_list_model, QWi
|
||||
: QDialog(parent), m_game_list_model(game_list_model)
|
||||
{
|
||||
setWindowTitle(tr("NetPlay Setup"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateMainLayout();
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
PadMappingDialog::PadMappingDialog(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowTitle(tr("Assign Controllers"));
|
||||
|
||||
CreateWidgets();
|
||||
|
@ -26,7 +26,6 @@ public:
|
||||
ParallelProgressDialog(Args&&... args) : m_dialog{std::forward<Args>(args)...}
|
||||
{
|
||||
setParent(m_dialog.parent());
|
||||
m_dialog.setWindowFlags(m_dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
ConnectSignalsAndSlots();
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ ResourcePackManager::ResourcePackManager(QWidget* widget) : QDialog(widget)
|
||||
RepopulateTable();
|
||||
|
||||
setWindowTitle(tr("Resource Pack Manager"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
resize(QSize(900, 600));
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ RiivolutionBootWidget::RiivolutionBootWidget(std::string game_id, std::optional<
|
||||
m_base_game_path(std::move(base_game_path))
|
||||
{
|
||||
setWindowTitle(tr("Start with Riivolution Patches"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <QLabel>
|
||||
#include <QRadioButton>
|
||||
#include <QSignalBlocker>
|
||||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
#include <cmath>
|
||||
|
||||
@ -25,6 +24,8 @@
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h"
|
||||
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
|
||||
#include "DolphinQt/QtUtils/QtUtils.h"
|
||||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
@ -89,6 +90,18 @@ void AdvancedPane::CreateLayout()
|
||||
"needed.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
||||
cpu_options_group_layout->addWidget(m_accurate_cpu_cache_checkbox);
|
||||
|
||||
auto* const timing_group = new QGroupBox(tr("Timing"));
|
||||
main_layout->addWidget(timing_group);
|
||||
auto* timing_group_layout = new QVBoxLayout{timing_group};
|
||||
auto* const correct_time_drift =
|
||||
new ConfigBool{tr("Correct Time Drift"), Config::MAIN_CORRECT_TIME_DRIFT};
|
||||
correct_time_drift->SetDescription(
|
||||
tr("Allow the emulated console to run fast after stutters,"
|
||||
"<br>pursuing accurate overall elapsed time unless paused or speed-adjusted."
|
||||
"<br><br>This may be useful for internet play."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
||||
timing_group_layout->addWidget(correct_time_drift);
|
||||
|
||||
auto* clock_override = new QGroupBox(tr("Clock Override"));
|
||||
auto* clock_override_layout = new QVBoxLayout();
|
||||
clock_override->setLayout(clock_override_layout);
|
||||
@ -103,12 +116,24 @@ void AdvancedPane::CreateLayout()
|
||||
cpu_clock_override_slider_layout->setContentsMargins(0, 0, 0, 0);
|
||||
clock_override_layout->addLayout(cpu_clock_override_slider_layout);
|
||||
|
||||
m_cpu_clock_override_slider = new QSlider(Qt::Horizontal);
|
||||
m_cpu_clock_override_slider->setRange(1, 400);
|
||||
m_cpu_clock_override_slider = new ConfigFloatSlider(0.01f, 4.0f, Config::MAIN_OVERCLOCK, 0.01f);
|
||||
cpu_clock_override_slider_layout->addWidget(m_cpu_clock_override_slider);
|
||||
|
||||
m_cpu_clock_override_slider_label = new QLabel();
|
||||
cpu_clock_override_slider_layout->addWidget(m_cpu_clock_override_slider_label);
|
||||
m_cpu_label = new QLabel();
|
||||
cpu_clock_override_slider_layout->addWidget(m_cpu_label);
|
||||
|
||||
std::function<void()> cpu_text = [this]() {
|
||||
const float multi = Config::Get(Config::MAIN_OVERCLOCK);
|
||||
const int percent = static_cast<int>(std::round(multi * 100.f));
|
||||
const int core_clock =
|
||||
Core::System::GetInstance().GetSystemTimers().GetTicksPerSecond() / std::pow(10, 6);
|
||||
const int clock = static_cast<int>(std::round(multi * core_clock));
|
||||
m_cpu_label->setText(tr("%1% (%2 MHz)").arg(QString::number(percent), QString::number(clock)));
|
||||
};
|
||||
|
||||
cpu_text();
|
||||
connect(m_cpu_clock_override_slider, &QSlider::valueChanged, this,
|
||||
[this, cpu_text]() { cpu_text(); });
|
||||
|
||||
m_cpu_clock_override_checkbox->SetDescription(
|
||||
tr("Adjusts the emulated CPU's clock rate.<br><br>"
|
||||
@ -135,12 +160,25 @@ void AdvancedPane::CreateLayout()
|
||||
vi_rate_override_slider_layout->setContentsMargins(0, 0, 0, 0);
|
||||
vi_rate_override_layout->addLayout(vi_rate_override_slider_layout);
|
||||
|
||||
m_vi_rate_override_slider = new QSlider(Qt::Horizontal);
|
||||
m_vi_rate_override_slider->setRange(1, 500);
|
||||
m_vi_rate_override_slider = new ConfigFloatSlider(0.01f, 5.0f, Config::MAIN_VI_OVERCLOCK, 0.01f);
|
||||
vi_rate_override_slider_layout->addWidget(m_vi_rate_override_slider);
|
||||
|
||||
m_vi_rate_override_slider_label = new QLabel();
|
||||
vi_rate_override_slider_layout->addWidget(m_vi_rate_override_slider_label);
|
||||
m_vi_label = new QLabel();
|
||||
vi_rate_override_slider_layout->addWidget(m_vi_label);
|
||||
std::function<void()> vi_text = [this]() {
|
||||
const int percent =
|
||||
static_cast<int>(std::round(Config::Get(Config::MAIN_VI_OVERCLOCK) * 100.f));
|
||||
float vps =
|
||||
static_cast<float>(Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate());
|
||||
if (vps == 0.0f || !Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE))
|
||||
vps = 59.94f * Config::Get(Config::MAIN_VI_OVERCLOCK);
|
||||
m_vi_label->setText(
|
||||
tr("%1% (%2 VPS)").arg(QString::number(percent), QString::number(vps, 'f', 2)));
|
||||
};
|
||||
|
||||
vi_text();
|
||||
connect(m_vi_rate_override_slider, &QSlider::valueChanged, this,
|
||||
[this, vi_text]() { vi_text(); });
|
||||
|
||||
m_vi_rate_override_checkbox->SetDescription(
|
||||
tr("Adjusts the VBI frequency. Also adjusts the emulated CPU's "
|
||||
@ -167,27 +205,34 @@ void AdvancedPane::CreateLayout()
|
||||
mem1_override_slider_layout->setContentsMargins(0, 0, 0, 0);
|
||||
ram_override_layout->addLayout(mem1_override_slider_layout);
|
||||
|
||||
m_mem1_override_slider = new QSlider(Qt::Horizontal);
|
||||
m_mem1_override_slider->setRange(24, 64);
|
||||
m_mem1_override_slider = new ConfigSliderU32(24, 64, Config::MAIN_MEM1_SIZE, 0x100000);
|
||||
mem1_override_slider_layout->addWidget(m_mem1_override_slider);
|
||||
|
||||
m_mem1_override_slider_label = new QLabel();
|
||||
mem1_override_slider_layout->addWidget(m_mem1_override_slider_label);
|
||||
m_mem1_label =
|
||||
new QLabel(tr("%1 MB (MEM1)").arg(QString::number(m_mem1_override_slider->value())));
|
||||
mem1_override_slider_layout->addWidget(m_mem1_label);
|
||||
connect(m_mem1_override_slider, &QSlider::valueChanged, this, [this](int value) {
|
||||
m_mem1_label->setText(tr("%1 MB (MEM1)").arg(QString::number(value)));
|
||||
});
|
||||
|
||||
auto* mem2_override_slider_layout = new QHBoxLayout();
|
||||
mem2_override_slider_layout->setContentsMargins(0, 0, 0, 0);
|
||||
ram_override_layout->addLayout(mem2_override_slider_layout);
|
||||
|
||||
m_mem2_override_slider = new QSlider(Qt::Horizontal);
|
||||
m_mem2_override_slider->setRange(64, 128);
|
||||
m_mem2_override_slider = new ConfigSliderU32(64, 128, Config::MAIN_MEM2_SIZE, 0x100000);
|
||||
mem2_override_slider_layout->addWidget(m_mem2_override_slider);
|
||||
|
||||
m_mem2_override_slider_label = new QLabel();
|
||||
mem2_override_slider_layout->addWidget(m_mem2_override_slider_label);
|
||||
m_mem2_label =
|
||||
new QLabel(tr("%1 MB (MEM2)").arg(QString::number(m_mem2_override_slider->value())));
|
||||
mem2_override_slider_layout->addWidget(m_mem2_label);
|
||||
connect(m_mem2_override_slider, &QSlider::valueChanged, this, [this](int value) {
|
||||
m_mem2_label->setText(tr("%1 MB (MEM2)").arg(QString::number(value)));
|
||||
});
|
||||
|
||||
m_ram_override_checkbox->SetDescription(
|
||||
tr("Adjusts the amount of RAM in the emulated console.<br><br>"
|
||||
"<b>WARNING</b>: Enabling this will completely break many games.<br>Only a small number "
|
||||
"<b>WARNING</b>: Enabling this will completely break many games.<br>Only a small "
|
||||
"number "
|
||||
"of games can benefit from this."
|
||||
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
|
||||
|
||||
@ -227,36 +272,12 @@ void AdvancedPane::ConnectLayout()
|
||||
Config::SetBaseOrCurrent(Config::MAIN_CPU_CORE, cpu_cores[index]);
|
||||
});
|
||||
|
||||
connect(m_cpu_clock_override_slider, &QSlider::valueChanged, [this](int oc_factor) {
|
||||
const float factor = m_cpu_clock_override_slider->value() / 100.f;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_OVERCLOCK, factor);
|
||||
Update();
|
||||
});
|
||||
|
||||
connect(m_vi_rate_override_slider, &QSlider::valueChanged, [this](int oc_factor) {
|
||||
const float factor = m_vi_rate_override_slider->value() / 100.f;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_VI_OVERCLOCK, factor);
|
||||
Update();
|
||||
});
|
||||
|
||||
m_ram_override_checkbox->setChecked(Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE));
|
||||
connect(m_ram_override_checkbox, &QCheckBox::toggled, [this](bool enable_ram_override) {
|
||||
Config::SetBaseOrCurrent(Config::MAIN_RAM_OVERRIDE_ENABLE, enable_ram_override);
|
||||
Update();
|
||||
});
|
||||
|
||||
connect(m_mem1_override_slider, &QSlider::valueChanged, [this](int slider_value) {
|
||||
const u32 mem1_size = m_mem1_override_slider->value() * 0x100000;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_MEM1_SIZE, mem1_size);
|
||||
Update();
|
||||
});
|
||||
|
||||
connect(m_mem2_override_slider, &QSlider::valueChanged, [this](int slider_value) {
|
||||
const u32 mem2_size = m_mem2_override_slider->value() * 0x100000;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_MEM2_SIZE, mem2_size);
|
||||
Update();
|
||||
});
|
||||
|
||||
connect(m_custom_rtc_datetime, &QDateTimeEdit::dateTimeChanged, [this](QDateTime date_time) {
|
||||
Config::SetBaseOrCurrent(Config::MAIN_CUSTOM_RTC_VALUE,
|
||||
static_cast<u32>(date_time.toSecsSinceEpoch()));
|
||||
@ -295,21 +316,7 @@ void AdvancedPane::Update()
|
||||
}
|
||||
|
||||
m_cpu_clock_override_slider->setEnabled(enable_cpu_clock_override_widgets);
|
||||
m_cpu_clock_override_slider_label->setEnabled(enable_cpu_clock_override_widgets);
|
||||
|
||||
{
|
||||
const QSignalBlocker blocker(m_cpu_clock_override_slider);
|
||||
m_cpu_clock_override_slider->setValue(
|
||||
static_cast<int>(std::round(Config::Get(Config::MAIN_OVERCLOCK) * 100.f)));
|
||||
}
|
||||
|
||||
m_cpu_clock_override_slider_label->setText([] {
|
||||
int core_clock =
|
||||
Core::System::GetInstance().GetSystemTimers().GetTicksPerSecond() / std::pow(10, 6);
|
||||
int percent = static_cast<int>(std::round(Config::Get(Config::MAIN_OVERCLOCK) * 100.f));
|
||||
int clock = static_cast<int>(std::round(Config::Get(Config::MAIN_OVERCLOCK) * core_clock));
|
||||
return tr("%1% (%2 MHz)").arg(QString::number(percent), QString::number(clock));
|
||||
}());
|
||||
m_cpu_label->setEnabled(enable_cpu_clock_override_widgets);
|
||||
|
||||
QFont vi_bf = font();
|
||||
vi_bf.setBold(Config::GetActiveLayerForConfig(Config::MAIN_VI_OVERCLOCK_ENABLE) !=
|
||||
@ -318,53 +325,16 @@ void AdvancedPane::Update()
|
||||
m_vi_rate_override_checkbox->setChecked(enable_vi_rate_override_widgets);
|
||||
|
||||
m_vi_rate_override_slider->setEnabled(enable_vi_rate_override_widgets);
|
||||
m_vi_rate_override_slider_label->setEnabled(enable_vi_rate_override_widgets);
|
||||
|
||||
{
|
||||
const QSignalBlocker blocker(m_vi_rate_override_slider);
|
||||
m_vi_rate_override_slider->setValue(
|
||||
static_cast<int>(std::round(Config::Get(Config::MAIN_VI_OVERCLOCK) * 100.f)));
|
||||
}
|
||||
|
||||
m_vi_rate_override_slider_label->setText([] {
|
||||
int percent = static_cast<int>(std::round(Config::Get(Config::MAIN_VI_OVERCLOCK) * 100.f));
|
||||
float vps =
|
||||
static_cast<float>(Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate());
|
||||
if (vps == 0.0f || !Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE))
|
||||
vps = 59.94f * Config::Get(Config::MAIN_VI_OVERCLOCK);
|
||||
return tr("%1% (%2 VPS)").arg(QString::number(percent), QString::number(vps, 'f', 2));
|
||||
}());
|
||||
m_vi_label->setEnabled(enable_vi_rate_override_widgets);
|
||||
|
||||
m_ram_override_checkbox->setEnabled(is_uninitialized);
|
||||
SignalBlocking(m_ram_override_checkbox)->setChecked(enable_ram_override_widgets);
|
||||
|
||||
m_mem1_override_slider->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
m_mem1_override_slider_label->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
|
||||
{
|
||||
const QSignalBlocker blocker(m_mem1_override_slider);
|
||||
const u32 mem1_size = Config::Get(Config::MAIN_MEM1_SIZE) / 0x100000;
|
||||
m_mem1_override_slider->setValue(mem1_size);
|
||||
}
|
||||
|
||||
m_mem1_override_slider_label->setText([] {
|
||||
const u32 mem1_size = Config::Get(Config::MAIN_MEM1_SIZE) / 0x100000;
|
||||
return tr("%1 MB (MEM1)").arg(QString::number(mem1_size));
|
||||
}());
|
||||
m_mem1_label->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
|
||||
m_mem2_override_slider->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
m_mem2_override_slider_label->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
|
||||
{
|
||||
const QSignalBlocker blocker(m_mem2_override_slider);
|
||||
const u32 mem2_size = Config::Get(Config::MAIN_MEM2_SIZE) / 0x100000;
|
||||
m_mem2_override_slider->setValue(mem2_size);
|
||||
}
|
||||
|
||||
m_mem2_override_slider_label->setText([] {
|
||||
const u32 mem2_size = Config::Get(Config::MAIN_MEM2_SIZE) / 0x100000;
|
||||
return tr("%1 MB (MEM2)").arg(QString::number(mem2_size));
|
||||
}());
|
||||
m_mem2_label->setEnabled(enable_ram_override_widgets && is_uninitialized);
|
||||
|
||||
m_custom_rtc_checkbox->setEnabled(is_uninitialized);
|
||||
SignalBlocking(m_custom_rtc_checkbox)->setChecked(Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE));
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include <QWidget>
|
||||
|
||||
class ConfigBool;
|
||||
class ConfigFloatSlider;
|
||||
class ConfigSlider;
|
||||
class ConfigSliderU32;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
@ -36,19 +39,19 @@ private:
|
||||
ConfigBool* m_pause_on_panic_checkbox;
|
||||
ConfigBool* m_accurate_cpu_cache_checkbox;
|
||||
ConfigBool* m_cpu_clock_override_checkbox;
|
||||
QSlider* m_cpu_clock_override_slider;
|
||||
QLabel* m_cpu_clock_override_slider_label;
|
||||
ConfigFloatSlider* m_cpu_clock_override_slider;
|
||||
QLabel* m_cpu_label;
|
||||
|
||||
ConfigBool* m_vi_rate_override_checkbox;
|
||||
QSlider* m_vi_rate_override_slider;
|
||||
QLabel* m_vi_rate_override_slider_label;
|
||||
ConfigFloatSlider* m_vi_rate_override_slider;
|
||||
QLabel* m_vi_label;
|
||||
|
||||
ConfigBool* m_custom_rtc_checkbox;
|
||||
QDateTimeEdit* m_custom_rtc_datetime;
|
||||
|
||||
ConfigBool* m_ram_override_checkbox;
|
||||
QSlider* m_mem1_override_slider;
|
||||
QLabel* m_mem1_override_slider_label;
|
||||
QSlider* m_mem2_override_slider;
|
||||
QLabel* m_mem2_override_slider_label;
|
||||
ConfigSliderU32* m_mem1_override_slider;
|
||||
QLabel* m_mem1_label;
|
||||
ConfigSliderU32* m_mem2_override_slider;
|
||||
QLabel* m_mem2_label;
|
||||
};
|
||||
|
@ -95,7 +95,6 @@ void BroadbandAdapterSettingsDialog::InitControls()
|
||||
}
|
||||
|
||||
setWindowTitle(window_title);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
m_address_input = new QLineEdit(current_address);
|
||||
m_address_input->setPlaceholderText(address_placeholder);
|
||||
|
@ -42,7 +42,6 @@ USBDeviceAddToWhitelistDialog::USBDeviceAddToWhitelistDialog(QWidget* parent) :
|
||||
void USBDeviceAddToWhitelistDialog::InitControls()
|
||||
{
|
||||
setWindowTitle(tr("Add New USB Device"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
m_whitelist_buttonbox = new QDialogButtonBox();
|
||||
auto* add_button = new QPushButton(tr("Add"));
|
||||
|
@ -45,7 +45,6 @@ ControllerEmu::InputOverrideFunction InputOverrider::GetInputOverrideFunction()
|
||||
|
||||
TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setWindowIcon(Resources::GetAppIcon());
|
||||
|
||||
QGridLayout* settings_layout = new QGridLayout;
|
||||
|
@ -59,7 +59,6 @@ void Updater::OnUpdateAvailable(const NewVersionInformation& info)
|
||||
QDialog* dialog = new QDialog(m_parent);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
dialog->setWindowTitle(tr("Update available"));
|
||||
dialog->setWindowFlags(dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
auto* label = new QLabel(
|
||||
tr("<h2>A new version of Dolphin is available!</h2>Dolphin %1 is available for "
|
||||
|
@ -96,7 +96,6 @@ static WiiUtils::UpdateResult ShowProgress(QWidget* parent, Callable function, A
|
||||
UpdateProgressDialog dialog{parent};
|
||||
dialog.setLabelText(QObject::tr("Preparing to update...\nThis can take a while."));
|
||||
dialog.setWindowTitle(QObject::tr("Updating"));
|
||||
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
// QProgressDialog doesn't set its minimum size correctly.
|
||||
dialog.setMinimumSize(360, 150);
|
||||
|
||||
|
@ -3,46 +3,54 @@
|
||||
|
||||
#include "VideoCommon/Assets/CustomAsset.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
CustomAsset::CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
|
||||
const CustomAssetLibrary::AssetID& asset_id)
|
||||
: m_owning_library(std::move(library)), m_asset_id(asset_id)
|
||||
const CustomAssetLibrary::AssetID& asset_id, u64 asset_handle)
|
||||
: m_owning_library(std::move(library)), m_asset_id(asset_id), m_handle(asset_handle)
|
||||
{
|
||||
}
|
||||
|
||||
bool CustomAsset::Load()
|
||||
{
|
||||
const auto load_information = LoadImpl(m_asset_id);
|
||||
if (load_information.m_bytes_loaded > 0)
|
||||
{
|
||||
std::lock_guard lk(m_info_lock);
|
||||
m_bytes_loaded = load_information.m_bytes_loaded;
|
||||
m_last_loaded_time = load_information.m_load_time;
|
||||
}
|
||||
return load_information.m_bytes_loaded != 0;
|
||||
}
|
||||
|
||||
CustomAssetLibrary::TimeType CustomAsset::GetLastWriteTime() const
|
||||
{
|
||||
return m_owning_library->GetLastAssetWriteTime(m_asset_id);
|
||||
}
|
||||
|
||||
const CustomAssetLibrary::TimeType& CustomAsset::GetLastLoadedTime() const
|
||||
std::size_t CustomAsset::Load()
|
||||
{
|
||||
std::lock_guard lk(m_info_lock);
|
||||
// The load time needs to come from before the data is actually read.
|
||||
// Using a time point from after the read marks the asset as more up-to-date than it actually is,
|
||||
// and has potential to race (and not be updated) if a change happens immediately after load.
|
||||
const auto load_time = ClockType::now();
|
||||
|
||||
const auto load_information = LoadImpl(m_asset_id);
|
||||
if (load_information.bytes_loaded > 0)
|
||||
{
|
||||
m_bytes_loaded = load_information.bytes_loaded;
|
||||
m_last_loaded_time = load_time;
|
||||
return m_bytes_loaded;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t CustomAsset::Unload()
|
||||
{
|
||||
std::lock_guard lk(m_info_lock);
|
||||
UnloadImpl();
|
||||
return std::exchange(m_bytes_loaded, 0);
|
||||
}
|
||||
|
||||
CustomAsset::TimeType CustomAsset::GetLastLoadedTime() const
|
||||
{
|
||||
return m_last_loaded_time;
|
||||
}
|
||||
|
||||
std::size_t CustomAsset::GetHandle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
const CustomAssetLibrary::AssetID& CustomAsset::GetAssetId() const
|
||||
{
|
||||
return m_asset_id;
|
||||
}
|
||||
|
||||
std::size_t CustomAsset::GetByteSizeInMemory() const
|
||||
{
|
||||
std::lock_guard lk(m_info_lock);
|
||||
return m_bytes_loaded;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
@ -6,9 +6,9 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
@ -17,42 +17,47 @@ namespace VideoCommon
|
||||
class CustomAsset
|
||||
{
|
||||
public:
|
||||
using ClockType = std::chrono::steady_clock;
|
||||
using TimeType = ClockType::time_point;
|
||||
|
||||
CustomAsset(std::shared_ptr<CustomAssetLibrary> library,
|
||||
const CustomAssetLibrary::AssetID& asset_id);
|
||||
const CustomAssetLibrary::AssetID& asset_id, u64 session_id);
|
||||
virtual ~CustomAsset() = default;
|
||||
CustomAsset(const CustomAsset&) = delete;
|
||||
CustomAsset(CustomAsset&&) = delete;
|
||||
CustomAsset& operator=(const CustomAsset&) = delete;
|
||||
CustomAsset& operator=(CustomAsset&&) = delete;
|
||||
|
||||
// Loads the asset from the library returning a pass/fail result
|
||||
bool Load();
|
||||
// Loads the asset from the library returning the number of bytes loaded
|
||||
std::size_t Load();
|
||||
|
||||
// Queries the last time the asset was modified or standard epoch time
|
||||
// if the asset hasn't been modified yet
|
||||
// Note: not thread safe, expected to be called by the loader
|
||||
CustomAssetLibrary::TimeType GetLastWriteTime() const;
|
||||
// Unloads the asset data, resets the bytes loaded and
|
||||
// returns the number of bytes unloaded
|
||||
std::size_t Unload();
|
||||
|
||||
// Returns the time that the data was last loaded
|
||||
const CustomAssetLibrary::TimeType& GetLastLoadedTime() const;
|
||||
TimeType GetLastLoadedTime() const;
|
||||
|
||||
// Returns an id that uniquely identifies this asset
|
||||
const CustomAssetLibrary::AssetID& GetAssetId() const;
|
||||
|
||||
// A rough estimate of how much space this asset
|
||||
// will take in memroy
|
||||
std::size_t GetByteSizeInMemory() const;
|
||||
// Returns an id that is unique to this game session
|
||||
// This is a faster form to hash and can be used
|
||||
// as an index
|
||||
std::size_t GetHandle() const;
|
||||
|
||||
protected:
|
||||
const std::shared_ptr<CustomAssetLibrary> m_owning_library;
|
||||
|
||||
private:
|
||||
virtual CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) = 0;
|
||||
virtual void UnloadImpl() = 0;
|
||||
CustomAssetLibrary::AssetID m_asset_id;
|
||||
std::size_t m_handle;
|
||||
|
||||
mutable std::mutex m_info_lock;
|
||||
std::size_t m_bytes_loaded = 0;
|
||||
CustomAssetLibrary::TimeType m_last_loaded_time = {};
|
||||
std::atomic<TimeType> m_last_loaded_time = {};
|
||||
};
|
||||
|
||||
// An abstract class that is expected to
|
||||
@ -83,6 +88,14 @@ protected:
|
||||
bool m_loaded = false;
|
||||
mutable std::mutex m_data_lock;
|
||||
std::shared_ptr<UnderlyingType> m_data;
|
||||
|
||||
private:
|
||||
void UnloadImpl() override
|
||||
{
|
||||
std::lock_guard lk(m_data_lock);
|
||||
m_loaded = false;
|
||||
m_data.reset();
|
||||
}
|
||||
};
|
||||
|
||||
// A helper struct that contains
|
||||
@ -96,7 +109,7 @@ template <typename AssetType>
|
||||
struct CachedAsset
|
||||
{
|
||||
std::shared_ptr<AssetType> m_asset;
|
||||
VideoCommon::CustomAssetLibrary::TimeType m_cached_write_time;
|
||||
CustomAsset::TimeType m_cached_write_time;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
@ -1,85 +0,0 @@
|
||||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
CustomAssetLibrary::LoadInfo CustomAssetLibrary::LoadGameTexture(const AssetID& asset_id,
|
||||
TextureData* data)
|
||||
{
|
||||
const auto load_info = LoadTexture(asset_id, data);
|
||||
if (load_info.m_bytes_loaded == 0)
|
||||
return {};
|
||||
|
||||
if (data->m_type != TextureData::Type::Type_Texture2D)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Custom asset '{}' is not a valid game texture, it is expected to be a 2d texture "
|
||||
"but was a '{}'.",
|
||||
asset_id, data->m_type);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Note: 'LoadTexture()' ensures we have a level loaded
|
||||
for (std::size_t slice_index = 0; slice_index < data->m_texture.m_slices.size(); slice_index++)
|
||||
{
|
||||
auto& slice = data->m_texture.m_slices[slice_index];
|
||||
const auto& first_mip = slice.m_levels[0];
|
||||
|
||||
// Verify that each mip level is the correct size (divide by 2 each time).
|
||||
u32 current_mip_width = first_mip.width;
|
||||
u32 current_mip_height = first_mip.height;
|
||||
for (u32 mip_level = 1; mip_level < static_cast<u32>(slice.m_levels.size()); mip_level++)
|
||||
{
|
||||
if (current_mip_width != 1 || current_mip_height != 1)
|
||||
{
|
||||
current_mip_width = std::max(current_mip_width / 2, 1u);
|
||||
current_mip_height = std::max(current_mip_height / 2, 1u);
|
||||
|
||||
const VideoCommon::CustomTextureData::ArraySlice::Level& level = slice.m_levels[mip_level];
|
||||
if (current_mip_width == level.width && current_mip_height == level.height)
|
||||
continue;
|
||||
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Invalid custom game texture size {}x{} for texture asset {}. Slice {} with "
|
||||
"mipmap level {} "
|
||||
"must be {}x{}.",
|
||||
level.width, level.height, asset_id, slice_index, mip_level,
|
||||
current_mip_width, current_mip_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It is invalid to have more than a single 1x1 mipmap.
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Custom game texture {} has too many 1x1 mipmaps for slice {}. Skipping extra levels.",
|
||||
asset_id, slice_index);
|
||||
}
|
||||
|
||||
// Drop this mip level and any others after it.
|
||||
while (slice.m_levels.size() > mip_level)
|
||||
slice.m_levels.pop_back();
|
||||
}
|
||||
|
||||
// All levels have to have the same format.
|
||||
if (std::ranges::any_of(slice.m_levels,
|
||||
[&first_mip](const auto& l) { return l.format != first_mip.format; }))
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Custom game texture {} has inconsistent formats across mip levels for slice {}.",
|
||||
asset_id, slice_index);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return load_info;
|
||||
}
|
||||
} // namespace VideoCommon
|
@ -10,10 +10,11 @@
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class CustomTextureData;
|
||||
struct MaterialData;
|
||||
struct MeshData;
|
||||
struct PixelShaderData;
|
||||
struct TextureData;
|
||||
struct TextureAndSamplerData;
|
||||
|
||||
// This class provides functionality to load
|
||||
// specific data (like textures). Where this data
|
||||
@ -21,28 +22,21 @@ struct TextureData;
|
||||
class CustomAssetLibrary
|
||||
{
|
||||
public:
|
||||
using TimeType = std::chrono::system_clock::time_point;
|
||||
|
||||
// The AssetID is a unique identifier for a particular asset
|
||||
using AssetID = std::string;
|
||||
|
||||
struct LoadInfo
|
||||
{
|
||||
std::size_t m_bytes_loaded = 0;
|
||||
TimeType m_load_time = {};
|
||||
std::size_t bytes_loaded = 0;
|
||||
};
|
||||
|
||||
virtual ~CustomAssetLibrary() = default;
|
||||
|
||||
// Loads a texture with a sampler and type, if there are no levels, bytes loaded will be empty
|
||||
virtual LoadInfo LoadTexture(const AssetID& asset_id, TextureAndSamplerData* data) = 0;
|
||||
|
||||
// Loads a texture, if there are no levels, bytes loaded will be empty
|
||||
virtual LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) = 0;
|
||||
|
||||
// Gets the last write time for a given asset id
|
||||
virtual TimeType GetLastAssetWriteTime(const AssetID& asset_id) const = 0;
|
||||
|
||||
// Loads a texture as a game texture, providing additional checks like confirming
|
||||
// each mip level size is correct and that the format is consistent across the data
|
||||
LoadInfo LoadGameTexture(const AssetID& asset_id, TextureData* data);
|
||||
virtual LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) = 0;
|
||||
|
||||
// Loads a pixel shader
|
||||
virtual LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) = 0;
|
||||
|
@ -1,108 +1,157 @@
|
||||
// Copyright 2023 Dolphin Emulator Project
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Assets/CustomAssetLoader.h"
|
||||
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
void CustomAssetLoader::Init()
|
||||
void CustomAssetLoader::Initialize()
|
||||
{
|
||||
m_asset_monitor_thread_shutdown.Clear();
|
||||
|
||||
const size_t sys_mem = Common::MemPhysical();
|
||||
const size_t recommended_min_mem = 2 * size_t(1024 * 1024 * 1024);
|
||||
// keep 2GB memory for system stability if system RAM is 4GB+ - use half of memory in other cases
|
||||
m_max_memory_available =
|
||||
(sys_mem / 2 < recommended_min_mem) ? (sys_mem / 2) : (sys_mem - recommended_min_mem);
|
||||
|
||||
m_asset_monitor_thread = std::thread([this]() {
|
||||
Common::SetCurrentThreadName("Asset monitor");
|
||||
while (true)
|
||||
{
|
||||
if (m_asset_monitor_thread_shutdown.IsSet())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(TIME_BETWEEN_ASSET_MONITOR_CHECKS);
|
||||
|
||||
std::lock_guard lk(m_asset_load_lock);
|
||||
for (auto& [asset_id, asset_to_monitor] : m_assets_to_monitor)
|
||||
{
|
||||
if (auto ptr = asset_to_monitor.lock())
|
||||
{
|
||||
const auto write_time = ptr->GetLastWriteTime();
|
||||
if (write_time > ptr->GetLastLoadedTime())
|
||||
{
|
||||
(void)ptr->Load();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_asset_load_thread.Reset("Custom Asset Loader", [this](std::weak_ptr<CustomAsset> asset) {
|
||||
if (auto ptr = asset.lock())
|
||||
{
|
||||
if (m_memory_exceeded)
|
||||
return;
|
||||
|
||||
if (ptr->Load())
|
||||
{
|
||||
std::lock_guard lk(m_asset_load_lock);
|
||||
const std::size_t asset_memory_size = ptr->GetByteSizeInMemory();
|
||||
m_total_bytes_loaded += asset_memory_size;
|
||||
m_assets_to_monitor.try_emplace(ptr->GetAssetId(), ptr);
|
||||
if (m_total_bytes_loaded > m_max_memory_available)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Asset memory exceeded with asset '{}', future assets won't load until "
|
||||
"memory is available.",
|
||||
ptr->GetAssetId());
|
||||
m_memory_exceeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
ResizeWorkerThreads(2);
|
||||
}
|
||||
|
||||
void CustomAssetLoader::Shutdown()
|
||||
{
|
||||
m_asset_load_thread.StopAndCancel();
|
||||
|
||||
m_asset_monitor_thread_shutdown.Set();
|
||||
m_asset_monitor_thread.join();
|
||||
m_assets_to_monitor.clear();
|
||||
m_total_bytes_loaded = 0;
|
||||
Reset(false);
|
||||
}
|
||||
|
||||
std::shared_ptr<GameTextureAsset>
|
||||
CustomAssetLoader::LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
bool CustomAssetLoader::StartWorkerThreads(u32 num_worker_threads)
|
||||
{
|
||||
return LoadOrCreateAsset<GameTextureAsset>(asset_id, m_game_textures, std::move(library));
|
||||
for (u32 i = 0; i < num_worker_threads; i++)
|
||||
{
|
||||
m_worker_threads.emplace_back(&CustomAssetLoader::WorkerThreadRun, this, i);
|
||||
}
|
||||
|
||||
return HasWorkerThreads();
|
||||
}
|
||||
|
||||
std::shared_ptr<PixelShaderAsset>
|
||||
CustomAssetLoader::LoadPixelShader(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
bool CustomAssetLoader::ResizeWorkerThreads(u32 num_worker_threads)
|
||||
{
|
||||
return LoadOrCreateAsset<PixelShaderAsset>(asset_id, m_pixel_shaders, std::move(library));
|
||||
if (m_worker_threads.size() == num_worker_threads)
|
||||
return true;
|
||||
|
||||
StopWorkerThreads();
|
||||
return StartWorkerThreads(num_worker_threads);
|
||||
}
|
||||
|
||||
std::shared_ptr<MaterialAsset>
|
||||
CustomAssetLoader::LoadMaterial(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
bool CustomAssetLoader::HasWorkerThreads() const
|
||||
{
|
||||
return LoadOrCreateAsset<MaterialAsset>(asset_id, m_materials, std::move(library));
|
||||
return !m_worker_threads.empty();
|
||||
}
|
||||
|
||||
std::shared_ptr<MeshAsset> CustomAssetLoader::LoadMesh(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<CustomAssetLibrary> library)
|
||||
void CustomAssetLoader::StopWorkerThreads()
|
||||
{
|
||||
return LoadOrCreateAsset<MeshAsset>(asset_id, m_meshes, std::move(library));
|
||||
if (!HasWorkerThreads())
|
||||
return;
|
||||
|
||||
// Signal worker threads to stop, and wake all of them.
|
||||
{
|
||||
std::lock_guard guard(m_assets_to_load_lock);
|
||||
m_exit_flag.Set();
|
||||
m_worker_thread_wake.notify_all();
|
||||
}
|
||||
|
||||
// Wait for worker threads to exit.
|
||||
for (std::thread& thr : m_worker_threads)
|
||||
thr.join();
|
||||
m_worker_threads.clear();
|
||||
m_exit_flag.Clear();
|
||||
}
|
||||
|
||||
void CustomAssetLoader::WorkerThreadRun(u32 thread_index)
|
||||
{
|
||||
Common::SetCurrentThreadName(fmt::format("Asset Loader {}", thread_index).c_str());
|
||||
|
||||
std::unique_lock load_lock(m_assets_to_load_lock);
|
||||
while (true)
|
||||
{
|
||||
m_worker_thread_wake.wait(load_lock,
|
||||
[&] { return !m_assets_to_load.empty() || m_exit_flag.IsSet(); });
|
||||
|
||||
if (m_exit_flag.IsSet())
|
||||
return;
|
||||
|
||||
// If more memory than allowed has already been loaded, we will load nothing more
|
||||
// until the next ScheduleAssetsToLoad from Manager.
|
||||
if (m_change_in_memory > m_allowed_memory)
|
||||
{
|
||||
m_assets_to_load.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* const item = m_assets_to_load.front();
|
||||
m_assets_to_load.pop_front();
|
||||
|
||||
// Make sure another thread isn't loading this handle.
|
||||
if (!m_handles_in_progress.insert(item->GetHandle()).second)
|
||||
continue;
|
||||
|
||||
load_lock.unlock();
|
||||
|
||||
// Unload previously loaded asset.
|
||||
m_change_in_memory -= item->Unload();
|
||||
|
||||
const std::size_t bytes_loaded = item->Load();
|
||||
m_change_in_memory += s64(bytes_loaded);
|
||||
|
||||
load_lock.lock();
|
||||
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "CustomAssetLoader thread {} loaded: {} ({})", thread_index,
|
||||
item->GetAssetId(), UICommon::FormatSize(bytes_loaded));
|
||||
|
||||
std::lock_guard lk{m_assets_loaded_lock};
|
||||
m_asset_handles_loaded.emplace_back(item->GetHandle(), bytes_loaded > 0);
|
||||
|
||||
// Make sure no other threads try to re-process this item.
|
||||
// Manager will take the handles and re-ScheduleAssetsToLoad based on timestamps if needed.
|
||||
std::erase(m_assets_to_load, item);
|
||||
}
|
||||
|
||||
m_handles_in_progress.erase(item->GetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
auto CustomAssetLoader::TakeLoadResults() -> LoadResults
|
||||
{
|
||||
std::lock_guard guard(m_assets_loaded_lock);
|
||||
return {std::move(m_asset_handles_loaded), m_change_in_memory.exchange(0)};
|
||||
}
|
||||
|
||||
void CustomAssetLoader::ScheduleAssetsToLoad(std::list<CustomAsset*> assets_to_load,
|
||||
u64 allowed_memory)
|
||||
{
|
||||
if (assets_to_load.empty()) [[unlikely]]
|
||||
return;
|
||||
|
||||
// There's new assets to process, notify worker threads
|
||||
std::lock_guard guard(m_assets_to_load_lock);
|
||||
m_allowed_memory = allowed_memory;
|
||||
m_assets_to_load = std::move(assets_to_load);
|
||||
m_worker_thread_wake.notify_all();
|
||||
}
|
||||
|
||||
void CustomAssetLoader::Reset(bool restart_worker_threads)
|
||||
{
|
||||
const std::size_t worker_thread_count = m_worker_threads.size();
|
||||
StopWorkerThreads();
|
||||
|
||||
m_assets_to_load.clear();
|
||||
m_asset_handles_loaded.clear();
|
||||
m_allowed_memory = 0;
|
||||
m_change_in_memory = 0;
|
||||
|
||||
if (restart_worker_threads)
|
||||
{
|
||||
StartWorkerThreads(static_cast<u32>(worker_thread_count));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user