mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
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:
1 parent
d3b454d48b
commit
53238c0a06
34 files changed
+503
-773
No files matched your search
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user