diff --git a/.gitmodules b/.gitmodules index a1d0af096..8e4c3f1af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,9 +43,6 @@ [submodule "teakra"] path = externals/teakra url = https://github.com/wwylele/teakra.git -[submodule "lodepng"] - path = externals/lodepng/lodepng - url = https://github.com/lvandeve/lodepng.git [submodule "zstd"] path = externals/zstd url = https://github.com/facebook/zstd.git @@ -73,3 +70,6 @@ [submodule "sirit"] path = externals/sirit url = https://github.com/GPUCode/sirit +[submodule "externals/libspng"] + path = externals/libspng + url = https://github.com/randy408/libspng diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 8ccb0e572..1ae545455 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -172,8 +172,8 @@ if (ENABLE_WEB_SERVICE) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) endif() -# lodepng -add_subdirectory(lodepng) +# libspng +add_subdirectory(libspng) # (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG if(ANDROID) diff --git a/externals/libspng b/externals/libspng new file mode 160000 index 000000000..75c39ce09 --- /dev/null +++ b/externals/libspng @@ -0,0 +1 @@ +Subproject commit 75c39ce0948d3b9c623a674ebfeb63a703a7c116 diff --git a/externals/lodepng/CMakeLists.txt b/externals/lodepng/CMakeLists.txt deleted file mode 100644 index df9a81850..000000000 --- a/externals/lodepng/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_library(lodepng - lodepng/lodepng.cpp - lodepng/lodepng.h -) - -create_target_directory_groups(lodepng) -target_include_directories(lodepng INTERFACE lodepng) diff --git a/externals/lodepng/lodepng b/externals/lodepng/lodepng deleted file mode 160000 index 18964554b..000000000 --- a/externals/lodepng/lodepng +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 18964554bc769255401942e0e6dfd09f2fab2093 diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index affbec3f8..37fce19d4 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -8,8 +8,6 @@ add_executable(citra default_ini.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h - lodepng_image_interface.cpp - lodepng_image_interface.h precompiled_headers.h resource.h ) @@ -17,7 +15,7 @@ add_executable(citra create_target_directory_groups(citra) target_link_libraries(citra PRIVATE common core input_common network) -target_link_libraries(citra PRIVATE inih glad lodepng) +target_link_libraries(citra PRIVATE inih glad) if (MSVC) target_link_libraries(citra PRIVATE getopt) endif() diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 65d9dc7be..fab20c43d 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -10,7 +10,6 @@ // This needs to be included before getopt.h because the latter #defines symbols used by it #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" @@ -351,9 +350,6 @@ int main(int argc, char** argv) { // Register frontend applets Frontend::RegisterDefaultApplets(); - // Register generic image interface - Core::System::GetInstance().RegisterImageInterface(std::make_shared()); - EmuWindow_SDL2::InitializeSDL2(); const auto emu_window{std::make_unique(fullscreen, false)}; diff --git a/src/citra/lodepng_image_interface.cpp b/src/citra/lodepng_image_interface.cpp deleted file mode 100644 index e537de4e4..000000000 --- a/src/citra/lodepng_image_interface.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "citra/lodepng_image_interface.h" -#include "common/logging/log.h" - -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) { - LOG_CRITICAL(Frontend, "Failed to decode {} because {}", path, - lodepng_error_text(lodepng_ret)); - return false; - } - return true; -} - -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) { - LOG_CRITICAL(Frontend, "Failed to encode {} because {}", path, - lodepng_error_text(lodepng_ret)); - return false; - } - return true; -} diff --git a/src/citra/lodepng_image_interface.h b/src/citra/lodepng_image_interface.h deleted file mode 100644 index 6880b10a0..000000000 --- a/src/citra/lodepng_image_interface.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/frontend/image_interface.h" - -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, - u32 height) override; -}; diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index d139594f0..9c22bcc46 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -167,8 +167,6 @@ add_executable(citra-qt precompiled_headers.h uisettings.cpp uisettings.h - qt_image_interface.cpp - qt_image_interface.h updater/updater.cpp updater/updater.h updater/updater_p.h diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index cddbeaeb8..eb1d2f844 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -50,7 +50,6 @@ #include "citra_qt/movie/movie_play_dialog.h" #include "citra_qt/movie/movie_record_dialog.h" #include "citra_qt/multiplayer/state.h" -#include "citra_qt/qt_image_interface.h" #include "citra_qt/uisettings.h" #include "citra_qt/updater/updater.h" #include "citra_qt/util/clickable_label.h" @@ -2649,9 +2648,6 @@ int main(int argc, char* argv[]) { system.RegisterMiiSelector(std::make_shared(main_window)); system.RegisterSoftwareKeyboard(std::make_shared(main_window)); - // Register Qt image interface - system.RegisterImageInterface(std::make_shared()); - main_window.show(); QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, diff --git a/src/citra_qt/qt_image_interface.cpp b/src/citra_qt/qt_image_interface.cpp deleted file mode 100644 index 00a5e1384..000000000 --- a/src/citra_qt/qt_image_interface.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include "citra_qt/qt_image_interface.h" -#include "common/logging/log.h" - -bool QtImageInterface::DecodePNG(std::vector& dst, u32& width, u32& height, - const std::string& path) { - QImage image(QString::fromStdString(path)); - - if (image.isNull()) { - LOG_ERROR(Frontend, "Failed to open {} for decoding", path); - return false; - } - width = image.width(); - height = image.height(); - - image = image.convertToFormat(QImage::Format_RGBA8888); - - // Write RGBA8 to vector - dst = std::vector(image.constBits(), image.constBits() + (width * height * 4)); - - return true; -} - -bool QtImageInterface::EncodePNG(const std::string& path, const std::vector& src, u32 width, - u32 height) { - QImage image(src.data(), width, height, QImage::Format_RGBA8888); - - if (!image.save(QString::fromStdString(path), "PNG")) { - LOG_ERROR(Frontend, "Failed to save {}", path); - return false; - } - return true; -} diff --git a/src/citra_qt/qt_image_interface.h b/src/citra_qt/qt_image_interface.h deleted file mode 100644 index 53b49b7b8..000000000 --- a/src/citra_qt/qt_image_interface.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/frontend/image_interface.h" - -class QtImageInterface 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, - u32 height) override; -}; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 590098389..0e65575a1 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -74,11 +74,14 @@ add_library(common STATIC common_precompiled_headers.h common_types.h construct.h + dds.h error.cpp error.h file_util.cpp file_util.h hash.h + image_util.cpp + image_util.h linear_disk_cache.h literals.h logging/backend.cpp @@ -143,7 +146,7 @@ add_library(common STATIC create_target_directory_groups(common) target_link_libraries(common PUBLIC fmt::fmt microprofile Boost::boost Boost::serialization) -target_link_libraries(common PRIVATE libzstd_static) +target_link_libraries(common PRIVATE libzstd_static spng_static) set_target_properties(common PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) if ("x86_64" IN_LIST ARCHITECTURE) diff --git a/src/common/dds.h b/src/common/dds.h new file mode 100644 index 000000000..866e2eee5 --- /dev/null +++ b/src/common/dds.h @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------------------- +// DDS.h +// +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +// http://go.microsoft.com/fwlink/?LinkID=615561 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include + +namespace Common::DirectX { + +#pragma pack(push, 1) + +const uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT { + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 +#define DDS_PAL8A 0x00000021 // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS +#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ + ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24)) +#endif /* defined(MAKEFOURCC) */ + +#define DDS_HEADER_FLAGS_TEXTURE \ + 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +// Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION +enum DDS_RESOURCE_DIMENSION { + DDS_DIMENSION_TEXTURE1D = 2, + DDS_DIMENSION_TEXTURE2D = 3, + DDS_DIMENSION_TEXTURE3D = 4, +}; + +struct DDS_HEADER { + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; +}; + +struct DDS_HEADER_DXT10 { + uint32_t dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see DDS_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 +}; + +#pragma pack(pop) + +static_assert(sizeof(DDS_HEADER) == 124, "DDS Header size mismatch"); +static_assert(sizeof(DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch"); + +constexpr DDS_PIXELFORMAT DDSPF_A8R8G8B8 = { + sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}; +constexpr DDS_PIXELFORMAT DDSPF_X8R8G8B8 = { + sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}; +constexpr DDS_PIXELFORMAT DDSPF_A8B8G8R8 = { + sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000}; +constexpr DDS_PIXELFORMAT DDSPF_X8B8G8R8 = { + sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}; +constexpr DDS_PIXELFORMAT DDSPF_R8G8B8 = { + sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}; + +} // namespace Common::DirectX diff --git a/src/common/file_util.h b/src/common/file_util.h index cdd55b665..d9207b3cc 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -339,6 +339,9 @@ public: [[nodiscard]] explicit operator bool() const { return IsGood(); } + [[nodiscard]] std::FILE* Handle() { + return m_file; + } bool Seek(s64 off, int origin); [[nodiscard]] u64 Tell() const; diff --git a/src/common/image_util.cpp b/src/common/image_util.cpp new file mode 100644 index 000000000..17c10d3e3 --- /dev/null +++ b/src/common/image_util.cpp @@ -0,0 +1,111 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/dds.h" +#include "common/file_util.h" +#include "common/image_util.h" +#include "common/logging/log.h" + +namespace Common { + +using namespace Common::DirectX; + +namespace { + +void spng_free(spng_ctx* ctx) { + if (ctx) { + spng_ctx_free(ctx); + } +} + +auto make_spng_ctx(int flags) { + return std::unique_ptr(spng_ctx_new(flags), spng_free); +} + +} // Anonymous namespace + +bool DecodePNG(std::span in_data, std::vector& out_data, u32& width, u32& height) { + auto ctx = make_spng_ctx(0); + if (!ctx) [[unlikely]] { + return false; + } + + if (spng_set_png_buffer(ctx.get(), in_data.data(), in_data.size())) { + return false; + } + + spng_ihdr ihdr{}; + if (spng_get_ihdr(ctx.get(), &ihdr)) { + return false; + } + + const int format = SPNG_FMT_RGBA8; + size_t decoded_len = 0; + if (spng_decoded_image_size(ctx.get(), format, &decoded_len)) { + return false; + } + + out_data.resize(decoded_len); + if (spng_decode_image(ctx.get(), out_data.data(), decoded_len, format, SPNG_DECODE_TRNS)) { + return false; + } + + width = ihdr.width; + height = ihdr.height; + return true; +} + +bool EncodePNG(std::span in_data, const std::string& out_path, u32 width, u32 height, + u32 stride, s32 level) { + auto ctx = make_spng_ctx(SPNG_CTX_ENCODER); + if (!ctx) [[unlikely]] { + return false; + } + + auto outfile = FileUtil::IOFile(out_path, "wb"); + if (spng_set_png_file(ctx.get(), outfile.Handle())) { + return false; + } + + if (spng_set_option(ctx.get(), SPNG_IMG_COMPRESSION_LEVEL, level)) { + return false; + } + + spng_ihdr ihdr{}; + ihdr.width = width; + ihdr.height = height; + ihdr.color_type = SPNG_COLOR_TYPE_TRUECOLOR_ALPHA; + ihdr.bit_depth = 8; + if (spng_set_ihdr(ctx.get(), &ihdr)) { + return false; + } + + if (spng_encode_image(ctx.get(), nullptr, 0, SPNG_FMT_PNG, + SPNG_ENCODE_PROGRESSIVE | SPNG_ENCODE_FINALIZE)) { + return false; + } + + for (u32 row = 0; row < height; row++) { + const int err = spng_encode_row(ctx.get(), &in_data[row * stride], stride); + if (err == SPNG_EOI) { + break; + } + + if (err) { + LOG_ERROR(Common, "Failed to save {} by {} image to {} at level {}: error {}", width, + height, out_path, level, err); + return false; + } + } + + size_t image_len = 0; + spng_decoded_image_size(ctx.get(), SPNG_FMT_PNG, &image_len); + LOG_ERROR(Common, "{} byte {} by {} image saved to {} at level {}", image_len, width, height, + out_path, level); + + return true; +} + +} // namespace Common diff --git a/src/common/image_util.h b/src/common/image_util.h new file mode 100644 index 000000000..e61914129 --- /dev/null +++ b/src/common/image_util.h @@ -0,0 +1,25 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/common_types.h" + +namespace Common { + +/** + * @brief DecodePNG Given a buffer of png input data decodes said data to RGBA8 format + * and writes the result to out_data, updating width and height to match the file dimentions + * @param in_data The input png data + * @param out_data The decoded RGBA8 pixel data + * @param width The output width of the png image + * @param height The output height of the png image + * @return true on decode success, false otherwise + */ +bool DecodePNG(std::span in_data, std::vector& out_data, u32& width, u32& height); + +bool EncodePNG(std::span in_data, const std::string& out_path, u32 width, u32 height, + u32 stride, s32 level); + +} // namespace Common diff --git a/src/core/custom_tex_cache.cpp b/src/core/custom_tex_cache.cpp index 9045df836..d42d81d6f 100644 --- a/src/core/custom_tex_cache.cpp +++ b/src/core/custom_tex_cache.cpp @@ -83,14 +83,14 @@ void CustomTexCache::PreloadTextures(Frontend::ImageInterface& image_interface) std::bitset<32> width_bits(tex_info.width); std::bitset<32> height_bits(tex_info.height); if (width_bits.count() == 1 && height_bits.count() == 1) { - LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path); + LOG_DEBUG(HW_GPU, "Loaded custom texture from {}", path_info.path); Common::FlipRGBA8Texture(tex_info.tex, tex_info.width, tex_info.height); CacheTexture(path_info.hash, tex_info.tex, tex_info.width, tex_info.height); } else { - LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path); + LOG_ERROR(HW_GPU, "Texture {} size is not a power of 2", path_info.path); } } else { - LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path); + LOG_ERROR(HW_GPU, "Failed to load custom texture {}", path_info.path); } } } diff --git a/src/core/custom_tex_cache.h b/src/core/custom_tex_cache.h index d23ad7b91..fbc21352e 100644 --- a/src/core/custom_tex_cache.h +++ b/src/core/custom_tex_cache.h @@ -12,7 +12,7 @@ namespace Frontend { class ImageInterface; -} // namespace Frontend +} namespace Core { struct CustomTexInfo { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index d3a74755f..c3e9182a0 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -28,6 +28,8 @@ add_library(video_core STATIC regs_texturing.h renderer_base.cpp renderer_base.h + rasterizer_cache/custom/custom_tex_manager.cpp + rasterizer_cache/custom/custom_tex_manager.h rasterizer_cache/framebuffer_base.cpp rasterizer_cache/framebuffer_base.h rasterizer_cache/pixel_format.cpp diff --git a/src/video_core/rasterizer_cache/custom/custom_tex_manager.cpp b/src/video_core/rasterizer_cache/custom/custom_tex_manager.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/video_core/rasterizer_cache/custom/custom_tex_manager.cpp @@ -0,0 +1 @@ + diff --git a/src/video_core/rasterizer_cache/custom/custom_tex_manager.h b/src/video_core/rasterizer_cache/custom/custom_tex_manager.h new file mode 100644 index 000000000..f199d91c2 --- /dev/null +++ b/src/video_core/rasterizer_cache/custom/custom_tex_manager.h @@ -0,0 +1,53 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include "common/common_types.h" + +namespace VideoCore { + +struct CustomTexture { + u32 width; + u32 height; + u32 levels; + std::vector pixels; +}; + +// This is to avoid parsing the filename multiple times +struct CustomTexPathInfo { + std::string path; + u64 hash; +}; + +// TODO: think of a better name for this class... +class CustomTexManager { +public: + explicit CustomTexManager(); + ~CustomTexManager(); + + bool IsTextureDumped(u64 hash) const; + void SetTextureDumped(u64 hash); + + bool IsTextureCached(u64 hash) const; + const CustomTexture& LookupTexture(u64 hash) const; + void CacheTexture(u64 hash, const std::vector& tex, u32 width, u32 height); + + void AddTexturePath(u64 hash, const std::string& path); + void FindCustomTextures(u64 program_id); + void PreloadTextures(Frontend::ImageInterface& image_interface); + bool CustomTextureExists(u64 hash) const; + const CustomTexPathInfo& LookupTexturePathInfo(u64 hash) const; + bool IsTexturePathMapEmpty() const; + +private: + std::unordered_set dumped_textures; + std::unordered_map custom_textures; + std::unordered_map custom_texture_paths; +}; +} // namespace VideoCore diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 2b638f8a0..97bcde994 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -173,9 +173,7 @@ Allocation TextureRuntime::Allocate(u32 width, u32 height, u32 levels, vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect) { MICROPROFILE_SCOPE(Vulkan_ImageAlloc); - // The internal format does not provide enough guarantee of texture uniqueness - // especially when many pixel formats fallback to RGBA8 - ASSERT(pixel_format != VideoCore::PixelFormat::Invalid); + ASSERT(pixel_format != VideoCore::PixelFormat::Invalid && levels >= 1); const HostTextureTag key = { .format = format, .pixel_format = pixel_format,