cached_surface: Remove custom texture logic

* Makes things more complicated and is in the way. It's probably already
broken by recent changes, so I'll need to reimplement it anyway
This commit is contained in:
emufan4568
2022-09-09 21:29:23 +03:00
committed by GPUCode
parent 5f8a884c2c
commit 77a99506cb
8 changed files with 61 additions and 196 deletions

View File

@ -89,12 +89,18 @@ public:
u8* GetPtr() {
return cptr;
}
std::byte* GetBytes() {
return reinterpret_cast<std::byte*>(cptr);
}
operator const u8*() const {
return cptr;
}
const u8* GetPtr() const {
return cptr;
}
const std::byte* GetBytes() const {
return reinterpret_cast<const std::byte*>(cptr);
}
std::size_t GetSize() const {
return csize;
}

View File

@ -20,69 +20,41 @@ namespace OpenGL {
CachedSurface::~CachedSurface() {
if (texture.handle) {
auto tag = is_custom ? HostTextureTag{PixelFormat::RGBA8,
custom_tex_info.width, custom_tex_info.height}
: HostTextureTag{pixel_format, GetScaledWidth(),
GetScaledHeight()};
const auto tag = HostTextureTag{pixel_format, GetScaledWidth(), GetScaledHeight()};
owner.host_texture_recycler.emplace(tag, std::move(texture));
}
}
MICROPROFILE_DEFINE(RasterizerCache_SurfaceLoad, "RasterizerCache", "Surface Load", MP_RGB(128, 192, 64));
void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
ASSERT(type != SurfaceType::Fill);
const bool need_swap =
GLES && (pixel_format == PixelFormat::RGBA8 || pixel_format == PixelFormat::RGB8);
DEBUG_ASSERT(load_start >= addr && load_end <= end);
u8* texture_ptr = VideoCore::g_memory->GetPhysicalPointer(addr);
if (texture_ptr == nullptr) {
auto texture_ptr = VideoCore::g_memory->GetPhysicalRef(load_start);
if (!texture_ptr) {
return;
}
// TODO: Should probably be done in ::Memory:: and check for other regions too
if (load_start < Memory::VRAM_VADDR_END && load_end > Memory::VRAM_VADDR_END)
load_end = Memory::VRAM_VADDR_END;
if (load_start < Memory::VRAM_VADDR && load_end > Memory::VRAM_VADDR)
load_start = Memory::VRAM_VADDR;
ASSERT(load_start >= addr && load_end <= end);
const u32 start_offset = load_start - addr;
const u32 byte_size = width * height * GetBytesPerPixel(pixel_format);
const u32 upload_size = std::clamp(load_end - load_start, 0u, static_cast<u32>(texture_ptr.GetSize()));
const u32 texture_size = width * height * GetBytesPerPixel(pixel_format);
const auto upload_data = std::span{texture_ptr.GetBytes(), upload_size};
if (gl_buffer.empty()) {
gl_buffer.resize(byte_size);
gl_buffer.resize(texture_size);
}
MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad);
if (!is_tiled) {
ASSERT(type == SurfaceType::Color);
if (need_swap) {
// TODO(liushuyu): check if the byteswap here is 100% correct
// cannot fully test this
if (pixel_format == PixelFormat::RGBA8) {
for (std::size_t i = start_offset; i < load_end - addr; i += 4) {
gl_buffer[i] = (std::byte)texture_ptr[i + 3];
gl_buffer[i + 1] = (std::byte)texture_ptr[i + 2];
gl_buffer[i + 2] = (std::byte)texture_ptr[i + 1];
gl_buffer[i + 3] = (std::byte)texture_ptr[i];
}
} else if (pixel_format == PixelFormat::RGB8) {
for (std::size_t i = start_offset; i < load_end - addr; i += 3) {
gl_buffer[i] = (std::byte)texture_ptr[i + 2];
gl_buffer[i + 1] = (std::byte)texture_ptr[i + 1];
gl_buffer[i + 2] = (std::byte)texture_ptr[i];
}
}
if (pixel_format == PixelFormat::RGBA8 && GLES) {
Pica::Texture::ConvertABGRToRGBA(upload_data, gl_buffer);
} else if (pixel_format == PixelFormat::RGB8 && GLES) {
Pica::Texture::ConvertBGRToRGB(upload_data, gl_buffer);
} else {
std::memcpy(gl_buffer.data() + start_offset, texture_ptr + start_offset, load_end - load_start);
std::memcpy(gl_buffer.data() + load_start - addr, texture_ptr, upload_size);
}
} else {
std::span<std::byte> texture_data{(std::byte*)texture_ptr, byte_size};
UnswizzleTexture(*this, load_start, load_end, texture_data, gl_buffer);
UnswizzleTexture(*this, load_start, load_end, upload_data, gl_buffer);
}
}
@ -150,92 +122,6 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
}
}
bool CachedSurface::LoadCustomTexture(u64 tex_hash) {
Core::System& system = Core::System::GetInstance();
auto& custom_tex_cache = system.CustomTexCache();
const auto& image_interface = system.GetImageInterface();
if (custom_tex_cache.IsTextureCached(tex_hash)) {
custom_tex_info = custom_tex_cache.LookupTexture(tex_hash);
return true;
}
if (!custom_tex_cache.CustomTextureExists(tex_hash)) {
return false;
}
const auto& path_info = custom_tex_cache.LookupTexturePathInfo(tex_hash);
if (!image_interface->DecodePNG(custom_tex_info.tex, custom_tex_info.width,
custom_tex_info.height, path_info.path)) {
LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path);
return false;
}
if (std::popcount(custom_tex_info.width) != 1 || std::popcount(custom_tex_info.height) != 1) {
LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path);
return false;
}
LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path);
Common::FlipRGBA8Texture(custom_tex_info.tex, custom_tex_info.width, custom_tex_info.height);
custom_tex_cache.CacheTexture(tex_hash, custom_tex_info.tex, custom_tex_info.width,
custom_tex_info.height);
return true;
}
void CachedSurface::DumpTexture(GLuint target_tex, u64 tex_hash) {
// Make sure the texture size is a power of 2
// If not, the surface is actually a framebuffer
if (std::popcount(width) != 1 || std::popcount(height) != 1) {
LOG_WARNING(Render_OpenGL, "Not dumping {:016X} because size isn't a power of 2 ({}x{})",
tex_hash, width, height);
return;
}
// Dump texture to RGBA8 and encode as PNG
Core::System& system = Core::System::GetInstance();
const auto& image_interface = system.GetImageInterface();
auto& custom_tex_cache = system.CustomTexCache();
std::string dump_path =
fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
system.Kernel().GetCurrentProcess()->codeset->program_id);
if (!FileUtil::CreateFullPath(dump_path)) {
LOG_ERROR(Render, "Unable to create {}", dump_path);
return;
}
dump_path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash, pixel_format);
if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(dump_path)) {
custom_tex_cache.SetTextureDumped(tex_hash);
LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path);
std::vector<u8> decoded_texture;
decoded_texture.resize(width * height * 4);
OpenGLState state = OpenGLState::GetCurState();
GLuint old_texture = state.texture_units[0].texture_2d;
state.Apply();
/*
GetTexImageOES is used even if not using OpenGL ES to work around a small issue that
happens if using custom textures with texture dumping at the same.
Let's say there's 2 textures that are both 32x32 and one of them gets replaced with a
higher quality 256x256 texture. If the 256x256 texture is displayed first and the
32x32 texture gets uploaded to the same underlying OpenGL texture, the 32x32 texture
will appear in the corner of the 256x256 texture. If texture dumping is enabled and
the 32x32 is undumped, Citra will attempt to dump it. Since the underlying OpenGL
texture is still 256x256, Citra crashes because it thinks the texture is only 32x32.
GetTexImageOES conveniently only dumps the specified region, and works on both
desktop and ES.
*/
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
height, width, &decoded_texture[0]);
state.texture_units[0].texture_2d = old_texture;
state.Apply();
Common::FlipRGBA8Texture(decoded_texture, width, height);
if (!image_interface->EncodePNG(dump_path, decoded_texture, width, height))
LOG_ERROR(Render_OpenGL, "Failed to save decoded texture");
}
}
MICROPROFILE_DEFINE(RasterizerCache_TextureUL, "RasterizerCache", "Texture Upload", MP_RGB(128, 192, 64));
void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
if (type == SurfaceType::Fill) {
@ -245,16 +131,6 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
MICROPROFILE_SCOPE(RasterizerCache_TextureUL);
ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
u64 tex_hash = 0;
if (Settings::values.dump_textures || Settings::values.custom_textures) {
tex_hash = Common::ComputeHash64(gl_buffer.data(), gl_buffer.size());
}
if (Settings::values.custom_textures) {
is_custom = LoadCustomTexture(tex_hash);
}
// Load data from memory to the surface
GLint x0 = static_cast<GLint>(rect.left);
GLint y0 = static_cast<GLint>(rect.bottom);
@ -265,16 +141,11 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
// surface
OGLTexture unscaled_tex;
if (IsScaled()) {
if (res_scale != 1) {
x0 = 0;
y0 = 0;
if (is_custom) {
unscaled_tex =
owner.AllocateSurfaceTexture(PixelFormat::RGBA8, custom_tex_info.width, custom_tex_info.height);
} else {
unscaled_tex = owner.AllocateSurfaceTexture(pixel_format, rect.GetWidth(), rect.GetHeight());
}
unscaled_tex = owner.AllocateSurfaceTexture(pixel_format, rect.GetWidth(), rect.GetHeight());
target_tex = unscaled_tex.handle;
}
@ -289,34 +160,15 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
if (is_custom) {
if (res_scale == 1) {
texture = owner.AllocateSurfaceTexture(PixelFormat::RGBA8,
custom_tex_info.width, custom_tex_info.height);
cur_state.texture_units[0].texture_2d = texture.handle;
cur_state.Apply();
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
// Always going to be using rgba8
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(custom_tex_info.width));
glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, custom_tex_info.width, custom_tex_info.height,
GL_RGBA, GL_UNSIGNED_BYTE, custom_tex_info.tex.data());
} else {
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
}
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
if (Settings::values.dump_textures && !is_custom) {
DumpTexture(target_tex, tex_hash);
}
cur_state.texture_units[0].texture_2d = old_tex;
cur_state.Apply();
@ -328,10 +180,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect) {
scaled_rect.right *= res_scale;
scaled_rect.bottom *= res_scale;
const u32 width = is_custom ? custom_tex_info.width : rect.GetWidth();
const u32 height = is_custom ? custom_tex_info.height : rect.GetHeight();
const Common::Rectangle<u32> from_rect{0, height, width, 0};
const Common::Rectangle<u32> from_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
if (!owner.texture_filterer->Filter(unscaled_tex, from_rect, texture, scaled_rect, type)) {
const TextureBlit texture_blit = {
.surface_type = type,
@ -374,7 +223,7 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect) {
const u32 buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
if (IsScaled()) {
if (res_scale != 1) {
auto scaled_rect = rect;
scaled_rect.left *= res_scale;
scaled_rect.top *= res_scale;

View File

@ -55,10 +55,6 @@ public:
void LoadGLBuffer(PAddr load_start, PAddr load_end);
void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
/// Custom texture loading and dumping
bool LoadCustomTexture(u64 tex_hash);
void DumpTexture(GLuint target_tex, u64 tex_hash);
/// Upload/Download data in gl_buffer in/to this surface's texture
void UploadGLTexture(Common::Rectangle<u32> rect);
void DownloadGLTexture(const Common::Rectangle<u32>& rect);
@ -114,10 +110,6 @@ public:
std::array<std::shared_ptr<SurfaceWatcher>, 7> level_watchers;
u32 max_level = 0;
// Information about custom textures
bool is_custom = false;
Core::CustomTexInfo custom_tex_info;
private:
RasterizerCache& owner;
TextureRuntime& runtime;

View File

@ -113,7 +113,12 @@ static void MortonCopy(u32 stride, u32 height,
}
};
u8* tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start);
u8* tile_buffer;
if constexpr (morton_to_linear) {
tile_buffer = (u8*)tiled_buffer.data();
} else {
tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start);
}
// If during a texture download the start coordinate is inside a tile, swizzle
// the tile to a temporary buffer and copy the part we are interested in

View File

@ -421,7 +421,7 @@ Surface RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf
// Allocate more mipmap level if necessary
if (surface->max_level < max_level) {
if (surface->is_custom || !texture_filterer->IsNull()) {
if (!texture_filterer->IsNull()) {
// TODO: proper mipmap support for custom textures
runtime.GenerateMipmaps(surface->texture, max_level);
}
@ -456,7 +456,7 @@ Surface RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
}
if (!surface->is_custom && texture_filterer->IsNull()) {
if (texture_filterer->IsNull()) {
const auto src_rect = level_surface->GetScaledRect();
const auto dst_rect = surface_params.GetScaledRect();
const TextureBlit texture_blit = {

View File

@ -77,19 +77,16 @@ void UnswizzleTexture(const SurfaceParams& params, u32 load_start, u32 load_end,
tex_info.SetDefaultStride();
tex_info.physical_address = params.addr;
const SurfaceInterval load_interval(load_start, load_end);
const auto rect = params.GetSubRect(params.FromInterval(load_interval));
DEBUG_ASSERT(params.FromInterval(load_interval).GetInterval() == load_interval);
const u32 start_pixel = params.PixelsInBytes(load_start - params.addr);
const u8* source_data = reinterpret_cast<const u8*>(source_tiled.data());
for (u32 y = rect.bottom; y < rect.top; y++) {
for (u32 x = rect.left; x < rect.right; x++) {
auto vec4 =
Pica::Texture::LookupTexture(source_data, x, params.height - 1 - y, tex_info);
const std::size_t offset = (x + (params.width * y)) * 4;
std::memcpy(dest_linear.data() + offset, vec4.AsArray(), 4);
}
for (u32 i = 0; i < params.PixelsInBytes(load_end - load_start); i++) {
const u32 x = (i + start_pixel) % params.stride;
const u32 y = (i + start_pixel) / params.stride;
auto vec4 = Pica::Texture::LookupTexture(source_data, x, params.height - 1 - y, tex_info);
std::memcpy(dest_linear.data() + i * sizeof(u32), vec4.AsArray(), sizeof(u32));
}
} else {
const u32 func_index = static_cast<u32>(params.pixel_format);
const MortonFunc UnswizzleImpl = UNSWIZZLE_TABLE[func_index];

View File

@ -222,6 +222,14 @@ TextureInfo TextureInfo::FromPicaRegister(const TexturingRegs::TextureConfig& co
return info;
}
void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> dest) {
for (std::size_t i = 0; i < source.size(); i += 3) {
dest[i] = source[i + 2];
dest[i + 1] = source[i + 1];
dest[i + 2] = source[i];
}
}
void ConvertBGRToRGBA(std::span<const std::byte> source, std::span<std::byte> dest) {
u32 j = 0;
for (u32 i = 0; i < source.size(); i += 3) {

View File

@ -55,6 +55,14 @@ Common::Vec4<u8> LookupTexture(const u8* source, unsigned int x, unsigned int y,
Common::Vec4<u8> LookupTexelInTile(const u8* source, unsigned int x, unsigned int y,
const TextureInfo& info, bool disable_alpha);
/**
* Converts pixel data encoded in BGR format to RGBA
*
* @param source Span to the source pixel data
* @param dest Span to the destination pixel data
*/
void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> dest);
/**
* Converts pixel data encoded in BGR format to RGBA
*