mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-04-28 13:27:58 +03:00
Merge branch 'hedge-dev:main' into ControllerHotplugImprovements
This commit is contained in:
commit
6e7a2f6a13
10 changed files with 193 additions and 32 deletions
|
@ -31,6 +31,7 @@
|
|||
#include <user/config.h>
|
||||
#include <sdl_listener.h>
|
||||
#include <xxHashMap.h>
|
||||
#include <os/process.h>
|
||||
|
||||
#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING)
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
@ -735,10 +736,13 @@ static void DestructTempResources()
|
|||
}
|
||||
|
||||
static std::thread::id g_presentThreadId = std::this_thread::get_id();
|
||||
static std::atomic<bool> g_readyForCommands;
|
||||
|
||||
PPC_FUNC_IMPL(__imp__sub_824ECA00);
|
||||
PPC_FUNC(sub_824ECA00)
|
||||
{
|
||||
// Guard against thread ownership changes when between command lists.
|
||||
g_readyForCommands.wait(false);
|
||||
g_presentThreadId = std::this_thread::get_id();
|
||||
__imp__sub_824ECA00(ctx, base);
|
||||
}
|
||||
|
@ -1623,6 +1627,9 @@ static void BeginCommandList()
|
|||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 1);
|
||||
commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 2);
|
||||
commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 3);
|
||||
|
||||
g_readyForCommands = true;
|
||||
g_readyForCommands.notify_one();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -1652,7 +1659,7 @@ static void ApplyLowEndDefaults()
|
|||
}
|
||||
}
|
||||
|
||||
bool Video::CreateHostDevice(const char *sdlVideoDriver)
|
||||
bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry)
|
||||
{
|
||||
for (uint32_t i = 0; i < 16; i++)
|
||||
g_inputSlots[i].index = i;
|
||||
|
@ -1672,17 +1679,39 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver)
|
|||
std::vector<RenderInterfaceFunction *> interfaceFunctions;
|
||||
|
||||
#ifdef UNLEASHED_RECOMP_D3D12
|
||||
bool allowVulkanRedirection = true;
|
||||
|
||||
if (graphicsApiRetry)
|
||||
{
|
||||
// If we are attempting to create again after a reboot due to a crash, swap the order.
|
||||
g_vulkan = !g_vulkan;
|
||||
|
||||
// Don't allow redirection to Vulkan if we are retrying after a crash,
|
||||
// so the user can at least boot the game with D3D12 if Vulkan fails to work.
|
||||
allowVulkanRedirection = false;
|
||||
}
|
||||
|
||||
interfaceFunctions.push_back(g_vulkan ? CreateVulkanInterfaceWrapper : CreateD3D12Interface);
|
||||
interfaceFunctions.push_back(g_vulkan ? CreateD3D12Interface : CreateVulkanInterfaceWrapper);
|
||||
#else
|
||||
interfaceFunctions.push_back(CreateVulkanInterfaceWrapper);
|
||||
#endif
|
||||
|
||||
for (RenderInterfaceFunction *interfaceFunction : interfaceFunctions)
|
||||
for (size_t i = 0; i < interfaceFunctions.size(); i++)
|
||||
{
|
||||
g_interface = interfaceFunction();
|
||||
if (g_interface != nullptr)
|
||||
RenderInterfaceFunction* interfaceFunction = interfaceFunctions[i];
|
||||
|
||||
#ifdef UNLEASHED_RECOMP_D3D12
|
||||
// Wrap the device creation in __try/__except to survive from driver crashes.
|
||||
__try
|
||||
#endif
|
||||
{
|
||||
g_interface = interfaceFunction();
|
||||
if (g_interface == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
g_device = g_interface->createDevice(Config::GraphicsDevice);
|
||||
if (g_device != nullptr)
|
||||
{
|
||||
|
@ -1691,16 +1720,40 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver)
|
|||
#ifdef UNLEASHED_RECOMP_D3D12
|
||||
if (interfaceFunction == CreateD3D12Interface)
|
||||
{
|
||||
if (deviceDescription.vendor == RenderDeviceVendor::AMD)
|
||||
if (allowVulkanRedirection)
|
||||
{
|
||||
// AMD Drivers before this version have a known issue where MSAA resolve targets will fail to work correctly.
|
||||
// If no specific graphics API was selected, we silently destroy this one and move to the next option as it'll
|
||||
// just work incorrectly otherwise and result in visual glitches and 3D rendering not working in general.
|
||||
constexpr uint64_t MinimumAMDDriverVersion = 0x1F00005DC2005CULL; // 31.0.24002.92
|
||||
if ((Config::GraphicsAPI == EGraphicsAPI::Auto) && (deviceDescription.driverVersion < MinimumAMDDriverVersion))
|
||||
bool redirectToVulkan = false;
|
||||
|
||||
if (deviceDescription.vendor == RenderDeviceVendor::AMD)
|
||||
{
|
||||
// AMD Drivers before this version have a known issue where MSAA resolve targets will fail to work correctly.
|
||||
// If no specific graphics API was selected, we silently destroy this one and move to the next option as it'll
|
||||
// just work incorrectly otherwise and result in visual glitches and 3D rendering not working in general.
|
||||
constexpr uint64_t MinimumAMDDriverVersion = 0x1F00005DC2005CULL; // 31.0.24002.92
|
||||
if ((Config::GraphicsAPI == EGraphicsAPI::Auto) && (deviceDescription.driverVersion < MinimumAMDDriverVersion))
|
||||
redirectToVulkan = true;
|
||||
}
|
||||
else if (deviceDescription.vendor == RenderDeviceVendor::INTEL)
|
||||
{
|
||||
// Intel drivers on D3D12 are extremely buggy, introducing various graphical glitches.
|
||||
// We will redirect users to Vulkan until a workaround can be found.
|
||||
if (Config::GraphicsAPI == EGraphicsAPI::Auto)
|
||||
redirectToVulkan = true;
|
||||
}
|
||||
|
||||
if (redirectToVulkan)
|
||||
{
|
||||
g_device.reset();
|
||||
g_interface.reset();
|
||||
|
||||
// In case Vulkan fails to initialize, we will try D3D12 again afterwards,
|
||||
// just to get the game to boot. This only really happens in very old Intel GPU drivers.
|
||||
if (!g_vulkan)
|
||||
{
|
||||
interfaceFunctions.push_back(CreateD3D12Interface);
|
||||
allowVulkanRedirection = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1719,6 +1772,22 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver)
|
|||
break;
|
||||
}
|
||||
}
|
||||
#ifdef UNLEASHED_RECOMP_D3D12
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
if (graphicsApiRetry)
|
||||
{
|
||||
// If we were retrying, and this also failed, then we'll show the user neither of the graphics APIs succeeded.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is the first crash we ran into, reboot and try the other graphics API.
|
||||
os::process::StartProcess(os::process::GetExecutablePath(), { "--graphics-api-retry" });
|
||||
std::_Exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (g_device == nullptr)
|
||||
|
@ -1726,6 +1795,14 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver)
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef UNLEASHED_RECOMP_D3D12
|
||||
if (graphicsApiRetry)
|
||||
{
|
||||
// If we managed to create a device after retrying it in a reboot, remember the one we picked.
|
||||
Config::GraphicsAPI = g_vulkan ? EGraphicsAPI::Vulkan : EGraphicsAPI::D3D12;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_capabilities = g_device->getCapabilities();
|
||||
|
||||
LoadEmbeddedResources();
|
||||
|
@ -2360,18 +2437,22 @@ static void DrawProfiler()
|
|||
|
||||
ImGui::NewLine();
|
||||
|
||||
O1HeapDiagnostics diagnostics, physicalDiagnostics;
|
||||
if (g_userHeap.heap != nullptr && g_userHeap.physicalHeap != nullptr)
|
||||
{
|
||||
std::lock_guard lock(g_userHeap.mutex);
|
||||
diagnostics = o1heapGetDiagnostics(g_userHeap.heap);
|
||||
O1HeapDiagnostics diagnostics, physicalDiagnostics;
|
||||
{
|
||||
std::lock_guard lock(g_userHeap.mutex);
|
||||
diagnostics = o1heapGetDiagnostics(g_userHeap.heap);
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(g_userHeap.physicalMutex);
|
||||
physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap);
|
||||
}
|
||||
|
||||
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
|
||||
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(g_userHeap.physicalMutex);
|
||||
physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap);
|
||||
}
|
||||
|
||||
ImGui::Text("Heap Allocated: %d MB", int32_t(diagnostics.allocated / (1024 * 1024)));
|
||||
ImGui::Text("Physical Heap Allocated: %d MB", int32_t(physicalDiagnostics.allocated / (1024 * 1024)));
|
||||
|
||||
ImGui::Text("GPU Waits: %d", int32_t(g_waitForGPUCount));
|
||||
ImGui::Text("Buffer Uploads: %d", int32_t(g_bufferUploadCount));
|
||||
ImGui::NewLine();
|
||||
|
@ -2710,6 +2791,8 @@ static std::atomic<bool> g_executedCommandList;
|
|||
|
||||
void Video::Present()
|
||||
{
|
||||
g_readyForCommands = false;
|
||||
|
||||
RenderCommand cmd;
|
||||
cmd.type = RenderCommandType::ExecutePendingStretchRectCommands;
|
||||
g_renderQueue.enqueue(cmd);
|
||||
|
|
|
@ -18,7 +18,7 @@ struct Video
|
|||
static inline uint32_t s_viewportWidth;
|
||||
static inline uint32_t s_viewportHeight;
|
||||
|
||||
static bool CreateHostDevice(const char *sdlVideoDriver);
|
||||
static bool CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry);
|
||||
static void WaitOnSwapChain();
|
||||
static void Present();
|
||||
static void StartPipelinePrecompilation();
|
||||
|
|
|
@ -233,7 +233,7 @@ CONFIG_DEFINE_ENUM_LOCALE(EControllerIcons)
|
|||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EControllerIcons::Auto, { "AUTO", "Auto : the game will determine which icons to use based on the current input device." } },
|
||||
{ EControllerIcons::Auto, { "AUTO", "Auto: the game will determine which icons to use based on the current input device." } },
|
||||
{ EControllerIcons::Xbox, { "XBOX", "" } },
|
||||
{ EControllerIcons::PlayStation, { "PLAYSTATION", "" } }
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ CONFIG_DEFINE_LOCALE(EffectsVolume)
|
|||
CONFIG_DEFINE_LOCALE(MusicAttenuation)
|
||||
{
|
||||
{ ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } },
|
||||
{ ELanguage::Japanese, { "BGM[減衰:げんすい]", "[外部:がいぶ]メディアを\u200B[再生:さいせい]すると\u200Bゲームの\u200B[音楽:おんがく]を\u200Bフェードアウトします" } },
|
||||
{ ELanguage::Japanese, { "BGM[減衰:げんすい]", "[外部:がいぶ]メディアを\u200B[再生:さいせい]すると\u200Bゲームの\u200B[音楽:おんがく]を\u200Bフェードアウト\u200Bします" } },
|
||||
{ ELanguage::German, { "Musikdämpfung", "Stelle die Musik des Spiels stumm während externe Medien abgespielt werden." } },
|
||||
{ ELanguage::French, { "Atténuation audio", "Abaisse le volume des musiques du jeu lorsqu'un média externe est en cours de lecture." } },
|
||||
{ ELanguage::Spanish, { "Atenuación de música", "Atenúa la música del juego cuando un reproductor multimedia se encuentra activo." } },
|
||||
|
@ -508,7 +508,7 @@ CONFIG_DEFINE_LOCALE(BattleTheme)
|
|||
CONFIG_DEFINE_LOCALE(WindowSize)
|
||||
{
|
||||
{ ELanguage::English, { "Window Size", "Adjust the size of the game window in windowed mode." } },
|
||||
{ ELanguage::Japanese, { "ウィンドウサイズ", "ウィンドウモードでの\u200Bゲームの\u200Bウィンドウサイズを\u200B[調整:ちょうせい]できます" } },
|
||||
{ ELanguage::Japanese, { "ウィンドウサイズ", "ウィンドウ\u200Bモードでの\u200Bゲームの\u200Bウィンドウサイズを\u200B[調整:ちょうせい]できます" } },
|
||||
{ ELanguage::German, { "Fenstergröße", "Ändere die Größe des Spielfensters im Fenstermodus." } },
|
||||
{ ELanguage::French, { "Taille de la fenêtre", "Modifie la taille de la fenêtre de jeu en mode fenêtré." } },
|
||||
{ ELanguage::Spanish, { "Tamaño de ventana", "Ajusta el tamaño de la ventana de juego." } },
|
||||
|
|
|
@ -366,7 +366,7 @@ std::unordered_map<std::string_view, std::unordered_map<ELanguage, std::string>>
|
|||
{ ELanguage::English, "Installation complete!\nThis project is brought to you by:" },
|
||||
{ ELanguage::Japanese, "インストール[完了:かんりょう]!\nプロジェクト[制作:せいさく]:" },
|
||||
{ ELanguage::German, "Installation abgeschlossen!\nDieses Projekt wird präsentiert von:" },
|
||||
{ ELanguage::French, "Installation terminée !\nCe projet vous est présenté par :" },
|
||||
{ ELanguage::French, "Installation terminée !\nCe projet vous est présenté\npar :" },
|
||||
{ ELanguage::Spanish, "¡Instalación completada!\nEste proyecto ha sido posible gracias a:" },
|
||||
{ ELanguage::Italian, "Installazione completata!\nQuesto progetto è stato creato da:" }
|
||||
}
|
||||
|
|
|
@ -208,6 +208,7 @@ int main(int argc, char *argv[])
|
|||
bool forceDLCInstaller = false;
|
||||
bool useDefaultWorkingDirectory = false;
|
||||
bool forceInstallationCheck = false;
|
||||
bool graphicsApiRetry = false;
|
||||
const char *sdlVideoDriver = nullptr;
|
||||
|
||||
for (uint32_t i = 1; i < argc; i++)
|
||||
|
@ -216,6 +217,7 @@ int main(int argc, char *argv[])
|
|||
forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0);
|
||||
useDefaultWorkingDirectory = useDefaultWorkingDirectory || (strcmp(argv[i], "--use-cwd") == 0);
|
||||
forceInstallationCheck = forceInstallationCheck || (strcmp(argv[i], "--install-check") == 0);
|
||||
graphicsApiRetry = graphicsApiRetry || (strcmp(argv[i], "--graphics-api-retry") == 0);
|
||||
|
||||
if (strcmp(argv[i], "--sdl-video-driver") == 0)
|
||||
{
|
||||
|
@ -234,9 +236,6 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
Config::Load();
|
||||
|
||||
if (!PersistentStorageManager::LoadBinary())
|
||||
LOGFN_ERROR("Failed to load persistent storage binary... (status code {})", (int)PersistentStorageManager::BinStatus);
|
||||
|
||||
if (forceInstallationCheck)
|
||||
{
|
||||
|
@ -326,7 +325,7 @@ int main(int argc, char *argv[])
|
|||
bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled;
|
||||
if (runInstallerWizard)
|
||||
{
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver))
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
|
||||
std::_Exit(1);
|
||||
|
@ -340,13 +339,16 @@ int main(int argc, char *argv[])
|
|||
|
||||
ModLoader::Init();
|
||||
|
||||
if (!PersistentStorageManager::LoadBinary())
|
||||
LOGFN_ERROR("Failed to load persistent storage binary... (status code {})", (int)PersistentStorageManager::BinStatus);
|
||||
|
||||
KiSystemStartup();
|
||||
|
||||
uint32_t entry = LdrLoadModule(modulePath);
|
||||
|
||||
if (!runInstallerWizard)
|
||||
{
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver))
|
||||
if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry))
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow);
|
||||
std::_Exit(1);
|
||||
|
|
|
@ -1650,3 +1650,39 @@ PPC_FUNC(sub_82E54950)
|
|||
__imp__sub_82E54950(ctx, base);
|
||||
}
|
||||
}
|
||||
|
||||
// Credits while Sonic is running are offseted by 133 pixels at 16:9.
|
||||
// We can make this dynamic by remembering the anchoring and shifting accordingly.
|
||||
void EndingTextAllocMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
r3.u32 += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static constexpr uint32_t ENDING_TEXT_SIZE = 0x164;
|
||||
|
||||
void EndingTextCtorRightMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
*reinterpret_cast<uint32_t*>(g_memory.base + r3.u32 + ENDING_TEXT_SIZE) = ALIGN_RIGHT;
|
||||
}
|
||||
|
||||
void EndingTextCtorLeftMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
*reinterpret_cast<uint32_t*>(g_memory.base + r3.u32 + ENDING_TEXT_SIZE) = ALIGN_LEFT;
|
||||
}
|
||||
|
||||
void EndingTextCtorCenterMidAsmHook(PPCRegister& r3)
|
||||
{
|
||||
*reinterpret_cast<uint32_t*>(g_memory.base + r3.u32 + ENDING_TEXT_SIZE) = ALIGN_CENTER;
|
||||
}
|
||||
|
||||
void EndingTextPositionMidAsmHook(PPCRegister& r31, PPCRegister& f13)
|
||||
{
|
||||
uint32_t align = *reinterpret_cast<uint32_t*>(g_memory.base + r31.u32 + ENDING_TEXT_SIZE);
|
||||
|
||||
// Since widescreen is always forced, 133 offset will always be part of the position.
|
||||
if (align == ALIGN_RIGHT)
|
||||
f13.f64 += -133.0 * (1.0 - g_aspectRatioNarrowScale);
|
||||
|
||||
else if (align == ALIGN_LEFT)
|
||||
f13.f64 += 133.0 * (1.0 - g_aspectRatioNarrowScale);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
VERSION_MILESTONE=""
|
||||
VERSION_MAJOR=1
|
||||
VERSION_MINOR=0
|
||||
VERSION_REVISION=2
|
||||
VERSION_REVISION=3
|
||||
|
|
|
@ -510,6 +510,11 @@ std::vector<std::string> Split(const char* strStart, const ImFont* font, float f
|
|||
if (*str == '\n')
|
||||
str++;
|
||||
|
||||
if (strncmp(str, "\u200B", 3) == 0)
|
||||
{
|
||||
str += 3;
|
||||
}
|
||||
|
||||
lineStart = str;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1106,3 +1106,38 @@ name = "UseAlternateTitleMidAsmHook"
|
|||
address = 0x82580F44
|
||||
jump_address_on_true = 0x82580F48
|
||||
jump_address_on_false = 0x82580FA0
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextAllocMidAsmHook"
|
||||
address = 0x8257E284
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextAllocMidAsmHook"
|
||||
address = 0x8257E45C
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextAllocMidAsmHook"
|
||||
address = 0x8257EDD8
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextCtorRightMidAsmHook"
|
||||
address = 0x8257E304
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextCtorLeftMidAsmHook"
|
||||
address = 0x8257E4DC
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextCtorCenterMidAsmHook"
|
||||
address = 0x8257EE40
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "EndingTextPositionMidAsmHook"
|
||||
address = 0x82580168
|
||||
registers = ["r31", "f13"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4897cf7ef2070120310c28a1a672b427d745dad8
|
||||
Subproject commit 56738e5893ed7c4dc108996590475c52726623e3
|
Loading…
Add table
Add a link
Reference in a new issue