citra-emu
/
citra
Archived
1
0
Fork 0

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
This commit is contained in:
Marshall Mohror 2020-04-02 22:42:50 -05:00 committed by GitHub
parent d26564d020
commit f14e973a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 676 additions and 779 deletions

View File

@ -132,8 +132,6 @@ void Config::ReadValues() {
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1)); static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1));
Settings::values.texture_filter_name = Settings::values.texture_filter_name =
sdl2_config->GetString("Renderer", "texture_filter_name", "none"); 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<Settings::StereoRenderOption>( Settings::values.render_3d = static_cast<Settings::StereoRenderOption>(
sdl2_config->GetInteger("Renderer", "render_3d", 0)); sdl2_config->GetInteger("Renderer", "render_3d", 0));

View File

@ -132,9 +132,8 @@ use_disk_shader_cache =
# factor for the 3DS resolution # factor for the 3DS resolution
resolution_factor = resolution_factor =
# Texture filter name and scale factor # Texture filter name
texture_filter_name = texture_filter_name =
texture_filter_factor =
# Turns on the frame limiter, which will limit frames output to the target game speed # Turns on the frame limiter, which will limit frames output to the target game speed
# 0: Off, 1: On (default) # 0: Off, 1: On (default)

View File

@ -454,8 +454,6 @@ void Config::ReadRendererValues() {
ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none")) ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none"))
.toString() .toString()
.toStdString(); .toStdString();
Settings::values.texture_filter_factor =
ReadSetting(QStringLiteral("texture_filter_factor"), 1).toInt();
qt_config->endGroup(); qt_config->endGroup();
} }
@ -893,8 +891,6 @@ void Config::SaveRendererValues() {
WriteSetting(QStringLiteral("texture_filter_name"), WriteSetting(QStringLiteral("texture_filter_name"),
QString::fromStdString(Settings::values.texture_filter_name), QString::fromStdString(Settings::values.texture_filter_name),
QStringLiteral("none")); QStringLiteral("none"));
WriteSetting(QStringLiteral("texture_filter_factor"), Settings::values.texture_filter_factor,
1);
qt_config->endGroup(); qt_config->endGroup();
} }

View File

@ -8,17 +8,14 @@
#include "core/settings.h" #include "core/settings.h"
#include "ui_configure_enhancements.h" #include "ui_configure_enhancements.h"
#include "video_core/renderer_opengl/post_processing_opengl.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) ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureEnhancements) { : QWidget(parent), ui(new Ui::ConfigureEnhancements) {
ui->setupUi(this); ui->setupUi(this);
for (const auto& filter : OpenGL::TextureFilterManager::TextureFilterMap()) for (const auto& filter : OpenGL::TextureFilterer::GetFilterNames())
ui->texture_filter_combobox->addItem(QString::fromStdString(filter.first.data())); ui->texture_filter_combobox->addItem(QString::fromStdString(filter.data()));
connect(ui->texture_filter_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureEnhancements::updateTextureFilter);
SetConfiguration(); SetConfiguration();
@ -60,7 +57,6 @@ void ConfigureEnhancements::SetConfiguration() {
ui->factor_3d->setValue(Settings::values.factor_3d); ui->factor_3d->setValue(Settings::values.factor_3d);
updateShaders(Settings::values.render_3d); updateShaders(Settings::values.render_3d);
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode); 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( int tex_filter_idx = ui->texture_filter_combobox->findText(
QString::fromStdString(Settings::values.texture_filter_name)); QString::fromStdString(Settings::values.texture_filter_name));
if (tex_filter_idx == -1) { if (tex_filter_idx == -1) {
@ -68,7 +64,6 @@ void ConfigureEnhancements::SetConfiguration() {
} else { } else {
ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx); ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx);
} }
updateTextureFilter(tex_filter_idx);
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
ui->swap_screen->setChecked(Settings::values.swap_screen); ui->swap_screen->setChecked(Settings::values.swap_screen);
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_hw_shader && 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() { void ConfigureEnhancements::RetranslateUI() {
ui->retranslateUi(this); ui->retranslateUi(this);
} }
@ -130,7 +114,6 @@ void ConfigureEnhancements::ApplyConfiguration() {
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString(); ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
Settings::values.filter_mode = ui->toggle_linear_filter->isChecked(); Settings::values.filter_mode = ui->toggle_linear_filter->isChecked();
Settings::values.texture_filter_name = ui->texture_filter_combobox->currentText().toStdString(); 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 = Settings::values.layout_option =
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.swap_screen = ui->swap_screen->isChecked();

View File

@ -131,42 +131,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QWidget" name="texture_filter_group" native="true">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Texture Scale Factor</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="texture_scale_spinbox">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -13,7 +13,6 @@
#include "core/hle/service/mic_u.h" #include "core/hle/service/mic_u.h"
#include "core/settings.h" #include "core/settings.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/texture_filters/texture_filter_manager.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
namespace Settings { namespace Settings {
@ -38,9 +37,7 @@ void Apply() {
VideoCore::g_renderer_bg_color_update_requested = true; VideoCore::g_renderer_bg_color_update_requested = true;
VideoCore::g_renderer_sampler_update_requested = true; VideoCore::g_renderer_sampler_update_requested = true;
VideoCore::g_renderer_shader_update_requested = true; VideoCore::g_renderer_shader_update_requested = true;
VideoCore::g_texture_filter_update_requested = true;
OpenGL::TextureFilterManager::GetInstance().SetTextureFilter(values.texture_filter_name,
values.texture_filter_factor);
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
if (system.IsPoweredOn()) { if (system.IsPoweredOn()) {
@ -88,7 +85,6 @@ void LogSettings() {
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
LogSetting("Renderer_PostProcessingShader", Settings::values.pp_shader_name); LogSetting("Renderer_PostProcessingShader", Settings::values.pp_shader_name);
LogSetting("Renderer_FilterMode", Settings::values.filter_mode); LogSetting("Renderer_FilterMode", Settings::values.filter_mode);
LogSetting("Renderer_TextureFilterFactor", Settings::values.texture_filter_factor);
LogSetting("Renderer_TextureFilterName", Settings::values.texture_filter_name); LogSetting("Renderer_TextureFilterName", Settings::values.texture_filter_name);
LogSetting("Stereoscopy_Render3d", static_cast<int>(Settings::values.render_3d)); LogSetting("Stereoscopy_Render3d", static_cast<int>(Settings::values.render_3d));
LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d); LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d);

View File

@ -148,7 +148,6 @@ struct Values {
u16 resolution_factor; u16 resolution_factor;
bool use_frame_limit; bool use_frame_limit;
u16 frame_limit; u16 frame_limit;
u16 texture_filter_factor;
std::string texture_filter_name; std::string texture_filter_name;
LayoutOption layout_option; LayoutOption layout_option;

View File

@ -43,6 +43,8 @@ add_library(video_core STATIC
renderer_opengl/gl_state.h renderer_opengl/gl_state.h
renderer_opengl/gl_stream_buffer.cpp renderer_opengl/gl_stream_buffer.cpp
renderer_opengl/gl_stream_buffer.h 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.cpp
renderer_opengl/gl_vars.h renderer_opengl/gl_vars.h
renderer_opengl/pica_to_gl.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/anime4k/anime4k_ultrafast.h
renderer_opengl/texture_filters/bicubic/bicubic.cpp renderer_opengl/texture_filters/bicubic/bicubic.cpp
renderer_opengl/texture_filters/bicubic/bicubic.h renderer_opengl/texture_filters/bicubic/bicubic.h
renderer_opengl/texture_filters/texture_filter_interface.h renderer_opengl/texture_filters/texture_filter_base.h
renderer_opengl/texture_filters/texture_filter_manager.cpp renderer_opengl/texture_filters/texture_filterer.cpp
renderer_opengl/texture_filters/texture_filter_manager.h renderer_opengl/texture_filters/texture_filterer.h
renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp renderer_opengl/texture_filters/xbrz/xbrz_freescale.cpp
renderer_opengl/texture_filters/xbrz/xbrz_freescale.h renderer_opengl/texture_filters/xbrz/xbrz_freescale.h
shader/debug_data.h shader/debug_data.h

View File

@ -35,7 +35,7 @@
#include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_vars.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/utils.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -494,125 +494,6 @@ static bool FillSurface(const Surface& surface, const u8* fill_data,
return true; 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<u32> 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<u32> 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<u32>(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<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
}
Common::Rectangle<u32> 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, bool CachedSurface::CanFill(const SurfaceParams& dest_surface,
SurfaceInterval fill_interval) const { SurfaceInterval fill_interval) const {
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
@ -654,47 +535,6 @@ bool CachedSurface::CanCopy(const SurfaceParams& dest_surface,
return false; 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)); MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface, void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface,
SurfaceInterval copy_interval) { SurfaceInterval copy_interval) {
@ -956,10 +796,6 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
if (Settings::values.custom_textures) if (Settings::values.custom_textures)
is_custom = LoadCustomTexture(tex_hash, custom_tex_info); 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 // Load data from memory to the surface
GLint x0 = static_cast<GLint>(rect.left); GLint x0 = static_cast<GLint>(rect.left);
GLint y0 = static_cast<GLint>(rect.bottom); GLint y0 = static_cast<GLint>(rect.bottom);
@ -971,7 +807,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in // If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
// surface // surface
OGLTexture unscaled_tex; OGLTexture unscaled_tex;
if (res_scale != default_scale) { if (res_scale != 1) {
x0 = 0; x0 = 0;
y0 = 0; y0 = 0;
@ -980,8 +816,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8), AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8),
custom_tex_info.width, custom_tex_info.height); custom_tex_info.width, custom_tex_info.height);
} else { } else {
AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth() * default_scale, AllocateSurfaceTexture(unscaled_tex.handle, tuple, rect.GetWidth(), rect.GetHeight());
rect.GetHeight() * default_scale);
} }
target_tex = unscaled_tex.handle; target_tex = unscaled_tex.handle;
} }
@ -1007,16 +842,6 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, custom_tex_info.width, custom_tex_info.height, 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()); 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 { } else {
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride)); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
@ -1027,13 +852,13 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
} }
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 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); DumpTexture(target_tex, tex_hash);
cur_state.texture_units[0].texture_2d = old_tex; cur_state.texture_units[0].texture_2d = old_tex;
cur_state.Apply(); cur_state.Apply();
if (res_scale != default_scale) { if (res_scale != 1) {
auto scaled_rect = rect; auto scaled_rect = rect;
scaled_rect.left *= res_scale; scaled_rect.left *= res_scale;
scaled_rect.top *= res_scale; scaled_rect.top *= res_scale;
@ -1042,8 +867,11 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
auto from_rect = auto from_rect =
is_custom ? Common::Rectangle<u32>{0, custom_tex_info.height, custom_tex_info.width, 0} is_custom ? Common::Rectangle<u32>{0, custom_tex_info.height, custom_tex_info.width, 0}
: Common::Rectangle<u32>{0, rect.GetHeight(), rect.GetWidth(), 0}; : Common::Rectangle<u32>{0, rect.GetHeight(), rect.GetWidth(), 0};
BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type, if (!owner.texture_filterer->Filter(unscaled_tex.handle, from_rect, texture.handle,
read_fb_handle, draw_fb_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(); InvalidateAllWatcher();
@ -1232,6 +1060,10 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
} }
RasterizerCacheOpenGL::RasterizerCacheOpenGL() { RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name,
resolution_scale_factor);
read_framebuffer.Create(); read_framebuffer.Create();
draw_framebuffer.Create(); draw_framebuffer.Create();
@ -1508,11 +1340,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf
params.height = info.height; params.height = info.height;
params.is_tiled = true; params.is_tiled = true;
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
TextureFilterInterface* filter{}; params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor;
params.res_scale = (filter = TextureFilterManager::GetInstance().GetTextureFilter())
? filter->scale_factor
: 1;
params.UpdateParams(); params.UpdateParams();
u32 min_width = info.width >> max_level; 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, glTexImage2D(GL_TEXTURE_2D, level, format_tuple.internal_format, width >> level,
height >> level, 0, format_tuple.format, format_tuple.type, nullptr); 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 // TODO: proper mipmap support for custom textures
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
} }
@ -1601,7 +1429,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf
} }
state.ResetTexture(level_surface->texture.handle); state.ResetTexture(level_surface->texture.handle);
state.Apply(); state.Apply();
if (!surface->is_custom) { if (!surface->is_custom && texture_filterer->IsNull()) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
level_surface->texture.handle, 0); level_surface->texture.handle, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
@ -1725,10 +1553,9 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
const auto& config = regs.framebuffer.framebuffer; const auto& config = regs.framebuffer.framebuffer;
// update resolution_scale_factor and reset cache if changed // update resolution_scale_factor and reset cache if changed
static u16 resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) |
if (resolution_scale_factor != VideoCore::GetResolutionScaleFactor() || (VideoCore::g_texture_filter_update_requested.exchange(false) &&
TextureFilterManager::GetInstance().IsUpdated()) { texture_filterer->Reset(Settings::values.texture_filter_name, resolution_scale_factor))) {
TextureFilterManager::GetInstance().Reset();
resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
FlushAll(); FlushAll();
while (!surface_cache.empty()) while (!surface_cache.empty())
@ -1813,7 +1640,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
} }
Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) { Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) {
Surface new_surface = std::make_shared<CachedSurface>(); Surface new_surface = std::make_shared<CachedSurface>(*this);
new_surface->addr = config.GetStartAddress(); new_surface->addr = config.GetStartAddress();
new_surface->end = config.GetEndAddress(); 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 RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
Surface surface = std::make_shared<CachedSurface>(); Surface surface = std::make_shared<CachedSurface>(*this);
static_cast<SurfaceParams&>(*surface) = params; static_cast<SurfaceParams&>(*surface) = params;
surface->texture.Create(); surface->texture.Create();

View File

@ -26,14 +26,15 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "core/custom_tex_cache.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_resource_manager.h"
#include "video_core/renderer_opengl/gl_surface_params.h"
#include "video_core/texture/texture_decode.h" #include "video_core/texture/texture_decode.h"
namespace OpenGL { namespace OpenGL {
class RasterizerCacheOpenGL;
class TextureFilterer;
struct TextureCubeConfig { struct TextureCubeConfig {
PAddr px; PAddr px;
PAddr nx; PAddr nx;
@ -76,11 +77,8 @@ struct hash<OpenGL::TextureCubeConfig> {
namespace OpenGL { namespace OpenGL {
struct CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSet = std::set<Surface>; using SurfaceSet = std::set<Surface>;
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>; using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>;
using SurfaceMap = using SurfaceMap =
boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less, boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less,
@ -104,212 +102,6 @@ enum class ScaleMatch {
Ignore // accept every scaled res Ignore // accept every scaled res
}; };
struct SurfaceParams {
private:
static constexpr std::array<unsigned int, 18> 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<std::size_t>(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<u32> 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<u32> GetRect() const {
return {0, height, width, 0};
}
Common::Rectangle<u32> 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<u32> GetSubRect(const SurfaceParams& sub_surface) const;
Common::Rectangle<u32> 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 * A watcher that notifies whether a cached surface has been changed. This is useful for caching
* surface collection objects, including texture cube and mipmap. * surface collection objects, including texture cube and mipmap.
@ -345,6 +137,8 @@ private:
}; };
struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> { struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface> {
CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {}
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const;
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
@ -422,6 +216,7 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
} }
private: private:
RasterizerCacheOpenGL& owner;
std::list<std::weak_ptr<SurfaceWatcher>> watchers; std::list<std::weak_ptr<SurfaceWatcher>> watchers;
}; };
@ -519,8 +314,12 @@ private:
OGLProgram d24s8_abgr_shader; OGLProgram d24s8_abgr_shader;
GLint d24s8_abgr_tbo_size_u_id; GLint d24s8_abgr_tbo_size_u_id;
GLint d24s8_abgr_viewport_u_id; GLint d24s8_abgr_viewport_u_id;
u16 resolution_scale_factor;
std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache; std::unordered_map<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
public:
std::unique_ptr<TextureFilterer> texture_filterer;
}; };
struct FormatTuple { struct FormatTuple {

View File

@ -38,13 +38,6 @@ constexpr GLuint ShadowTexturePZ = 5;
constexpr GLuint ShadowTextureNZ = 6; constexpr GLuint ShadowTextureNZ = 6;
} // namespace ImageUnits } // namespace ImageUnits
struct Viewport {
GLint x;
GLint y;
GLsizei width;
GLsizei height;
};
class OpenGLState { class OpenGLState {
public: public:
struct { struct {
@ -142,7 +135,12 @@ public:
GLsizei height; GLsizei height;
} scissor; } scissor;
Viewport viewport; struct {
GLint x;
GLint y;
GLsizei width;
GLsizei height;
} viewport;
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE

View File

@ -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<u32> 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<u32> 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<u32>(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<u32>(x0, y0 + sub_surface.height, x0 + sub_surface.width, y0);
}
Common::Rectangle<u32> 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

View File

@ -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 <array>
#include <climits>
#include <boost/icl/interval.hpp>
#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<CachedSurface>;
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
struct SurfaceParams {
private:
static constexpr std::array<unsigned int, 18> 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<std::size_t>(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<u32> 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<u32> GetRect() const {
return {0, height, width, 0};
}
Common::Rectangle<u32> 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<u32> GetSubRect(const SurfaceParams& sub_surface) const;
Common::Rectangle<u32> 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

View File

@ -32,7 +32,6 @@
#include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/gl_vars.h"
#include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/post_processing_opengl.h"
#include "video_core/renderer_opengl/renderer_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" #include "video_core/video_core.h"
namespace Frontend { namespace Frontend {
@ -1179,14 +1178,10 @@ VideoCore::ResultStatus RendererOpenGL::Init() {
RefreshRasterizerSetting(); RefreshRasterizerSetting();
TextureFilterManager::GetInstance().Reset();
return VideoCore::ResultStatus::Success; return VideoCore::ResultStatus::Success;
} }
/// Shutdown the renderer /// Shutdown the renderer
void RendererOpenGL::ShutDown() { void RendererOpenGL::ShutDown() {}
TextureFilterManager::GetInstance().Destroy();
}
} // namespace OpenGL } // namespace OpenGL

View File

@ -42,18 +42,17 @@
namespace OpenGL { 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 OpenGLState cur_state = OpenGLState::GetCurState();
const auto setup_temp_tex = [this, scale_factor](TempTex& texture, GLint internal_format, const auto setup_temp_tex = [this](TempTex& texture, GLint internal_format, GLint format) {
GLint format) {
texture.fbo.Create(); texture.fbo.Create();
texture.tex.Create(); texture.tex.Create();
state.draw.draw_framebuffer = texture.fbo.handle; state.draw.draw_framebuffer = texture.fbo.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, texture.tex.handle); glBindTexture(GL_TEXTURE_RECTANGLE, texture.tex.handle);
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internal_format, 1024 * scale_factor, glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internal_format, 1024 * internal_scale_factor,
1024 * scale_factor, 0, format, GL_HALF_FLOAT, nullptr); 1024 * internal_scale_factor, 0, format, GL_HALF_FLOAT, nullptr);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE,
texture.tex.handle, 0); texture.tex.handle, 0);
}; };
@ -61,7 +60,6 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterInterface(sc
setup_temp_tex(XY, GL_RG16F, GL_RG); setup_temp_tex(XY, GL_RG16F, GL_RG);
vao.Create(); vao.Create();
out_fbo.Create();
for (std::size_t idx = 0; idx < samplers.size(); ++idx) { for (std::size_t idx = 0; idx < samplers.size(); ++idx) {
samplers[idx].Create(); samplers[idx].Create();
@ -86,30 +84,26 @@ Anime4kUltrafast::Anime4kUltrafast(u16 scale_factor) : TextureFilterInterface(sc
state.draw.shader_program = refine_program.handle; state.draw.shader_program = refine_program.handle;
state.Apply(); state.Apply();
glUniform1i(glGetUniformLocation(refine_program.handle, "LUMAD"), 1); glUniform1i(glGetUniformLocation(refine_program.handle, "LUMAD"), 1);
glUniform1f(glGetUniformLocation(refine_program.handle, "final_scale"),
static_cast<GLfloat>(internal_scale_factor) / scale_factor);
cur_state.Apply(); cur_state.Apply();
} }
void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle<u32>& rect, void Anime4kUltrafast::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect,
std::size_t buffer_offset) { GLuint dst_tex, const Common::Rectangle<u32>& dst_rect,
GLuint read_fb_handle, GLuint draw_fb_handle) {
const OpenGLState cur_state = OpenGLState::GetCurState(); const OpenGLState cur_state = OpenGLState::GetCurState();
OGLTexture src_tex; state.viewport = {static_cast<GLint>(src_rect.left * internal_scale_factor),
src_tex.Create(); static_cast<GLint>(src_rect.bottom * internal_scale_factor),
static_cast<GLsizei>(src_rect.GetWidth() * internal_scale_factor),
state.viewport = RectToViewport(rect); static_cast<GLsizei>(src_rect.GetHeight() * internal_scale_factor)};
state.texture_units[0].texture_2d = src_tex;
state.texture_units[0].texture_2d = src_tex.handle;
state.draw.draw_framebuffer = XY.fbo.handle; state.draw.draw_framebuffer = XY.fbo.handle;
state.draw.shader_program = gradient_x_program.handle; state.draw.shader_program = gradient_x_program.handle;
state.Apply(); state.Apply();
const FormatTuple tuple = GetFormatTuple(surface.pixel_format);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(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); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, LUMAD.tex.handle); glBindTexture(GL_TEXTURE_RECTANGLE, LUMAD.tex.handle);
glActiveTexture(GL_TEXTURE2); glActiveTexture(GL_TEXTURE2);
@ -124,14 +118,17 @@ void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle<u32
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// refine pass // refine pass
state.draw.draw_framebuffer = out_fbo.handle; state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
static_cast<GLsizei>(dst_rect.GetWidth()),
static_cast<GLsizei>(dst_rect.GetHeight())};
state.draw.draw_framebuffer = draw_fb_handle;
state.draw.shader_program = refine_program.handle; state.draw.shader_program = refine_program.handle;
state.Apply(); 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); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
cur_state.Apply(); cur_state.Apply();
} }

View File

@ -6,29 +6,25 @@
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.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 { namespace OpenGL {
class Anime4kUltrafast : public TextureFilterInterface { class Anime4kUltrafast : public TextureFilterBase {
public: public:
static TextureFilterInfo GetInfo() { static constexpr std::string_view NAME = "Anime4K Ultrafast";
TextureFilterInfo info;
info.name = "Anime4K Ultrafast";
info.clamp_scale = {2, 2};
info.constructor = std::make_unique<Anime4kUltrafast, u16>;
return info;
}
Anime4kUltrafast(u16 scale_factor); explicit Anime4kUltrafast(u16 scale_factor);
void scale(CachedSurface& surface, const Common::Rectangle<u32>& rect, void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
std::size_t buffer_offset) override; const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
GLuint draw_fb_handle) override;
private: private:
static constexpr u8 internal_scale_factor = 2;
OpenGLState state{}; OpenGLState state{};
OGLVertexArray vao; OGLVertexArray vao;
OGLFramebuffer out_fbo;
struct TempTex { struct TempTex {
OGLTexture tex; OGLTexture tex;

View File

@ -8,6 +8,8 @@ uniform sampler2D HOOKED;
uniform sampler2DRect LUMAD; uniform sampler2DRect LUMAD;
uniform sampler2DRect LUMAG; uniform sampler2DRect LUMAG;
uniform float final_scale;
const float LINE_DETECT_THRESHOLD = 0.4; const float LINE_DETECT_THRESHOLD = 0.4;
const float STRENGTH = 0.6; const float STRENGTH = 0.6;
@ -24,7 +26,7 @@ vec4 getAverage(vec4 cc, vec4 a, vec4 b, vec4 c) {
#define GetRGBAL(offset) \ #define GetRGBAL(offset) \
RGBAL(textureOffset(HOOKED, tex_coord, 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) { float min3v(float a, float b, float c) {
return min(min(a, b), c); return min(min(a, b), c);

View File

@ -10,45 +10,36 @@
namespace OpenGL { 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()); program.Create(tex_coord_vert.data(), bicubic_frag.data());
vao.Create(); vao.Create();
draw_fbo.Create();
src_sampler.Create(); src_sampler.Create();
state.draw.shader_program = program.handle; state.draw.shader_program = program.handle;
state.draw.vertex_array = vao.handle; state.draw.vertex_array = vao.handle;
state.draw.shader_program = program.handle; state.draw.shader_program = program.handle;
state.draw.draw_framebuffer = draw_fbo.handle;
state.texture_units[0].sampler = src_sampler.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_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_MAG_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_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(src_sampler.handle, GL_TEXTURE_WRAP_T, 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<u32>& rect, void Bicubic::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
std::size_t buffer_offset) { const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
GLuint draw_fb_handle) {
const OpenGLState cur_state = OpenGLState::GetCurState(); const OpenGLState cur_state = OpenGLState::GetCurState();
state.texture_units[0].texture_2d = src_tex;
OGLTexture src_tex; state.draw.draw_framebuffer = draw_fb_handle;
src_tex.Create(); state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
state.texture_units[0].texture_2d = src_tex.handle; static_cast<GLsizei>(dst_rect.GetWidth()),
static_cast<GLsizei>(dst_rect.GetHeight())};
state.viewport = RectToViewport(rect);
state.Apply(); state.Apply();
const FormatTuple tuple = GetFormatTuple(surface.pixel_format); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(surface.stride)); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
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);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
cur_state.Apply(); cur_state.Apply();
} }

View File

@ -6,27 +6,24 @@
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.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 { namespace OpenGL {
class Bicubic : public TextureFilterInterface {
public:
static TextureFilterInfo GetInfo() {
TextureFilterInfo info;
info.name = "Bicubic";
info.constructor = std::make_unique<Bicubic, u16>;
return info;
}
Bicubic(u16 scale_factor); class Bicubic : public TextureFilterBase {
void scale(CachedSurface& surface, const Common::Rectangle<u32>& rect, public:
std::size_t buffer_offset) override; static constexpr std::string_view NAME = "Bicubic";
explicit Bicubic(u16 scale_factor);
void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
GLuint draw_fb_handle) override;
private: private:
OpenGLState state{}; OpenGLState state{};
OGLProgram program{}; OGLProgram program{};
OGLVertexArray vao{}; OGLVertexArray vao{};
OGLFramebuffer draw_fbo{};
OGLSampler src_sampler{}; OGLSampler src_sampler{};
}; };
} // namespace OpenGL } // namespace OpenGL

View File

@ -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<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& 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

View File

@ -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 <functional>
#include <string_view>
#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<u32>& rect,
std::size_t buffer_offset) = 0;
virtual ~TextureFilterInterface() = default;
protected:
Viewport RectToViewport(const Common::Rectangle<u32>& 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<std::unique_ptr<TextureFilterInterface>(u16 scale_factor)> constructor;
};
} // namespace OpenGL

View File

@ -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<u32>& rect) {
return {
static_cast<GLint>(rect.left) * scale_factor,
static_cast<GLint>(rect.top) * scale_factor,
static_cast<GLsizei>(rect.GetWidth()) * scale_factor,
static_cast<GLsizei>(rect.GetHeight()) * scale_factor,
};
}
namespace {
template <typename T>
std::pair<std::string_view, TextureFilterInfo> 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<std::string_view, TextureFilterInfo, TextureFilterManager::FilterNameComp>&
TextureFilterManager::TextureFilterMap() {
static const std::map<std::string_view, TextureFilterInfo, FilterNameComp> filter_map{
FilterMapPair<NoFilter>(),
FilterMapPair<Anime4kUltrafast>(),
FilterMapPair<Bicubic>(),
FilterMapPair<XbrzFreescale>(),
};
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<std::mutex> 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<std::mutex> 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

View File

@ -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 <atomic>
#include <map>
#include <memory>
#include <mutex>
#include <string_view>
#include <tuple>
#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<std::string_view, TextureFilterInfo, FilterNameComp>& 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<bool> updated{false};
std::mutex mutex;
std::string name{"none"};
u16 scale_factor{1};
std::unique_ptr<TextureFilterInterface> filter;
};
} // namespace OpenGL

View File

@ -0,0 +1,86 @@
/// Copyright 2020 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <functional>
#include <unordered_map>
#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<std::unique_ptr<TextureFilterBase>(u16)>;
template <typename T>
std::pair<std::string_view, TextureFilterContructor> FilterMapPair() {
return {T::NAME, std::make_unique<T, u16>};
};
static const std::unordered_map<std::string_view, TextureFilterContructor> filter_map{
{TextureFilterer::NONE, [](u16) { return nullptr; }},
FilterMapPair<Anime4kUltrafast>(),
FilterMapPair<Bicubic>(),
FilterMapPair<XbrzFreescale>(),
};
} // 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<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& 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<std::string_view> TextureFilterer::GetFilterNames() {
std::vector<std::string_view> 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

View File

@ -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 <memory>
#include <string_view>
#include <vector>
#include <glad/glad.h>
#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<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& dst_rect, SurfaceParams::SurfaceType type,
GLuint read_fb_handle, GLuint draw_fb_handle);
static std::vector<std::string_view> GetFilterNames();
private:
std::string_view filter_name = NONE;
std::unique_ptr<TextureFilterBase> filter;
};
} // namespace OpenGL

View File

@ -48,12 +48,11 @@
namespace OpenGL { namespace OpenGL {
XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterInterface(scale_factor) { XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterBase(scale_factor) {
const OpenGLState cur_state = OpenGLState::GetCurState(); const OpenGLState cur_state = OpenGLState::GetCurState();
program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data()); program.Create(xbrz_freescale_vert.data(), xbrz_freescale_frag.data());
vao.Create(); vao.Create();
draw_fbo.Create();
src_sampler.Create(); src_sampler.Create();
state.draw.shader_program = program.handle; state.draw.shader_program = program.handle;
@ -68,31 +67,24 @@ XbrzFreescale::XbrzFreescale(u16 scale_factor) : TextureFilterInterface(scale_fa
cur_state.Apply(); cur_state.Apply();
state.draw.vertex_array = vao.handle; state.draw.vertex_array = vao.handle;
state.draw.shader_program = program.handle; state.draw.shader_program = program.handle;
state.draw.draw_framebuffer = draw_fbo.handle;
state.texture_units[0].sampler = src_sampler.handle; state.texture_units[0].sampler = src_sampler.handle;
} }
void XbrzFreescale::scale(CachedSurface& surface, const Common::Rectangle<u32>& rect, void XbrzFreescale::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
std::size_t buffer_offset) { const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
GLuint draw_fb_handle) {
const OpenGLState cur_state = OpenGLState::GetCurState(); const OpenGLState cur_state = OpenGLState::GetCurState();
OGLTexture src_tex; state.texture_units[0].texture_2d = src_tex;
src_tex.Create(); state.draw.draw_framebuffer = draw_fb_handle;
state.texture_units[0].texture_2d = src_tex.handle; state.viewport = {static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
static_cast<GLsizei>(dst_rect.GetWidth()),
state.viewport = RectToViewport(rect); static_cast<GLsizei>(dst_rect.GetHeight())};
state.Apply(); state.Apply();
const FormatTuple tuple = GetFormatTuple(surface.pixel_format); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(surface.stride)); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
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);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
cur_state.Apply(); cur_state.Apply();
} }

View File

@ -6,28 +6,23 @@
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.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 { namespace OpenGL {
class XbrzFreescale : public TextureFilterInterface { class XbrzFreescale : public TextureFilterBase {
public: public:
static TextureFilterInfo GetInfo() { static constexpr std::string_view NAME = "xBRZ freescale";
TextureFilterInfo info;
info.name = "xBRZ freescale";
info.constructor = std::make_unique<XbrzFreescale, u16>;
return info;
}
XbrzFreescale(u16 scale_factor); explicit XbrzFreescale(u16 scale_factor);
void scale(CachedSurface& surface, const Common::Rectangle<u32>& rect, void Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
std::size_t buffer_offset) override; const Common::Rectangle<u32>& dst_rect, GLuint read_fb_handle,
GLuint draw_fb_handle) override;
private: private:
OpenGLState state{}; OpenGLState state{};
OGLProgram program{}; OGLProgram program{};
OGLVertexArray vao{}; OGLVertexArray vao{};
OGLFramebuffer draw_fbo{};
OGLSampler src_sampler{}; OGLSampler src_sampler{};
}; };
} // namespace OpenGL } // namespace OpenGL

View File

@ -26,6 +26,7 @@ std::atomic<bool> g_use_disk_shader_cache;
std::atomic<bool> g_renderer_bg_color_update_requested; std::atomic<bool> g_renderer_bg_color_update_requested;
std::atomic<bool> g_renderer_sampler_update_requested; std::atomic<bool> g_renderer_sampler_update_requested;
std::atomic<bool> g_renderer_shader_update_requested; std::atomic<bool> g_renderer_shader_update_requested;
std::atomic<bool> g_texture_filter_update_requested;
// Screenshot // Screenshot
std::atomic<bool> g_renderer_screenshot_requested; std::atomic<bool> g_renderer_screenshot_requested;
void* g_screenshot_bits; void* g_screenshot_bits;

View File

@ -35,6 +35,7 @@ extern std::atomic<bool> g_use_disk_shader_cache;
extern std::atomic<bool> g_renderer_bg_color_update_requested; extern std::atomic<bool> g_renderer_bg_color_update_requested;
extern std::atomic<bool> g_renderer_sampler_update_requested; extern std::atomic<bool> g_renderer_sampler_update_requested;
extern std::atomic<bool> g_renderer_shader_update_requested; extern std::atomic<bool> g_renderer_shader_update_requested;
extern std::atomic<bool> g_texture_filter_update_requested;
// Screenshot // Screenshot
extern std::atomic<bool> g_renderer_screenshot_requested; extern std::atomic<bool> g_renderer_screenshot_requested;
extern void* g_screenshot_bits; extern void* g_screenshot_bits;