diff --git a/common/emitter/cpudetect.cpp b/common/emitter/cpudetect.cpp
index 1d2a2ed7ad..baee148596 100644
--- a/common/emitter/cpudetect.cpp
+++ b/common/emitter/cpudetect.cpp
@@ -46,6 +46,11 @@ using namespace x86Emitter;
alignas(16) x86capabilities x86caps;
+#ifdef _MSC_VER
+// We disable optimizations for this function, because we need x86capabilities for AVX
+// detection, but if we keep opts on, it'll use AVX instructions for inlining memzero.
+#pragma optimize("", off)
+#endif
x86capabilities::x86capabilities()
: isIdentified(false)
, VendorID(x86Vendor_Unknown)
@@ -65,6 +70,9 @@ x86capabilities::x86capabilities()
memzero(VendorName);
memzero(FamilyName);
}
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif
// Warning! We've had problems with the MXCSR detection code causing stack corruption in
// MSVC PGO builds. The problem was fixed when I moved the MXCSR code to this function, and
diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt
index b562c7b0a8..56dee96bac 100644
--- a/pcsx2-qt/CMakeLists.txt
+++ b/pcsx2-qt/CMakeLists.txt
@@ -19,6 +19,7 @@ target_sources(pcsx2-qt PRIVATE
AutoUpdaterDialog.ui
DisplayWidget.cpp
DisplayWidget.h
+ EarlyHardwareCheck.cpp
EmuThread.cpp
EmuThread.h
Main.cpp
diff --git a/pcsx2-qt/EarlyHardwareCheck.cpp b/pcsx2-qt/EarlyHardwareCheck.cpp
new file mode 100644
index 0000000000..78af2e6d6e
--- /dev/null
+++ b/pcsx2-qt/EarlyHardwareCheck.cpp
@@ -0,0 +1,59 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2022 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 "PrecompiledHeader.h"
+
+#if defined(_WIN32) && defined(_MSC_VER)
+
+#include "pcsx2/VMManager.h"
+
+#include "common/RedtapeWindows.h"
+
+// The problem with AVX2 builds on Windows, is that MSVC generates AVX instructions for zeroing memory,
+// which is pretty common in our global object constructors. So, we have to use a special object which
+// gets initialized before all other global objects, that does the hardware check, and terminates the
+// process before main() or any of the other objects are constructed (which would subsequently crash).
+struct EarlyHardwareCheckObject
+{
+#pragma optimize("", off)
+ EarlyHardwareCheckObject()
+ {
+ const char* error;
+ if (VMManager::PerformEarlyHardwareChecks(&error))
+ return;
+
+ // we can't use StringUtil::UTF8StringToWideString because *that* constructor uses AVX..
+ const int error_len = static_cast(std::strlen(error));
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, error, error_len, nullptr, 0);
+ if (wlen > 0)
+ {
+ wchar_t* werror = static_cast(HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t) * (error_len + 1)));
+ if (werror && (wlen = MultiByteToWideChar(CP_UTF8, 0, error, error_len, werror, wlen)) > 0)
+ {
+ werror[wlen] = 0;
+ MessageBoxW(NULL, werror, L"Hardware Check Failed", MB_ICONERROR);
+ HeapFree(GetProcessHeap(), 0, werror);
+ }
+ }
+
+ TerminateProcess(GetCurrentProcess(), 0xFFFFFFFF);
+ }
+#pragma optimize("", on)
+};
+#pragma warning(disable : 4075) // warning C4075: initializers put in unrecognized initialization area
+#pragma init_seg(".CRT$XCT")
+EarlyHardwareCheckObject s_hardware_checker;
+
+#endif
diff --git a/pcsx2-qt/Main.cpp b/pcsx2-qt/Main.cpp
index f2e082f8df..2778dd703b 100644
--- a/pcsx2-qt/Main.cpp
+++ b/pcsx2-qt/Main.cpp
@@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include
+#include
#include
#include
@@ -182,6 +183,23 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr autoboot;
if (!ParseCommandLineOptions(argc, argv, autoboot))
return EXIT_FAILURE;
diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj
index 5f7e505aa4..37107c4487 100644
--- a/pcsx2-qt/pcsx2-qt.vcxproj
+++ b/pcsx2-qt/pcsx2-qt.vcxproj
@@ -134,6 +134,7 @@
+
diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters
index 015a7cd874..99627fdc11 100644
--- a/pcsx2-qt/pcsx2-qt.vcxproj.filters
+++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters
@@ -204,6 +204,7 @@
Tools\Input Recording
+
diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp
index 301b171c8b..cf287c4b7a 100644
--- a/pcsx2/VMManager.cpp
+++ b/pcsx2/VMManager.cpp
@@ -128,6 +128,38 @@ static s32 s_current_save_slot = 1;
static u32 s_frame_advance_count = 0;
static u32 s_mxcsr_saved;
+bool VMManager::PerformEarlyHardwareChecks(const char** error)
+{
+#define COMMON_DOWNLOAD_MESSAGE \
+ "PCSX2 builds can be downloaded from https://pcsx2.net/downloads/"
+
+#if defined(_M_X86)
+ // On Windows, this gets called as a global object constructor, before any of our objects are constructed.
+ // So, we have to put it on the stack instead.
+ x86capabilities temp_x86_caps;
+ temp_x86_caps.Identify();
+
+ if (!temp_x86_caps.hasStreamingSIMD4Extensions)
+ {
+ *error = "PCSX2 requires the Streaming SIMD 4 Extensions instruction set, which your CPU does not support.\n\n"
+ "SSE4 is now a minimum requirement for PCSX2. You should either upgrade your CPU, or use an older build such as 1.6.0.\n\n" COMMON_DOWNLOAD_MESSAGE;
+ return false;
+ }
+
+#if _M_SSE >= 0x0501
+ if (!temp_x86_caps.hasAVX || !temp_x86_caps.hasAVX2)
+ {
+ *error = "This build of PCSX2 requires the Advanced Vector Extensions 2 instruction set, which your CPU does not support.\n\n"
+ "You should download and run the SSE4 build of PCSX2 instead, or upgrade to a CPU that supports AVX2 to use this build.\n\n" COMMON_DOWNLOAD_MESSAGE;
+ return false;
+ }
+#endif
+#endif
+
+#undef COMMON_DOWNLOAD_MESSAGE
+ return true;
+}
+
VMState VMManager::GetState()
{
return s_state.load();
diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h
index b5e4c5486b..4910b7791d 100644
--- a/pcsx2/VMManager.h
+++ b/pcsx2/VMManager.h
@@ -52,6 +52,9 @@ struct VMBootParameters
namespace VMManager
{
+ /// Makes sure that AVX2 is available if we were compiled with it.
+ bool PerformEarlyHardwareChecks(const char** error);
+
/// Returns the current state of the VM.
VMState GetState();