diff --git a/Makefile.common b/Makefile.common index 5cbed8d5f9..04f80c36b0 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1636,7 +1636,6 @@ DOLPHIN_SOURCES_CXX += \ $(CORE_DIR)/Source/Core/Core/HW/GBACore.cpp \ $(CORE_DIR)/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp \ $(CORE_DIR)/Source/Core/UICommon/GameFile.cpp \ - $(CORE_DIR)/Source/Core/UICommon/GameFileCache.cpp \ $(CORE_DIR)/Source/Core/UICommon/ResourcePack/Manager.cpp \ $(CORE_DIR)/Source/Core/UICommon/ResourcePack/Manifest.cpp \ $(CORE_DIR)/Source/Core/UICommon/ResourcePack/ResourcePack.cpp \ diff --git a/Source/Core/Core/HW/MMIO.h b/Source/Core/Core/HW/MMIO.h index cee439ab43..1f0258cf3f 100644 --- a/Source/Core/Core/HW/MMIO.h +++ b/Source/Core/Core/HW/MMIO.h @@ -4,12 +4,13 @@ #pragma once #include -#include #include #include #include #include +#include + #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Core/HW/GPFifo.h" @@ -85,19 +86,20 @@ inline u16* LowPart(u32* ptr) { return (u16*)ptr; } -inline u16* LowPart(std::atomic* ptr) +/* A register shared between threads: the halves are its storage, as the CPU sees it. */ +inline u16* LowPart(retro_atomic_int_t* ptr) { - static_assert(std::atomic::is_always_lock_free && sizeof(std::atomic) == sizeof(u32)); - return LowPart(std::bit_cast(ptr)); + static_assert(sizeof(retro_atomic_int_t) == sizeof(u32)); + return LowPart(reinterpret_cast(ptr)); } inline u16* HighPart(u32* ptr) { return LowPart(ptr) + 1; } -inline u16* HighPart(std::atomic* ptr) +inline u16* HighPart(retro_atomic_int_t* ptr) { - static_assert(std::atomic::is_always_lock_free && sizeof(std::atomic) == sizeof(u32)); - return HighPart(std::bit_cast(ptr)); + static_assert(sizeof(retro_atomic_int_t) == sizeof(u32)); + return HighPart(reinterpret_cast(ptr)); } } // namespace Utils diff --git a/Source/Core/DiscIO/Blob.h b/Source/Core/DiscIO/Blob.h index 8119694961..e38b45b5ee 100644 --- a/Source/Core/DiscIO/Blob.h +++ b/Source/Core/DiscIO/Blob.h @@ -27,7 +27,6 @@ namespace DiscIO { enum class WIARVZCompressionType : u32; -// Increment CACHE_REVISION (GameFileCache.cpp) if the enum below is modified enum class BlobType { PLAIN, diff --git a/Source/Core/DiscIO/CachedBlob.cpp b/Source/Core/DiscIO/CachedBlob.cpp index 9c0cd23a08..f3b6f7a434 100644 --- a/Source/Core/DiscIO/CachedBlob.cpp +++ b/Source/Core/DiscIO/CachedBlob.cpp @@ -3,9 +3,9 @@ #include "DiscIO/CachedBlob.h" -#include #include +#include #include #include "Common/CommonTypes.h" @@ -29,7 +29,7 @@ public: ~CacheFiller() { - m_stop_thread.store(true, std::memory_order_relaxed); + retro_atomic_store_relaxed_int(&m_stop_thread, 1); Common::JoinThread(m_thread); } @@ -69,7 +69,7 @@ private: CacheState GetCacheState(u64 offset, u64 size) { - const auto cache_pos = m_cache_filled_pos.load(std::memory_order_acquire); + const u64 cache_pos = static_cast(retro_atomic_load_acquire_64(&m_cache_filled_pos)); const auto end_pos = offset + size; if (end_pos > cache_pos) @@ -131,7 +131,7 @@ private: while (true) { - if (m_stop_thread.load(std::memory_order_relaxed)) + if (retro_atomic_load_relaxed_int(&m_stop_thread)) { INFO_LOG_FMT(DISCIO, "CachedBlobReader: Stopped"); break; @@ -164,7 +164,7 @@ private: } read_offset += read_size; - m_cache_filled_pos.store(read_offset, std::memory_order_release); + retro_atomic_store_release_64(&m_cache_filled_pos, static_cast(read_offset)); if (const auto now = Clock::now(); now >= next_log_time) { @@ -176,14 +176,14 @@ private: } // The thread has read non-scrubbed bytes into memory up to this point. - std::atomic m_cache_filled_pos{}; + retro_atomic_64_t m_cache_filled_pos{}; Common::LazyMemoryRegion m_memory_region; u8* m_memory_region_data{}; DiscScrubber m_scrubber; - std::atomic_bool m_stop_thread{}; + retro_atomic_int_t m_stop_thread{}; std::unique_ptr m_thread_reader; // handed to the thread bool m_attempt_to_scrub; sthread_t* m_thread = nullptr; diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h index 62ad9e846a..6eafca957b 100644 --- a/Source/Core/DiscIO/Enums.h +++ b/Source/Core/DiscIO/Enums.h @@ -10,7 +10,6 @@ namespace DiscIO { -// Increment CACHE_REVISION (GameFileCache.cpp) if these enums are modified enum class Platform { diff --git a/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h b/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h index 839b3f7949..7c1ab8ec45 100644 --- a/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h +++ b/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include #include #include @@ -15,8 +15,38 @@ #include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControllerInterface/CoreDevice.h" +#include + namespace ControllerEmu { +/* A setting's value lives in one atomic 64-bit word, as its bits. */ +inline s64 SettingToBits(double value) +{ + s64 bits; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; +} +inline s64 SettingToBits(int value) +{ + return value; +} +inline s64 SettingToBits(bool value) +{ + return value ? 1 : 0; +} +inline void SettingFromBits(s64 bits, double* value) +{ + std::memcpy(value, &bits, sizeof(*value)); +} +inline void SettingFromBits(s64 bits, int* value) +{ + *value = static_cast(bits); +} +inline void SettingFromBits(s64 bits, bool* value) +{ + *value = bits != 0; +} + enum class SettingType { Int, @@ -185,26 +215,36 @@ public: // Otherwise settings will all change to 0 when window focus is lost. // This is very undesirable for things like battery level or attached extension. if (!IsSimpleValue() && ControlReference::GetInputGate()) - m_value = m_input.GetState(); + StoreValue(m_input.GetState()); - return m_value; + return GetCachedValue(); } - ValueType GetCachedValue() const { return m_value; } + ValueType GetCachedValue() const + { + ValueType value; + SettingFromBits(retro_atomic_load_relaxed_64(&m_value), &value); + return value; + } bool IsSimpleValue() const { return !m_input.HasExpression(); } virtual void SetValue(const ValueType value) { - m_value = value; + StoreValue(value); // Clear the expression to use our new "simple" value. m_input.SetExpression(""); } private: + void StoreValue(ValueType value) const + { + retro_atomic_store_relaxed_64(&m_value, SettingToBits(value)); + } + // Values are R/W by both UI and CPU threads. - mutable std::atomic m_value = {}; + mutable retro_atomic_64_t m_value{}; // Unfortunately InputReference's state grabbing is non-const requiring mutable here. mutable InputReference m_input; diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp index 467cb11000..9e0dd0f4fa 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.cpp @@ -4,8 +4,8 @@ #include "InputCommon/ControllerInterface/Android/Android.h" #include -#include #include +#include #include #include #include @@ -17,6 +17,8 @@ #include #include +#include + #include "Common/Assert.h" #include "Common/Contains.h" #include "Common/Logging/Log.h" @@ -31,6 +33,21 @@ namespace { +/* A ControlState shared between threads lives in an atomic 64-bit word, as its bits. */ +s64 StateToBits(ControlState state) +{ + s64 bits; + std::memcpy(&bits, &state, sizeof(bits)); + return bits; +} + +ControlState StateFromBits(s64 bits) +{ + ControlState state; + std::memcpy(&state, &bits, sizeof(state)); + return state; +} + std::string SOURCE = "Android"; jclass s_list_class; @@ -496,18 +513,21 @@ public: ControlState GetState() const override { - m_last_polled.store(Clock::now(), std::memory_order_relaxed); - return m_state.load(std::memory_order_relaxed); + retro_atomic_store_relaxed_64(&m_last_polled, Clock::now().time_since_epoch().count()); + return StateFromBits(retro_atomic_load_relaxed_64(&m_state)); } - void SetState(ControlState state) { m_state.store(state, std::memory_order_relaxed); } + void SetState(ControlState state) { retro_atomic_store_relaxed_64(&m_state, StateToBits(state)); } - Clock::time_point GetLastPolled() const { return m_last_polled.load(std::memory_order_relaxed); } + Clock::time_point GetLastPolled() const + { + return Clock::time_point(Clock::duration(retro_atomic_load_relaxed_64(&m_last_polled))); + } private: std::string m_name; - std::atomic m_state = 0; - mutable std::atomic m_last_polled{}; + retro_atomic_64_t m_state{}; + mutable retro_atomic_64_t m_last_polled{}; }; class AndroidKey final : public AndroidInput @@ -556,7 +576,7 @@ public: ControlState GetState() const override { - if (m_is_suspended.load(std::memory_order_relaxed)) + if (retro_atomic_load_relaxed_int(&m_is_suspended)) { IDCache::GetEnvForThread()->CallVoidMethod( m_sensor_event_listener, s_sensor_event_listener_request_unsuspend_sensor, m_j_name); @@ -572,13 +592,13 @@ public: void NotifyIsSuspended(bool is_suspended) { - m_is_suspended.store(is_suspended, std::memory_order_relaxed); + retro_atomic_store_relaxed_int(&m_is_suspended, is_suspended ? 1 : 0); } private: const jobject m_sensor_event_listener; const jstring m_j_name; - std::atomic m_is_suspended = true; + retro_atomic_int_t m_is_suspended{1}; }; class AndroidMotor : public Core::Device::Output @@ -595,7 +615,7 @@ public: void SetState(ControlState state) override { - ControlState old_state = m_state.exchange(state, std::memory_order_relaxed); + ControlState old_state = StateFromBits(retro_atomic_exchange_64(&m_state, StateToBits(state))); if (old_state < 0.5 && state >= 0.5) { @@ -612,7 +632,7 @@ public: private: const jobject m_vibrator; const jint m_index; - std::atomic m_state = 0; + retro_atomic_64_t m_state{}; }; class AndroidDevice final : public Core::Device diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index d5dc313f52..c584d4b4c2 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -4,6 +4,7 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include +#include #include "Common/Assert.h" #include "Common/Logging/Log.h" @@ -31,12 +32,12 @@ static thread_local bool tls_is_updating_devices = false; void ControllerInterface::Initialize(const WindowSystemInfo& wsi) { - if (m_is_init) + if (IsInit()) return; m_wsi = wsi; - m_populating_devices_counter = 1; + retro_atomic_store_release_int(&m_populating_devices_counter, 1); #if defined(CIFACE_USE_ANDROID) && !defined(__LIBRETRO__) m_input_backends.emplace_back(ciface::Android::CreateInputBackend(this)); @@ -50,7 +51,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) // Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned // there. Or they'd end up waiting on the devices mutex if populated from another thread. - m_is_init = true; + retro_atomic_store_release_int(&m_is_init, 1); RefreshDevices(); @@ -61,13 +62,13 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) devices_empty = !list || list->devices.empty(); } - if (m_populating_devices_counter.fetch_sub(1) == 1 && !devices_empty) + if (retro_atomic_fetch_sub_int(&m_populating_devices_counter, 1) == 1 && !devices_empty) InvokeDevicesChangedCallbacks(); } void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason) { - if (!m_is_init) + if (!IsInit()) return; if (m_wsi.render_window == hwnd) @@ -85,7 +86,7 @@ void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason) void ControllerInterface::RefreshDevices(RefreshReason reason) { - if (!m_is_init) + if (!IsInit()) return; // Backends with a hotplug thread may add or remove devices meanwhile; every change is @@ -94,18 +95,18 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) // Instead only refresh devices that require the window handle. if (reason == RefreshReason::WindowChangeOnly) { - m_populating_devices_counter.fetch_add(1); + retro_atomic_fetch_add_int(&m_populating_devices_counter, 1); for (auto& backend : m_input_backends) backend->HandleWindowChange(); - if (m_populating_devices_counter.fetch_sub(1) == 1) + if (retro_atomic_fetch_sub_int(&m_populating_devices_counter, 1) == 1) InvokeDevicesChangedCallbacks(); return; } - m_populating_devices_counter.fetch_add(1); + retro_atomic_fetch_add_int(&m_populating_devices_counter, 1); // Make sure shared_ptr objects are released before repopulating. ClearDevices(); @@ -123,33 +124,33 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) WiimoteReal::PopulateDevices(); - if (m_populating_devices_counter.fetch_sub(1) == 1) + if (retro_atomic_fetch_sub_int(&m_populating_devices_counter, 1) == 1) InvokeDevicesChangedCallbacks(); } void ControllerInterface::PlatformPopulateDevices(const std::function& callback) { - if (!m_is_init) + if (!IsInit()) return; - m_populating_devices_counter.fetch_add(1); + retro_atomic_fetch_add_int(&m_populating_devices_counter, 1); callback(); - if (m_populating_devices_counter.fetch_sub(1) == 1) + if (retro_atomic_fetch_sub_int(&m_populating_devices_counter, 1) == 1) InvokeDevicesChangedCallbacks(); } // Remove all devices and call library cleanup functions void ControllerInterface::Shutdown() { - if (!m_is_init) + if (!IsInit()) return; // Prevent additional devices from being added during shutdown. - m_is_init = false; + retro_atomic_store_release_int(&m_is_init, 0); // Additional safety measure to avoid InvokeDevicesChangedCallbacks() - m_populating_devices_counter = 1; + retro_atomic_store_release_int(&m_populating_devices_counter, 1); // Update control references so shared_ptrs are freed up BEFORE we shutdown the backends. ClearDevices(); @@ -191,7 +192,7 @@ void ControllerInterface::ClearDevices() bool ControllerInterface::AddDevice(std::shared_ptr device) { // If we are shutdown (or in process of shutting down) ignore this request: - if (!m_is_init) + if (!IsInit()) return false; ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices, @@ -236,7 +237,7 @@ bool ControllerInterface::AddDevice(std::shared_ptr device }); NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Added device: {}", device->GetQualifiedName()); - if (!m_populating_devices_counter) + if (!retro_atomic_load_acquire_int(&m_populating_devices_counter)) InvokeDevicesChangedCallbacks(); return true; } @@ -245,7 +246,7 @@ void ControllerInterface::RemoveDevice(std::function 1) return {1.f, ar}; @@ -343,12 +349,12 @@ Common::Vec2 ControllerInterface::GetWindowInputScale() const void ControllerInterface::SetMouseCenteringRequested(bool center) { - m_requested_mouse_centering = center; + retro_atomic_store_release_int(&m_requested_mouse_centering, center ? 1 : 0); } bool ControllerInterface::IsMouseCenteringRequested() const { - return m_requested_mouse_centering.load(); + return retro_atomic_load_acquire_int(&m_requested_mouse_centering) != 0; } // Register a callback to be called when a device is added or removed (as from the input backends' diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 5fa6440c3b..d13bcb73b6 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include @@ -14,6 +13,8 @@ #include "InputCommon/ControllerInterface/CoreDevice.h" #include "InputCommon/ControllerInterface/InputBackend.h" +#include + // enable disable sources #define CIFACE_USE_DUALSHOCKUDPCLIENT #if defined(HAVE_HIDAPI) @@ -62,7 +63,7 @@ public: Other }; - ControllerInterface() : m_is_init(false) {} + ControllerInterface() {} void Initialize(const WindowSystemInfo& wsi); // Only call from one thread at a time. void ChangeWindow(void* hwnd, WindowChangeReason reason = WindowChangeReason::Other); @@ -78,7 +79,7 @@ public: // Runs a backend's population, holding back the devices-changed callbacks until it is // done so they run once for the batch. void PlatformPopulateDevices(const std::function& callback); - bool IsInit() const { return m_is_init; } + bool IsInit() const { return retro_atomic_load_acquire_int(&m_is_init) != 0; } void UpdateInput(); // Set adjustment from the full render window aspect-ratio to the drawn aspect-ratio. @@ -109,12 +110,12 @@ private: Common::HookableEvent<> m_devices_changed_event; - std::atomic m_is_init; + retro_atomic_int_t m_is_init{}; // Populations in progress on any thread; the last to finish runs the callbacks. - std::atomic m_populating_devices_counter; + retro_atomic_int_t m_populating_devices_counter{}; WindowSystemInfo m_wsi; - std::atomic m_aspect_ratio_adjustment = 1; - std::atomic m_requested_mouse_centering = false; + retro_atomic_int_t m_aspect_ratio_adjustment{0x3f800000}; /* the bits of a float, 1.0f */ + retro_atomic_int_t m_requested_mouse_centering{}; std::vector> m_input_backends; }; diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp index e7724c1840..7af96bdbef 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp @@ -3,6 +3,8 @@ #include "InputCommon/ControllerInterface/Wiimote/WiimoteController.h" +#include + #include "Common/BitUtils.h" #include "Common/Logging/Log.h" #include "Common/MathUtil.h" @@ -81,14 +83,19 @@ using UndetectableSignedAnalogInput = SignedInput; class Motor final : public Core::Device::Output { public: - Motor(std::atomic* value) : m_value(*value) {} + Motor(retro_atomic_64_t* value) : m_value(value) {} std::string GetName() const override { return "Motor"; } - void SetState(ControlState state) override { m_value = state; } + void SetState(ControlState state) override + { + s64 bits; + std::memcpy(&bits, &state, sizeof(bits)); + retro_atomic_store_release_64(m_value, bits); + } private: - std::atomic& m_value; + retro_atomic_64_t* m_value; }; template @@ -1422,8 +1429,11 @@ void Device::UpdateRumble() { static constexpr auto rumble_period = std::chrono::milliseconds(100); - const auto on_time = - std::chrono::duration_cast(rumble_period * m_rumble_level.load()); + const s64 bits = retro_atomic_load_acquire_64(&m_rumble_level); + ControlState rumble_level; + std::memcpy(&rumble_level, &bits, sizeof(rumble_level)); + + const auto on_time = std::chrono::duration_cast(rumble_period * rumble_level); const auto off_time = rumble_period - on_time; const auto now = Clock::now(); diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h index db0dcb138b..e992073cf8 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include #include #include @@ -18,6 +17,8 @@ #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "InputCommon/ControllerInterface/CoreDevice.h" +#include + namespace ciface::WiimoteController { using namespace WiimoteCommon; @@ -264,7 +265,7 @@ private: bool m_rumble = false; // For pulse of rumble motor to simulate multiple levels. - std::atomic m_rumble_level; + retro_atomic_64_t m_rumble_level{}; /* the bits of a ControlState */ Clock::time_point m_last_rumble_change = Clock::now(); // Assume mode is disabled so one gets set. diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp deleted file mode 100644 index 898e818852..0000000000 --- a/Source/Core/UICommon/GameFileCache.cpp +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "UICommon/GameFileCache.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "Common/ChunkFile.h" -#include "Common/CommonTypes.h" -#include "Common/FileSearch.h" -#include "Common/FileUtil.h" -#include "Common/IOFile.h" - -#include "DiscIO/DirectoryBlob.h" - -#include "UICommon/GameFile.h" - -namespace UICommon -{ -static constexpr u32 CACHE_REVISION = 27; // Last changed in PR 13844 - -std::vector FindAllGamePaths(std::span directories_to_scan, - bool recursive_scan) -{ - constexpr auto search_extensions = - std::to_array({".gcm", ".tgc", ".bin", ".iso", ".ciso", ".gcz", ".wbfs", - ".wia", ".rvz", ".nfs", ".wad", ".dol", ".elf", ".json"}); - - // TODO: We could process paths iteratively as they are found - return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan); -} - -GameFileCache::GameFileCache() : m_path(File::GetUserPath(D_CACHE_IDX) + "gamelist.cache") -{ -} - -void GameFileCache::ForEach(const ForEachFn& f) const -{ - for (const std::shared_ptr& item : m_cached_files) - f(item); -} - -size_t GameFileCache::GetSize() const -{ - return m_cached_files.size(); -} - -void GameFileCache::Clear(DeleteOnDisk delete_on_disk) -{ - if (delete_on_disk != DeleteOnDisk::No) - File::Delete(m_path); - - m_cached_files.clear(); -} - -std::shared_ptr GameFileCache::AddOrGet(const std::string& path, - bool* cache_changed) -{ - auto it = std::ranges::find(m_cached_files, path, &GameFile::GetFilePath); - const bool found = it != m_cached_files.cend(); - if (!found) - { - std::shared_ptr game = std::make_shared(path); - if (!game->IsValid()) - return nullptr; - m_cached_files.emplace_back(std::move(game)); - } - std::shared_ptr& result = found ? *it : m_cached_files.back(); - if (UpdateAdditionalMetadata(&result) || !found) - *cache_changed = true; - - return result; -} - -bool GameFileCache::Update(std::span all_game_paths, - const GameAddedToCacheFn& game_added_to_cache, - const GameRemovedFromCacheFn& game_removed_from_cache, - const std::atomic_bool& processing_halted) -{ - // Copy game paths into a set, except ones that match DiscIO::ShouldHideFromGameList. - // TODO: Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all? - // TODO: Make DoFileSearch support filter predicates so we don't have remove things afterwards? - std::unordered_set game_paths; - game_paths.reserve(all_game_paths.size()); - for (const std::string& path : all_game_paths) - { - if (!DiscIO::ShouldHideFromGameList(path)) - game_paths.insert(path); - } - - bool cache_changed = false; - - // Delete paths that aren't in game_paths from m_cached_files, - // while simultaneously deleting paths that are in m_cached_files from game_paths. - // For the sake of speed, we don't care about maintaining the order of m_cached_files. - { - auto it = m_cached_files.begin(); - auto end = m_cached_files.end(); - while (it != end) - { - if (processing_halted) - break; - - if (game_paths.erase((*it)->GetFilePath())) - { - ++it; - } - else - { - if (game_removed_from_cache) - game_removed_from_cache((*it)->GetFilePath()); - - cache_changed = true; - --end; - *it = std::move(*end); - } - } - m_cached_files.erase(it, m_cached_files.end()); - } - - // Now that the previous loop has run, game_paths only contains paths that - // aren't in m_cached_files, so we simply add all of them to m_cached_files. - for (const std::string& path : game_paths) - { - if (processing_halted) - break; - - auto file = std::make_shared(path); - if (file->IsValid()) - { - if (game_added_to_cache) - game_added_to_cache(file); - - cache_changed = true; - m_cached_files.push_back(std::move(file)); - } - } - - return cache_changed; -} - -bool GameFileCache::UpdateAdditionalMetadata(const GameUpdatedFn& game_updated, - const std::atomic_bool& processing_halted) -{ - bool cache_changed = false; - - for (std::shared_ptr& file : m_cached_files) - { - if (processing_halted) - break; - - const bool updated = UpdateAdditionalMetadata(&file); - cache_changed |= updated; - if (game_updated && updated) - game_updated(file); - } - - return cache_changed; -} - -bool GameFileCache::UpdateAdditionalMetadata(std::shared_ptr* game_file) -{ - const bool xml_metadata_changed = (*game_file)->XMLMetadataChanged(); - const bool wii_banner_changed = (*game_file)->WiiBannerChanged(); - const bool custom_banner_changed = (*game_file)->CustomBannerChanged(); - - (*game_file)->DownloadDefaultCover(); - - const bool default_cover_changed = (*game_file)->DefaultCoverChanged(); - const bool custom_cover_changed = (*game_file)->CustomCoverChanged(); - - if (!xml_metadata_changed && !wii_banner_changed && !custom_banner_changed && - !default_cover_changed && !custom_cover_changed) - { - return false; - } - - // If a cached file needs an update, apply the updates to a copy and delete the original. - // This makes the usage of cached files in other threads safe. - - std::shared_ptr copy = std::make_shared(**game_file); - if (xml_metadata_changed) - copy->XMLMetadataCommit(); - if (wii_banner_changed) - copy->WiiBannerCommit(); - if (custom_banner_changed) - copy->CustomBannerCommit(); - if (default_cover_changed) - copy->DefaultCoverCommit(); - if (custom_cover_changed) - copy->CustomCoverCommit(); - - *game_file = std::move(copy); - - return true; -} - -bool GameFileCache::Load() -{ - return SyncCacheFile(false); -} - -bool GameFileCache::Save() -{ - return SyncCacheFile(true); -} - -bool GameFileCache::SyncCacheFile(bool save) -{ - const char* open_mode = save ? "wb" : "rb"; - File::IOFile f(m_path, open_mode); - if (!f) - return false; - bool success = false; - if (save) - { - // Measure the size of the buffer. - u8* ptr = nullptr; - PointerWrap p_measure(&ptr, 0, PointerWrap::Mode::Measure); - DoState(&p_measure); - const size_t buffer_size = reinterpret_cast(ptr); - - // Then actually do the write. - std::vector buffer(buffer_size); - ptr = buffer.data(); - PointerWrap p(&ptr, buffer_size, PointerWrap::Mode::Write); - DoState(&p, buffer_size); - if (f.WriteBytes(buffer.data(), buffer.size())) - success = true; - } - else - { - std::vector buffer(f.GetSize()); - if (!buffer.empty() && f.ReadBytes(buffer.data(), buffer.size())) - { - u8* ptr = buffer.data(); - PointerWrap p(&ptr, buffer.size(), PointerWrap::Mode::Read); - DoState(&p, buffer.size()); - if (p.IsReadMode()) - success = true; - } - } - if (!success) - { - // If some file operation failed, try to delete the probably-corrupted cache - f.Close(); - File::Delete(m_path); - } - return success; -} - -void GameFileCache::DoState(PointerWrap* p, u64 size) -{ - struct - { - u32 revision; - u64 expected_size; - } header = {CACHE_REVISION, size}; - p->Do(header); - if (p->IsReadMode()) - { - if (header.revision != CACHE_REVISION || header.expected_size != size) - { - p->SetMeasureMode(); - return; - } - } - p->DoEachElement(m_cached_files, [](PointerWrap& state, std::shared_ptr& elem) { - if (state.IsReadMode()) - elem = std::make_shared(); - elem->DoState(state); - }); -} - -} // namespace UICommon diff --git a/Source/Core/UICommon/GameFileCache.h b/Source/Core/UICommon/GameFileCache.h deleted file mode 100644 index 4c4ab15557..0000000000 --- a/Source/Core/UICommon/GameFileCache.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" - -class PointerWrap; - -namespace UICommon -{ -class GameFile; - -std::vector FindAllGamePaths(std::span directories_to_scan, - bool recursive_scan); - -class GameFileCache -{ -public: - enum class DeleteOnDisk - { - No = 0, - Yes = 1, - }; - - using ForEachFn = std::function&)>; - using GameAddedToCacheFn = std::function&)>; - using GameRemovedFromCacheFn = std::function; - using GameUpdatedFn = std::function&)>; - - GameFileCache(); - - void ForEach(const ForEachFn& f) const; - - size_t GetSize() const; - void Clear(DeleteOnDisk delete_on_disk); - - // Returns nullptr if the file is invalid. - std::shared_ptr AddOrGet(const std::string& path, bool* cache_changed); - - // These functions return true if the call modified the cache. - bool Update(std::span all_game_paths, - const GameAddedToCacheFn& game_added_to_cache = {}, - const GameRemovedFromCacheFn& game_removed_from_cache = {}, - const std::atomic_bool& processing_halted = false); - bool UpdateAdditionalMetadata(const GameUpdatedFn& game_updated = {}, - const std::atomic_bool& processing_halted = false); - - bool Load(); - bool Save(); - -private: - bool UpdateAdditionalMetadata(std::shared_ptr* game_file); - - bool SyncCacheFile(bool save); - void DoState(PointerWrap* p, u64 size = 0); - - std::string m_path; - std::vector> m_cached_files; -}; - -} // namespace UICommon diff --git a/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp index 631d164847..146cff1317 100644 --- a/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/D3DNativeVertexFormat.cpp @@ -142,7 +142,8 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& vtx_decl) D3DVertexFormat::~D3DVertexFormat() { - ID3D11InputLayout* layout = m_layout.load(); + ID3D11InputLayout* layout = + static_cast(retro_atomic_load_acquire_ptr(&m_layout)); if (layout) layout->Release(); } @@ -151,7 +152,8 @@ ID3D11InputLayout* D3DVertexFormat::GetInputLayout(const void* vs_bytecode, size { // CreateInputLayout requires a shader input, but it only looks at the signature of the shader, // so we don't need to recompute it if the shader changes. - ID3D11InputLayout* layout = m_layout.load(); + ID3D11InputLayout* layout = + static_cast(retro_atomic_load_acquire_ptr(&m_layout)); if (layout) return layout; @@ -161,13 +163,12 @@ ID3D11InputLayout* D3DVertexFormat::GetInputLayout(const void* vs_bytecode, size // This method can be called from multiple threads, so ensure that only one thread sets the // cached input layout pointer. If another thread beats this thread, use the existing layout. - ID3D11InputLayout* expected = nullptr; - if (!m_layout.compare_exchange_strong(expected, layout)) + if (!retro_atomic_cas_ptr(&m_layout, nullptr, layout)) { if (layout) layout->Release(); - layout = expected; + layout = static_cast(retro_atomic_load_acquire_ptr(&m_layout)); } return layout; diff --git a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp index 1da7f2683d..014b5a2ce2 100644 --- a/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D/D3DPerfQuery.cpp @@ -27,20 +27,20 @@ PerfQuery::~PerfQuery() = default; void PerfQuery::EnableQuery(PerfQueryGroup group) { - u32 query_count = m_query_count.load(std::memory_order_relaxed); + u32 query_count = QueryCount(); // Is this sane? if (query_count > m_query_buffer.size() / 2) { WeakFlush(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); } if (m_query_buffer.size() == query_count) { // TODO FlushOne(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); ERROR_LOG_FMT(VIDEO, "Flushed query buffer early!"); } @@ -52,7 +52,7 @@ void PerfQuery::EnableQuery(PerfQueryGroup group) D3D::context->Begin(entry.query.Get()); entry.query_group = group; - m_query_count.fetch_add(1, std::memory_order_relaxed); + AddQueryCount(1); } } @@ -61,8 +61,7 @@ void PerfQuery::DisableQuery(PerfQueryGroup group) // stop query if (group == PQG_ZCOMP_ZCOMPLOC || group == PQG_ZCOMP) { - auto& entry = m_query_buffer[(m_query_read_pos + m_query_count.load(std::memory_order_relaxed) + - m_query_buffer.size() - 1) % + auto& entry = m_query_buffer[(m_query_read_pos + QueryCount() + m_query_buffer.size() - 1) % m_query_buffer.size()]; D3D::context->End(entry.query.Get()); } @@ -70,9 +69,7 @@ void PerfQuery::DisableQuery(PerfQueryGroup group) void PerfQuery::ResetQuery() { - m_query_count.store(0, std::memory_order_relaxed); - for (auto& result : m_results) - result.store(0, std::memory_order_relaxed); + ResetQueryState(); } u32 PerfQuery::GetQueryResult(PerfQueryType type) @@ -81,20 +78,19 @@ u32 PerfQuery::GetQueryResult(PerfQueryType type) if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC) { - result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP); } else if (type == PQ_BLEND_INPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) + - m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP) + QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_EFB_COPY_CLOCKS) { - result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed); + result = QueryResult(PQG_EFB_COPY_CLOCKS); } return result / 4; @@ -119,11 +115,10 @@ void PerfQuery::FlushOne() g_framebuffer_manager->GetEFBHeight(); if (g_ActiveConfig.iMultisamples > 1) native_res_result /= g_ActiveConfig.iMultisamples; - m_results[entry.query_group].fetch_add(static_cast(native_res_result), - std::memory_order_relaxed); + AddQueryResult(entry.query_group, static_cast(native_res_result)); m_query_read_pos = (m_query_read_pos + 1) % m_query_buffer.size(); - m_query_count.fetch_sub(1, std::memory_order_relaxed); + SubQueryCount(1); } // TODO: could selectively flush things, but I don't think that will do much @@ -148,11 +143,10 @@ void PerfQuery::WeakFlush() // NOTE: Reported pixel metrics should be referenced to native resolution const u64 native_res_result = result * EFB_WIDTH / g_framebuffer_manager->GetEFBWidth() * EFB_HEIGHT / g_framebuffer_manager->GetEFBHeight(); - m_results[entry.query_group].store(static_cast(native_res_result), - std::memory_order_relaxed); + SetQueryResult(entry.query_group, static_cast(native_res_result)); m_query_read_pos = (m_query_read_pos + 1) % m_query_buffer.size(); - m_query_count.fetch_sub(1, std::memory_order_relaxed); + SubQueryCount(1); } else { @@ -163,7 +157,7 @@ void PerfQuery::WeakFlush() bool PerfQuery::IsFlushed() const { - return m_query_count.load(std::memory_order_relaxed) == 0; + return QueryCount() == 0; } } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/D3DVertexManager.h b/Source/Core/VideoBackends/D3D/D3DVertexManager.h index 18a9194ece..dcbbedb43d 100644 --- a/Source/Core/VideoBackends/D3D/D3DVertexManager.h +++ b/Source/Core/VideoBackends/D3D/D3DVertexManager.h @@ -4,10 +4,11 @@ #pragma once #include -#include #include #include +#include + #include "VideoBackends/D3D/D3DBase.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/VertexManagerBase.h" @@ -29,7 +30,7 @@ private: std::array m_elems{}; UINT m_num_elems = 0; - std::atomic m_layout{nullptr}; + retro_atomic_ptr_t m_layout{}; }; class VertexManager : public VertexManagerBase diff --git a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp index a27633c344..df7d6677cb 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp +++ b/Source/Core/VideoBackends/D3D12/D3D12PerfQuery.cpp @@ -55,7 +55,7 @@ void PerfQuery::EnableQuery(PerfQueryGroup group) { // Block if there are no free slots. // Otherwise, try to keep half of them available. - const u32 query_count = m_query_count.load(std::memory_order_relaxed); + const u32 query_count = QueryCount(); if (query_count > m_query_buffer.size() / 2) { const bool do_resolve = m_unresolved_queries > m_query_buffer.size() / 2; @@ -88,20 +88,19 @@ void PerfQuery::DisableQuery(PerfQueryGroup group) g_dx_context->GetCommandList()->EndQuery(m_query_heap.Get(), D3D12_QUERY_TYPE_OCCLUSION, m_query_next_pos); m_query_next_pos = (m_query_next_pos + 1) % PERF_QUERY_BUFFER_SIZE; - m_query_count.fetch_add(1, std::memory_order_relaxed); + AddQueryCount(1); m_unresolved_queries++; } } void PerfQuery::ResetQuery() { - m_query_count.store(0, std::memory_order_relaxed); + retro_atomic_store_release_int(&m_query_count, 0); m_unresolved_queries = 0; m_query_resolve_pos = 0; m_query_readback_pos = 0; m_query_next_pos = 0; - for (size_t i = 0; i < m_results.size(); ++i) - m_results[i].store(0, std::memory_order_relaxed); + ResetQueryResults(); for (auto& entry : m_query_buffer) { entry.fence_value = 0; @@ -115,20 +114,19 @@ u32 PerfQuery::GetQueryResult(PerfQueryType type) u32 result = 0; if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC) { - result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP); } else if (type == PQ_BLEND_INPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) + - m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP) + QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_EFB_COPY_CLOCKS) { - result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed); + result = QueryResult(PQG_EFB_COPY_CLOCKS); } return result / 4; @@ -142,7 +140,7 @@ void PerfQuery::FlushResults() bool PerfQuery::IsFlushed() const { - return m_query_count.load(std::memory_order_relaxed) == 0; + return QueryCount() == 0; } void PerfQuery::ResolveQueries() @@ -180,7 +178,7 @@ void PerfQuery::ReadbackQueries(bool blocking) u64 completed_fence_counter = g_dx_context->GetCompletedFenceValue(); // Need to save these since ProcessResults will modify them. - const u32 outstanding_queries = m_query_count.load(std::memory_order_relaxed); + const u32 outstanding_queries = QueryCount(); u32 readback_count = 0; for (u32 i = 0; i < outstanding_queries; i++) { @@ -218,7 +216,7 @@ void PerfQuery::ReadbackQueries(bool blocking) void PerfQuery::AccumulateQueriesFromBuffer(u32 query_count) { // Should be at maximum query_count queries pending. - ASSERT(query_count <= m_query_count.load(std::memory_order_relaxed) && + ASSERT(query_count <= QueryCount() && (m_query_readback_pos + query_count) <= PERF_QUERY_BUFFER_SIZE); const D3D12_RANGE read_range = {m_query_readback_pos * sizeof(PerfQueryDataType), @@ -251,15 +249,14 @@ void PerfQuery::AccumulateQueriesFromBuffer(u32 query_count) g_framebuffer_manager->GetEFBHeight(); if (g_ActiveConfig.iMultisamples > 1) native_res_result /= g_ActiveConfig.iMultisamples; - m_results[entry.query_group].fetch_add(static_cast(native_res_result), - std::memory_order_relaxed); + AddQueryResult(entry.query_group, static_cast(native_res_result)); } constexpr D3D12_RANGE write_range = {0, 0}; m_query_readback_buffer->Unmap(0, &write_range); m_query_readback_pos = (m_query_readback_pos + query_count) % PERF_QUERY_BUFFER_SIZE; - m_query_count.fetch_sub(query_count, std::memory_order_relaxed); + SubQueryCount(query_count); } void PerfQuery::PartialFlush(bool resolve, bool blocking) diff --git a/Source/Core/VideoBackends/Metal/MTLPerfQuery.h b/Source/Core/VideoBackends/Metal/MTLPerfQuery.h index 993b92e93c..63711bcfc4 100644 --- a/Source/Core/VideoBackends/Metal/MTLPerfQuery.h +++ b/Source/Core/VideoBackends/Metal/MTLPerfQuery.h @@ -22,7 +22,7 @@ public: /// Notify PerfQuery of a new pending encoder /// One call to ReturnResults should be made for every call to IncCount - void IncCount() { m_query_count.fetch_add(1, std::memory_order_relaxed); } + void IncCount() { AddQueryCount(1); } /// May be called from any thread void ReturnResults(const u64* data, const PerfQueryGroup* groups, size_t count, u32 query_id); diff --git a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm index 37036f28aa..c60888fa2e 100644 --- a/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm +++ b/Source/Core/VideoBackends/Metal/MTLPerfQuery.mm @@ -21,8 +21,7 @@ void Metal::PerfQuery::ResetQuery() { std::lock_guard lock(m_results_mtx); m_current_query++; - for (std::atomic& result : m_results) - result.store(0, std::memory_order_relaxed); + ResetQueryResults(); } u32 Metal::PerfQuery::GetQueryResult(PerfQueryType type) @@ -30,20 +29,19 @@ u32 Metal::PerfQuery::GetQueryResult(PerfQueryType type) u32 result = 0; if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC) { - result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP); } else if (type == PQ_BLEND_INPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) + - m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP) + QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_EFB_COPY_CLOCKS) { - result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed); + result = QueryResult(PQG_EFB_COPY_CLOCKS); } return result / 4; @@ -65,7 +63,7 @@ void Metal::PerfQuery::FlushResults() bool Metal::PerfQuery::IsFlushed() const { - return m_query_count.load(std::memory_order_acquire) == 0; + return QueryCount() == 0; } void Metal::PerfQuery::ReturnResults(const u64* data, const PerfQueryGroup* groups, size_t count, @@ -83,10 +81,10 @@ void Metal::PerfQuery::ReturnResults(const u64* data, const PerfQueryGroup* grou native_res_result /= g_ActiveConfig.iMultisamples; - m_results[groups[i]].fetch_add(native_res_result, std::memory_order_relaxed); + AddQueryResult(groups[i], static_cast(native_res_result)); } } - m_query_count.fetch_sub(1, std::memory_order_release); + SubQueryCount(1); } m_cv.notify_one(); } diff --git a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp index 022b4de70e..b8f269c8af 100644 --- a/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPerfQuery.cpp @@ -41,7 +41,7 @@ void PerfQuery::DisableQuery(PerfQueryGroup group) bool PerfQuery::IsFlushed() const { - return m_query_count.load(std::memory_order_relaxed) == 0; + return QueryCount() == 0; } // TODO: could selectively flush things, but I don't think that will do much @@ -52,9 +52,7 @@ void PerfQuery::FlushResults() void PerfQuery::ResetQuery() { - m_query_count.store(0, std::memory_order_relaxed); - for (auto& result : m_results) - result.store(0, std::memory_order_relaxed); + ResetQueryState(); } u32 PerfQuery::GetQueryResult(PerfQueryType type) @@ -63,20 +61,19 @@ u32 PerfQuery::GetQueryResult(PerfQueryType type) if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC) { - result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP); } else if (type == PQ_BLEND_INPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) + - m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP) + QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_EFB_COPY_CLOCKS) { - result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed); + result = QueryResult(PQG_EFB_COPY_CLOCKS); } return result / 4; @@ -97,19 +94,19 @@ PerfQueryGL::~PerfQueryGL() void PerfQueryGL::EnableQuery(PerfQueryGroup group) { - u32 query_count = m_query_count.load(std::memory_order_relaxed); + u32 query_count = QueryCount(); // Is this sane? if (query_count > m_query_buffer.size() / 2) { WeakFlush(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); } if (m_query_buffer.size() == query_count) { FlushOne(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); // ERROR_LOG_FMT(VIDEO, "Flushed query buffer early!"); } @@ -121,7 +118,7 @@ void PerfQueryGL::EnableQuery(PerfQueryGroup group) glBeginQuery(m_query_type, entry.query_id); entry.query_group = group; - m_query_count.fetch_add(1, std::memory_order_relaxed); + AddQueryCount(1); } } void PerfQueryGL::DisableQuery(PerfQueryGroup group) @@ -170,10 +167,10 @@ void PerfQueryGL::FlushOne() if (g_ActiveConfig.iMultisamples > 1) result /= g_ActiveConfig.iMultisamples; - m_results[entry.query_group].fetch_add(result, std::memory_order_relaxed); + AddQueryResult(entry.query_group, result); m_query_read_pos = (m_query_read_pos + 1) % m_query_buffer.size(); - m_query_count.fetch_sub(1, std::memory_order_relaxed); + SubQueryCount(1); } // TODO: could selectively flush things, but I don't think that will do much @@ -197,19 +194,19 @@ PerfQueryGLESNV::~PerfQueryGLESNV() void PerfQueryGLESNV::EnableQuery(PerfQueryGroup group) { - u32 query_count = m_query_count.load(std::memory_order_relaxed); + u32 query_count = QueryCount(); // Is this sane? if (query_count > m_query_buffer.size() / 2) { WeakFlush(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); } if (m_query_buffer.size() == query_count) { FlushOne(); - query_count = m_query_count.load(std::memory_order_relaxed); + query_count = QueryCount(); // ERROR_LOG_FMT(VIDEO, "Flushed query buffer early!"); } @@ -221,7 +218,7 @@ void PerfQueryGLESNV::EnableQuery(PerfQueryGroup group) glBeginOcclusionQueryNV(entry.query_id); entry.query_group = group; - m_query_count.fetch_add(1, std::memory_order_relaxed); + AddQueryCount(1); } } void PerfQueryGLESNV::DisableQuery(PerfQueryGroup group) @@ -266,11 +263,10 @@ void PerfQueryGLESNV::FlushOne() const u64 native_res_result = static_cast(result) * EFB_WIDTH * EFB_HEIGHT / (g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight()); - m_results[entry.query_group].fetch_add(static_cast(native_res_result), - std::memory_order_relaxed); + AddQueryResult(entry.query_group, static_cast(native_res_result)); m_query_read_pos = (m_query_read_pos + 1) % m_query_buffer.size(); - m_query_count.fetch_sub(1, std::memory_order_relaxed); + SubQueryCount(1); } // TODO: could selectively flush things, but I don't think that will do much diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index c82417eb8c..b9206c5d5a 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -234,7 +234,7 @@ bool CommandBufferManager::CreateSubmitThread() SubmitCommandBuffer(submit.command_buffer_index, submit.present_swap_chain, submit.present_image_index); CmdBufferResources& resources = m_command_buffers[submit.command_buffer_index]; - resources.waiting_for_submit.store(false, std::memory_order_release); + retro_atomic_store_release_int(&resources.waiting_for_submit, 0); }); return true; @@ -272,10 +272,10 @@ void CommandBufferManager::WaitForCommandBufferCompletion(u32 index) CmdBufferResources& resources = m_command_buffers[index]; // Ensure this command buffer has been submitted. - if (resources.waiting_for_submit.load(std::memory_order_acquire)) + if (retro_atomic_load_acquire_int(&resources.waiting_for_submit)) { WaitForWorkerThreadIdle(); - ASSERT_MSG(VIDEO, !resources.waiting_for_submit.load(std::memory_order_relaxed), + ASSERT_MSG(VIDEO, !retro_atomic_load_relaxed_int(&resources.waiting_for_submit), "Submit thread is idle but command buffer is still waiting for submission!"); } @@ -329,7 +329,7 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread, // Submitting off-thread? if (m_use_threaded_submission && submit_on_worker_thread && !wait_for_completion) { - resources.waiting_for_submit.store(true, std::memory_order_relaxed); + retro_atomic_store_relaxed_int(&resources.waiting_for_submit, 1); // Push to the pending submit queue. m_submit_thread.Push({present_swap_chain, present_image_index, m_current_cmd_buffer}); } diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h index 45a322c270..e4abb98d07 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h @@ -8,6 +8,8 @@ #include #include +#include + #include #include "Common/Flag.h" @@ -118,7 +120,7 @@ private: u64 fence_counter = 0; bool init_command_buffer_used = false; bool semaphore_used = false; - std::atomic waiting_for_submit{false}; + retro_atomic_int_t waiting_for_submit{}; u32 frame_index = 0; std::vector> cleanup_resources; diff --git a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp index 9949535d88..c07900b660 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPerfQuery.cpp @@ -44,7 +44,7 @@ void PerfQuery::EnableQuery(PerfQueryGroup group) { // Block if there are no free slots. // Otherwise, try to keep half of them available. - const u32 query_count = m_query_count.load(std::memory_order_relaxed); + const u32 query_count = QueryCount(); if (query_count > m_query_buffer.size() / 2) PartialFlush(query_count == PERF_QUERY_BUFFER_SIZE); @@ -79,17 +79,16 @@ void PerfQuery::DisableQuery(PerfQueryGroup group) entry.fence_counter = g_command_buffer_mgr->GetCurrentFenceCounter(); m_query_next_pos = (m_query_next_pos + 1) % PERF_QUERY_BUFFER_SIZE; - m_query_count.fetch_add(1, std::memory_order_relaxed); + AddQueryCount(1); } } void PerfQuery::ResetQuery() { - m_query_count.store(0, std::memory_order_relaxed); + retro_atomic_store_release_int(&m_query_count, 0); m_query_readback_pos = 0; m_query_next_pos = 0; - for (size_t i = 0; i < m_results.size(); ++i) - m_results[i].store(0, std::memory_order_relaxed); + ResetQueryResults(); // Reset entire query pool, ensuring all queries are ready to write to. StateTracker::GetInstance()->EndRenderPass(); @@ -104,20 +103,19 @@ u32 PerfQuery::GetQueryResult(PerfQueryType type) u32 result = 0; if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC) { - result = m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP); } else if (type == PQ_BLEND_INPUT) { - result = m_results[PQG_ZCOMP].load(std::memory_order_relaxed) + - m_results[PQG_ZCOMP_ZCOMPLOC].load(std::memory_order_relaxed); + result = QueryResult(PQG_ZCOMP) + QueryResult(PQG_ZCOMP_ZCOMPLOC); } else if (type == PQ_EFB_COPY_CLOCKS) { - result = m_results[PQG_EFB_COPY_CLOCKS].load(std::memory_order_relaxed); + result = QueryResult(PQG_EFB_COPY_CLOCKS); } return result / 4; @@ -133,7 +131,7 @@ void PerfQuery::FlushResults() bool PerfQuery::IsFlushed() const { - return m_query_count.load(std::memory_order_relaxed) == 0; + return QueryCount() == 0; } bool PerfQuery::CreateQueryPool() @@ -162,7 +160,7 @@ void PerfQuery::ReadbackQueries() const u64 completed_fence_counter = g_command_buffer_mgr->GetCompletedFenceCounter(); // Need to save these since ProcessResults will modify them. - const u32 outstanding_queries = m_query_count.load(std::memory_order_relaxed); + const u32 outstanding_queries = QueryCount(); u32 readback_count = 0; for (u32 i = 0; i < outstanding_queries; i++) { @@ -189,7 +187,7 @@ void PerfQuery::ReadbackQueries() void PerfQuery::ReadbackQueries(u32 query_count) { // Should be at maximum query_count queries pending. - ASSERT(query_count <= m_query_count.load(std::memory_order_relaxed) && + ASSERT(query_count <= QueryCount() && (m_query_readback_pos + query_count) <= PERF_QUERY_BUFFER_SIZE); // Read back from the GPU. @@ -221,12 +219,11 @@ void PerfQuery::ReadbackQueries(u32 query_count) g_framebuffer_manager->GetEFBHeight(); if (g_ActiveConfig.iMultisamples > 1) native_res_result /= g_ActiveConfig.iMultisamples; - m_results[entry.query_group].fetch_add(static_cast(native_res_result), - std::memory_order_relaxed); + AddQueryResult(entry.query_group, static_cast(native_res_result)); } m_query_readback_pos = (m_query_readback_pos + query_count) % PERF_QUERY_BUFFER_SIZE; - m_query_count.fetch_sub(query_count, std::memory_order_relaxed); + SubQueryCount(query_count); } void PerfQuery::PartialFlush(bool blocking) diff --git a/Source/Core/VideoCommon/Assets/CustomAsset.cpp b/Source/Core/VideoCommon/Assets/CustomAsset.cpp index 3105575d80..2d41eb86a6 100644 --- a/Source/Core/VideoCommon/Assets/CustomAsset.cpp +++ b/Source/Core/VideoCommon/Assets/CustomAsset.cpp @@ -32,7 +32,7 @@ std::size_t CustomAsset::Load() if (load_information.bytes_loaded > 0) { m_bytes_loaded = load_information.bytes_loaded; - m_last_loaded_time = load_time; + retro_atomic_store_release_64(&m_last_loaded_time, load_time.time_since_epoch().count()); retro_atomic_store_release_int(&m_busy, 0); return load_information.bytes_loaded; } @@ -52,7 +52,7 @@ std::size_t CustomAsset::Unload() CustomAsset::TimeType CustomAsset::GetLastLoadedTime() const { - return m_last_loaded_time; + return TimeType(ClockType::duration(retro_atomic_load_acquire_64(&m_last_loaded_time))); } std::size_t CustomAsset::GetHandle() const diff --git a/Source/Core/VideoCommon/Assets/CustomAsset.h b/Source/Core/VideoCommon/Assets/CustomAsset.h index e7bc9c64af..f3ff014b6e 100644 --- a/Source/Core/VideoCommon/Assets/CustomAsset.h +++ b/Source/Core/VideoCommon/Assets/CustomAsset.h @@ -9,7 +9,6 @@ #include "Common/CommonTypes.h" #include "VideoCommon/Assets/CustomAssetLibrary.h" -#include #include namespace VideoCommon @@ -64,7 +63,7 @@ private: // Held (with a CAS) by the one thread loading or unloading; anyone else backs off. retro_atomic_int_t m_busy{}; std::size_t m_bytes_loaded = 0; // only touched while busy - std::atomic m_last_loaded_time = {}; + retro_atomic_64_t m_last_loaded_time{}; /* TimeType ticks */ }; // An abstract class that is expected to diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp index 0a1e1a486c..60863c7366 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp +++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp @@ -97,7 +97,7 @@ void CustomAssetLoader::WorkerThreadRun(u32 thread_index) { // 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 > batch->allowed_memory) + if (retro_atomic_load_acquire_64(&m_change_in_memory) > batch->allowed_memory) retro_atomic_store_release_size(&batch->cursor, batch->assets.size()); const size_t index = retro_atomic_fetch_add_size(&batch->cursor, 1); @@ -126,10 +126,10 @@ void CustomAssetLoader::WorkerThreadRun(u32 thread_index) // Unload previously loaded asset. An asset another thread is busy with comes back 0 // and is skipped; the Manager schedules it again. - m_change_in_memory -= item->Unload(); + retro_atomic_fetch_sub_64(&m_change_in_memory, static_cast(item->Unload())); const std::size_t bytes_loaded = item->Load(); - m_change_in_memory += s64(bytes_loaded); + retro_atomic_fetch_add_64(&m_change_in_memory, static_cast(bytes_loaded)); INFO_LOG_FMT(VIDEO, "CustomAssetLoader thread {} loaded: {} ({})", thread_index, item->GetAssetId(), UICommon::FormatSize(bytes_loaded)); @@ -149,7 +149,7 @@ auto CustomAssetLoader::TakeLoadResults() -> LoadResults results.asset_handles.push_back(loaded->handle); delete loaded; } - results.change_in_memory = m_change_in_memory.exchange(0); + results.change_in_memory = retro_atomic_exchange_64(&m_change_in_memory, 0); return results; } @@ -177,7 +177,7 @@ void CustomAssetLoader::Reset(bool restart_worker_threads) delete static_cast(retro_atomic_exchange_ptr(&m_batch, nullptr)); quiesce_deinit(&m_batch_quiesce); TakeLoadResults(); - m_change_in_memory = 0; + retro_atomic_store_release_64(&m_change_in_memory, 0); if (restart_worker_threads) { diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLoader.h b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h index 703bf0f03e..702e0c62fe 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLoader.h +++ b/Source/Core/VideoCommon/Assets/CustomAssetLoader.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include @@ -96,6 +95,6 @@ private: mpsc_stack_t m_loaded{}; // Change in memory from just-loaded/unloaded asset results yet to be taken by the Manager. - std::atomic m_change_in_memory = 0; + retro_atomic_64_t m_change_in_memory{}; }; } // namespace VideoCommon diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 1dbf466f1c..eb2cd58e3e 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -3,7 +3,6 @@ #include "VideoCommon/CommandProcessor.h" -#include #include #include "Common/Assert.h" @@ -34,26 +33,26 @@ static void UpdateInterrupts_Wrapper(Core::System& system, u64 userdata, s64 cyc void SCPFifoStruct::Init() { - CPBase = 0; - CPEnd = 0; + StoreReg(&CPBase, 0); + StoreReg(&CPEnd, 0); CPHiWatermark = 0; CPLoWatermark = 0; - CPReadWriteDistance = 0; - CPWritePointer = 0; - CPReadPointer = 0; - CPBreakpoint = 0; - SafeCPReadPointer = 0; + StoreReg(&CPReadWriteDistance, 0); + StoreReg(&CPWritePointer, 0); + StoreReg(&CPReadPointer, 0); + StoreReg(&CPBreakpoint, 0); + StoreReg(&SafeCPReadPointer, 0); - bFF_GPLinkEnable = 0; - bFF_GPReadEnable = 0; - bFF_BPEnable = 0; - bFF_BPInt = 0; + StoreReg(&bFF_GPLinkEnable, 0); + StoreReg(&bFF_GPReadEnable, 0); + StoreReg(&bFF_BPEnable, 0); + StoreReg(&bFF_BPInt, 0); - bFF_Breakpoint.store(0, std::memory_order_relaxed); - bFF_HiWatermark.store(0, std::memory_order_relaxed); - bFF_HiWatermarkInt.store(0, std::memory_order_relaxed); - bFF_LoWatermark.store(0, std::memory_order_relaxed); - bFF_LoWatermarkInt.store(0, std::memory_order_relaxed); + StoreReg(&bFF_Breakpoint, 0); + StoreReg(&bFF_HiWatermark, 0); + StoreReg(&bFF_HiWatermarkInt, 0); + StoreReg(&bFF_LoWatermark, 0); + StoreReg(&bFF_LoWatermarkInt, 0); } void SCPFifoStruct::DoState(PointerWrap& p) @@ -94,10 +93,9 @@ void CommandProcessorManager::DoState(PointerWrap& p) p.Do(m_interrupt_waiting); } -static inline void WriteHigh(std::atomic& reg, u16 highbits) +static inline void WriteHigh(retro_atomic_int_t* reg, u16 highbits) { - reg.store((reg.load(std::memory_order_relaxed) & 0x0000FFFF) | (static_cast(highbits) << 16), - std::memory_order_relaxed); + StoreReg(reg, (LoadReg(reg) & 0x0000FFFF) | (static_cast(highbits) << 16)); } CommandProcessorManager::CommandProcessorManager(Core::System& system) : m_system{system} @@ -232,18 +230,14 @@ void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) { fifo_rw_distance_lo_r = MMIO::ComplexRead([](Core::System& system_, u32) { const auto& fifo_ = system_.GetCommandProcessor().GetFifo(); - if (fifo_.CPWritePointer.load(std::memory_order_relaxed) >= - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed)) + if (LoadReg(&fifo_.CPWritePointer) >= LoadReg(&fifo_.SafeCPReadPointer)) { - return static_cast(fifo_.CPWritePointer.load(std::memory_order_relaxed) - - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed)); + return static_cast(LoadReg(&fifo_.CPWritePointer) - LoadReg(&fifo_.SafeCPReadPointer)); } else { - return static_cast(fifo_.CPEnd.load(std::memory_order_relaxed) - - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed) + - fifo_.CPWritePointer.load(std::memory_order_relaxed) - - fifo_.CPBase.load(std::memory_order_relaxed) + 32); + return static_cast(LoadReg(&fifo_.CPEnd) - LoadReg(&fifo_.SafeCPReadPointer) + + LoadReg(&fifo_.CPWritePointer) - LoadReg(&fifo_.CPBase) + 32); } }); } @@ -262,19 +256,14 @@ void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) fifo_rw_distance_hi_r = MMIO::ComplexRead([](Core::System& system_, u32) { const auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - if (fifo_.CPWritePointer.load(std::memory_order_relaxed) >= - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed)) + if (LoadReg(&fifo_.CPWritePointer) >= LoadReg(&fifo_.SafeCPReadPointer)) { - return (fifo_.CPWritePointer.load(std::memory_order_relaxed) - - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed)) >> - 16; + return (LoadReg(&fifo_.CPWritePointer) - LoadReg(&fifo_.SafeCPReadPointer)) >> 16; } else { - return (fifo_.CPEnd.load(std::memory_order_relaxed) - - fifo_.SafeCPReadPointer.load(std::memory_order_relaxed) + - fifo_.CPWritePointer.load(std::memory_order_relaxed) - - fifo_.CPBase.load(std::memory_order_relaxed) + 32) >> + return (LoadReg(&fifo_.CPEnd) - LoadReg(&fifo_.SafeCPReadPointer) + + LoadReg(&fifo_.CPWritePointer) - LoadReg(&fifo_.CPBase) + 32) >> 16; } }); @@ -284,14 +273,14 @@ void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) fifo_rw_distance_hi_r = MMIO::ComplexRead([](Core::System& system_, u32) { const auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - return fifo_.CPReadWriteDistance.load(std::memory_order_relaxed) >> 16; + return LoadReg(&fifo_.CPReadWriteDistance) >> 16; }); } mmio->Register(base | FIFO_RW_DISTANCE_HI, fifo_rw_distance_hi_r, MMIO::ComplexWrite([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) { auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - WriteHigh(fifo_.CPReadWriteDistance, val & WMASK_HI_RESTRICT); + WriteHigh(&fifo_.CPReadWriteDistance, val & WMASK_HI_RESTRICT); system_.GetFifo().RunGpu(); })); @@ -308,15 +297,14 @@ void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) fifo_read_hi_r = MMIO::ComplexRead([](Core::System& system_, u32) { auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - return fifo_.SafeCPReadPointer.load(std::memory_order_relaxed) >> 16; + return LoadReg(&fifo_.SafeCPReadPointer) >> 16; }); fifo_read_hi_w = MMIO::ComplexWrite([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) { auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - WriteHigh(fifo_.CPReadPointer, val & WMASK_HI_RESTRICT); - fifo_.SafeCPReadPointer.store(fifo_.CPReadPointer.load(std::memory_order_relaxed), - std::memory_order_relaxed); + WriteHigh(&fifo_.CPReadPointer, val & WMASK_HI_RESTRICT); + StoreReg(&fifo_.SafeCPReadPointer, LoadReg(&fifo_.CPReadPointer)); }); } else @@ -324,13 +312,13 @@ void CommandProcessorManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base) fifo_read_hi_r = MMIO::ComplexRead([](Core::System& system_, u32) { const auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - return fifo_.CPReadPointer.load(std::memory_order_relaxed) >> 16; + return LoadReg(&fifo_.CPReadPointer) >> 16; }); fifo_read_hi_w = MMIO::ComplexWrite([WMASK_HI_RESTRICT](Core::System& system_, u32, u16 val) { auto& fifo_ = system_.GetCommandProcessor().GetFifo(); system_.GetFifo().SyncGPUForRegisterAccess(); - WriteHigh(fifo_.CPReadPointer, val & WMASK_HI_RESTRICT); + WriteHigh(&fifo_.CPReadPointer, val & WMASK_HI_RESTRICT); }); } mmio->Register(base | FIFO_READ_POINTER_HI, fifo_read_hi_r, fifo_read_hi_w); @@ -349,9 +337,9 @@ void CommandProcessorManager::GatherPipeBursted() { // In multibuffer mode is not allowed write in the same FIFO attached to the GPU. // Fix Pokemon XD in DC mode. - if ((processor_interface.m_fifo_cpu_end == m_fifo.CPEnd.load(std::memory_order_relaxed)) && - (processor_interface.m_fifo_cpu_base == m_fifo.CPBase.load(std::memory_order_relaxed)) && - m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > 0) + if ((processor_interface.m_fifo_cpu_end == LoadReg(&m_fifo.CPEnd)) && + (processor_interface.m_fifo_cpu_base == LoadReg(&m_fifo.CPBase)) && + LoadReg(&m_fifo.CPReadWriteDistance) > 0) { m_system.GetFifo().FlushGpu(); } @@ -361,48 +349,42 @@ void CommandProcessorManager::GatherPipeBursted() } // update the fifo pointer - if (m_fifo.CPWritePointer.load(std::memory_order_relaxed) == - m_fifo.CPEnd.load(std::memory_order_relaxed)) + if (LoadReg(&m_fifo.CPWritePointer) == LoadReg(&m_fifo.CPEnd)) { - m_fifo.CPWritePointer.store(m_fifo.CPBase, std::memory_order_relaxed); + StoreReg(&m_fifo.CPWritePointer, LoadReg(&m_fifo.CPBase)); } else { - m_fifo.CPWritePointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); + retro_atomic_fetch_add_int(&m_fifo.CPWritePointer, GPFifo::GATHER_PIPE_SIZE); } if (m_cp_ctrl_reg.GPReadEnable && m_cp_ctrl_reg.GPLinkEnable) { - processor_interface.m_fifo_cpu_write_pointer = - m_fifo.CPWritePointer.load(std::memory_order_relaxed); - processor_interface.m_fifo_cpu_base = m_fifo.CPBase.load(std::memory_order_relaxed); - processor_interface.m_fifo_cpu_end = m_fifo.CPEnd.load(std::memory_order_relaxed); + processor_interface.m_fifo_cpu_write_pointer = LoadReg(&m_fifo.CPWritePointer); + processor_interface.m_fifo_cpu_base = LoadReg(&m_fifo.CPBase); + processor_interface.m_fifo_cpu_end = LoadReg(&m_fifo.CPEnd); } // If the game is running close to overflowing, make the exception checking more frequent. - if (m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0) + if (LoadReg(&m_fifo.bFF_HiWatermark) != 0) m_system.GetCoreTiming().ForceExceptionCheck(0); - m_fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst); + retro_atomic_fetch_add_seq_cst_int(&m_fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); m_system.GetFifo().RunGpu(); ASSERT_MSG(COMMANDPROCESSOR, - m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) <= - m_fifo.CPEnd.load(std::memory_order_relaxed) - - m_fifo.CPBase.load(std::memory_order_relaxed), + LoadReg(&m_fifo.CPReadWriteDistance) <= + LoadReg(&m_fifo.CPEnd) - LoadReg(&m_fifo.CPBase), "FIFO is overflowed by GatherPipe !\nCPU thread is too fast!"); // check if we are in sync ASSERT_MSG(COMMANDPROCESSOR, - m_fifo.CPWritePointer.load(std::memory_order_relaxed) == - processor_interface.m_fifo_cpu_write_pointer, + LoadReg(&m_fifo.CPWritePointer) == processor_interface.m_fifo_cpu_write_pointer, "FIFOs linked but out of sync"); - ASSERT_MSG(COMMANDPROCESSOR, - m_fifo.CPBase.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_base, + ASSERT_MSG(COMMANDPROCESSOR, LoadReg(&m_fifo.CPBase) == processor_interface.m_fifo_cpu_base, "FIFOs linked but out of sync"); - ASSERT_MSG(COMMANDPROCESSOR, - m_fifo.CPEnd.load(std::memory_order_relaxed) == processor_interface.m_fifo_cpu_end, + ASSERT_MSG(COMMANDPROCESSOR, LoadReg(&m_fifo.CPEnd) == processor_interface.m_fifo_cpu_end, "FIFOs linked but out of sync"); } @@ -442,26 +424,23 @@ bool CommandProcessorManager::IsInterruptWaiting() const void CommandProcessorManager::SetCPStatusFromGPU() { // breakpoint - const bool breakpoint = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed); - if (m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) != 0) + const bool breakpoint = LoadReg(&m_fifo.bFF_Breakpoint); + if (LoadReg(&m_fifo.bFF_BPEnable) != 0) { - if (m_fifo.CPBreakpoint.load(std::memory_order_relaxed) == - m_fifo.CPReadPointer.load(std::memory_order_relaxed)) + if (LoadReg(&m_fifo.CPBreakpoint) == LoadReg(&m_fifo.CPReadPointer)) { if (!breakpoint) { - DEBUG_LOG_FMT(COMMANDPROCESSOR, "Hit breakpoint at {}", - m_fifo.CPReadPointer.load(std::memory_order_relaxed)); - m_fifo.bFF_Breakpoint.store(1, std::memory_order_relaxed); + DEBUG_LOG_FMT(COMMANDPROCESSOR, "Hit breakpoint at {}", LoadReg(&m_fifo.CPReadPointer)); + StoreReg(&m_fifo.bFF_Breakpoint, 1); } } else { if (breakpoint) { - DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}", - m_fifo.CPReadPointer.load(std::memory_order_relaxed)); - m_fifo.bFF_Breakpoint.store(0, std::memory_order_relaxed); + DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}", LoadReg(&m_fifo.CPReadPointer)); + StoreReg(&m_fifo.bFF_Breakpoint, 0); } } } @@ -469,26 +448,18 @@ void CommandProcessorManager::SetCPStatusFromGPU() { if (breakpoint) { - DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}", - m_fifo.CPReadPointer.load(std::memory_order_relaxed)); - m_fifo.bFF_Breakpoint = false; + DEBUG_LOG_FMT(COMMANDPROCESSOR, "Cleared breakpoint at {}", LoadReg(&m_fifo.CPReadPointer)); + StoreReg(&m_fifo.bFF_Breakpoint, 0); } } // overflow & underflow check - m_fifo.bFF_HiWatermark.store( - (m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > m_fifo.CPHiWatermark), - std::memory_order_relaxed); - m_fifo.bFF_LoWatermark.store( - (m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < m_fifo.CPLoWatermark), - std::memory_order_relaxed); + StoreReg(&m_fifo.bFF_HiWatermark, (LoadReg(&m_fifo.CPReadWriteDistance) > m_fifo.CPHiWatermark)); + StoreReg(&m_fifo.bFF_LoWatermark, (LoadReg(&m_fifo.CPReadWriteDistance) < m_fifo.CPLoWatermark)); - bool bpInt = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) && - m_fifo.bFF_BPInt.load(std::memory_order_relaxed); - bool ovfInt = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) && - m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed); - bool undfInt = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed) && - m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed); + bool bpInt = LoadReg(&m_fifo.bFF_Breakpoint) && LoadReg(&m_fifo.bFF_BPInt); + bool ovfInt = LoadReg(&m_fifo.bFF_HiWatermark) && LoadReg(&m_fifo.bFF_HiWatermarkInt); + bool undfInt = LoadReg(&m_fifo.bFF_LoWatermark) && LoadReg(&m_fifo.bFF_LoWatermarkInt); bool interrupt = (bpInt || ovfInt || undfInt) && m_cp_ctrl_reg.GPReadEnable; @@ -514,19 +485,12 @@ void CommandProcessorManager::SetCPStatusFromGPU() void CommandProcessorManager::SetCPStatusFromCPU() { // overflow & underflow check - m_fifo.bFF_HiWatermark.store( - (m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) > m_fifo.CPHiWatermark), - std::memory_order_relaxed); - m_fifo.bFF_LoWatermark.store( - (m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) < m_fifo.CPLoWatermark), - std::memory_order_relaxed); + StoreReg(&m_fifo.bFF_HiWatermark, (LoadReg(&m_fifo.CPReadWriteDistance) > m_fifo.CPHiWatermark)); + StoreReg(&m_fifo.bFF_LoWatermark, (LoadReg(&m_fifo.CPReadWriteDistance) < m_fifo.CPLoWatermark)); - bool bpInt = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) && - m_fifo.bFF_BPInt.load(std::memory_order_relaxed); - bool ovfInt = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed) && - m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed); - bool undfInt = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed) && - m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed); + bool bpInt = LoadReg(&m_fifo.bFF_Breakpoint) && LoadReg(&m_fifo.bFF_BPInt); + bool ovfInt = LoadReg(&m_fifo.bFF_HiWatermark) && LoadReg(&m_fifo.bFF_HiWatermarkInt); + bool undfInt = LoadReg(&m_fifo.bFF_LoWatermark) && LoadReg(&m_fifo.bFF_LoWatermarkInt); bool interrupt = (bpInt || ovfInt || undfInt) && m_cp_ctrl_reg.GPReadEnable; @@ -552,15 +516,13 @@ void CommandProcessorManager::SetCPStatusFromCPU() void CommandProcessorManager::SetCpStatusRegister() { // Here always there is one fifo attached to the GPU - m_cp_status_reg.Breakpoint = m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed); - m_cp_status_reg.ReadIdle = !m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) || - (m_fifo.CPReadPointer.load(std::memory_order_relaxed) == - m_fifo.CPWritePointer.load(std::memory_order_relaxed)); - m_cp_status_reg.CommandIdle = !m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed) || - Fifo::AtBreakpoint(m_system) || - !m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed); - m_cp_status_reg.UnderflowLoWatermark = m_fifo.bFF_LoWatermark.load(std::memory_order_relaxed); - m_cp_status_reg.OverflowHiWatermark = m_fifo.bFF_HiWatermark.load(std::memory_order_relaxed); + m_cp_status_reg.Breakpoint = LoadReg(&m_fifo.bFF_Breakpoint); + m_cp_status_reg.ReadIdle = !LoadReg(&m_fifo.CPReadWriteDistance) || + (LoadReg(&m_fifo.CPReadPointer) == LoadReg(&m_fifo.CPWritePointer)); + m_cp_status_reg.CommandIdle = !LoadReg(&m_fifo.CPReadWriteDistance) || + Fifo::AtBreakpoint(m_system) || !LoadReg(&m_fifo.bFF_GPReadEnable); + m_cp_status_reg.UnderflowLoWatermark = LoadReg(&m_fifo.bFF_LoWatermark); + m_cp_status_reg.OverflowHiWatermark = LoadReg(&m_fifo.bFF_HiWatermark); DEBUG_LOG_FMT(COMMANDPROCESSOR, "\t Read from STATUS_REGISTER : {:04x}", m_cp_status_reg.Hex); DEBUG_LOG_FMT(COMMANDPROCESSOR, @@ -574,21 +536,21 @@ void CommandProcessorManager::SetCpStatusRegister() void CommandProcessorManager::SetCpControlRegister() { // Just before disabling reads, give the GPU a chance to catch up. - if (m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) && !m_cp_ctrl_reg.GPReadEnable) + if (LoadReg(&m_fifo.bFF_GPReadEnable) && !m_cp_ctrl_reg.GPReadEnable) { m_system.GetFifo().SyncGPUForRegisterAccess(); } - m_fifo.bFF_GPReadEnable.store(m_cp_ctrl_reg.GPReadEnable, std::memory_order_relaxed); - m_fifo.bFF_BPInt.store(m_cp_ctrl_reg.BPInt, std::memory_order_relaxed); - m_fifo.bFF_BPEnable.store(m_cp_ctrl_reg.BPEnable, std::memory_order_relaxed); - m_fifo.bFF_HiWatermarkInt.store(m_cp_ctrl_reg.FifoOverflowIntEnable, std::memory_order_relaxed); - m_fifo.bFF_LoWatermarkInt.store(m_cp_ctrl_reg.FifoUnderflowIntEnable, std::memory_order_relaxed); - m_fifo.bFF_GPLinkEnable.store(m_cp_ctrl_reg.GPLinkEnable, std::memory_order_relaxed); + StoreReg(&m_fifo.bFF_GPReadEnable, m_cp_ctrl_reg.GPReadEnable); + StoreReg(&m_fifo.bFF_BPInt, m_cp_ctrl_reg.BPInt); + StoreReg(&m_fifo.bFF_BPEnable, m_cp_ctrl_reg.BPEnable); + StoreReg(&m_fifo.bFF_HiWatermarkInt, m_cp_ctrl_reg.FifoOverflowIntEnable); + StoreReg(&m_fifo.bFF_LoWatermarkInt, m_cp_ctrl_reg.FifoUnderflowIntEnable); + StoreReg(&m_fifo.bFF_GPLinkEnable, m_cp_ctrl_reg.GPLinkEnable); DEBUG_LOG_FMT(COMMANDPROCESSOR, "\t GPREAD {} | BP {} | Int {} | OvF {} | UndF {} | LINK {}", - m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "ON" : "OFF", - m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "ON" : "OFF", - m_fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "ON" : "OFF", + LoadReg(&m_fifo.bFF_GPReadEnable) ? "ON" : "OFF", + LoadReg(&m_fifo.bFF_BPEnable) ? "ON" : "OFF", + LoadReg(&m_fifo.bFF_BPInt) ? "ON" : "OFF", m_cp_ctrl_reg.FifoOverflowIntEnable ? "ON" : "OFF", m_cp_ctrl_reg.FifoUnderflowIntEnable ? "ON" : "OFF", m_cp_ctrl_reg.GPLinkEnable ? "ON" : "OFF"); @@ -647,21 +609,17 @@ void CommandProcessorManager::HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, "{:#010x}, bFF_GPReadEnable: {}, bFF_BPEnable: {}, bFF_BPInt: {}, bFF_Breakpoint: " "{}, bFF_GPLinkEnable: {}, bFF_HiWatermarkInt: {}, bFF_LoWatermarkInt: {}, " "approximate PC: {:08x}, approximate LR: {:08x}", - cmd_byte, fmt::ptr(buffer), preprocess ? "yes" : "no", - m_fifo.CPBase.load(std::memory_order_relaxed), m_fifo.CPEnd.load(std::memory_order_relaxed), - m_fifo.CPHiWatermark, m_fifo.CPLoWatermark, - m_fifo.CPReadWriteDistance.load(std::memory_order_relaxed), - m_fifo.CPWritePointer.load(std::memory_order_relaxed), - m_fifo.CPReadPointer.load(std::memory_order_relaxed), - m_fifo.CPBreakpoint.load(std::memory_order_relaxed), - m_fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", - m_fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", ppc_state.pc, - LR(ppc_state)); + cmd_byte, fmt::ptr(buffer), preprocess ? "yes" : "no", LoadReg(&m_fifo.CPBase), + LoadReg(&m_fifo.CPEnd), m_fifo.CPHiWatermark, m_fifo.CPLoWatermark, + LoadReg(&m_fifo.CPReadWriteDistance), LoadReg(&m_fifo.CPWritePointer), + LoadReg(&m_fifo.CPReadPointer), LoadReg(&m_fifo.CPBreakpoint), + LoadReg(&m_fifo.bFF_GPReadEnable) ? "true" : "false", + LoadReg(&m_fifo.bFF_BPEnable) ? "true" : "false", + LoadReg(&m_fifo.bFF_BPInt) ? "true" : "false", + LoadReg(&m_fifo.bFF_Breakpoint) ? "true" : "false", + LoadReg(&m_fifo.bFF_GPLinkEnable) ? "true" : "false", + LoadReg(&m_fifo.bFF_HiWatermarkInt) ? "true" : "false", + LoadReg(&m_fifo.bFF_LoWatermarkInt) ? "true" : "false", ppc_state.pc, LR(ppc_state)); if (!m_is_fifo_error_seen && !suppress_panic_alert) { diff --git a/Source/Core/VideoCommon/CommandProcessor.h b/Source/Core/VideoCommon/CommandProcessor.h index 976592c829..f23f6852ba 100644 --- a/Source/Core/VideoCommon/CommandProcessor.h +++ b/Source/Core/VideoCommon/CommandProcessor.h @@ -3,11 +3,11 @@ #pragma once -#include - #include "Common/CommonTypes.h" #include "Common/Flag.h" +#include + class PointerWrap; namespace MMIO { @@ -24,30 +24,41 @@ struct EventType; namespace CommandProcessor { +/* The FIFO registers the CPU and GPU threads share: u32 values in atomic words. */ +inline u32 LoadReg(const retro_atomic_int_t* reg) +{ + return static_cast(retro_atomic_load_relaxed_int(reg)); +} + +inline void StoreReg(retro_atomic_int_t* reg, u32 value) +{ + retro_atomic_store_relaxed_int(reg, static_cast(value)); +} + struct SCPFifoStruct { // fifo registers - std::atomic CPBase = 0; - std::atomic CPEnd = 0; + retro_atomic_int_t CPBase{}; + retro_atomic_int_t CPEnd{}; u32 CPHiWatermark = 0; u32 CPLoWatermark = 0; - std::atomic CPReadWriteDistance = 0; - std::atomic CPWritePointer = 0; - std::atomic CPReadPointer = 0; - std::atomic CPBreakpoint = 0; - std::atomic SafeCPReadPointer = 0; + retro_atomic_int_t CPReadWriteDistance{}; + retro_atomic_int_t CPWritePointer{}; + retro_atomic_int_t CPReadPointer{}; + retro_atomic_int_t CPBreakpoint{}; + retro_atomic_int_t SafeCPReadPointer{}; - std::atomic bFF_GPLinkEnable = 0; - std::atomic bFF_GPReadEnable = 0; - std::atomic bFF_BPEnable = 0; - std::atomic bFF_BPInt = 0; - std::atomic bFF_Breakpoint = 0; + retro_atomic_int_t bFF_GPLinkEnable{}; + retro_atomic_int_t bFF_GPReadEnable{}; + retro_atomic_int_t bFF_BPEnable{}; + retro_atomic_int_t bFF_BPInt{}; + retro_atomic_int_t bFF_Breakpoint{}; - std::atomic bFF_LoWatermarkInt = 0; - std::atomic bFF_HiWatermarkInt = 0; + retro_atomic_int_t bFF_LoWatermarkInt{}; + retro_atomic_int_t bFF_HiWatermarkInt{}; - std::atomic bFF_LoWatermark = 0; - std::atomic bFF_HiWatermark = 0; + retro_atomic_int_t bFF_LoWatermark{}; + retro_atomic_int_t bFF_HiWatermark{}; void Init(); void DoState(PointerWrap& p); diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp index 75a219c7c2..08df32e8e1 100644 --- a/Source/Core/VideoCommon/Fifo.cpp +++ b/Source/Core/VideoCommon/Fifo.cpp @@ -3,7 +3,6 @@ #include "VideoCommon/Fifo.h" -#include #include #include "Common/Assert.h" @@ -34,6 +33,9 @@ namespace Fifo { +using CommandProcessor::LoadReg; +using CommandProcessor::StoreReg; + static constexpr int GPU_TIME_SLOT_SIZE = 1000; FifoManager::FifoManager(Core::System& system) : m_system{system} @@ -53,14 +55,15 @@ void FifoManager::RefreshConfig() void FifoManager::DoState(PointerWrap& p) { p.DoArray(m_video_buffer, FIFO_SIZE); - u8* write_ptr = m_video_buffer_write_ptr; + u8* write_ptr = GetWritePtr(); p.DoPointer(write_ptr, m_video_buffer); - m_video_buffer_write_ptr = write_ptr; + SetWritePtr(write_ptr); p.DoPointer(m_video_buffer_read_ptr, m_video_buffer); if (p.IsReadMode() && m_use_deterministic_gpu_thread) { // We're good and paused, right? - m_video_buffer_seen_ptr = m_video_buffer_pp_read_ptr = m_video_buffer_read_ptr; + m_video_buffer_pp_read_ptr = m_video_buffer_read_ptr; + SetSeenPtr(m_video_buffer_read_ptr); } p.Do(m_sync_ticks); @@ -95,7 +98,7 @@ void FifoManager::Init() ResetVideoBuffer(); if (m_system.IsDualCoreMode()) m_gpu_mainloop.Prepare(); - m_sync_ticks.store(0); + retro_atomic_store_release_int(&m_sync_ticks, 0); } void FifoManager::Shutdown() @@ -105,10 +108,10 @@ void FifoManager::Shutdown() Common::FreeMemoryPages(m_video_buffer, FIFO_SIZE + 4); m_video_buffer = nullptr; - m_video_buffer_write_ptr = nullptr; + SetWritePtr(nullptr); m_video_buffer_pp_read_ptr = nullptr; m_video_buffer_read_ptr = nullptr; - m_video_buffer_seen_ptr = nullptr; + SetSeenPtr(nullptr); m_fifo_aux_write_ptr = nullptr; m_fifo_aux_read_ptr = nullptr; @@ -127,7 +130,7 @@ void FifoManager::ExitGpuLoop() auto& fifo = command_processor.GetFifo(); // This should break the wait loop in CPU thread - fifo.bFF_GPReadEnable.store(0, std::memory_order_relaxed); + StoreReg(&fifo.bFF_GPReadEnable, 0); FlushGpu(); // Terminate GPU thread loop @@ -141,7 +144,7 @@ void FifoManager::ExitGpuLoop() // and this Stop() does nothing, so the request is kept for RunGpuLoop() to act on. void FifoManager::StopGpuLoop() { - m_stop_gpu_loop_requested.store(true); + retro_atomic_store_release_int(&m_stop_gpu_loop_requested, 1); m_gpu_mainloop.Stop(Common::BlockingLoop::StopMode::NonBlock); } #endif @@ -176,7 +179,7 @@ void FifoManager::SyncGPU(SyncGPUReason reason, bool may_move_read_ptr) if (may_move_read_ptr) { - u8* write_ptr = m_video_buffer_write_ptr; + u8* write_ptr = GetWritePtr(); // what's left over in the buffer size_t size = write_ptr - m_video_buffer_pp_read_ptr; @@ -185,10 +188,11 @@ void FifoManager::SyncGPU(SyncGPUReason reason, bool may_move_read_ptr) // This change always decreases the pointers. We write seen_ptr // after write_ptr here, and read it before in RunGpuLoop, so // 'write_ptr > seen_ptr' there cannot become spuriously true. - m_video_buffer_write_ptr = write_ptr = m_video_buffer + size; + write_ptr = m_video_buffer + size; + SetWritePtr(write_ptr); m_video_buffer_pp_read_ptr = m_video_buffer; m_video_buffer_read_ptr = m_video_buffer; - m_video_buffer_seen_ptr = write_ptr; + SetSeenPtr(write_ptr); } } } @@ -225,10 +229,10 @@ void* FifoManager::PopFifoAuxBuffer(size_t size) // Description: RunGpuLoop() sends data through this function. void FifoManager::ReadDataFromFifo(u32 read_ptr) { - if (GPFifo::GATHER_PIPE_SIZE > - static_cast(m_video_buffer + FIFO_SIZE - m_video_buffer_write_ptr)) + u8* write_ptr = GetWritePtr(); + if (GPFifo::GATHER_PIPE_SIZE > static_cast(m_video_buffer + FIFO_SIZE - write_ptr)) { - const size_t existing_len = m_video_buffer_write_ptr - m_video_buffer_read_ptr; + const size_t existing_len = write_ptr - m_video_buffer_read_ptr; if (GPFifo::GATHER_PIPE_SIZE > static_cast(FIFO_SIZE - existing_len)) { PanicAlertFmt("FIFO out of bounds (existing {} + new {} > {})", existing_len, @@ -236,19 +240,19 @@ void FifoManager::ReadDataFromFifo(u32 read_ptr) return; } memmove(m_video_buffer, m_video_buffer_read_ptr, existing_len); - m_video_buffer_write_ptr = m_video_buffer + existing_len; + write_ptr = m_video_buffer + existing_len; m_video_buffer_read_ptr = m_video_buffer; } // Copy new video instructions to m_video_buffer for future use in rendering the new picture auto& memory = m_system.GetMemory(); - memory.CopyFromEmu(m_video_buffer_write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE); - m_video_buffer_write_ptr += GPFifo::GATHER_PIPE_SIZE; + memory.CopyFromEmu(write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE); + SetWritePtr(write_ptr + GPFifo::GATHER_PIPE_SIZE); } // The deterministic_gpu_thread version. void FifoManager::ReadDataFromFifoOnCPU(u32 read_ptr) { - u8* write_ptr = m_video_buffer_write_ptr; + u8* write_ptr = GetWritePtr(); if (GPFifo::GATHER_PIPE_SIZE > static_cast(m_video_buffer + FIFO_SIZE - write_ptr)) { // We can't wrap around while the GPU is working on the data. @@ -265,7 +269,7 @@ void FifoManager::ReadDataFromFifoOnCPU(u32 read_ptr) PanicAlertFmt("Desynced read pointers"); return; } - write_ptr = m_video_buffer_write_ptr; + write_ptr = GetWritePtr(); const size_t existing_len = write_ptr - m_video_buffer_pp_read_ptr; if (GPFifo::GATHER_PIPE_SIZE > static_cast(FIFO_SIZE - existing_len)) { @@ -275,18 +279,18 @@ void FifoManager::ReadDataFromFifoOnCPU(u32 read_ptr) } } auto& memory = m_system.GetMemory(); - memory.CopyFromEmu(m_video_buffer_write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE); + memory.CopyFromEmu(write_ptr, read_ptr, GPFifo::GATHER_PIPE_SIZE); m_video_buffer_pp_read_ptr = OpcodeDecoder::RunFifo( DataReader(m_video_buffer_pp_read_ptr, write_ptr + GPFifo::GATHER_PIPE_SIZE), nullptr); // This would have to be locked if the GPU thread didn't spin. - m_video_buffer_write_ptr = write_ptr + GPFifo::GATHER_PIPE_SIZE; + SetWritePtr(write_ptr + GPFifo::GATHER_PIPE_SIZE); } void FifoManager::ResetVideoBuffer() { m_video_buffer_read_ptr = m_video_buffer; - m_video_buffer_write_ptr = m_video_buffer; - m_video_buffer_seen_ptr = m_video_buffer; + SetWritePtr(m_video_buffer); + SetSeenPtr(m_video_buffer); m_video_buffer_pp_read_ptr = m_video_buffer; m_fifo_aux_write_ptr = m_fifo_aux_data; m_fifo_aux_read_ptr = m_fifo_aux_data; @@ -299,7 +303,7 @@ void FifoManager::RunGpuLoop() m_gpu_mainloop.Run( [this] { #ifdef __LIBRETRO__ - if (m_stop_gpu_loop_requested.load()) + if (retro_atomic_load_acquire_int(&m_stop_gpu_loop_requested)) m_gpu_mainloop.Stop(Common::BlockingLoop::StopMode::NonBlock); #endif @@ -313,14 +317,14 @@ void FifoManager::RunGpuLoop() if (m_use_deterministic_gpu_thread) { // All the fifo/CP stuff is on the CPU. We just need to run the opcode decoder. - u8* seen_ptr = m_video_buffer_seen_ptr; - u8* write_ptr = m_video_buffer_write_ptr; + u8* seen_ptr = GetSeenPtr(); + u8* write_ptr = GetWritePtr(); // See comment in SyncGPU if (write_ptr > seen_ptr) { m_video_buffer_read_ptr = OpcodeDecoder::RunFifo(DataReader(m_video_buffer_read_ptr, write_ptr), nullptr); - m_video_buffer_seen_ptr = write_ptr; + SetSeenPtr(write_ptr); } } else @@ -330,41 +334,39 @@ void FifoManager::RunGpuLoop() command_processor.SetCPStatusFromGPU(); // check if we are able to run this buffer - while (!command_processor.IsInterruptWaiting() && - fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) && - fifo.CPReadWriteDistance.load(std::memory_order_relaxed) && - !AtBreakpoint(m_system)) + while (!command_processor.IsInterruptWaiting() && LoadReg(&fifo.bFF_GPReadEnable) && + LoadReg(&fifo.CPReadWriteDistance) && !AtBreakpoint(m_system)) { - if (m_config_sync_gpu && m_sync_ticks.load() < m_config_sync_gpu_min_distance) + if (m_config_sync_gpu && + retro_atomic_load_seq_cst_int(&m_sync_ticks) < m_config_sync_gpu_min_distance) break; u32 cyclesExecuted = 0; - u32 readPtr = fifo.CPReadPointer.load(std::memory_order_relaxed); + u32 readPtr = LoadReg(&fifo.CPReadPointer); ReadDataFromFifo(readPtr); - if (readPtr == fifo.CPEnd.load(std::memory_order_relaxed)) - readPtr = fifo.CPBase.load(std::memory_order_relaxed); + if (readPtr == LoadReg(&fifo.CPEnd)) + readPtr = LoadReg(&fifo.CPBase); else readPtr += GPFifo::GATHER_PIPE_SIZE; const s32 distance = - static_cast(fifo.CPReadWriteDistance.load(std::memory_order_relaxed)) - - GPFifo::GATHER_PIPE_SIZE; + static_cast(LoadReg(&fifo.CPReadWriteDistance)) - GPFifo::GATHER_PIPE_SIZE; ASSERT_MSG(COMMANDPROCESSOR, distance >= 0, "Negative fifo.CPReadWriteDistance = {} in FIFO Loop !\nThat can produce " "instability in the game. Please report it.", distance); - u8* write_ptr = m_video_buffer_write_ptr; + u8* write_ptr = GetWritePtr(); m_video_buffer_read_ptr = OpcodeDecoder::RunFifo( DataReader(m_video_buffer_read_ptr, write_ptr), &cyclesExecuted); - fifo.CPReadPointer.store(readPtr, std::memory_order_relaxed); - fifo.CPReadWriteDistance.fetch_sub(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst); + StoreReg(&fifo.CPReadPointer, readPtr); + retro_atomic_fetch_add_seq_cst_int(&fifo.CPReadWriteDistance, + -static_cast(GPFifo::GATHER_PIPE_SIZE)); if ((write_ptr - m_video_buffer_read_ptr) == 0) { - fifo.SafeCPReadPointer.store(fifo.CPReadPointer.load(std::memory_order_relaxed), - std::memory_order_relaxed); + StoreReg(&fifo.SafeCPReadPointer, LoadReg(&fifo.CPReadPointer)); } command_processor.SetCPStatusFromGPU(); @@ -372,7 +374,8 @@ void FifoManager::RunGpuLoop() if (m_config_sync_gpu) { cyclesExecuted = (int)(cyclesExecuted / m_config_sync_gpu_overclock); - int old = m_sync_ticks.fetch_sub(cyclesExecuted); + int old = retro_atomic_fetch_add_seq_cst_int(&m_sync_ticks, + -static_cast(cyclesExecuted)); if (old >= m_config_sync_gpu_max_distance && old - (int)cyclesExecuted < m_config_sync_gpu_max_distance) { @@ -388,9 +391,9 @@ void FifoManager::RunGpuLoop() } // fast skip remaining GPU time if fifo is empty - if (m_sync_ticks.load() > 0) + if (retro_atomic_load_seq_cst_int(&m_sync_ticks) > 0) { - int old = m_sync_ticks.exchange(0); + int old = retro_atomic_exchange_int(&m_sync_ticks, 0); if (old >= m_config_sync_gpu_max_distance) m_sync_wakeup_event.Set(); } @@ -402,7 +405,7 @@ void FifoManager::RunGpuLoop() } }); #ifdef __LIBRETRO__ - m_stop_gpu_loop_requested.store(false); + retro_atomic_store_release_int(&m_stop_gpu_loop_requested, 0); #endif } @@ -423,9 +426,8 @@ bool AtBreakpoint(Core::System& system) { auto& command_processor = system.GetCommandProcessor(); const auto& fifo = command_processor.GetFifo(); - return fifo.bFF_BPEnable.load(std::memory_order_relaxed) && - (fifo.CPReadPointer.load(std::memory_order_relaxed) == - fifo.CPBreakpoint.load(std::memory_order_relaxed)); + return LoadReg(&fifo.bFF_BPEnable) && + (LoadReg(&fifo.CPReadPointer) == LoadReg(&fifo.CPBreakpoint)); } void FifoManager::RunGpu() @@ -455,14 +457,14 @@ int FifoManager::RunGpuOnCpu(int ticks) auto& command_processor = m_system.GetCommandProcessor(); auto& fifo = command_processor.GetFifo(); bool reset_simd_state = false; - int available_ticks = int(ticks * m_config_sync_gpu_overclock) + m_sync_ticks.load(); - while (fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) && - fifo.CPReadWriteDistance.load(std::memory_order_relaxed) && !AtBreakpoint(m_system) && - available_ticks >= 0) + int available_ticks = + int(ticks * m_config_sync_gpu_overclock) + retro_atomic_load_seq_cst_int(&m_sync_ticks); + while (LoadReg(&fifo.bFF_GPReadEnable) && LoadReg(&fifo.CPReadWriteDistance) && + !AtBreakpoint(m_system) && available_ticks >= 0) { if (m_use_deterministic_gpu_thread) { - ReadDataFromFifoOnCPU(fifo.CPReadPointer.load(std::memory_order_relaxed)); + ReadDataFromFifoOnCPU(LoadReg(&fifo.CPReadPointer)); m_gpu_mainloop.Wakeup(); } else @@ -473,25 +475,23 @@ int FifoManager::RunGpuOnCpu(int ticks) Common::FPU::LoadDefaultSIMDState(); reset_simd_state = true; } - ReadDataFromFifo(fifo.CPReadPointer.load(std::memory_order_relaxed)); + ReadDataFromFifo(LoadReg(&fifo.CPReadPointer)); u32 cycles = 0; - m_video_buffer_read_ptr = OpcodeDecoder::RunFifo( - DataReader(m_video_buffer_read_ptr, m_video_buffer_write_ptr), &cycles); + m_video_buffer_read_ptr = + OpcodeDecoder::RunFifo(DataReader(m_video_buffer_read_ptr, GetWritePtr()), &cycles); available_ticks -= cycles; } - if (fifo.CPReadPointer.load(std::memory_order_relaxed) == - fifo.CPEnd.load(std::memory_order_relaxed)) + if (LoadReg(&fifo.CPReadPointer) == LoadReg(&fifo.CPEnd)) { - fifo.CPReadPointer.store(fifo.CPBase.load(std::memory_order_relaxed), - std::memory_order_relaxed); + StoreReg(&fifo.CPReadPointer, LoadReg(&fifo.CPBase)); } else { - fifo.CPReadPointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); + retro_atomic_fetch_add_int(&fifo.CPReadPointer, GPFifo::GATHER_PIPE_SIZE); } - fifo.CPReadWriteDistance.fetch_sub(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); + retro_atomic_fetch_sub_int(&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); } command_processor.SetCPStatusFromGPU(); @@ -502,7 +502,7 @@ int FifoManager::RunGpuOnCpu(int ticks) } // Discard all available ticks as there is nothing to do any more. - m_sync_ticks.store(std::min(available_ticks, 0)); + retro_atomic_store_release_int(&m_sync_ticks, std::min(available_ticks, 0)); // If the GPU is idle, drop the handler. if (available_ticks >= 0) @@ -538,7 +538,8 @@ void FifoManager::UpdateWantDeterminism(bool want) if (gpu_thread) { // These haven't been updated in non-deterministic mode. - m_video_buffer_seen_ptr = m_video_buffer_pp_read_ptr = m_video_buffer_read_ptr; + m_video_buffer_pp_read_ptr = m_video_buffer_read_ptr; + SetSeenPtr(m_video_buffer_read_ptr); CopyPreprocessCPStateFromMain(); VertexLoaderManager::MarkAllDirty(); } @@ -552,7 +553,7 @@ void FifoManager::UpdateWantDeterminism(bool want) */ int FifoManager::WaitForGpuThread(int ticks) { - int old = m_sync_ticks.fetch_add(ticks); + int old = retro_atomic_fetch_add_seq_cst_int(&m_sync_ticks, ticks); int now = old + ticks; // GPU is idle, so stop polling. diff --git a/Source/Core/VideoCommon/Fifo.h b/Source/Core/VideoCommon/Fifo.h index cff4e7980b..7064660a69 100644 --- a/Source/Core/VideoCommon/Fifo.h +++ b/Source/Core/VideoCommon/Fifo.h @@ -3,9 +3,9 @@ #pragma once -#include #include #include +#include #include "Common/BlockingLoop.h" #include "Common/CommonTypes.h" @@ -92,7 +92,7 @@ private: Common::BlockingLoop m_gpu_mainloop; #ifdef __LIBRETRO__ // Set by StopGpuLoop(), so that a stop requested before RunGpuLoop() got going still ends it. - std::atomic m_stop_gpu_loop_requested{false}; + retro_atomic_int_t m_stop_gpu_loop_requested{}; #endif Common::Flag m_emu_running_state; @@ -111,8 +111,8 @@ private: // STATE_TO_SAVE u8* m_video_buffer = nullptr; u8* m_video_buffer_read_ptr = nullptr; - std::atomic m_video_buffer_write_ptr = nullptr; - std::atomic m_video_buffer_seen_ptr = nullptr; + retro_atomic_ptr_t m_video_buffer_write_ptr{}; + retro_atomic_ptr_t m_video_buffer_seen_ptr{}; u8* m_video_buffer_pp_read_ptr = nullptr; // The read_ptr is always owned by the GPU thread. In normal mode, so is the // write_ptr, despite it being atomic. In deterministic GPU thread mode, @@ -126,7 +126,18 @@ private: // polls, it's just atomic. // - The pp_read_ptr is the CPU preprocessing version of the read_ptr. - std::atomic m_sync_ticks = 0; + retro_atomic_int_t m_sync_ticks{}; + + u8* GetWritePtr() const + { + return static_cast(retro_atomic_load_acquire_ptr(&m_video_buffer_write_ptr)); + } + void SetWritePtr(u8* ptr) { retro_atomic_store_release_ptr(&m_video_buffer_write_ptr, ptr); } + u8* GetSeenPtr() const + { + return static_cast(retro_atomic_load_acquire_ptr(&m_video_buffer_seen_ptr)); + } + void SetSeenPtr(u8* ptr) { retro_atomic_store_release_ptr(&m_video_buffer_seen_ptr, ptr); } bool m_syncing_suspended = false; Common::Event m_sync_wakeup_event; diff --git a/Source/Core/VideoCommon/PerfQueryBase.h b/Source/Core/VideoCommon/PerfQueryBase.h index 183a27c80c..9afd6b6666 100644 --- a/Source/Core/VideoCommon/PerfQueryBase.h +++ b/Source/Core/VideoCommon/PerfQueryBase.h @@ -3,10 +3,10 @@ #pragma once -#include -#include #include +#include + #include "Common/CommonTypes.h" enum PerfQueryType @@ -31,7 +31,7 @@ enum PerfQueryGroup class PerfQueryBase { public: - PerfQueryBase() : m_query_count(0) {} + PerfQueryBase() {} virtual ~PerfQueryBase() {} virtual bool Initialize() { return true; } @@ -65,8 +65,45 @@ public: virtual bool IsFlushed() const { return true; } protected: - std::atomic m_query_count; - std::array, PQG_NUM_MEMBERS> m_results; + /* The GPU thread retires queries while the CPU thread reads results: u32s in atomic words. */ + u32 QueryCount() const { return static_cast(retro_atomic_load_acquire_int(&m_query_count)); } + void AddQueryCount(u32 count) + { + retro_atomic_fetch_add_int(&m_query_count, static_cast(count)); + } + void SubQueryCount(u32 count) + { + retro_atomic_fetch_sub_int(&m_query_count, static_cast(count)); + } + u32 QueryResult(PerfQueryGroup group) const + { + return static_cast(retro_atomic_load_acquire_int(&m_results[group])); + } + void AddQueryResult(PerfQueryGroup group, u32 value) + { + retro_atomic_fetch_add_int(&m_results[group], static_cast(value)); + } + void SetQueryResult(PerfQueryGroup group, u32 value) + { + retro_atomic_store_release_int(&m_results[group], static_cast(value)); + } + /* Drops the pending queries and zeroes the results. */ + void ResetQueryState() + { + int i; + retro_atomic_store_release_int(&m_query_count, 0); + for (i = 0; i < PQG_NUM_MEMBERS; i++) + retro_atomic_store_release_int(&m_results[i], 0); + } + void ResetQueryResults() + { + int i; + for (i = 0; i < PQG_NUM_MEMBERS; i++) + retro_atomic_store_release_int(&m_results[i], 0); + } + + retro_atomic_int_t m_query_count{}; + retro_atomic_int_t m_results[PQG_NUM_MEMBERS]{}; }; extern std::unique_ptr g_perf_query; diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp index 13ff604077..b41e778204 100644 --- a/Source/Core/VideoCommon/Present.cpp +++ b/Source/Core/VideoCommon/Present.cpp @@ -107,7 +107,7 @@ Presenter::Presenter() video_events.config_changed_event.Register([this](u32 bits) { ConfigChanged(bits); }); m_end_field_hook = video_events.vi_end_field_event.Register( - [this] { m_immediate_swap_happened_this_field.store(false, std::memory_order_relaxed); }); + [this] { retro_atomic_store_relaxed_int(&m_immediate_swap_happened_this_field, 0); }); } Presenter::~Presenter() @@ -120,7 +120,7 @@ bool Presenter::Initialize() { UpdateDrawRectangle(); - m_immediate_swap_happened_this_field.store(false, std::memory_order_relaxed); + retro_atomic_store_relaxed_int(&m_immediate_swap_happened_this_field, 0); if (!g_gfx->IsHeadless()) { @@ -237,7 +237,7 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height) { - if (m_immediate_swap_happened_this_field.exchange(true, std::memory_order_relaxed) && + if (retro_atomic_exchange_int(&m_immediate_swap_happened_this_field, 1) && Config::Get(Config::GFX_HACK_CAP_IMMEDIATE_XFB)) { return; @@ -959,7 +959,7 @@ void Presenter::DoState(PointerWrap& p) m_next_swap_estimated_ticks = m_last_xfb_ticks; - m_immediate_swap_happened_this_field.store(false, std::memory_order_relaxed); + retro_atomic_store_relaxed_int(&m_immediate_swap_happened_this_field, 0); #ifdef __LIBRETRO__ if (g_video_backend && g_video_backend->GetConfigName() != "OGL") diff --git a/Source/Core/VideoCommon/Present.h b/Source/Core/VideoCommon/Present.h index 0b92a9acdf..a4d13be095 100644 --- a/Source/Core/VideoCommon/Present.h +++ b/Source/Core/VideoCommon/Present.h @@ -13,6 +13,8 @@ #include #include +#include + class AbstractTexture; struct SurfaceInfo; enum class AspectMode : int; @@ -165,7 +167,7 @@ private: // Can be used for presentation of ImmediateXFB swaps which don't have timing information. u64 m_next_swap_estimated_ticks = 0; - std::atomic_bool m_immediate_swap_happened_this_field{}; + retro_atomic_int_t m_immediate_swap_happened_this_field{}; }; } // namespace VideoCommon