diff --git a/src/citra/config.cpp b/src/citra/config.cpp index aaa5f5ea4..1b18f4536 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -165,6 +165,8 @@ void Config::ReadValues() { // Utility Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false); Settings::values.custom_textures = sdl2_config->GetBoolean("Utility", "custom_textures", false); + Settings::values.preload_textures = + sdl2_config->GetBoolean("Utility", "preload_textures", false); // Audio Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 04e7a14b8..2b6aa2147 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -186,6 +186,10 @@ dump_textures = # 0 (default): Off, 1: On custom_textures = +# Loads all custom textures into memory before booting. +# 0 (default): Off, 1: On +preload_textures = + [Audio] # Whether or not to enable DSP LLE # 0 (default): No, 1: Yes diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 5bfe2f993..85c17f5f1 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -17,8 +17,6 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) ui->setupUi(this); SetConfiguration(); - ui->layoutBox->setEnabled(!Settings::values.custom_layout); - ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked()); connect(ui->toggle_hw_renderer, &QCheckBox::toggled, ui->hw_renderer_group, &QWidget::setEnabled); @@ -36,21 +34,6 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) } }); #endif - - connect(ui->render_3d_combobox, - static_cast(&QComboBox::currentIndexChanged), this, - [this](int currentIndex) { - updateShaders(static_cast(currentIndex) == - Settings::StereoRenderOption::Anaglyph); - }); - - connect(ui->bg_button, &QPushButton::clicked, this, [this] { - const QColor new_bg_color = QColorDialog::getColor(bg_color); - if (!new_bg_color.isValid()) { - return; - } - UpdateBackgroundColorButton(new_bg_color); - }); } ConfigureGraphics::~ConfigureGraphics() = default; @@ -60,15 +43,6 @@ void ConfigureGraphics::SetConfiguration() { ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader); ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul); ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); - ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor); - ui->render_3d_combobox->setCurrentIndex(static_cast(Settings::values.render_3d)); - ui->factor_3d->setValue(Settings::values.factor_3d); - updateShaders(Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph); - ui->toggle_linear_filter->setChecked(Settings::values.filter_mode); - ui->layout_combobox->setCurrentIndex(static_cast(Settings::values.layout_option)); - ui->swap_screen->setChecked(Settings::values.swap_screen); - UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, - Settings::values.bg_blue)); } void ConfigureGraphics::ApplyConfiguration() { @@ -76,49 +50,6 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked(); Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked(); Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); - Settings::values.resolution_factor = - static_cast(ui->resolution_factor_combobox->currentIndex()); - Settings::values.render_3d = - static_cast(ui->render_3d_combobox->currentIndex()); - Settings::values.factor_3d = ui->factor_3d->value(); - Settings::values.pp_shader_name = - ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString(); - Settings::values.filter_mode = ui->toggle_linear_filter->isChecked(); - Settings::values.layout_option = - static_cast(ui->layout_combobox->currentIndex()); - Settings::values.swap_screen = ui->swap_screen->isChecked(); - Settings::values.dump_textures = ui->toggle_dump_textures->isChecked(); - Settings::values.custom_textures = ui->toggle_custom_textures->isChecked(); - Settings::values.bg_red = static_cast(bg_color.redF()); - Settings::values.bg_green = static_cast(bg_color.greenF()); - Settings::values.bg_blue = static_cast(bg_color.blueF()); -} - -void ConfigureGraphics::updateShaders(bool anaglyph) { - ui->shader_combobox->clear(); - - if (anaglyph) - ui->shader_combobox->addItem("dubois (builtin)"); - else - ui->shader_combobox->addItem("none (builtin)"); - - ui->shader_combobox->setCurrentIndex(0); - - for (const auto& shader : OpenGL::GetPostProcessingShaderList(anaglyph)) { - ui->shader_combobox->addItem(QString::fromStdString(shader)); - if (Settings::values.pp_shader_name == shader) - ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1); - } -} - -void ConfigureGraphics::UpdateBackgroundColorButton(const QColor& color) { - bg_color = color; - - QPixmap pixmap(ui->bg_button->size()); - pixmap.fill(bg_color); - - const QIcon color_icon(pixmap); - ui->bg_button->setIcon(color_icon); } void ConfigureGraphics::RetranslateUI() { diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index c95bbb800..c44dc454b 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -354,6 +354,16 @@ + + + + <html><head/><body><p>Load all custom textures into memory on boot, instead of loading them when the game requires them.</p></body></html> + + + Preload Custom Textures + + + diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 642d5f14a..8cac708b8 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -161,15 +161,16 @@ ConfigureInput::ConfigureInput(QWidget* parent) continue; button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); connect(button_map[button_id], &QPushButton::clicked, [=]() { - HandleClick(button_map[button_id], - [=](const Common::ParamPackage& params) { - buttons_param[button_id] = params; - // If the user closes the dialog, the changes are reverted in - // `GMainWindow::OnConfigure()` - ApplyConfiguration(); - Settings::SaveProfile(ui->profile->currentIndex()); - }, - InputCommon::Polling::DeviceType::Button); + HandleClick( + button_map[button_id], + [=](const Common::ParamPackage& params) { + buttons_param[button_id] = params; + // If the user closes the dialog, the changes are reverted in + // `GMainWindow::OnConfigure()` + ApplyConfiguration(); + Settings::SaveProfile(ui->profile->currentIndex()); + }, + InputCommon::Polling::DeviceType::Button); }); connect(button_map[button_id], &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { @@ -198,14 +199,15 @@ ConfigureInput::ConfigureInput(QWidget* parent) analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( Qt::CustomContextMenu); connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() { - HandleClick(analog_map_buttons[analog_id][sub_button_id], - [=](const Common::ParamPackage& params) { - SetAnalogButton(params, analogs_param[analog_id], - analog_sub_buttons[sub_button_id]); - ApplyConfiguration(); - Settings::SaveProfile(ui->profile->currentIndex()); - }, - InputCommon::Polling::DeviceType::Button); + HandleClick( + analog_map_buttons[analog_id][sub_button_id], + [=](const Common::ParamPackage& params) { + SetAnalogButton(params, analogs_param[analog_id], + analog_sub_buttons[sub_button_id]); + ApplyConfiguration(); + Settings::SaveProfile(ui->profile->currentIndex()); + }, + InputCommon::Polling::DeviceType::Button); }); connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index ba9706737..89ee75dd2 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -84,6 +84,8 @@ add_library(common STATIC swap.h telemetry.cpp telemetry.h + texture.cpp + texture.h thread.cpp thread.h thread_queue_list.h diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index f268d6021..f2b4939df 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp @@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function task) { std::unique_lock lock{instance->mutex}; --instance->count; std::notify_all_at_thread_exit(instance->cv, std::move(lock)); - }) - .detach(); + }).detach(); } } // namespace Common diff --git a/src/common/texture.cpp b/src/common/texture.cpp new file mode 100644 index 000000000..0729aeba1 --- /dev/null +++ b/src/common/texture.cpp @@ -0,0 +1,22 @@ +#include +#include "common/assert.h" +#include "common/common_types.h" + +namespace Common { +void FlipRGBA8Texture(std::vector& tex, u64 width, u64 height) { + ASSERT(tex.size() == width * height * 4); + const u64 line_size = width * 4; + u8* temp_row = new u8[line_size]; + u32 offset_1; + u32 offset_2; + for (u64 line = 0; line < height / 2; line++) { + offset_1 = line * line_size; + offset_2 = (height - line - 1) * line_size; + // Swap lines + std::memcpy(temp_row, &tex[offset_1], line_size); + std::memcpy(&tex[offset_1], &tex[offset_2], line_size); + std::memcpy(&tex[offset_2], temp_row, line_size); + } + delete[] temp_row; +} +} // namespace Common \ No newline at end of file diff --git a/src/common/texture.h b/src/common/texture.h new file mode 100644 index 000000000..b58338123 --- /dev/null +++ b/src/common/texture.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include "common/common_types.h" + +namespace Common { +void FlipRGBA8Texture(std::vector& tex, u64 width, u64 height); +} \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6ce91f76..93be0c29e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -460,7 +460,7 @@ endif() create_target_directory_groups(core) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives) +target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives lodepng) if (ENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) target_link_libraries(core PRIVATE web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index 4a1ac0b9a..64f41865b 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -4,10 +4,12 @@ #include #include +#include #include "audio_core/dsp_interface.h" #include "audio_core/hle/hle.h" #include "audio_core/lle/lle.h" #include "common/logging/log.h" +#include "common/texture.h" #include "core/arm/arm_interface.h" #ifdef ARCHITECTURE_x86_64 #include "core/arm/dynarmic/arm_dynarmic.h" @@ -159,7 +161,52 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st perf_stats = std::make_unique(title_id); ======= custom_tex_cache = std::make_unique(); +<<<<<<< HEAD >>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory +======= + if (Settings::values.preload_textures) { + // Custom textures are currently stored as + // load/textures/[TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png + const std::string load_path = + fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), + process->codeset->program_id); + + if (FileUtil::Exists(load_path)) { + FileUtil::FSTEntry texture_files; + FileUtil::ScanDirectoryTree(load_path, texture_files); + for (const auto& file : texture_files.children) { + if (file.isDirectory) + continue; + if (file.virtualName.substr(0, 5) != "tex1_") + continue; + + u32 width; + u32 height; + u64 hash; + u32 format; // unused + // TODO: more modern way of doing this + if (std::sscanf(file.virtualName.c_str(), "tex1_%ux%u_%llX_%u.png", &width, &height, + &hash, &format) == 4) { + u32 png_width; + u32 png_height; + std::vector decoded_png; + + u32 lodepng_ret = + lodepng::decode(decoded_png, png_width, png_height, file.physicalName); + if (lodepng_ret) + LOG_CRITICAL(Render_OpenGL, "Failed to preload custom texture: {}", + lodepng_error_text(lodepng_ret)); + else { + LOG_INFO(Render_OpenGL, "Preloaded custom texture from {}", + file.physicalName); + Common::FlipRGBA8Texture(decoded_png, png_width, png_height); + custom_tex_cache->CacheTexture(hash, decoded_png, png_width, png_height); + } + } + } + } + } +>>>>>>> 015582b2... implement custom texture preload status = ResultStatus::Success; m_emu_window = &emu_window; m_filepath = filepath; @@ -195,8 +242,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo timing = std::make_unique(); - kernel = std::make_unique(*memory, *timing, - [this] { PrepareReschedule(); }, system_mode); + kernel = std::make_unique( + *memory, *timing, [this] { PrepareReschedule(); }, system_mode); if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 281f48026..cc7565d2a 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -1340,7 +1340,8 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ case CecDataPathType::MboxData: case CecDataPathType::MboxIcon: case CecDataPathType::MboxTitle: - default: {} + default: { + } } } diff --git a/src/core/movie.h b/src/core/movie.h index f1be86946..b9f1ee478 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -41,8 +41,8 @@ public: return s_instance; } - void StartPlayback(const std::string& movie_file, - std::function completion_callback = [] {}); + void StartPlayback( + const std::string& movie_file, std::function completion_callback = [] {}); void StartRecording(const std::string& movie_file); /// Prepare to override the clock before playing back movies diff --git a/src/core/settings.h b/src/core/settings.h index 1d00a71a6..213cc3ec3 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -172,6 +172,7 @@ struct Values { bool dump_textures; bool custom_textures; + bool preload_textures; // Audio bool enable_dsp_lle; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 887436550..f0f76493e 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -217,8 +217,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie success_callback(); else failure_callback(); - }) - .detach(); + }).detach(); } CalibrationConfigurationJob::CalibrationConfigurationJob( @@ -269,8 +268,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( complete_event.Wait(); socket.Stop(); worker_thread.join(); - }) - .detach(); + }).detach(); } CalibrationConfigurationJob::~CalibrationConfigurationJob() { diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index dbbc21c8c..88566d7f2 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -17,7 +17,8 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) timing = std::make_unique(); memory = std::make_unique(); - kernel = std::make_unique(*memory, *timing, [] {}, 0); + kernel = std::make_unique( + *memory, *timing, [] {}, 0); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index fb549f829..367c3b7ea 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -23,7 +23,8 @@ static std::shared_ptr MakeObject(Kernel::KernelSystem& kernel) { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { Core::Timing timing; Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0); + Kernel::KernelSystem kernel( + memory, timing, [] {}, 0); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); @@ -235,7 +236,8 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { Core::Timing timing; Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0); + Kernel::KernelSystem kernel( + memory, timing, [] {}, 0); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index 4a6d54bf7..b33a08adf 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -13,7 +13,8 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { Core::Timing timing; Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0); + Kernel::KernelSystem kernel( + memory, timing, [] {}, 0); SECTION("these regions should not be mapped on an empty process") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index ab198117b..b88ab7848 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -22,6 +22,7 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" +#include "common/texture.h" #include "common/vector_math.h" #include "core/core.h" #include "core/custom_tex_cache.h" @@ -855,25 +856,6 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) { } } -// TODO: move this function to a better place -void FlipRGBA8Texture(std::vector& tex, u64 width, u64 height) { - ASSERT(tex.size() == width * height * 4); - const u64 line_size = width * 4; - // Thanks MSVC for not being able to make variable length arrays - u8* temp_row = new u8[line_size]; - u32 offset_1; - u32 offset_2; - for (u64 line = 0; line < height / 2; line++) { - offset_1 = line * line_size; - offset_2 = (height - line - 1) * line_size; - // Swap lines - std::memcpy(temp_row, &tex[offset_1], line_size); - std::memcpy(&tex[offset_1], &tex[offset_2], line_size); - std::memcpy(&tex[offset_2], temp_row, line_size); - } - delete[] temp_row; -} - MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint read_fb_handle, GLuint draw_fb_handle) { @@ -893,7 +875,8 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r u32 png_width = 0; u32 png_height = 0; u64 tex_hash = 0; - Common::Rectangle custom_rect = rect; // Required for rect to function properly with custom textures + Common::Rectangle custom_rect = + rect; // Required for rect to function properly with custom textures if (Settings::values.dump_textures || Settings::values.custom_textures) tex_hash = Common::ComputeHash64(gl_buffer.get(), gl_buffer_size); @@ -913,7 +896,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r lodepng_error_text(lodepng_ret)); else { LOG_INFO(Render_OpenGL, "Loaded custom texture from {}", load_path); - FlipRGBA8Texture(decoded_png, png_width, png_height); + Common::FlipRGBA8Texture(decoded_png, png_width, png_height); custom_tex_cache.CacheTexture(tex_hash, decoded_png, png_width, png_height); use_custom_tex = true; } @@ -1014,7 +997,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r glBindTexture(GL_TEXTURE_2D, target_tex); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]); glBindTexture(GL_TEXTURE_2D, 0); - FlipRGBA8Texture(decoded_texture, width, height); + Common::FlipRGBA8Texture(decoded_texture, width, height); u32 png_error = lodepng::encode(dump_path, decoded_texture, width, height); if (png_error) { LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture! {}", @@ -1034,8 +1017,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r scaled_rect.bottom *= res_scale; BlitTextures(unscaled_tex.handle, {0, custom_rect.GetHeight(), custom_rect.GetWidth(), 0}, - texture.handle, - scaled_rect, type, read_fb_handle, draw_fb_handle); + texture.handle, scaled_rect, type, read_fb_handle, draw_fb_handle); } InvalidateAllWatcher();