diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 9ed593b46..86066dfe9 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -8,8 +8,8 @@ add_executable(citra default_ini.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h - generic_image_interface.cpp - generic_image_interface.h + lodepng_image_interface.cpp + lodepng_image_interface.h resource.h ) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index cdf688832..294ba7b88 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -20,6 +20,7 @@ #include "citra/config.h" #include "citra/emu_window/emu_window_sdl2.h" +#include "citra/lodepng_image_interface.h" #include "common/common_paths.h" #include "common/detached_tasks.h" #include "common/file_util.h" @@ -40,7 +41,6 @@ #include "core/loader/loader.h" #include "core/movie.h" #include "core/settings.h" -#include "generic_image_interface.h" #include "network/network.h" #include "video_core/video_core.h" @@ -344,7 +344,7 @@ int main(int argc, char** argv) { Frontend::RegisterDefaultApplets(); // Register generic image interface - Core::System::GetInstance().RegisterImageInterface(std::make_shared()); + Core::System::GetInstance().RegisterImageInterface(std::make_shared()); std::unique_ptr emu_window{std::make_unique(fullscreen)}; diff --git a/src/citra/generic_image_interface.cpp b/src/citra/lodepng_image_interface.cpp similarity index 84% rename from src/citra/generic_image_interface.cpp rename to src/citra/lodepng_image_interface.cpp index 663e8eb34..824e14b93 100644 --- a/src/citra/generic_image_interface.cpp +++ b/src/citra/lodepng_image_interface.cpp @@ -4,9 +4,9 @@ #include #include "common/logging/log.h" -#include "generic_image_interface.h" +#include "lodepng_image_interface.h" -bool GenericImageInterface::DecodePNG(std::vector& dst, u32& width, u32& height, +bool LodePNGImageInterface::DecodePNG(std::vector& dst, u32& width, u32& height, const std::string& path) { u32 lodepng_ret = lodepng::decode(dst, width, height, path); if (lodepng_ret) { @@ -17,7 +17,7 @@ bool GenericImageInterface::DecodePNG(std::vector& dst, u32& width, u32& hei return true; } -bool GenericImageInterface::EncodePNG(const std::string& path, const std::vector& src, +bool LodePNGImageInterface::EncodePNG(const std::string& path, const std::vector& src, u32 width, u32 height) { u32 lodepng_ret = lodepng::encode(path, src, width, height); if (lodepng_ret) { diff --git a/src/citra/generic_image_interface.h b/src/citra/lodepng_image_interface.h similarity index 87% rename from src/citra/generic_image_interface.h rename to src/citra/lodepng_image_interface.h index 7e6e82335..1c233961a 100644 --- a/src/citra/generic_image_interface.h +++ b/src/citra/lodepng_image_interface.h @@ -6,7 +6,7 @@ #include "core/frontend/image_interface.h" -class GenericImageInterface final : public Frontend::ImageInterface { +class LodePNGImageInterface final : public Frontend::ImageInterface { public: bool DecodePNG(std::vector& dst, u32& width, u32& height, const std::string& path) override; bool EncodePNG(const std::string& path, const std::vector& src, u32 width, diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui index 4c634f314..efe53606e 100644 --- a/src/citra_qt/configuration/configure.ui +++ b/src/citra_qt/configuration/configure.ui @@ -140,7 +140,7 @@
configuration/configure_graphics.h
1 - + ConfigureEnhancements QWidget
configuration/configure_enhancements.h
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index d137d8472..78574f6c6 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -458,6 +458,9 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra QAction* open_extdata_location = context_menu.addAction(tr("Open Extra Data Location")); QAction* open_application_location = context_menu.addAction(tr("Open Application Location")); QAction* open_update_location = context_menu.addAction(tr("Open Update Data Location")); + QAction* open_texture_dump_location = context_menu.addAction(tr("Open Texture Dump Location")); + QAction* open_texture_load_location = + context_menu.addAction(tr("Open Custom Texture Location")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); const bool is_application = @@ -484,6 +487,10 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra program_id + 0xe00000000) + "content/")); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); + + open_texture_dump_location->setVisible(is_application); + open_texture_load_location->setVisible(is_application); + navigate_to_gamedb_entry->setVisible(it != compatibility_list.end()); connect(open_save_location, &QAction::triggered, [this, program_id] { @@ -498,6 +505,18 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra connect(open_update_location, &QAction::triggered, [this, program_id] { emit OpenFolderRequested(program_id, GameListOpenTarget::UPDATE_DATA); }); + connect(open_texture_dump_location, &QAction::triggered, [this, program_id] { + if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/", + FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + program_id))) + emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_DUMP); + }); + connect(open_texture_load_location, &QAction::triggered, [this, program_id] { + if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/", + FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), + program_id))) + emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_LOAD); + }); connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index bef23f31b..b98e79b57 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -29,7 +29,14 @@ class QTreeView; class QToolButton; class QVBoxLayout; -enum class GameListOpenTarget { SAVE_DATA = 0, EXT_DATA = 1, APPLICATION = 2, UPDATE_DATA = 3 }; +enum class GameListOpenTarget { + SAVE_DATA = 0, + EXT_DATA = 1, + APPLICATION = 2, + UPDATE_DATA = 3, + TEXTURE_DUMP = 4, + TEXTURE_LOAD = 5 +}; class GameList : public QWidget { Q_OBJECT diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index dfaa19df4..2b5bbaf62 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1081,6 +1081,16 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) { path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, data_id + 0xe00000000) + "content/"; break; + case GameListOpenTarget::TEXTURE_DUMP: + open_target = "Dumped Textures"; + path = fmt::format("{}textures/{:016X}/", + FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), data_id); + break; + case GameListOpenTarget::TEXTURE_LOAD: + open_target = "Custom Textures"; + path = fmt::format("{}textures/{:016X}/", + FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id); + break; default: LOG_ERROR(Frontend, "Unexpected target {}", static_cast(target)); return; diff --git a/src/citra_qt/qt_image_interface.cpp b/src/citra_qt/qt_image_interface.cpp index befc77c81..5e75ccfdd 100644 --- a/src/citra_qt/qt_image_interface.cpp +++ b/src/citra_qt/qt_image_interface.cpp @@ -2,12 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include #include +#include "citra_qt/qt_image_interface.h" #include "common/logging/log.h" -#include "core/frontend/image_interface.h" -#include "qt_image_interface.h" bool QtImageInterface::DecodePNG(std::vector& dst, u32& width, u32& height, const std::string& path) { @@ -20,16 +18,10 @@ bool QtImageInterface::DecodePNG(std::vector& dst, u32& width, u32& height, width = image.width(); height = image.height(); + image = image.convertToFormat(QImage::Format_RGBA8888); + // Write RGBA8 to vector - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - const QColor pixel(image.pixelColor(x, y)); - dst.push_back(pixel.red()); - dst.push_back(pixel.green()); - dst.push_back(pixel.blue()); - dst.push_back(pixel.alpha()); - } - } + dst = std::vector(image.constBits(), image.constBits() + (width * height * 4)); return true; } diff --git a/src/core/core.cpp b/src/core/core.cpp index 1da7189aa..af041ac06 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -196,10 +196,11 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st } perf_stats = std::make_unique(title_id); custom_tex_cache = std::make_unique(); - if (Settings::values.custom_textures) + if (Settings::values.custom_textures) { FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), Kernel().GetCurrentProcess()->codeset->program_id)); + } if (Settings::values.preload_textures) PreloadCustomTextures(); status = ResultStatus::Success; diff --git a/src/core/custom_tex_cache.cpp b/src/core/custom_tex_cache.cpp index 16250752d..ed4ba9014 100644 --- a/src/core/custom_tex_cache.cpp +++ b/src/core/custom_tex_cache.cpp @@ -2,10 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include -#include "common/common_types.h" -#include "custom_tex_cache.h" +#include "core/custom_tex_cache.h" namespace Core { CustomTexCache::CustomTexCache() = default; @@ -13,18 +10,18 @@ CustomTexCache::CustomTexCache() = default; CustomTexCache::~CustomTexCache() = default; bool CustomTexCache::IsTextureDumped(u64 hash) const { - return dumped_textures.find(hash) != dumped_textures.end(); + return dumped_textures.count(hash); } void CustomTexCache::SetTextureDumped(const u64 hash) { - dumped_textures[hash] = true; + dumped_textures.insert(hash); } bool CustomTexCache::IsTextureCached(u64 hash) const { - return custom_textures.find(hash) != custom_textures.end(); + return custom_textures.count(hash); } -const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) { +const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) const { return custom_textures.at(hash); } diff --git a/src/core/custom_tex_cache.h b/src/core/custom_tex_cache.h index 4cae738dd..a340d3e0d 100644 --- a/src/core/custom_tex_cache.h +++ b/src/core/custom_tex_cache.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "common/common_types.h" @@ -25,11 +26,11 @@ public: void SetTextureDumped(u64 hash); bool IsTextureCached(u64 hash) const; - const CustomTexInfo& LookupTexture(const u64 hash); + const CustomTexInfo& LookupTexture(u64 hash) const; void CacheTexture(u64 hash, const std::vector& tex, u32 width, u32 height); private: - std::unordered_map dumped_textures; + std::unordered_set dumped_textures; std::unordered_map custom_textures; }; } // namespace Core \ No newline at end of file diff --git a/src/core/frontend/image_interface.h b/src/core/frontend/image_interface.h index f5ccd1c29..7caa63f36 100644 --- a/src/core/frontend/image_interface.h +++ b/src/core/frontend/image_interface.h @@ -7,7 +7,6 @@ #include #include #include "common/common_types.h" -#include "common/logging/log.h" namespace Frontend { diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index f95e8e422..53225a1b5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -866,7 +866,10 @@ bool CachedSurface::LoadCustomTexture(u64 tex_hash, Core::CustomTexInfo& tex_inf Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id, width, height, tex_hash, static_cast(pixel_format)); - if (!custom_tex_cache.IsTextureCached(tex_hash)) { + if (custom_tex_cache.IsTextureCached(tex_hash)) { + tex_info = custom_tex_cache.LookupTexture(tex_hash); + result = true; + } else { if (FileUtil::Exists(load_path)) { if (image_interface->DecodePNG(tex_info.tex, tex_info.width, tex_info.height, load_path)) { @@ -879,9 +882,6 @@ bool CachedSurface::LoadCustomTexture(u64 tex_hash, Core::CustomTexInfo& tex_inf LOG_CRITICAL(Render_OpenGL, "Failed to load custom texture"); } } - } else { - tex_info = custom_tex_cache.LookupTexture(tex_hash); - result = true; } if (result) { @@ -915,9 +915,23 @@ std::optional CachedSurface::GetDumpPath(u64 tex_hash) { return {}; } -void CachedSurface::DumpTexture(GLuint target_tex, const std::string& dump_path) { +void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) { // Dump texture to RGBA8 and encode as PNG const auto& image_interface = Core::System::GetInstance().GetImageInterface(); + auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache(); + std::string dump_path = + fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id); + if (!FileUtil::CreateFullPath(dump_path)) { + LOG_ERROR(Render, "Unable to create {}", dump_path); + return; + } + + dump_path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash, + static_cast(pixel_format)); + if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(dump_path)) + custom_tex_cache.SetTextureDumped(tex_hash); + LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path); std::vector decoded_texture; decoded_texture.resize(width * height * 4); @@ -930,7 +944,7 @@ void CachedSurface::DumpTexture(GLuint target_tex, const std::string& dump_path) glBindTexture(GL_TEXTURE_2D, 0); Common::FlipRGBA8Texture(decoded_texture, width, height); if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height)) - LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture"); + LOG_ERROR(Render_OpenGL, "Failed to save decoded texture"); } MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); @@ -945,12 +959,11 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r // Read custom texture auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache(); - bool dump_tex = false; bool use_custom_tex = false; std::string dump_path; // Has to be declared here for logging later u64 tex_hash = 0; - Common::Rectangle custom_rect = - rect; // Required for rect to function properly with custom textures + // Required for rect to function properly with custom textures + Common::Rectangle custom_rect = rect; if (Settings::values.dump_textures || Settings::values.custom_textures) tex_hash = Common::ComputeHash64(gl_buffer.get(), gl_buffer_size); @@ -958,14 +971,6 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r if (Settings::values.custom_textures) is_custom = use_custom_tex = LoadCustomTexture(tex_hash, custom_tex_info, custom_rect); - if (Settings::values.dump_textures && !use_custom_tex) { - auto temp_dump_path = GetDumpPath(tex_hash); - if (temp_dump_path.has_value()) { - dump_path = *temp_dump_path; - dump_tex = true; - } - } - // Load data from memory to the surface GLint x0 = static_cast(custom_rect.left); GLint y0 = static_cast(custom_rect.bottom); @@ -982,7 +987,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r y0 = 0; unscaled_tex.Create(); - if (use_custom_tex) { + if (is_custom) { AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8), custom_tex_info.width, custom_tex_info.height); } else { @@ -1000,7 +1005,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r // Ensure no bad interactions with GL_UNPACK_ALIGNMENT ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0); - if (use_custom_tex) { + if (is_custom) { if (res_scale == 1) { AllocateSurfaceTexture(texture.handle, GetFormatTuple(PixelFormat::RGBA8), custom_tex_info.width, custom_tex_info.height); @@ -1023,8 +1028,8 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle& rect, GLuint r } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - if (dump_tex) - DumpTexture(target_tex, dump_path); + if (Settings::values.dump_textures) + DumpTexture(target_tex, tex_hash); cur_state.texture_units[0].texture_2d = old_tex; cur_state.Apply(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index bc8183164..909f91f63 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -385,7 +385,7 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this& custom_rect); std::optional GetDumpPath(u64 tex_hash); - void DumpTexture(GLuint target_tex, const std::string& dump_path); + void DumpTexture(GLuint target_tex, u64 tex_hash); // Upload/Download data in gl_buffer in/to this surface's texture void UploadGLTexture(const Common::Rectangle& rect, GLuint read_fb_handle,