Jelajahi Sumber

Merge pull request #4946 from ameerj/alpha-test

renderer_vulkan: Implement alpha testing
Rodrigo Locatti 5 tahun lalu
induk
melakukan
b834c21894

+ 8 - 1
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp

@@ -8,6 +8,7 @@
 
 #include <boost/functional/hash.hpp>
 
+#include "common/bit_cast.h"
 #include "common/cityhash.h"
 #include "common/common_types.h"
 #include "video_core/renderer_vulkan/fixed_pipeline_state.h"
@@ -60,7 +61,13 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
     rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
     topology.Assign(regs.draw.topology);
 
-    std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
+    alpha_raw = 0;
+    const auto test_func =
+        regs.alpha_test_enabled == 1 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
+    alpha_test_func.Assign(PackComparisonOp(test_func));
+    alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
+
+    point_size = Common::BitCast<u32>(regs.point_size);
 
     for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
         binding_divisors[index] =

+ 7 - 0
src/video_core/renderer_vulkan/fixed_pipeline_state.h

@@ -187,6 +187,13 @@ struct FixedPipelineState {
         BitField<23, 1, u32> rasterize_enable;
         BitField<24, 4, Maxwell::PrimitiveTopology> topology;
     };
+
+    u32 alpha_test_ref; ///< Alpha test reference value
+    union {
+        u32 alpha_raw;
+        BitField<0, 3, u32> alpha_test_func;
+    };
+
     u32 point_size;
     std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
     std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;

+ 6 - 0
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "common/bit_cast.h"
 #include "common/microprofile.h"
 #include "core/core.h"
 #include "core/memory.h"
@@ -344,6 +345,11 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
     }
     specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
 
+    // Alpha test
+    specialization.alpha_test_func =
+        FixedPipelineState::UnpackComparisonOp(fixed_state.alpha_test_func.Value());
+    specialization.alpha_test_ref = Common::BitCast<float>(fixed_state.alpha_test_ref);
+
     SPIRVProgram program;
     std::vector<VkDescriptorSetLayoutBinding> bindings;
 

+ 42 - 2
src/video_core/renderer_vulkan/vk_shader_decompiler.cpp

@@ -2075,6 +2075,45 @@ private:
         return {};
     }
 
+    Id MaxwellToSpirvComparison(Maxwell::ComparisonOp compare_op, Id operand_1, Id operand_2) {
+        using Compare = Maxwell::ComparisonOp;
+        switch (compare_op) {
+        case Compare::NeverOld:
+            return v_false; // Never let the test pass
+        case Compare::LessOld:
+            return OpFOrdLessThan(t_bool, operand_1, operand_2);
+        case Compare::EqualOld:
+            return OpFOrdEqual(t_bool, operand_1, operand_2);
+        case Compare::LessEqualOld:
+            return OpFOrdLessThanEqual(t_bool, operand_1, operand_2);
+        case Compare::GreaterOld:
+            return OpFOrdGreaterThan(t_bool, operand_1, operand_2);
+        case Compare::NotEqualOld:
+            return OpFOrdNotEqual(t_bool, operand_1, operand_2);
+        case Compare::GreaterEqualOld:
+            return OpFOrdGreaterThanEqual(t_bool, operand_1, operand_2);
+        default:
+            UNREACHABLE();
+        }
+    }
+
+    void AlphaTest(Id pointer) {
+        if (specialization.alpha_test_func == Maxwell::ComparisonOp::AlwaysOld) {
+            return;
+        }
+        const Id true_label = OpLabel();
+        const Id discard_label = OpLabel();
+        const Id alpha_reference = Constant(t_float, specialization.alpha_test_ref);
+        const Id alpha_value = OpLoad(t_float, pointer);
+        const Id condition =
+            MaxwellToSpirvComparison(specialization.alpha_test_func, alpha_value, alpha_reference);
+
+        OpBranchConditional(condition, true_label, discard_label);
+        AddLabel(discard_label);
+        OpKill();
+        AddLabel(true_label);
+    }
+
     void PreExit() {
         if (stage == ShaderType::Vertex && specialization.ndc_minus_one_to_one) {
             const u32 position_index = out_indices.position.value();
@@ -2097,8 +2136,6 @@ private:
             UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0,
                                  "Sample mask write is unimplemented");
 
-            // TODO(Rodrigo): Alpha testing
-
             // Write the color outputs using the data in the shader registers, disabled
             // rendertargets/components are skipped in the register assignment.
             u32 current_reg = 0;
@@ -2110,6 +2147,9 @@ private:
                     }
                     const Id pointer = AccessElement(t_out_float, frag_colors[rt], component);
                     OpStore(pointer, SafeGetRegister(current_reg));
+                    if (rt == 0 && component == 3) {
+                        AlphaTest(pointer);
+                    }
                     ++current_reg;
                 }
             }

+ 2 - 0
src/video_core/renderer_vulkan/vk_shader_decompiler.h

@@ -95,6 +95,8 @@ struct Specialization final {
     std::bitset<Maxwell::NumVertexAttributes> enabled_attributes;
     std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
     bool ndc_minus_one_to_one{};
+    float alpha_test_ref{};
+    Maxwell::ComparisonOp alpha_test_func{};
 };
 // Old gcc versions don't consider this trivially copyable.
 // static_assert(std::is_trivially_copyable_v<Specialization>);