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));
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<Settings::StereoRenderOption>(
sdl2_config->GetInteger("Renderer", "render_3d", 0));

View file

@ -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)

View file

@ -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();
}

View file

@ -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<int>::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<int>(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<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked();

View file

@ -131,42 +131,6 @@
</item>
</layout>
</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>
</widget>
</item>

View file

@ -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<int>(Settings::values.render_3d));
LogSetting("Stereoscopy_Factor3d", Settings::values.factor_3d);

View file

@ -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;

View file

@ -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

View file

@ -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<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,
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<u32> 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<GLint>(rect.left);
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
// 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<u32> 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<u32> 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<GLint>(stride));
@ -1027,13 +852,13 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> 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,9 +867,12 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
auto from_rect =
is_custom ? Common::Rectangle<u32>{0, custom_tex_info.height, custom_tex_info.width, 0}
: Common::Rectangle<u32>{0, rect.GetHeight(), rect.GetWidth(), 0};
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<TextureFilterer>(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<CachedSurface>();
Surface new_surface = std::make_shared<CachedSurface>(*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<CachedSurface>();
Surface surface = std::make_shared<CachedSurface>(*this);
static_cast<SurfaceParams&>(*surface) = params;
surface->texture.Create();

View file

@ -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<OpenGL::TextureCubeConfig> {
namespace OpenGL {
struct CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSet = std::set<Surface>;
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>;
using SurfaceMap =
boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, std::less,
@ -104,212 +102,6 @@ enum class ScaleMatch {
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
* surface collection objects, including texture cube and mipmap.
@ -345,6 +137,8 @@ private:
};
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 CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const;
@ -422,6 +216,7 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
}
private:
RasterizerCacheOpenGL& owner;
std::list<std::weak_ptr<SurfaceWatcher>> 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<TextureCubeConfig, CachedTextureCube> texture_cube_cache;
public:
std::unique_ptr<TextureFilterer> texture_filterer;
};
struct FormatTuple {

View file

@ -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<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/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

View file

@ -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<GLfloat>(internal_scale_factor) / scale_factor);
cur_state.Apply();
}
void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle<u32>& rect,
std::size_t buffer_offset) {
void Anime4kUltrafast::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) {
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<GLint>(src_rect.left * internal_scale_factor),
static_cast<GLint>(src_rect.bottom * internal_scale_factor),
static_cast<GLsizei>(src_rect.GetWidth() * internal_scale_factor),
static_cast<GLsizei>(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<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);
glBindTexture(GL_TEXTURE_RECTANGLE, LUMAD.tex.handle);
glActiveTexture(GL_TEXTURE2);
@ -124,14 +118,17 @@ void Anime4kUltrafast::scale(CachedSurface& surface, const Common::Rectangle<u32
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 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.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();
}

View file

@ -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<Anime4kUltrafast, u16>;
return info;
}
static constexpr std::string_view NAME = "Anime4K Ultrafast";
Anime4kUltrafast(u16 scale_factor);
void scale(CachedSurface& surface, const Common::Rectangle<u32>& rect,
std::size_t buffer_offset) override;
explicit Anime4kUltrafast(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:
static constexpr u8 internal_scale_factor = 2;
OpenGLState state{};
OGLVertexArray vao;
OGLFramebuffer out_fbo;
struct TempTex {
OGLTexture tex;

View file

@ -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);

View file

@ -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<u32>& rect,
std::size_t buffer_offset) {
void Bicubic::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) {
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<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
static_cast<GLsizei>(dst_rect.GetWidth()),
static_cast<GLsizei>(dst_rect.GetHeight())};
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]);
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();
}

View file

@ -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<Bicubic, u16>;
return info;
}
Bicubic(u16 scale_factor);
void scale(CachedSurface& surface, const Common::Rectangle<u32>& 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<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& 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

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 {
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<u32>& rect,
std::size_t buffer_offset) {
void XbrzFreescale::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) {
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<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
static_cast<GLsizei>(dst_rect.GetWidth()),
static_cast<GLsizei>(dst_rect.GetHeight())};
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]);
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();
}

View file

@ -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<XbrzFreescale, u16>;
return info;
}
static constexpr std::string_view NAME = "xBRZ freescale";
XbrzFreescale(u16 scale_factor);
void scale(CachedSurface& surface, const Common::Rectangle<u32>& rect,
std::size_t buffer_offset) override;
explicit XbrzFreescale(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:
OpenGLState state{};
OGLProgram program{};
OGLVertexArray vao{};
OGLFramebuffer draw_fbo{};
OGLSampler src_sampler{};
};
} // 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_sampler_update_requested;
std::atomic<bool> g_renderer_shader_update_requested;
std::atomic<bool> g_texture_filter_update_requested;
// Screenshot
std::atomic<bool> g_renderer_screenshot_requested;
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_sampler_update_requested;
extern std::atomic<bool> g_renderer_shader_update_requested;
extern std::atomic<bool> g_texture_filter_update_requested;
// Screenshot
extern std::atomic<bool> g_renderer_screenshot_requested;
extern void* g_screenshot_bits;