3 Commits
Author SHA1 Message Date
ExzapandGitHub ada8bbb3b4 Linux/MacOS: Greatly improve performance (#370)
std::unordered_set is implemented as a flat hashtable on libstdc++ which makes clearing expensive due to invoking memset on the entire table. To get the best performance across all platforms this replaces the unordered_set with a custom high-performance sparse bitset
2022-10-14 13:45:40 +02:00
ExzapandGitHub a19ed46b2a Windows: Fix file and folder dialog freeze (#369)
Initializing the COM library immediately seems to be more robust than doing it on demand
2022-10-14 12:49:41 +02:00
Exzap d251ce07e0 XAudio2: Don't quit on failed CoInitializeEx()
It returns an error code when already initialized
2022-10-13 12:18:34 +02:00
8 changed files with 72 additions and 54 deletions
+65 -8
View File
@@ -1005,8 +1005,67 @@ void LatteBufferCache_getStats(uint32& heapSize, uint32& allocationSize, uint32&
}
FSpinlock g_spinlockDCFlushQueue;
std::unordered_set<uint32>* g_DCFlushQueue = new std::unordered_set<uint32>(); // queued pages
std::unordered_set<uint32>* g_DCFlushQueueAlternate = new std::unordered_set<uint32>();
class SparseBitset
{
static inline constexpr size_t TABLE_MASK = 0xFF;
public:
bool Empty() const
{
return m_numNonEmptyVectors == 0;
}
void Set(uint32 index)
{
auto& v = m_bits[index & TABLE_MASK];
if (std::find(v.cbegin(), v.cend(), index) != v.end())
return;
if (v.empty())
{
m_nonEmptyVectors[m_numNonEmptyVectors] = &v;
m_numNonEmptyVectors++;
}
v.emplace_back(index);
}
template<typename TFunc>
void ForAllAndClear(TFunc callbackFunc)
{
auto vCurrent = m_nonEmptyVectors + 0;
auto vEnd = m_nonEmptyVectors + m_numNonEmptyVectors;
while (vCurrent < vEnd)
{
std::vector<uint32>* vec = *vCurrent;
vCurrent++;
for (const auto& it : *vec)
callbackFunc(it);
vec->clear();
}
m_numNonEmptyVectors = 0;
}
void Clear()
{
auto vCurrent = m_nonEmptyVectors + 0;
auto vEnd = m_nonEmptyVectors + m_numNonEmptyVectors;
while (vCurrent < vEnd)
{
std::vector<uint32>* vec = *vCurrent;
vCurrent++;
vec->clear();
}
m_numNonEmptyVectors = 0;
}
private:
std::vector<uint32> m_bits[TABLE_MASK + 1];
std::vector<uint32>* m_nonEmptyVectors[TABLE_MASK + 1];
size_t m_numNonEmptyVectors{ 0 };
};
SparseBitset* s_DCFlushQueue = new SparseBitset();
SparseBitset* s_DCFlushQueueAlternate = new SparseBitset();
void LatteBufferCache_notifyDCFlush(MPTR address, uint32 size)
{
@@ -1017,20 +1076,18 @@ void LatteBufferCache_notifyDCFlush(MPTR address, uint32 size)
uint32 lastPage = (address + size - 1) / CACHE_PAGE_SIZE;
g_spinlockDCFlushQueue.acquire();
for (uint32 i = firstPage; i <= lastPage; i++)
g_DCFlushQueue->emplace(i);
s_DCFlushQueue->Set(i);
g_spinlockDCFlushQueue.release();
}
void LatteBufferCache_processDCFlushQueue()
{
if (g_DCFlushQueue->empty()) // accessing this outside of the lock is technically undefined/unsafe behavior but on all known implementations this is fine and we can avoid the spinlock
if (s_DCFlushQueue->Empty()) // quick check to avoid locking if there is no work to do
return;
g_spinlockDCFlushQueue.acquire();
std::swap(g_DCFlushQueue, g_DCFlushQueueAlternate);
std::swap(s_DCFlushQueue, s_DCFlushQueueAlternate);
g_spinlockDCFlushQueue.release();
for (auto& itr : *g_DCFlushQueueAlternate)
LatteBufferCache_invalidatePage(itr * CACHE_PAGE_SIZE);
g_DCFlushQueueAlternate->clear();
s_DCFlushQueueAlternate->ForAllAndClear([](uint32 index) {LatteBufferCache_invalidatePage(index * CACHE_PAGE_SIZE); });
}
void LatteBufferCache_notifyDrawDone()
-18
View File
@@ -167,25 +167,11 @@ void CubebAPI::SetVolume(sint32 volume)
bool CubebAPI::InitializeStatic()
{
#if BOOST_OS_WINDOWS
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)));
#endif
if (cubeb_init(&s_context, "Cemu Cubeb", nullptr))
{
cemuLog_force("can't create cubeb audio api");
#if BOOST_OS_WINDOWS
if (s_com_initialized)
{
CoUninitialize();
s_com_initialized = false;
}
#endif
return false;
}
return true;
}
@@ -193,10 +179,6 @@ void CubebAPI::Destroy()
{
if (s_context)
cubeb_destroy(s_context);
#if BOOST_OS_WINDOWS
if (s_com_initialized)
CoUninitialize();
#endif
}
std::vector<IAudioAPI::DeviceDescriptionPtr> CubebAPI::GetDevices()
-1
View File
@@ -41,7 +41,6 @@ public:
static void Destroy();
private:
inline static bool s_com_initialized = false;
inline static cubeb* s_context = nullptr;
cubeb_stream* m_stream = nullptr;
-9
View File
@@ -5,7 +5,6 @@
static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers");
HMODULE XAudio27API::s_xaudio_dll = nullptr;
bool XAudio27API::s_com_initialized = false;
std::unique_ptr<IXAudio2, XAudio27API::XAudioDeleter> XAudio27API::s_xaudio;
XAudio27API::XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
@@ -115,8 +114,6 @@ bool XAudio27API::InitializeStatic()
if (s_xaudio)
return true;
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)));
#ifdef _DEBUG
s_xaudio_dll = LoadLibraryExW(L"XAudioD2_7.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if(!s_xaudio_dll)
@@ -142,9 +139,6 @@ bool XAudio27API::InitializeStatic()
if (s_xaudio_dll)
FreeLibrary(s_xaudio_dll);
if (s_com_initialized)
CoUninitialize();
return false;
}
}
@@ -155,9 +149,6 @@ void XAudio27API::Destroy()
if (s_xaudio_dll)
FreeLibrary(s_xaudio_dll);
if (s_com_initialized)
CoUninitialize();
}
std::vector<XAudio27API::DeviceDescriptionPtr> XAudio27API::GetDevices()
-1
View File
@@ -58,7 +58,6 @@ private:
};
static HMODULE s_xaudio_dll;
static bool s_com_initialized;
static std::unique_ptr<IXAudio2, XAudioDeleter> s_xaudio;
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
-13
View File
@@ -23,7 +23,6 @@ static const GUID DEVINTERFACE_AUDIO_RENDER_GUID = { 0xe6327cad, 0xdcec, 0x4949,
static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers");
HMODULE XAudio2API::s_xaudio_dll = nullptr;
bool XAudio2API::s_com_initialized = false;
std::vector<XAudio2API::DeviceDescriptionPtr> XAudio2API::s_devices;
XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
@@ -143,8 +142,6 @@ bool XAudio2API::InitializeStatic()
{
if (s_xaudio_dll)
return true;
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)));
// win 10
s_xaudio_dll = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
@@ -166,9 +163,6 @@ bool XAudio2API::InitializeStatic()
if (s_xaudio_dll)
FreeLibrary(s_xaudio_dll);
if (s_com_initialized)
CoUninitialize();
return false;
}
}
@@ -177,19 +171,12 @@ void XAudio2API::Destroy()
{
if (s_xaudio_dll)
FreeLibrary(s_xaudio_dll);
if (s_com_initialized)
CoUninitialize();
}
const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices()
{
// this function must be called from the same thread as we called CoInitializeEx
s_devices.clear();
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
return s_devices;
try
{
struct IWbemLocator *wbem_locator = nullptr;
-1
View File
@@ -59,7 +59,6 @@ private:
};
static HMODULE s_xaudio_dll;
static bool s_com_initialized;
static std::vector<DeviceDescriptionPtr> s_devices;
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
+7 -3
View File
@@ -323,10 +323,10 @@ void HandlePostUpdate()
fs::remove(filename, ec);
}
#else
while( fs::exists(filename) )
while (fs::exists(filename))
{
std::error_code ec;
fs::remove(filename, ec);
fs::remove(filename, ec);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
#endif
@@ -338,8 +338,10 @@ void ToolShaderCacheMerger();
#if BOOST_OS_WINDOWS
// entrypoint for release builds
int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd)
int wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd)
{
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
SDL_SetMainReady();
if (!LaunchSettings::HandleCommandline(lpCmdLine))
return 0;
@@ -350,6 +352,8 @@ int wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ L
// entrypoint for debug builds with console
int main(int argc, char* argv[])
{
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
SDL_SetMainReady();
if (!LaunchSettings::HandleCommandline(argc, argv))
return 0;