From d56e6427a97a6d09b6ba07bca0579fad714b011e Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 17 Dec 2021 23:05:50 -0600 Subject: [PATCH] GS: Add MultiISA files --- pcsx2/CMakeLists.txt | 2 + pcsx2/GS/GSUtil.cpp | 26 ++++------ pcsx2/GS/GSUtil.h | 4 -- pcsx2/GS/MultiISA.cpp | 92 +++++++++++++++++++++++++++++++++ pcsx2/GS/MultiISA.h | 85 ++++++++++++++++++++++++++++++ pcsx2/pcsx2.vcxproj | 2 + pcsx2/pcsx2.vcxproj.filters | 6 +++ pcsx2/pcsx2core.vcxproj | 2 + pcsx2/pcsx2core.vcxproj.filters | 6 +++ 9 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 pcsx2/GS/MultiISA.cpp create mode 100644 pcsx2/GS/MultiISA.h diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 8eb44fc454..9a56b61402 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -673,6 +673,7 @@ set(pcsx2GSSources GS/GSTables.cpp GS/GSUtil.cpp GS/GSVector.cpp + GS/MultiISA.cpp GS/Renderers/Common/GSDevice.cpp GS/Renderers/Common/GSDirtyRect.cpp GS/Renderers/Common/GSFunctionMap.cpp @@ -732,6 +733,7 @@ set(pcsx2GSHeaders GS/GSVector4i.h GS/GSVector8.h GS/GSVector8i.h + GS/MultiISA.h GS/Renderers/Common/GSDevice.h GS/Renderers/Common/GSDirtyRect.h GS/Renderers/Common/GSFastList.h diff --git a/pcsx2/GS/GSUtil.cpp b/pcsx2/GS/GSUtil.cpp index 03f4ab7f77..95574d6305 100644 --- a/pcsx2/GS/GSUtil.cpp +++ b/pcsx2/GS/GSUtil.cpp @@ -17,6 +17,7 @@ #include "GS.h" #include "GSExtra.h" #include "GSUtil.h" +#include "MultiISA.h" #include "common/StringUtil.h" #ifdef _WIN32 @@ -29,8 +30,6 @@ #define SVN_MODS 0 #endif -Xbyak::util::Cpu g_cpu; - static class GSUtilMaps { public: @@ -149,37 +148,30 @@ bool GSUtil::HasCompatibleBits(u32 spsm, u32 dpsm) bool GSUtil::CheckSSE() { - bool status = true; - struct ISA { - Xbyak::util::Cpu::Type type; + ProcessorFeatures::VectorISA isa; const char* name; }; ISA checks[] = { - {Xbyak::util::Cpu::tSSE41, "SSE41"}, + {ProcessorFeatures::VectorISA::SSE4, "SSE 4.1"}, #if _M_SSE >= 0x500 - {Xbyak::util::Cpu::tAVX, "AVX1"}, + {ProcessorFeatures::VectorISA::AVX, "AVX"}, #endif #if _M_SSE >= 0x501 - {Xbyak::util::Cpu::tAVX2, "AVX2"}, - {Xbyak::util::Cpu::tBMI1, "BMI1"}, - {Xbyak::util::Cpu::tBMI2, "BMI2"}, + {ProcessorFeatures::VectorISA::AVX2, "AVX2"}, #endif }; - for (const ISA& check : checks) { - if (!g_cpu.has(check.type)) + if (g_cpu.vectorISA < check.isa) { - fprintf(stderr, "This CPU does not support %s\n", check.name); - - status = false; + Console.Error("This CPU does not support %s", check.name); + return false; } } - - return status; + return true; } CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type) diff --git a/pcsx2/GS/GSUtil.h b/pcsx2/GS/GSUtil.h index df893a3146..2986ed116e 100644 --- a/pcsx2/GS/GSUtil.h +++ b/pcsx2/GS/GSUtil.h @@ -22,8 +22,6 @@ #include #endif -#include - class GSUtil { public: @@ -52,5 +50,3 @@ void GSmkdir(const char* dir); std::string GStempdir(); const char* psm_str(int psm); - -extern Xbyak::util::Cpu g_cpu; diff --git a/pcsx2/GS/MultiISA.cpp b/pcsx2/GS/MultiISA.cpp new file mode 100644 index 0000000000..5daf775a77 --- /dev/null +++ b/pcsx2/GS/MultiISA.cpp @@ -0,0 +1,92 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 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" +#include "MultiISA.h" +#include + +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif + +static Xbyak::util::Cpu s_cpu; + +static ProcessorFeatures::VectorISA getCurrentISA() +{ + // For debugging + if (const char* over = getenv("OVERRIDE_VECTOR_ISA")) + { + if (strcasecmp(over, "avx2") == 0) + { + fprintf(stderr, "Vector ISA Override: AVX2\n"); + return ProcessorFeatures::VectorISA::AVX2; + } + if (strcasecmp(over, "avx") == 0) + { + fprintf(stderr, "Vector ISA Override: AVX\n"); + return ProcessorFeatures::VectorISA::AVX; + } + if (strcasecmp(over, "sse4") == 0) + { + fprintf(stderr, "Vector ISA Override: SSE4\n"); + return ProcessorFeatures::VectorISA::SSE4; + } + } + if (s_cpu.has(Xbyak::util::Cpu::tAVX2) && s_cpu.has(Xbyak::util::Cpu::tBMI1) && s_cpu.has(Xbyak::util::Cpu::tBMI2)) + return ProcessorFeatures::VectorISA::AVX2; + else if (s_cpu.has(Xbyak::util::Cpu::tAVX)) + return ProcessorFeatures::VectorISA::AVX; + else if (s_cpu.has(Xbyak::util::Cpu::tSSE41)) + return ProcessorFeatures::VectorISA::SSE4; + else + return ProcessorFeatures::VectorISA::None; +} + +static ProcessorFeatures getProcessorFeatures() +{ + ProcessorFeatures features = {}; + features.vectorISA = getCurrentISA(); + features.hasFMA = s_cpu.has(Xbyak::util::Cpu::tFMA); + if (const char* over = getenv("OVERRIDE_FMA")) + { + features.hasFMA = over[0] == 'Y' || over[0] == 'y' || over[0] == '1'; + fprintf(stderr, "Processor FMA override: %s\n", features.hasFMA ? "Supported" : "Unsupported"); + } + features.hasSlowGather = false; + if (const char* over = getenv("OVERRIDE_SLOW_GATHER")) // Easy override for comparing on vs off + { + features.hasSlowGather = over[0] == 'Y' || over[0] == 'y' || over[0] == '1'; + fprintf(stderr, "Processor gather override: %s\n", features.hasSlowGather ? "Slow" : "Fast"); + } + else if (features.vectorISA == ProcessorFeatures::VectorISA::AVX2) + { + if (s_cpu.has(Xbyak::util::Cpu::tINTEL)) + { + // Slow on Haswell + // CPUID data from https://en.wikichip.org/wiki/intel/cpuid + features.hasSlowGather = s_cpu.displayModel == 0x46 || s_cpu.displayModel == 0x45 || s_cpu.displayModel == 0x3c; + } + else + { + // Currently no Zen CPUs with fast VPGATHERDD + // Check https://uops.info/table.html as new CPUs come out for one that doesn't split it into like 40 µops + // Doing it manually is about 28 µops (8x xmm -> gpr, 6x extr, 8x load, 6x insr) + features.hasSlowGather = true; + } + } + return features; +} + +const ProcessorFeatures g_cpu = getProcessorFeatures(); diff --git a/pcsx2/GS/MultiISA.h b/pcsx2/GS/MultiISA.h new file mode 100644 index 0000000000..5b0fd54bbc --- /dev/null +++ b/pcsx2/GS/MultiISA.h @@ -0,0 +1,85 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 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 "PCSX2Base.h" + +// For multiple-isa compilation +#ifdef MULTI_ISA_UNSHARED_COMPILATION + // Preprocessor should have MULTI_ISA_UNSHARED_COMPILATION defined to `isa_sse4`, `isa_avx`, or `isa_avx2` + #define CURRENT_ISA MULTI_ISA_UNSHARED_COMPILATION +#else + // Define to isa_native in shared section in addition to multi-isa-off so if someone tries to use it they'll hopefully get a linker error and notice + #define CURRENT_ISA isa_native +#endif + +#if defined(MULTI_ISA_UNSHARED_COMPILATION) + // Preprocessor should have MULTI_ISA_IS_FIRST defined to 0 or 1 + /// Use with `#if MULTI_ISA_COMPILE_ONCE` to make a single definition of something in an otherwise multi-isa-compiled file. + #define MULTI_ISA_COMPILE_ONCE MULTI_ISA_IS_FIRST +#elif !defined(MULTI_ISA_SHARED_COMPILATION) + #define MULTI_ISA_COMPILE_ONCE 1 +#else + #define MULTI_ISA_COMPILE_ONCE static_assert(0, "You don't need MULTI_ISA_COMPILE_ONCE in a non-multi-isa file!"); +#endif + +#ifndef MULTI_ISA_SHARED_COMPILATION + /// Mark the start of a header defining code that will be compiled multiple times in multi-isa mode + /// Anything between this and a `MULTI_ISA_UNSHARED_END` will be placed in a different namespace for each of the multilple compilations + #define MULTI_ISA_UNSHARED_START namespace CURRENT_ISA { + /// Mark the end of a header defining code that will be compiled multiple times in multi-isa mode + #define MULTI_ISA_UNSHARED_END } + /// Mark the beginning of a file implementing things that will be compiled multiple times in multi-isa mode + /// Takes advantage of the fact that a `using namespace` declaration will also affect any implementations of things as long as they're not valid without it + /// Fully global variables are valid as-is, however, and will need to have `CURRENT_ISA::` manually prepended to them. + /// If you forget to do this, it will show up as a linker error (either multiple definitions of the function/variable, or a "failed to find isa_native::function") + #define MULTI_ISA_UNSHARED_IMPL using namespace CURRENT_ISA +#else + #define MULTI_ISA_UNSHARED_START static_assert(0, "This file should not be included by multi-isa shared compilation!"); + #define MULTI_ISA_UNSHARED_IMPL static_assert(0, "This file should be compiled unshared in multi-isa mode!"); + #define MULTI_ISA_UNSHARED_END +#endif + +struct ProcessorFeatures +{ + enum class VectorISA { None, SSE4, AVX, AVX2 }; + VectorISA vectorISA; + bool hasFMA; + bool hasSlowGather; +}; + +extern const ProcessorFeatures g_cpu; + +#if defined(MULTI_ISA_UNSHARED_COMPILATION) || defined(MULTI_ISA_SHARED_COMPILATION) + #define MULTI_ISA_DEF(...) \ + namespace isa_sse4 { __VA_ARGS__ } \ + namespace isa_avx { __VA_ARGS__ } \ + namespace isa_avx2 { __VA_ARGS__ } + + #define MULTI_ISA_FRIEND(klass) \ + friend class isa_sse4::klass; \ + friend class isa_avx ::klass; \ + friend class isa_avx2::klass; + + #define MULTI_ISA_SELECT(fn) (\ + ::g_cpu.vectorISA == ProcessorFeatures::VectorISA::AVX2 ? isa_avx2::fn : \ + ::g_cpu.vectorISA == ProcessorFeatures::VectorISA::AVX ? isa_avx ::fn : \ + isa_sse4::fn) +#else + #define MULTI_ISA_DEF(...) namespace isa_native { __VA_ARGS__ } + #define MULTI_ISA_FRIEND(klass) friend class isa_native::klass; + #define MULTI_ISA_SELECT(fn) (isa_native::fn) +#endif diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 1358ded6de..43d90ceebd 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -475,6 +475,7 @@ + @@ -875,6 +876,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 22cdebe4cf..e7f4a82968 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1520,6 +1520,9 @@ System\Ps2\GS + + System\Ps2\GS + AppHost\Dialogs @@ -2722,6 +2725,9 @@ System\Ps2\GS + + System\Ps2\GS + System\Ps2\GS diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index ab925372ce..dba1d1216a 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -328,6 +328,7 @@ + @@ -651,6 +652,7 @@ + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index 3346bd29ed..d08052581c 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -1013,6 +1013,9 @@ System\Ps2\GS + + System\Ps2\GS + System\Ps2\GS @@ -1871,6 +1874,9 @@ System\Ps2\GS + + System\Ps2\GS + System\Ps2\GS