diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index 7705f5c61..218dcaf0f 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -185,6 +185,10 @@ bool ReadbackLinearImagesEnabled() { return g_config->readback_linear_images; } +bool ReadbackTiledImagesEnabled() { + return g_config->readback_tiled_images; +} + bool SyncRawImageBuffersEnabled() { return g_config->sync_raw_image_buffers; } diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 4387dafc9..71d54de0b 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -85,6 +85,7 @@ struct ConfigOptions { bool gpu_assisted_validation_enabled = false; bool renderdoc_enabled = false; bool readback_linear_images = false; + bool readback_tiled_images = false; bool sync_raw_image_buffers = false; bool tessellation_enabled = false; bool trophy_enabled = true; @@ -150,6 +151,7 @@ bool GpuAssistedValidationEnabled(); bool RenderDocEnabled(); bool ReadbackLinearImagesEnabled(); +bool ReadbackTiledImagesEnabled(); bool SyncRawImageBuffersEnabled(); bool TessellationEnabled(); bool TrophyEnabled(); diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index dd62b80d6..6a95f07e4 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -34,6 +34,8 @@ namespace Libs::Graphics { namespace { constexpr uint64_t NumFramesBeforeRemoval = 32; +// Largest GPU-written tiled image (in texels) that is written back to guest memory on submit. +constexpr uint64_t SmallTiledReadbackTexels = 64 * 64; [[nodiscard]] bool DecodeColorClear(const TextureCache::ImageDesc& desc, uint8_t code, vk::ClearColorValue& clear) { @@ -122,7 +124,8 @@ TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler m_blit_helper(graphics, scheduler), m_tiler(graphics, scheduler, buffer_cache.GetUtilityBuffer(MemoryUsage::Stream)), m_buffer_cache(buffer_cache), - m_readback_linear_images(Config::ReadbackLinearImagesEnabled()) { + m_readback_linear_images(Config::ReadbackLinearImagesEnabled()), + m_readback_tiled_images(Config::ReadbackTiledImagesEnabled()) { if (m_graphics.CanReportMemoryUsage()) { constexpr int64_t GiB = 1024ll * 1024 * 1024; const auto budget = @@ -401,7 +404,14 @@ void TextureCache::UntrackImageTail(ImageId id) { } void TextureCache::TrackImageDownload(ImageId id, Image& image) { - if (m_readback_linear_images && !image.info.IsTiled()) { + // Small tiled targets are written back too: games read GPU results such as reductions and + // simulation state from them on the CPU (PPSA04263 keeps the origin of its ocean wave grid + // in a 1x1 R32G32 target, and its water physics read it to sample the wave heights). + const auto& info = image.info; + const auto texels = static_cast(info.extent.width) * info.extent.height * + info.extent.depth * info.resources.layers; + if (info.IsTiled() ? m_readback_tiled_images && texels <= SmallTiledReadbackTexels + : m_readback_linear_images) { if (!image.IsGpuModified()) { EXIT("TextureCache: cannot enroll a non-GPU-owned image for download\n"); } @@ -1956,7 +1966,7 @@ void TextureCache::RunGarbageCollector() { } void TextureCache::ProcessDownloadImages() { - if (!m_readback_linear_images) { + if (!m_readback_linear_images && !m_readback_tiled_images) { return; } std::scoped_lock lock {m_lock}; diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.h b/src/graphics/host_gpu/renderer/cache/textureCache.h index ebf3b6b7b..203fc0132 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.h +++ b/src/graphics/host_gpu/renderer/cache/textureCache.h @@ -180,6 +180,7 @@ private: uint64_t m_gc_tick = 0; mutable uint32_t m_image_query_epoch = 0; bool m_readback_linear_images = false; + bool m_readback_tiled_images = false; friend struct TextureCacheTestAccess; friend class BufferCache; diff --git a/src/launcher/forms/configuration_edit_dialog.ui b/src/launcher/forms/configuration_edit_dialog.ui index 7d869632c..9845e149d 100644 --- a/src/launcher/forms/configuration_edit_dialog.ui +++ b/src/launcher/forms/configuration_edit_dialog.ui @@ -168,12 +168,18 @@ + + Read back small writable tiled images + Enable tiled readback + + + Draw tessellated geometry. Disabled by default; unsupported tessellation may cause crashes. Enable tessellation support (experimental) - + Enable HDR10 output for supported games. Requires HDR enabled on the host display. Enable HDR display support diff --git a/src/launcher/include/configuration.h b/src/launcher/include/configuration.h index 8a3a820d1..bb2560a3d 100644 --- a/src/launcher/include/configuration.h +++ b/src/launcher/include/configuration.h @@ -126,6 +126,7 @@ public: bool hdr_enabled = false; bool hide_cursor_enabled = false; bool readback_linear_images = false; + bool readback_tiled_images = false; bool sync_raw_image_buffers = false; bool tessellation_enabled = false; bool trophy_enabled = true; @@ -165,6 +166,7 @@ public: hdr_enabled = other.hdr_enabled; hide_cursor_enabled = other.hide_cursor_enabled; readback_linear_images = other.readback_linear_images; + readback_tiled_images = other.readback_tiled_images; sync_raw_image_buffers = other.sync_raw_image_buffers; tessellation_enabled = other.tessellation_enabled; trophy_enabled = other.trophy_enabled; diff --git a/src/launcher/src/configuration.cpp b/src/launcher/src/configuration.cpp index a6e63dfba..e64f9ba76 100644 --- a/src/launcher/src/configuration.cpp +++ b/src/launcher/src/configuration.cpp @@ -13,6 +13,7 @@ QVariantMap Configuration::GameSettings() const { KYTY_CFG_SET(hdr_enabled); KYTY_CFG_SET(hide_cursor_enabled); KYTY_CFG_SET(readback_linear_images); + KYTY_CFG_SET(readback_tiled_images); KYTY_CFG_SET(sync_raw_image_buffers); KYTY_CFG_SET(tessellation_enabled); KYTY_CFG_SET(trophy_enabled); @@ -62,6 +63,7 @@ void Configuration::ReadGameSettingsValues(const Settings& s) { KYTY_CFG_GET(hdr_enabled); KYTY_CFG_GET(hide_cursor_enabled); KYTY_CFG_GET(readback_linear_images); + KYTY_CFG_GET(readback_tiled_images); KYTY_CFG_GET(sync_raw_image_buffers); KYTY_CFG_GET(tessellation_enabled); trophy_enabled = s.value("trophy_enabled", trophy_enabled).toBool(); diff --git a/src/launcher/src/configurationEditDialog.cpp b/src/launcher/src/configurationEditDialog.cpp index dcfcdfeb5..fa8caeab0 100644 --- a/src/launcher/src/configurationEditDialog.cpp +++ b/src/launcher/src/configurationEditDialog.cpp @@ -286,6 +286,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) { m_ui->checkBox_hdr->setChecked(info.hdr_enabled); m_ui->checkBox_hide_cursor->setChecked(info.hide_cursor_enabled); m_ui->checkBox_readback->setChecked(info.readback_linear_images); + m_ui->checkBox_readback_tiled->setChecked(info.readback_tiled_images); m_ui->checkBox_sync_raw_image_buffers->setChecked(info.sync_raw_image_buffers); m_ui->checkBox_tessellation->setChecked(info.tessellation_enabled); m_ui->checkBox_skip_notice_screen->setChecked(info.skip_notice_screen); @@ -479,6 +480,7 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui, boo info.hdr_enabled = ui.checkBox_hdr->isChecked(); info.hide_cursor_enabled = ui.checkBox_hide_cursor->isChecked(); info.readback_linear_images = ui.checkBox_readback->isChecked(); + info.readback_tiled_images = ui.checkBox_readback_tiled->isChecked(); info.sync_raw_image_buffers = ui.checkBox_sync_raw_image_buffers->isChecked(); info.tessellation_enabled = ui.checkBox_tessellation->isChecked(); info.skip_notice_screen = ui.checkBox_skip_notice_screen->isChecked(); diff --git a/src/launcher/src/mainDialog.cpp b/src/launcher/src/mainDialog.cpp index 710929128..caf98c553 100644 --- a/src/launcher/src/mainDialog.cpp +++ b/src/launcher/src/mainDialog.cpp @@ -279,6 +279,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) { AddFlag(args, info.hdr_enabled, "--hdr"); AddFlag(args, info.hide_cursor_enabled, "--hide-cursor"); AddFlag(args, info.readback_linear_images, "--readback-linear-images"); + AddFlag(args, info.readback_tiled_images, "--readback-tiled-images"); AddFlag(args, info.sync_raw_image_buffers, "--sync-raw-image-buffers"); AddFlag(args, !info.trophy_enabled, "--no-trophy-notifications"); args << "--trophy-duration" << QString::number(info.trophy_duration_seconds); diff --git a/src/main.cpp b/src/main.cpp index 7ebd62517..451a1eb89 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,7 @@ static void PrintUsage() { ::printf(" --profile Enable the Tracy profiler.\n"); ::printf(" --spirv-debug-printf Enable SPIR-V debug printf.\n"); ::printf(" --readback-linear-images Read back writable linear images on submit.\n"); + ::printf(" --readback-tiled-images Read back small writable tiled images on submit.\n"); ::printf(" --sync-raw-image-buffers Synchronize raw reads of GPU images.\n"); ::printf(" --no-trophy-notifications Disable trophy unlock toasts and their sound.\n"); ::printf( @@ -206,6 +207,7 @@ static constexpr Flag FLAGS[] = { {"--profile", &ConfigOptions::profiler_enabled}, {"--spirv-debug-printf", &ConfigOptions::spirv_debug_printf_enabled}, {"--readback-linear-images", &ConfigOptions::readback_linear_images}, + {"--readback-tiled-images", &ConfigOptions::readback_tiled_images}, {"--sync-raw-image-buffers", &ConfigOptions::sync_raw_image_buffers}, {"--no-trophy-notifications", &ConfigOptions::trophy_enabled, false}, {"--perf-overlay", &ConfigOptions::perf_overlay_enabled},