custom_tex_manager: Allow multiple hash mappings per texture

This commit is contained in:
GPUCode
2023-05-01 14:08:01 +03:00
parent 4f9af86cba
commit f5668cdb25
7 changed files with 47 additions and 37 deletions

View File

@@ -112,11 +112,14 @@ void CustomTexManager::FindCustomTextures() {
if (!ParseFilename(file, texture)) { if (!ParseFilename(file, texture)) {
continue; continue;
} }
auto& material = material_map[texture->hash]; for (const u64 hash : texture->hashes) {
if (!material) { auto& material = material_map[hash];
material = std::make_unique<Material>(); if (!material) {
material = std::make_unique<Material>();
}
material->hash = hash;
material->AddMapTexture(texture);
} }
material->AddMapTexture(texture);
} }
textures_loaded = true; textures_loaded = true;
} }
@@ -146,21 +149,25 @@ bool CustomTexManager::ParseFilename(const FileUtil::FSTEntry& file, CustomTextu
parts.pop_back(); parts.pop_back();
} }
// First check if the path is mapped directly to a hash // First look if this file is mapped to any number of hashes.
// before trying to parse the texture filename. std::vector<u64>& hashes = texture->hashes;
const auto it = path_to_hash_map.find(file.virtualName); const auto it = path_to_hash_map.find(file.virtualName);
if (it != path_to_hash_map.end()) { if (it != path_to_hash_map.end()) {
texture->hash = it->second; hashes = it->second;
} else { }
u32 width;
u32 height; // It's also possible for pack creators to retain the default texture name
u32 format; // still map the texture to another hash. Support that as well.
unsigned long long hash{}; u32 width;
if (std::sscanf(parts.back().c_str(), "tex1_%ux%u_%llX_%u", &width, &height, &hash, u32 height;
&format) != 4) { u32 format;
return false; unsigned long long hash{};
} const bool is_parsed = std::sscanf(parts.back().c_str(), "tex1_%ux%u_%llX_%u", &width, &height,
texture->hash = hash; &hash, &format) == 4;
const bool is_mapped =
!hashes.empty() && std::find(hashes.begin(), hashes.end(), hash) != hashes.end();
if (is_parsed && !is_mapped) {
hashes.push_back(hash);
} }
texture->path = file.physicalName; texture->path = file.physicalName;
@@ -182,9 +189,9 @@ void CustomTexManager::WriteConfig() {
json["description"] = "A graphics pack"; json["description"] = "A graphics pack";
auto& options = json["options"]; auto& options = json["options"];
options["skip_mipmap"] = skip_mipmap; options["skip_mipmap"] = false;
options["flip_png_files"] = flip_png_files; options["flip_png_files"] = true;
options["use_new_hash"] = use_new_hash; options["use_new_hash"] = true;
FileUtil::IOFile file{pack_config, "w"}; FileUtil::IOFile file{pack_config, "w"};
const std::string output = json.dump(4); const std::string output = json.dump(4);
@@ -303,7 +310,7 @@ void CustomTexManager::ReadConfig(const std::string& load_path) {
return; return;
} }
nlohmann::json json = nlohmann::json::parse(config); nlohmann::json json = nlohmann::json::parse(config, nullptr, false, true);
const auto& options = json["options"]; const auto& options = json["options"];
skip_mipmap = options["skip_mipmap"].get<bool>(); skip_mipmap = options["skip_mipmap"].get<bool>();
@@ -322,13 +329,7 @@ void CustomTexManager::ReadConfig(const std::string& load_path) {
const auto parse = [&](const std::string& file) { const auto parse = [&](const std::string& file) {
const std::string filename{FileUtil::GetFilename(file)}; const std::string filename{FileUtil::GetFilename(file)};
auto [it, new_hash] = path_to_hash_map.try_emplace(filename); auto [it, new_hash] = path_to_hash_map.try_emplace(filename);
if (!new_hash) { it->second.push_back(hash);
LOG_ERROR(Render,
"File {} with key {} already exists and is mapped to {:#016X}, skipping",
file, material.key(), path_to_hash_map[filename]);
return;
}
it->second = hash;
}; };
const auto value = material.value(); const auto value = material.value();
if (value.is_string()) { if (value.is_string()) {

View File

@@ -79,7 +79,7 @@ private:
Frontend::ImageInterface& image_interface; Frontend::ImageInterface& image_interface;
std::unordered_set<u64> dumped_textures; std::unordered_set<u64> dumped_textures;
std::unordered_map<u64, std::unique_ptr<Material>> material_map; std::unordered_map<u64, std::unique_ptr<Material>> material_map;
std::unordered_map<std::string, u64> path_to_hash_map; std::unordered_map<std::string, std::vector<u64>> path_to_hash_map;
std::vector<std::unique_ptr<CustomTexture>> custom_textures; std::vector<std::unique_ptr<CustomTexture>> custom_textures;
std::list<AsyncUpload> async_uploads; std::list<AsyncUpload> async_uploads;
std::unique_ptr<Common::ThreadWorker> workers; std::unique_ptr<Common::ThreadWorker> workers;

View File

@@ -55,6 +55,11 @@ CustomTexture::CustomTexture(Frontend::ImageInterface& image_interface_)
CustomTexture::~CustomTexture() = default; CustomTexture::~CustomTexture() = default;
void CustomTexture::LoadFromDisk(bool flip_png) { void CustomTexture::LoadFromDisk(bool flip_png) {
std::scoped_lock lock{decode_mutex};
if (IsLoaded()) {
return;
}
FileUtil::IOFile file{path, "rb"}; FileUtil::IOFile file{path, "rb"};
std::vector<u8> input(file.GetSize()); std::vector<u8> input(file.GetSize());
if (file.ReadBytes(input.data(), input.size()) != input.size()) { if (file.ReadBytes(input.data(), input.size()) != input.size()) {
@@ -71,7 +76,6 @@ void CustomTexture::LoadFromDisk(bool flip_png) {
break; break;
default: default:
LOG_ERROR(Render, "Unknown file format {}", file_format); LOG_ERROR(Render, "Unknown file format {}", file_format);
return;
} }
} }
@@ -102,8 +106,7 @@ void Material::LoadFromDisk(bool flip_png) noexcept {
} }
texture->LoadFromDisk(flip_png); texture->LoadFromDisk(flip_png);
size += texture->data.size(); size += texture->data.size();
LOG_DEBUG(Render, "Loading {} map {} with hash {:#016X}", MapTypeName(texture->type), LOG_DEBUG(Render, "Loading {} map {}", MapTypeName(texture->type), texture->path);
texture->path, texture->hash);
} }
if (!textures[0]) { if (!textures[0]) {
LOG_ERROR(Render, "Unable to create material without color texture!"); LOG_ERROR(Render, "Unable to create material without color texture!");
@@ -121,7 +124,7 @@ void Material::LoadFromDisk(bool flip_png) noexcept {
LOG_ERROR(Render, LOG_ERROR(Render,
"{} map {} of material with hash {:#016X} has dimentions {}x{} " "{} map {} of material with hash {:#016X} has dimentions {}x{} "
"which do not match the color texture dimentions {}x{}", "which do not match the color texture dimentions {}x{}",
MapTypeName(texture->type), texture->path, texture->hash, texture->width, MapTypeName(texture->type), texture->path, hash, texture->width,
texture->height, width, height); texture->height, width, height);
state = DecodeState::Failed; state = DecodeState::Failed;
return; return;

View File

@@ -6,6 +6,7 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <mutex>
#include <span> #include <span>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -39,7 +40,7 @@ public:
void LoadFromDisk(bool flip_png); void LoadFromDisk(bool flip_png);
[[nodiscard]] bool IsParsed() const noexcept { [[nodiscard]] bool IsParsed() const noexcept {
return file_format != CustomFileFormat::None && hash != 0; return file_format != CustomFileFormat::None && !hashes.empty();
} }
[[nodiscard]] bool IsLoaded() const noexcept { [[nodiscard]] bool IsLoaded() const noexcept {
@@ -56,7 +57,8 @@ public:
std::string path; std::string path;
u32 width; u32 width;
u32 height; u32 height;
u64 hash; std::vector<u64> hashes;
std::mutex decode_mutex;
CustomPixelFormat format; CustomPixelFormat format;
CustomFileFormat file_format; CustomFileFormat file_format;
std::vector<u8> data; std::vector<u8> data;
@@ -67,6 +69,7 @@ struct Material {
u32 width; u32 width;
u32 height; u32 height;
u64 size; u64 size;
u64 hash;
CustomPixelFormat format; CustomPixelFormat format;
std::array<CustomTexture*, MAX_MAPS> textures; std::array<CustomTexture*, MAX_MAPS> textures;
std::atomic<DecodeState> state{}; std::atomic<DecodeState> state{};

View File

@@ -2,12 +2,15 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once
#include <type_traits> #include <type_traits>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/settings.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/custom_textures/custom_tex_manager.h"
#include "video_core/rasterizer_cache/rasterizer_cache_base.h" #include "video_core/rasterizer_cache/rasterizer_cache_base.h"

View File

@@ -674,7 +674,7 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 colo
Framebuffer::~Framebuffer() = default; Framebuffer::~Framebuffer() = default;
Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params) { Sampler::Sampler(TextureRuntime&, VideoCore::SamplerParams params) {
const GLenum mag_filter = PicaToGL::TextureMagFilterMode(params.mag_filter); const GLenum mag_filter = PicaToGL::TextureMagFilterMode(params.mag_filter);
const GLenum min_filter = PicaToGL::TextureMinFilterMode(params.min_filter, params.mip_filter); const GLenum min_filter = PicaToGL::TextureMinFilterMode(params.min_filter, params.mip_filter);
const GLenum wrap_s = PicaToGL::WrapMode(params.wrap_s); const GLenum wrap_s = PicaToGL::WrapMode(params.wrap_s);

View File

@@ -213,7 +213,7 @@ private:
class Sampler { class Sampler {
public: public:
explicit Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params); explicit Sampler(TextureRuntime&, VideoCore::SamplerParams params);
~Sampler(); ~Sampler();
Sampler(const Sampler&) = delete; Sampler(const Sampler&) = delete;