citra/src/video_core/custom_textures/material.cpp
GPUCode 2e655f73b8
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
2023-05-07 02:34:28 +03:00

155 lines
4.7 KiB
C++

// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/texture.h"
#include "core/frontend/image_interface.h"
#include "video_core/custom_textures/material.h"
namespace VideoCore {
namespace {
CustomPixelFormat ToCustomPixelFormat(ddsktx_format format) {
switch (format) {
case DDSKTX_FORMAT_RGBA8:
return CustomPixelFormat::RGBA8;
case DDSKTX_FORMAT_BC1:
return CustomPixelFormat::BC1;
case DDSKTX_FORMAT_BC3:
return CustomPixelFormat::BC3;
case DDSKTX_FORMAT_BC5:
return CustomPixelFormat::BC5;
case DDSKTX_FORMAT_BC7:
return CustomPixelFormat::BC7;
case DDSKTX_FORMAT_ASTC4x4:
return CustomPixelFormat::ASTC4;
case DDSKTX_FORMAT_ASTC6x6:
return CustomPixelFormat::ASTC6;
case DDSKTX_FORMAT_ASTC8x6:
return CustomPixelFormat::ASTC8;
default:
LOG_ERROR(Common, "Unknown dds/ktx pixel format {}", format);
return CustomPixelFormat::RGBA8;
}
}
std::string_view MapTypeName(MapType type) {
switch (type) {
case MapType::Color:
return "Color";
case MapType::Normal:
return "Normal";
default:
return "Invalid";
}
}
} // Anonymous namespace
CustomTexture::CustomTexture(Frontend::ImageInterface& image_interface_)
: image_interface{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()) {
LOG_CRITICAL(Render, "Failed to open custom texture: {}", path);
return;
}
switch (file_format) {
case CustomFileFormat::PNG:
LoadPNG(input, flip_png);
break;
case CustomFileFormat::DDS:
case CustomFileFormat::KTX:
LoadDDS(input);
break;
default:
LOG_ERROR(Render, "Unknown file format {}", file_format);
}
}
void CustomTexture::LoadPNG(std::span<const u8> input, bool flip_png) {
if (!image_interface.DecodePNG(data, width, height, input)) {
LOG_ERROR(Render, "Failed to decode png: {}", path);
return;
}
if (flip_png) {
Common::FlipRGBA8Texture(data, width, height);
}
format = CustomPixelFormat::RGBA8;
}
void CustomTexture::LoadDDS(std::span<const u8> input) {
ddsktx_format dds_format{};
image_interface.DecodeDDS(data, width, height, dds_format, input);
format = ToCustomPixelFormat(dds_format);
}
void Material::LoadFromDisk(bool flip_png) noexcept {
if (IsDecoded()) {
return;
}
for (CustomTexture* const texture : textures) {
if (!texture || texture->IsLoaded()) {
continue;
}
texture->LoadFromDisk(flip_png);
size += texture->data.size();
LOG_DEBUG(Render, "Loading {} map {}", MapTypeName(texture->type), texture->path);
}
if (!textures[0]) {
LOG_ERROR(Render, "Unable to create material without color texture!");
state = DecodeState::Failed;
return;
}
width = textures[0]->width;
height = textures[0]->height;
format = textures[0]->format;
for (const CustomTexture* texture : textures) {
if (!texture) {
continue;
}
if (texture->width != width || texture->height != height) {
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, hash, texture->width,
texture->height, width, height);
state = DecodeState::Failed;
return;
}
if (texture->format != format) {
LOG_ERROR(
Render, "{} map {} is stored with {} format which does not match color format {}",
MapTypeName(texture->type), texture->path,
CustomPixelFormatAsString(texture->format), CustomPixelFormatAsString(format));
state = DecodeState::Failed;
return;
}
}
state = DecodeState::Decoded;
}
void Material::AddMapTexture(CustomTexture* texture) noexcept {
const std::size_t index = static_cast<std::size_t>(texture->type);
if (textures[index]) {
LOG_ERROR(Render, "Textures {} and {} are assigned to the same material, ignoring!",
textures[index]->path, texture->path);
return;
}
textures[index] = texture;
}
} // namespace VideoCore