VideoCommon, backends, input, DiscIO: std::atomic -> retro_atomic

- CommandProcessor: the FIFO registers shared by the CPU and GPU
  threads are retro_atomic words, read and written through LoadReg and
  StoreReg; MMIO's LowPart/HighPart take those words.
- Fifo: the video buffer write and seen pointers, the sync ticks and
  the stop request.
- PerfQueryBase: the query count and results sit behind base-class
  helpers that every backend (Metal too) uses instead of touching the
  atomics.
- Custom assets: the loader's memory delta and an asset's last load
  time are 64-bit words; Present's swap flag is an int.
- D3D's input layout, Vulkan's waiting_for_submit.
- InputCommon: a NumericSetting's value and the Wiimote and Android
  rumble levels are kept as their bits in a 64-bit word; the
  ControllerInterface flags, counter and aspect ratio are int words.
- CachedBlob's fill position and stop flag.

UICommon/GameFileCache is deleted: only the Android app's game list
used it, and that is not part of the core.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-06 00:19:39 +01:00
1 parent d3b454d48b
commit 53238c0a06
34 files changed
+503 -773

No files matched your search

-1
View File
@@ -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 \
+9 -7
View File
@@ -4,12 +4,13 @@
#pragma once
#include <array>
#include <atomic>
#include <bit>
#include <string>
#include <tuple>
#include <type_traits>
#include <retro_atomic.h>
#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<u32>* 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<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
return LowPart(std::bit_cast<u32*>(ptr));
static_assert(sizeof(retro_atomic_int_t) == sizeof(u32));
return LowPart(reinterpret_cast<u32*>(ptr));
}
inline u16* HighPart(u32* ptr)
{
return LowPart(ptr) + 1;
}
inline u16* HighPart(std::atomic<u32>* ptr)
inline u16* HighPart(retro_atomic_int_t* ptr)
{
static_assert(std::atomic<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
return HighPart(std::bit_cast<u32*>(ptr));
static_assert(sizeof(retro_atomic_int_t) == sizeof(u32));
return HighPart(reinterpret_cast<u32*>(ptr));
}
} // namespace Utils
-1
View File
@@ -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,
+7 -7
View File
@@ -3,9 +3,9 @@
#include "DiscIO/CachedBlob.h"
#include <atomic>
#include <chrono>
#include <retro_atomic.h>
#include <rthreads/rthreads.h>
#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<u64>(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<s64>(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<u64> 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<BlobReader> m_thread_reader; // handed to the thread
bool m_attempt_to_scrub;
sthread_t* m_thread = nullptr;
-1
View File
@@ -10,7 +10,6 @@
namespace DiscIO
{
// Increment CACHE_REVISION (GameFileCache.cpp) if these enums are modified
enum class Platform
{
@@ -4,7 +4,7 @@
#pragma once
#include <algorithm>
#include <atomic>
#include <cstring>
#include <functional>
#include <string>
#include <utility>
@@ -15,8 +15,38 @@
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include <retro_atomic.h>
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<int>(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<ValueType>();
StoreValue(m_input.GetState<ValueType>());
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<ValueType> m_value = {};
mutable retro_atomic_64_t m_value{};
// Unfortunately InputReference's state grabbing is non-const requiring mutable here.
mutable InputReference m_input;
@@ -4,8 +4,8 @@
#include "InputCommon/ControllerInterface/Android/Android.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstring>
#include <memory>
#include <string>
#include <unordered_map>
@@ -17,6 +17,8 @@
#include <android/keycodes.h>
#include <jni.h>
#include <retro_atomic.h>
#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<ControlState> m_state = 0;
mutable std::atomic<Clock::time_point> 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<bool> 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<ControlState> m_state = 0;
retro_atomic_64_t m_state{};
};
class AndroidDevice final : public Core::Device
@@ -4,6 +4,7 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include <algorithm>
#include <cstring>
#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<Device> 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<void()>& 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_ptr<Device>s are freed up BEFORE we shutdown the backends.
ClearDevices();
@@ -191,7 +192,7 @@ void ControllerInterface::ClearDevices()
bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> 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<ciface::Core::Device> 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<bool(const ciface::Core::De
bool force_devices_release)
{
// If we are shutdown (or in process of shutting down) ignore this request:
if (!m_is_init)
if (!IsInit())
return;
ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices,
@@ -265,7 +266,8 @@ void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::De
}) != 0;
});
if (any_removed && (!m_populating_devices_counter || force_devices_release))
if (any_removed &&
(!retro_atomic_load_acquire_int(&m_populating_devices_counter) || force_devices_release))
InvokeDevicesChangedCallbacks();
}
@@ -273,8 +275,8 @@ void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::De
void ControllerInterface::UpdateInput()
{
// This should never happen
ASSERT(m_is_init);
if (!m_is_init)
ASSERT(IsInit());
if (!IsInit())
return;
// The devices to remove are collected as weak pointers: they are not kept alive by this, and
@@ -328,12 +330,16 @@ WindowSystemInfo ControllerInterface::GetWindowSystemInfo() const
void ControllerInterface::SetAspectRatioAdjustment(float value)
{
m_aspect_ratio_adjustment = value;
int bits;
std::memcpy(&bits, &value, sizeof(bits));
retro_atomic_store_release_int(&m_aspect_ratio_adjustment, bits);
}
Common::Vec2 ControllerInterface::GetWindowInputScale() const
{
const auto ar = m_aspect_ratio_adjustment.load();
const int bits = retro_atomic_load_acquire_int(&m_aspect_ratio_adjustment);
float ar;
std::memcpy(&ar, &bits, sizeof(ar));
if (ar > 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'
@@ -3,7 +3,6 @@
#pragma once
#include <atomic>
#include <functional>
#include <memory>
@@ -14,6 +13,8 @@
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/ControllerInterface/InputBackend.h"
#include <retro_atomic.h>
// 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<void()>& 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<bool> 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<int> m_populating_devices_counter;
retro_atomic_int_t m_populating_devices_counter{};
WindowSystemInfo m_wsi;
std::atomic<float> m_aspect_ratio_adjustment = 1;
std::atomic<bool> 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<std::unique_ptr<ciface::InputBackend>> m_input_backends;
};
@@ -3,6 +3,8 @@
#include "InputCommon/ControllerInterface/Wiimote/WiimoteController.h"
#include <cstring>
#include "Common/BitUtils.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
@@ -81,14 +83,19 @@ using UndetectableSignedAnalogInput = SignedInput<false>;
class Motor final : public Core::Device::Output
{
public:
Motor(std::atomic<ControlState>* 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<ControlState>& m_value;
retro_atomic_64_t* m_value;
};
template <typename T>
@@ -1422,8 +1429,11 @@ void Device::UpdateRumble()
{
static constexpr auto rumble_period = std::chrono::milliseconds(100);
const auto on_time =
std::chrono::duration_cast<Clock::duration>(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<Clock::duration>(rumble_period * rumble_level);
const auto off_time = rumble_period - on_time;
const auto now = Clock::now();
@@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
@@ -18,6 +17,8 @@
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include <retro_atomic.h>
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<ControlState> 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.
-281
View File
@@ -1,281 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "UICommon/GameFileCache.h"
#include <algorithm>
#include <cstddef>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#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<std::string> FindAllGamePaths(std::span<const std::string_view> directories_to_scan,
bool recursive_scan)
{
constexpr auto search_extensions =
std::to_array<std::string_view>({".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<GameFile>& 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<const GameFile> 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<UICommon::GameFile> game = std::make_shared<GameFile>(path);
if (!game->IsValid())
return nullptr;
m_cached_files.emplace_back(std::move(game));
}
std::shared_ptr<GameFile>& result = found ? *it : m_cached_files.back();
if (UpdateAdditionalMetadata(&result) || !found)
*cache_changed = true;
return result;
}
bool GameFileCache::Update(std::span<const std::string> 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<std::string> 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<GameFile>(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<GameFile>& 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<GameFile>* 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<GameFile> copy = std::make_shared<GameFile>(**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<size_t>(ptr);
// Then actually do the write.
std::vector<u8> 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<u8> 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<GameFile>& elem) {
if (state.IsReadMode())
elem = std::make_shared<GameFile>();
elem->DoState(state);
});
}
} // namespace UICommon
-70
View File
@@ -1,70 +0,0 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <cstddef>
#include <functional>
#include <memory>
#include <span>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
class PointerWrap;
namespace UICommon
{
class GameFile;
std::vector<std::string> FindAllGamePaths(std::span<const std::string_view> directories_to_scan,
bool recursive_scan);
class GameFileCache
{
public:
enum class DeleteOnDisk
{
No = 0,
Yes = 1,
};
using ForEachFn = std::function<void(const std::shared_ptr<const GameFile>&)>;
using GameAddedToCacheFn = std::function<void(const std::shared_ptr<const GameFile>&)>;
using GameRemovedFromCacheFn = std::function<void(const std::string&)>;
using GameUpdatedFn = std::function<void(const std::shared_ptr<const GameFile>&)>;
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<const GameFile> AddOrGet(const std::string& path, bool* cache_changed);
// These functions return true if the call modified the cache.
bool Update(std::span<const std::string> 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<GameFile>* game_file);
bool SyncCacheFile(bool save);
void DoState(PointerWrap* p, u64 size = 0);
std::string m_path;
std::vector<std::shared_ptr<GameFile>> m_cached_files;
};
} // namespace UICommon
@@ -142,7 +142,8 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& vtx_decl)
D3DVertexFormat::~D3DVertexFormat()
{
ID3D11InputLayout* layout = m_layout.load();
ID3D11InputLayout* layout =
static_cast<ID3D11InputLayout*>(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<ID3D11InputLayout*>(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<ID3D11InputLayout*>(retro_atomic_load_acquire_ptr(&m_layout));
}
return layout;
+15 -21
View File
@@ -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<u32>(native_res_result),
std::memory_order_relaxed);
AddQueryResult(entry.query_group, static_cast<u32>(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<u32>(native_res_result),
std::memory_order_relaxed);
SetQueryResult(entry.query_group, static_cast<u32>(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
@@ -4,10 +4,11 @@
#pragma once
#include <array>
#include <atomic>
#include <memory>
#include <vector>
#include <retro_atomic.h>
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/VertexManagerBase.h"
@@ -29,7 +30,7 @@ private:
std::array<D3D11_INPUT_ELEMENT_DESC, 32> m_elems{};
UINT m_num_elems = 0;
std::atomic<ID3D11InputLayout*> m_layout{nullptr};
retro_atomic_ptr_t m_layout{};
};
class VertexManager : public VertexManagerBase
@@ -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<u32>(native_res_result),
std::memory_order_relaxed);
AddQueryResult(entry.query_group, static_cast<u32>(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)
@@ -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);
@@ -21,8 +21,7 @@ void Metal::PerfQuery::ResetQuery()
{
std::lock_guard<std::mutex> lock(m_results_mtx);
m_current_query++;
for (std::atomic<u32>& 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<u32>(native_res_result));
}
}
m_query_count.fetch_sub(1, std::memory_order_release);
SubQueryCount(1);
}
m_cv.notify_one();
}
+18 -22
View File
@@ -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<u64>(result) * EFB_WIDTH * EFB_HEIGHT /
(g_framebuffer_manager->GetEFBWidth() * g_framebuffer_manager->GetEFBHeight());
m_results[entry.query_group].fetch_add(static_cast<u32>(native_res_result),
std::memory_order_relaxed);
AddQueryResult(entry.query_group, static_cast<u32>(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
@@ -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});
}
@@ -8,6 +8,8 @@
#include <memory>
#include <vector>
#include <retro_atomic.h>
#include <Common/WorkQueueThread.h>
#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<bool> waiting_for_submit{false};
retro_atomic_int_t waiting_for_submit{};
u32 frame_index = 0;
std::vector<std::function<void()>> cleanup_resources;
@@ -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<u32>(native_res_result),
std::memory_order_relaxed);
AddQueryResult(entry.query_group, static_cast<u32>(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)
@@ -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
+1 -2
View File
@@ -9,7 +9,6 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include <atomic>
#include <memory>
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<TimeType> m_last_loaded_time = {};
retro_atomic_64_t m_last_loaded_time{}; /* TimeType ticks */
};
// An abstract class that is expected to
@@ -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<s64>(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<s64>(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<Batch*>(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)
{
@@ -3,7 +3,6 @@
#pragma once
#include <atomic>
#include <list>
#include <vector>
@@ -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<s64> m_change_in_memory = 0;
retro_atomic_64_t m_change_in_memory{};
};
} // namespace VideoCommon
+96 -138
View File
@@ -3,7 +3,6 @@
#include "VideoCommon/CommandProcessor.h"
#include <atomic>
#include <fmt/format.h>
#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<u32>& 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<u32>(highbits) << 16),
std::memory_order_relaxed);
StoreReg(reg, (LoadReg(reg) & 0x0000FFFF) | (static_cast<u32>(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<u16>([](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<u16>(fifo_.CPWritePointer.load(std::memory_order_relaxed) -
fifo_.SafeCPReadPointer.load(std::memory_order_relaxed));
return static_cast<u16>(LoadReg(&fifo_.CPWritePointer) - LoadReg(&fifo_.SafeCPReadPointer));
}
else
{
return static_cast<u16>(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<u16>(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<u16>([](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<u16>([](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<u16>([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<u16>([](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<u16>([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<u16>([](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<u16>([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)
{
+29 -18
View File
@@ -3,11 +3,11 @@
#pragma once
#include <atomic>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
#include <retro_atomic.h>
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<u32>(retro_atomic_load_relaxed_int(reg));
}
inline void StoreReg(retro_atomic_int_t* reg, u32 value)
{
retro_atomic_store_relaxed_int(reg, static_cast<int>(value));
}
struct SCPFifoStruct
{
// fifo registers
std::atomic<u32> CPBase = 0;
std::atomic<u32> CPEnd = 0;
retro_atomic_int_t CPBase{};
retro_atomic_int_t CPEnd{};
u32 CPHiWatermark = 0;
u32 CPLoWatermark = 0;
std::atomic<u32> CPReadWriteDistance = 0;
std::atomic<u32> CPWritePointer = 0;
std::atomic<u32> CPReadPointer = 0;
std::atomic<u32> CPBreakpoint = 0;
std::atomic<u32> 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<u32> bFF_GPLinkEnable = 0;
std::atomic<u32> bFF_GPReadEnable = 0;
std::atomic<u32> bFF_BPEnable = 0;
std::atomic<u32> bFF_BPInt = 0;
std::atomic<u32> 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<u32> bFF_LoWatermarkInt = 0;
std::atomic<u32> bFF_HiWatermarkInt = 0;
retro_atomic_int_t bFF_LoWatermarkInt{};
retro_atomic_int_t bFF_HiWatermarkInt{};
std::atomic<u32> bFF_LoWatermark = 0;
std::atomic<u32> bFF_HiWatermark = 0;
retro_atomic_int_t bFF_LoWatermark{};
retro_atomic_int_t bFF_HiWatermark{};
void Init();
void DoState(PointerWrap& p);
+68 -67
View File
@@ -3,7 +3,6 @@
#include "VideoCommon/Fifo.h"
#include <atomic>
#include <cstring>
#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<size_t>(m_video_buffer + FIFO_SIZE - m_video_buffer_write_ptr))
u8* write_ptr = GetWritePtr();
if (GPFifo::GATHER_PIPE_SIZE > static_cast<size_t>(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<size_t>(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<size_t>(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<size_t>(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<true>(
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<s32>(fifo.CPReadWriteDistance.load(std::memory_order_relaxed)) -
GPFifo::GATHER_PIPE_SIZE;
static_cast<s32>(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<int>(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<int>(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.
+16 -5
View File
@@ -3,9 +3,9 @@
#pragma once
#include <atomic>
#include <cstddef>
#include <optional>
#include <retro_atomic.h>
#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<bool> 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<u8*> m_video_buffer_write_ptr = nullptr;
std::atomic<u8*> 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<int> m_sync_ticks = 0;
retro_atomic_int_t m_sync_ticks{};
u8* GetWritePtr() const
{
return static_cast<u8*>(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<u8*>(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;
+42 -5
View File
@@ -3,10 +3,10 @@
#pragma once
#include <array>
#include <atomic>
#include <memory>
#include <retro_atomic.h>
#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<u32> m_query_count;
std::array<std::atomic<u32>, 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<u32>(retro_atomic_load_acquire_int(&m_query_count)); }
void AddQueryCount(u32 count)
{
retro_atomic_fetch_add_int(&m_query_count, static_cast<int>(count));
}
void SubQueryCount(u32 count)
{
retro_atomic_fetch_sub_int(&m_query_count, static_cast<int>(count));
}
u32 QueryResult(PerfQueryGroup group) const
{
return static_cast<u32>(retro_atomic_load_acquire_int(&m_results[group]));
}
void AddQueryResult(PerfQueryGroup group, u32 value)
{
retro_atomic_fetch_add_int(&m_results[group], static_cast<int>(value));
}
void SetQueryResult(PerfQueryGroup group, u32 value)
{
retro_atomic_store_release_int(&m_results[group], static_cast<int>(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<PerfQueryBase> g_perf_query;
+4 -4
View File
@@ -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")
+3 -1
View File
@@ -13,6 +13,8 @@
#include <memory>
#include <tuple>
#include <retro_atomic.h>
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