From f14e973a271b1c2982bf88fce482f5c5d8b7c38a Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Thu, 2 Apr 2020 22:42:50 -0500 Subject: [PATCH] Texture Filtering v2 (#5166) * video_core/renderer_opengl: Move SurfaceParams into its own file Some of its enums are needed outside of the rasterizer cache and trying to use it caused circular dependencies. * video_core/renderer_opengl: Overhaul the texture filter framework This should make it less intrusive. Now texture filtering doesn't have any mutable global state. The texture filters now always upscale to the internal rendering resolution. This simplifies the logic in UploadGLTexture and it simply takes the role of BlitTextures at the end of the function. This also prevent extra blitting required when uploading to a framebuffer surface with a mismatched size. * video_core/renderer_opengl: Use generated mipmaps for filtered textures The filtered guest mipmaps often looked terrible. * core/settings: Remove texture filter factor * sdl/config: Remove texture filter factor * qt/config: Remove texture filter factor --- src/citra/config.cpp | 2 - src/citra/default_ini.h | 3 +- src/citra_qt/configuration/config.cpp | 4 - .../configuration/configure_enhancements.cpp | 23 +- .../configuration/configure_enhancements.ui | 36 --- src/core/settings.cpp | 6 +- src/core/settings.h | 1 - src/video_core/CMakeLists.txt | 8 +- .../renderer_opengl/gl_rasterizer_cache.cpp | 217 ++--------------- .../renderer_opengl/gl_rasterizer_cache.h | 223 +---------------- src/video_core/renderer_opengl/gl_state.h | 14 +- .../renderer_opengl/gl_surface_params.cpp | 171 +++++++++++++ .../renderer_opengl/gl_surface_params.h | 229 ++++++++++++++++++ .../renderer_opengl/renderer_opengl.cpp | 7 +- .../anime4k/anime4k_ultrafast.cpp | 45 ++-- .../anime4k/anime4k_ultrafast.h | 22 +- .../texture_filters/anime4k/refine.frag | 4 +- .../texture_filters/bicubic/bicubic.cpp | 33 +-- .../texture_filters/bicubic/bicubic.h | 23 +- .../texture_filters/texture_filter_base.h | 26 ++ .../texture_filter_interface.h | 38 --- .../texture_filter_manager.cpp | 89 ------- .../texture_filters/texture_filter_manager.h | 55 ----- .../texture_filters/texture_filterer.cpp | 86 +++++++ .../texture_filters/texture_filterer.h | 39 +++ .../texture_filters/xbrz/xbrz_freescale.cpp | 30 +-- .../texture_filters/xbrz/xbrz_freescale.h | 19 +- src/video_core/video_core.cpp | 1 + src/video_core/video_core.h | 1 + 29 files changed, 676 insertions(+), 779 deletions(-) create mode 100644 src/video_core/renderer_opengl/gl_surface_params.cpp create mode 100644 src/video_core/renderer_opengl/gl_surface_params.h create mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filter_base.h delete mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filter_interface.h delete mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filter_manager.cpp delete mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filter_manager.h create mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp create mode 100644 src/video_core/renderer_opengl/texture_filters/texture_filterer.h diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 47270ab36..f8c3a4f94 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -132,8 +132,6 @@ void Config::ReadValues() { static_cast(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1)); Settings::values.texture_filter_name = sdl2_config->GetString("Renderer", "texture_filter_name", "none"); - Settings::values.texture_filter_factor = - sdl2_config->GetInteger("Renderer", "texture_filter_factor", 1); Settings::values.render_3d = static_cast( sdl2_config->GetInteger("Renderer", "render_3d", 0)); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 881700913..b7996d1cb 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -132,9 +132,8 @@ use_disk_shader_cache = # factor for the 3DS resolution resolution_factor = -# Texture filter name and scale factor +# Texture filter name texture_filter_name = -texture_filter_factor = # Turns on the frame limiter, which will limit frames output to the target game speed # 0: Off, 1: On (default) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index d139a89e9..0bc28a43a 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -454,8 +454,6 @@ void Config::ReadRendererValues() { ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none")) .toString() .toStdString(); - Settings::values.texture_filter_factor = - ReadSetting(QStringLiteral("texture_filter_factor"), 1).toInt(); qt_config->endGroup(); } @@ -893,8 +891,6 @@ void Config::SaveRendererValues() { WriteSetting(QStringLiteral("texture_filter_name"), QString::fromStdString(Settings::values.texture_filter_name), QStringLiteral("none")); - WriteSetting(QStringLiteral("texture_filter_factor"), Settings::values.texture_filter_factor, - 1); qt_config->endGroup(); } diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index e1c6b6bdd..f25b9c1e1 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -8,17 +8,14 @@ #include "core/settings.h" #include "ui_configure_enhancements.h" #include "video_core/renderer_opengl/post_processing_opengl.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h" +#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureEnhancements) { ui->setupUi(this); - for (const auto& filter : OpenGL::TextureFilterManager::TextureFilterMap()) - ui->texture_filter_combobox->addItem(QString::fromStdString(filter.first.data())); - - connect(ui->texture_filter_combobox, QOverload::of(&QComboBox::currentIndexChanged), this, - &ConfigureEnhancements::updateTextureFilter); + for (const auto& filter : OpenGL::TextureFilterer::GetFilterNames()) + ui->texture_filter_combobox->addItem(QString::fromStdString(filter.data())); SetConfiguration(); @@ -60,7 +57,6 @@ void ConfigureEnhancements::SetConfiguration() { ui->factor_3d->setValue(Settings::values.factor_3d); updateShaders(Settings::values.render_3d); ui->toggle_linear_filter->setChecked(Settings::values.filter_mode); - ui->texture_scale_spinbox->setValue(Settings::values.texture_filter_factor); int tex_filter_idx = ui->texture_filter_combobox->findText( QString::fromStdString(Settings::values.texture_filter_name)); if (tex_filter_idx == -1) { @@ -68,7 +64,6 @@ void ConfigureEnhancements::SetConfiguration() { } else { ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx); } - updateTextureFilter(tex_filter_idx); ui->layout_combobox->setCurrentIndex(static_cast(Settings::values.layout_option)); ui->swap_screen->setChecked(Settings::values.swap_screen); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_hw_shader && @@ -105,17 +100,6 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op } } -void ConfigureEnhancements::updateTextureFilter(int index) { - if (index == -1) - return; - ui->texture_filter_group->setEnabled(index != 0); - const auto& clamp = OpenGL::TextureFilterManager::TextureFilterMap() - .at(ui->texture_filter_combobox->currentText().toStdString()) - .clamp_scale; - ui->texture_scale_spinbox->setMinimum(clamp.min); - ui->texture_scale_spinbox->setMaximum(clamp.max); -} - void ConfigureEnhancements::RetranslateUI() { ui->retranslateUi(this); } @@ -130,7 +114,6 @@ void ConfigureEnhancements::ApplyConfiguration() { ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString(); Settings::values.filter_mode = ui->toggle_linear_filter->isChecked(); Settings::values.texture_filter_name = ui->texture_filter_combobox->currentText().toStdString(); - Settings::values.texture_filter_factor = ui->texture_scale_spinbox->value(); Settings::values.layout_option = static_cast(ui->layout_combobox->currentIndex()); Settings::values.swap_screen = ui->swap_screen->isChecked(); diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index d3193e9eb..909a98643 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -131,42 +131,6 @@ - - - - - 16 - - - 0 - - - 0 - - - 0 - - - - - - - Texture Scale Factor - - - - - - - 1 - - - - - - - - diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 13b1e965f..6af80205b 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -13,7 +13,6 @@ #include "core/hle/service/mic_u.h" #include "core/settings.h" #include "video_core/renderer_base.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h" #include "video_core/video_core.h" namespace Settings { @@ -38,9 +37,7 @@ void Apply() { VideoCore::g_renderer_bg_color_update_requested = true; VideoCore::g_renderer_sampler_update_requested = true; VideoCore::g_renderer_shader_update_requested = true; - - OpenGL::TextureFilterManager::GetInstance().SetTextureFilter(values.texture_filter_name, - values.texture_filter_factor); + VideoCore::g_texture_filter_update_requested = true; auto& system = Core::System::GetInstance(); if (system.IsPoweredOn()) { @@ -88,7 +85,6 @@ void LogSettings() { LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); LogSetting("Renderer_PostProcessingShader", Settings::values.pp_shader_name); LogSetting("Renderer_FilterMode", Settings::values.filter_mode); - LogSetting("Renderer_TextureFilterFactor", Settings::values.texture_filter_factor); LogSetting("Renderer_TextureFilterName", Settings::values.texture_filter_name); LogSetting("Stereoscopy_Render3d", static_cast(Settings::values.render_3d)); LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d); diff --git a/src/core/settings.h b/src/core/settings.h index aa7e781b5..980156663 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -148,7 +148,6 @@ struct Values { u16 resolution_factor; bool use_frame_limit; u16 frame_limit; - u16 texture_filter_factor; std::string texture_filter_name; LayoutOption layout_option; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index a7dbc42de..3553fe0a8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -43,6 +43,8 @@ add_library(video_core STATIC renderer_opengl/gl_state.h renderer_opengl/gl_stream_buffer.cpp renderer_opengl/gl_stream_buffer.h + renderer_opengl/gl_surface_params.cpp + renderer_opengl/gl_surface_params.h renderer_opengl/gl_vars.cpp renderer_opengl/gl_vars.h renderer_opengl/pica_to_gl.h @@ -54,9 +56,9 @@ add_library(video_core STATIC renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h renderer_opengl/texture_filters/bicubic/bicubic.cpp renderer_opengl/texture_filters/bicubic/bicubic.h - renderer_opengl/texture_filters/texture_filter_interface.h - renderer_opengl/texture_filters/texture_filter_manager.cpp - renderer_opengl/texture_filters/texture_filter_manager.h + renderer_opengl/texture_filters/texture_filter_base.h + renderer_opengl/texture_filters/texture_filterer.cpp + renderer_opengl/texture_filters/texture_filterer.h renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp renderer_opengl/texture_filters/xbrz/xbrz_freescale.h shader/debug_data.h diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 56d65133f..868bcaecd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -35,7 +35,7 @@ #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_vars.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h" +#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" #include "video_core/utils.h" #include "video_core/video_core.h" @@ -494,125 +494,6 @@ static bool FillSurface(const Surface& surface, const u8* fill_data, return true; } -SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const { - SurfaceParams params = *this; - const u32 tiled_size = is_tiled ? 8 : 1; - const u32 stride_tiled_bytes = BytesInPixels(stride * tiled_size); - PAddr aligned_start = - addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes); - PAddr aligned_end = - addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes); - - if (aligned_end - aligned_start > stride_tiled_bytes) { - params.addr = aligned_start; - params.height = (aligned_end - aligned_start) / BytesInPixels(stride); - } else { - // 1 row - ASSERT(aligned_end - aligned_start == stride_tiled_bytes); - const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1); - aligned_start = - addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment); - aligned_end = - addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment); - params.addr = aligned_start; - params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size; - params.stride = params.width; - params.height = tiled_size; - } - params.UpdateParams(); - - return params; -} - -SurfaceInterval SurfaceParams::GetSubRectInterval(Common::Rectangle unscaled_rect) const { - if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) { - return {}; - } - - if (is_tiled) { - unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8; - unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8; - unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8; - unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8; - } - - const u32 stride_tiled = !is_tiled ? stride : stride * 8; - - const u32 pixel_offset = - stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) + - unscaled_rect.left; - - const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth(); - - return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)}; -} - -Common::Rectangle SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const { - const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr); - - if (is_tiled) { - const int x0 = (begin_pixel_index % (stride * 8)) / 8; - const int y0 = (begin_pixel_index / (stride * 8)) * 8; - // Top to bottom - return Common::Rectangle(x0, height - y0, x0 + sub_surface.width, - height - (y0 + sub_surface.height)); - } - - const int x0 = begin_pixel_index % stride; - const int y0 = begin_pixel_index / stride; - // Bottom to top - return Common::Rectangle(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0); -} - -Common::Rectangle SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const { - auto rect = GetSubRect(sub_surface); - rect.left = rect.left * res_scale; - rect.right = rect.right * res_scale; - rect.top = rect.top * res_scale; - rect.bottom = rect.bottom * res_scale; - return rect; -} - -bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { - return std::tie(other_surface.addr, other_surface.width, other_surface.height, - other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) == - std::tie(addr, width, height, stride, pixel_format, is_tiled) && - pixel_format != PixelFormat::Invalid; -} - -bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { - return sub_surface.addr >= addr && sub_surface.end <= end && - sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid && - sub_surface.is_tiled == is_tiled && - (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && - (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) && - GetSubRect(sub_surface).right <= stride; -} - -bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { - return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format && - addr <= expanded_surface.end && expanded_surface.addr <= end && - is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride && - (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) % - BytesInPixels(stride * (is_tiled ? 8 : 1)) == - 0; -} - -bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { - if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr || - end < texcopy_params.end) { - return false; - } - if (texcopy_params.width != texcopy_params.stride) { - const u32 tile_stride = BytesInPixels(stride * (is_tiled ? 8 : 1)); - return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && - texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 && - (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) && - ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride; - } - return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval(); -} - bool CachedSurface::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const { if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && @@ -654,47 +535,6 @@ bool CachedSurface::CanCopy(const SurfaceParams& dest_surface, return false; } -SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const { - SurfaceInterval result{}; - const auto valid_regions = - SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions; - for (auto& valid_interval : valid_regions) { - const SurfaceInterval aligned_interval{ - addr + Common::AlignUp(boost::icl::first(valid_interval) - addr, - BytesInPixels(is_tiled ? 8 * 8 : 1)), - addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr, - BytesInPixels(is_tiled ? 8 * 8 : 1))}; - - if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) || - boost::icl::length(aligned_interval) == 0) { - continue; - } - - // Get the rectangle within aligned_interval - const u32 stride_bytes = BytesInPixels(stride) * (is_tiled ? 8 : 1); - SurfaceInterval rect_interval{ - addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes), - addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes), - }; - if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) { - // 1 row - rect_interval = aligned_interval; - } else if (boost::icl::length(rect_interval) == 0) { - // 2 rows that do not make a rectangle, return the larger one - const SurfaceInterval row1{boost::icl::first(aligned_interval), - boost::icl::first(rect_interval)}; - const SurfaceInterval row2{boost::icl::first(rect_interval), - boost::icl::last_next(aligned_interval)}; - rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2; - } - - if (boost::icl::length(rect_interval) > boost::icl::length(result)) { - result = rect_interval; - } - } - return result; -} - MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64)); void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface, SurfaceInterval copy_interval) { @@ -956,10 +796,6 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ if (Settings::values.custom_textures) is_custom = LoadCustomTexture(tex_hash, custom_tex_info); - TextureFilterInterface* const texture_filter = - is_custom ? nullptr : TextureFilterManager::GetInstance().GetTextureFilter(); - const u16 default_scale = texture_filter ? texture_filter->scale_factor : 1; - // Load data from memory to the surface GLint x0 = static_cast(rect.left); GLint y0 = static_cast(rect.bottom); @@ -971,7 +807,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in // surface OGLTexture unscaled_tex; - if (res_scale != default_scale) { + if (res_scale != 1) { x0 = 0; y0 = 0; @@ -980,8 +816,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8), custom_tex_info.width, custom_tex_info.height); } else { - AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth() * default_scale, - rect.GetHeight() * default_scale); + AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight()); } target_tex = unscaled_tex.handle; } @@ -1007,16 +842,6 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ 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 if (texture_filter) { - if (res_scale == default_scale) { - AllocateSurfaceTexture(texture.handle, GetFormatTuple(pixel_format), - rect.GetWidth() * default_scale, - rect.GetHeight() * default_scale); - cur_state.texture_units[0].texture_2d = texture.handle; - cur_state.Apply(); - } - texture_filter->scale(*this, {(u32)x0, (u32)y0, rect.GetWidth(), rect.GetHeight()}, - buffer_offset); } else { glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(stride)); @@ -1027,13 +852,13 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - if (Settings::values.dump_textures && !is_custom && !texture_filter) + if (Settings::values.dump_textures && !is_custom) DumpTexture(target_tex, tex_hash); cur_state.texture_units[0].texture_2d = old_tex; cur_state.Apply(); - if (res_scale != default_scale) { + if (res_scale != 1) { auto scaled_rect = rect; scaled_rect.left *= res_scale; scaled_rect.top *= res_scale; @@ -1042,8 +867,11 @@ void CachedSurface::UploadGLTexture(Common::Rectangle rect, GLuint read_fb_ auto from_rect = is_custom ? Common::Rectangle{0, custom_tex_info.height, custom_tex_info.width, 0} : Common::Rectangle{0, rect.GetHeight(), rect.GetWidth(), 0}; - BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type, - read_fb_handle, draw_fb_handle); + if (!owner.texture_filterer->Filter(unscaled_tex.handle, from_rect, texture.handle, + scaled_rect, type, read_fb_handle, draw_fb_handle)) { + BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type, + read_fb_handle, draw_fb_handle); + } } InvalidateAllWatcher(); @@ -1232,6 +1060,10 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params } RasterizerCacheOpenGL::RasterizerCacheOpenGL() { + resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); + texture_filterer = std::make_unique(Settings::values.texture_filter_name, + resolution_scale_factor); + read_framebuffer.Create(); draw_framebuffer.Create(); @@ -1508,11 +1340,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf params.height = info.height; params.is_tiled = true; params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); - TextureFilterInterface* filter{}; - - params.res_scale = (filter = TextureFilterManager::GetInstance().GetTextureFilter()) - ? filter->scale_factor - : 1; + params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor; params.UpdateParams(); u32 min_width = info.width >> max_level; @@ -1565,7 +1393,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf glTexImage2D(GL_TEXTURE_2D, level, format_tuple.internal_format, width >> level, height >> level, 0, format_tuple.format, format_tuple.type, nullptr); } - if (surface->is_custom) { + if (surface->is_custom || !texture_filterer->IsNull()) { // TODO: proper mipmap support for custom textures glGenerateMipmap(GL_TEXTURE_2D); } @@ -1601,7 +1429,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf } state.ResetTexture(level_surface->texture.handle); state.Apply(); - if (!surface->is_custom) { + if (!surface->is_custom && texture_filterer->IsNull()) { glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, level_surface->texture.handle, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, @@ -1725,10 +1553,9 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( const auto& config = regs.framebuffer.framebuffer; // update resolution_scale_factor and reset cache if changed - static u16 resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); - if (resolution_scale_factor != VideoCore::GetResolutionScaleFactor() || - TextureFilterManager::GetInstance().IsUpdated()) { - TextureFilterManager::GetInstance().Reset(); + if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) | + (VideoCore::g_texture_filter_update_requested.exchange(false) && + texture_filterer->Reset(Settings::values.texture_filter_name, resolution_scale_factor))) { resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); FlushAll(); while (!surface_cache.empty()) @@ -1813,7 +1640,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( } Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) { - Surface new_surface = std::make_shared(); + Surface new_surface = std::make_shared(*this); new_surface->addr = config.GetStartAddress(); new_surface->end = config.GetEndAddress(); @@ -2041,7 +1868,7 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface } Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { - Surface surface = std::make_shared(); + Surface surface = std::make_shared(*this); static_cast(*surface) = params; surface->texture.Create(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 466ebb8d0..dffcbb05c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -26,14 +26,15 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/custom_tex_cache.h" -#include "core/hw/gpu.h" -#include "video_core/regs_framebuffer.h" -#include "video_core/regs_texturing.h" #include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/renderer_opengl/gl_surface_params.h" #include "video_core/texture/texture_decode.h" namespace OpenGL { +class RasterizerCacheOpenGL; +class TextureFilterer; + struct TextureCubeConfig { PAddr px; PAddr nx; @@ -76,11 +77,8 @@ struct hash { namespace OpenGL { -struct CachedSurface; -using Surface = std::shared_ptr; using SurfaceSet = std::set; -using SurfaceInterval = boost::icl::right_open_interval; using SurfaceRegions = boost::icl::interval_set; using SurfaceMap = boost::icl::interval_map BPP_TABLE = { - 32, // RGBA8 - 24, // RGB8 - 16, // RGB5A1 - 16, // RGB565 - 16, // RGBA4 - 16, // IA8 - 16, // RG8 - 8, // I8 - 8, // A8 - 8, // IA4 - 4, // I4 - 4, // A4 - 4, // ETC1 - 8, // ETC1A4 - 16, // D16 - 0, - 24, // D24 - 32, // D24S8 - }; - -public: - enum class PixelFormat { - // First 5 formats are shared between textures and color buffers - RGBA8 = 0, - RGB8 = 1, - RGB5A1 = 2, - RGB565 = 3, - RGBA4 = 4, - - // Texture-only formats - IA8 = 5, - RG8 = 6, - I8 = 7, - A8 = 8, - IA4 = 9, - I4 = 10, - A4 = 11, - ETC1 = 12, - ETC1A4 = 13, - - // Depth buffer-only formats - D16 = 14, - // gap - D24 = 16, - D24S8 = 17, - - Invalid = 255, - }; - - enum class SurfaceType { - Color = 0, - Texture = 1, - Depth = 2, - DepthStencil = 3, - Fill = 4, - Invalid = 5 - }; - - static constexpr unsigned int GetFormatBpp(PixelFormat format) { - const auto format_idx = static_cast(format); - DEBUG_ASSERT_MSG(format_idx < BPP_TABLE.size(), "Invalid pixel format {}", format_idx); - return BPP_TABLE[format_idx]; - } - - unsigned int GetFormatBpp() const { - return GetFormatBpp(pixel_format); - } - - static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { - return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) { - return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { - return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14) - : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { - switch (format) { - // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat - case GPU::Regs::PixelFormat::RGB565: - return PixelFormat::RGB565; - case GPU::Regs::PixelFormat::RGB5A1: - return PixelFormat::RGB5A1; - default: - return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; - } - } - - static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { - SurfaceType a_type = GetFormatType(pixel_format_a); - SurfaceType b_type = GetFormatType(pixel_format_b); - - if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && - (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { - return true; - } - - if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { - return true; - } - - if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { - return true; - } - - return false; - } - - static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { - if ((unsigned int)pixel_format < 5) { - return SurfaceType::Color; - } - - if ((unsigned int)pixel_format < 14) { - return SurfaceType::Texture; - } - - if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { - return SurfaceType::Depth; - } - - if (pixel_format == PixelFormat::D24S8) { - return SurfaceType::DepthStencil; - } - - return SurfaceType::Invalid; - } - - /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" - /// and "pixel_format" - void UpdateParams() { - if (stride == 0) { - stride = width; - } - type = GetFormatType(pixel_format); - size = !is_tiled ? BytesInPixels(stride * (height - 1) + width) - : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8); - end = addr + size; - } - - SurfaceInterval GetInterval() const { - return SurfaceInterval(addr, end); - } - - // Returns the outer rectangle containing "interval" - SurfaceParams FromInterval(SurfaceInterval interval) const; - - SurfaceInterval GetSubRectInterval(Common::Rectangle unscaled_rect) const; - - // Returns the region of the biggest valid rectange within interval - SurfaceInterval GetCopyableInterval(const Surface& src_surface) const; - - u32 GetScaledWidth() const { - return width * res_scale; - } - - u32 GetScaledHeight() const { - return height * res_scale; - } - - Common::Rectangle GetRect() const { - return {0, height, width, 0}; - } - - Common::Rectangle GetScaledRect() const { - return {0, GetScaledHeight(), GetScaledWidth(), 0}; - } - - u32 PixelsInBytes(u32 size) const { - return size * CHAR_BIT / GetFormatBpp(pixel_format); - } - - u32 BytesInPixels(u32 pixels) const { - return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; - } - - bool ExactMatch(const SurfaceParams& other_surface) const; - bool CanSubRect(const SurfaceParams& sub_surface) const; - bool CanExpand(const SurfaceParams& expanded_surface) const; - bool CanTexCopy(const SurfaceParams& texcopy_params) const; - - Common::Rectangle GetSubRect(const SurfaceParams& sub_surface) const; - Common::Rectangle GetScaledSubRect(const SurfaceParams& sub_surface) const; - - PAddr addr = 0; - PAddr end = 0; - u32 size = 0; - - u32 width = 0; - u32 height = 0; - u32 stride = 0; - u16 res_scale = 1; - - bool is_tiled = false; - PixelFormat pixel_format = PixelFormat::Invalid; - SurfaceType type = SurfaceType::Invalid; -}; - /** * A watcher that notifies whether a cached surface has been changed. This is useful for caching * surface collection objects, including texture cube and mipmap. @@ -345,6 +137,8 @@ private: }; struct CachedSurface : SurfaceParams, std::enable_shared_from_this { + CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} + bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; @@ -422,6 +216,7 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this> watchers; }; @@ -519,8 +314,12 @@ private: OGLProgram d24s8_abgr_shader; GLint d24s8_abgr_tbo_size_u_id; GLint d24s8_abgr_viewport_u_id; + u16 resolution_scale_factor; std::unordered_map texture_cube_cache; + +public: + std::unique_ptr texture_filterer; }; struct FormatTuple { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 5d655a127..cc7a864a2 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -38,13 +38,6 @@ constexpr GLuint ShadowTexturePZ = 5; constexpr GLuint ShadowTextureNZ = 6; } // namespace ImageUnits -struct Viewport { - GLint x; - GLint y; - GLsizei width; - GLsizei height; -}; - class OpenGLState { public: struct { @@ -142,7 +135,12 @@ public: GLsizei height; } scissor; - Viewport viewport; + struct { + GLint x; + GLint y; + GLsizei width; + GLsizei height; + } viewport; std::array clip_distance; // GL_CLIP_DISTANCE diff --git a/src/video_core/renderer_opengl/gl_surface_params.cpp b/src/video_core/renderer_opengl/gl_surface_params.cpp new file mode 100644 index 000000000..ceb0359ec --- /dev/null +++ b/src/video_core/renderer_opengl/gl_surface_params.cpp @@ -0,0 +1,171 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "video_core/renderer_opengl/gl_rasterizer_cache.h" +#include "video_core/renderer_opengl/gl_surface_params.h" + +namespace OpenGL { + +SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const { + SurfaceParams params = *this; + const u32 tiled_size = is_tiled ? 8 : 1; + const u32 stride_tiled_bytes = BytesInPixels(stride * tiled_size); + PAddr aligned_start = + addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes); + PAddr aligned_end = + addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes); + + if (aligned_end - aligned_start > stride_tiled_bytes) { + params.addr = aligned_start; + params.height = (aligned_end - aligned_start) / BytesInPixels(stride); + } else { + // 1 row + ASSERT(aligned_end - aligned_start == stride_tiled_bytes); + const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1); + aligned_start = + addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment); + aligned_end = + addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment); + params.addr = aligned_start; + params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size; + params.stride = params.width; + params.height = tiled_size; + } + params.UpdateParams(); + + return params; +} + +SurfaceInterval SurfaceParams::GetSubRectInterval(Common::Rectangle unscaled_rect) const { + if (unscaled_rect.GetHeight() == 0 || unscaled_rect.GetWidth() == 0) { + return {}; + } + + if (is_tiled) { + unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8) * 8; + unscaled_rect.bottom = Common::AlignDown(unscaled_rect.bottom, 8) / 8; + unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8) * 8; + unscaled_rect.top = Common::AlignUp(unscaled_rect.top, 8) / 8; + } + + const u32 stride_tiled = !is_tiled ? stride : stride * 8; + + const u32 pixel_offset = + stride_tiled * (!is_tiled ? unscaled_rect.bottom : (height / 8) - unscaled_rect.top) + + unscaled_rect.left; + + const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride_tiled + unscaled_rect.GetWidth(); + + return {addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels)}; +} + +SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) const { + SurfaceInterval result{}; + const auto valid_regions = + SurfaceRegions(GetInterval() & src_surface->GetInterval()) - src_surface->invalid_regions; + for (auto& valid_interval : valid_regions) { + const SurfaceInterval aligned_interval{ + addr + Common::AlignUp(boost::icl::first(valid_interval) - addr, + BytesInPixels(is_tiled ? 8 * 8 : 1)), + addr + Common::AlignDown(boost::icl::last_next(valid_interval) - addr, + BytesInPixels(is_tiled ? 8 * 8 : 1))}; + + if (BytesInPixels(is_tiled ? 8 * 8 : 1) > boost::icl::length(valid_interval) || + boost::icl::length(aligned_interval) == 0) { + continue; + } + + // Get the rectangle within aligned_interval + const u32 stride_bytes = BytesInPixels(stride) * (is_tiled ? 8 : 1); + SurfaceInterval rect_interval{ + addr + Common::AlignUp(boost::icl::first(aligned_interval) - addr, stride_bytes), + addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - addr, stride_bytes), + }; + if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) { + // 1 row + rect_interval = aligned_interval; + } else if (boost::icl::length(rect_interval) == 0) { + // 2 rows that do not make a rectangle, return the larger one + const SurfaceInterval row1{boost::icl::first(aligned_interval), + boost::icl::first(rect_interval)}; + const SurfaceInterval row2{boost::icl::first(rect_interval), + boost::icl::last_next(aligned_interval)}; + rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2; + } + + if (boost::icl::length(rect_interval) > boost::icl::length(result)) { + result = rect_interval; + } + } + return result; +} + +Common::Rectangle SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const { + const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr); + + if (is_tiled) { + const int x0 = (begin_pixel_index % (stride * 8)) / 8; + const int y0 = (begin_pixel_index / (stride * 8)) * 8; + // Top to bottom + return Common::Rectangle(x0, height - y0, x0 + sub_surface.width, + height - (y0 + sub_surface.height)); + } + + const int x0 = begin_pixel_index % stride; + const int y0 = begin_pixel_index / stride; + // Bottom to top + return Common::Rectangle(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0); +} + +Common::Rectangle SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const { + auto rect = GetSubRect(sub_surface); + rect.left = rect.left * res_scale; + rect.right = rect.right * res_scale; + rect.top = rect.top * res_scale; + rect.bottom = rect.bottom * res_scale; + return rect; +} + +bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { + return std::tie(other_surface.addr, other_surface.width, other_surface.height, + other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) == + std::tie(addr, width, height, stride, pixel_format, is_tiled) && + pixel_format != PixelFormat::Invalid; +} + +bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { + return sub_surface.addr >= addr && sub_surface.end <= end && + sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid && + sub_surface.is_tiled == is_tiled && + (sub_surface.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && + (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) && + GetSubRect(sub_surface).right <= stride; +} + +bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { + return pixel_format != PixelFormat::Invalid && pixel_format == expanded_surface.pixel_format && + addr <= expanded_surface.end && expanded_surface.addr <= end && + is_tiled == expanded_surface.is_tiled && stride == expanded_surface.stride && + (std::max(expanded_surface.addr, addr) - std::min(expanded_surface.addr, addr)) % + BytesInPixels(stride * (is_tiled ? 8 : 1)) == + 0; +} + +bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { + if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr || + end < texcopy_params.end) { + return false; + } + if (texcopy_params.width != texcopy_params.stride) { + const u32 tile_stride = BytesInPixels(stride * (is_tiled ? 8 : 1)); + return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 && + texcopy_params.width % BytesInPixels(is_tiled ? 64 : 1) == 0 && + (texcopy_params.height == 1 || texcopy_params.stride == tile_stride) && + ((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride; + } + return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval(); +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_surface_params.h b/src/video_core/renderer_opengl/gl_surface_params.h new file mode 100644 index 000000000..56d33e0d4 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_surface_params.h @@ -0,0 +1,229 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/assert.h" +#include "common/math_util.h" +#include "core/hw/gpu.h" +#include "video_core/regs_framebuffer.h" +#include "video_core/regs_texturing.h" + +namespace OpenGL { + +struct CachedSurface; +using Surface = std::shared_ptr; + +using SurfaceInterval = boost::icl::right_open_interval; + +struct SurfaceParams { +private: + static constexpr std::array BPP_TABLE = { + 32, // RGBA8 + 24, // RGB8 + 16, // RGB5A1 + 16, // RGB565 + 16, // RGBA4 + 16, // IA8 + 16, // RG8 + 8, // I8 + 8, // A8 + 8, // IA4 + 4, // I4 + 4, // A4 + 4, // ETC1 + 8, // ETC1A4 + 16, // D16 + 0, + 24, // D24 + 32, // D24S8 + }; + +public: + enum class PixelFormat { + // First 5 formats are shared between textures and color buffers + RGBA8 = 0, + RGB8 = 1, + RGB5A1 = 2, + RGB565 = 3, + RGBA4 = 4, + + // Texture-only formats + IA8 = 5, + RG8 = 6, + I8 = 7, + A8 = 8, + IA4 = 9, + I4 = 10, + A4 = 11, + ETC1 = 12, + ETC1A4 = 13, + + // Depth buffer-only formats + D16 = 14, + // gap + D24 = 16, + D24S8 = 17, + + Invalid = 255, + }; + + enum class SurfaceType { + Color = 0, + Texture = 1, + Depth = 2, + DepthStencil = 3, + Fill = 4, + Invalid = 5 + }; + + static constexpr unsigned int GetFormatBpp(PixelFormat format) { + const auto format_idx = static_cast(format); + DEBUG_ASSERT_MSG(format_idx < BPP_TABLE.size(), "Invalid pixel format {}", format_idx); + return BPP_TABLE[format_idx]; + } + + unsigned int GetFormatBpp() const { + return GetFormatBpp(pixel_format); + } + + static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { + return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) { + return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { + return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14) + : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { + switch (format) { + // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat + case GPU::Regs::PixelFormat::RGB565: + return PixelFormat::RGB565; + case GPU::Regs::PixelFormat::RGB5A1: + return PixelFormat::RGB5A1; + default: + return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; + } + } + + static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { + SurfaceType a_type = GetFormatType(pixel_format_a); + SurfaceType b_type = GetFormatType(pixel_format_b); + + if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && + (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { + return true; + } + + if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { + return true; + } + + if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { + return true; + } + + return false; + } + + static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { + if ((unsigned int)pixel_format < 5) { + return SurfaceType::Color; + } + + if ((unsigned int)pixel_format < 14) { + return SurfaceType::Texture; + } + + if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { + return SurfaceType::Depth; + } + + if (pixel_format == PixelFormat::D24S8) { + return SurfaceType::DepthStencil; + } + + return SurfaceType::Invalid; + } + + /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" + /// and "pixel_format" + void UpdateParams() { + if (stride == 0) { + stride = width; + } + type = GetFormatType(pixel_format); + size = !is_tiled ? BytesInPixels(stride * (height - 1) + width) + : BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8); + end = addr + size; + } + + SurfaceInterval GetInterval() const { + return SurfaceInterval(addr, end); + } + + // Returns the outer rectangle containing "interval" + SurfaceParams FromInterval(SurfaceInterval interval) const; + + SurfaceInterval GetSubRectInterval(Common::Rectangle unscaled_rect) const; + + // Returns the region of the biggest valid rectange within interval + SurfaceInterval GetCopyableInterval(const Surface& src_surface) const; + + u32 GetScaledWidth() const { + return width * res_scale; + } + + u32 GetScaledHeight() const { + return height * res_scale; + } + + Common::Rectangle GetRect() const { + return {0, height, width, 0}; + } + + Common::Rectangle GetScaledRect() const { + return {0, GetScaledHeight(), GetScaledWidth(), 0}; + } + + u32 PixelsInBytes(u32 size) const { + return size * CHAR_BIT / GetFormatBpp(pixel_format); + } + + u32 BytesInPixels(u32 pixels) const { + return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; + } + + bool ExactMatch(const SurfaceParams& other_surface) const; + bool CanSubRect(const SurfaceParams& sub_surface) const; + bool CanExpand(const SurfaceParams& expanded_surface) const; + bool CanTexCopy(const SurfaceParams& texcopy_params) const; + + Common::Rectangle GetSubRect(const SurfaceParams& sub_surface) const; + Common::Rectangle GetScaledSubRect(const SurfaceParams& sub_surface) const; + + PAddr addr = 0; + PAddr end = 0; + u32 size = 0; + + u32 width = 0; + u32 height = 0; + u32 stride = 0; + u16 res_scale = 1; + + bool is_tiled = false; + PixelFormat pixel_format = PixelFormat::Invalid; + SurfaceType type = SurfaceType::Invalid; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e7e0bb72f..5046895e0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -32,7 +32,6 @@ #include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/renderer_opengl.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h" #include "video_core/video_core.h" namespace Frontend { @@ -1179,14 +1178,10 @@ VideoCore::ResultStatus RendererOpenGL::Init() { RefreshRasterizerSetting(); - TextureFilterManager::GetInstance().Reset(); - return VideoCore::ResultStatus::Success; } /// Shutdown the renderer -void RendererOpenGL::ShutDown() { - TextureFilterManager::GetInstance().Destroy(); -} +void RendererOpenGL::ShutDown() {} } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp index 86b4a85b6..b70cc14f4 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp +++ b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.cpp @@ -42,18 +42,17 @@ namespace OpenGL { -Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterInterface(scale_factor) { +Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterBase(scale_factor) { const OpenGLState cur_state = OpenGLState::GetCurState(); - const auto setup_temp_tex = [this, scale_factor](TempTex& texture, GLint internal_format, - GLint format) { + const auto setup_temp_tex = [this](TempTex& texture, GLint internal_format, GLint format) { texture.fbo.Create(); texture.tex.Create(); state.draw.draw_framebuffer = texture.fbo.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_RECTANGLE, texture.tex.handle); - glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internal_format, 1024 * scale_factor, - 1024 * scale_factor, 0, format, GL_HALF_FLOAT, nullptr); + glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internal_format, 1024 * internal_scale_factor, + 1024 * internal_scale_factor, 0, format, GL_HALF_FLOAT, nullptr); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, texture.tex.handle, 0); }; @@ -61,7 +60,6 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterInterface(sc setup_temp_tex(XY, GL_RG16F, GL_RG); vao.Create(); - out_fbo.Create(); for (std::size_t idx = 0; idx < samplers.size(); ++idx) { samplers[idx].Create(); @@ -86,30 +84,26 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterInterface(sc state.draw.shader_program = refine_program.handle; state.Apply(); glUniform1i(glGetUniformLocation(refine_program.handle, "LUMAD"), 1); + glUniform1f(glGetUniformLocation(refine_program.handle, "final_scale"), + static_cast(internal_scale_factor) / scale_factor); cur_state.Apply(); } -void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) { +void Anime4kUltrafast::Filter(GLuint src_tex, const Common::Rectangle& src_rect, + GLuint dst_tex, const Common::Rectangle& dst_rect, + GLuint read_fb_handle, GLuint draw_fb_handle) { const OpenGLState cur_state = OpenGLState::GetCurState(); - OGLTexture src_tex; - src_tex.Create(); - - state.viewport = RectToViewport(rect); - - state.texture_units[0].texture_2d = src_tex.handle; + state.viewport = {static_cast(src_rect.left * internal_scale_factor), + static_cast(src_rect.bottom * internal_scale_factor), + static_cast(src_rect.GetWidth() * internal_scale_factor), + static_cast(src_rect.GetHeight() * internal_scale_factor)}; + state.texture_units[0].texture_2d = src_tex; state.draw.draw_framebuffer = XY.fbo.handle; state.draw.shader_program = gradient_x_program.handle; state.Apply(); - const FormatTuple tuple = GetFormatTuple(surface.pixel_format); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(surface.stride)); - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, rect.GetWidth(), rect.GetHeight(), 0, - tuple.format, tuple.type, &surface.gl_buffer[buffer_offset]); - glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_RECTANGLE, LUMAD.tex.handle); glActiveTexture(GL_TEXTURE2); @@ -124,14 +118,17 @@ void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle(dst_rect.left), static_cast(dst_rect.bottom), + static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight())}; + state.draw.draw_framebuffer = draw_fb_handle; state.draw.shader_program = refine_program.handle; state.Apply(); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - cur_state.texture_units[0].texture_2d, 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); cur_state.Apply(); } diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h index 79a058fd5..9e89da816 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h +++ b/src/video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h @@ -6,29 +6,25 @@ #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_interface.h" +#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" namespace OpenGL { -class Anime4kUltrafast : public TextureFilterInterface { +class Anime4kUltrafast : public TextureFilterBase { public: - static TextureFilterInfo GetInfo() { - TextureFilterInfo info; - info.name = "Anime4K Ultrafast"; - info.clamp_scale = {2, 2}; - info.constructor = std::make_unique; - return info; - } + static constexpr std::string_view NAME = "Anime4K Ultrafast"; - Anime4kUltrafast(u16 scale_factor); - void scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) override; + explicit Anime4kUltrafast(u16 scale_factor); + void Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) override; private: + static constexpr u8 internal_scale_factor = 2; + OpenGLState state{}; OGLVertexArray vao; - OGLFramebuffer out_fbo; struct TempTex { OGLTexture tex; diff --git a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag b/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag index 26f2526ba..4417b96f6 100644 --- a/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag +++ b/src/video_core/renderer_opengl/texture_filters/anime4k/refine.frag @@ -8,6 +8,8 @@ uniform sampler2D HOOKED; uniform sampler2DRect LUMAD; uniform sampler2DRect LUMAG; +uniform float final_scale; + const float LINE_DETECT_THRESHOLD = 0.4; const float STRENGTH = 0.6; @@ -24,7 +26,7 @@ vec4 getAverage(vec4 cc, vec4 a, vec4 b, vec4 c) { #define GetRGBAL(offset) \ RGBAL(textureOffset(HOOKED, tex_coord, offset), \ - texture(LUMAD, clamp(gl_FragCoord.xy + offset, vec2(0.0), input_max)).x) + texture(LUMAD, clamp((gl_FragCoord.xy + offset) * final_scale, vec2(0.0), input_max)).x) float min3v(float a, float b, float c) { return min(min(a, b), c); diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp index 6c0bc1f82..ce039c211 100644 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp +++ b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.cpp @@ -10,45 +10,36 @@ namespace OpenGL { -Bicubic::Bicubic(u16 scale_factor) : TextureFilterInterface(scale_factor) { +Bicubic::Bicubic(u16 scale_factor) : TextureFilterBase(scale_factor) { program.Create(tex_coord_vert.data(), bicubic_frag.data()); vao.Create(); - draw_fbo.Create(); src_sampler.Create(); state.draw.shader_program = program.handle; state.draw.vertex_array = vao.handle; state.draw.shader_program = program.handle; - state.draw.draw_framebuffer = draw_fbo.handle; state.texture_units[0].sampler = src_sampler.handle; glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} +} // namespace OpenGL -void Bicubic::scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) { +void Bicubic::Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) { const OpenGLState cur_state = OpenGLState::GetCurState(); - - OGLTexture src_tex; - src_tex.Create(); - state.texture_units[0].texture_2d = src_tex.handle; - - state.viewport = RectToViewport(rect); + state.texture_units[0].texture_2d = src_tex; + state.draw.draw_framebuffer = draw_fb_handle; + state.viewport = {static_cast(dst_rect.left), static_cast(dst_rect.bottom), + static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight())}; state.Apply(); - const FormatTuple tuple = GetFormatTuple(surface.pixel_format); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(surface.stride)); - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, rect.GetWidth(), rect.GetHeight(), 0, - tuple.format, tuple.type, &surface.gl_buffer[buffer_offset]); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - cur_state.texture_units[0].texture_2d, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); cur_state.Apply(); } diff --git a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h index a16bdafcf..b982cc973 100644 --- a/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h +++ b/src/video_core/renderer_opengl/texture_filters/bicubic/bicubic.h @@ -6,27 +6,24 @@ #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_interface.h" +#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" namespace OpenGL { -class Bicubic : public TextureFilterInterface { -public: - static TextureFilterInfo GetInfo() { - TextureFilterInfo info; - info.name = "Bicubic"; - info.constructor = std::make_unique; - return info; - } - Bicubic(u16 scale_factor); - void scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) override; +class Bicubic : public TextureFilterBase { +public: + static constexpr std::string_view NAME = "Bicubic"; + + explicit Bicubic(u16 scale_factor); + void Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) override; private: OpenGLState state{}; OGLProgram program{}; OGLVertexArray vao{}; - OGLFramebuffer draw_fbo{}; OGLSampler src_sampler{}; }; + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h b/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h new file mode 100644 index 000000000..126fdb108 --- /dev/null +++ b/src/video_core/renderer_opengl/texture_filters/texture_filter_base.h @@ -0,0 +1,26 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/renderer_opengl/gl_surface_params.h" + +namespace OpenGL { + +class TextureFilterBase { + friend class TextureFilterer; + virtual void Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) = 0; + +public: + explicit TextureFilterBase(u16 scale_factor) : scale_factor{scale_factor} {}; + virtual ~TextureFilterBase() = default; + + const u16 scale_factor{}; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filter_interface.h b/src/video_core/renderer_opengl/texture_filters/texture_filter_interface.h deleted file mode 100644 index c2bf2a686..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filter_interface.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "common/common_types.h" -#include "common/math_util.h" - -namespace OpenGL { - -struct CachedSurface; -struct Viewport; - -class TextureFilterInterface { -public: - const u16 scale_factor{}; - TextureFilterInterface(u16 scale_factor) : scale_factor{scale_factor} {} - virtual void scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) = 0; - virtual ~TextureFilterInterface() = default; - -protected: - Viewport RectToViewport(const Common::Rectangle& rect); -}; - -// every texture filter should have a static GetInfo function -struct TextureFilterInfo { - std::string_view name; - struct { - u16 min, max; - } clamp_scale{1, 10}; - std::function(u16 scale_factor)> constructor; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.cpp b/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.cpp deleted file mode 100644 index 77d07111e..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/logging/log.h" -#include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" -#include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h" -#include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" - -namespace OpenGL { - -Viewport TextureFilterInterface::RectToViewport(const Common::Rectangle& rect) { - return { - static_cast(rect.left) * scale_factor, - static_cast(rect.top) * scale_factor, - static_cast(rect.GetWidth()) * scale_factor, - static_cast(rect.GetHeight()) * scale_factor, - }; -} - -namespace { -template -std::pair FilterMapPair() { - return {T::GetInfo().name, T::GetInfo()}; -}; - -struct NoFilter { - static TextureFilterInfo GetInfo() { - TextureFilterInfo info; - info.name = TextureFilterManager::NONE; - info.clamp_scale = {1, 1}; - info.constructor = [](u16) { return nullptr; }; - return info; - } -}; -} // namespace - -const std::map& -TextureFilterManager::TextureFilterMap() { - static const std::map filter_map{ - FilterMapPair(), - FilterMapPair(), - FilterMapPair(), - FilterMapPair(), - }; - return filter_map; -} - -void TextureFilterManager::SetTextureFilter(std::string filter_name, u16 new_scale_factor) { - if (name == filter_name && scale_factor == new_scale_factor) - return; - std::lock_guard lock{mutex}; - name = std::move(filter_name); - scale_factor = new_scale_factor; - updated = true; -} - -TextureFilterInterface* TextureFilterManager::GetTextureFilter() const { - return filter.get(); -} - -bool TextureFilterManager::IsUpdated() const { - return updated; -} - -void TextureFilterManager::Reset() { - std::lock_guard lock{mutex}; - updated = false; - auto iter = TextureFilterMap().find(name); - if (iter == TextureFilterMap().end()) { - LOG_ERROR(Render_OpenGL, "Invalid texture filter: {}", name); - filter = nullptr; - return; - } - - const auto& filter_info = iter->second; - - u16 clamped_scale = - std::clamp(scale_factor, filter_info.clamp_scale.min, filter_info.clamp_scale.max); - if (clamped_scale != scale_factor) - LOG_ERROR(Render_OpenGL, "Invalid scale factor {} for texture filter {}, clamped to {}", - scale_factor, filter_info.name, clamped_scale); - - filter = filter_info.constructor(clamped_scale); -} - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.h b/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.h deleted file mode 100644 index 1299a9151..000000000 --- a/src/video_core/renderer_opengl/texture_filters/texture_filter_manager.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "video_core/renderer_opengl/texture_filters/texture_filter_interface.h" - -namespace OpenGL { - -class TextureFilterManager { -public: - static constexpr std::string_view NONE = "none"; - struct FilterNameComp { - bool operator()(const std::string_view a, const std::string_view b) const { - bool na = a == NONE; - bool nb = b == NONE; - if (na | nb) - return na & !nb; - return a < b; - } - }; - // function ensures map is initialized before use - static const std::map& TextureFilterMap(); - - static TextureFilterManager& GetInstance() { - static TextureFilterManager singleton; - return singleton; - } - - void Destroy() { - filter.reset(); - } - void SetTextureFilter(std::string filter_name, u16 new_scale_factor); - TextureFilterInterface* GetTextureFilter() const; - // returns true if filter has been changed and a cache reset is needed - bool IsUpdated() const; - void Reset(); - -private: - std::atomic updated{false}; - std::mutex mutex; - std::string name{"none"}; - u16 scale_factor{1}; - - std::unique_ptr filter; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp new file mode 100644 index 000000000..0afa6b876 --- /dev/null +++ b/src/video_core/renderer_opengl/texture_filters/texture_filterer.cpp @@ -0,0 +1,86 @@ +/// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "common/logging/log.h" +#include "video_core/renderer_opengl/texture_filters/anime4k/anime4k_ultrafast.h" +#include "video_core/renderer_opengl/texture_filters/bicubic/bicubic.h" +#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" +#include "video_core/renderer_opengl/texture_filters/texture_filterer.h" +#include "video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h" + +namespace OpenGL { + +namespace { + +using TextureFilterContructor = std::function(u16)>; + +template +std::pair FilterMapPair() { + return {T::NAME, std::make_unique}; +}; + +static const std::unordered_map filter_map{ + {TextureFilterer::NONE, [](u16) { return nullptr; }}, + FilterMapPair(), + FilterMapPair(), + FilterMapPair(), +}; + +} // namespace + +TextureFilterer::TextureFilterer(std::string_view filter_name, u16 scale_factor) { + Reset(filter_name, scale_factor); +} + +bool TextureFilterer::Reset(std::string_view new_filter_name, u16 new_scale_factor) { + if (filter_name == new_filter_name && (IsNull() || filter->scale_factor == new_scale_factor)) + return false; + + auto iter = filter_map.find(new_filter_name); + if (iter == filter_map.end()) { + LOG_ERROR(Render_OpenGL, "Invalid texture filter: {}", new_filter_name); + filter = nullptr; + return true; + } + + filter_name = iter->first; + filter = iter->second(new_scale_factor); + return true; +} + +bool TextureFilterer::IsNull() const { + return !filter; +} + +bool TextureFilterer::Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, + SurfaceParams::SurfaceType type, GLuint read_fb_handle, + GLuint draw_fb_handle) { + // depth / stencil texture filtering is not supported for now + if (IsNull() || + (type != SurfaceParams::SurfaceType::Color && type != SurfaceParams::SurfaceType::Texture)) + return false; + filter->Filter(src_tex, src_rect, dst_tex, dst_rect, read_fb_handle, draw_fb_handle); + return true; +} + +std::vector TextureFilterer::GetFilterNames() { + std::vector ret; + std::transform(filter_map.begin(), filter_map.end(), std::back_inserter(ret), + [](auto pair) { return pair.first; }); + std::sort(ret.begin(), ret.end(), [](std::string_view lhs, std::string_view rhs) { + // sort lexicographically with none at the top + bool lhs_is_none{lhs == NONE}; + bool rhs_is_none{rhs == NONE}; + if (lhs_is_none || rhs_is_none) + return lhs_is_none && !rhs_is_none; + return lhs < rhs; + }); + return ret; +} + +} // namespace OpenGL \ No newline at end of file diff --git a/src/video_core/renderer_opengl/texture_filters/texture_filterer.h b/src/video_core/renderer_opengl/texture_filters/texture_filterer.h new file mode 100644 index 000000000..de3666356 --- /dev/null +++ b/src/video_core/renderer_opengl/texture_filters/texture_filterer.h @@ -0,0 +1,39 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/renderer_opengl/gl_surface_params.h" +#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" + +namespace OpenGL { + +class TextureFilterer { +public: + static constexpr std::string_view NONE = "none"; + + explicit TextureFilterer(std::string_view filter_name, u16 scale_factor); + // returns true if the filter actually changed + bool Reset(std::string_view new_filter_name, u16 new_scale_factor); + // returns true if there is no active filter + bool IsNull() const; + // returns true if the texture was able to be filtered + bool Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, SurfaceParams::SurfaceType type, + GLuint read_fb_handle, GLuint draw_fb_handle); + + static std::vector GetFilterNames(); + +private: + std::string_view filter_name = NONE; + std::unique_ptr filter; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp index 5e7c34d1d..b1dcefc03 100644 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp +++ b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp @@ -48,12 +48,11 @@ namespace OpenGL { -XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterInterface(scale_factor) { +XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) { const OpenGLState cur_state = OpenGLState::GetCurState(); program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); vao.Create(); - draw_fbo.Create(); src_sampler.Create(); state.draw.shader_program = program.handle; @@ -68,31 +67,24 @@ XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterInterface(scale_fa cur_state.Apply(); state.draw.vertex_array = vao.handle; state.draw.shader_program = program.handle; - state.draw.draw_framebuffer = draw_fbo.handle; state.texture_units[0].sampler = src_sampler.handle; } -void XbrzFreescale::scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) { +void XbrzFreescale::Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) { const OpenGLState cur_state = OpenGLState::GetCurState(); - OGLTexture src_tex; - src_tex.Create(); - state.texture_units[0].texture_2d = src_tex.handle; - - state.viewport = RectToViewport(rect); + state.texture_units[0].texture_2d = src_tex; + state.draw.draw_framebuffer = draw_fb_handle; + state.viewport = {static_cast(dst_rect.left), static_cast(dst_rect.bottom), + static_cast(dst_rect.GetWidth()), + static_cast(dst_rect.GetHeight())}; state.Apply(); - const FormatTuple tuple = GetFormatTuple(surface.pixel_format); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(surface.stride)); - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, rect.GetWidth(), rect.GetHeight(), 0, - tuple.format, tuple.type, &surface.gl_buffer[buffer_offset]); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - cur_state.texture_units[0].texture_2d, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); cur_state.Apply(); } diff --git a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h index aad10f308..02c6d5d7e 100644 --- a/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h +++ b/src/video_core/renderer_opengl/texture_filters/xbrz/xbrz_freescale.h @@ -6,28 +6,23 @@ #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/texture_filters/texture_filter_interface.h" +#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h" namespace OpenGL { -class XbrzFreescale : public TextureFilterInterface { +class XbrzFreescale : public TextureFilterBase { public: - static TextureFilterInfo GetInfo() { - TextureFilterInfo info; - info.name = "xBRZ freescale"; - info.constructor = std::make_unique; - return info; - } + static constexpr std::string_view NAME = "xBRZ freescale"; - XbrzFreescale(u16 scale_factor); - void scale(CachedSurface& surface, const Common::Rectangle& rect, - std::size_t buffer_offset) override; + explicit XbrzFreescale(u16 scale_factor); + void Filter(GLuint src_tex, const Common::Rectangle& src_rect, GLuint dst_tex, + const Common::Rectangle& dst_rect, GLuint read_fb_handle, + GLuint draw_fb_handle) override; private: OpenGLState state{}; OGLProgram program{}; OGLVertexArray vao{}; - OGLFramebuffer draw_fbo{}; OGLSampler src_sampler{}; }; } // namespace OpenGL diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a7ed5fb9e..1748efcc6 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -26,6 +26,7 @@ std::atomic g_use_disk_shader_cache; std::atomic g_renderer_bg_color_update_requested; std::atomic g_renderer_sampler_update_requested; std::atomic g_renderer_shader_update_requested; +std::atomic g_texture_filter_update_requested; // Screenshot std::atomic g_renderer_screenshot_requested; void* g_screenshot_bits; diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index f11b67839..9951cd9a7 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -35,6 +35,7 @@ extern std::atomic g_use_disk_shader_cache; extern std::atomic g_renderer_bg_color_update_requested; extern std::atomic g_renderer_sampler_update_requested; extern std::atomic g_renderer_shader_update_requested; +extern std::atomic g_texture_filter_update_requested; // Screenshot extern std::atomic g_renderer_screenshot_requested; extern void* g_screenshot_bits;