mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Custom textures rewrite (#6452)
* common: Add thread pool from yuzu * Is really useful for asynchronous operations like shader compilation and custom textures, will be used in following PRs * core: Improve ImageInterface * Provide a default implementation so frontends don't have to duplicate code registering the lodepng version * Add a dds version too which we will use in the next commit * rasterizer_cache: Rewrite custom textures * There's just too much to talk about here, look at the PR description for more details * rasterizer_cache: Implement basic pack configuration file * custom_tex_manager: Flip dumped textures * custom_tex_manager: Optimize custom texture hashing * If no convertions are needed then we can hash the decoded data directly removing the needed for duplicate decode * custom_tex_manager: Implement asynchronous texture loading * The file loading and decoding is offloaded into worker threads, while the upload itself still occurs in the main thread to avoid having to manage shared contexts * Address review comments * custom_tex_manager: Introduce custom material support * video_core: Move custom textures to separate directory * Also split the files to make the code cleaner * gl_texture_runtime: Generate mipmaps for material * custom_tex_manager: Prevent memory overflow when preloading * externals: Add dds-ktx as submodule * string_util: Return vector from SplitString * No code benefits from passing it as an argument * custom_textures: Use json config file * gl_rasterizer: Only bind material for unit 0 * Address review comments
This commit is contained in:
		
							parent
							
								
									d16dce6d99
								
							
						
					
					
						commit
						06f3c90cfb
					
				
					 87 changed files with 2154 additions and 544 deletions
				
			
		|  | @ -56,7 +56,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config: | |||
| // This must be in alphabetical order according to action name as it must have the same order as
 | ||||
| // UISetting::values.shortcuts, which is alphabetically ordered.
 | ||||
| // clang-format off
 | ||||
| const std::array<UISettings::Shortcut, 27> Config::default_hotkeys {{ | ||||
| const std::array<UISettings::Shortcut, 28> Config::default_hotkeys {{ | ||||
|      {QStringLiteral("Advance Frame"),            QStringLiteral("Main Window"), {QStringLiteral(""),     Qt::ApplicationShortcut}}, | ||||
|      {QStringLiteral("Capture Screenshot"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, | ||||
|      {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"),     Qt::WindowShortcut}}, | ||||
|  | @ -84,6 +84,7 @@ const std::array<UISettings::Shortcut, 27> Config::default_hotkeys {{ | |||
|      {QStringLiteral("Toggle Screen Layout"),     QStringLiteral("Main Window"), {QStringLiteral("F10"),    Qt::WindowShortcut}}, | ||||
|      {QStringLiteral("Toggle Status Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, | ||||
|      {QStringLiteral("Toggle Texture Dumping"),   QStringLiteral("Main Window"), {QStringLiteral(""),       Qt::ApplicationShortcut}}, | ||||
|      {QStringLiteral("Toggle Custom Textures"),   QStringLiteral("Main Window"), {QStringLiteral("F7"),     Qt::ApplicationShortcut}}, | ||||
|     }}; | ||||
| // clang-format on
 | ||||
| 
 | ||||
|  | @ -439,6 +440,7 @@ void Config::ReadUtilityValues() { | |||
|     ReadGlobalSetting(Settings::values.dump_textures); | ||||
|     ReadGlobalSetting(Settings::values.custom_textures); | ||||
|     ReadGlobalSetting(Settings::values.preload_textures); | ||||
|     ReadGlobalSetting(Settings::values.async_custom_loading); | ||||
| 
 | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
|  | @ -949,6 +951,7 @@ void Config::SaveUtilityValues() { | |||
|     WriteGlobalSetting(Settings::values.dump_textures); | ||||
|     WriteGlobalSetting(Settings::values.custom_textures); | ||||
|     WriteGlobalSetting(Settings::values.preload_textures); | ||||
|     WriteGlobalSetting(Settings::values.async_custom_loading); | ||||
| 
 | ||||
|     qt_config->endGroup(); | ||||
| } | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ public: | |||
| 
 | ||||
|     static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | ||||
|     static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | ||||
|     static const std::array<UISettings::Shortcut, 27> default_hotkeys; | ||||
|     static const std::array<UISettings::Shortcut, 28> default_hotkeys; | ||||
| 
 | ||||
| private: | ||||
|     void Initialize(const std::string& config_name); | ||||
|  |  | |||
|  | @ -41,8 +41,10 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) | |||
|     }); | ||||
| 
 | ||||
|     ui->toggle_preload_textures->setEnabled(ui->toggle_custom_textures->isChecked()); | ||||
|     ui->toggle_async_custom_loading->setEnabled(ui->toggle_custom_textures->isChecked()); | ||||
|     connect(ui->toggle_custom_textures, &QCheckBox::toggled, this, [this] { | ||||
|         ui->toggle_preload_textures->setEnabled(ui->toggle_custom_textures->isChecked()); | ||||
|         ui->toggle_async_custom_loading->setEnabled(ui->toggle_custom_textures->isChecked()); | ||||
|         if (!ui->toggle_preload_textures->isEnabled()) | ||||
|             ui->toggle_preload_textures->setChecked(false); | ||||
|     }); | ||||
|  | @ -83,6 +85,7 @@ void ConfigureEnhancements::SetConfiguration() { | |||
|     ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue()); | ||||
|     ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue()); | ||||
|     ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue()); | ||||
|     ui->toggle_async_custom_loading->setChecked(Settings::values.async_custom_loading.GetValue()); | ||||
|     bg_color = | ||||
|         QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), | ||||
|                          Settings::values.bg_blue.GetValue()); | ||||
|  | @ -159,6 +162,8 @@ void ConfigureEnhancements::ApplyConfiguration() { | |||
|                                              ui->toggle_custom_textures, custom_textures); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.preload_textures, | ||||
|                                              ui->toggle_preload_textures, preload_textures); | ||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_custom_loading, | ||||
|                                              ui->toggle_async_custom_loading, async_custom_loading); | ||||
| 
 | ||||
|     Settings::values.bg_red = static_cast<float>(bg_color.redF()); | ||||
|     Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | ||||
|  | @ -176,6 +181,8 @@ void ConfigureEnhancements::SetupPerGameUI() { | |||
|         ui->toggle_dump_textures->setEnabled(Settings::values.dump_textures.UsingGlobal()); | ||||
|         ui->toggle_custom_textures->setEnabled(Settings::values.custom_textures.UsingGlobal()); | ||||
|         ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal()); | ||||
|         ui->toggle_async_custom_loading->setEnabled( | ||||
|             Settings::values.async_custom_loading.UsingGlobal()); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -195,6 +202,9 @@ void ConfigureEnhancements::SetupPerGameUI() { | |||
|                                             Settings::values.custom_textures, custom_textures); | ||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_preload_textures, | ||||
|                                             Settings::values.preload_textures, preload_textures); | ||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_async_custom_loading, | ||||
|                                             Settings::values.async_custom_loading, | ||||
|                                             async_custom_loading); | ||||
| 
 | ||||
|     ConfigurationShared::SetColoredComboBox( | ||||
|         ui->resolution_factor_combobox, ui->widget_resolution, | ||||
|  |  | |||
|  | @ -44,5 +44,6 @@ private: | |||
|     ConfigurationShared::CheckState dump_textures; | ||||
|     ConfigurationShared::CheckState custom_textures; | ||||
|     ConfigurationShared::CheckState preload_textures; | ||||
|     ConfigurationShared::CheckState async_custom_loading; | ||||
|     QColor bg_color; | ||||
| }; | ||||
|  |  | |||
|  | @ -494,6 +494,16 @@ | |||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QCheckBox" name="toggle_async_custom_loading"> | ||||
|         <property name="toolTip"> | ||||
|          <string><html><head/><body><p>Load custom textures asynchronously with background threads to reduce loading stutter</p></body></html></string> | ||||
|         </property> | ||||
|         <property name="text"> | ||||
|          <string>Async Custom Texture Loading</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|  |  | |||
|  | @ -206,13 +206,11 @@ void OptionSetDialog::SetCheckBoxDefaults(const std::string& initial_value) { | |||
|         } | ||||
|     } else { | ||||
|         // This is a combination of constants, splitted with + or |
 | ||||
|         std::vector<std::string> tmp; | ||||
|         Common::SplitString(initial_value, '+', tmp); | ||||
|         const auto tmp = Common::SplitString(initial_value, '+'); | ||||
| 
 | ||||
|         std::vector<std::string> out; | ||||
|         std::vector<std::string> tmp2; | ||||
|         for (const auto& str : tmp) { | ||||
|             Common::SplitString(str, '|', tmp2); | ||||
|             const auto tmp2 = Common::SplitString(str, '|'); | ||||
|             out.insert(out.end(), tmp2.begin(), tmp2.end()); | ||||
|         } | ||||
|         for (int i = 0; i < ui->checkBoxLayout->count(); ++i) { | ||||
|  |  | |||
|  | @ -2,13 +2,13 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QKeySequence> | ||||
| #include <QShortcut> | ||||
| #include <QtGlobal> | ||||
| #include "citra_qt/hotkeys.h" | ||||
| #include "citra_qt/uisettings.h" | ||||
| 
 | ||||
| HotkeyRegistry::HotkeyRegistry() = default; | ||||
| 
 | ||||
| HotkeyRegistry::~HotkeyRegistry() = default; | ||||
| 
 | ||||
| void HotkeyRegistry::SaveHotkeys() { | ||||
|  |  | |||
|  | @ -5,11 +5,13 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <QKeySequence> | ||||
| #include <QString> | ||||
| 
 | ||||
| class QDialog; | ||||
| class QKeySequence; | ||||
| class QSettings; | ||||
| class QShortcut; | ||||
| class QWidget; | ||||
| 
 | ||||
| class HotkeyRegistry final { | ||||
| public: | ||||
|  |  | |||
|  | @ -578,6 +578,8 @@ void GMainWindow::InitializeHotkeys() { | |||
|     }); | ||||
|     connect_shortcut(QStringLiteral("Toggle Texture Dumping"), | ||||
|                      [&] { Settings::values.dump_textures = !Settings::values.dump_textures; }); | ||||
|     connect_shortcut(QStringLiteral("Toggle Custom Textures"), | ||||
|                      [&] { Settings::values.custom_textures = !Settings::values.custom_textures; }); | ||||
|     // We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes
 | ||||
|     // the variable hold a garbage value after this function exits
 | ||||
|     static constexpr u16 SPEED_LIMIT_STEP = 5; | ||||
|  |  | |||
|  | @ -8,11 +8,10 @@ | |||
| #include "common/logging/log.h" | ||||
| 
 | ||||
| bool QtImageInterface::DecodePNG(std::vector<u8>& dst, u32& width, u32& height, | ||||
|                                  const std::string& path) { | ||||
|     QImage image(QString::fromStdString(path)); | ||||
| 
 | ||||
|                                  std::span<const u8> src) { | ||||
|     QImage image(QImage::fromData(src.data(), static_cast<int>(src.size()))); | ||||
|     if (image.isNull()) { | ||||
|         LOG_ERROR(Frontend, "Failed to open {} for decoding", path); | ||||
|         LOG_ERROR(Frontend, "Failed to decode png because image is null"); | ||||
|         return false; | ||||
|     } | ||||
|     width = image.width(); | ||||
|  | @ -21,13 +20,15 @@ bool QtImageInterface::DecodePNG(std::vector<u8>& dst, u32& width, u32& height, | |||
|     image = image.convertToFormat(QImage::Format_RGBA8888); | ||||
| 
 | ||||
|     // Write RGBA8 to vector
 | ||||
|     dst = std::vector<u8>(image.constBits(), image.constBits() + (width * height * 4)); | ||||
|     const size_t image_size = width * height * 4; | ||||
|     dst.resize(image_size); | ||||
|     std::memcpy(dst.data(), image.constBits(), image_size); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool QtImageInterface::EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width, | ||||
|                                  u32 height) { | ||||
| bool QtImageInterface::EncodePNG(const std::string& path, u32 width, u32 height, | ||||
|                                  std::span<const u8> src) { | ||||
|     QImage image(src.data(), width, height, QImage::Format_RGBA8888); | ||||
| 
 | ||||
|     if (!image.save(QString::fromStdString(path), "PNG")) { | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 
 | ||||
| class QtImageInterface final : public Frontend::ImageInterface { | ||||
| public: | ||||
|     bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, const std::string& path) override; | ||||
|     bool EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width, | ||||
|                    u32 height) override; | ||||
|     bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, std::span<const u8> src) override; | ||||
|     bool EncodePNG(const std::string& path, u32 width, u32 height, | ||||
|                    std::span<const u8> src) override; | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue