mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-04 05:04:33 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c669e38c8 | ||
|
|
1a607257fb | ||
|
|
14decb1667 | ||
|
|
0b7d3ce2f6 | ||
|
|
016c08b9c3 | ||
|
|
0094344360 |
38
README.md
38
README.md
@@ -76,18 +76,35 @@ For x86 machines (most systems), you will need a CPU that supports the SSE4.1 in
|
||||
|
||||
The main releases page is limited to the last 30 releases due to automatic updater limitations. Older releases can be downloaded from https://github.com/duckstation/old-releases/releases.
|
||||
|
||||
### Update Channels
|
||||
|
||||
The automatic updater in DuckStation has two channels: "Stable" and "Preview".
|
||||
- "Stable Releases": Less frequent updates, and tracks the "latest" release on GitHub. Releases in this channel have had more testing.
|
||||
- "Preview Releases": Built whenever a commit is pushed to the repository, and tracks the pre-release on GitHub. This channel contains builds which have had minimal testing, and may contain bugs or issues.
|
||||
|
||||
By default, the updater will track the channel you downloaded from. You can change the channel in `Settings -> Interface -> Updates`.
|
||||
|
||||
### Windows
|
||||
|
||||
DuckStation **requires** Windows 10/11, specifically version 1809 or newer. If you are still using Windows 7/8/8.1, DuckStation **will not run** on your operating system. Running these operating systems in 2023 should be considered a security risk, and I would recommend updating to something which receives vendor support.
|
||||
If you must use an older operating system, [v0.1-5624](https://github.com/duckstation/old-releases/releases/tag/v0.1-5624) is the last version which will run. But do not expect to recieve any assistance, these builds are no longer supported.
|
||||
DuckStation **requires** Windows 10/11, specifically version 1809 or newer. If you are still using Windows 7/8/8.1, DuckStation **will not run** on your operating system. Running these operating systems in 2026 should be considered a security risk, and I would recommend updating to something which receives vendor support.
|
||||
If you must use an older operating system, [v0.1-5624](https://github.com/duckstation/old-releases/releases/tag/v0.1-5624) is the last version which will run. But do not expect to receive any assistance, these builds are no longer supported.
|
||||
|
||||
To download:
|
||||
Windows builds are provided in two formats:
|
||||
- **Installer (.exe, recommended):** An installer which extracts DuckStation to your user-local programs directory, and optionally creates Start Menu/Desktop shortcuts.
|
||||
- **Archive (.zip):** A zip archive containing the prebuilt binary. Choose this option if you want a "portable" installation, or do not want to run an installer.
|
||||
|
||||
1. Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download the Windows x64 build. This is a zip archive containing the prebuilt binary. If you have an ARM64 Windows machine such as Snapdragon, download the Windows ARM64 build.
|
||||
2. Alternatively, direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip
|
||||
3. Extract the archive **to a subdirectory**. The archive has no root subdirectory, so extracting to the current directory will drop a bunch of files in your download directory if you do not extract to a subdirectory.
|
||||
To use the installer, simply download the installer from the releases page, run it, and follow the prompts.
|
||||
- Direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-installer.exe
|
||||
- ARM64 download link (Snapdragon laptops): https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-arm64-installer.exe
|
||||
- Legacy SSE2 installer (for pre-2008 CPUs): https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-sse2-installer.exe
|
||||
|
||||
Once downloaded and extracted, you can launch the emulator with `duckstation-qt-x64-ReleaseLTCG.exe`. Follow the Setup Wizard to get started.
|
||||
The installer is still a new addition, so if you encounter issues please let us know via Discord.
|
||||
|
||||
To use the archive or portable installation, follow these steps:
|
||||
1. Download https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip. If you have an ARM64 Windows machine such as Snapdragon, download `duckstation-windows-arm64-release.zip` instead.
|
||||
2. Extract the archive **to a subdirectory**. The archive has no root subdirectory, so extracting to the current directory will drop a bunch of files in your download directory if you do not extract to a subdirectory.
|
||||
3. If you want a portable installation (see [User Directories](#user-directories)), create an empty file named `portable.txt` in the same directory as the executable.
|
||||
4. Once downloaded and extracted, you can launch the emulator with `duckstation-qt-x64-ReleaseLTCG.exe`. Follow the Setup Wizard to get started.
|
||||
|
||||
**If you get an error about `vcruntime140_1.dll` being missing, you will need to update your Visual C++ runtime.** You can do that from this page: https://support.microsoft.com/en-au/help/2977003/the-latest-supported-visual-c-downloads. Specifically, you want the x64 runtime, which can be downloaded from https://aka.ms/vs/17/release/vc_redist.x64.exe.
|
||||
|
||||
@@ -99,8 +116,9 @@ DuckStation is provided for x86_64/ARM32/ARM64 Linux in AppImage formats.
|
||||
|
||||
The AppImages require a distribution equivalent to Ubuntu 22.04 or newer to run.
|
||||
|
||||
1. Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download `duckstation-x64.AppImage`.
|
||||
2. Run `chmod a+x` on the downloaded AppImage -- following this step, the AppImage can be run like a typical executable.
|
||||
1. Download https://github.com/stenzek/duckstation/releases/download/latest/DuckStation-x64.AppImage. If you have an ARM64 Linux machine, you should download `DuckStation-arm64.AppImage`.
|
||||
2. Run `chmod a+x` on the downloaded AppImage -- following this step, the AppImage can be run like a typical executable. Alternatively, in your file manager of choice, enable execute permissions via the file properties dialog.
|
||||
3. When running the AppImage for the first time, it will prompt to create a launcher shortcut.
|
||||
|
||||
If you were previously using the Flatpak package, to migrate your data from the Flatpak to the AppImage, you can run the following command:
|
||||
```bash
|
||||
@@ -117,7 +135,7 @@ macOS Ventura (13.3) is required, as this is also the minimum requirement for Qt
|
||||
|
||||
To download:
|
||||
|
||||
1. Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download `duckstation-mac-release.zip`.
|
||||
1. Download https://github.com/stenzek/duckstation/releases/download/latest/duckstation-mac-release.zip.
|
||||
2. Extract the zip by double-clicking it.
|
||||
3. Open `DuckStation.app`, optionally moving it to your desired location first.
|
||||
|
||||
|
||||
@@ -2766,6 +2766,16 @@ void GPU_HW::DrawLine(const GPUBackendDrawCommand* cmd, const GSVector4 bounds,
|
||||
|
||||
void GPU_HW::DrawSprite(const GPUBackendDrawRectangleCommand* cmd)
|
||||
{
|
||||
const GSVector2i pos = GSVector2i::load<true>(&cmd->x);
|
||||
const GSVector2i size = GSVector2i::load<true>(&cmd->width).u16to32();
|
||||
const GSVector4i rect = GSVector4i::xyxy(pos, pos.add32(size));
|
||||
const GSVector4i clamped_rect = m_clamped_drawing_area.rintersect(rect);
|
||||
if (clamped_rect.rempty())
|
||||
{
|
||||
GL_INS_FMT("Culling off-screen sprite {}", rect);
|
||||
return;
|
||||
}
|
||||
|
||||
// Treat non-textured sprite draws as fills, so we don't break the TC on framebuffer clears.
|
||||
bool draw_with_software_renderer = m_draw_with_software_renderer;
|
||||
if (m_use_texture_cache && !cmd->transparency_enable && !cmd->shading_enable && !cmd->texture_enable &&
|
||||
@@ -2783,23 +2793,15 @@ void GPU_HW::DrawSprite(const GPUBackendDrawRectangleCommand* cmd)
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_INS_FMT("Treating non-textured sprite as VRAM fill at {},{} size {}x{}", cmd->x, cmd->y, cmd->width,
|
||||
cmd->height);
|
||||
FillVRAM(cmd->x, cmd->y, cmd->width, cmd->height, cmd->color, cmd->interlaced_rendering, cmd->active_line_lsb);
|
||||
const GSVector2i clamped_size = clamped_rect.rsize();
|
||||
GL_INS_FMT("Treating non-textured sprite as VRAM fill at {},{} size {}x{} (clamped {})", cmd->x, cmd->y,
|
||||
cmd->width, cmd->height, clamped_rect);
|
||||
FillVRAM(clamped_rect.left, clamped_rect.top, clamped_size.x, clamped_size.y, cmd->color,
|
||||
cmd->interlaced_rendering, cmd->active_line_lsb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const GSVector2i pos = GSVector2i::load<true>(&cmd->x);
|
||||
const GSVector2i size = GSVector2i::load<true>(&cmd->width).u16to32();
|
||||
const GSVector4i rect = GSVector4i::xyxy(pos, pos.add32(size));
|
||||
const GSVector4i clamped_rect = m_clamped_drawing_area.rintersect(rect);
|
||||
if (clamped_rect.rempty())
|
||||
{
|
||||
GL_INS_FMT("Culling off-screen sprite {}", rect);
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareDraw(cmd);
|
||||
SetBatchDepthBuffer(cmd, false);
|
||||
SetBatchSpriteMode(cmd, m_allow_sprite_mode);
|
||||
|
||||
@@ -2178,6 +2178,10 @@ void System::DestroySystem()
|
||||
FullscreenUI::OnSystemDestroyed();
|
||||
|
||||
Host::OnSystemDestroyed();
|
||||
|
||||
// Revert to global settings.
|
||||
UpdateGameSettingsLayer();
|
||||
ApplySettings(true);
|
||||
}
|
||||
|
||||
void System::AbnormalShutdown(const std::string_view reason)
|
||||
|
||||
@@ -72,7 +72,8 @@ static void WakeThread();
|
||||
static void WakeThreadIfSleeping();
|
||||
static bool SleepThread(bool allow_sleep);
|
||||
|
||||
static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool preserve_imgui_on_failure, Error* error);
|
||||
static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool start_fullscreen_ui,
|
||||
bool preserve_imgui_on_failure, Error* error);
|
||||
static void DestroyDeviceOnThread(bool preserve_imgui_state);
|
||||
static void ResizeRenderWindowOnThread(u32 width, u32 height, float scale, float refresh_rate);
|
||||
static void RecreateRenderWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen);
|
||||
@@ -80,8 +81,8 @@ static void RenderWindowResizedOnThread();
|
||||
static bool CheckExclusiveFullscreenOnThread();
|
||||
|
||||
static void ReconfigureOnThread(VideoThreadReconfigureCommand* cmd);
|
||||
static bool CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram, const GPUSettings* old_settings,
|
||||
Error* error);
|
||||
static bool CreateGPUBackendOnThread(bool hardware_renderer, bool upload_vram, const GPUSettings* old_settings,
|
||||
GPURenderer* out_renderer, Error* error);
|
||||
static void DestroyGPUBackendOnThread();
|
||||
static void DestroyGPUPresenterOnThread();
|
||||
|
||||
@@ -102,7 +103,8 @@ struct ALIGN_TO_CACHE_LINE State
|
||||
Threading::ThreadHandle thread_handle;
|
||||
Common::unique_aligned_ptr<u8[]> command_fifo_data;
|
||||
WindowInfo render_window_info;
|
||||
std::optional<GPURenderer> requested_renderer; // TODO: Non thread safe accessof this
|
||||
std::optional<GPURenderer> requested_renderer;
|
||||
bool requested_fullscreen_ui = false;
|
||||
bool use_thread = false;
|
||||
bool fullscreen_state = false;
|
||||
|
||||
@@ -118,7 +120,6 @@ struct ALIGN_TO_CACHE_LINE State
|
||||
u8 run_idle_reasons = 0;
|
||||
bool run_idle_flag = false;
|
||||
GPUVSyncMode requested_vsync = GPUVSyncMode::Disabled;
|
||||
bool requested_fullscreen_ui = false;
|
||||
std::string game_title;
|
||||
std::string game_serial;
|
||||
std::string game_path;
|
||||
@@ -540,21 +541,24 @@ bool VideoThread::Reconfigure(std::optional<GPURenderer> renderer, bool upload_v
|
||||
{
|
||||
INFO_LOG("Reconfiguring video thread.");
|
||||
|
||||
s_state.requested_renderer = renderer;
|
||||
s_state.fullscreen_state = fullscreen.value_or(s_state.fullscreen_state);
|
||||
const bool new_requested_fullscreen_ui = start_fullscreen_ui.value_or(s_state.requested_fullscreen_ui);
|
||||
const bool new_fullscreen_state = fullscreen.value_or(s_state.fullscreen_state);
|
||||
|
||||
GPURenderer created_renderer = GPURenderer::Count;
|
||||
VideoThreadReconfigureCommand::Result result = VideoThreadReconfigureCommand::Result::Failed;
|
||||
|
||||
bool result = false;
|
||||
VideoThreadReconfigureCommand* cmd =
|
||||
AllocateCommand<VideoThreadReconfigureCommand>(VideoThreadCommandType::Reconfigure);
|
||||
cmd->renderer = s_state.requested_renderer;
|
||||
cmd->fullscreen = s_state.fullscreen_state;
|
||||
cmd->start_fullscreen_ui = start_fullscreen_ui;
|
||||
cmd->renderer = renderer;
|
||||
cmd->fullscreen = new_fullscreen_state;
|
||||
cmd->start_fullscreen_ui = new_requested_fullscreen_ui;
|
||||
cmd->vsync_mode = System::GetEffectiveVSyncMode();
|
||||
cmd->present_skip_mode = System::GetEffectivePresentSkipMode();
|
||||
cmd->force_recreate_device = recreate_device;
|
||||
cmd->upload_vram = upload_vram;
|
||||
cmd->error_ptr = error;
|
||||
cmd->out_result = &result;
|
||||
cmd->out_created_renderer = &created_renderer;
|
||||
cmd->settings = g_settings;
|
||||
|
||||
if (!s_state.use_thread) [[unlikely]]
|
||||
@@ -562,7 +566,23 @@ bool VideoThread::Reconfigure(std::optional<GPURenderer> renderer, bool upload_v
|
||||
else
|
||||
PushCommandAndSync(cmd, false);
|
||||
|
||||
return result;
|
||||
// Update CPU thread state.
|
||||
if (result == VideoThreadReconfigureCommand::Result::FailedWithDeviceLoss)
|
||||
{
|
||||
s_state.requested_renderer.reset();
|
||||
s_state.requested_fullscreen_ui = false;
|
||||
s_state.fullscreen_state = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// But the renderer may not have been successfully switched. Keep our CPU thread state in sync.
|
||||
if (created_renderer == GPURenderer::Count)
|
||||
s_state.requested_renderer.reset();
|
||||
else
|
||||
s_state.requested_renderer = renderer;
|
||||
s_state.requested_fullscreen_ui = new_requested_fullscreen_ui;
|
||||
s_state.fullscreen_state = new_fullscreen_state;
|
||||
return (result == VideoThreadReconfigureCommand::Result::Success);
|
||||
}
|
||||
|
||||
bool VideoThread::StartFullscreenUI(bool fullscreen, Error* error)
|
||||
@@ -570,7 +590,7 @@ bool VideoThread::StartFullscreenUI(bool fullscreen, Error* error)
|
||||
// Don't need to reconfigure if we already have a system.
|
||||
if (System::IsValid())
|
||||
{
|
||||
RunOnThread([]() { s_state.requested_fullscreen_ui = true; });
|
||||
s_state.requested_fullscreen_ui = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -584,10 +604,10 @@ bool VideoThread::IsFullscreenUIRequested()
|
||||
|
||||
void VideoThread::StopFullscreenUI()
|
||||
{
|
||||
// Don't need to reconfigure if we already have a system.
|
||||
// shouldn't be changing this while we have a system
|
||||
if (System::IsValid())
|
||||
{
|
||||
RunOnThread([]() { s_state.requested_fullscreen_ui = false; });
|
||||
s_state.requested_fullscreen_ui = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -607,7 +627,6 @@ bool VideoThread::CreateGPUBackend(GPURenderer renderer, bool upload_vram, std::
|
||||
void VideoThread::DestroyGPUBackend()
|
||||
{
|
||||
Reconfigure(std::nullopt, false, std::nullopt, std::nullopt, false, nullptr);
|
||||
s_state.requested_renderer.reset();
|
||||
}
|
||||
|
||||
bool VideoThread::HasGPUBackend()
|
||||
@@ -626,7 +645,8 @@ bool VideoThread::IsFullscreen()
|
||||
return s_state.fullscreen_state;
|
||||
}
|
||||
|
||||
bool VideoThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool preserve_imgui_on_failure, Error* error)
|
||||
bool VideoThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool start_fullscreen_ui,
|
||||
bool preserve_imgui_on_failure, Error* error)
|
||||
{
|
||||
DebugAssert(!g_gpu_device);
|
||||
|
||||
@@ -731,7 +751,7 @@ bool VideoThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool pres
|
||||
static_cast<float>(sc->GetHeight()));
|
||||
}
|
||||
|
||||
if (s_state.requested_fullscreen_ui)
|
||||
if (start_fullscreen_ui)
|
||||
FullscreenUI::Initialize();
|
||||
|
||||
if (const GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain())
|
||||
@@ -780,8 +800,8 @@ void VideoThread::DestroyDeviceOnThread(bool preserve_imgui_state)
|
||||
s_state.render_window_info = WindowInfo();
|
||||
}
|
||||
|
||||
bool VideoThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram, const GPUSettings* old_settings,
|
||||
Error* error)
|
||||
bool VideoThread::CreateGPUBackendOnThread(bool hardware_renderer, bool upload_vram, const GPUSettings* old_settings,
|
||||
GPURenderer* out_renderer, Error* error)
|
||||
{
|
||||
Error local_error;
|
||||
|
||||
@@ -811,9 +831,7 @@ bool VideoThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vra
|
||||
ImGuiManager::UpdateDebugWindowConfig();
|
||||
#endif
|
||||
|
||||
const bool is_hardware = (renderer != GPURenderer::Software);
|
||||
|
||||
if (is_hardware)
|
||||
if (hardware_renderer)
|
||||
s_state.gpu_backend = GPUBackend::CreateHardwareBackend();
|
||||
else
|
||||
s_state.gpu_backend = GPUBackend::CreateSoftwareBackend();
|
||||
@@ -821,16 +839,19 @@ bool VideoThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vra
|
||||
bool okay = s_state.gpu_backend->Initialize(upload_vram, &local_error);
|
||||
if (!okay)
|
||||
{
|
||||
ERROR_LOG("Failed to create {} renderer: {}", Settings::GetRendererName(renderer), local_error.GetDescription());
|
||||
ERROR_LOG("Failed to create {} renderer: {}", hardware_renderer ? "hardware" : "software",
|
||||
local_error.GetDescription());
|
||||
|
||||
if (is_hardware && !System::IsStartupCancelled())
|
||||
if (hardware_renderer && !System::IsStartupCancelled())
|
||||
{
|
||||
Host::AddIconOSDMessage(
|
||||
OSDMessageType::Error, "GPUBackendCreationFailed", ICON_FA_PAINT_ROLLER,
|
||||
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to initialize {} renderer, falling back to software renderer."),
|
||||
Settings::GetRendererName(s_state.requested_renderer.value())));
|
||||
fmt::format(
|
||||
"{}\n{}",
|
||||
TRANSLATE_SV("OSDMessage", "Failed to initialize hardware renderer, falling back to software renderer."),
|
||||
local_error.GetDescription()));
|
||||
|
||||
s_state.requested_renderer = GPURenderer::Software;
|
||||
hardware_renderer = false;
|
||||
s_state.gpu_backend = GPUBackend::CreateSoftwareBackend();
|
||||
if (!s_state.gpu_backend->Initialize(upload_vram, &local_error))
|
||||
Panic("Failed to initialize fallback software renderer");
|
||||
@@ -844,6 +865,9 @@ bool VideoThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vra
|
||||
}
|
||||
}
|
||||
|
||||
*out_renderer =
|
||||
hardware_renderer ? Settings::GetRendererForRenderAPI(g_gpu_device->GetRenderAPI()) : GPURenderer::Software;
|
||||
|
||||
g_gpu_device->SetGPUTimingEnabled(g_gpu_settings.display_show_gpu_usage);
|
||||
s_state.gpu_backend->RestoreDeviceContext();
|
||||
SetRunIdleReason(RunIdleReason::NoGPUBackend, false);
|
||||
@@ -853,18 +877,19 @@ bool VideoThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vra
|
||||
void VideoThread::ReconfigureOnThread(VideoThreadReconfigureCommand* cmd)
|
||||
{
|
||||
// Store state.
|
||||
s_state.requested_fullscreen_ui = cmd->start_fullscreen_ui.value_or(s_state.requested_fullscreen_ui);
|
||||
s_state.requested_vsync = cmd->vsync_mode;
|
||||
VideoPresenter::SetPresentSkipMode(cmd->present_skip_mode);
|
||||
|
||||
// Are we shutting down everything?
|
||||
if (!cmd->renderer.has_value() && !s_state.requested_fullscreen_ui)
|
||||
if (!cmd->renderer.has_value() && !cmd->start_fullscreen_ui)
|
||||
{
|
||||
// Serial clear must be after backend destroy, otherwise textures won't dump.
|
||||
DestroyGPUBackendOnThread();
|
||||
DestroyGPUPresenterOnThread();
|
||||
DestroyDeviceOnThread(false);
|
||||
ClearGameInfoOnThread();
|
||||
*cmd->out_result = VideoThreadReconfigureCommand::Result::Success;
|
||||
*cmd->out_created_renderer = GPURenderer::Count;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -891,9 +916,10 @@ void VideoThread::ReconfigureOnThread(VideoThreadReconfigureCommand* cmd)
|
||||
// Device recreation?
|
||||
const RenderAPI current_api = g_gpu_device ? g_gpu_device->GetRenderAPI() : RenderAPI::None;
|
||||
const RenderAPI expected_api =
|
||||
(cmd->renderer.has_value() && cmd->renderer.value() == GPURenderer::Software && current_api != RenderAPI::None) ?
|
||||
(!cmd->force_recreate_device && current_api != RenderAPI::None &&
|
||||
(!cmd->renderer.has_value() || cmd->renderer.value() == GPURenderer::Software)) ?
|
||||
current_api :
|
||||
Settings::GetRenderAPIForRenderer(s_state.requested_renderer.value_or(g_gpu_settings.gpu_renderer));
|
||||
Settings::GetRenderAPIForRenderer(cmd->renderer.value_or(g_gpu_settings.gpu_renderer));
|
||||
if (cmd->force_recreate_device || !GPUDevice::IsSameRenderAPI(current_api, expected_api))
|
||||
{
|
||||
Timer timer;
|
||||
@@ -901,7 +927,8 @@ void VideoThread::ReconfigureOnThread(VideoThreadReconfigureCommand* cmd)
|
||||
DestroyDeviceOnThread(true);
|
||||
|
||||
Error local_error;
|
||||
if (!CreateDeviceOnThread(expected_api, cmd->fullscreen, current_api != RenderAPI::None, &local_error))
|
||||
if (!CreateDeviceOnThread(expected_api, cmd->fullscreen, cmd->start_fullscreen_ui, current_api != RenderAPI::None,
|
||||
&local_error))
|
||||
{
|
||||
Host::AddIconOSDMessage(
|
||||
OSDMessageType::Error, "DeviceSwitchFailed", ICON_FA_PAINT_ROLLER,
|
||||
@@ -909,59 +936,66 @@ void VideoThread::ReconfigureOnThread(VideoThreadReconfigureCommand* cmd)
|
||||
GPUDevice::RenderAPIToString(expected_api), GPUDevice::RenderAPIToString(current_api),
|
||||
local_error.GetDescription()));
|
||||
|
||||
Host::ReleaseRenderWindow();
|
||||
if (current_api == RenderAPI::None || !CreateDeviceOnThread(current_api, cmd->fullscreen, false, &local_error))
|
||||
if (current_api == RenderAPI::None ||
|
||||
!CreateDeviceOnThread(current_api, cmd->fullscreen, cmd->start_fullscreen_ui, false, &local_error))
|
||||
{
|
||||
if (cmd->error_ptr)
|
||||
*cmd->error_ptr = local_error;
|
||||
|
||||
*cmd->out_result = false;
|
||||
*cmd->out_result = VideoThreadReconfigureCommand::Result::FailedWithDeviceLoss;
|
||||
*cmd->out_created_renderer = GPURenderer::Count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG("GPU device recreated in {:.2f}ms", timer.GetTimeMilliseconds());
|
||||
INFO_LOG("GPU device created in {:.2f}ms", timer.GetTimeMilliseconds());
|
||||
}
|
||||
|
||||
// Full shutdown case handled above.
|
||||
Assert(cmd->renderer.has_value() || s_state.requested_fullscreen_ui);
|
||||
if (cmd->renderer.has_value())
|
||||
{
|
||||
Timer timer;
|
||||
|
||||
// Do we want a renderer?
|
||||
if (!(*cmd->out_result =
|
||||
CreateGPUBackendOnThread(cmd->renderer.value(), cmd->upload_vram, &old_settings, cmd->error_ptr)))
|
||||
if (CreateGPUBackendOnThread(cmd->renderer.value() != GPURenderer::Software, cmd->upload_vram, &old_settings,
|
||||
cmd->out_created_renderer, cmd->error_ptr))
|
||||
{
|
||||
*cmd->out_result = VideoThreadReconfigureCommand::Result::Success;
|
||||
INFO_LOG("GPU backend created in {:.2f}ms", timer.GetTimeMilliseconds());
|
||||
}
|
||||
else
|
||||
{
|
||||
// No renderer created.
|
||||
*cmd->out_result = VideoThreadReconfigureCommand::Result::Failed;
|
||||
*cmd->out_created_renderer = GPURenderer::Count;
|
||||
|
||||
// If we had a renderer, it means it was a switch, and we need to bail out the thread.
|
||||
if (had_renderer)
|
||||
{
|
||||
VideoThread::ReportFatalErrorAndShutdown("Failed to switch GPU backend.");
|
||||
*cmd->out_result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No point keeping the presenter around.
|
||||
DestroyGPUBackendOnThread();
|
||||
DestroyGPUPresenterOnThread();
|
||||
ClearGameInfoOnThread();
|
||||
|
||||
// Drop device if we're not running FSUI.
|
||||
if (!cmd->start_fullscreen_ui)
|
||||
DestroyDeviceOnThread(false);
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG("GPU device recreated in {:.2f}ms", timer.GetTimeMilliseconds());
|
||||
}
|
||||
else if (s_state.requested_fullscreen_ui)
|
||||
else
|
||||
{
|
||||
// s_state.requested_fullscreen_ui starts FullscreenUI in CreateDeviceOnThread().
|
||||
if (!(*cmd->out_result =
|
||||
g_gpu_device || CreateDeviceOnThread(expected_api, cmd->fullscreen, false, cmd->error_ptr)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Full shutdown case handled above. This is just for running FSUI.
|
||||
DebugAssert(cmd->start_fullscreen_ui);
|
||||
DebugAssert(g_gpu_device);
|
||||
|
||||
// Don't need to present game frames anymore.
|
||||
DestroyGPUPresenterOnThread();
|
||||
ClearGameInfoOnThread();
|
||||
|
||||
*cmd->out_result = VideoThreadReconfigureCommand::Result::Success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,13 +70,21 @@ struct VideoThreadCommand
|
||||
|
||||
struct VideoThreadReconfigureCommand : public VideoThreadCommand
|
||||
{
|
||||
enum class Result : u8
|
||||
{
|
||||
Success,
|
||||
Failed,
|
||||
FailedWithDeviceLoss,
|
||||
};
|
||||
|
||||
Error* error_ptr;
|
||||
bool* out_result;
|
||||
Result* out_result;
|
||||
GPURenderer* out_created_renderer;
|
||||
std::optional<GPURenderer> renderer;
|
||||
std::optional<bool> start_fullscreen_ui;
|
||||
GPUVSyncMode vsync_mode;
|
||||
PresentSkipMode present_skip_mode;
|
||||
bool fullscreen;
|
||||
bool start_fullscreen_ui;
|
||||
bool force_recreate_device;
|
||||
bool upload_vram;
|
||||
GPUSettings settings;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -367,8 +367,8 @@ void PostProcessing::Config::RemoveStage(SettingsInterface& si, const char* sect
|
||||
const u32 new_stage_count = stage_count - 1;
|
||||
si.ClearSection(GetStageConfigSection(section, new_stage_count));
|
||||
|
||||
// if game settings, wipe the field out so we can potentially remove the file
|
||||
if (&si != Core::GetBaseSettingsLayer())
|
||||
// if game settings and no stages left, wipe the field out so we can potentially remove the file
|
||||
if (&si != Core::GetBaseSettingsLayer() && new_stage_count == 0)
|
||||
si.DeleteValue(section, "StageCount");
|
||||
else
|
||||
si.SetUIntValue(section, "StageCount", new_stage_count);
|
||||
@@ -414,9 +414,9 @@ void PostProcessing::Config::ClearStages(SettingsInterface& si, const char* sect
|
||||
|
||||
// if game settings, wipe the field out so we can potentially remove the file
|
||||
if (&si != Core::GetBaseSettingsLayer())
|
||||
si.SetUIntValue(section, "StageCount", 0);
|
||||
else
|
||||
si.DeleteValue(section, "StageCount");
|
||||
else
|
||||
si.SetUIntValue(section, "StageCount", 0);
|
||||
}
|
||||
|
||||
PostProcessing::Chain::Chain(const char* section) : m_section(section)
|
||||
|
||||
Reference in New Issue
Block a user