diff --git a/README.md b/README.md
index 423fefac7..652d924a4 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 yuzu emulator early access
 =============
 
-This is the source code for early-access 2211.
+This is the source code for early-access 2212.
 
 ## Legal Notice
 
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 05e5c94f3..c89a5d693 100755
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -83,6 +83,7 @@ enum class DepthFormat : u32 {
     S8_UINT_Z24_UNORM = 0x14,
     D24X8_UNORM = 0x15,
     D24S8_UNORM = 0x16,
+    S8_UINT = 0x17,
     D24C8_UNORM = 0x18,
     D32_FLOAT_S8X24_UINT = 0x19,
 };
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 06d4c12da..3dbdcc2c4 100755
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -148,6 +148,8 @@ GLenum AttachmentType(PixelFormat format) {
     switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) {
     case SurfaceType::Depth:
         return GL_DEPTH_ATTACHMENT;
+    case SurfaceType::Stencil:
+        return GL_STENCIL_ATTACHMENT;
     case SurfaceType::DepthStencil:
         return GL_DEPTH_STENCIL_ATTACHMENT;
     default:
@@ -395,6 +397,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
     UNREACHABLE_MSG("Invalid image format={}", format);
     return GL_R32UI;
 }
+
+[[nodiscard]] u32 NextPow2(u32 value) {
+    return 1U << (32U - std::countl_zero(value - 1U));
+}
 } // Anonymous namespace
 
 ImageBufferMap::~ImageBufferMap() {
@@ -910,6 +916,8 @@ void Image::Scale(bool up_scale) {
             return GL_COLOR_ATTACHMENT0;
         case SurfaceType::Depth:
             return GL_DEPTH_ATTACHMENT;
+        case SurfaceType::Stencil:
+            return GL_STENCIL_ATTACHMENT;
         case SurfaceType::DepthStencil:
             return GL_DEPTH_STENCIL_ATTACHMENT;
         default:
@@ -923,8 +931,10 @@ void Image::Scale(bool up_scale) {
             return GL_COLOR_BUFFER_BIT;
         case SurfaceType::Depth:
             return GL_DEPTH_BUFFER_BIT;
+        case SurfaceType::Stencil:
+            return GL_STENCIL_BUFFER_BIT;
         case SurfaceType::DepthStencil:
-            return GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+            return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
         default:
             UNREACHABLE();
             return GL_COLOR_BUFFER_BIT;
@@ -936,8 +946,10 @@ void Image::Scale(bool up_scale) {
             return 0;
         case SurfaceType::Depth:
             return 1;
-        case SurfaceType::DepthStencil:
+        case SurfaceType::Stencil:
             return 2;
+        case SurfaceType::DepthStencil:
+            return 3;
         default:
             UNREACHABLE();
             return 0;
@@ -1267,10 +1279,20 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
     }
 
     if (const ImageView* const image_view = depth_buffer; image_view) {
-        if (GetFormatType(image_view->format) == SurfaceType::DepthStencil) {
-            buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
-        } else {
+        switch (GetFormatType(image_view->format)) {
+        case SurfaceType::Depth:
             buffer_bits |= GL_DEPTH_BUFFER_BIT;
+            break;
+        case SurfaceType::Stencil:
+            buffer_bits |= GL_STENCIL_BUFFER_BIT;
+            break;
+        case SurfaceType::DepthStencil:
+            buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
+            break;
+        default:
+            UNREACHABLE();
+            buffer_bits |= GL_DEPTH_BUFFER_BIT;
+            break;
         }
         const GLenum attachment = AttachmentType(image_view->format);
         AttachTexture(handle, attachment, image_view);
@@ -1311,7 +1333,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
         const u32 copy_size = region.width * region.height * region.depth * img_bpp;
         if (pbo_size < copy_size) {
             intermediate_pbo.Create();
-            pbo_size = copy_size;
+            pbo_size = NextPow2(copy_size);
             glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
         }
         // Copy from source to PBO
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 54622e9b1..c0534b1f1 100755
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -164,8 +164,8 @@ private:
 
     std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{};
 
-    std::array<OGLFramebuffer, 3> rescale_draw_fbos;
-    std::array<OGLFramebuffer, 3> rescale_read_fbos;
+    std::array<OGLFramebuffer, 4> rescale_draw_fbos;
+    std::array<OGLFramebuffer, 4> rescale_read_fbos;
     const Settings::ResolutionScalingInfo& resolution;
 };
 
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 39158aa3e..daba42ed9 100755
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -108,6 +108,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
     {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV},                // E5B9G9R9_FLOAT
     {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT},            // D32_FLOAT
     {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT},    // D16_UNORM
+    {GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE},                // S8_UINT
     {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},    // D24_UNORM_S8_UINT
     {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},    // S8_UINT_D24_UNORM
     {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL,
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 68a23b602..31adada56 100755
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -208,6 +208,9 @@ struct FormatTuple {
     {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT
     {VK_FORMAT_D16_UNORM, Attachable},  // D16_UNORM
 
+    // Stencil formats
+    {VK_FORMAT_S8_UINT, Attachable}, // S8_UINT
+
     // DepthStencil formats
     {VK_FORMAT_D24_UNORM_S8_UINT, Attachable},  // D24_UNORM_S8_UINT
     {VK_FORMAT_D24_UNORM_S8_UINT, Attachable},  // S8_UINT_D24_UNORM (emulated)
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 407fd2a15..9bc846b94 100755
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -102,6 +102,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
             usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
             break;
         case VideoCore::Surface::SurfaceType::Depth:
+        case VideoCore::Surface::SurfaceType::Stencil:
         case VideoCore::Surface::SurfaceType::DepthStencil:
             usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
             break;
@@ -173,6 +174,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
         return VK_IMAGE_ASPECT_COLOR_BIT;
     case VideoCore::Surface::SurfaceType::Depth:
         return VK_IMAGE_ASPECT_DEPTH_BIT;
+    case VideoCore::Surface::SurfaceType::Stencil:
+        return VK_IMAGE_ASPECT_STENCIL_BIT;
     case VideoCore::Surface::SurfaceType::DepthStencil:
         return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
     default:
@@ -195,6 +198,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
     case PixelFormat::D16_UNORM:
     case PixelFormat::D32_FLOAT:
         return VK_IMAGE_ASPECT_DEPTH_BIT;
+    case PixelFormat::S8_UINT:
+        return VK_IMAGE_ASPECT_STENCIL_BIT;
     default:
         return VK_IMAGE_ASPECT_COLOR_BIT;
     }
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 58d262446..a36015c8c 100755
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -82,6 +82,8 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
         return PixelFormat::D32_FLOAT;
     case Tegra::DepthFormat::D16_UNORM:
         return PixelFormat::D16_UNORM;
+    case Tegra::DepthFormat::S8_UINT:
+        return PixelFormat::S8_UINT;
     case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT:
         return PixelFormat::D32_FLOAT_S8_UINT;
     default:
@@ -213,6 +215,11 @@ SurfaceType GetFormatType(PixelFormat pixel_format) {
         return SurfaceType::Depth;
     }
 
+    if (static_cast<std::size_t>(pixel_format) <
+        static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) {
+        return SurfaceType::Stencil;
+    }
+
     if (static_cast<std::size_t>(pixel_format) <
         static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
         return SurfaceType::DepthStencil;
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 2ce7c7d33..33e8d24ab 100755
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -110,8 +110,12 @@ enum class PixelFormat {
 
     MaxDepthFormat,
 
+    // Stencil formats
+    S8_UINT = MaxDepthFormat,
+    MaxStencilFormat,
+
     // DepthStencil formats
-    D24_UNORM_S8_UINT = MaxDepthFormat,
+    D24_UNORM_S8_UINT = MaxStencilFormat,
     S8_UINT_D24_UNORM,
     D32_FLOAT_S8_UINT,
 
@@ -125,8 +129,9 @@ constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max
 enum class SurfaceType {
     ColorTexture = 0,
     Depth = 1,
-    DepthStencil = 2,
-    Invalid = 3,
+    Stencil = 2,
+    DepthStencil = 3,
+    Invalid = 4,
 };
 
 enum class SurfaceTarget {
@@ -229,6 +234,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
     1,  // E5B9G9R9_FLOAT
     1,  // D32_FLOAT
     1,  // D16_UNORM
+    1,  // S8_UINT
     1,  // D24_UNORM_S8_UINT
     1,  // S8_UINT_D24_UNORM
     1,  // D32_FLOAT_S8_UINT
@@ -328,6 +334,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
     1,  // E5B9G9R9_FLOAT
     1,  // D32_FLOAT
     1,  // D16_UNORM
+    1,  // S8_UINT
     1,  // D24_UNORM_S8_UINT
     1,  // S8_UINT_D24_UNORM
     1,  // D32_FLOAT_S8_UINT
@@ -427,6 +434,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
     32,  // E5B9G9R9_FLOAT
     32,  // D32_FLOAT
     16,  // D16_UNORM
+    8,   // S8_UINT
     32,  // D24_UNORM_S8_UINT
     32,  // S8_UINT_D24_UNORM
     64,  // D32_FLOAT_S8_UINT
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index c6cf0583f..b2c81057b 100755
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -194,6 +194,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
                 return "D32_FLOAT";
             case PixelFormat::D16_UNORM:
                 return "D16_UNORM";
+            case PixelFormat::S8_UINT:
+                return "S8_UINT";
             case PixelFormat::D24_UNORM_S8_UINT:
                 return "D24_UNORM_S8_UINT";
             case PixelFormat::S8_UINT_D24_UNORM:
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 95106f88f..d782af09c 100755
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -21,6 +21,13 @@
 namespace Vulkan {
 namespace {
 namespace Alternatives {
+constexpr std::array S8_UINT{
+    VK_FORMAT_D16_UNORM_S8_UINT,
+    VK_FORMAT_D24_UNORM_S8_UINT,
+    VK_FORMAT_D32_SFLOAT_S8_UINT,
+    VK_FORMAT_UNDEFINED,
+};
+
 constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{
     VK_FORMAT_D32_SFLOAT_S8_UINT,
     VK_FORMAT_D16_UNORM_S8_UINT,
@@ -74,6 +81,8 @@ void SetNext(void**& next, T& data) {
 
 constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
     switch (format) {
+    case VK_FORMAT_S8_UINT:
+        return Alternatives::S8_UINT.data();
     case VK_FORMAT_D24_UNORM_S8_UINT:
         return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
     case VK_FORMAT_D16_UNORM_S8_UINT:
@@ -145,6 +154,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
         VK_FORMAT_R4G4B4A4_UNORM_PACK16,
         VK_FORMAT_D32_SFLOAT,
         VK_FORMAT_D16_UNORM,
+        VK_FORMAT_S8_UINT,
         VK_FORMAT_D16_UNORM_S8_UINT,
         VK_FORMAT_D24_UNORM_S8_UINT,
         VK_FORMAT_D32_SFLOAT_S8_UINT,