diff --git a/src/common/settings.h b/src/common/settings.h
index 9da447ce0..84dab5217 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -61,10 +61,11 @@ enum class ResolutionSetup : u32 {
 };
 
 enum class ScalingFilter : u32 {
-    Bilinear = 0,
-    Bicubic = 1,
-    ScaleForce = 2,
-    Fsr = 3,
+    NearestNeighbor = 0,
+    Bilinear = 1,
+    Bicubic = 2,
+    ScaleForce = 3,
+    Fsr = 4,
 };
 
 struct ResolutionScalingInfo {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 955dbc744..68423601c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -264,6 +264,10 @@ void RendererOpenGL::InitOpenGLObjects() {
     glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
+    present_sampler_nn.Create();
+    glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
     // Generate VBO handle for drawing
     vertex_buffer.Create();
 
@@ -346,6 +350,9 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     GLuint fragment_handle;
     const auto filter = Settings::values.scaling_filter.GetValue();
     switch (filter) {
+    case Settings::ScalingFilter::NearestNeighbor:
+        fragment_handle = present_bilinear_fragment.handle;
+        break;
     case Settings::ScalingFilter::Bilinear:
         fragment_handle = present_bilinear_fragment.handle;
         break;
@@ -355,6 +362,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     case Settings::ScalingFilter::ScaleForce:
         fragment_handle = present_scaleforce_fragment.handle;
         break;
+    case Settings::ScalingFilter::Fsr:
+        LOG_WARNING(
+            Render_OpenGL,
+            "FidelityFX FSR Super Sampling is not supported in OpenGL, changing to ScaleForce");
+        fragment_handle = present_scaleforce_fragment.handle;
+        break;
     default:
         fragment_handle = present_bilinear_fragment.handle;
         break;
@@ -464,7 +477,11 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     }
 
     glBindTextureUnit(0, screen_info.display_texture);
-    glBindSampler(0, present_sampler.handle);
+    if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
+        glBindSampler(0, present_sampler.handle);
+    } else {
+        glBindSampler(0, present_sampler_nn.handle);
+    }
 
     glClear(GL_COLOR_BUFFER_BIT);
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index bf3d3502c..504ddbe7b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -109,6 +109,7 @@ private:
 
     // OpenGL object IDs
     OGLSampler present_sampler;
+    OGLSampler present_sampler_nn;
     OGLBuffer vertex_buffer;
     OGLProgram present_vertex;
     OGLProgram present_bilinear_fragment;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 8ce60e874..334eeb92e 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -152,7 +152,9 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
         use_accelerated ? screen_info.image_view : *raw_image_views[image_index];
 
     if (!fsr) {
-        UpdateDescriptorSet(image_index, source_image_view);
+        const bool is_nn =
+            Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
+        UpdateDescriptorSet(image_index, source_image_view, is_nn);
     }
 
     BufferData data;
@@ -247,7 +249,7 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
         crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
         VkImageView fsr_image_view =
             fsr->Draw(scheduler, image_index, source_image_view, crop_rect);
-        UpdateDescriptorSet(image_index, fsr_image_view);
+        UpdateDescriptorSet(image_index, fsr_image_view, true);
     }
 
     scheduler.Record(
@@ -286,6 +288,9 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
             const auto filter = Settings::values.scaling_filter.GetValue();
             cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
             switch (filter) {
+            case Settings::ScalingFilter::NearestNeighbor:
+                cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
+                break;
             case Settings::ScalingFilter::Bilinear:
                 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
                 break;
@@ -745,13 +750,33 @@ void VKBlitScreen::CreateGraphicsPipeline() {
 }
 
 void VKBlitScreen::CreateSampler() {
-    bool linear = Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr;
     const VkSamplerCreateInfo ci{
         .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
         .pNext = nullptr,
         .flags = 0,
-        .magFilter = linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST,
-        .minFilter = linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST,
+        .magFilter = VK_FILTER_LINEAR,
+        .minFilter = VK_FILTER_LINEAR,
+        .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
+        .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+        .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+        .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+        .mipLodBias = 0.0f,
+        .anisotropyEnable = VK_FALSE,
+        .maxAnisotropy = 0.0f,
+        .compareEnable = VK_FALSE,
+        .compareOp = VK_COMPARE_OP_NEVER,
+        .minLod = 0.0f,
+        .maxLod = 0.0f,
+        .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
+        .unnormalizedCoordinates = VK_FALSE,
+    };
+
+    const VkSamplerCreateInfo ci_nn{
+        .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+        .pNext = nullptr,
+        .flags = 0,
+        .magFilter = VK_FILTER_NEAREST,
+        .minFilter = VK_FILTER_NEAREST,
         .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
         .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
         .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
@@ -768,6 +793,7 @@ void VKBlitScreen::CreateSampler() {
     };
 
     sampler = device.GetLogical().CreateSampler(ci);
+    nn_sampler = device.GetLogical().CreateSampler(ci_nn);
 }
 
 void VKBlitScreen::CreateFramebuffers() {
@@ -862,7 +888,8 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
     }
 }
 
-void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const {
+void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view,
+                                       bool nn) const {
     const VkDescriptorBufferInfo buffer_info{
         .buffer = *buffer,
         .offset = offsetof(BufferData, uniform),
@@ -883,7 +910,7 @@ void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView imag
     };
 
     const VkDescriptorImageInfo image_info{
-        .sampler = *sampler,
+        .sampler = nn ? *nn_sampler : *sampler,
         .imageView = image_view,
         .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
     };
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 337931468..448a2fbe6 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -90,7 +90,7 @@ private:
     void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
     void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
 
-    void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
+    void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const;
     void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
     void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
                        const Layout::FramebufferLayout layout) const;
@@ -115,12 +115,14 @@ private:
     vk::DescriptorPool descriptor_pool;
     vk::DescriptorSetLayout descriptor_set_layout;
     vk::PipelineLayout pipeline_layout;
+    vk::Pipeline nearest_neightbor_pipeline;
     vk::Pipeline bilinear_pipeline;
     vk::Pipeline bicubic_pipeline;
     vk::Pipeline scaleforce_pipeline;
     vk::RenderPass renderpass;
     std::vector<vk::Framebuffer> framebuffers;
     vk::DescriptorSets descriptor_sets;
+    vk::Sampler nn_sampler;
     vk::Sampler sampler;
 
     vk::Buffer buffer;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 014ca6683..fe2f6bb7f 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -387,6 +387,11 @@
            </item>
            <item>
             <widget class="QComboBox" name="scaling_filter_combobox">
+             <item>
+              <property name="text">
+               <string>Nearest Neighbor</string>
+              </property>
+             </item>
              <item>
               <property name="text">
                <string>Bilinear</string>
@@ -404,7 +409,7 @@
              </item>
              <item>
               <property name="text">
-               <string>FidelityFX Super Resolution</string>
+               <string>FidelityFX Super Resolution [Vulkan Only]</string>
               </property>
              </item>
             </widget>