From 826a350e2b6aadb4f123189c28f065b0e7926264 Mon Sep 17 00:00:00 2001
From: FernandoS27 <fsahmkow27@gmail.com>
Date: Tue, 19 Oct 2021 19:41:57 +0200
Subject: [PATCH] Vulkan Rasterizer: Fix clears on integer textures.

---
 .../renderer_vulkan/vk_rasterizer.cpp         | 34 +++++++++++++-
 src/video_core/surface.cpp                    | 47 +++++++++++++++++++
 src/video_core/surface.h                      |  4 ++
 3 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index a9334e1018..ff75d14a10 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -211,6 +211,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
     EndTransformFeedback();
 }
 
+#pragma optimize("", off)
+
 void RasterizerVulkan::Clear() {
     MICROPROFILE_SCOPE(Vulkan_Clearing);
 
@@ -260,7 +262,37 @@ void RasterizerVulkan::Clear() {
     const u32 color_attachment = regs.clear_buffers.RT;
     if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
         VkClearValue clear_value;
-        std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
+        bool is_integer = false;
+        bool is_signed = false;
+        size_t int_size = 8;
+        for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++i) {
+            const auto& this_rt = regs.rt[i];
+            if (this_rt.Address() == 0) {
+                continue;
+            }
+            if (this_rt.format == Tegra::RenderTargetFormat::NONE) {
+                continue;
+            }
+            const auto format =
+                VideoCore::Surface::PixelFormatFromRenderTargetFormat(this_rt.format);
+            is_integer = IsPixelFormatInteger(format);
+            is_signed = IsPixelFormatSignedInteger(format);
+            int_size = PixelComponentSizeBitsInteger(format);
+            break;
+        }
+        if (!is_integer) {
+            std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
+        } else if (!is_signed) {
+            for (size_t i = 0; i < 4; i++) {
+                clear_value.color.uint32[i] =
+                    static_cast<u32>(static_cast<u64>(int_size << 1U) * regs.clear_color[i]);
+            }
+        } else {
+            for (size_t i = 0; i < 4; i++) {
+                clear_value.color.int32[i] = static_cast<s32>(
+                    (static_cast<s32>(int_size - 1) << 1) * (regs.clear_color[i] - 0.5f));
+            }
+        }
 
         scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
             const VkClearAttachment attachment{
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 64941a486f..58d262446f 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -306,6 +306,53 @@ bool IsPixelFormatInteger(PixelFormat format) {
     }
 }
 
+bool IsPixelFormatSignedInteger(PixelFormat format) {
+    switch (format) {
+    case PixelFormat::A8B8G8R8_SINT:
+    case PixelFormat::R8_SINT:
+    case PixelFormat::R16G16B16A16_SINT:
+    case PixelFormat::R32G32B32A32_SINT:
+    case PixelFormat::R32G32_SINT:
+    case PixelFormat::R16_SINT:
+    case PixelFormat::R16G16_SINT:
+    case PixelFormat::R8G8_SINT:
+    case PixelFormat::R32_SINT:
+        return true;
+    default:
+        return false;
+    }
+}
+
+size_t PixelComponentSizeBitsInteger(PixelFormat format) {
+    switch (format) {
+    case PixelFormat::A8B8G8R8_SINT:
+    case PixelFormat::A8B8G8R8_UINT:
+    case PixelFormat::R8_SINT:
+    case PixelFormat::R8_UINT:
+    case PixelFormat::R8G8_SINT:
+    case PixelFormat::R8G8_UINT:
+        return 8;
+    case PixelFormat::A2B10G10R10_UINT:
+        return 10;
+    case PixelFormat::R16G16B16A16_SINT:
+    case PixelFormat::R16G16B16A16_UINT:
+    case PixelFormat::R16_UINT:
+    case PixelFormat::R16_SINT:
+    case PixelFormat::R16G16_UINT:
+    case PixelFormat::R16G16_SINT:
+        return 16;
+    case PixelFormat::R32G32B32A32_UINT:
+    case PixelFormat::R32G32B32A32_SINT:
+    case PixelFormat::R32G32_SINT:
+    case PixelFormat::R32G32_UINT:
+    case PixelFormat::R32_UINT:
+    case PixelFormat::R32_SINT:
+        return 32;
+    default:
+        return 0;
+    }
+}
+
 std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
     return {DefaultBlockWidth(format), DefaultBlockHeight(format)};
 }
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 3bb24abb70..2ce7c7d338 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -462,6 +462,10 @@ bool IsPixelFormatSRGB(PixelFormat format);
 
 bool IsPixelFormatInteger(PixelFormat format);
 
+bool IsPixelFormatSignedInteger(PixelFormat format);
+
+size_t PixelComponentSizeBitsInteger(PixelFormat format);
+
 std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
 
 u64 EstimatedDecompressedSize(u64 base_size, PixelFormat format);