mirror of
https://github.com/PabloMK7/citra.git
synced 2025-11-03 15:18:47 +00:00
Rasterizer cache refactor v2 (#6479)
* rasterizer_cache: Switch to template * Eliminates all opengl references in the rasterizer cache headers thus completing the backend abstraction * rasterizer_cache: Switch to page table * Surface storage isn't particularly interval sensitive so we can use a page table to make it faster * rasterizer_cache: Move sampler management out of rasterizer cache * rasterizer_cache: Remove shared_ptr usage * Switches to yuzu's slot vector for improved memory locality. * rasterizer_cache: Rework reinterpretation lookup * citra_qt: Per game texture filter * rasterizer_cache: Log additional settings * gl_texture_runtime: Resolve shadow map comment * rasterizer_cache: Don't use float for viewport * gl_texture_runtime: Fix custom allocation recycling * rasterizer_cache: Minor cleanups * Cleanup texture cubes when all the faces have been unregistered from the cache * custom_tex_manager: Allow multiple hash mappings per texture * code: Move slot vector to common * rasterizer_cache: Prevent texture cube crashes * rasterizer_cache: Improve mipmap validation * CanSubRect now works properly when validating multi-level surfaces, for example Dark Moon validates a 4 level surface from a 3 level one and it works * gl_blit_handler: Unbind sampler on reinterpretation
This commit is contained in:
parent
322d7a8287
commit
2e655f73b8
32 changed files with 2238 additions and 1927 deletions
|
|
@ -13,6 +13,7 @@
|
|||
#include "core/frontend/image_interface.h"
|
||||
#include "video_core/custom_textures/custom_tex_manager.h"
|
||||
#include "video_core/rasterizer_cache/surface_params.h"
|
||||
#include "video_core/rasterizer_cache/utils.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ namespace {
|
|||
MICROPROFILE_DEFINE(CustomTexManager_TickFrame, "CustomTexManager", "TickFrame",
|
||||
MP_RGB(54, 16, 32));
|
||||
|
||||
constexpr std::size_t MAX_UPLOADS_PER_TICK = 16;
|
||||
constexpr std::size_t MAX_UPLOADS_PER_TICK = 8;
|
||||
|
||||
bool IsPow2(u32 value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
|
|
@ -111,11 +112,14 @@ void CustomTexManager::FindCustomTextures() {
|
|||
if (!ParseFilename(file, texture)) {
|
||||
continue;
|
||||
}
|
||||
auto& material = material_map[texture->hash];
|
||||
if (!material) {
|
||||
material = std::make_unique<Material>();
|
||||
for (const u64 hash : texture->hashes) {
|
||||
auto& material = material_map[hash];
|
||||
if (!material) {
|
||||
material = std::make_unique<Material>();
|
||||
}
|
||||
material->hash = hash;
|
||||
material->AddMapTexture(texture);
|
||||
}
|
||||
material->AddMapTexture(texture);
|
||||
}
|
||||
textures_loaded = true;
|
||||
}
|
||||
|
|
@ -145,21 +149,25 @@ bool CustomTexManager::ParseFilename(const FileUtil::FSTEntry& file, CustomTextu
|
|||
parts.pop_back();
|
||||
}
|
||||
|
||||
// First check if the path is mapped directly to a hash
|
||||
// before trying to parse the texture filename.
|
||||
// First look if this file is mapped to any number of hashes.
|
||||
std::vector<u64>& hashes = texture->hashes;
|
||||
const auto it = path_to_hash_map.find(file.virtualName);
|
||||
if (it != path_to_hash_map.end()) {
|
||||
texture->hash = it->second;
|
||||
} else {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 format;
|
||||
unsigned long long hash{};
|
||||
if (std::sscanf(parts.back().c_str(), "tex1_%ux%u_%llX_%u", &width, &height, &hash,
|
||||
&format) != 4) {
|
||||
return false;
|
||||
}
|
||||
texture->hash = hash;
|
||||
hashes = it->second;
|
||||
}
|
||||
|
||||
// It's also possible for pack creators to retain the default texture name
|
||||
// still map the texture to another hash. Support that as well.
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 format;
|
||||
unsigned long long hash{};
|
||||
const bool is_parsed = std::sscanf(parts.back().c_str(), "tex1_%ux%u_%llX_%u", &width, &height,
|
||||
&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;
|
||||
|
|
@ -181,9 +189,9 @@ void CustomTexManager::WriteConfig() {
|
|||
json["description"] = "A graphics pack";
|
||||
|
||||
auto& options = json["options"];
|
||||
options["skip_mipmap"] = skip_mipmap;
|
||||
options["flip_png_files"] = flip_png_files;
|
||||
options["use_new_hash"] = use_new_hash;
|
||||
options["skip_mipmap"] = false;
|
||||
options["flip_png_files"] = true;
|
||||
options["use_new_hash"] = true;
|
||||
|
||||
FileUtil::IOFile file{pack_config, "w"};
|
||||
const std::string output = json.dump(4);
|
||||
|
|
@ -311,7 +319,7 @@ void CustomTexManager::ReadConfig(const std::string& load_path) {
|
|||
return;
|
||||
}
|
||||
|
||||
nlohmann::json json = nlohmann::json::parse(config);
|
||||
nlohmann::json json = nlohmann::json::parse(config, nullptr, false, true);
|
||||
|
||||
const auto& options = json["options"];
|
||||
skip_mipmap = options["skip_mipmap"].get<bool>();
|
||||
|
|
@ -330,13 +338,7 @@ void CustomTexManager::ReadConfig(const std::string& load_path) {
|
|||
const auto parse = [&](const std::string& file) {
|
||||
const std::string filename{FileUtil::GetFilename(file)};
|
||||
auto [it, new_hash] = path_to_hash_map.try_emplace(filename);
|
||||
if (!new_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;
|
||||
it->second.push_back(hash);
|
||||
};
|
||||
const auto value = material.value();
|
||||
if (value.is_string()) {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ private:
|
|||
Frontend::ImageInterface& image_interface;
|
||||
std::unordered_set<u64> dumped_textures;
|
||||
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::list<AsyncUpload> async_uploads;
|
||||
std::unique_ptr<Common::ThreadWorker> workers;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ CustomTexture::CustomTexture(Frontend::ImageInterface& image_interface_)
|
|||
CustomTexture::~CustomTexture() = default;
|
||||
|
||||
void CustomTexture::LoadFromDisk(bool flip_png) {
|
||||
std::scoped_lock lock{decode_mutex};
|
||||
if (IsLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile file{path, "rb"};
|
||||
std::vector<u8> input(file.GetSize());
|
||||
if (file.ReadBytes(input.data(), input.size()) != input.size()) {
|
||||
|
|
@ -71,7 +76,6 @@ void CustomTexture::LoadFromDisk(bool flip_png) {
|
|||
break;
|
||||
default:
|
||||
LOG_ERROR(Render, "Unknown file format {}", file_format);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,8 +106,7 @@ void Material::LoadFromDisk(bool flip_png) noexcept {
|
|||
}
|
||||
texture->LoadFromDisk(flip_png);
|
||||
size += texture->data.size();
|
||||
LOG_DEBUG(Render, "Loading {} map {} with hash {:#016X}", MapTypeName(texture->type),
|
||||
texture->path, texture->hash);
|
||||
LOG_DEBUG(Render, "Loading {} map {}", MapTypeName(texture->type), texture->path);
|
||||
}
|
||||
if (!textures[0]) {
|
||||
LOG_ERROR(Render, "Unable to create material without color texture!");
|
||||
|
|
@ -121,7 +124,7 @@ void Material::LoadFromDisk(bool flip_png) noexcept {
|
|||
LOG_ERROR(Render,
|
||||
"{} map {} of material with hash {:#016X} has 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);
|
||||
state = DecodeState::Failed;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
@ -39,7 +40,7 @@ public:
|
|||
void LoadFromDisk(bool flip_png);
|
||||
|
||||
[[nodiscard]] bool IsParsed() const noexcept {
|
||||
return file_format != CustomFileFormat::None && hash != 0;
|
||||
return file_format != CustomFileFormat::None && !hashes.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsLoaded() const noexcept {
|
||||
|
|
@ -56,7 +57,8 @@ public:
|
|||
std::string path;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u64 hash;
|
||||
std::vector<u64> hashes;
|
||||
std::mutex decode_mutex;
|
||||
CustomPixelFormat format;
|
||||
CustomFileFormat file_format;
|
||||
std::vector<u8> data;
|
||||
|
|
@ -67,6 +69,7 @@ struct Material {
|
|||
u32 width;
|
||||
u32 height;
|
||||
u64 size;
|
||||
u64 hash;
|
||||
CustomPixelFormat format;
|
||||
std::array<CustomTexture*, MAX_MAPS> textures;
|
||||
std::atomic<DecodeState> state{};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue