This commit is contained in:
KojoZero 2026-06-05 09:36:14 +01:00 committed by GitHub
commit 25198f8e71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
92 changed files with 26756 additions and 270 deletions

View file

@ -47,6 +47,9 @@ foreach(KEY IN ITEMS
"frame_limit" "frame_limit"
"turbo_limit" "turbo_limit"
"texture_filter" "texture_filter"
"antialiasing_filter"
"output_scaling"
"fsr_sharpness"
"texture_sampling" "texture_sampling"
"delay_game_render_thread_us" "delay_game_render_thread_us"
"simulate_3ds_gpu_timings" "simulate_3ds_gpu_timings"

View file

@ -28,6 +28,17 @@ QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C; border: 1px solid #76797C;
} }
QPushButton#StatusBarButton {
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#StatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#button_reset_defaults { QPushButton#button_reset_defaults {
min-width: 57px; min-width: 57px;
padding: 4px 8px; padding: 4px 8px;

View file

@ -44,6 +44,9 @@ object SettingKeys {
external fun frame_limit(): String external fun frame_limit(): String
external fun turbo_limit(): String external fun turbo_limit(): String
external fun texture_filter(): String external fun texture_filter(): String
external fun antialiasing_filter(): String
external fun fsr_sharpness(): String
external fun output_scaling(): String
external fun texture_sampling(): String external fun texture_sampling(): String
external fun delay_game_render_thread_us(): String external fun delay_game_render_thread_us(): String
external fun simulate_3ds_gpu_timings(): String external fun simulate_3ds_gpu_timings(): String

View file

@ -48,6 +48,9 @@ enum class IntSetting(
PORTRAIT_BOTTOM_HEIGHT(SettingKeys.custom_portrait_bottom_height(),Settings.SECTION_LAYOUT,480), PORTRAIT_BOTTOM_HEIGHT(SettingKeys.custom_portrait_bottom_height(),Settings.SECTION_LAYOUT,480),
AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0), AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0),
CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100), CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100),
ANTIALIASING_FILTER(SettingKeys.antialiasing_filter(), Settings.SECTION_RENDERER, 0),
FSR_SHARPNESS(SettingKeys.fsr_sharpness(), Settings.SECTION_RENDERER, 50),
OUTPUT_SCALING(SettingKeys.output_scaling(), Settings.SECTION_RENDERER, 2),
TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0), TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0),
TEXTURE_SAMPLING(SettingKeys.texture_sampling(), Settings.SECTION_RENDERER, 0), TEXTURE_SAMPLING(SettingKeys.texture_sampling(), Settings.SECTION_RENDERER, 0),
USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, 1), USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, 1),

View file

@ -931,14 +931,48 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
) )
) )
add( add(
SwitchSetting( SingleChoiceSetting(
BooleanSetting.LINEAR_FILTERING, IntSetting.OUTPUT_SCALING,
R.string.linear_filtering, R.string.output_scaling_name,
R.string.linear_filtering_description, R.string.output_scaling_description,
BooleanSetting.LINEAR_FILTERING.key, R.array.outputScalingNames,
BooleanSetting.LINEAR_FILTERING.defaultValue R.array.outputScalingValues,
IntSetting.OUTPUT_SCALING.key,
IntSetting.OUTPUT_SCALING.defaultValue
) )
) )
add(
SliderSetting(
IntSetting.FSR_SHARPNESS,
R.string.fsr_sharpness_name,
R.string.fsr_sharpness_description,
0,
100,
"%",
IntSetting.FSR_SHARPNESS.key,
IntSetting.FSR_SHARPNESS.defaultValue.toFloat()
)
)
add(
SingleChoiceSetting(
IntSetting.ANTIALIASING_FILTER,
R.string.antialiasing_filter_name,
R.string.antialiasing_filter_description,
R.array.antialiasingFilterNames,
R.array.antialiasingFilterValues,
IntSetting.ANTIALIASING_FILTER.key,
IntSetting.ANTIALIASING_FILTER.defaultValue
)
)
// add(
// SwitchSetting(
// BooleanSetting.LINEAR_FILTERING,
// R.string.linear_filtering,
// R.string.linear_filtering_description,
// BooleanSetting.LINEAR_FILTERING.key,
// BooleanSetting.LINEAR_FILTERING.defaultValue
// )
// )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.SHADERS_ACCURATE_MUL, BooleanSetting.SHADERS_ACCURATE_MUL,

View file

@ -153,6 +153,9 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_disk_shader_cache); ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.use_vsync); ReadSetting("Renderer", Settings::values.use_vsync);
ReadSetting("Renderer", Settings::values.texture_filter); ReadSetting("Renderer", Settings::values.texture_filter);
ReadSetting("Renderer", Settings::values.output_scaling);
ReadSetting("Renderer", Settings::values.fsr_sharpness);
ReadSetting("Renderer", Settings::values.antialiasing_filter);
ReadSetting("Renderer", Settings::values.texture_sampling); ReadSetting("Renderer", Settings::values.texture_sampling);
ReadSetting("Renderer", Settings::values.turbo_limit); ReadSetting("Renderer", Settings::values.turbo_limit);
// Workaround to map Android setting for enabling the frame limiter to the format Citra expects // Workaround to map Android setting for enabling the frame limiter to the format Citra expects

View file

@ -190,6 +190,17 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# Loaded from shaders/anaglyph # Loaded from shaders/anaglyph
)") DECLARE_KEY(anaglyph_shader_name) BOOST_HANA_STRING(R"( )") DECLARE_KEY(anaglyph_shader_name) BOOST_HANA_STRING(R"(
# Scaling mode for image output
# 0: Nearest, 1: Bilinear, 2 (default): Adaptive, 3: FSR 1, 4: Sharp Bilinear
)") DECLARE_KEY(output_scaling) BOOST_HANA_STRING(R"(
# Antialiasing filter to use
# 0 (default): None, 1: FXAA, 2: SMAA
)") DECLARE_KEY(antialiasing_filter) BOOST_HANA_STRING(R"(
# 0 - 100: Strength of FSR Sharpening. 50 (default)
)") DECLARE_KEY(fsr_sharpness) BOOST_HANA_STRING(R"(
# Whether to enable linear filtering or not # Whether to enable linear filtering or not
# This is required for some shaders to work correctly # This is required for some shaders to work correctly
# 0: Nearest, 1 (default): Linear # 0: Nearest, 1 (default): Linear

View file

@ -297,6 +297,34 @@
<item>5</item> <item>5</item>
</integer-array> </integer-array>
<string-array name="outputScalingNames">
<item>@string/nearest</item>
<item>@string/bilinear</item>
<item>@string/adaptive</item>
<item>@string/fsr</item>
<item>@string/sharpbilinear</item>
</string-array>
<integer-array name="outputScalingValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</integer-array>
<string-array name="antialiasingFilterNames">
<item>@string/none</item>
<item>@string/fxaa</item>
<item>@string/smaa</item>
</string-array>
<integer-array name="antialiasingFilterValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="textureSamplingNames"> <string-array name="textureSamplingNames">
<item>@string/game_controlled</item> <item>@string/game_controlled</item>
<item>@string/nearest_neighbor</item> <item>@string/nearest_neighbor</item>

View file

@ -264,6 +264,12 @@
<string name="use_integer_scaling_description">Scales the screens with an integer multiplier of the original 3DS screen. For layouts with two different screen sizes, the largest screen is integer-scaled.</string> <string name="use_integer_scaling_description">Scales the screens with an integer multiplier of the original 3DS screen. For layouts with two different screen sizes, the largest screen is integer-scaled.</string>
<string name="texture_filter_name">Texture Filter</string> <string name="texture_filter_name">Texture Filter</string>
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string> <string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
<string name="antialiasing_filter_name">Antialiasing Filter</string>
<string name="antialiasing_filter_description">Reduces the amount of aliasing, resulting in smoother edges. FXAA is extremely lightweight but more aggressive and lower quality. SMAA is heavier but is higher quality and more selective with the edges it smooths.</string>
<string name="output_scaling_name">Output Scaling</string>
<string name="output_scaling_description">The method by which the game output is scaled to the screen. Nearest doesn\'t smooth visuals but causes uneven pixels at non-integer ratios. Bilinear smooths visuals but may cause aliasing when downscaling. Adaptive uses bilinear for upscaling, but uses area sampling to downscale without creating aliasing. FSR 1 is an upscaler which allows sharpening and allows better visuals without increasing the internal resolution, but needs to be used with antialiasing and a minimum internal resolution of 2x Native. Sharp Bilinear looks similar to nearest but is slightly smoothed to prevent uneven pixels at non-integer ratios.</string>
<string name="fsr_sharpness_name">FSR Sharpness</string>
<string name="fsr_sharpness_description">Specifies the strength of the sharpening pass when using FSR</string>
<string name="delay_render_thread">Delay Game Render Thread</string> <string name="delay_render_thread">Delay Game Render Thread</string>
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string> <string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string>
<string name="simulate_3ds_gpu_timings">Simulate 3DS GPU Timings</string> <string name="simulate_3ds_gpu_timings">Simulate 3DS GPU Timings</string>
@ -797,6 +803,18 @@
<string name="xbrz">xBRZ</string> <string name="xbrz">xBRZ</string>
<string name="mmpx">MMPX</string> <string name="mmpx">MMPX</string>
<!-- Output scaling names -->
<string name="nearest">Nearest</string>
<string name="bilinear">Bilinear</string>
<string name="adaptive">Adaptive</string>
<string name="fsr">AMD FSR 1</string>
<string name="sharpbilinear">Sharp Bilinear</string>
<!-- Antialiasing names -->
<!-- None is already defined -->
<string name="fxaa">FXAA</string>
<string name="smaa">SMAA</string>
<!-- Texture Sampling names --> <!-- Texture Sampling names -->
<string name="game_controlled">Game Controlled</string> <string name="game_controlled">Game Controlled</string>
<string name="nearest_neighbor">Nearest Neighbor</string> <string name="nearest_neighbor">Nearest Neighbor</string>

View file

@ -630,6 +630,23 @@ void GMainWindow::InitializeWidgets() {
statusBar()->insertPermanentWidget(0, graphics_api_button); statusBar()->insertPermanentWidget(0, graphics_api_button);
// Setup Output Scaling button
output_scaling_button = new QPushButton();
output_scaling_button->setObjectName(QStringLiteral("StatusBarButton"));
output_scaling_button->setFocusPolicy(Qt::NoFocus);
UpdateOutputScalingIndicator();
connect(output_scaling_button, &QPushButton::clicked, this, [this] { UpdateOutputScalingIndicator(true); });
statusBar()->insertPermanentWidget(1, output_scaling_button);
// Setup Antialiasing Filter button
antialiasing_filter_button = new QPushButton();
antialiasing_filter_button->setObjectName(QStringLiteral("StatusBarButton"));
antialiasing_filter_button->setFocusPolicy(Qt::NoFocus);
UpdateAntialiasingFilterIndicator();
connect(antialiasing_filter_button, &QPushButton::clicked, this, [this] { UpdateAntialiasingFilterIndicator(true); });
statusBar()->insertPermanentWidget(2, antialiasing_filter_button);
// Setup Volume button and slider
volume_popup = new QWidget(this); volume_popup = new QWidget(this);
volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup); volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup);
volume_popup->setLayout(new QVBoxLayout()); volume_popup->setLayout(new QVBoxLayout());
@ -660,7 +677,7 @@ void GMainWindow::InitializeWidgets() {
bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height()); bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height());
volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height()))); volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height())));
}); });
statusBar()->insertPermanentWidget(1, volume_button); statusBar()->insertPermanentWidget(3, volume_button);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText()); statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon()); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
@ -3783,8 +3800,45 @@ void GMainWindow::UpdateAPIIndicator(bool update) {
graphics_api_button->setStyleSheet(style_sheet); graphics_api_button->setStyleSheet(style_sheet);
} }
void GMainWindow::UpdateOutputScalingIndicator(bool update) {
static std::array output_scaling_options = {
QStringLiteral("NEAREST"),
QStringLiteral("BILINEAR"),
QStringLiteral("ADAPTIVE"),
QStringLiteral("FSR"),
QStringLiteral("SHARP BILINEAR"),
};
u32 selection_index = static_cast<u32>(Settings::values.output_scaling.GetValue());
if (update) {
selection_index = (selection_index + 1) % output_scaling_options.size();
Settings::values.output_scaling = static_cast<Settings::OutputScaling>(selection_index);
}
output_scaling_button->setText(output_scaling_options[selection_index]);
}
void GMainWindow::UpdateAntialiasingFilterIndicator(bool update) {
static std::array antialiasing_filter_options = {
QStringLiteral("NO AA"),
QStringLiteral("FXAA"),
QStringLiteral("SMAA"),
};
u32 selection_index = static_cast<u32>(Settings::values.antialiasing_filter.GetValue());
if (update) {
selection_index = (selection_index + 1) % antialiasing_filter_options.size();
Settings::values.antialiasing_filter = static_cast<Settings::AntiAliasingFilter>(selection_index);
}
antialiasing_filter_button->setText(antialiasing_filter_options[selection_index]);
}
void GMainWindow::UpdateStatusButtons() { void GMainWindow::UpdateStatusButtons() {
UpdateAPIIndicator(); UpdateAPIIndicator();
UpdateOutputScalingIndicator();
UpdateAntialiasingFilterIndicator();
UpdateVolumeUI(); UpdateVolumeUI();
} }

View file

@ -333,6 +333,8 @@ private:
void OpenPerGameConfiguration(u64 title_id, const QString& file_name); void OpenPerGameConfiguration(u64 title_id, const QString& file_name);
void UpdateVolumeUI(); void UpdateVolumeUI();
void UpdateAPIIndicator(bool update = false); void UpdateAPIIndicator(bool update = false);
void UpdateOutputScalingIndicator(bool update = false);
void UpdateAntialiasingFilterIndicator(bool update = false);
void UpdateStatusButtons(); void UpdateStatusButtons();
#ifdef __unix__ #ifdef __unix__
void SetGamemodeEnabled(bool state); void SetGamemodeEnabled(bool state);
@ -359,6 +361,8 @@ private:
QLabel* emu_frametime_label = nullptr; QLabel* emu_frametime_label = nullptr;
QPushButton* graphics_api_button = nullptr; QPushButton* graphics_api_button = nullptr;
QPushButton* volume_button = nullptr; QPushButton* volume_button = nullptr;
QPushButton* output_scaling_button = nullptr;
QPushButton* antialiasing_filter_button = nullptr;
QWidget* volume_popup = nullptr; QWidget* volume_popup = nullptr;
QSlider* volume_slider = nullptr; QSlider* volume_slider = nullptr;
QTimer status_bar_update_timer; QTimer status_bar_update_timer;

View file

@ -725,6 +725,9 @@ void QtConfig::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_blue); ReadGlobalSetting(Settings::values.bg_blue);
ReadGlobalSetting(Settings::values.texture_filter); ReadGlobalSetting(Settings::values.texture_filter);
ReadGlobalSetting(Settings::values.antialiasing_filter);
ReadGlobalSetting(Settings::values.output_scaling);
ReadGlobalSetting(Settings::values.fsr_sharpness);
ReadGlobalSetting(Settings::values.texture_sampling); ReadGlobalSetting(Settings::values.texture_sampling);
ReadGlobalSetting(Settings::values.delay_game_render_thread_us); ReadGlobalSetting(Settings::values.delay_game_render_thread_us);
@ -1274,6 +1277,9 @@ void QtConfig::SaveRendererValues() {
WriteGlobalSetting(Settings::values.bg_blue); WriteGlobalSetting(Settings::values.bg_blue);
WriteGlobalSetting(Settings::values.texture_filter); WriteGlobalSetting(Settings::values.texture_filter);
WriteGlobalSetting(Settings::values.antialiasing_filter);
WriteGlobalSetting(Settings::values.output_scaling);
WriteGlobalSetting(Settings::values.fsr_sharpness);
WriteGlobalSetting(Settings::values.texture_sampling); WriteGlobalSetting(Settings::values.texture_sampling);
WriteGlobalSetting(Settings::values.delay_game_render_thread_us); WriteGlobalSetting(Settings::values.delay_game_render_thread_us);

View file

@ -27,6 +27,8 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex)); updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex));
}); });
connect(ui->fsr_sharpness_slider, &QSlider::valueChanged, this,
&ConfigureEnhancements::SetFSRSharpnessIndicatorText);
ui->toggle_preload_textures->setEnabled(ui->toggle_custom_textures->isChecked()); ui->toggle_preload_textures->setEnabled(ui->toggle_custom_textures->isChecked());
ui->toggle_async_custom_loading->setEnabled(ui->toggle_custom_textures->isChecked()); ui->toggle_async_custom_loading->setEnabled(ui->toggle_custom_textures->isChecked());
connect(ui->toggle_custom_textures, &QCheckBox::toggled, this, [this] { connect(ui->toggle_custom_textures, &QCheckBox::toggled, this, [this] {
@ -35,12 +37,18 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
if (!ui->toggle_preload_textures->isEnabled()) if (!ui->toggle_preload_textures->isEnabled())
ui->toggle_preload_textures->setChecked(false); ui->toggle_preload_textures->setChecked(false);
}); });
connect(ui->output_scaling_combobox, &QComboBox::currentIndexChanged, this, &ConfigureEnhancements::SetFSRSharpnessEnabled);
} }
ConfigureEnhancements::~ConfigureEnhancements() = default; ConfigureEnhancements::~ConfigureEnhancements() = default;
void ConfigureEnhancements::SetConfiguration() { void ConfigureEnhancements::SetConfiguration() {
const s32 sharpness =
static_cast<s32>(Settings::values.fsr_sharpness.GetValue());
ui->fsr_sharpness_slider->setValue(sharpness);
SetFSRSharpnessIndicatorText(ui->fsr_sharpness_slider->sliderPosition());
if (!Settings::IsConfiguringGlobal()) { if (!Settings::IsConfiguringGlobal()) {
ConfigurationShared::SetPerGameSetting(ui->resolution_factor_combobox, ConfigurationShared::SetPerGameSetting(ui->resolution_factor_combobox,
&Settings::values.resolution_factor); &Settings::values.resolution_factor);
@ -48,13 +56,25 @@ void ConfigureEnhancements::SetConfiguration() {
&Settings::values.texture_filter); &Settings::values.texture_filter);
ConfigurationShared::SetHighlight(ui->widget_texture_filter, ConfigurationShared::SetHighlight(ui->widget_texture_filter,
!Settings::values.texture_filter.UsingGlobal()); !Settings::values.texture_filter.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->antialiasing_filter_combobox,
&Settings::values.antialiasing_filter);
ConfigurationShared::SetHighlight(ui->widget_antialiasing_filter,
!Settings::values.antialiasing_filter.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->output_scaling_combobox,
&Settings::values.output_scaling);
ConfigurationShared::SetHighlight(ui->widget_output_scaling,
!Settings::values.output_scaling.UsingGlobal());
} else { } else {
ui->resolution_factor_combobox->setCurrentIndex( ui->resolution_factor_combobox->setCurrentIndex(
Settings::values.resolution_factor.GetValue()); Settings::values.resolution_factor.GetValue());
ui->texture_filter_combobox->setCurrentIndex( ui->texture_filter_combobox->setCurrentIndex(
static_cast<int>(Settings::values.texture_filter.GetValue())); static_cast<int>(Settings::values.texture_filter.GetValue()));
ui->antialiasing_filter_combobox->setCurrentIndex(
static_cast<int>(Settings::values.antialiasing_filter.GetValue()));
ui->output_scaling_combobox->setCurrentIndex(
static_cast<int>(Settings::values.output_scaling.GetValue()));
} }
ui->fsr_sharpness_slider->setEnabled(ui->output_scaling_combobox->currentIndex() == 3);
ui->render_3d_combobox->setCurrentIndex( ui->render_3d_combobox->setCurrentIndex(
static_cast<int>(Settings::values.render_3d.GetValue())); static_cast<int>(Settings::values.render_3d.GetValue()));
ui->swap_eyes_3d->setChecked(Settings::values.swap_eyes_3d.GetValue()); ui->swap_eyes_3d->setChecked(Settings::values.swap_eyes_3d.GetValue());
@ -62,7 +82,6 @@ void ConfigureEnhancements::SetConfiguration() {
ui->mono_rendering_eye->setCurrentIndex( ui->mono_rendering_eye->setCurrentIndex(
static_cast<int>(Settings::values.mono_render_option.GetValue())); static_cast<int>(Settings::values.mono_render_option.GetValue()));
updateShaders(Settings::values.render_3d.GetValue()); updateShaders(Settings::values.render_3d.GetValue());
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
ui->use_integer_scaling->setChecked(Settings::values.use_integer_scaling.GetValue()); ui->use_integer_scaling->setChecked(Settings::values.use_integer_scaling.GetValue());
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue()); ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue()); ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
@ -71,6 +90,13 @@ void ConfigureEnhancements::SetConfiguration() {
ui->disable_right_eye_render->setChecked(Settings::values.disable_right_eye_render.GetValue()); ui->disable_right_eye_render->setChecked(Settings::values.disable_right_eye_render.GetValue());
} }
void ConfigureEnhancements::SetFSRSharpnessIndicatorText(int percentage) {
ui->fsr_sharpness_indicator->setText(tr("%1%", "FSR Sharpness (e.g. 50%)").arg(percentage));
}
void ConfigureEnhancements::SetFSRSharpnessEnabled(int output) {
ui->fsr_sharpness_slider->setEnabled(output == 3);
}
void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_option) { void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_option) {
ui->shader_combobox->clear(); ui->shader_combobox->clear();
ui->shader_combobox->setEnabled(true); ui->shader_combobox->setEnabled(true);
@ -124,14 +150,17 @@ void ConfigureEnhancements::ApplyConfiguration() {
Settings::values.pp_shader_name = Settings::values.pp_shader_name =
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString(); ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
} }
Settings::values.fsr_sharpness = ui->fsr_sharpness_slider->sliderPosition();
Settings::values.disable_right_eye_render = ui->disable_right_eye_render->isChecked(); Settings::values.disable_right_eye_render = ui->disable_right_eye_render->isChecked();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
ui->toggle_linear_filter, linear_filter);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_integer_scaling, ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_integer_scaling,
ui->use_integer_scaling, use_integer_scaling); ui->use_integer_scaling, use_integer_scaling);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter, ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter,
ui->texture_filter_combobox); ui->texture_filter_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.antialiasing_filter,
ui->antialiasing_filter_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.output_scaling,
ui->output_scaling_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.dump_textures, ConfigurationShared::ApplyPerGameSetting(&Settings::values.dump_textures,
ui->toggle_dump_textures, dump_textures); ui->toggle_dump_textures, dump_textures);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.custom_textures, ConfigurationShared::ApplyPerGameSetting(&Settings::values.custom_textures,
@ -150,7 +179,8 @@ void ConfigureEnhancements::SetupPerGameUI() {
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal()); ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal());
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal()); ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal());
ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal()); ui->widget_antialiasing_filter->setEnabled(Settings::values.antialiasing_filter.UsingGlobal());
ui->widget_output_scaling->setEnabled(Settings::values.output_scaling.UsingGlobal());
ui->use_integer_scaling->setEnabled(Settings::values.use_integer_scaling.UsingGlobal()); ui->use_integer_scaling->setEnabled(Settings::values.use_integer_scaling.UsingGlobal());
ui->toggle_dump_textures->setEnabled(Settings::values.dump_textures.UsingGlobal()); ui->toggle_dump_textures->setEnabled(Settings::values.dump_textures.UsingGlobal());
ui->toggle_custom_textures->setEnabled(Settings::values.custom_textures.UsingGlobal()); ui->toggle_custom_textures->setEnabled(Settings::values.custom_textures.UsingGlobal());
@ -168,8 +198,6 @@ void ConfigureEnhancements::SetupPerGameUI() {
ui->widget_shader->setVisible(false); ui->widget_shader->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
linear_filter);
ConfigurationShared::SetColoredTristate( ConfigurationShared::SetColoredTristate(
ui->use_integer_scaling, Settings::values.use_integer_scaling, use_integer_scaling); ui->use_integer_scaling, Settings::values.use_integer_scaling, use_integer_scaling);
ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures, ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures,
@ -192,4 +220,12 @@ void ConfigureEnhancements::SetupPerGameUI() {
ConfigurationShared::SetColoredComboBox( ConfigurationShared::SetColoredComboBox(
ui->texture_filter_combobox, ui->widget_texture_filter, ui->texture_filter_combobox, ui->widget_texture_filter,
static_cast<int>(Settings::values.texture_filter.GetValue(true))); static_cast<int>(Settings::values.texture_filter.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->antialiasing_filter_combobox, ui->widget_antialiasing_filter,
static_cast<int>(Settings::values.antialiasing_filter.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->output_scaling_combobox, ui->widget_output_scaling,
static_cast<int>(Settings::values.output_scaling.GetValue(true)));
} }

View file

@ -36,7 +36,8 @@ public:
private: private:
void updateShaders(Settings::StereoRenderOption stereo_option); void updateShaders(Settings::StereoRenderOption stereo_option);
void updateTextureFilter(int index); void updateTextureFilter(int index);
void SetFSRSharpnessIndicatorText(int percentage);
void SetFSRSharpnessEnabled(int output);
std::unique_ptr<Ui::ConfigureEnhancements> ui; std::unique_ptr<Ui::ConfigureEnhancements> ui;
ConfigurationShared::CheckState linear_filter; ConfigurationShared::CheckState linear_filter;
ConfigurationShared::CheckState use_integer_scaling; ConfigurationShared::CheckState use_integer_scaling;

View file

@ -112,19 +112,237 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="use_integer_scaling"> <widget class="QCheckBox" name="use_integer_scaling">
<property name="text">
<string>Use Integer Scaling</string>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use Integer Scaling&lt;/p&gt;&lt;p&gt;Enforces that the larger screen in all layouts is an integer scale of the 240px height of the original 3DS screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use Integer Scaling&lt;/p&gt;&lt;p&gt;Enforces that the larger screen in all layouts is an integer scale of the 240px height of the original 3DS screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text">
<string>Use Integer Scaling</string>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="toggle_linear_filter"> <widget class="QWidget" name="widget_output_scaling" native="true">
<property name="text"> <property name="enabled">
<string>Enable Linear Filtering</string> <bool>true</bool>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="output_scaling_label">
<property name="text">
<string>Output Scaling</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="output_scaling_combobox">
<item>
<property name="text">
<string>Nearest</string>
</property>
</item>
<item>
<property name="text">
<string>Bilinear</string>
</property>
</item>
<item>
<property name="text">
<string>Adaptive</string>
</property>
</item>
<item>
<property name="text">
<string>AMD FidelityFX Super Resolution 1</string>
</property>
</item>
<item>
<property name="text">
<string>Sharp Bilinear</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_fsr_sharpness" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget_fsr_sharpness_1" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_17">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="fsr_sharpness_label">
<property name="text">
<string>FSR Sharpness</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_fsr_sharpness_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="fsr_sharpness_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="pageStep">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fsr_sharpness_indicator">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0 %</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_antialiasing_filter" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="antialiasing_filter_label">
<property name="text">
<string>Anti-Aliasing</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="antialiasing_filter_combobox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>FXAA</string>
</property>
</item>
<item>
<property name="text">
<string>SMAA</string>
</property>
</item>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
@ -329,12 +547,12 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="disable_right_eye_render"> <widget class="QCheckBox" name="disable_right_eye_render">
<property name="text">
<string>Disable Right Eye Rendering</string>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text">
<string>Disable Right Eye Rendering</string>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -399,7 +617,7 @@
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -413,9 +631,9 @@
</widget> </widget>
<tabstops> <tabstops>
<tabstop>resolution_factor_combobox</tabstop> <tabstop>resolution_factor_combobox</tabstop>
<tabstop>toggle_linear_filter</tabstop>
<tabstop>shader_combobox</tabstop> <tabstop>shader_combobox</tabstop>
<tabstop>texture_filter_combobox</tabstop> <tabstop>texture_filter_combobox</tabstop>
<tabstop>antialiasing_filter_combobox</tabstop>
<tabstop>render_3d_combobox</tabstop> <tabstop>render_3d_combobox</tabstop>
<tabstop>factor_3d</tabstop> <tabstop>factor_3d</tabstop>
<tabstop>mono_rendering_eye</tabstop> <tabstop>mono_rendering_eye</tabstop>

View file

@ -57,6 +57,35 @@ std::string_view GetTextureFilterName(TextureFilter filter) {
} }
} }
std::string_view GetAntialiasingFilterName(AntiAliasingFilter filter) {
switch (filter) {
case AntiAliasingFilter::None:
return "None";
case AntiAliasingFilter::FXAA:
return "FXAA";
case AntiAliasingFilter::SMAA:
return "SMAA";
default:
return "Invalid";
}
}
std::string_view GetOutputScalingName(OutputScaling scaling) {
switch (scaling) {
case OutputScaling::Nearest:
return "Nearest";
case OutputScaling::Bilinear:
return "Bilinear";
case OutputScaling::Adaptive:
return "Adaptive";
case OutputScaling::FSR:
return "AMD FidelityFX Super Resolution 1";
case OutputScaling::SharpBilinear:
return "Sharp Bilinear";
default:
return "Invalid";
}
}
std::string_view GetTextureSamplingName(TextureSampling sampling) { std::string_view GetTextureSamplingName(TextureSampling sampling) {
switch (sampling) { switch (sampling) {
case TextureSampling::GameControlled: case TextureSampling::GameControlled:
@ -103,6 +132,8 @@ void LogSettings() {
log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue()); log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue());
log_setting("Renderer_FilterMode", values.filter_mode.GetValue()); log_setting("Renderer_FilterMode", values.filter_mode.GetValue());
log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue())); log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue()));
log_setting("Renderer_AntialiasingFilter", GetAntialiasingFilterName(values.antialiasing_filter.GetValue()));
log_setting("Renderer_OutputScaling", GetOutputScalingName(values.output_scaling.GetValue()));
log_setting("Renderer_TextureSampling", log_setting("Renderer_TextureSampling",
GetTextureSamplingName(values.texture_sampling.GetValue())); GetTextureSamplingName(values.texture_sampling.GetValue()));
log_setting("Renderer_DelayGameRenderThreasUs", values.delay_game_render_thread_us.GetValue()); log_setting("Renderer_DelayGameRenderThreasUs", values.delay_game_render_thread_us.GetValue());
@ -216,6 +247,9 @@ void RestoreGlobalState(bool is_powered_on) {
values.use_integer_scaling.SetGlobal(true); values.use_integer_scaling.SetGlobal(true);
values.frame_limit.SetGlobal(true); values.frame_limit.SetGlobal(true);
values.texture_filter.SetGlobal(true); values.texture_filter.SetGlobal(true);
values.antialiasing_filter.SetGlobal(true);
values.output_scaling.SetGlobal(true);
values.fsr_sharpness.SetGlobal(true);
values.texture_sampling.SetGlobal(true); values.texture_sampling.SetGlobal(true);
values.delay_game_render_thread_us.SetGlobal(true); values.delay_game_render_thread_us.SetGlobal(true);
values.simulate_3ds_gpu_timings.SetGlobal(true); values.simulate_3ds_gpu_timings.SetGlobal(true);

View file

@ -112,6 +112,20 @@ enum class TextureFilter : u32 {
MMPX = 5, MMPX = 5,
}; };
enum class AntiAliasingFilter : u32 {
None = 0,
FXAA = 1,
SMAA = 2,
};
enum class OutputScaling : u32 {
Nearest = 0,
Bilinear = 1,
Adaptive = 2,
FSR = 3,
SharpBilinear = 4,
};
enum class TextureSampling : u32 { enum class TextureSampling : u32 {
GameControlled = 0, GameControlled = 0,
NearestNeighbor = 1, NearestNeighbor = 1,
@ -539,6 +553,9 @@ struct Values {
SwitchableSetting<double, true> frame_limit{100, 0, 1000, Keys::frame_limit}; SwitchableSetting<double, true> frame_limit{100, 0, 1000, Keys::frame_limit};
SwitchableSetting<double, true> turbo_limit{200, 0, 1000, Keys::turbo_limit}; SwitchableSetting<double, true> turbo_limit{200, 0, 1000, Keys::turbo_limit};
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, Keys::texture_filter}; SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, Keys::texture_filter};
SwitchableSetting<AntiAliasingFilter> antialiasing_filter{AntiAliasingFilter::None, Keys::antialiasing_filter};
SwitchableSetting<OutputScaling> output_scaling{OutputScaling::Adaptive, Keys::output_scaling};
SwitchableSetting<int, true> fsr_sharpness{50, 0, 100, Keys::fsr_sharpness};
SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled, SwitchableSetting<TextureSampling> texture_sampling{TextureSampling::GameControlled,
Keys::texture_sampling}; Keys::texture_sampling};
SwitchableSetting<u16, true> delay_game_render_thread_us{0, 0, 65000, SwitchableSetting<u16, true> delay_game_render_thread_us{0, 0, 65000,

View file

@ -13,17 +13,75 @@ set(SHADER_FILES
texture_filtering/mmpx.frag texture_filtering/mmpx.frag
texture_filtering/x_gradient.frag texture_filtering/x_gradient.frag
texture_filtering/y_gradient.frag texture_filtering/y_gradient.frag
antialiasing/OpenGL/opengl_fxaa.frag
antialiasing/OpenGL/opengl_fxaa.vert
antialiasing/OpenGL/opengl_smaa_pass0_pre.frag
antialiasing/OpenGL/opengl_smaa_pass0_pre.vert
antialiasing/OpenGL/opengl_smaa_pass0_post.frag
antialiasing/OpenGL/opengl_smaa_pass0_post.vert
antialiasing/OpenGL/opengl_smaa_pass1_pre.frag
antialiasing/OpenGL/opengl_smaa_pass1_pre.vert
antialiasing/OpenGL/opengl_smaa_pass1_post.frag
antialiasing/OpenGL/opengl_smaa_pass1_post.vert
antialiasing/OpenGL/opengl_smaa_pass2_pre.frag
antialiasing/OpenGL/opengl_smaa_pass2_pre.vert
antialiasing/OpenGL/opengl_smaa_pass2_post.frag
antialiasing/OpenGL/opengl_smaa_pass2_post.vert
antialiasing/OpenGL/opengl_smaa.hlsl
antialiasing/Vulkan/vulkan_fxaa.frag
antialiasing/Vulkan/vulkan_fxaa.vert
antialiasing/Vulkan/vulkan_smaa_pass0_pre.frag
antialiasing/Vulkan/vulkan_smaa_pass0_pre.vert
antialiasing/Vulkan/vulkan_smaa_pass0_post.frag
antialiasing/Vulkan/vulkan_smaa_pass0_post.vert
antialiasing/Vulkan/vulkan_smaa_pass1_pre.frag
antialiasing/Vulkan/vulkan_smaa_pass1_pre.vert
antialiasing/Vulkan/vulkan_smaa_pass1_post.frag
antialiasing/Vulkan/vulkan_smaa_pass1_post.vert
antialiasing/Vulkan/vulkan_smaa_pass2_pre.frag
antialiasing/Vulkan/vulkan_smaa_pass2_pre.vert
antialiasing/Vulkan/vulkan_smaa_pass2_post.frag
antialiasing/Vulkan/vulkan_smaa_pass2_post.vert
antialiasing/Vulkan/vulkan_smaa.hlsl
scaling/opengl_area_sampling.frag
scaling/opengl_area_sampling.vert
scaling/vulkan_area_sampling.frag
scaling/vulkan_area_sampling.vert
scaling/FSR/OpenGL/opengl_fsr_pass0.vert
scaling/FSR/OpenGL/opengl_fsr_pass0_part1.frag
scaling/FSR/OpenGL/opengl_fsr_pass0_part2.frag
scaling/FSR/OpenGL/opengl_fsr_pass1.vert
scaling/FSR/OpenGL/opengl_fsr_pass1_part1.frag
scaling/FSR/OpenGL/opengl_fsr_pass1_part2.frag
scaling/FSR/OpenGL/opengl_fsr_pass1_part3.frag
scaling/FSR/ffx_a.h
scaling/FSR/ffx_fsr1.h
scaling/SharpBilinear/OpenGL/opengl_sharpbilinear.vert
scaling/SharpBilinear/OpenGL/opengl_sharpbilinear.frag
scaling/FSR/Vulkan/vulkan_fsr_pass0.vert
scaling/FSR/Vulkan/vulkan_fsr_pass0_part1.frag
scaling/FSR/Vulkan/vulkan_fsr_pass0_part2.frag
scaling/FSR/Vulkan/vulkan_fsr_pass1.vert
scaling/FSR/Vulkan/vulkan_fsr_pass1_part1.frag
scaling/FSR/Vulkan/vulkan_fsr_pass1_part2.frag
scaling/FSR/Vulkan/vulkan_fsr_pass1_part3.frag
scaling/SharpBilinear/Vulkan/vulkan_sharpbilinear.vert
scaling/SharpBilinear/Vulkan/vulkan_sharpbilinear.frag
full_screen_triangle.vert full_screen_triangle.vert
opengl_present.frag opengl_present.frag
opengl_present.vert opengl_present.vert
opengl_present_anaglyph.frag opengl_present_anaglyph.frag
opengl_present_interlaced.frag opengl_present_interlaced.frag
opengl_simple_present.frag
opengl_simple_present.vert
vulkan_depth_to_buffer.comp vulkan_depth_to_buffer.comp
vulkan_present.frag vulkan_present.frag
vulkan_present.vert vulkan_present.vert
vulkan_present_anaglyph.frag vulkan_present_anaglyph.frag
vulkan_present_interlaced.frag vulkan_present_interlaced.frag
vulkan_blit_depth_stencil.frag vulkan_blit_depth_stencil.frag
vulkan_simple_present.frag
vulkan_simple_present.vert
vulkan_cursor.frag vulkan_cursor.frag
vulkan_cursor.vert vulkan_cursor.vert
) )

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,256 @@
/**
* @license
* Copyright (c) 2011 NVIDIA Corporation. All rights reserved.
*
* TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
* *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS
* OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT,IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA
* OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
* OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY
* OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
/*
FXAA_PRESET - Choose compile-in knob preset 0-5.
------------------------------------------------------------------------------
FXAA_EDGE_THRESHOLD - The minimum amount of local contrast required
to apply algorithm.
1.0/3.0 - too little
1.0/4.0 - good start
1.0/8.0 - applies to more edges
1.0/16.0 - overkill
------------------------------------------------------------------------------
FXAA_EDGE_THRESHOLD_MIN - Trims the algorithm from processing darks.
Perf optimization.
1.0/32.0 - visible limit (smaller isn't visible)
1.0/16.0 - good compromise
1.0/12.0 - upper limit (seeing artifacts)
------------------------------------------------------------------------------
FXAA_SEARCH_STEPS - Maximum number of search steps for end of span.
------------------------------------------------------------------------------
FXAA_SEARCH_THRESHOLD - Controls when to stop searching.
1.0/4.0 - seems to be the best quality wise
------------------------------------------------------------------------------
FXAA_SUBPIX_TRIM - Controls sub-pixel aliasing removal.
1.0/2.0 - low removal
1.0/3.0 - medium removal
1.0/4.0 - default removal
1.0/8.0 - high removal
0.0 - complete removal
------------------------------------------------------------------------------
FXAA_SUBPIX_CAP - Insures fine detail is not completely removed.
This is important for the transition of sub-pixel detail,
like fences and wires.
3.0/4.0 - default (medium amount of filtering)
7.0/8.0 - high amount of filtering
1.0 - no capping of sub-pixel aliasing removal
*/
//? #version 450
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform vec4 i_resolution;
uniform int convert_colors;
#ifndef FXAA_PRESET
#define FXAA_PRESET 5
#endif
#if (FXAA_PRESET == 3)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/16.0)
#define FXAA_SEARCH_STEPS 16
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#if (FXAA_PRESET == 4)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
#define FXAA_SEARCH_STEPS 24
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#if (FXAA_PRESET == 5)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
#define FXAA_SEARCH_STEPS 32
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM))
// Return the luma, the estimation of luminance from rgb inputs.
// This approximates luma using one FMA instruction,
// skipping normalization and tossing out blue.
// FxaaLuma() will range 0.0 to 2.963210702.
float FxaaLuma(vec3 rgb) {
return rgb.y * (0.587/0.299) + rgb.x;
}
vec3 FxaaLerp3(vec3 a, vec3 b, float amountOfA) {
return (vec3(-amountOfA) * b) + ((a * vec3(amountOfA)) + b);
}
vec4 FxaaTexOff(sampler2D tex, vec2 pos, ivec2 off, vec2 rcpFrame) {
float x = pos.x + float(off.x) * rcpFrame.x;
float y = pos.y + float(off.y) * rcpFrame.y;
return texture(tex, vec2(x, y));
}
// pos is the output of FxaaVertexShader interpolated across screen.
// xy -> actual texture position {0.0 to 1.0}
// rcpFrame should be a uniform equal to {1.0/frameWidth, 1.0/frameHeight}
vec3 FxaaPixelShader(vec2 pos, sampler2D tex, vec2 rcpFrame)
{
vec3 rgbN = FxaaTexOff(tex, pos.xy, ivec2( 0,-1), rcpFrame).xyz;
vec3 rgbW = FxaaTexOff(tex, pos.xy, ivec2(-1, 0), rcpFrame).xyz;
vec3 rgbM = FxaaTexOff(tex, pos.xy, ivec2( 0, 0), rcpFrame).xyz;
vec3 rgbE = FxaaTexOff(tex, pos.xy, ivec2( 1, 0), rcpFrame).xyz;
vec3 rgbS = FxaaTexOff(tex, pos.xy, ivec2( 0, 1), rcpFrame).xyz;
float lumaN = FxaaLuma(rgbN);
float lumaW = FxaaLuma(rgbW);
float lumaM = FxaaLuma(rgbM);
float lumaE = FxaaLuma(rgbE);
float lumaS = FxaaLuma(rgbS);
float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE)));
float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE)));
float range = rangeMax - rangeMin;
if(range < max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * FXAA_EDGE_THRESHOLD))
{
return rgbM;
}
vec3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS;
float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25;
float rangeL = abs(lumaL - lumaM);
float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE;
blendL = min(FXAA_SUBPIX_CAP, blendL);
vec3 rgbNW = FxaaTexOff(tex, pos.xy, ivec2(-1,-1), rcpFrame).xyz;
vec3 rgbNE = FxaaTexOff(tex, pos.xy, ivec2( 1,-1), rcpFrame).xyz;
vec3 rgbSW = FxaaTexOff(tex, pos.xy, ivec2(-1, 1), rcpFrame).xyz;
vec3 rgbSE = FxaaTexOff(tex, pos.xy, ivec2( 1, 1), rcpFrame).xyz;
rgbL += (rgbNW + rgbNE + rgbSW + rgbSE);
rgbL *= vec3(1.0/9.0);
float lumaNW = FxaaLuma(rgbNW);
float lumaNE = FxaaLuma(rgbNE);
float lumaSW = FxaaLuma(rgbSW);
float lumaSE = FxaaLuma(rgbSE);
float edgeVert =
abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) +
abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) +
abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE));
float edgeHorz =
abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) +
abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) +
abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE));
bool horzSpan = edgeHorz >= edgeVert;
float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x;
if(!horzSpan)
{
lumaN = lumaW;
lumaS = lumaE;
}
float gradientN = abs(lumaN - lumaM);
float gradientS = abs(lumaS - lumaM);
lumaN = (lumaN + lumaM) * 0.5;
lumaS = (lumaS + lumaM) * 0.5;
if (gradientN < gradientS)
{
lumaN = lumaS;
lumaN = lumaS;
gradientN = gradientS;
lengthSign *= -1.0;
}
vec2 posN;
posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5);
posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0);
gradientN *= FXAA_SEARCH_THRESHOLD;
vec2 posP = posN;
vec2 offNP = horzSpan ? vec2(rcpFrame.x, 0.0) : vec2(0.0, rcpFrame.y);
float lumaEndN = lumaN;
float lumaEndP = lumaN;
bool doneN = false;
bool doneP = false;
posN += offNP * vec2(-1.0, -1.0);
posP += offNP * vec2( 1.0, 1.0);
for(int i = 0; i < FXAA_SEARCH_STEPS; i++) {
if(!doneN)
{
lumaEndN = FxaaLuma(texture(tex, posN.xy).xyz);
}
if(!doneP)
{
lumaEndP = FxaaLuma(texture(tex, posP.xy).xyz);
}
doneN = doneN || (abs(lumaEndN - lumaN) >= gradientN);
doneP = doneP || (abs(lumaEndP - lumaN) >= gradientN);
if(doneN && doneP)
{
break;
}
if(!doneN)
{
posN -= offNP;
}
if(!doneP)
{
posP += offNP;
}
}
float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y;
float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y;
bool directionN = dstN < dstP;
lumaEndN = directionN ? lumaEndN : lumaEndP;
if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0))
{
lengthSign = 0.0;
}
float spanLength = (dstP + dstN);
dstN = directionN ? dstN : dstP;
float subPixelOffset = (0.5 + (dstN * (-1.0/spanLength))) * lengthSign;
vec3 rgbF = texture(tex, vec2(
pos.x + (horzSpan ? 0.0 : subPixelOffset),
pos.y + (horzSpan ? subPixelOffset : 0.0))).xyz;
return FxaaLerp3(rgbL, rgbF, blendL);
}
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
void main()
{
vec4 pixel = vec4(FxaaPixelShader(frag_tex_coord, color_texture, vec2(i_resolution.z, i_resolution.w)), 1.0) * 1.0;
if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,13 @@
//? #version 450
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
uniform vec4 i_resolution;
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
void main() {
if (SMAA_EDT == 0.0) {
color = vec4(SMAALumaEdgeDetectionPS(frag_tex_coord, offset, color_texture), 0.0, 0.0);
} else if (SMAA_EDT <= 1.0) {
color = vec4(SMAAColorEdgeDetectionPS(frag_tex_coord, offset, color_texture), 0.0, 0.0);
}
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAAEdgeDetectionVS(vert_tex_coord, offset);
}

View file

@ -0,0 +1,25 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Edge Detection Shaders (First Pass)
uniform vec4 i_resolution;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_FLIP_Y 1
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec4 offset[3];
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,19 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Edge Detection Shaders (First Pass)
uniform vec4 i_resolution;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,4 @@
void main() {
vec4 subsampleIndices = vec4(0.0);
color = SMAABlendingWeightCalculationPS(frag_tex_coord, pixcoord, offset, color_texture, areaTex, searchTex, subsampleIndices);
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAABlendingWeightCalculationVS(vert_tex_coord, pixcoord, offset);
}

View file

@ -0,0 +1,22 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Blending Weight Calculation Shader (Second Pass)
uniform vec4 i_resolution;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec2 pixcoord;
layout(location = 2) in vec4 offset[3];
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform sampler2D areaTex;
uniform sampler2D searchTex;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,20 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Blending Weight Calculation Shader (Second Pass)
uniform vec4 i_resolution;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec2 pixcoord;
layout(location = 2) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,11 @@
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = SMAANeighborhoodBlendingPS(frag_tex_coord, offset, SMAA_Input, color_texture);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAANeighborhoodBlendingVS(vert_tex_coord, offset);
}

View file

@ -0,0 +1,19 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Neighborhood Blending Shader (Third Pass)
uniform vec4 i_resolution;
uniform int convert_colors;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec4 offset;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform sampler2D SMAA_Input;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,17 @@
//? #version 450
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Neighborhood Blending Shader (Third Pass)
uniform vec4 i_resolution;
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 1
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec4 offset;
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,132 @@
/**
* Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com)
* Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com)
* Copyright (C) 2013 Belen Masia (bmasia@unizar.es)
* Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com)
* Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to
* do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. As clarification, there
* is no requirement that the copyright notice and permission be included in
* binary distributions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef SEARCHTEX_H
#define SEARCHTEX_H
#define SEARCHTEX_WIDTH 64
#define SEARCHTEX_HEIGHT 16
#define SEARCHTEX_PITCH SEARCHTEX_WIDTH
#define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH)
/**
* Stored in R8 format. Load it in the following format:
* - DX9: D3DFMT_L8
* - DX10: DXGI_FORMAT_R8_UNORM
*/
static const unsigned char searchTexBytes[] = {
0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe,
0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe,
0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f,
0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f,
0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
#endif

View file

@ -0,0 +1,265 @@
/**
* @license
* Copyright (c) 2011 NVIDIA Corporation. All rights reserved.
*
* TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
* *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS
* OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT,IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA
* OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
* OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY
* OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
/*
FXAA_PRESET - Choose compile-in knob preset 0-5.
------------------------------------------------------------------------------
FXAA_EDGE_THRESHOLD - The minimum amount of local contrast required
to apply algorithm.
1.0/3.0 - too little
1.0/4.0 - good start
1.0/8.0 - applies to more edges
1.0/16.0 - overkill
------------------------------------------------------------------------------
FXAA_EDGE_THRESHOLD_MIN - Trims the algorithm from processing darks.
Perf optimization.
1.0/32.0 - visible limit (smaller isn't visible)
1.0/16.0 - good compromise
1.0/12.0 - upper limit (seeing artifacts)
------------------------------------------------------------------------------
FXAA_SEARCH_STEPS - Maximum number of search steps for end of span.
------------------------------------------------------------------------------
FXAA_SEARCH_THRESHOLD - Controls when to stop searching.
1.0/4.0 - seems to be the best quality wise
------------------------------------------------------------------------------
FXAA_SUBPIX_TRIM - Controls sub-pixel aliasing removal.
1.0/2.0 - low removal
1.0/3.0 - medium removal
1.0/4.0 - default removal
1.0/8.0 - high removal
0.0 - complete removal
------------------------------------------------------------------------------
FXAA_SUBPIX_CAP - Insures fine detail is not completely removed.
This is important for the transition of sub-pixel detail,
like fences and wires.
3.0/4.0 - default (medium amount of filtering)
7.0/8.0 - high amount of filtering
1.0 - no capping of sub-pixel aliasing removal
*/
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#ifndef FXAA_PRESET
#define FXAA_PRESET 5
#endif
#if (FXAA_PRESET == 3)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/16.0)
#define FXAA_SEARCH_STEPS 16
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#if (FXAA_PRESET == 4)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
#define FXAA_SEARCH_STEPS 24
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#if (FXAA_PRESET == 5)
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
#define FXAA_SEARCH_STEPS 32
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
#define FXAA_SUBPIX_CAP (3.0/4.0)
#define FXAA_SUBPIX_TRIM (1.0/4.0)
#endif
#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM))
// Return the luma, the estimation of luminance from rgb inputs.
// This approximates luma using one FMA instruction,
// skipping normalization and tossing out blue.
// FxaaLuma() will range 0.0 to 2.963210702.
float FxaaLuma(vec3 rgb) {
return rgb.y * (0.587/0.299) + rgb.x;
}
vec3 FxaaLerp3(vec3 a, vec3 b, float amountOfA) {
return (vec3(-amountOfA) * b) + ((a * vec3(amountOfA)) + b);
}
vec4 FxaaTexOff(sampler2D tex, vec2 pos, ivec2 off, vec2 rcpFrame) {
float x = pos.x + float(off.x) * rcpFrame.x;
float y = pos.y + float(off.y) * rcpFrame.y;
return texture(tex, vec2(x, y));
}
// pos is the output of FxaaVertexShader interpolated across screen.
// xy -> actual texture position {0.0 to 1.0}
// rcpFrame should be a uniform equal to {1.0/frameWidth, 1.0/frameHeight}
vec3 FxaaPixelShader(vec2 pos, sampler2D tex, vec2 rcpFrame)
{
vec3 rgbN = FxaaTexOff(tex, pos.xy, ivec2( 0,-1), rcpFrame).xyz;
vec3 rgbW = FxaaTexOff(tex, pos.xy, ivec2(-1, 0), rcpFrame).xyz;
vec3 rgbM = FxaaTexOff(tex, pos.xy, ivec2( 0, 0), rcpFrame).xyz;
vec3 rgbE = FxaaTexOff(tex, pos.xy, ivec2( 1, 0), rcpFrame).xyz;
vec3 rgbS = FxaaTexOff(tex, pos.xy, ivec2( 0, 1), rcpFrame).xyz;
float lumaN = FxaaLuma(rgbN);
float lumaW = FxaaLuma(rgbW);
float lumaM = FxaaLuma(rgbM);
float lumaE = FxaaLuma(rgbE);
float lumaS = FxaaLuma(rgbS);
float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE)));
float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE)));
float range = rangeMax - rangeMin;
if(range < max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * FXAA_EDGE_THRESHOLD))
{
return rgbM;
}
vec3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS;
float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25;
float rangeL = abs(lumaL - lumaM);
float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE;
blendL = min(FXAA_SUBPIX_CAP, blendL);
vec3 rgbNW = FxaaTexOff(tex, pos.xy, ivec2(-1,-1), rcpFrame).xyz;
vec3 rgbNE = FxaaTexOff(tex, pos.xy, ivec2( 1,-1), rcpFrame).xyz;
vec3 rgbSW = FxaaTexOff(tex, pos.xy, ivec2(-1, 1), rcpFrame).xyz;
vec3 rgbSE = FxaaTexOff(tex, pos.xy, ivec2( 1, 1), rcpFrame).xyz;
rgbL += (rgbNW + rgbNE + rgbSW + rgbSE);
rgbL *= vec3(1.0/9.0);
float lumaNW = FxaaLuma(rgbNW);
float lumaNE = FxaaLuma(rgbNE);
float lumaSW = FxaaLuma(rgbSW);
float lumaSE = FxaaLuma(rgbSE);
float edgeVert =
abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) +
abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) +
abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE));
float edgeHorz =
abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) +
abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) +
abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE));
bool horzSpan = edgeHorz >= edgeVert;
float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x;
if(!horzSpan)
{
lumaN = lumaW;
lumaS = lumaE;
}
float gradientN = abs(lumaN - lumaM);
float gradientS = abs(lumaS - lumaM);
lumaN = (lumaN + lumaM) * 0.5;
lumaS = (lumaS + lumaM) * 0.5;
if (gradientN < gradientS)
{
lumaN = lumaS;
lumaN = lumaS;
gradientN = gradientS;
lengthSign *= -1.0;
}
vec2 posN;
posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5);
posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0);
gradientN *= FXAA_SEARCH_THRESHOLD;
vec2 posP = posN;
vec2 offNP = horzSpan ? vec2(rcpFrame.x, 0.0) : vec2(0.0, rcpFrame.y);
float lumaEndN = lumaN;
float lumaEndP = lumaN;
bool doneN = false;
bool doneP = false;
posN += offNP * vec2(-1.0, -1.0);
posP += offNP * vec2( 1.0, 1.0);
for(int i = 0; i < FXAA_SEARCH_STEPS; i++) {
if(!doneN)
{
lumaEndN = FxaaLuma(texture(tex, posN.xy).xyz);
}
if(!doneP)
{
lumaEndP = FxaaLuma(texture(tex, posP.xy).xyz);
}
doneN = doneN || (abs(lumaEndN - lumaN) >= gradientN);
doneP = doneP || (abs(lumaEndP - lumaN) >= gradientN);
if(doneN && doneP)
{
break;
}
if(!doneN)
{
posN -= offNP;
}
if(!doneP)
{
posP += offNP;
}
}
float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y;
float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y;
bool directionN = dstN < dstP;
lumaEndN = directionN ? lumaEndN : lumaEndP;
if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0))
{
lengthSign = 0.0;
}
float spanLength = (dstP + dstN);
dstN = directionN ? dstN : dstP;
float subPixelOffset = (0.5 + (dstN * (-1.0/spanLength))) * lengthSign;
vec3 rgbF = texture(tex, vec2(
pos.x + (horzSpan ? 0.0 : subPixelOffset),
pos.y + (horzSpan ? subPixelOffset : 0.0))).xyz;
return FxaaLerp3(rgbL, rgbF, blendL);
}
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
void main()
{
vec4 pixel = vec4(FxaaPixelShader(frag_tex_coord, color_texture, vec2(i_resolution.z, i_resolution.w)), 1.0) * 1.0;
if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,24 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
void main() {
if (SMAA_EDT == 0.0) {
color = vec4(SMAALumaEdgeDetectionPS(frag_tex_coord, offset, color_texture), 0.0, 0.0);
} else if (SMAA_EDT <= 1.0) {
color = vec4(SMAAColorEdgeDetectionPS(frag_tex_coord, offset, color_texture), 0.0, 0.0);
}
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAAEdgeDetectionVS(vert_tex_coord, offset);
}

View file

@ -0,0 +1,30 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Edge Detection Shaders (First Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec4 offset[3];
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,30 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Edge Detection Shaders (First Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,4 @@
void main() {
vec4 subsampleIndices = vec4(0.0);
color = SMAABlendingWeightCalculationPS(frag_tex_coord, pixcoord, offset, color_texture, areaTex, searchTex, subsampleIndices);
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAABlendingWeightCalculationVS(vert_tex_coord, pixcoord, offset);
}

View file

@ -0,0 +1,33 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Blending Weight Calculation Shader (Second Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec2 pixcoord;
layout(location = 2) in vec4 offset[3];
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (set = 0, binding = 1) uniform sampler2D areaTex;
layout (set = 0, binding = 2) uniform sampler2D searchTex;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,31 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Blending Weight Calculation Shader (Second Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
#define SMAA_PRESET_ULTRA
#define SMAA_EDT 1.0
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec2 pixcoord;
layout(location = 2) out vec4 offset[3];
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,11 @@
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = SMAANeighborhoodBlendingPS(frag_tex_coord, offset, SMAA_Input, color_texture);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,5 @@
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
SMAANeighborhoodBlendingVS(vert_tex_coord, offset);
}

View file

@ -0,0 +1,29 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Neighborhood Blending Shader (Third Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec4 offset;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (set = 0, binding = 1) uniform sampler2D SMAA_Input;
#define SMAA_INCLUDE_VS 0
//#include "SMAA.hlsl"

View file

@ -0,0 +1,28 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
// SPDX-License-Identifier: Unlicense
//-----------------------------------------------------------------------------
// Neighborhood Blending Shader (Third Pass)
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define SMAA_RT_METRICS vec4(i_resolution.z, i_resolution.w, i_resolution.x, i_resolution.y)
#define SMAA_GLSL_4
#define SMAA_FLIP_Y 0
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec4 offset;
#define SMAA_INCLUDE_PS 0
//#include "SMAA.hlsl"

View file

@ -9,10 +9,22 @@ layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture; layout(binding = 0) uniform sampler2D color_texture;
uniform vec4 i_resolution; uniform int convert_colors;
uniform vec4 o_resolution;
uniform int layer; vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() { void main() {
color = texture(color_texture, frag_tex_coord); vec4 pixel = texture(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
} else if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
} }

View file

@ -0,0 +1,28 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
//? #version 430 core
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform int convert_colors;
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = texture(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
} else if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,13 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
//? #version 430 core
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,12 @@
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,20 @@
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform vec4 i_resolution;
uniform vec4 o_resolution;
#define A_GPU 1
#define A_GLSL 1
// #include "ffx_a.h"
// // We intentionally do NOT define FSR_EASU_F here.
// // We only need FsrEasuCon (which compiles under A_GPU alone),
// // and we inline the EASU filter logic below to avoid the
// // textureGather-based callback system entirely.
// // This yields 12 texelFetch calls instead of the original
// // 12 textureGather calls (4 gathers x 3 channels), and is
// // faster than emulating gathers with 48 individual fetches.
// #include "ffx_fsr1.h"

View file

@ -0,0 +1,193 @@
void main() {
// --- Setup constants (same as original) ---
AU4 con0, con1, con2, con3;
FsrEasuCon(con0, con1, con2, con3,
i_resolution.x, i_resolution.y,
i_resolution.x, i_resolution.y,
o_resolution.x, o_resolution.y);
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy);
// --- Get position of 'f' (the center texel of the kernel) ---
AF2 pp = AF2(gxy) * AF2_AU2(con0.xy) + AF2_AU2(con0.zw);
AF2 fp = floor(pp);
pp -= fp;
// --- Fetch all 12 unique texels directly as RGB ---
// The 12-tap kernel layout relative to 'fp':
// b c (0,-1) (1,-1)
// e f g h (-1,0) (0,0) (1,0) (2,0)
// i j k l (-1,1) (0,1) (1,1) (2,1)
// n o (0, 2) (1, 2)
ivec2 sp = ivec2(fp);
AF3 b = texelFetch(color_texture, sp + ivec2( 0,-1), 0).rgb;
AF3 c = texelFetch(color_texture, sp + ivec2( 1,-1), 0).rgb;
AF3 e = texelFetch(color_texture, sp + ivec2(-1, 0), 0).rgb;
AF3 f = texelFetch(color_texture, sp + ivec2( 0, 0), 0).rgb;
AF3 g = texelFetch(color_texture, sp + ivec2( 1, 0), 0).rgb;
AF3 h = texelFetch(color_texture, sp + ivec2( 2, 0), 0).rgb;
AF3 i = texelFetch(color_texture, sp + ivec2(-1, 1), 0).rgb;
AF3 j = texelFetch(color_texture, sp + ivec2( 0, 1), 0).rgb;
AF3 k = texelFetch(color_texture, sp + ivec2( 1, 1), 0).rgb;
AF3 l = texelFetch(color_texture, sp + ivec2( 2, 1), 0).rgb;
AF3 n = texelFetch(color_texture, sp + ivec2( 0, 2), 0).rgb;
AF3 o = texelFetch(color_texture, sp + ivec2( 1, 2), 0).rgb;
// --- Approximate luma (luma times 2, in 2 FMA/MAD) ---
AF1 bL = b.b * AF1_(0.5) + (b.r * AF1_(0.5) + b.g);
AF1 cL = c.b * AF1_(0.5) + (c.r * AF1_(0.5) + c.g);
AF1 eL = e.b * AF1_(0.5) + (e.r * AF1_(0.5) + e.g);
AF1 fL = f.b * AF1_(0.5) + (f.r * AF1_(0.5) + f.g);
AF1 gL = g.b * AF1_(0.5) + (g.r * AF1_(0.5) + g.g);
AF1 hL = h.b * AF1_(0.5) + (h.r * AF1_(0.5) + h.g);
AF1 iL = i.b * AF1_(0.5) + (i.r * AF1_(0.5) + i.g);
AF1 jL = j.b * AF1_(0.5) + (j.r * AF1_(0.5) + j.g);
AF1 kL = k.b * AF1_(0.5) + (k.r * AF1_(0.5) + k.g);
AF1 lL = l.b * AF1_(0.5) + (l.r * AF1_(0.5) + l.g);
AF1 nL = n.b * AF1_(0.5) + (n.r * AF1_(0.5) + n.g);
AF1 oL = o.b * AF1_(0.5) + (o.r * AF1_(0.5) + o.g);
// --- Accumulate direction and length ---
// Inlined FsrEasuSetF for each of the 4 bilinear quadrants.
// Each quadrant computes gradient direction and edge length
// from its 5-tap cross pattern centered on the quadrant's
// nearest texel.
//
// Quadrant layout (bilinear weights):
// s=(1-x)(1-y) t=x(1-y)
// u=(1-x)y v=xy
//
// Cross pattern for each quadrant:
// s: center=f, left=e, right=g, up=b, down=j
// t: center=g, left=f, right=h, up=c, down=k
// u: center=j, left=i, right=k, up=f, down=n
// v: center=k, left=j, right=l, up=g, down=o
AF2 dir = AF2_(0.0);
AF1 len = AF1_(0.0);
// Quadrant s
{
AF1 w = (AF1_(1.0) - pp.x) * (AF1_(1.0) - pp.y);
AF1 dc = gL - fL; AF1 cb = fL - eL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = gL - eL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = jL - fL; AF1 ca = fL - bL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = jL - bL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant t
{
AF1 w = pp.x * (AF1_(1.0) - pp.y);
AF1 dc = hL - gL; AF1 cb = gL - fL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = hL - fL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = kL - gL; AF1 ca = gL - cL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = kL - cL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant u
{
AF1 w = (AF1_(1.0) - pp.x) * pp.y;
AF1 dc = kL - jL; AF1 cb = jL - iL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = kL - iL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = nL - jL; AF1 ca = jL - fL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = nL - fL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant v
{
AF1 w = pp.x * pp.y;
AF1 dc = lL - kL; AF1 cb = kL - jL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = lL - jL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = oL - kL; AF1 ca = kL - gL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = oL - gL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// --- Normalize direction ---
AF2 dir2 = dir * dir;
AF1 dirR = dir2.x + dir2.y;
AP1 zro = dirR < AF1_(1.0 / 32768.0);
dirR = APrxLoRsqF1(dirR);
dirR = zro ? AF1_(1.0) : dirR;
dir.x = zro ? AF1_(1.0) : dir.x;
dir *= AF2_(dirR);
// --- Shape length ---
len = len * AF1_(0.5);
len *= len;
AF1 stretch = (dir.x * dir.x + dir.y * dir.y) * APrxLoRcpF1(max(abs(dir.x), abs(dir.y)));
AF2 len2 = AF2(AF1_(1.0) + (stretch - AF1_(1.0)) * len, AF1_(1.0) + AF1_(-0.5) * len);
AF1 lob = AF1_(0.5) + AF1_((1.0 / 4.0 - 0.04) - 0.5) * len;
AF1 clp = APrxLoRcpF1(lob);
// --- Min/max of 4 nearest (f, g, j, k) for de-ringing ---
AF3 min4 = min(min(f, g), min(j, k));
AF3 max4 = max(max(f, g), max(j, k));
// --- Accumulate 12 taps (inlined FsrEasuTapF) ---
AF3 aC = AF3_(0.0);
AF1 aW = AF1_(0.0);
// Macro for the Lanczos-like kernel evaluation per tap.
// Rotates offset by direction, applies anisotropic scaling,
// evaluates the approximated windowed Lanczos kernel, accumulates.
#define FSR_EASU_TAP(OFF_X, OFF_Y, COLOR) { \
AF2 v; \
v.x = ((OFF_X) - pp.x) * dir.x + ((OFF_Y) - pp.y) * dir.y; \
v.y = ((OFF_X) - pp.x) * (-dir.y) + ((OFF_Y) - pp.y) * dir.x; \
v *= len2; \
AF1 d2 = min(v.x * v.x + v.y * v.y, clp); \
AF1 wB = AF1_(2.0 / 5.0) * d2 + AF1_(-1.0); \
AF1 wA = lob * d2 + AF1_(-1.0); \
wB *= wB; wA *= wA; \
wB = AF1_(25.0 / 16.0) * wB + AF1_(-(25.0 / 16.0 - 1.0)); \
AF1 w = wB * wA; \
aC += (COLOR) * w; aW += w; }
FSR_EASU_TAP( 0.0, -1.0, b) // b
FSR_EASU_TAP( 1.0, -1.0, c) // c
FSR_EASU_TAP(-1.0, 1.0, i) // i
FSR_EASU_TAP( 0.0, 1.0, j) // j
FSR_EASU_TAP( 0.0, 0.0, f) // f
FSR_EASU_TAP(-1.0, 0.0, e) // e
FSR_EASU_TAP( 1.0, 1.0, k) // k
FSR_EASU_TAP( 2.0, 1.0, l) // l
FSR_EASU_TAP( 2.0, 0.0, h) // h
FSR_EASU_TAP( 1.0, 0.0, g) // g
FSR_EASU_TAP( 1.0, 2.0, o) // o
FSR_EASU_TAP( 0.0, 2.0, n) // n
#undef FSR_EASU_TAP
// --- Normalize and de-ring ---
AF3 pix = min(max4, max(min4, aC * AF3_(ARcpF1(aW))));
color = vec4(pix, 1.0);
}

View file

@ -0,0 +1,12 @@
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
//? #version 450
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,10 @@
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform float FSR_SHARPENING;
uniform vec4 o_resolution;
#define A_GPU 1
#define A_GLSL 1
// #include "ffx_a.h"

View file

@ -0,0 +1,7 @@
#define FSR_RCAS_F 1
AU4 con0;
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(color_texture, p, 0)); }
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
// #include "ffx_fsr1.h"

View file

@ -0,0 +1,9 @@
void main() {
FsrRcasCon(con0, FSR_SHARPENING);
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy); // Integer pixel position in output.
AF3 Gamma2Color = AF3(0, 0, 0);
FsrRcasF(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, gxy, con0);
color = vec4(Gamma2Color, 1.0);
}

View file

@ -0,0 +1,26 @@
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,32 @@
// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING
// SM 4.0 compatible: no textureGather, direct texelFetch of 12 unique texels.
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
#define A_GPU 1
#define A_GLSL 1
// #include "ffx_a.h"
// // We intentionally do NOT define FSR_EASU_F here.
// // We only need FsrEasuCon (which compiles under A_GPU alone),
// // and we inline the EASU filter logic below to avoid the
// // textureGather-based callback system entirely.
// // This yields 12 texelFetch calls instead of the original
// // 12 textureGather calls (4 gathers x 3 channels), and is
// // faster than emulating gathers with 48 individual fetches.
// #include "ffx_fsr1.h"

View file

@ -0,0 +1,193 @@
void main() {
// --- Setup constants (same as original) ---
AU4 con0, con1, con2, con3;
FsrEasuCon(con0, con1, con2, con3,
i_resolution.x, i_resolution.y,
i_resolution.x, i_resolution.y,
o_resolution.x, o_resolution.y);
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy);
// --- Get position of 'f' (the center texel of the kernel) ---
AF2 pp = AF2(gxy) * AF2_AU2(con0.xy) + AF2_AU2(con0.zw);
AF2 fp = floor(pp);
pp -= fp;
// --- Fetch all 12 unique texels directly as RGB ---
// The 12-tap kernel layout relative to 'fp':
// b c (0,-1) (1,-1)
// e f g h (-1,0) (0,0) (1,0) (2,0)
// i j k l (-1,1) (0,1) (1,1) (2,1)
// n o (0, 2) (1, 2)
ivec2 sp = ivec2(fp);
AF3 b = texelFetch(color_texture, sp + ivec2( 0,-1), 0).rgb;
AF3 c = texelFetch(color_texture, sp + ivec2( 1,-1), 0).rgb;
AF3 e = texelFetch(color_texture, sp + ivec2(-1, 0), 0).rgb;
AF3 f = texelFetch(color_texture, sp + ivec2( 0, 0), 0).rgb;
AF3 g = texelFetch(color_texture, sp + ivec2( 1, 0), 0).rgb;
AF3 h = texelFetch(color_texture, sp + ivec2( 2, 0), 0).rgb;
AF3 i = texelFetch(color_texture, sp + ivec2(-1, 1), 0).rgb;
AF3 j = texelFetch(color_texture, sp + ivec2( 0, 1), 0).rgb;
AF3 k = texelFetch(color_texture, sp + ivec2( 1, 1), 0).rgb;
AF3 l = texelFetch(color_texture, sp + ivec2( 2, 1), 0).rgb;
AF3 n = texelFetch(color_texture, sp + ivec2( 0, 2), 0).rgb;
AF3 o = texelFetch(color_texture, sp + ivec2( 1, 2), 0).rgb;
// --- Approximate luma (luma times 2, in 2 FMA/MAD) ---
AF1 bL = b.b * AF1_(0.5) + (b.r * AF1_(0.5) + b.g);
AF1 cL = c.b * AF1_(0.5) + (c.r * AF1_(0.5) + c.g);
AF1 eL = e.b * AF1_(0.5) + (e.r * AF1_(0.5) + e.g);
AF1 fL = f.b * AF1_(0.5) + (f.r * AF1_(0.5) + f.g);
AF1 gL = g.b * AF1_(0.5) + (g.r * AF1_(0.5) + g.g);
AF1 hL = h.b * AF1_(0.5) + (h.r * AF1_(0.5) + h.g);
AF1 iL = i.b * AF1_(0.5) + (i.r * AF1_(0.5) + i.g);
AF1 jL = j.b * AF1_(0.5) + (j.r * AF1_(0.5) + j.g);
AF1 kL = k.b * AF1_(0.5) + (k.r * AF1_(0.5) + k.g);
AF1 lL = l.b * AF1_(0.5) + (l.r * AF1_(0.5) + l.g);
AF1 nL = n.b * AF1_(0.5) + (n.r * AF1_(0.5) + n.g);
AF1 oL = o.b * AF1_(0.5) + (o.r * AF1_(0.5) + o.g);
// --- Accumulate direction and length ---
// Inlined FsrEasuSetF for each of the 4 bilinear quadrants.
// Each quadrant computes gradient direction and edge length
// from its 5-tap cross pattern centered on the quadrant's
// nearest texel.
//
// Quadrant layout (bilinear weights):
// s=(1-x)(1-y) t=x(1-y)
// u=(1-x)y v=xy
//
// Cross pattern for each quadrant:
// s: center=f, left=e, right=g, up=b, down=j
// t: center=g, left=f, right=h, up=c, down=k
// u: center=j, left=i, right=k, up=f, down=n
// v: center=k, left=j, right=l, up=g, down=o
AF2 dir = AF2_(0.0);
AF1 len = AF1_(0.0);
// Quadrant s
{
AF1 w = (AF1_(1.0) - pp.x) * (AF1_(1.0) - pp.y);
AF1 dc = gL - fL; AF1 cb = fL - eL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = gL - eL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = jL - fL; AF1 ca = fL - bL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = jL - bL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant t
{
AF1 w = pp.x * (AF1_(1.0) - pp.y);
AF1 dc = hL - gL; AF1 cb = gL - fL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = hL - fL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = kL - gL; AF1 ca = gL - cL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = kL - cL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant u
{
AF1 w = (AF1_(1.0) - pp.x) * pp.y;
AF1 dc = kL - jL; AF1 cb = jL - iL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = kL - iL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = nL - jL; AF1 ca = jL - fL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = nL - fL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// Quadrant v
{
AF1 w = pp.x * pp.y;
AF1 dc = lL - kL; AF1 cb = kL - jL;
AF1 lenX = max(abs(dc), abs(cb));
lenX = APrxLoRcpF1(lenX);
AF1 dirX = lL - jL;
dir.x += dirX * w;
lenX = ASatF1(abs(dirX) * lenX); lenX *= lenX; len += lenX * w;
AF1 ec = oL - kL; AF1 ca = kL - gL;
AF1 lenY = max(abs(ec), abs(ca));
lenY = APrxLoRcpF1(lenY);
AF1 dirY = oL - gL;
dir.y += dirY * w;
lenY = ASatF1(abs(dirY) * lenY); lenY *= lenY; len += lenY * w;
}
// --- Normalize direction ---
AF2 dir2 = dir * dir;
AF1 dirR = dir2.x + dir2.y;
AP1 zro = dirR < AF1_(1.0 / 32768.0);
dirR = APrxLoRsqF1(dirR);
dirR = zro ? AF1_(1.0) : dirR;
dir.x = zro ? AF1_(1.0) : dir.x;
dir *= AF2_(dirR);
// --- Shape length ---
len = len * AF1_(0.5);
len *= len;
AF1 stretch = (dir.x * dir.x + dir.y * dir.y) * APrxLoRcpF1(max(abs(dir.x), abs(dir.y)));
AF2 len2 = AF2(AF1_(1.0) + (stretch - AF1_(1.0)) * len, AF1_(1.0) + AF1_(-0.5) * len);
AF1 lob = AF1_(0.5) + AF1_((1.0 / 4.0 - 0.04) - 0.5) * len;
AF1 clp = APrxLoRcpF1(lob);
// --- Min/max of 4 nearest (f, g, j, k) for de-ringing ---
AF3 min4 = min(min(f, g), min(j, k));
AF3 max4 = max(max(f, g), max(j, k));
// --- Accumulate 12 taps (inlined FsrEasuTapF) ---
AF3 aC = AF3_(0.0);
AF1 aW = AF1_(0.0);
// Macro for the Lanczos-like kernel evaluation per tap.
// Rotates offset by direction, applies anisotropic scaling,
// evaluates the approximated windowed Lanczos kernel, accumulates.
#define FSR_EASU_TAP(OFF_X, OFF_Y, COLOR) { \
AF2 v; \
v.x = ((OFF_X) - pp.x) * dir.x + ((OFF_Y) - pp.y) * dir.y; \
v.y = ((OFF_X) - pp.x) * (-dir.y) + ((OFF_Y) - pp.y) * dir.x; \
v *= len2; \
AF1 d2 = min(v.x * v.x + v.y * v.y, clp); \
AF1 wB = AF1_(2.0 / 5.0) * d2 + AF1_(-1.0); \
AF1 wA = lob * d2 + AF1_(-1.0); \
wB *= wB; wA *= wA; \
wB = AF1_(25.0 / 16.0) * wB + AF1_(-(25.0 / 16.0 - 1.0)); \
AF1 w = wB * wA; \
aC += (COLOR) * w; aW += w; }
FSR_EASU_TAP( 0.0, -1.0, b) // b
FSR_EASU_TAP( 1.0, -1.0, c) // c
FSR_EASU_TAP(-1.0, 1.0, i) // i
FSR_EASU_TAP( 0.0, 1.0, j) // j
FSR_EASU_TAP( 0.0, 0.0, f) // f
FSR_EASU_TAP(-1.0, 0.0, e) // e
FSR_EASU_TAP( 1.0, 1.0, k) // k
FSR_EASU_TAP( 2.0, 1.0, l) // l
FSR_EASU_TAP( 2.0, 0.0, h) // h
FSR_EASU_TAP( 1.0, 0.0, g) // g
FSR_EASU_TAP( 1.0, 2.0, o) // o
FSR_EASU_TAP( 0.0, 2.0, n) // n
#undef FSR_EASU_TAP
// --- Normalize and de-ring ---
AF3 pix = min(max4, max(min4, aC * AF3_(ARcpF1(aW))));
color = vec4(pix, 1.0);
}

View file

@ -0,0 +1,26 @@
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
//? #version 450
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
void main()
{
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,22 @@
// FSR - [RCAS] ROBUST CONTRAST ADAPTIVE SHARPENING
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
float FSR_SHARPENING;
};
#define A_GPU 1
#define A_GLSL 1
// #include "ffx_a.h"

View file

@ -0,0 +1,7 @@
#define FSR_RCAS_F 1
AU4 con0;
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(color_texture, p, 0)); }
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
// #include "ffx_fsr1.h"

View file

@ -0,0 +1,9 @@
void main() {
FsrRcasCon(con0, FSR_SHARPENING);
AU2 gxy = AU2(frag_tex_coord.xy * o_resolution.xy); // Integer pixel position in output.
AF3 Gamma2Color = AF3(0, 0, 0);
FsrRcasF(Gamma2Color.r, Gamma2Color.g, Gamma2Color.b, gxy, con0);
color = vec4(Gamma2Color, 1.0);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
/*
Author: KojoZero (modified from rsn8887's shader)
License: Public domain
This is an integer prescale filter that should be combined
with a bilinear hardware filtering (GL_BILINEAR filter or some such) to achieve
a smooth scaling result with minimum blur. This is good for pixelgraphics
that are scaled by non-integer factors.
This is a modified version rsn8887's shader which has been modified to scale
until above the output resolution, rather than right below the output resolution.
The prescale factor and texel coordinates are precalculated
in the vertex shader for speed.
*/
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec2 precalc_texel;
layout(location = 2) in vec2 precalc_scale;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform vec4 i_resolution;
uniform vec4 o_resolution;
uniform int convert_colors;
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main()
{
vec2 texel = precalc_texel;
vec2 scale = precalc_scale;
vec2 texel_floored = floor(texel);
vec2 s = fract(texel);
vec2 region_range = 0.5 - 0.5 / scale;
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
vec2 center_dist = s - 0.5;
vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
vec2 mod_texel = texel_floored + f;
vec4 pixel = vec4(texture(color_texture, mod_texel / i_resolution.xy).rgb, 1.0);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,16 @@
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec2 precalc_texel;
layout(location = 2) out vec2 precalc_scale;
uniform mat3x2 modelview_matrix;
uniform vec4 i_resolution;
uniform vec4 o_resolution;
void main()
{
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
precalc_scale = ceil(o_resolution.xy / i_resolution.xy);
precalc_texel = vert_tex_coord.xy * i_resolution.xy;
}

View file

@ -0,0 +1,61 @@
/*
Author: KojoZero (modified from rsn8887's shader)
License: Public domain
This is an integer prescale filter that should be combined
with a bilinear hardware filtering (GL_BILINEAR filter or some such) to achieve
a smooth scaling result with minimum blur. This is good for pixelgraphics
that are scaled by non-integer factors.
This is a modified version rsn8887's shader which has been modified to scale
until above the output resolution, rather than right below the output resolution.
The prescale factor and texel coordinates are precalculated
in the vertex shader for speed.
*/
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 1) in vec2 precalc_texel;
layout(location = 2) in vec2 precalc_scale;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main()
{
vec2 texel = precalc_texel;
vec2 scale = precalc_scale;
vec2 texel_floored = floor(texel);
vec2 s = fract(texel);
vec2 region_range = 0.5 - 0.5 / scale;
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
vec2 center_dist = s - 0.5;
vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
vec2 mod_texel = texel_floored + f;
vec4 pixel = vec4(texture(color_texture, mod_texel / i_resolution.xy).rgb, 1.0);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,27 @@
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout(location = 1) out vec2 precalc_texel;
layout(location = 2) out vec2 precalc_scale;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
void main()
{
vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix;
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
precalc_scale = ceil(o_resolution.xy / i_resolution.xy);
precalc_texel = vert_tex_coord.xy * i_resolution.xy;
}

View file

@ -0,0 +1,93 @@
//? #version 460 core
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout(binding = 0) uniform sampler2D color_texture;
uniform vec4 i_resolution;
uniform vec4 o_resolution;
uniform int convert_colors;
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords) {
// Determine the sizes of the source and target images.
vec2 source_size = i_resolution.xy;
vec2 inverted_target_size = o_resolution.zw;
// Determine the range of the source image that the target pixel will cover.
vec2 range = source_size * inverted_target_size;
vec2 beg = (texCoords.xy * source_size) - (range * 0.5);
vec2 end = beg + range;
// Compute the top-left and bottom-right corners of the pixel box.
ivec2 f_beg = ivec2(floor(beg));
ivec2 f_end = ivec2(floor(end));
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
// Accumulate corner pixels.
avg_color += area_nw * texelFetch(textureSampler, ivec2(f_beg.x, f_beg.y), 0);
avg_color += area_ne * texelFetch(textureSampler, ivec2(f_end.x, f_beg.y), 0);
avg_color += area_sw * texelFetch(textureSampler, ivec2(f_beg.x, f_end.y), 0);
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
int x_range = int(float(f_end.x) - float(f_beg.x) - 0.5);
int y_range = int(float(f_end.y) - float(f_beg.y) - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += area_n * texelFetch(textureSampler, ivec2(x, f_beg.y), 0);
avg_color += area_s * texelFetch(textureSampler, ivec2(x, f_end.y), 0);
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) {
avg_color += area_w * texelFetch(textureSampler, ivec2(f_beg.x, y), 0);
avg_color += area_e * texelFetch(textureSampler, ivec2(f_end.x, y), 0);
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += texelFetch(textureSampler, ivec2(x, y), 0);
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
}
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = AreaSampling(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,11 @@
//? #version 460
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
uniform mat3x2 modelview_matrix;
void main()
{
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -0,0 +1,102 @@
//? #version 460 core
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 frag_tex_coord;
layout(location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D color_texture;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
vec4 AreaSampling(sampler2D textureSampler, vec2 texCoords) {
// Determine the sizes of the source and target images.
vec2 source_size = i_resolution.xy;
vec2 inverted_target_size = o_resolution.zw;
// Determine the range of the source image that the target pixel will cover.
vec2 range = source_size * inverted_target_size;
vec2 beg = (texCoords.xy * source_size) - (range * 0.5);
vec2 end = beg + range;
// Compute the top-left and bottom-right corners of the pixel box.
ivec2 f_beg = ivec2(floor(beg));
ivec2 f_end = ivec2(floor(end));
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
// Accumulate corner pixels.
avg_color += area_nw * texelFetch(textureSampler, ivec2(f_beg.x, f_beg.y), 0);
avg_color += area_ne * texelFetch(textureSampler, ivec2(f_end.x, f_beg.y), 0);
avg_color += area_sw * texelFetch(textureSampler, ivec2(f_beg.x, f_end.y), 0);
avg_color += area_se * texelFetch(textureSampler, ivec2(f_end.x, f_end.y), 0);
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x - 0.5);
int y_range = int(f_end.y - f_beg.y - 0.5);
// Accumulate top and bottom edge pixels.
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += area_n * texelFetch(textureSampler, ivec2(x, f_beg.y), 0);
avg_color += area_s * texelFetch(textureSampler, ivec2(x, f_end.y), 0);
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y) {
avg_color += area_w * texelFetch(textureSampler, ivec2(f_beg.x, y), 0);
avg_color += area_e * texelFetch(textureSampler, ivec2(f_end.x, y), 0);
for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x) {
avg_color += texelFetch(textureSampler, ivec2(x, y), 0);
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
}
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = AreaSampling(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,24 @@
//? #version 460
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 vert_position;
layout(location = 1) in vec2 vert_tex_coord;
layout(location = 0) out vec2 frag_tex_coord;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
void main()
{
vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix;
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -16,25 +16,25 @@ layout (push_constant, std140) uniform DrawInfo {
int screen_id_r; int screen_id_r;
int layer; int layer;
int reverse_interlaced; int reverse_interlaced;
int convert_colors;
}; };
layout (set = 0, binding = 0) uniform sampler2D screen_textures[3]; layout (set = 0, binding = 0) uniform sampler2D color_texture;
vec4 GetScreen(int screen_id) { vec3 sRGBToLinear(vec3 c) {
#ifdef ARRAY_DYNAMIC_INDEX return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
return texture(screen_textures[screen_id], frag_tex_coord);
#else
switch (screen_id) {
case 0:
return texture(screen_textures[0], frag_tex_coord);
case 1:
return texture(screen_textures[1], frag_tex_coord);
case 2:
return texture(screen_textures[2], frag_tex_coord);
} }
#endif
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
} }
void main() { void main() {
color = GetScreen(screen_id_l); vec4 pixel = texture(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
} else if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
} }

View file

@ -1,7 +1,6 @@
// Copyright 2022 Citra Emulator Project // Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#version 450 core #version 450 core
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable

View file

@ -30,25 +30,11 @@ layout (push_constant, std140) uniform DrawInfo {
int reverse_interlaced; int reverse_interlaced;
}; };
layout (set = 0, binding = 0) uniform sampler2D screen_textures[3]; layout (set = 0, binding = 0) uniform sampler2D color_texture_l;
layout (set = 0, binding = 1) uniform sampler2D color_texture_r;
vec4 GetScreen(int screen_id) {
#ifdef ARRAY_DYNAMIC_INDEX
return texture(screen_textures[screen_id], frag_tex_coord);
#else
switch (screen_id) {
case 0:
return texture(screen_textures[0], frag_tex_coord);
case 1:
return texture(screen_textures[1], frag_tex_coord);
case 2:
return texture(screen_textures[2], frag_tex_coord);
}
#endif
}
void main() { void main() {
vec4 color_tex_l = GetScreen(screen_id_l); vec4 color_tex_l = texture(color_texture_l, frag_tex_coord);
vec4 color_tex_r = GetScreen(screen_id_r); vec4 color_tex_r = texture(color_texture_r, frag_tex_coord);
color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a);
} }

View file

@ -18,27 +18,13 @@ layout (push_constant, std140) uniform DrawInfo {
int reverse_interlaced; int reverse_interlaced;
}; };
layout (set = 0, binding = 0) uniform sampler2D screen_textures[3]; layout (set = 0, binding = 0) uniform sampler2D color_texture_l;
layout (set = 0, binding = 1) uniform sampler2D color_texture_r;
vec4 GetScreen(int screen_id) {
#ifdef ARRAY_DYNAMIC_INDEX
return texture(screen_textures[screen_id], frag_tex_coord);
#else
switch (screen_id) {
case 0:
return texture(screen_textures[0], frag_tex_coord);
case 1:
return texture(screen_textures[1], frag_tex_coord);
case 2:
return texture(screen_textures[2], frag_tex_coord);
}
#endif
}
void main() { void main() {
float screen_row = o_resolution.x * frag_tex_coord.x; float screen_row = o_resolution.x * frag_tex_coord.x;
if (int(screen_row) % 2 == reverse_interlaced) if (int(screen_row) % 2 == reverse_interlaced)
color = GetScreen(screen_id_l); color = texture(color_texture_l, frag_tex_coord);
else else
color = GetScreen(screen_id_r); color = texture(color_texture_r, frag_tex_coord);
} }

View file

@ -0,0 +1,40 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
int reverse_interlaced;
int convert_colors;
};
layout (set = 0, binding = 0) uniform sampler2D color_texture;
vec3 sRGBToLinear(vec3 c) {
return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));
}
vec3 LinearTosRGB(vec3 c) {
return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(0.0031308, c));
}
void main() {
vec4 pixel = texture(color_texture, frag_tex_coord);
if (convert_colors == 2){
pixel = vec4(LinearTosRGB(pixel.rgb), pixel.a);
} else if (convert_colors == 1){
pixel = vec4(sRGBToLinear(pixel.rgb), pixel.a);
}
color = pixel;
}

View file

@ -0,0 +1,23 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450 core
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 vert_position;
layout (location = 1) in vec2 vert_tex_coord;
layout (location = 0) out vec2 frag_tex_coord;
layout (push_constant, std140) uniform DrawInfo {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id_l;
int screen_id_r;
int layer;
};
void main() {
gl_Position = vec4(vert_position, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View file

@ -75,7 +75,7 @@ void OGLTexture::Allocate(GLenum target, GLsizei levels, GLenum internalformat,
glTexStorage3D(target, levels, internalformat, width, height, depth); glTexStorage3D(target, levels, internalformat, width, height, depth);
break; break;
} }
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

View file

@ -9,7 +9,9 @@
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/frontend/framebuffer_layout.h" #include "core/frontend/framebuffer_layout.h"
#include "core/memory.h" #include "core/memory.h"
#include "gl_state.h"
#include "video_core/pica/pica_core.h" #include "video_core/pica/pica_core.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_texture_mailbox.h" #include "video_core/renderer_opengl/gl_texture_mailbox.h"
#include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/post_processing_opengl.h"
@ -20,6 +22,39 @@
#include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_interlaced_frag.h" #include "video_core/host_shaders/opengl_present_interlaced_frag.h"
#include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/host_shaders/opengl_simple_present_frag.h"
#include "video_core/host_shaders/opengl_simple_present_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_fxaa_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_fxaa_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass0_pre_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass0_pre_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass0_post_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass0_post_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass1_pre_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass1_pre_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass1_post_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass1_post_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass2_pre_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass2_pre_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass2_post_frag.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_pass2_post_vert.h"
#include "video_core/host_shaders/antialiasing/OpenGL/opengl_smaa_hlsl.h"
#include "video_core/host_shaders/antialiasing/AreaTex.h"
#include "video_core/host_shaders/antialiasing/SearchTex.h"
#include "video_core/host_shaders/scaling/opengl_area_sampling_frag.h"
#include "video_core/host_shaders/scaling/opengl_area_sampling_vert.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_vert.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_part1_frag.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass0_part2_frag.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_vert.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part1_frag.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part2_frag.h"
#include "video_core/host_shaders/scaling/FSR/OpenGL/opengl_fsr_pass1_part3_frag.h"
#include "video_core/host_shaders/scaling/FSR/ffx_a_h.h"
#include "video_core/host_shaders/scaling/FSR/ffx_fsr1_h.h"
#include "video_core/host_shaders/scaling/SharpBilinear/OpenGL/opengl_sharpbilinear_vert.h"
#include "video_core/host_shaders/scaling/SharpBilinear/OpenGL/opengl_sharpbilinear_frag.h"
namespace OpenGL { namespace OpenGL {
@ -103,6 +138,7 @@ void RendererOpenGL::SwapBuffers() {
render_window.SwapBuffers(); render_window.SwapBuffers();
#else #else
const auto& main_layout = render_window.GetFramebufferLayout(); const auto& main_layout = render_window.GetFramebufferLayout();
isSecondaryWindow = false;
RenderToMailbox(main_layout, render_window.mailbox, false); RenderToMailbox(main_layout, render_window.mailbox, false);
#ifdef ANDROID #ifdef ANDROID
@ -110,6 +146,7 @@ void RendererOpenGL::SwapBuffers() {
// it means we have a second display // it means we have a second display
if (secondary_window) { if (secondary_window) {
const auto& secondary_layout = secondary_window->GetFramebufferLayout(); const auto& secondary_layout = secondary_window->GetFramebufferLayout();
isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false); RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents(); secondary_window->PollEvents();
} }
@ -117,6 +154,7 @@ void RendererOpenGL::SwapBuffers() {
if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) { if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) {
ASSERT(secondary_window); ASSERT(secondary_window);
const auto& secondary_layout = secondary_window->GetFramebufferLayout(); const auto& secondary_layout = secondary_window->GetFramebufferLayout();
isSecondaryWindow = true;
RenderToMailbox(secondary_layout, secondary_window->mailbox, false); RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
secondary_window->PollEvents(); secondary_window->PollEvents();
} }
@ -315,12 +353,94 @@ void RendererOpenGL::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuff
} }
} }
std::vector<unsigned char> flipVertically(const unsigned char* data, int width, int height, int channels)
{
int rowSize = width * channels;
std::vector<unsigned char> flipped(width * height * channels);
for (int y = 0; y < height; y++)
{
const unsigned char* src = data + (height - 1 - y) * rowSize;
unsigned char* dst = flipped.data() + y * rowSize;
memcpy(dst, src, rowSize);
}
return flipped;
}
void RendererOpenGL::AllocateSMAATextures(){
//Load AreaTex and SearchTex to OGLTexture Objects
areatex.Create();
searchtex.Create();
std::vector<unsigned char> areaTexBytes_Flipped = flipVertically(areaTexBytes, AREATEX_WIDTH, AREATEX_HEIGHT, 2);
std::vector<unsigned char> searchTexBytes_Flipped = flipVertically(searchTexBytes, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, 1);
GLuint old_tex = OpenGLState::GetCurState().texture_units[0].texture_2d;
glBindTexture(GL_TEXTURE_2D, areatex.handle);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT, 0, GL_RG, GL_UNSIGNED_BYTE, areaTexBytes_Flipped.data());
glBindTexture(GL_TEXTURE_2D, searchtex.handle);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, 0, GL_RED, GL_UNSIGNED_BYTE, searchTexBytes_Flipped.data());
glBindTexture(GL_TEXTURE_2D, old_tex);
}
void RendererOpenGL::AllocatePPTextures(){
for (int j = 0; j < intermediateTextures[0].size(); j++){
intermediateTextures[0][j].Release();
intermediateTextures[0][j].Create();
intermediateTextures[0][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
intermediateTextures[1][j].Release();
intermediateTextures[1][j].Create();
intermediateTextures[1][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
}
antialiasFBOTexture[0].Release();
antialiasFBOTexture[0].Create();
antialiasFBOTexture[0].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currTopTextureWidth, currTopTextureHeight);
antialiasFBOTexture[1].Release();
antialiasFBOTexture[1].Create();
antialiasFBOTexture[1].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currBottomTextureWidth, currBottomTextureHeight);
LOG_INFO(Render_OpenGL, "Reallocated Textures");
}
void RendererOpenGL::AllocateOutputSizeTextures(){
for (int i = 0; i < intermediateOutputSizeTextures[isSecondaryWindow].size(); i++){
if (currOutputScreenRects[isSecondaryWindow][i].GetHeight() != 0 && currOutputScreenRects[isSecondaryWindow][i].GetWidth() != 0){
for (int j = 0; j < intermediateOutputSizeTextures[isSecondaryWindow][0].size(); j++){
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Release();
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Create();
intermediateOutputSizeTextures[isSecondaryWindow][i][j].Allocate(GL_TEXTURE_2D, 1, GL_RGBA16F, currOutputScreenRects[isSecondaryWindow][i].GetWidth(), currOutputScreenRects[isSecondaryWindow][i].GetHeight());
}
}
}
LOG_INFO(Render_OpenGL, "Reallocated OutputSize Textures.\nPrevRects:\n{}x{}\n{}x{}\n{}x{}\nCurrRects:\n{}x{}\n{}x{}\n{}x{}",
prevOutputScreenRects[isSecondaryWindow][0].GetWidth(), prevOutputScreenRects[isSecondaryWindow][0].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][1].GetWidth(), prevOutputScreenRects[isSecondaryWindow][1].GetHeight(),
prevOutputScreenRects[isSecondaryWindow][2].GetWidth(), prevOutputScreenRects[isSecondaryWindow][2].GetHeight(),
currOutputScreenRects[isSecondaryWindow][0].GetWidth(), currOutputScreenRects[isSecondaryWindow][0].GetHeight(),
currOutputScreenRects[isSecondaryWindow][1].GetWidth(), currOutputScreenRects[isSecondaryWindow][1].GetHeight(),
currOutputScreenRects[isSecondaryWindow][2].GetWidth(), currOutputScreenRects[isSecondaryWindow][2].GetHeight()
);
}
/** /**
* Initializes the OpenGL state and creates persistent objects. * Initializes the OpenGL state and creates persistent objects.
*/ */
void RendererOpenGL::InitOpenGLObjects() { void RendererOpenGL::InitOpenGLObjects() {
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 1.0f); Settings::values.bg_blue.GetValue(), 0.0f);
for (std::size_t i = 0; i < samplers.size(); i++) { for (std::size_t i = 0; i < samplers.size(); i++) {
samplers[i].Create(); samplers[i].Create();
@ -354,6 +474,10 @@ void RendererOpenGL::InitOpenGLObjects() {
glEnableVertexAttribArray(attrib_position); glEnableVertexAttribArray(attrib_position);
glEnableVertexAttribArray(attrib_tex_coord); glEnableVertexAttribArray(attrib_tex_coord);
// Allocate textures for Post Processing
AllocateSMAATextures();
textureFBO.Create();
// Allocate textures for each screen // Allocate textures for each screen
for (auto& screen_info : screen_infos) { for (auto& screen_info : screen_infos) {
screen_info.texture.resource.Create(); screen_info.texture.resource.Create();
@ -373,7 +497,7 @@ void RendererOpenGL::InitOpenGLObjects() {
screen_info.display_texture = screen_info.texture.resource.handle; screen_info.display_texture = screen_info.texture.resource.handle;
} }
AllocatePPTextures();
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture_2d = 0;
state.Apply(); state.Apply();
} }
@ -411,30 +535,86 @@ void RendererOpenGL::ReloadShader(Settings::StereoRenderOption render_3d) {
} }
} }
} }
shader.Create(HostShaders::OPENGL_PRESENT_VERT, shader_data); Present_shader.Create(HostShaders::OPENGL_PRESENT_VERT, shader_data);
state.draw.shader_program = shader.handle; state.draw.shader_program = Present_shader.handle;
AttachUniforms();
// Setup FXAA, SMAA and Simple Present Shaders
std::string FXAA_shader_data = fragment_shader_precision_OES;
FXAA_shader_data += HostShaders::OPENGL_FXAA_FRAG;
FXAA_shader.Create(HostShaders::OPENGL_FXAA_VERT, FXAA_shader_data);
std::string AREA_SAMPLING_shader_data = fragment_shader_precision_OES;
AREA_SAMPLING_shader_data += HostShaders::OPENGL_AREA_SAMPLING_FRAG;
AREA_SAMPLING_shader.Create(HostShaders::OPENGL_AREA_SAMPLING_VERT, AREA_SAMPLING_shader_data);
std::string SimplePresent_shader_data = fragment_shader_precision_OES;
SimplePresent_shader_data += HostShaders::OPENGL_SIMPLE_PRESENT_FRAG;
SimplePresent_shader.Create(HostShaders::OPENGL_SIMPLE_PRESENT_VERT, SimplePresent_shader_data);
std::string SMAA_PASS_0_shader_frag_data = fragment_shader_precision_OES;
SMAA_PASS_0_shader_frag_data += HostShaders::OPENGL_SMAA_PASS0_PRE_FRAG;
SMAA_PASS_0_shader_frag_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_0_shader_frag_data += HostShaders::OPENGL_SMAA_PASS0_POST_FRAG;
std::string SMAA_PASS_0_shader_vert_data;
SMAA_PASS_0_shader_vert_data += HostShaders::OPENGL_SMAA_PASS0_PRE_VERT;
SMAA_PASS_0_shader_vert_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_0_shader_vert_data += HostShaders::OPENGL_SMAA_PASS0_POST_VERT;
SMAA_PASS_0_shader.Create(SMAA_PASS_0_shader_vert_data, SMAA_PASS_0_shader_frag_data);
std::string SMAA_PASS_1_shader_frag_data = fragment_shader_precision_OES;
SMAA_PASS_1_shader_frag_data += HostShaders::OPENGL_SMAA_PASS1_PRE_FRAG;
SMAA_PASS_1_shader_frag_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_1_shader_frag_data += HostShaders::OPENGL_SMAA_PASS1_POST_FRAG;
std::string SMAA_PASS_1_shader_vert_data;
SMAA_PASS_1_shader_vert_data += HostShaders::OPENGL_SMAA_PASS1_PRE_VERT;
SMAA_PASS_1_shader_vert_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_1_shader_vert_data += HostShaders::OPENGL_SMAA_PASS1_POST_VERT;
SMAA_PASS_1_shader.Create(SMAA_PASS_1_shader_vert_data, SMAA_PASS_1_shader_frag_data);
std::string SMAA_PASS_2_shader_frag_data = fragment_shader_precision_OES;
SMAA_PASS_2_shader_frag_data += HostShaders::OPENGL_SMAA_PASS2_PRE_FRAG;
SMAA_PASS_2_shader_frag_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_2_shader_frag_data += HostShaders::OPENGL_SMAA_PASS2_POST_FRAG;
std::string SMAA_PASS_2_shader_vert_data;
SMAA_PASS_2_shader_vert_data += HostShaders::OPENGL_SMAA_PASS2_PRE_VERT;
SMAA_PASS_2_shader_vert_data += HostShaders::OPENGL_SMAA_HLSL;
SMAA_PASS_2_shader_vert_data += HostShaders::OPENGL_SMAA_PASS2_POST_VERT;
SMAA_PASS_2_shader.Create(SMAA_PASS_2_shader_vert_data, SMAA_PASS_2_shader_frag_data);
std::string FSR_PASS_0_shader_frag_data = fragment_shader_precision_OES;
FSR_PASS_0_shader_frag_data += HostShaders::OPENGL_FSR_PASS0_PART1_FRAG;
FSR_PASS_0_shader_frag_data += HostShaders::FFX_A_H;
FSR_PASS_0_shader_frag_data += HostShaders::FFX_FSR1_H;
FSR_PASS_0_shader_frag_data += HostShaders::OPENGL_FSR_PASS0_PART2_FRAG;
FSR_PASS_0_shader.Create(HostShaders::OPENGL_FSR_PASS0_VERT, FSR_PASS_0_shader_frag_data);
std::string FSR_PASS_1_shader_frag_data = fragment_shader_precision_OES;
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART1_FRAG;
FSR_PASS_1_shader_frag_data += HostShaders::FFX_A_H;
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART2_FRAG;
FSR_PASS_1_shader_frag_data += HostShaders::FFX_FSR1_H;
FSR_PASS_1_shader_frag_data += HostShaders::OPENGL_FSR_PASS1_PART3_FRAG;
FSR_PASS_1_shader.Create(HostShaders::OPENGL_FSR_PASS1_VERT, FSR_PASS_1_shader_frag_data);
std::string SharpBilinear_shader_frag_data = fragment_shader_precision_OES;
SharpBilinear_shader_frag_data += HostShaders::OPENGL_SHARPBILINEAR_FRAG;
SharpBilinear_shader.Create(HostShaders::OPENGL_SHARPBILINEAR_VERT, SharpBilinear_shader_frag_data);
state.Apply(); state.Apply();
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture");
if (render_3d == Settings::StereoRenderOption::Anaglyph || if (render_3d == Settings::StereoRenderOption::Anaglyph ||
render_3d == Settings::StereoRenderOption::Interlaced || render_3d == Settings::StereoRenderOption::Interlaced ||
render_3d == Settings::StereoRenderOption::ReverseInterlaced) { render_3d == Settings::StereoRenderOption::ReverseInterlaced) {
uniform_color_texture_r = glGetUniformLocation(shader.handle, "color_texture_r");
} }
if (render_3d == Settings::StereoRenderOption::Interlaced || if (render_3d == Settings::StereoRenderOption::Interlaced ||
render_3d == Settings::StereoRenderOption::ReverseInterlaced) { render_3d == Settings::StereoRenderOption::ReverseInterlaced) {
GLuint uniform_reverse_interlaced =
glGetUniformLocation(shader.handle, "reverse_interlaced");
if (render_3d == Settings::StereoRenderOption::ReverseInterlaced) if (render_3d == Settings::StereoRenderOption::ReverseInterlaced)
glUniform1i(uniform_reverse_interlaced, 1); glUniform1i(uniform_reverse_interlaced, 1);
else else
glUniform1i(uniform_reverse_interlaced, 0); glUniform1i(uniform_reverse_interlaced, 0);
} }
uniform_i_resolution = glGetUniformLocation(shader.handle, "i_resolution");
uniform_o_resolution = glGetUniformLocation(shader.handle, "o_resolution");
uniform_layer = glGetUniformLocation(shader.handle, "layer");
attrib_position = glGetAttribLocation(shader.handle, "vert_position");
attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
} }
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@ -505,69 +685,520 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
state.Apply(); state.Apply();
} }
void RendererOpenGL::AttachUniforms(){
uniform_modelview_matrix = glGetUniformLocation(state.draw.shader_program, "modelview_matrix");
uniform_color_texture = glGetUniformLocation(state.draw.shader_program, "color_texture");
uniform_color_texture_r = glGetUniformLocation(state.draw.shader_program, "color_texture_r");
uniform_reverse_interlaced = glGetUniformLocation(state.draw.shader_program, "reverse_interlaced");
uniform_i_resolution = glGetUniformLocation(state.draw.shader_program, "i_resolution");
uniform_o_resolution = glGetUniformLocation(state.draw.shader_program, "o_resolution");
uniform_convert_colors = glGetUniformLocation(state.draw.shader_program, "convert_colors");
uniform_fsr_sharpening = glGetUniformLocation(state.draw.shader_program, "FSR_SHARPENING");
uniform_layer = glGetUniformLocation(state.draw.shader_program, "layer");
attrib_position = glGetAttribLocation(state.draw.shader_program, "vert_position");
attrib_tex_coord = glGetAttribLocation(state.draw.shader_program, "vert_tex_coord");
}
/** /**
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
* rotation. * rotation.
*/ */
void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float screenLeft, float screenTop, float screenWidth,
float h, Layout::DisplayOrientation orientation) { float screenHeight, Layout::DisplayOrientation orientation) {
const auto& texcoords = screen_info.display_texcoords; const auto& texcoords = screen_info.display_texcoords;
const u32 scale_factor = GetResolutionScaleFactor();
float textureWidth = static_cast<float>(screen_info.texture.height * scale_factor);
float textureHeight = static_cast<float>(screen_info.texture.width * scale_factor);
int currScreen;
if (textureWidth == currTopTextureWidth && textureHeight == currTopTextureHeight){
currScreen = 0;
} else {
currScreen = 1;
}
std::array<ScreenRectVertex, 4> vertices; // Texture Width and Height when correctly rotated to landscape
bool isDownsampling = false;
int scalingMode; //0 is Nearest Neighbor, 1 is Gamma Corrected Bilinear, 2 is Adaptive (Bilinear/Area), 3 is FSR, 4 is Sharp Bilinear
scalingMode = static_cast<int>(Settings::values.output_scaling.GetValue());
int antialiasingMode = static_cast<int>(Settings::values.antialiasing_filter.GetValue()); //0 is none, 1 is FXAA, 2 is SMAA
float fsr_sharpening = 2 - (2 * (Settings::values.fsr_sharpness.GetValue()/ 100.0f));
if (orientation == Layout::DisplayOrientation::Landscape || orientation == Layout::DisplayOrientation::LandscapeFlipped) {
if (textureWidth > screenWidth){
isDownsampling = true;
}
} else {
if (textureWidth > screenHeight){
isDownsampling = true;
}
}
// Rotate Internal Texture to Landscape (The 3DS stores images rotated 90° internally)
std::array<ScreenRectVertex, 4> rotate_vertices;
rotate_vertices = {{
ScreenRectVertex(-1.f, 1.f, texcoords.bottom, texcoords.left), //Left, Top
ScreenRectVertex(1.f, 1.f, texcoords.bottom, texcoords.right), //Right, Top
ScreenRectVertex(-1.f, -1.f, texcoords.top, texcoords.left), //Left, Bottom
ScreenRectVertex(1.f, -1.f, texcoords.top, texcoords.right), //Right, Bottom
}};
// Vertices for 1:1 Texture Mapping.
std::array<ScreenRectVertex, 4> pass_through_vertices;
pass_through_vertices = {{
ScreenRectVertex(-1.f, 1.f, 0.f, 1.f), //Left, Top
ScreenRectVertex(1.f, 1.f, 1.f, 1.f), //Right, Top
ScreenRectVertex(-1.f, -1.f, 0.f, 0.f), //Left, Bottom
ScreenRectVertex(1.f, -1.f, 1.f, 0.f), //Right, Bottom
}};
// Vertices for Azahar's Output Layout
std::array<ScreenRectVertex, 4> output_vertices;
switch (orientation) { switch (orientation) {
case Layout::DisplayOrientation::Landscape: case Layout::DisplayOrientation::Landscape:
vertices = {{ output_vertices = {{
ScreenRectVertex(x, y, texcoords.bottom, texcoords.left), ScreenRectVertex(screenLeft, screenTop, 0.f, 1.f), //Left, Top
ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right), ScreenRectVertex(screenLeft + screenWidth, screenTop, 1.f, 1.f), //Right, Top
ScreenRectVertex(x, y + h, texcoords.top, texcoords.left), ScreenRectVertex(screenLeft, screenTop + screenHeight, 0.f, 0.f), //Left, Bottom
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right), ScreenRectVertex(screenLeft + screenWidth, screenTop + screenHeight, 1.f, 0.f), //Right, Bottom
}}; }};
break; break;
case Layout::DisplayOrientation::Portrait: case Layout::DisplayOrientation::Portrait:
vertices = {{ output_vertices = {{
ScreenRectVertex(x, y, texcoords.bottom, texcoords.right), ScreenRectVertex(screenLeft, screenTop, 1.f, 1.f), //Left, Top
ScreenRectVertex(x + w, y, texcoords.top, texcoords.right), ScreenRectVertex(screenLeft + screenWidth, screenTop, 1.f, 0.f), //Right, Top
ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left), ScreenRectVertex(screenLeft, screenTop + screenHeight, 0.f, 1.f), //Left, Bottom
ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left), ScreenRectVertex(screenLeft + screenWidth, screenTop + screenHeight, 0.f, 0.f), //Right, Bottom
}}; }};
std::swap(h, w); std::swap(screenHeight, screenWidth);
break; break;
case Layout::DisplayOrientation::LandscapeFlipped: case Layout::DisplayOrientation::LandscapeFlipped:
vertices = {{ output_vertices = {{
ScreenRectVertex(x, y, texcoords.top, texcoords.right), ScreenRectVertex(screenLeft, screenTop, 0.f, 0.f), //Left, Top
ScreenRectVertex(x + w, y, texcoords.top, texcoords.left), ScreenRectVertex(screenLeft + screenWidth, screenTop, 1.f, 0.f), //Right, Top
ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.right), ScreenRectVertex(screenLeft, screenTop + screenHeight, 0.f, 1.f), //Left, Bottom
ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.left), ScreenRectVertex(screenLeft + screenWidth, screenTop + screenHeight, 1.f, 1.f), //Right, Bottom
}}; }};
break; break;
case Layout::DisplayOrientation::PortraitFlipped: case Layout::DisplayOrientation::PortraitFlipped:
vertices = {{ output_vertices = {{
ScreenRectVertex(x, y, texcoords.top, texcoords.left), ScreenRectVertex(screenLeft, screenTop, 0.f, 0.f), //Left, Top
ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.left), ScreenRectVertex(screenLeft + screenWidth, screenTop, 0.f, 1.f), //Right, Top
ScreenRectVertex(x, y + h, texcoords.top, texcoords.right), ScreenRectVertex(screenLeft, screenTop + screenHeight, 1.f, 0.f), //Left, Bottom
ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.right), ScreenRectVertex(screenLeft + screenWidth, screenTop + screenHeight, 1.f, 1.f), //Right, Bottom
}}; }};
std::swap(h, w); std::swap(screenHeight, screenWidth);
break; break;
default: default:
LOG_ERROR(Render_OpenGL, "Unknown DisplayOrientation: {}", orientation); LOG_ERROR(Render_OpenGL, "Unknown DisplayOrientation: {}", orientation);
break; break;
} }
const u32 scale_factor = GetResolutionScaleFactor(); GLuint originalReadFramebuffer = state.draw.read_framebuffer;
const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; GLuint originalDrawFramebuffer = state.draw.draw_framebuffer;
glUniform4f(uniform_i_resolution, static_cast<float>(screen_info.texture.width * scale_factor), if (antialiasingMode == 1){
static_cast<float>(screen_info.texture.height * scale_factor), //Pass 1
1.0f / static_cast<float>(screen_info.texture.width * scale_factor), state.draw.read_framebuffer = textureFBO.handle;
1.0f / static_cast<float>(screen_info.texture.height * scale_factor)); state.draw.draw_framebuffer = textureFBO.handle;
glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w);
state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].sampler = sampler;
state.Apply(); state.Apply();
state.viewport.x = 0;
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][0].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SimplePresent_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(rotate_vertices), rotate_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//Pass 2
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FXAA_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
if (scalingMode == 3){
glUniform1i(uniform_convert_colors, 0);
} else {
glUniform1i(uniform_convert_colors, 1);
}
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else if (antialiasingMode == 2){
// Landscape Gamma Space Texture
state.draw.read_framebuffer = textureFBO.handle;
state.draw.draw_framebuffer = textureFBO.handle;
state.Apply();
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][0].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SimplePresent_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(rotate_vertices), rotate_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Landscape Linear Space Texture
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][3].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SimplePresent_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 1);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Edge Detection
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][1].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SMAA_PASS_0_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Blending Weight Calculation
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][2].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SMAA_PASS_1_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][1].handle;
state.texture_units[0].sampler = samplers[1].handle;
state.texture_units[1].texture_2d = areatex.handle;
state.texture_units[1].sampler = samplers[1].handle;
state.texture_units[2].texture_2d = searchtex.handle;
state.texture_units[2].sampler = samplers[1].handle;
GLuint uniform_areatex = glGetUniformLocation(state.draw.shader_program, "areaTex");
GLuint uniform_searchtex = glGetUniformLocation(state.draw.shader_program, "searchTex");
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_areatex, 1);
glUniform1i(uniform_searchtex, 2);
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Neighborhood Blending
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SMAA_PASS_2_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][2].handle;
state.texture_units[0].sampler = samplers[1].handle;
state.texture_units[1].texture_2d = intermediateTextures[currScreen][3].handle;
state.texture_units[1].sampler = samplers[1].handle;
GLuint uniform_smaa_input = glGetUniformLocation(state.draw.shader_program, "SMAA_Input");
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_smaa_input, 1);
if (scalingMode == 3){
glUniform1i(uniform_convert_colors, 2);
} else {
glUniform1i(uniform_convert_colors, 0);
}
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
state.draw.read_framebuffer = textureFBO.handle;
state.draw.draw_framebuffer = textureFBO.handle;
state.Apply();
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, antialiasFBOTexture[currScreen].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = SimplePresent_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = screen_info.display_texture;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
if (scalingMode == 3){
glUniform1i(uniform_convert_colors, 0);
} else {
glUniform1i(uniform_convert_colors, 1);
}
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(rotate_vertices), rotate_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
if (scalingMode == 2){
if (isDownsampling){
//Output
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = AREA_SAMPLING_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
state.texture_units[0].sampler = samplers[0].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 2);
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
//Output
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = Present_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 2);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
} else if (scalingMode == 3){
//Use intermiedatetextures[currScreen]
if (isDownsampling){
// EASU (1x)
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][4].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_0_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
state.texture_units[0].sampler = samplers[0].handle;
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
glUniform4f(uniform_o_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// RCAS
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = textureWidth;
state.viewport.height = textureHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateTextures[currScreen][5].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_1_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][4].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1f(uniform_fsr_sharpening, fsr_sharpening);
glUniform4f(uniform_o_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Area Sampling
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = AREA_SAMPLING_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateTextures[currScreen][5].handle;
state.texture_units[0].sampler = samplers[0].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
// EASU (to output resolution)
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_0_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
state.texture_units[0].sampler = samplers[0].handle;
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// RCAS
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = screenWidth;
state.viewport.height = screenHeight;
state.Apply();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle, 0);
glClear(GL_COLOR_BUFFER_BIT);
state.draw.shader_program = FSR_PASS_1_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][0].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1f(uniform_fsr_sharpening, fsr_sharpening);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(pass_through_vertices), pass_through_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Normal Present
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = Present_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = intermediateOutputSizeTextures[isSecondaryWindow][currOutputScreen][1].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 0);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
} else if (scalingMode == 4){
//Output
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = SharpBilinear_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
state.texture_units[0].sampler = samplers[1].handle;
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 2);
glUniform4f(uniform_i_resolution, textureWidth, textureHeight, 1.0f / textureWidth, 1.0f / textureHeight);
glUniform4f(uniform_o_resolution, screenWidth, screenHeight, 1.0f / screenWidth, 1.0f / screenHeight);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
//Output
state.draw.read_framebuffer = originalReadFramebuffer;
state.draw.draw_framebuffer = originalDrawFramebuffer;
state.Apply();
state.viewport.x = originalViewport[0];
state.viewport.y = originalViewport[1];
state.viewport.width = originalViewport[2];
state.viewport.height = originalViewport[3];
state.Apply();
state.draw.shader_program = Present_shader.handle;
state.Apply();
AttachUniforms();
state.texture_units[0].texture_2d = antialiasFBOTexture[currScreen].handle;
if (scalingMode == 1){
state.texture_units[0].sampler = samplers[1].handle;
} else {
state.texture_units[0].sampler = samplers[0].handle;
}
glUniform1i(uniform_color_texture, 0);
glUniform1i(uniform_convert_colors, 2);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(output_vertices), output_vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture_2d = 0;
state.texture_units[0].sampler = 0; state.texture_units[0].sampler = 0;
state.Apply(); state.Apply();
@ -625,13 +1256,15 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
} }
const u32 scale_factor = GetResolutionScaleFactor(); const u32 scale_factor = GetResolutionScaleFactor();
const GLuint sampler = samplers[Settings::values.filter_mode.GetValue()].handle; int scalingMode = static_cast<int>(Settings::values.output_scaling.GetValue());
const GLuint sampler = samplers[scalingMode > 0 ? 1 : 0].handle;
glUniform4f(uniform_i_resolution, glUniform4f(uniform_i_resolution,
static_cast<float>(screen_info_l.texture.width * scale_factor), static_cast<float>(screen_info_l.texture.width * scale_factor),
static_cast<float>(screen_info_l.texture.height * scale_factor), static_cast<float>(screen_info_l.texture.height * scale_factor),
1.0f / static_cast<float>(screen_info_l.texture.width * scale_factor), 1.0f / static_cast<float>(screen_info_l.texture.width * scale_factor),
1.0f / static_cast<float>(screen_info_l.texture.height * scale_factor)); 1.0f / static_cast<float>(screen_info_l.texture.height * scale_factor));
glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
state.texture_units[0].texture_2d = screen_info_l.display_texture; state.texture_units[0].texture_2d = screen_info_l.display_texture;
state.texture_units[1].texture_2d = screen_info_r.display_texture; state.texture_units[1].texture_2d = screen_info_r.display_texture;
state.texture_units[0].sampler = sampler; state.texture_units[0].sampler = sampler;
@ -655,12 +1288,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
if (settings.bg_color_update_requested.exchange(false)) { if (settings.bg_color_update_requested.exchange(false)) {
// Update background color before drawing // Update background color before drawing
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 1.0f); Settings::values.bg_blue.GetValue(), 0.0f);
} }
if (settings.shader_update_requested.exchange(false)) { if (settings.shader_update_requested.exchange(false)) {
// Update fragment shader before drawing // Update fragment shader before drawing
shader.Release(); Present_shader.Release();
// Link shaders and get variable locations // Link shaders and get variable locations
ReloadShader(layout.render_3d_mode); ReloadShader(layout.render_3d_mode);
} }
@ -668,15 +1301,52 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
const auto& top_screen = layout.top_screen; const auto& top_screen = layout.top_screen;
const auto& bottom_screen = layout.bottom_screen; const auto& bottom_screen = layout.bottom_screen;
glViewport(0, 0, layout.width, layout.height); // Track Texture Changes
currTopTextureWidth = static_cast<float>(screen_infos[0].texture.height * GetResolutionScaleFactor());
currTopTextureHeight = static_cast<float>(screen_infos[0].texture.width * GetResolutionScaleFactor());
currBottomTextureWidth = static_cast<float>(screen_infos[2].texture.height * GetResolutionScaleFactor());
currBottomTextureHeight = static_cast<float>(screen_infos[2].texture.width * GetResolutionScaleFactor());
if (currTopTextureWidth != prevTopTextureWidth || currTopTextureHeight != prevTopTextureHeight || currBottomTextureWidth != prevBottomTextureWidth || currBottomTextureHeight != prevBottomTextureHeight){
AllocatePPTextures();
// LOG_INFO(Render_OpenGL, "PrevTopTexture Res: {}x{}, CurrTopTexture Res: {}x{}, PrevBottomTexture Res: {}x{}, CurrBottomTexture Res: {}x{}", prevTopTextureWidth, prevTopTextureHeight, currTopTextureWidth, currTopTextureHeight, prevBottomTextureWidth, prevBottomTextureHeight, currBottomTextureWidth, currBottomTextureHeight);
}
prevTopTextureWidth = currTopTextureWidth;
prevTopTextureHeight = currTopTextureHeight;
prevBottomTextureWidth = currBottomTextureWidth;
prevBottomTextureHeight = currBottomTextureHeight;
//Track Layout Changes
currOutputScreenRects[isSecondaryWindow][0] = layout.top_screen;
currOutputScreenRects[isSecondaryWindow][1] = layout.bottom_screen;
currOutputScreenRects[isSecondaryWindow][2] = layout.additional_screen;
if (currOutputScreenRects[isSecondaryWindow][0] != prevOutputScreenRects[isSecondaryWindow][0] || currOutputScreenRects[isSecondaryWindow][1] != prevOutputScreenRects[isSecondaryWindow][1]){
if (layout.additional_screen_enabled){
if (currOutputScreenRects[isSecondaryWindow][2] != prevOutputScreenRects[isSecondaryWindow][2]){
AllocateOutputSizeTextures();
}
} else {
AllocateOutputSizeTextures();
}
}
prevOutputScreenRects[isSecondaryWindow][0] = currOutputScreenRects[isSecondaryWindow][0];
prevOutputScreenRects[isSecondaryWindow][1] = currOutputScreenRects[isSecondaryWindow][1];
prevOutputScreenRects[isSecondaryWindow][2] = currOutputScreenRects[isSecondaryWindow][2];
//Set the Viewport
state.viewport.x = 0;
state.viewport.y = 0;
state.viewport.width = layout.width;
state.viewport.height = layout.height;
originalViewport = {0, 0, static_cast<int>(layout.width), static_cast<int>(layout.height)};
state.Apply();
if (render_window.NeedsClearing()) { if (render_window.NeedsClearing()) {
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
// Set projection matrix // Set projection matrix
std::array<GLfloat, 3 * 2> ortho_matrix = ortho_matrix = MakeOrthographicMatrix((float)layout.width, (float)layout.height, flipped);
MakeOrthographicMatrix((float)layout.width, (float)layout.height, flipped);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
// Bind texture in Texture Unit 0 // Bind texture in Texture Unit 0
glUniform1i(uniform_color_texture, 0); glUniform1i(uniform_color_texture, 0);
@ -693,18 +1363,23 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
if (!Settings::values.swap_screen.GetValue()) { if (!Settings::values.swap_screen.GetValue()) {
currOutputScreen = 0;
DrawTopScreen(layout, top_screen); DrawTopScreen(layout, top_screen);
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
ApplySecondLayerOpacity(layout.bottom_opacity); ApplySecondLayerOpacity(layout.bottom_opacity);
currOutputScreen = 1;
DrawBottomScreen(layout, bottom_screen); DrawBottomScreen(layout, bottom_screen);
} else { } else {
currOutputScreen = 1;
DrawBottomScreen(layout, bottom_screen); DrawBottomScreen(layout, bottom_screen);
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
ApplySecondLayerOpacity(layout.top_opacity); ApplySecondLayerOpacity(layout.top_opacity);
currOutputScreen = 0;
DrawTopScreen(layout, top_screen); DrawTopScreen(layout, top_screen);
} }
if (layout.additional_screen_enabled) { if (layout.additional_screen_enabled) {
currOutputScreen = 2;
const auto& additional_screen = layout.additional_screen; const auto& additional_screen = layout.additional_screen;
if (!Settings::values.swap_screen.GetValue()) { if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, additional_screen); DrawTopScreen(layout, additional_screen);

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include "gl_resource_manager.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/frame_dumper_opengl.h" #include "video_core/renderer_opengl/frame_dumper_opengl.h"
#include "video_core/renderer_opengl/gl_driver.h" #include "video_core/renderer_opengl/gl_driver.h"
@ -57,6 +58,9 @@ public:
private: private:
void InitOpenGLObjects(); void InitOpenGLObjects();
void ReloadShader(Settings::StereoRenderOption render_3d); void ReloadShader(Settings::StereoRenderOption render_3d);
void AllocateSMAATextures();
void AllocatePPTextures();
void AllocateOutputSizeTextures();
void PrepareRendertarget(); void PrepareRendertarget();
void RenderScreenshot(); void RenderScreenshot();
void RenderToMailbox(const Layout::FramebufferLayout& layout, void RenderToMailbox(const Layout::FramebufferLayout& layout,
@ -80,6 +84,8 @@ private:
// Loads framebuffer from emulated memory into the display information structure // Loads framebuffer from emulated memory into the display information structure
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info, void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
bool right_eye, const Pica::ColorFill& color_fill); bool right_eye, const Pica::ColorFill& color_fill);
// Attach Uniforms to the current shader
void AttachUniforms();
private: private:
Pica::PicaCore& pica; Pica::PicaCore& pica;
@ -90,17 +96,46 @@ private:
// OpenGL object IDs // OpenGL object IDs
OGLVertexArray vertex_array; OGLVertexArray vertex_array;
OGLBuffer vertex_buffer; OGLBuffer vertex_buffer;
OGLProgram shader; OGLProgram Present_shader;
OGLProgram SimplePresent_shader;
OGLProgram FXAA_shader;
OGLProgram SMAA_PASS_0_shader;
OGLProgram SMAA_PASS_1_shader;
OGLProgram SMAA_PASS_2_shader;
OGLProgram AREA_SAMPLING_shader;
OGLProgram FSR_PASS_0_shader;
OGLProgram FSR_PASS_1_shader;
OGLProgram SharpBilinear_shader;
OGLFramebuffer screenshot_framebuffer; OGLFramebuffer screenshot_framebuffer;
std::array<OGLSampler, 2> samplers; std::array<OGLSampler, 2> samplers;
// OpenGL objects for post processing
OGLFramebuffer textureFBO;
//Textures for Top and Bottom Screen Respectively
std::array<std::array<OGLTexture, 7>, 2> intermediateTextures;
std::array<OGLTexture, 2> antialiasFBOTexture;
//Intermediate Textures at output size. These are for Top Screen, Bottom Screen and Additional Screen Respectively
std::array<std::array<std::array<OGLTexture, 3>, 3>, 2> intermediateOutputSizeTextures;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> prevOutputScreenRects;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> currOutputScreenRects;
int currOutputScreen;
OGLTexture areatex;
OGLTexture searchtex;
// Display information for top and bottom screens respectively // Display information for top and bottom screens respectively
std::array<ScreenInfo, 3> screen_infos; std::array<ScreenInfo, 3> screen_infos;
std::array<GLfloat, 3 * 2> ortho_matrix;
// Shader uniform location indices // Shader uniform location indices
GLuint uniform_modelview_matrix; GLuint uniform_modelview_matrix;
GLuint uniform_color_texture; GLuint uniform_color_texture;
GLuint uniform_color_texture_r; GLuint uniform_color_texture_r;
GLuint uniform_reverse_interlaced;
// Shader Uniform for converting colors. 0 is no conversion, 1 is sRGB -> linear, 2 is Linear -> sRGB
GLuint uniform_convert_colors;
GLuint uniform_fsr_sharpening;
// Shader uniform for Dolphin compatibility // Shader uniform for Dolphin compatibility
GLuint uniform_i_resolution; GLuint uniform_i_resolution;
@ -112,6 +147,21 @@ private:
GLuint attrib_tex_coord; GLuint attrib_tex_coord;
FrameDumperOpenGL frame_dumper; FrameDumperOpenGL frame_dumper;
// Variables tracking texture changes
float prevTopTextureWidth;
float prevTopTextureHeight;
float prevBottomTextureWidth;
float prevBottomTextureHeight;
float currTopTextureWidth;
float currTopTextureHeight;
float currBottomTextureWidth;
float currBottomTextureHeight;
std::array<int, 4> originalViewport;
// Secondary Layout Fix
bool isSecondaryWindow;
}; };
} // namespace OpenGL } // namespace OpenGL

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,36 @@ struct TextureInfo {
VmaAllocation allocation; VmaAllocation allocation;
}; };
struct StagedTextureInfo {
u32 width;
u32 height;
u32 channels;
u32 size;
Pica::PixelFormat format;
vk::Buffer buffer;
VmaAllocation bufferAllocation;
vk::Image image;
vk::ImageView image_view;
VmaAllocation imageAllocation;
void* bufferDataPtr;
TextureInfo texInfo;
};
struct ScreenRectVertex {
ScreenRectVertex() = default;
ScreenRectVertex(float x, float y, float u, float v)
: position{Common::MakeVec(x, y)}, tex_coord{Common::MakeVec(u, v)} {}
Common::Vec2f position;
Common::Vec2f tex_coord;
};
struct VertexBufferPointer {
unsigned char* data;
unsigned int offset;
bool invalidate;
};
struct ScreenInfo { struct ScreenInfo {
TextureInfo texture; TextureInfo texture;
Common::Rectangle<f32> texcoords; Common::Rectangle<f32> texcoords;
@ -62,13 +92,16 @@ struct PresentUniformData {
int screen_id_r = 0; int screen_id_r = 0;
int layer = 0; int layer = 0;
int reverse_interlaced = 0; int reverse_interlaced = 0;
int convert_colors;
float FSR_SHARPENING;
}; };
static_assert(sizeof(PresentUniformData) == 112, static_assert(sizeof(PresentUniformData) == 120,
"PresentUniformData does not structure in shader!"); "PresentUniformData does not structure in shader!");
class RendererVulkan : public VideoCore::RendererBase { class RendererVulkan : public VideoCore::RendererBase {
static constexpr std::size_t PRESENT_PIPELINES = 3; static constexpr std::size_t PRESENT_PIPELINES = 3;
static constexpr std::size_t POST_PIPELINES_SCREEN = 2;
static constexpr std::size_t POST_PIPELINES_TEXTURE = 7;
public: public:
explicit RendererVulkan(Core::System& system, Pica::PicaCore& pica, Frontend::EmuWindow& window, explicit RendererVulkan(Core::System& system, Pica::PicaCore& pica, Frontend::EmuWindow& window,
Frontend::EmuWindow* secondary_window); Frontend::EmuWindow* secondary_window);
@ -95,7 +128,18 @@ private:
void RenderScreenshot(); void RenderScreenshot();
void RenderScreenshotWithStagingCopy(); void RenderScreenshotWithStagingCopy();
bool TryRenderScreenshotWithHostMemory(); bool TryRenderScreenshotWithHostMemory();
void PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout); // Sets up command buffer for sampling from a screen_info to the screen framebuffer
void PrepareDrawFromScreenInfo(Frame* frame, const Layout::FramebufferLayout& layout, vk::Pipeline shaderPipeline, std::vector<u32> screenids, int filterMode);
// Sets up command buffer for sampling from a texture to the screen framebuffer
void PrepareDrawFromTextureInfo(Frame* frame, const Layout::FramebufferLayout& layout, vk::Pipeline shaderPipeline, std::vector<TextureInfo> texturesToSample, int filterMode);
// Sets up command buffer for sampling from a texture to an intermediate texture framebuffer
void PrepareTextureDrawFromTextureInfo(TextureInfo framebufferTexture, vk::Framebuffer framebuffer, vk::Pipeline shaderPipeline, std::vector<TextureInfo> texturesToSample, int filterMode);
// Sets up command buffer for sampling from a screen_info to an intermediate texture framebuffer
void PrepareTextureDrawFromScreenInfo(TextureInfo framebufferTexture, vk::Framebuffer framebuffer, vk::Pipeline shaderPipeline, std::vector<u32> screenids, int filterMode);
void UpdateVertexBuffer(std::array<ScreenRectVertex, 4> vertices, VertexBufferPointer vbp);
void Draw(VertexBufferPointer vbp, PresentUniformData pushconstant);
void RenderToWindow(PresentWindow& window, const Layout::FramebufferLayout& layout, void RenderToWindow(PresentWindow& window, const Layout::FramebufferLayout& layout,
bool flipped); bool flipped);
@ -110,7 +154,7 @@ private:
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w, void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
float h, Layout::DisplayOrientation orientation); float h, Layout::DisplayOrientation orientation);
void ApplySecondLayerOpacity(float alpha); void ApplySecondLayerOpacity();
void DrawCursor(const Layout::FramebufferLayout& layout); void DrawCursor(const Layout::FramebufferLayout& layout);
@ -118,6 +162,23 @@ private:
bool right_eye); bool right_eye);
void FillScreen(Common::Vec3<u8> color, const TextureInfo& texture); void FillScreen(Common::Vec3<u8> color, const TextureInfo& texture);
void AllocateTexture(TextureInfo& texture, int width, int height, vk::Format colorFormat);
void AllocateStagedTexture(StagedTextureInfo& texture, int width, int height, vk::Format colorFormat);
void CreateTextureFramebuffer(TextureInfo& texture, vk::Framebuffer& framebuffer);
// Create Renderpass used for Textures
void CreateTextureRenderPass();
// Allocate Post Processing Textures
void AllocateSMAATextures();
void UploadImageDataToBuffer(StagedTextureInfo& texture, unsigned char* imageData);
void CreateImageStagingBuffer(StagedTextureInfo& texture);
void UploadBufferToImage(StagedTextureInfo& texture);
void AllocatePPTextures();
void AllocateOutputSizeTextures();
// Create Framebuffers that are attached to the Post Processing Textures
void CreatePPTextureFramebuffers();
void CreateOutputSizeTextureFramebuffers();
void SetStagedTextureTexInfo(StagedTextureInfo& texture);
private: private:
Memory::MemorySystem& memory; Memory::MemorySystem& memory;
Pica::PicaCore& pica; Pica::PicaCore& pica;
@ -138,19 +199,78 @@ private:
DescriptorHeap present_heap; DescriptorHeap present_heap;
vk::UniquePipelineLayout present_pipeline_layout; vk::UniquePipelineLayout present_pipeline_layout;
std::array<vk::Pipeline, PRESENT_PIPELINES> present_pipelines; std::array<vk::Pipeline, PRESENT_PIPELINES> present_pipelines;
// Post Processing Pipelines for use with RGBA16F Textures. Contains: Simple Present, FXAA, SMAA Pass 0, SMAA Pass 1, SMAA Pass 2, FSR Pass 0, FSR Pass 1
std::array<vk::Pipeline, POST_PIPELINES_TEXTURE> post_pipelines_texture;
// Post Processing Pipelines for presenting to screen. Contains: Area Sampling and Sharp Bilinear
std::array<vk::Pipeline, POST_PIPELINES_SCREEN> post_pipelines_screen;
std::array<vk::ShaderModule, PRESENT_PIPELINES> present_shaders; std::array<vk::ShaderModule, PRESENT_PIPELINES> present_shaders;
// Post Processing Shaders for use with RGBA16F Textures. Contains: Simple Present, FXAA, SMAA Pass 0, SMAA Pass 1, SMAA Pass 2, FSR Pass 0, FSR Pass 1
std::array<vk::ShaderModule, POST_PIPELINES_TEXTURE> post_vert_shaders_texture;
std::array<vk::ShaderModule, POST_PIPELINES_TEXTURE> post_frag_shaders_texture;
// Post Processing Shaders for presenting to screen. Contains: Area Sampling and Sharp Bilinear
std::array<vk::ShaderModule, POST_PIPELINES_SCREEN> post_vert_shaders_screen;
std::array<vk::ShaderModule, POST_PIPELINES_SCREEN> post_frag_shaders_screen;
// Linear and Nearest Sampler Respectively
std::array<vk::Sampler, 2> present_samplers; std::array<vk::Sampler, 2> present_samplers;
StagedTextureInfo areaTexInfo;
StagedTextureInfo searchTexInfo;
vk::ShaderModule present_vertex_shader; vk::ShaderModule present_vertex_shader;
u32 current_pipeline = 0; vk::ShaderModule simplepresent_vertex_shader;
vk::ShaderModule simplepresent_frag_shader;
vk::ShaderModule area_sampling_vertex_shader;
vk::ShaderModule area_sampling_frag_shader;
vk::ShaderModule fxaa_vertex_shader;
vk::ShaderModule fxaa_frag_shader;
vk::ShaderModule smaa_pass_0_vertex_shader;
vk::ShaderModule smaa_pass_0_frag_shader;
vk::ShaderModule smaa_pass_1_vertex_shader;
vk::ShaderModule smaa_pass_1_frag_shader;
vk::ShaderModule smaa_pass_2_vertex_shader;
vk::ShaderModule smaa_pass_2_frag_shader;
// Renderpass for RGBA16F Textures
vk::RenderPass textureRenderpass;
// Array of textures. 0 is top screen, 1 is bottom screen.
std::array<std::array<TextureInfo, 7>, 2> intermediateTextures;
std::array<TextureInfo, 2> antialiasTextures;
// Array of framebuffer objects. 0 is top screen, 1 is bottom screen.
std::array<std::array<vk::Framebuffer, 7>, 2> intermediateTextureFBOs;
std::array<vk::Framebuffer, 2 > antialiasTextureFBOs;
std::array<std::array<std::array<TextureInfo, 3>, 3>, 2> intermediateOutputSizeTextures;
std::array<std::array<std::array<vk::Framebuffer, 3>, 3>, 2> intermediateOutputSizeTextureFBOs;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> prevOutputScreenRects;
std::array<std::array<Common::Rectangle<u32>, 3>, 2> currOutputScreenRects;
int currOutputScreen;
float currTopTextureWidth;
float currTopTextureHeight;
float currBottomTextureWidth;
float currBottomTextureHeight;
float prevTopTextureWidth;
float prevTopTextureHeight;
float prevBottomTextureWidth;
float prevBottomTextureHeight;
int currentPass = 0;
u32 current_pipeline = 0;
Frame* currentFrame;
Layout::FramebufferLayout currentFramebufferLayout;
bool clearingColorAttachment = true;
bool applyingOpacity = true;
bool drawingPrimaryScreen = false;
bool usingTopOpacity = false;
std::array<ScreenInfo, 3> screen_infos{}; std::array<ScreenInfo, 3> screen_infos{};
PresentUniformData draw_info{}; PresentUniformData draw_info{};
vk::ClearColorValue clear_color{}; vk::ClearColorValue clear_color{};
vk::ShaderModule cursor_vertex_shader{}; vk::ShaderModule cursor_vertex_shader{};
vk::ShaderModule cursor_fragment_shader{}; vk::ShaderModule cursor_fragment_shader{};
vk::Pipeline cursor_pipeline{}; vk::Pipeline cursor_pipeline{};
vk::UniquePipelineLayout cursor_pipeline_layout{}; vk::UniquePipelineLayout cursor_pipeline_layout{};
// Secondary Layout Fix
bool isSecondaryWindow;
bool secondaryWindowEnabled;
bool usingAndroid;
}; };
} // namespace Vulkan } // namespace Vulkan

View file

@ -318,7 +318,6 @@ vk::UniqueInstance CreateInstance(const Common::DynamicLibrary& library,
.engineVersion = VK_MAKE_VERSION(1, 0, 0), .engineVersion = VK_MAKE_VERSION(1, 0, 0),
.apiVersion = TargetVulkanApiVersion, .apiVersion = TargetVulkanApiVersion,
}; };
boost::container::static_vector<const char*, 2> layers; boost::container::static_vector<const char*, 2> layers;
if (enable_validation) { if (enable_validation) {
layers.push_back("VK_LAYER_KHRONOS_validation"); layers.push_back("VK_LAYER_KHRONOS_validation");

View file

@ -104,7 +104,7 @@ PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& i
surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface}, surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface},
swapchain{instance, emu_window.GetFramebufferLayout().width, swapchain{instance, emu_window.GetFramebufferLayout().width,
emu_window.GetFramebufferLayout().height, surface, low_refresh_rate_}, emu_window.GetFramebufferLayout().height, surface, low_refresh_rate_},
graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()}, graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()}, present_load_renderpass{CreateLoadRenderpass()},
vsync_enabled{Settings::values.use_vsync.GetValue()}, vsync_enabled{Settings::values.use_vsync.GetValue()},
blit_supported{ blit_supported{
CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)}, CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)},
@ -157,6 +157,7 @@ PresentWindow::~PresentWindow() {
const vk::Device device = instance.GetDevice(); const vk::Device device = instance.GetDevice();
device.destroyCommandPool(command_pool); device.destroyCommandPool(command_pool);
device.destroyRenderPass(present_renderpass); device.destroyRenderPass(present_renderpass);
device.destroyRenderPass(present_load_renderpass);
for (auto& frame : swap_chain) { for (auto& frame : swap_chain) {
device.destroyImageView(frame.image_view); device.destroyImageView(frame.image_view);
device.destroyFramebuffer(frame.framebuffer); device.destroyFramebuffer(frame.framebuffer);
@ -489,7 +490,7 @@ void PresentWindow::CopyToSwapchain(Frame* frame) {
vk::RenderPass PresentWindow::CreateRenderpass() { vk::RenderPass PresentWindow::CreateRenderpass() {
const vk::AttachmentReference color_ref = { const vk::AttachmentReference color_ref = {
.attachment = 0, .attachment = 0,
.layout = vk::ImageLayout::eGeneral, .layout = vk::ImageLayout::eColorAttachmentOptimal,
}; };
const vk::SubpassDescription subpass = { const vk::SubpassDescription subpass = {
@ -512,16 +513,72 @@ vk::RenderPass PresentWindow::CreateRenderpass() {
.finalLayout = vk::ImageLayout::eTransferSrcOptimal, .finalLayout = vk::ImageLayout::eTransferSrcOptimal,
}; };
const vk::SubpassDependency dependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput,
.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader,
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
};
const vk::RenderPassCreateInfo renderpass_info = { const vk::RenderPassCreateInfo renderpass_info = {
.attachmentCount = 1, .attachmentCount = 1,
.pAttachments = &color_attachment, .pAttachments = &color_attachment,
.subpassCount = 1, .subpassCount = 1,
.pSubpasses = &subpass, .pSubpasses = &subpass,
.dependencyCount = 0, .dependencyCount = 1,
.pDependencies = nullptr, .pDependencies = &dependency,
}; };
return instance.GetDevice().createRenderPass(renderpass_info); return instance.GetDevice().createRenderPass(renderpass_info);
} }
vk::RenderPass PresentWindow::CreateLoadRenderpass() {
const vk::AttachmentReference color_ref = {
.attachment = 0,
.layout = vk::ImageLayout::eColorAttachmentOptimal,
};
const vk::SubpassDescription subpass = {
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = 1u,
.pColorAttachments = &color_ref,
.pResolveAttachments = 0,
.pDepthStencilAttachment = nullptr,
};
const vk::AttachmentDescription color_attachment = {
.format = swapchain.GetSurfaceFormat().format,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eLoad,
.storeOp = vk::AttachmentStoreOp::eStore,
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
.initialLayout = vk::ImageLayout::eTransferSrcOptimal,
.finalLayout = vk::ImageLayout::eTransferSrcOptimal,
};
const vk::SubpassDependency dependency = {
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput,
.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader,
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
};
const vk::RenderPassCreateInfo renderpass_info = {
.attachmentCount = 1,
.pAttachments = &color_attachment,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
return instance.GetDevice().createRenderPass(renderpass_info);
}
} // namespace Vulkan } // namespace Vulkan

View file

@ -58,6 +58,10 @@ public:
[[nodiscard]] vk::RenderPass Renderpass() const noexcept { [[nodiscard]] vk::RenderPass Renderpass() const noexcept {
return present_renderpass; return present_renderpass;
} }
// Returns Renderpass that doesn't clear the image
[[nodiscard]] vk::RenderPass LoadRenderpass() const noexcept {
return present_load_renderpass;
}
u32 ImageCount() const noexcept { u32 ImageCount() const noexcept {
return swapchain.GetImageCount(); return swapchain.GetImageCount();
@ -69,7 +73,7 @@ private:
void CopyToSwapchain(Frame* frame); void CopyToSwapchain(Frame* frame);
vk::RenderPass CreateRenderpass(); vk::RenderPass CreateRenderpass();
vk::RenderPass CreateLoadRenderpass();
private: private:
Frontend::EmuWindow& emu_window; Frontend::EmuWindow& emu_window;
const Instance& instance; const Instance& instance;
@ -81,6 +85,7 @@ private:
vk::CommandPool command_pool; vk::CommandPool command_pool;
vk::Queue graphics_queue; vk::Queue graphics_queue;
vk::RenderPass present_renderpass; vk::RenderPass present_renderpass;
vk::RenderPass present_load_renderpass;
std::vector<Frame> swap_chain; std::vector<Frame> swap_chain;
std::queue<Frame*> free_queue; std::queue<Frame*> free_queue;
std::queue<Frame*> present_queue; std::queue<Frame*> present_queue;

View file

@ -6,15 +6,17 @@
// High precision may or may not be supported in GLES3. If it isn't, use medium precision instead. // High precision may or may not be supported in GLES3. If it isn't, use medium precision instead.
static constexpr char fragment_shader_precision_OES[] = R"( static constexpr char fragment_shader_precision_OES[] = R"(
#if GL_ES #ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH #ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp int; precision highp int;
precision highp float; precision highp float;
precision highp sampler2D;
precision highp samplerBuffer; precision highp samplerBuffer;
precision highp uimage2D; precision highp uimage2D;
#else #else
precision mediump int; precision mediump int;
precision mediump float; precision mediump float;
precision highp sampler2D;
precision mediump samplerBuffer; precision mediump samplerBuffer;
precision mediump uimage2D; precision mediump uimage2D;
#endif // GL_FRAGMENT_PRECISION_HIGH #endif // GL_FRAGMENT_PRECISION_HIGH