diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 9bcb046b3..e00d54ea5 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -163,8 +163,12 @@
- @string/scaling_filter_bicubic
- @string/scaling_filter_gaussian
- @string/scaling_filter_scale_force
+ - @string/scaling_filter_scale_fx
+ - @string/scaling_filter_lanczos
- @string/scaling_filter_fsr
- @string/scaling_filter_fsr2
+ - @string/scaling_filter_crt_easymode
+ - @string/scaling_filter_crt_royale
@@ -175,6 +179,10 @@
- 4
- 5
- 6
+ - 7
+ - 8
+ - 9
+ - 10
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 461436a3d..58dfbc2a1 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -654,8 +654,12 @@
Bicubic
Gaussian
ScaleForce
+ ScaleFX
+ Lanczos
AMD FidelityFX™ Super Resolution
AMD FidelityFX™ Super Resolution 2.0
+ CRT EasyMode
+ CRT Royale
None
diff --git a/src/citron/configuration/shared_translation.cpp b/src/citron/configuration/shared_translation.cpp
index 9d8e48701..46fa6cfcf 100644
--- a/src/citron/configuration/shared_translation.cpp
+++ b/src/citron/configuration/shared_translation.cpp
@@ -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 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 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::Index(),
{
diff --git a/src/citron/configuration/shared_translation.h b/src/citron/configuration/shared_translation.h
index fad796d8d..71ec60f88 100644
--- a/src/citron/configuration/shared_translation.h
+++ b/src/citron/configuration/shared_translation.h
@@ -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 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 use_docked_mode_texts_map = {
diff --git a/src/common/settings.h b/src/common/settings.h
index e6c0f5500..7dd29b402 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -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 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 crt_curvature{linkage,
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ "crt_curvature",
+ Category::Renderer,
+ Specialization::Scalar,
+ true,
+ true};
+ SwitchableSetting 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 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 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 crt_brightness{linkage,
+ 1.0f, // Default brightness (1.0 = no change)
+ 0.0f,
+ 2.0f,
+ "crt_brightness",
+ Category::Renderer,
+ Specialization::Scalar,
+ true,
+ true};
+ SwitchableSetting crt_alpha{linkage,
+ 1.0f, // Default alpha (1.0 = fully opaque)
+ 0.0f,
+ 1.0f,
+ "crt_alpha",
+ Category::Renderer,
+ Specialization::Scalar,
+ true,
+ true};
+
SwitchableSetting lanczos_quality{linkage,
3, // Default value
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 2fcf4b79b..1b654bfb0 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -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::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::Index() {
return 26;
}
+
template
inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata::Canonicalizations();
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index a14010350..a24cc5ecc 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -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
diff --git a/src/video_core/host_shaders/vulkan_crt_easymode.frag b/src/video_core/host_shaders/vulkan_crt_easymode.frag
new file mode 100644
index 000000000..bae9dd3aa
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_crt_easymode.frag
@@ -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);
+}
diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp
index e9dc2be22..673b1ea8a 100644
--- a/src/video_core/renderer_vulkan/present/filters.cpp
+++ b/src/video_core/renderer_vulkan/present/filters.cpp
@@ -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 MakeLanczos(const Device& device, VkFormat fram
BuildShader(device, PRESENT_LANCZOS_FRAG_SPV));
}
+std::unique_ptr MakeCRT(const Device& device, VkFormat frame_format) {
+ return std::make_unique(device, frame_format, CreateBilinearSampler(device),
+ BuildShader(device, VULKAN_CRT_EASYMODE_FRAG_SPV));
+}
+
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h
index a69abf209..d7d047cb1 100644
--- a/src/video_core/renderer_vulkan/present/filters.h
+++ b/src/video_core/renderer_vulkan/present/filters.h
@@ -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 MakeLanczos(const Device& device, VkFormat fram
std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format);
std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format);
std::unique_ptr MakeScaleFx(const Device& device, VkFormat frame_format);
+std::unique_ptr MakeCRT(const Device& device, VkFormat frame_format);
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
index 46f477c82..2f576b997 100644
--- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
@@ -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(render_area.width);
+ crt_constants.screen_height = static_cast(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 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 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,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 07d329872..d59da892c 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -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: