implement custom texture preload

This commit is contained in:
Khangaroo 2019-08-06 11:54:12 -04:00 committed by James Rowe
parent 657a129b60
commit 59b475a4b9
19 changed files with 139 additions and 126 deletions

View File

@ -165,6 +165,8 @@ void Config::ReadValues() {
// Utility // Utility
Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false); Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false);
Settings::values.custom_textures = sdl2_config->GetBoolean("Utility", "custom_textures", false); Settings::values.custom_textures = sdl2_config->GetBoolean("Utility", "custom_textures", false);
Settings::values.preload_textures =
sdl2_config->GetBoolean("Utility", "preload_textures", false);
// Audio // Audio
Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false); Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false);

View File

@ -186,6 +186,10 @@ dump_textures =
# 0 (default): Off, 1: On # 0 (default): Off, 1: On
custom_textures = custom_textures =
# Loads all custom textures into memory before booting.
# 0 (default): Off, 1: On
preload_textures =
[Audio] [Audio]
# Whether or not to enable DSP LLE # Whether or not to enable DSP LLE
# 0 (default): No, 1: Yes # 0 (default): No, 1: Yes

View File

@ -17,8 +17,6 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
ui->setupUi(this); ui->setupUi(this);
SetConfiguration(); SetConfiguration();
ui->layoutBox->setEnabled(!Settings::values.custom_layout);
ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked()); ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked());
connect(ui->toggle_hw_renderer, &QCheckBox::toggled, ui->hw_renderer_group, connect(ui->toggle_hw_renderer, &QCheckBox::toggled, ui->hw_renderer_group,
&QWidget::setEnabled); &QWidget::setEnabled);
@ -36,21 +34,6 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
} }
}); });
#endif #endif
connect(ui->render_3d_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) {
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex) ==
Settings::StereoRenderOption::Anaglyph);
});
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
const QColor new_bg_color = QColorDialog::getColor(bg_color);
if (!new_bg_color.isValid()) {
return;
}
UpdateBackgroundColorButton(new_bg_color);
});
} }
ConfigureGraphics::~ConfigureGraphics() = default; ConfigureGraphics::~ConfigureGraphics() = default;
@ -60,15 +43,6 @@ void ConfigureGraphics::SetConfiguration() {
ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader); ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader);
ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul); ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul);
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor);
ui->render_3d_combobox->setCurrentIndex(static_cast<int>(Settings::values.render_3d));
ui->factor_3d->setValue(Settings::values.factor_3d);
updateShaders(Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph);
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode);
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
ui->swap_screen->setChecked(Settings::values.swap_screen);
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
Settings::values.bg_blue));
} }
void ConfigureGraphics::ApplyConfiguration() { void ConfigureGraphics::ApplyConfiguration() {
@ -76,49 +50,6 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked(); Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked();
Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked(); Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked();
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
Settings::values.resolution_factor =
static_cast<u16>(ui->resolution_factor_combobox->currentIndex());
Settings::values.render_3d =
static_cast<Settings::StereoRenderOption>(ui->render_3d_combobox->currentIndex());
Settings::values.factor_3d = ui->factor_3d->value();
Settings::values.pp_shader_name =
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
Settings::values.filter_mode = ui->toggle_linear_filter->isChecked();
Settings::values.layout_option =
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked();
Settings::values.dump_textures = ui->toggle_dump_textures->isChecked();
Settings::values.custom_textures = ui->toggle_custom_textures->isChecked();
Settings::values.bg_red = static_cast<float>(bg_color.redF());
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
}
void ConfigureGraphics::updateShaders(bool anaglyph) {
ui->shader_combobox->clear();
if (anaglyph)
ui->shader_combobox->addItem("dubois (builtin)");
else
ui->shader_combobox->addItem("none (builtin)");
ui->shader_combobox->setCurrentIndex(0);
for (const auto& shader : OpenGL::GetPostProcessingShaderList(anaglyph)) {
ui->shader_combobox->addItem(QString::fromStdString(shader));
if (Settings::values.pp_shader_name == shader)
ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1);
}
}
void ConfigureGraphics::UpdateBackgroundColorButton(const QColor& color) {
bg_color = color;
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
} }
void ConfigureGraphics::RetranslateUI() { void ConfigureGraphics::RetranslateUI() {

View File

@ -354,6 +354,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="toggle_preload_textures">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load all custom textures into memory on boot, instead of loading them when the game requires them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Preload Custom Textures</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -161,7 +161,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
continue; continue;
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
connect(button_map[button_id], &QPushButton::clicked, [=]() { connect(button_map[button_id], &QPushButton::clicked, [=]() {
HandleClick(button_map[button_id], HandleClick(
button_map[button_id],
[=](const Common::ParamPackage& params) { [=](const Common::ParamPackage& params) {
buttons_param[button_id] = params; buttons_param[button_id] = params;
// If the user closes the dialog, the changes are reverted in // If the user closes the dialog, the changes are reverted in
@ -198,7 +199,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
Qt::CustomContextMenu); Qt::CustomContextMenu);
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() { connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() {
HandleClick(analog_map_buttons[analog_id][sub_button_id], HandleClick(
analog_map_buttons[analog_id][sub_button_id],
[=](const Common::ParamPackage& params) { [=](const Common::ParamPackage& params) {
SetAnalogButton(params, analogs_param[analog_id], SetAnalogButton(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]); analog_sub_buttons[sub_button_id]);

View File

@ -84,6 +84,8 @@ add_library(common STATIC
swap.h swap.h
telemetry.cpp telemetry.cpp
telemetry.h telemetry.h
texture.cpp
texture.h
thread.cpp thread.cpp
thread.h thread.h
thread_queue_list.h thread_queue_list.h

View File

@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock lock{instance->mutex}; std::unique_lock lock{instance->mutex};
--instance->count; --instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock)); std::notify_all_at_thread_exit(instance->cv, std::move(lock));
}) }).detach();
.detach();
} }
} // namespace Common } // namespace Common

22
src/common/texture.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
namespace Common {
void FlipRGBA8Texture(std::vector<u8>& tex, u64 width, u64 height) {
ASSERT(tex.size() == width * height * 4);
const u64 line_size = width * 4;
u8* temp_row = new u8[line_size];
u32 offset_1;
u32 offset_2;
for (u64 line = 0; line < height / 2; line++) {
offset_1 = line * line_size;
offset_2 = (height - line - 1) * line_size;
// Swap lines
std::memcpy(temp_row, &tex[offset_1], line_size);
std::memcpy(&tex[offset_1], &tex[offset_2], line_size);
std::memcpy(&tex[offset_2], temp_row, line_size);
}
delete[] temp_row;
}
} // namespace Common

8
src/common/texture.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common {
void FlipRGBA8Texture(std::vector<u8>& tex, u64 width, u64 height);
}

View File

@ -460,7 +460,7 @@ endif()
create_target_directory_groups(core) create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives lodepng)
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service) target_link_libraries(core PRIVATE web_service)

View File

@ -4,10 +4,12 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <lodepng.h>
#include "audio_core/dsp_interface.h" #include "audio_core/dsp_interface.h"
#include "audio_core/hle/hle.h" #include "audio_core/hle/hle.h"
#include "audio_core/lle/lle.h" #include "audio_core/lle/lle.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/texture.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dynarmic/arm_dynarmic.h"
@ -159,7 +161,52 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
perf_stats = std::make_unique<PerfStats>(title_id); perf_stats = std::make_unique<PerfStats>(title_id);
======= =======
custom_tex_cache = std::make_unique<Core::CustomTexCache>(); custom_tex_cache = std::make_unique<Core::CustomTexCache>();
<<<<<<< HEAD
>>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory >>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory
=======
if (Settings::values.preload_textures) {
// Custom textures are currently stored as
// load/textures/[TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png
const std::string load_path =
fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
process->codeset->program_id);
if (FileUtil::Exists(load_path)) {
FileUtil::FSTEntry texture_files;
FileUtil::ScanDirectoryTree(load_path, texture_files);
for (const auto& file : texture_files.children) {
if (file.isDirectory)
continue;
if (file.virtualName.substr(0, 5) != "tex1_")
continue;
u32 width;
u32 height;
u64 hash;
u32 format; // unused
// TODO: more modern way of doing this
if (std::sscanf(file.virtualName.c_str(), "tex1_%ux%u_%llX_%u.png", &width, &height,
&hash, &format) == 4) {
u32 png_width;
u32 png_height;
std::vector<u8> decoded_png;
u32 lodepng_ret =
lodepng::decode(decoded_png, png_width, png_height, file.physicalName);
if (lodepng_ret)
LOG_CRITICAL(Render_OpenGL, "Failed to preload custom texture: {}",
lodepng_error_text(lodepng_ret));
else {
LOG_INFO(Render_OpenGL, "Preloaded custom texture from {}",
file.physicalName);
Common::FlipRGBA8Texture(decoded_png, png_width, png_height);
custom_tex_cache->CacheTexture(hash, decoded_png, png_width, png_height);
}
}
}
}
}
>>>>>>> 015582b2... implement custom texture preload
status = ResultStatus::Success; status = ResultStatus::Success;
m_emu_window = &emu_window; m_emu_window = &emu_window;
m_filepath = filepath; m_filepath = filepath;
@ -195,8 +242,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
timing = std::make_unique<Timing>(); timing = std::make_unique<Timing>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, kernel = std::make_unique<Kernel::KernelSystem>(
[this] { PrepareReschedule(); }, system_mode); *memory, *timing, [this] { PrepareReschedule(); }, system_mode);
if (Settings::values.use_cpu_jit) { if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64

View File

@ -1340,7 +1340,8 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_
case CecDataPathType::MboxData: case CecDataPathType::MboxData:
case CecDataPathType::MboxIcon: case CecDataPathType::MboxIcon:
case CecDataPathType::MboxTitle: case CecDataPathType::MboxTitle:
default: {} default: {
}
} }
} }

View File

@ -41,8 +41,8 @@ public:
return s_instance; return s_instance;
} }
void StartPlayback(const std::string& movie_file, void StartPlayback(
std::function<void()> completion_callback = [] {}); const std::string& movie_file, std::function<void()> completion_callback = [] {});
void StartRecording(const std::string& movie_file); void StartRecording(const std::string& movie_file);
/// Prepare to override the clock before playing back movies /// Prepare to override the clock before playing back movies

View File

@ -172,6 +172,7 @@ struct Values {
bool dump_textures; bool dump_textures;
bool custom_textures; bool custom_textures;
bool preload_textures;
// Audio // Audio
bool enable_dsp_lle; bool enable_dsp_lle;

View File

@ -217,8 +217,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
success_callback(); success_callback();
else else
failure_callback(); failure_callback();
}) }).detach();
.detach();
} }
CalibrationConfigurationJob::CalibrationConfigurationJob( CalibrationConfigurationJob::CalibrationConfigurationJob(
@ -269,8 +268,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
complete_event.Wait(); complete_event.Wait();
socket.Stop(); socket.Stop();
worker_thread.join(); worker_thread.join();
}) }).detach();
.detach();
} }
CalibrationConfigurationJob::~CalibrationConfigurationJob() { CalibrationConfigurationJob::~CalibrationConfigurationJob() {

View File

@ -17,7 +17,8 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
timing = std::make_unique<Core::Timing>(); timing = std::make_unique<Core::Timing>();
memory = std::make_unique<Memory::MemorySystem>(); memory = std::make_unique<Memory::MemorySystem>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0); kernel = std::make_unique<Kernel::KernelSystem>(
*memory, *timing, [] {}, 0);
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;

View File

@ -23,7 +23,8 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
Core::Timing timing; Core::Timing timing;
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(
memory, timing, [] {}, 0);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr); HLERequestContext context(kernel, std::move(server), nullptr);
@ -235,7 +236,8 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
Core::Timing timing; Core::Timing timing;
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(
memory, timing, [] {}, 0);
auto [server, client] = kernel.CreateSessionPair(); auto [server, client] = kernel.CreateSessionPair();
HLERequestContext context(kernel, std::move(server), nullptr); HLERequestContext context(kernel, std::move(server), nullptr);

View File

@ -13,7 +13,8 @@
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
Core::Timing timing; Core::Timing timing;
Memory::MemorySystem memory; Memory::MemorySystem memory;
Kernel::KernelSystem kernel(memory, timing, [] {}, 0); Kernel::KernelSystem kernel(
memory, timing, [] {}, 0);
SECTION("these regions should not be mapped on an empty process") { SECTION("these regions should not be mapped on an empty process") {
auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);

View File

@ -22,6 +22,7 @@
#include "common/math_util.h" #include "common/math_util.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/texture.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "core/core.h" #include "core/core.h"
#include "core/custom_tex_cache.h" #include "core/custom_tex_cache.h"
@ -855,25 +856,6 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
} }
} }
// TODO: move this function to a better place
void FlipRGBA8Texture(std::vector<u8>& tex, u64 width, u64 height) {
ASSERT(tex.size() == width * height * 4);
const u64 line_size = width * 4;
// Thanks MSVC for not being able to make variable length arrays
u8* temp_row = new u8[line_size];
u32 offset_1;
u32 offset_2;
for (u64 line = 0; line < height / 2; line++) {
offset_1 = line * line_size;
offset_2 = (height - line - 1) * line_size;
// Swap lines
std::memcpy(temp_row, &tex[offset_1], line_size);
std::memcpy(&tex[offset_1], &tex[offset_2], line_size);
std::memcpy(&tex[offset_2], temp_row, line_size);
}
delete[] temp_row;
}
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64)); MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint read_fb_handle, void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint read_fb_handle,
GLuint draw_fb_handle) { GLuint draw_fb_handle) {
@ -893,7 +875,8 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
u32 png_width = 0; u32 png_width = 0;
u32 png_height = 0; u32 png_height = 0;
u64 tex_hash = 0; u64 tex_hash = 0;
Common::Rectangle custom_rect = rect; // Required for rect to function properly with custom textures Common::Rectangle custom_rect =
rect; // Required for rect to function properly with custom textures
if (Settings::values.dump_textures || Settings::values.custom_textures) if (Settings::values.dump_textures || Settings::values.custom_textures)
tex_hash = Common::ComputeHash64(gl_buffer.get(), gl_buffer_size); tex_hash = Common::ComputeHash64(gl_buffer.get(), gl_buffer_size);
@ -913,7 +896,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
lodepng_error_text(lodepng_ret)); lodepng_error_text(lodepng_ret));
else { else {
LOG_INFO(Render_OpenGL, "Loaded custom texture from {}", load_path); LOG_INFO(Render_OpenGL, "Loaded custom texture from {}", load_path);
FlipRGBA8Texture(decoded_png, png_width, png_height); Common::FlipRGBA8Texture(decoded_png, png_width, png_height);
custom_tex_cache.CacheTexture(tex_hash, decoded_png, png_width, png_height); custom_tex_cache.CacheTexture(tex_hash, decoded_png, png_width, png_height);
use_custom_tex = true; use_custom_tex = true;
} }
@ -1014,7 +997,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
glBindTexture(GL_TEXTURE_2D, target_tex); glBindTexture(GL_TEXTURE_2D, target_tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
FlipRGBA8Texture(decoded_texture, width, height); Common::FlipRGBA8Texture(decoded_texture, width, height);
u32 png_error = lodepng::encode(dump_path, decoded_texture, width, height); u32 png_error = lodepng::encode(dump_path, decoded_texture, width, height);
if (png_error) { if (png_error) {
LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture! {}", LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture! {}",
@ -1034,8 +1017,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
scaled_rect.bottom *= res_scale; scaled_rect.bottom *= res_scale;
BlitTextures(unscaled_tex.handle, {0, custom_rect.GetHeight(), custom_rect.GetWidth(), 0}, BlitTextures(unscaled_tex.handle, {0, custom_rect.GetHeight(), custom_rect.GetWidth(), 0},
texture.handle, texture.handle, scaled_rect, type, read_fb_handle, draw_fb_handle);
scaled_rect, type, read_fb_handle, draw_fb_handle);
} }
InvalidateAllWatcher(); InvalidateAllWatcher();