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:
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
*
|
||||
|
Reference in New Issue
Block a user