common: Move image decoders to common
* Replace the various image interfaces with spng which is very lightweight and fast. Also add a dds header which will be useful when support for that format is implemented
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -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
|
||||
|
4
externals/CMakeLists.txt
vendored
4
externals/CMakeLists.txt
vendored
@ -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)
|
||||
|
1
externals/libspng
vendored
Submodule
1
externals/libspng
vendored
Submodule
Submodule externals/libspng added at 75c39ce094
7
externals/lodepng/CMakeLists.txt
vendored
7
externals/lodepng/CMakeLists.txt
vendored
@ -1,7 +0,0 @@
|
||||
add_library(lodepng
|
||||
lodepng/lodepng.cpp
|
||||
lodepng/lodepng.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(lodepng)
|
||||
target_include_directories(lodepng INTERFACE lodepng)
|
1
externals/lodepng/lodepng
vendored
1
externals/lodepng/lodepng
vendored
Submodule externals/lodepng/lodepng deleted from 18964554bc
@ -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()
|
||||
|
@ -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<LodePNGImageInterface>());
|
||||
|
||||
EmuWindow_SDL2::InitializeSDL2();
|
||||
|
||||
const auto emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen, false)};
|
||||
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <lodepng.h>
|
||||
#include "citra/lodepng_image_interface.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
bool LodePNGImageInterface::DecodePNG(std::vector<u8>& 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<u8>& 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;
|
||||
}
|
@ -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<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;
|
||||
};
|
@ -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
|
||||
|
@ -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<QtMiiSelector>(main_window));
|
||||
system.RegisterSoftwareKeyboard(std::make_shared<QtKeyboard>(main_window));
|
||||
|
||||
// Register Qt image interface
|
||||
system.RegisterImageInterface(std::make_shared<QtImageInterface>());
|
||||
|
||||
main_window.show();
|
||||
|
||||
QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
#include "citra_qt/qt_image_interface.h"
|
||||
#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));
|
||||
|
||||
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<u8>(image.constBits(), image.constBits() + (width * height * 4));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QtImageInterface::EncodePNG(const std::string& path, const std::vector<u8>& 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;
|
||||
}
|
@ -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<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;
|
||||
};
|
@ -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)
|
||||
|
111
src/common/dds.h
Normal file
111
src/common/dds.h
Normal file
@ -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 <cstdint>
|
||||
|
||||
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
|
@ -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;
|
||||
|
111
src/common/image_util.cpp
Normal file
111
src/common/image_util.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <spng.h>
|
||||
#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, decltype(&spng_free)>(spng_ctx_new(flags), spng_free);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
bool DecodePNG(std::span<const u8> in_data, std::vector<u8>& 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<u8> 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
|
25
src/common/image_util.h
Normal file
25
src/common/image_util.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#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<const u8> in_data, std::vector<u8>& out_data, u32& width, u32& height);
|
||||
|
||||
bool EncodePNG(std::span<u8> in_data, const std::string& out_path, u32 width, u32 height,
|
||||
u32 stride, s32 level);
|
||||
|
||||
} // namespace Common
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
namespace Frontend {
|
||||
class ImageInterface;
|
||||
} // namespace Frontend
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
struct CustomTexInfo {
|
||||
|
@ -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
|
||||
|
@ -0,0 +1 @@
|
||||
|
53
src/video_core/rasterizer_cache/custom/custom_tex_manager.h
Normal file
53
src/video_core/rasterizer_cache/custom/custom_tex_manager.h
Normal file
@ -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 <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
struct CustomTexture {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 levels;
|
||||
std::vector<u8> 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<u8>& 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<u64> dumped_textures;
|
||||
std::unordered_map<u64, CustomTexInfo> custom_textures;
|
||||
std::unordered_map<u64, CustomTexPathInfo> custom_texture_paths;
|
||||
};
|
||||
} // namespace VideoCore
|
@ -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,
|
||||
|
Reference in New Issue
Block a user