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:
parent
d26564d020
commit
f14e973a27
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,8 +867,11 @@ 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};
|
||||
BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type,
|
||||
read_fb_handle, draw_fb_handle);
|
||||
if (!owner.texture_filterer->Filter(unscaled_tex.handle, from_rect, texture.handle,
|
||||
scaled_rect, type, read_fb_handle, draw_fb_handle)) {
|
||||
BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type,
|
||||
read_fb_handle, draw_fb_handle);
|
||||
}
|
||||
}
|
||||
|
||||
InvalidateAllWatcher();
|
||||
@ -1232,6 +1060,10 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
|
||||
}
|
||||
|
||||
RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
|
||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||
texture_filterer = std::make_unique<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();
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
171
src/video_core/renderer_opengl/gl_surface_params.cpp
Normal file
171
src/video_core/renderer_opengl/gl_surface_params.cpp
Normal 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
|
229
src/video_core/renderer_opengl/gl_surface_params.h
Normal file
229
src/video_core/renderer_opengl/gl_surface_params.h
Normal 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
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user