Qt: Add hardware check for SSE4 and AVX2

This commit is contained in:
Connor McLaughlin 2022-05-27 19:48:59 +10:00 committed by refractionpcsx2
parent c70afc24bd
commit 30096a5ae4
8 changed files with 129 additions and 0 deletions

View File

@ -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

View File

@ -19,6 +19,7 @@ target_sources(pcsx2-qt PRIVATE
AutoUpdaterDialog.ui
DisplayWidget.cpp
DisplayWidget.h
EarlyHardwareCheck.cpp
EmuThread.cpp
EmuThread.h
Main.cpp

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<int>(std::strlen(error));
int wlen = MultiByteToWideChar(CP_UTF8, 0, error, error_len, nullptr, 0);
if (wlen > 0)
{
wchar_t* werror = static_cast<wchar_t*>(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

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QMessageBox>
#include <cstdlib>
#include <csignal>
@ -182,6 +183,23 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
return true;
}
#ifndef _WIN32
// See note at the end of the file as to why we don't do this on Windows.
static bool PerformEarlyHardwareChecks()
{
// NOTE: No point translating this message, because the configuration isn't loaded yet, so we
// won't know which language to use, and loading the configuration uses float instructions.
const char* error;
if (VMManager::PerformEarlyHardwareChecks(&error))
return true;
QMessageBox::critical(nullptr, QStringLiteral("Hardware Check Failed"), QString::fromUtf8(error));
return false;
}
#endif
int main(int argc, char* argv[])
{
CrashHandler::Install();
@ -191,6 +209,12 @@ int main(int argc, char* argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication app(argc, argv);
#ifndef _WIN32
if (!PerformEarlyHardwareChecks())
return EXIT_FAILURE;
#endif
std::shared_ptr<VMBootParameters> autoboot;
if (!ParseCommandLineOptions(argc, argv, autoboot))
return EXIT_FAILURE;

View File

@ -134,6 +134,7 @@
<Manifest Include="..\pcsx2\windows\PCSX2.manifest" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />

View File

@ -204,6 +204,7 @@
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp">
<Filter>Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="EarlyHardwareCheck.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">

View File

@ -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();

View File

@ -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();