From e5716922a3da3092e4c46c5060a0afa5823314b7 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 9 May 2022 20:28:57 +1000 Subject: [PATCH] Common/Threading: Move a bunch of unnecessary wrappers to gui --- common/CMakeLists.txt | 2 - common/Darwin/DarwinThreads.cpp | 6 + common/Exceptions.cpp | 1 + common/Linux/LnxThreads.cpp | 5 + common/Semaphore.cpp | 5 +- common/ThreadTools.cpp | 26 ---- common/Threading.h | 168 ++---------------------- common/Windows/WinThreads.cpp | 5 + common/common.vcxproj | 2 - common/common.vcxproj.filters | 6 - common/emitter/instructions.h | 1 + common/emitter/legacy.cpp | 1 + common/emitter/x86types.h | 2 + pcsx2/CMakeLists.txt | 2 +- {common => pcsx2/gui}/Mutex.cpp | 2 +- pcsx2/gui/PersistentThread.h | 226 +++++++++++++++++++++++++++++++- pcsx2/gui/ScopedPtrMT.h | 140 -------------------- pcsx2/pcsx2.vcxproj | 2 +- pcsx2/pcsx2.vcxproj.filters | 12 +- 19 files changed, 274 insertions(+), 340 deletions(-) delete mode 100644 common/ThreadTools.cpp rename {common => pcsx2/gui}/Mutex.cpp (99%) delete mode 100644 pcsx2/gui/ScopedPtrMT.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c8416d9dfc..f0ac231576 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -20,7 +20,6 @@ target_sources(common PRIVATE FastFormatString.cpp FastJmp.cpp FileSystem.cpp - Mutex.cpp Misc.cpp MD5Digest.cpp PathUtils.cpp @@ -33,7 +32,6 @@ target_sources(common PRIVATE StringHelpers.cpp StringUtil.cpp Timer.cpp - ThreadTools.cpp WindowInfo.cpp emitter/bmi.cpp emitter/cpudetect.cpp diff --git a/common/Darwin/DarwinThreads.cpp b/common/Darwin/DarwinThreads.cpp index d1aaa3347a..d4d68f82e2 100644 --- a/common/Darwin/DarwinThreads.cpp +++ b/common/Darwin/DarwinThreads.cpp @@ -15,6 +15,7 @@ #if defined(__APPLE__) +#include #include #include #include @@ -32,6 +33,11 @@ __forceinline void Threading::Sleep(int ms) usleep(1000 * ms); } +__forceinline void Threading::Timeslice() +{ + sched_yield(); +} + // For use in spin/wait loops, acts as a hint to Intel CPUs and should, in theory // improve performance and reduce cpu power consumption. __forceinline void Threading::SpinWait() diff --git a/common/Exceptions.cpp b/common/Exceptions.cpp index aa7bc58f81..5952519c79 100644 --- a/common/Exceptions.cpp +++ b/common/Exceptions.cpp @@ -23,6 +23,7 @@ #include "common/Dependencies.h" // _ macro #include "common/Threading.h" #include "common/General.h" +#include "common/StringHelpers.h" // for lack of a better place... Fnptr_OutOfMemory pxDoOutOfMemory = NULL; diff --git a/common/Linux/LnxThreads.cpp b/common/Linux/LnxThreads.cpp index 8368d3fb83..6b5d61290c 100644 --- a/common/Linux/LnxThreads.cpp +++ b/common/Linux/LnxThreads.cpp @@ -55,6 +55,11 @@ __forceinline void Threading::Sleep(int ms) usleep(1000 * ms); } +__forceinline void Threading::Timeslice() +{ + sched_yield(); +} + // For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory // improve performance and reduce cpu power consumption. __forceinline void Threading::SpinWait() diff --git a/common/Semaphore.cpp b/common/Semaphore.cpp index 89fafe454d..e961793637 100644 --- a/common/Semaphore.cpp +++ b/common/Semaphore.cpp @@ -14,11 +14,14 @@ */ #include "common/Threading.h" +#include "common/Assertions.h" #ifdef _WIN32 #include "common/RedtapeWindows.h" #endif +#include + // -------------------------------------------------------------------------------------- // Semaphore Implementations // -------------------------------------------------------------------------------------- @@ -168,4 +171,4 @@ bool Threading::KernelSemaphore::TryWait() #endif } -#endif \ No newline at end of file +#endif diff --git a/common/ThreadTools.cpp b/common/ThreadTools.cpp deleted file mode 100644 index cba098f3cd..0000000000 --- a/common/ThreadTools.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#include "common/Threading.h" - -void Threading::pxTestCancel() -{ - pthread_testcancel(); -} - -__fi void Threading::Timeslice() -{ - sched_yield(); -} diff --git a/common/Threading.h b/common/Threading.h index 7921647ace..21bef6e88e 100644 --- a/common/Threading.h +++ b/common/Threading.h @@ -15,21 +15,15 @@ #pragma once -#include -#ifdef _WIN32 -// thanks I hate it. -#include -#define HAVE_MODE_T -#endif -#include -#include // EBUSY -#include -#ifdef __APPLE__ -#include -#endif #include "common/Pcsx2Defs.h" -#include "common/TraceLog.h" #include "common/General.h" + +#if defined(__APPLE__) +#include +#elif !defined(_WIN32) +#include +#endif + #include // -------------------------------------------------------------------------------------- @@ -52,12 +46,11 @@ namespace Threading { - class ThreadHandle; - class pxThread; - class RwMutex; - - extern void pxTestCancel(); - extern void YieldToMain(); + // -------------------------------------------------------------------------------------- + // Platform Specific External APIs + // -------------------------------------------------------------------------------------- + // The following set of documented functions have Linux/Win32 specific implementations, + // which are found in WinThreads.cpp and LnxThreads.cpp extern u64 GetThreadCpuTime(); extern u64 GetThreadTicksPerSecond(); @@ -65,17 +58,6 @@ namespace Threading /// Set the name of the current thread extern void SetNameOfCurrentThread(const char* name); - extern const wxTimeSpan def_yieldgui_interval; -} // namespace Threading - -namespace Threading -{ - // -------------------------------------------------------------------------------------- - // Platform Specific External APIs - // -------------------------------------------------------------------------------------- - // The following set of documented functions have Linux/Win32 specific implementations, - // which are found in WinThreads.cpp and LnxThreads.cpp - // Releases a timeslice to other threads. extern void Timeslice(); @@ -129,44 +111,6 @@ namespace Threading #endif }; - // -------------------------------------------------------------------------------------- - // NonblockingMutex - // -------------------------------------------------------------------------------------- - // This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's - // trylock(), but without any of the extra overhead needed to set up a structure capable - // of blocking waits. It basically optimizes to a single InterlockedExchange. - // - // Simple use: if TryAcquire() returns false, the Bool is already interlocked by another thread. - // If TryAcquire() returns true, you've locked the object and are *responsible* for unlocking - // it later. - // - class NonblockingMutex - { - protected: - std::atomic_flag val; - - public: - NonblockingMutex() { val.clear(); } - virtual ~NonblockingMutex() = default; - - bool TryAcquire() noexcept - { - return !val.test_and_set(); - } - - // Can be done with a TryAcquire/Release but it is likely better to do it outside of the object - bool IsLocked() - { - pxAssertMsg(0, "IsLocked isn't supported for NonblockingMutex"); - return false; - } - - void Release() - { - val.clear(); - } - }; - /// A semaphore that may not have a fast userspace path /// (Used in other semaphore-based algorithms where the semaphore is just used for its thread sleep/wake ability) class KernelSemaphore @@ -261,92 +205,4 @@ namespace Threading /// Should be called by the worker thread if it restarts after dying void Reset(); }; - - class Mutex - { - protected: - pthread_mutex_t m_mutex; - - public: - Mutex(); - virtual ~Mutex(); - virtual bool IsRecursive() const { return false; } - - void Recreate(); - bool RecreateIfLocked(); - void Detach(); - - void Acquire(); - bool Acquire(const wxTimeSpan& timeout); - bool TryAcquire(); - void Release(); - - void AcquireWithoutYield(); - bool AcquireWithoutYield(const wxTimeSpan& timeout); - - void Wait(); - void WaitWithSpin(); - bool Wait(const wxTimeSpan& timeout); - void WaitWithoutYield(); - bool WaitWithoutYield(const wxTimeSpan& timeout); - - protected: - // empty constructor used by MutexLockRecursive - Mutex(bool) {} - }; - - class MutexRecursive : public Mutex - { - public: - MutexRecursive(); - virtual ~MutexRecursive(); - virtual bool IsRecursive() const { return true; } - }; - - // -------------------------------------------------------------------------------------- - // ScopedLock - // -------------------------------------------------------------------------------------- - // Helper class for using Mutexes. Using this class provides an exception-safe (and - // generally clean) method of locking code inside a function or conditional block. The lock - // will be automatically released on any return or exit from the function. - // - // Const qualification note: - // ScopedLock takes const instances of the mutex, even though the mutex is modified - // by locking and unlocking. Two rationales: - // - // 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes, - // this class needs a const hack to allow those accessors to be const (which is typically - // *very* important). - // - // 2) The state of the Mutex is guaranteed to be unchanged when the calling function or - // scope exits, by any means. Only via manual calls to Release or Acquire does that - // change, and typically those are only used in very special circumstances of their own. - // - class ScopedLock - { - DeclareNoncopyableObject(ScopedLock); - - protected: - Mutex* m_lock; - bool m_IsLocked; - - public: - virtual ~ScopedLock(); - explicit ScopedLock(const Mutex* locker = NULL); - explicit ScopedLock(const Mutex& locker); - void AssignAndLock(const Mutex& locker); - void AssignAndLock(const Mutex* locker); - - void Assign(const Mutex& locker); - void Assign(const Mutex* locker); - - void Release(); - void Acquire(); - - bool IsLocked() const { return m_IsLocked; } - - protected: - // Special constructor used by ScopedTryLock - ScopedLock(const Mutex& locker, bool isTryLock); - }; } // namespace Threading diff --git a/common/Windows/WinThreads.cpp b/common/Windows/WinThreads.cpp index 02718314cf..8c8c63ce77 100644 --- a/common/Windows/WinThreads.cpp +++ b/common/Windows/WinThreads.cpp @@ -24,6 +24,11 @@ __fi void Threading::Sleep(int ms) ::Sleep(ms); } +__fi void Threading::Timeslice() +{ + ::Sleep(0); +} + // For use in spin/wait loops, Acts as a hint to Intel CPUs and should, in theory // improve performance and reduce cpu power consumption. __fi void Threading::SpinWait() diff --git a/common/common.vcxproj b/common/common.vcxproj index bab7b615ec..50e9354df4 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -98,9 +98,7 @@ - - diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index 48be9dec75..b3b0a47fc4 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -52,9 +52,6 @@ Source Files - - Source Files - Source Files @@ -76,9 +73,6 @@ Source Files - - Source Files - Source Files diff --git a/common/emitter/instructions.h b/common/emitter/instructions.h index c9ff4f6635..cc5322e870 100644 --- a/common/emitter/instructions.h +++ b/common/emitter/instructions.h @@ -29,6 +29,7 @@ */ #pragma once +#include namespace x86Emitter { diff --git a/common/emitter/legacy.cpp b/common/emitter/legacy.cpp index 4d0715f945..d653f9d806 100644 --- a/common/emitter/legacy.cpp +++ b/common/emitter/legacy.cpp @@ -26,6 +26,7 @@ //------------------------------------------------------------------ #include "common/emitter/legacy_internal.h" +#include "common/Console.h" emitterT void ModRM(uint mod, uint reg, uint rm) { diff --git a/common/emitter/x86types.h b/common/emitter/x86types.h index 1da6014725..08c5448d94 100644 --- a/common/emitter/x86types.h +++ b/common/emitter/x86types.h @@ -16,6 +16,8 @@ #pragma once #include "common/Threading.h" +#include "common/Assertions.h" +#include "common/Pcsx2Defs.h" static const uint iREGCNT_XMM = 16; static const uint iREGCNT_GPR = 16; diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index e7d858e0f6..89f5c5d02f 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1119,6 +1119,7 @@ set(pcsx2GuiSources gui/MainFrame.cpp gui/MainMenuClicks.cpp gui/MessageBoxes.cpp + gui/Mutex.cpp gui/MSWstuff.cpp gui/Panels/BaseApplicableConfigPanel.cpp gui/Panels/BiosSelectorPanel.cpp @@ -1195,7 +1196,6 @@ set(pcsx2GuiHeaders gui/pxStaticText.h gui/RecentIsoList.h gui/Saveslots.h - gui/ScopedPtrMT.h gui/SysThreads.h gui/ThreadingDialogs.h gui/ThreadingDialogs.cpp diff --git a/common/Mutex.cpp b/pcsx2/gui/Mutex.cpp similarity index 99% rename from common/Mutex.cpp rename to pcsx2/gui/Mutex.cpp index ee078b3b44..321f64e504 100644 --- a/common/Mutex.cpp +++ b/pcsx2/gui/Mutex.cpp @@ -13,7 +13,7 @@ * If not, see . */ -#include "common/Threading.h" +#include "PersistentThread.h" namespace Threading { diff --git a/pcsx2/gui/PersistentThread.h b/pcsx2/gui/PersistentThread.h index 7e02a13900..f71a130013 100644 --- a/pcsx2/gui/PersistentThread.h +++ b/pcsx2/gui/PersistentThread.h @@ -17,7 +17,16 @@ #include "common/Threading.h" #include "common/EventSource.h" -#include "ScopedPtrMT.h" +#include "common/Console.h" +#include "common/TraceLog.h" +#include +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif #undef Yield // release the burden of windows.h global namespace spam. @@ -113,12 +122,227 @@ namespace Exception namespace Threading { + extern void pxTestCancel(); + extern void YieldToMain(); + + extern const wxTimeSpan def_yieldgui_interval; + extern pxThread* pxGetCurrentThread(); extern wxString pxGetCurrentThreadName(); extern bool _WaitGui_RecursionGuard(const wxChar* name); extern bool AllowDeletions(); + class Mutex + { + protected: + pthread_mutex_t m_mutex; + + public: + Mutex(); + virtual ~Mutex(); + virtual bool IsRecursive() const { return false; } + + void Recreate(); + bool RecreateIfLocked(); + void Detach(); + + void Acquire(); + bool Acquire(const wxTimeSpan& timeout); + bool TryAcquire(); + void Release(); + + void AcquireWithoutYield(); + bool AcquireWithoutYield(const wxTimeSpan& timeout); + + void Wait(); + void WaitWithSpin(); + bool Wait(const wxTimeSpan& timeout); + void WaitWithoutYield(); + bool WaitWithoutYield(const wxTimeSpan& timeout); + + protected: + // empty constructor used by MutexLockRecursive + Mutex(bool) {} + }; + + class MutexRecursive : public Mutex + { + public: + MutexRecursive(); + virtual ~MutexRecursive(); + virtual bool IsRecursive() const { return true; } + }; + + // -------------------------------------------------------------------------------------- + // ScopedLock + // -------------------------------------------------------------------------------------- + // Helper class for using Mutexes. Using this class provides an exception-safe (and + // generally clean) method of locking code inside a function or conditional block. The lock + // will be automatically released on any return or exit from the function. + // + // Const qualification note: + // ScopedLock takes const instances of the mutex, even though the mutex is modified + // by locking and unlocking. Two rationales: + // + // 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes, + // this class needs a const hack to allow those accessors to be const (which is typically + // *very* important). + // + // 2) The state of the Mutex is guaranteed to be unchanged when the calling function or + // scope exits, by any means. Only via manual calls to Release or Acquire does that + // change, and typically those are only used in very special circumstances of their own. + // + class ScopedLock + { + DeclareNoncopyableObject(ScopedLock); + + protected: + Mutex* m_lock; + bool m_IsLocked; + + public: + virtual ~ScopedLock(); + explicit ScopedLock(const Mutex* locker = NULL); + explicit ScopedLock(const Mutex& locker); + void AssignAndLock(const Mutex& locker); + void AssignAndLock(const Mutex* locker); + + void Assign(const Mutex& locker); + void Assign(const Mutex* locker); + + void Release(); + void Acquire(); + + bool IsLocked() const { return m_IsLocked; } + + protected: + // Special constructor used by ScopedTryLock + ScopedLock(const Mutex& locker, bool isTryLock); + }; + + // -------------------------------------------------------------------------------------- + // ScopedPtrMT + // -------------------------------------------------------------------------------------- + + template + class ScopedPtrMT + { + DeclareNoncopyableObject(ScopedPtrMT); + + protected: + std::atomic m_ptr; + Threading::Mutex m_mtx; + + public: + typedef T element_type; + + wxEXPLICIT ScopedPtrMT(T* ptr = nullptr) + { + m_ptr = ptr; + } + + ~ScopedPtrMT() { _Delete_unlocked(); } + + ScopedPtrMT& Reassign(T* ptr = nullptr) + { + T* doh = m_ptr.exchange(ptr); + if (ptr != doh) + delete doh; + return *this; + } + + ScopedPtrMT& Delete() noexcept + { + ScopedLock lock(m_mtx); + _Delete_unlocked(); + } + + // Removes the pointer from scoped management, but does not delete! + // (ScopedPtr will be nullptr after this method) + T* DetachPtr() + { + ScopedLock lock(m_mtx); + + return m_ptr.exchange(nullptr); + } + + // Returns the managed pointer. Can return nullptr as a valid result if the ScopedPtrMT + // has no object in management. + T* GetPtr() const + { + return m_ptr; + } + + void SwapPtr(ScopedPtrMT& other) + { + ScopedLock lock(m_mtx); + m_ptr.exchange(other.m_ptr.exchange(m_ptr.load())); + T* const tmp = other.m_ptr; + other.m_ptr = m_ptr; + m_ptr = tmp; + } + + // ---------------------------------------------------------------------------- + // ScopedPtrMT Operators + // ---------------------------------------------------------------------------- + // I've decided to use the ATL's approach to pointer validity tests, opposed to + // the wx/boost approach (which uses some bizarre member method pointer crap, and can't + // allow the T* implicit casting. + + bool operator!() const noexcept + { + return m_ptr.load() == nullptr; + } + + // Equality + bool operator==(T* pT) const noexcept + { + return m_ptr == pT; + } + + // Inequality + bool operator!=(T* pT) const noexcept + { + return !operator==(pT); + } + + // Convenient assignment operator. ScopedPtrMT = nullptr will issue an automatic deletion + // of the managed pointer. + ScopedPtrMT& operator=(T* src) + { + return Reassign(src); + } + + #if 0 + operator T*() const + { + return m_ptr; + } + + // Dereference operator, returns a handle to the managed pointer. + // Generates a debug assertion if the object is nullptr! + T& operator*() const + { + pxAssert(m_ptr != nullptr); + return *m_ptr; + } + + T* operator->() const + { + pxAssert(m_ptr != nullptr); + return m_ptr; + } + #endif + + protected: + void _Delete_unlocked() noexcept + { + delete m_ptr.exchange(nullptr); + } + }; + + // ---------------------------------------------------------------------------------------- // RecursionGuard - Basic protection against function recursion // ---------------------------------------------------------------------------------------- diff --git a/pcsx2/gui/ScopedPtrMT.h b/pcsx2/gui/ScopedPtrMT.h deleted file mode 100644 index 3402743eb6..0000000000 --- a/pcsx2/gui/ScopedPtrMT.h +++ /dev/null @@ -1,140 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -#include "common/Threading.h" -using Threading::ScopedLock; - -// -------------------------------------------------------------------------------------- -// ScopedPtrMT -// -------------------------------------------------------------------------------------- - -template -class ScopedPtrMT -{ - DeclareNoncopyableObject(ScopedPtrMT); - -protected: - std::atomic m_ptr; - Threading::Mutex m_mtx; - -public: - typedef T element_type; - - wxEXPLICIT ScopedPtrMT(T* ptr = nullptr) - { - m_ptr = ptr; - } - - ~ScopedPtrMT() { _Delete_unlocked(); } - - ScopedPtrMT& Reassign(T* ptr = nullptr) - { - T* doh = m_ptr.exchange(ptr); - if (ptr != doh) - delete doh; - return *this; - } - - ScopedPtrMT& Delete() noexcept - { - ScopedLock lock(m_mtx); - _Delete_unlocked(); - } - - // Removes the pointer from scoped management, but does not delete! - // (ScopedPtr will be nullptr after this method) - T* DetachPtr() - { - ScopedLock lock(m_mtx); - - return m_ptr.exchange(nullptr); - } - - // Returns the managed pointer. Can return nullptr as a valid result if the ScopedPtrMT - // has no object in management. - T* GetPtr() const - { - return m_ptr; - } - - void SwapPtr(ScopedPtrMT& other) - { - ScopedLock lock(m_mtx); - m_ptr.exchange(other.m_ptr.exchange(m_ptr.load())); - T* const tmp = other.m_ptr; - other.m_ptr = m_ptr; - m_ptr = tmp; - } - - // ---------------------------------------------------------------------------- - // ScopedPtrMT Operators - // ---------------------------------------------------------------------------- - // I've decided to use the ATL's approach to pointer validity tests, opposed to - // the wx/boost approach (which uses some bizarre member method pointer crap, and can't - // allow the T* implicit casting. - - bool operator!() const noexcept - { - return m_ptr.load() == nullptr; - } - - // Equality - bool operator==(T* pT) const noexcept - { - return m_ptr == pT; - } - - // Inequality - bool operator!=(T* pT) const noexcept - { - return !operator==(pT); - } - - // Convenient assignment operator. ScopedPtrMT = nullptr will issue an automatic deletion - // of the managed pointer. - ScopedPtrMT& operator=(T* src) - { - return Reassign(src); - } - -#if 0 - operator T*() const - { - return m_ptr; - } - - // Dereference operator, returns a handle to the managed pointer. - // Generates a debug assertion if the object is nullptr! - T& operator*() const - { - pxAssert(m_ptr != nullptr); - return *m_ptr; - } - - T* operator->() const - { - pxAssert(m_ptr != nullptr); - return m_ptr; - } -#endif - -protected: - void _Delete_unlocked() noexcept - { - delete m_ptr.exchange(nullptr); - } -}; diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 10c9916455..4262cb0fbc 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -354,6 +354,7 @@ + @@ -818,7 +819,6 @@ - diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 84059ffc80..d61759fb75 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1769,7 +1769,12 @@ System\Ps2\GS\Renderers\Direct3D12 - + + AppHost + + + AppHost + @@ -2946,8 +2951,9 @@ System\Ps2\GS\Renderers\Direct3D12 - - + + AppHost +