mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-02-01 15:23:32 +00:00
Merge pull request 'feat(renderer): add CRT shader filter with configurable effects' (#82) from feature/renderer/crt-shader into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/82
This commit is contained in:
@@ -163,8 +163,12 @@
|
||||
<item>@string/scaling_filter_bicubic</item>
|
||||
<item>@string/scaling_filter_gaussian</item>
|
||||
<item>@string/scaling_filter_scale_force</item>
|
||||
<item>@string/scaling_filter_scale_fx</item>
|
||||
<item>@string/scaling_filter_lanczos</item>
|
||||
<item>@string/scaling_filter_fsr</item>
|
||||
<item>@string/scaling_filter_fsr2</item>
|
||||
<item>@string/scaling_filter_crt_easymode</item>
|
||||
<item>@string/scaling_filter_crt_royale</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="rendererScalingFilterValues">
|
||||
@@ -175,6 +179,10 @@
|
||||
<item>4</item>
|
||||
<item>5</item>
|
||||
<item>6</item>
|
||||
<item>7</item>
|
||||
<item>8</item>
|
||||
<item>9</item>
|
||||
<item>10</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="fsr2QualityModeNames">
|
||||
|
||||
@@ -654,8 +654,12 @@
|
||||
<string name="scaling_filter_bicubic">Bicubic</string>
|
||||
<string name="scaling_filter_gaussian">Gaussian</string>
|
||||
<string name="scaling_filter_scale_force">ScaleForce</string>
|
||||
<string name="scaling_filter_scale_fx">ScaleFX</string>
|
||||
<string name="scaling_filter_lanczos">Lanczos</string>
|
||||
<string name="scaling_filter_fsr">AMD FidelityFX™ Super Resolution</string>
|
||||
<string name="scaling_filter_fsr2">AMD FidelityFX™ Super Resolution 2.0</string>
|
||||
<string name="scaling_filter_crt_easymode">CRT EasyMode</string>
|
||||
<string name="scaling_filter_crt_royale">CRT Royale</string>
|
||||
|
||||
<!-- Anti-Aliasing -->
|
||||
<string name="anti_aliasing_none">None</string>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "citron/configuration/shared_translation.h"
|
||||
@@ -130,6 +130,20 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
INSERT(Settings, lanczos_quality, tr("Lanczos Quality:"), tr("The quality of the Lanczos filter. Higher is sharper but more expensive."));
|
||||
INSERT(Settings, fsr2_quality_mode, tr("FSR 2.0 Quality Mode:"),
|
||||
tr("Selects the quality mode for FSR 2.0 upscaling. Quality provides better image quality, Performance provides better performance."));
|
||||
INSERT(Settings, crt_scanline_strength, tr("CRT Scanline Strength:"),
|
||||
tr("Controls the intensity of scanlines. Higher values create more pronounced horizontal lines."));
|
||||
INSERT(Settings, crt_curvature, tr("CRT Curvature:"),
|
||||
tr("Applies barrel distortion to simulate the curved screen of a CRT monitor."));
|
||||
INSERT(Settings, crt_gamma, tr("CRT Gamma:"),
|
||||
tr("Adjusts the gamma correction curve. Higher values brighten the image, lower values darken it."));
|
||||
INSERT(Settings, crt_bloom, tr("CRT Bloom:"),
|
||||
tr("Controls the glow effect around bright areas, simulating phosphor persistence."));
|
||||
INSERT(Settings, crt_mask_type, tr("CRT Mask Type:"),
|
||||
tr("Selects the phosphor mask pattern: None, Aperture Grille (vertical stripes), or Shadow Mask (triangular pattern)."));
|
||||
INSERT(Settings, crt_brightness, tr("CRT Brightness:"),
|
||||
tr("Adjusts overall brightness of the CRT effect. Use to compensate for darkening from other effects."));
|
||||
INSERT(Settings, crt_alpha, tr("CRT Alpha:"),
|
||||
tr("Controls transparency of the CRT effect. Lower values make the effect more transparent."));
|
||||
|
||||
INSERT(Settings, frame_skipping, tr("Frame Skipping:"),
|
||||
tr("Skips frames to maintain performance when the system cannot keep up with the target frame rate."));
|
||||
@@ -426,6 +440,8 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
PAIR(ScalingFilter, ScaleFx, tr("ScaleFX")),
|
||||
PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")),
|
||||
PAIR(ScalingFilter, Fsr2, tr("AMD FidelityFX™️ Super Resolution 2.0")),
|
||||
PAIR(ScalingFilter, CRTEasyMode, tr("CRT EasyMode")),
|
||||
PAIR(ScalingFilter, CRTRoyale, tr("CRT Royale")),
|
||||
}});
|
||||
translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -46,6 +46,10 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleFX"))},
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
{Settings::ScalingFilter::Fsr2, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR 2.0"))},
|
||||
{Settings::ScalingFilter::CRTEasyMode,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "CRT EasyMode"))},
|
||||
{Settings::ScalingFilter::CRTRoyale,
|
||||
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "CRT Royale"))},
|
||||
};
|
||||
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -350,6 +350,71 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
// CRT Shader Settings (only active when CRT filter is selected)
|
||||
SwitchableSetting<float, true> crt_scanline_strength{linkage,
|
||||
1.0f, // 100/100 = 1.0 (range 0-200, actual 0.0-2.0)
|
||||
0.0f,
|
||||
2.0f,
|
||||
"crt_scanline_strength",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_curvature{linkage,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_curvature",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_gamma{linkage,
|
||||
1.0f, // 100 maps to 1.0 (range 1-300, actual 1.0-3.0)
|
||||
1.0f,
|
||||
3.0f,
|
||||
"crt_gamma",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_bloom{linkage,
|
||||
0.33f, // 33/100 = 0.33 (range 0-100, actual 0.0-1.0)
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_bloom",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<int, true> crt_mask_type{linkage,
|
||||
1, // Already correct
|
||||
0,
|
||||
2,
|
||||
"crt_mask_type",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true}; // 0=none, 1=aperture, 2=shadow
|
||||
SwitchableSetting<float, true> crt_brightness{linkage,
|
||||
1.0f, // Default brightness (1.0 = no change)
|
||||
0.0f,
|
||||
2.0f,
|
||||
"crt_brightness",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
SwitchableSetting<float, true> crt_alpha{linkage,
|
||||
1.0f, // Default alpha (1.0 = fully opaque)
|
||||
0.0f,
|
||||
1.0f,
|
||||
"crt_alpha",
|
||||
Category::Renderer,
|
||||
Specialization::Scalar,
|
||||
true,
|
||||
true};
|
||||
|
||||
|
||||
SwitchableSetting<int, true> lanczos_quality{linkage,
|
||||
3, // Default value
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -673,7 +673,9 @@ enum class ScalingFilter : u32 {
|
||||
Lanczos = 6,
|
||||
Fsr = 7,
|
||||
Fsr2 = 8,
|
||||
MaxEnum = 9,
|
||||
CRTEasyMode = 9,
|
||||
CRTRoyale = 10,
|
||||
MaxEnum = 11,
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -689,6 +691,8 @@ EnumMetadata<ScalingFilter>::Canonicalizations() {
|
||||
{"Lanczos", ScalingFilter::Lanczos},
|
||||
{"Fsr", ScalingFilter::Fsr},
|
||||
{"Fsr2", ScalingFilter::Fsr2},
|
||||
{"CRTEasyMode", ScalingFilter::CRTEasyMode},
|
||||
{"CRTRoyale", ScalingFilter::CRTRoyale},
|
||||
{"MaxEnum", ScalingFilter::MaxEnum},
|
||||
};
|
||||
}
|
||||
@@ -876,6 +880,7 @@ inline u32 EnumMetadata<ExtendedDynamicState>::Index() {
|
||||
return 26;
|
||||
}
|
||||
|
||||
|
||||
template <typename Type>
|
||||
inline std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
# SPDX-FileCopyrightText: 2026 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr)
|
||||
@@ -74,6 +74,7 @@ set(SHADER_FILES
|
||||
vulkan_present_scaleforce_fp32.frag
|
||||
vulkan_present_scalefx_fp16.frag
|
||||
vulkan_present_scalefx_fp32.frag
|
||||
vulkan_crt_easymode.frag
|
||||
vulkan_quad_indexed.comp
|
||||
vulkan_taa.frag
|
||||
vulkan_taa.vert
|
||||
|
||||
133
src/video_core/host_shaders/vulkan_crt_easymode.frag
Normal file
133
src/video_core/host_shaders/vulkan_crt_easymode.frag
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
//
|
||||
// CRT EasyMode shader - Single-pass CRT effects
|
||||
// Based on Libretro's crt-easymode shader
|
||||
// https://github.com/libretro/common-shaders/blob/master/crt/shaders/crt-easymode.cg
|
||||
|
||||
#version 460 core
|
||||
|
||||
layout(location = 0) in vec2 frag_tex_coord;
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
layout(push_constant) uniform CRTPushConstants {
|
||||
layout(offset = 132) float scanline_strength;
|
||||
layout(offset = 136) float curvature;
|
||||
layout(offset = 140) float gamma;
|
||||
layout(offset = 144) float bloom;
|
||||
layout(offset = 148) int mask_type;
|
||||
layout(offset = 152) float brightness;
|
||||
layout(offset = 156) float alpha;
|
||||
layout(offset = 160) float screen_width;
|
||||
layout(offset = 164) float screen_height;
|
||||
} crt_params;
|
||||
|
||||
const float PI = 3.141592653589793;
|
||||
|
||||
// Apply barrel distortion (curvature)
|
||||
vec2 applyCurvature(vec2 coord) {
|
||||
if (crt_params.curvature <= 0.0) {
|
||||
return coord;
|
||||
}
|
||||
|
||||
vec2 centered = coord - 0.5;
|
||||
float dist = length(centered);
|
||||
float distortion = 1.0 + crt_params.curvature * dist * dist;
|
||||
vec2 curved = centered * distortion + 0.5;
|
||||
|
||||
// Clamp to valid texture coordinates
|
||||
return clamp(curved, vec2(0.0), vec2(1.0));
|
||||
}
|
||||
|
||||
// Generate scanlines
|
||||
float scanline(float y) {
|
||||
if (crt_params.scanline_strength <= 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
float scanline_pos = y * crt_params.screen_height;
|
||||
float scanline_factor = abs(sin(scanline_pos * PI));
|
||||
|
||||
// Make scanlines more subtle
|
||||
return 1.0 - crt_params.scanline_strength * scanline_factor * 0.5;
|
||||
}
|
||||
|
||||
// Apply phosphor mask (aperture grille or shadow mask)
|
||||
vec3 applyMask(vec2 coord) {
|
||||
if (crt_params.mask_type == 0) {
|
||||
return vec3(1.0); // No mask
|
||||
}
|
||||
|
||||
vec2 screen_pos = coord * vec2(crt_params.screen_width, crt_params.screen_height);
|
||||
|
||||
if (crt_params.mask_type == 1) {
|
||||
// Aperture grille (vertical RGB stripes)
|
||||
float mask = sin(screen_pos.x * PI * 3.0) * 0.5 + 0.5;
|
||||
return vec3(
|
||||
1.0 - mask * 0.2,
|
||||
1.0 - mask * 0.15,
|
||||
1.0 - mask * 0.2
|
||||
);
|
||||
} else if (crt_params.mask_type == 2) {
|
||||
// Shadow mask (triangular pattern)
|
||||
float x = screen_pos.x * 3.0;
|
||||
float y = screen_pos.y * 3.0;
|
||||
float mask = sin(x * PI) * sin(y * PI) * 0.5 + 0.5;
|
||||
return vec3(1.0 - mask * 0.15);
|
||||
}
|
||||
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
// Simple bloom effect (multi-tap blur approximation)
|
||||
vec3 applyBloom(vec2 coord, vec3 original) {
|
||||
if (crt_params.bloom <= 0.0) {
|
||||
return original;
|
||||
}
|
||||
|
||||
vec2 texel_size = 1.0 / vec2(crt_params.screen_width, crt_params.screen_height);
|
||||
vec3 bloom_color = original;
|
||||
|
||||
// Simple 5-tap horizontal blur
|
||||
for (int i = -2; i <= 2; i++) {
|
||||
vec2 offset = vec2(float(i) * texel_size.x, 0.0);
|
||||
vec3 sample_color = texture(color_texture, clamp(coord + offset, vec2(0.0), vec2(1.0))).rgb;
|
||||
bloom_color += sample_color;
|
||||
}
|
||||
|
||||
bloom_color /= 6.0; // Average of 5 taps + original
|
||||
|
||||
// Mix original with bloom
|
||||
return mix(original, bloom_color, crt_params.bloom * 0.3);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Apply curvature distortion first
|
||||
vec2 curved_coord = applyCurvature(frag_tex_coord);
|
||||
|
||||
// Sample the texture
|
||||
vec3 rgb = texture(color_texture, curved_coord).rgb;
|
||||
|
||||
// Apply bloom
|
||||
rgb = applyBloom(curved_coord, rgb);
|
||||
|
||||
// Apply phosphor mask
|
||||
rgb *= applyMask(curved_coord);
|
||||
|
||||
// Apply scanlines
|
||||
float scan = scanline(curved_coord.y);
|
||||
rgb *= scan;
|
||||
|
||||
// Gamma correction
|
||||
if (crt_params.gamma > 0.0 && crt_params.gamma != 1.0) {
|
||||
rgb = pow(clamp(rgb, vec3(0.0), vec3(1.0)), vec3(1.0 / crt_params.gamma));
|
||||
}
|
||||
|
||||
// Apply brightness adjustment
|
||||
rgb *= crt_params.brightness;
|
||||
|
||||
// Clamp to valid range and apply alpha
|
||||
color = vec4(clamp(rgb, vec3(0.0), vec3(1.0)), crt_params.alpha);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scalefx_fp16_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_present_scalefx_fp32_frag_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_crt_easymode_frag_spv.h"
|
||||
#include "video_core/renderer_vulkan/present/filters.h"
|
||||
#include "video_core/renderer_vulkan/present/util.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
@@ -75,4 +76,9 @@ std::unique_ptr<WindowAdaptPass> MakeLanczos(const Device& device, VkFormat fram
|
||||
BuildShader(device, PRESENT_LANCZOS_FRAG_SPV));
|
||||
}
|
||||
|
||||
std::unique_ptr<WindowAdaptPass> MakeCRT(const Device& device, VkFormat frame_format) {
|
||||
return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
|
||||
BuildShader(device, VULKAN_CRT_EASYMODE_FRAG_SPV));
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -17,5 +17,6 @@ std::unique_ptr<WindowAdaptPass> MakeLanczos(const Device& device, VkFormat fram
|
||||
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeScaleFx(const Device& device, VkFormat frame_format);
|
||||
std::unique_ptr<WindowAdaptPass> MakeCRT(const Device& device, VkFormat frame_format);
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@@ -92,18 +92,51 @@ void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, s
|
||||
BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
|
||||
cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
|
||||
|
||||
const auto current_scaling_filter = Settings::values.scaling_filter.GetValue();
|
||||
const bool is_crt_enabled = current_scaling_filter == Settings::ScalingFilter::CRTEasyMode ||
|
||||
current_scaling_filter == Settings::ScalingFilter::CRTRoyale;
|
||||
|
||||
for (size_t i = 0; i < layer_count; i++) {
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]);
|
||||
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
|
||||
sizeof(PresentPushConstants), &push_constants[i]);
|
||||
|
||||
|
||||
if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Lanczos) {
|
||||
// Push Lanczos quality if using Lanczos filter
|
||||
if (current_scaling_filter == Settings::ScalingFilter::Lanczos && !is_crt_enabled) {
|
||||
const s32 lanczos_a = Settings::values.lanczos_quality.GetValue();
|
||||
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
sizeof(PresentPushConstants), sizeof(s32), &lanczos_a);
|
||||
}
|
||||
|
||||
// Push CRT parameters if CRT filter is enabled
|
||||
if (is_crt_enabled) {
|
||||
struct CRTPushConstants {
|
||||
float scanline_strength;
|
||||
float curvature;
|
||||
float gamma;
|
||||
float bloom;
|
||||
int mask_type;
|
||||
float brightness;
|
||||
float alpha;
|
||||
float screen_width;
|
||||
float screen_height;
|
||||
} crt_constants;
|
||||
|
||||
crt_constants.scanline_strength = Settings::values.crt_scanline_strength.GetValue();
|
||||
crt_constants.curvature = Settings::values.crt_curvature.GetValue();
|
||||
crt_constants.gamma = Settings::values.crt_gamma.GetValue();
|
||||
crt_constants.bloom = Settings::values.crt_bloom.GetValue();
|
||||
crt_constants.mask_type = Settings::values.crt_mask_type.GetValue();
|
||||
crt_constants.brightness = Settings::values.crt_brightness.GetValue();
|
||||
crt_constants.alpha = Settings::values.crt_alpha.GetValue();
|
||||
crt_constants.screen_width = static_cast<float>(render_area.width);
|
||||
crt_constants.screen_height = static_cast<float>(render_area.height);
|
||||
|
||||
cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
sizeof(PresentPushConstants) + sizeof(s32),
|
||||
sizeof(CRTPushConstants), &crt_constants);
|
||||
}
|
||||
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
|
||||
descriptor_sets[i], {});
|
||||
cmdbuf.Draw(4, 1, 0, 0);
|
||||
@@ -127,8 +160,11 @@ void WindowAdaptPass::CreateDescriptorSetLayout() {
|
||||
}
|
||||
|
||||
void WindowAdaptPass::CreatePipelineLayout() {
|
||||
|
||||
std::array<VkPushConstantRange, 2> ranges{};
|
||||
// Support up to 3 push constant ranges:
|
||||
// 0: PresentPushConstants (vertex shader)
|
||||
// 1: Lanczos quality (fragment shader) - optional
|
||||
// 2: CRT parameters (fragment shader) - optional
|
||||
std::array<VkPushConstantRange, 3> ranges{};
|
||||
|
||||
// Range 0: The existing constants for the Vertex Shader
|
||||
ranges[0] = {
|
||||
@@ -137,13 +173,33 @@ void WindowAdaptPass::CreatePipelineLayout() {
|
||||
.size = sizeof(PresentPushConstants),
|
||||
};
|
||||
|
||||
// Range 1: Our new constant for the Fragment Shader
|
||||
// Range 1: Lanczos quality for the Fragment Shader
|
||||
ranges[1] = {
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = sizeof(PresentPushConstants),
|
||||
.size = sizeof(s32),
|
||||
};
|
||||
|
||||
// Range 2: CRT parameters for the Fragment Shader
|
||||
// Offset after PresentPushConstants + Lanczos (if used)
|
||||
// CRT constants: 8 floats + 1 int = 36 bytes
|
||||
struct CRTPushConstants {
|
||||
float scanline_strength;
|
||||
float curvature;
|
||||
float gamma;
|
||||
float bloom;
|
||||
int mask_type;
|
||||
float brightness;
|
||||
float alpha;
|
||||
float screen_width;
|
||||
float screen_height;
|
||||
};
|
||||
ranges[2] = {
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = sizeof(PresentPushConstants) + sizeof(s32),
|
||||
.size = sizeof(CRTPushConstants),
|
||||
};
|
||||
|
||||
pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
#include "video_core/present.h"
|
||||
#include "video_core/renderer_vulkan/present/filters.h"
|
||||
@@ -50,6 +51,10 @@ void BlitScreen::SetWindowAdaptPass() {
|
||||
case Settings::ScalingFilter::ScaleFx:
|
||||
window_adapt = MakeScaleFx(device, swapchain_view_format);
|
||||
break;
|
||||
case Settings::ScalingFilter::CRTEasyMode:
|
||||
case Settings::ScalingFilter::CRTRoyale:
|
||||
window_adapt = MakeCRT(device, swapchain_view_format);
|
||||
break;
|
||||
case Settings::ScalingFilter::Fsr:
|
||||
case Settings::ScalingFilter::Fsr2:
|
||||
case Settings::ScalingFilter::Bilinear:
|
||||
|
||||
Reference in New Issue
Block a user