Przeglądaj źródła

Implement scaled vertex buffer format emulation

These formats are unsupported by mobile GPUs so they need to be emulated in shaders instead.
Billy Laws 3 lat temu
rodzic
commit
158a1896ec

+ 17 - 27
src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp

@@ -10,27 +10,6 @@
 
 
 namespace Shader::Backend::SPIRV {
 namespace Shader::Backend::SPIRV {
 namespace {
 namespace {
-struct AttrInfo {
-    Id pointer;
-    Id id;
-    bool needs_cast;
-};
-
-std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
-    const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
-    switch (type) {
-    case AttributeType::Float:
-        return AttrInfo{ctx.input_f32, ctx.F32[1], false};
-    case AttributeType::UnsignedInt:
-        return AttrInfo{ctx.input_u32, ctx.U32[1], true};
-    case AttributeType::SignedInt:
-        return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
-    case AttributeType::Disabled:
-        return std::nullopt;
-    }
-    throw InvalidArgument("Invalid attribute type {}", type);
-}
-
 template <typename... Args>
 template <typename... Args>
 Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
 Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
     switch (ctx.stage) {
     switch (ctx.stage) {
@@ -302,15 +281,26 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
     const u32 element{static_cast<u32>(attr) % 4};
     const u32 element{static_cast<u32>(attr) % 4};
     if (IR::IsGeneric(attr)) {
     if (IR::IsGeneric(attr)) {
         const u32 index{IR::GenericAttributeIndex(attr)};
         const u32 index{IR::GenericAttributeIndex(attr)};
-        const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
-        if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
+        const auto& generic{ctx.input_generics.at(index)};
+        if (!ValidId(generic.id)) {
             // Attribute is disabled or varying component is not written
             // Attribute is disabled or varying component is not written
             return ctx.Const(element == 3 ? 1.0f : 0.0f);
             return ctx.Const(element == 3 ? 1.0f : 0.0f);
         }
         }
-        const Id generic_id{ctx.input_generics.at(index)};
-        const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
-        const Id value{ctx.OpLoad(type->id, pointer)};
-        return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
+        const Id pointer{
+            AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(element))};
+        const Id value{ctx.OpLoad(generic.component_type, pointer)};
+        return [&ctx, generic, value]() {
+            switch (generic.load_op) {
+            case InputGenericLoadOp::Bitcast:
+                return ctx.OpBitcast(ctx.F32[1], value);
+            case InputGenericLoadOp::SToF:
+                return ctx.OpConvertSToF(ctx.F32[1], value);
+            case InputGenericLoadOp::UToF:
+                return ctx.OpConvertUToF(ctx.F32[1], value);
+            default:
+                return value;
+            };
+        }();
     }
     }
     switch (attr) {
     switch (attr) {
     case IR::Attribute::PrimitiveId:
     case IR::Attribute::PrimitiveId:

+ 40 - 21
src/shader_recompiler/backend/spirv/spirv_emit_context.cpp

@@ -25,12 +25,6 @@ enum class Operation {
     FPMax,
     FPMax,
 };
 };
 
 
-struct AttrInfo {
-    Id pointer;
-    Id id;
-    bool needs_cast;
-};
-
 Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
 Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
     const spv::ImageFormat format{spv::ImageFormat::Unknown};
     const spv::ImageFormat format{spv::ImageFormat::Unknown};
     const Id type{ctx.F32[1]};
     const Id type{ctx.F32[1]};
@@ -206,23 +200,37 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) {
         return ctx.TypeVector(ctx.TypeInt(32, true), 4);
         return ctx.TypeVector(ctx.TypeInt(32, true), 4);
     case AttributeType::UnsignedInt:
     case AttributeType::UnsignedInt:
         return ctx.U32[4];
         return ctx.U32[4];
+    case AttributeType::SignedScaled:
+        return ctx.profile.support_scaled_attributes ? ctx.F32[4]
+                                                     : ctx.TypeVector(ctx.TypeInt(32, true), 4);
+    case AttributeType::UnsignedScaled:
+        return ctx.profile.support_scaled_attributes ? ctx.F32[4] : ctx.U32[4];
     case AttributeType::Disabled:
     case AttributeType::Disabled:
         break;
         break;
     }
     }
     throw InvalidArgument("Invalid attribute type {}", type);
     throw InvalidArgument("Invalid attribute type {}", type);
 }
 }
 
 
-std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
-    const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
+InputGenericInfo GetAttributeInfo(EmitContext& ctx, AttributeType type, Id id) {
     switch (type) {
     switch (type) {
     case AttributeType::Float:
     case AttributeType::Float:
-        return AttrInfo{ctx.input_f32, ctx.F32[1], false};
+        return InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None};
     case AttributeType::UnsignedInt:
     case AttributeType::UnsignedInt:
-        return AttrInfo{ctx.input_u32, ctx.U32[1], true};
+        return InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::Bitcast};
     case AttributeType::SignedInt:
     case AttributeType::SignedInt:
-        return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
+        return InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
+                                InputGenericLoadOp::Bitcast};
+    case AttributeType::SignedScaled:
+        return ctx.profile.support_scaled_attributes
+                   ? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
+                   : InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
+                                      InputGenericLoadOp::SToF};
+    case AttributeType::UnsignedScaled:
+        return ctx.profile.support_scaled_attributes
+                   ? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
+                   : InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::UToF};
     case AttributeType::Disabled:
     case AttributeType::Disabled:
-        return std::nullopt;
+        return InputGenericInfo{};
     }
     }
     throw InvalidArgument("Invalid attribute type {}", type);
     throw InvalidArgument("Invalid attribute type {}", type);
 }
 }
@@ -746,18 +754,29 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
                 continue;
                 continue;
             }
             }
             AddLabel(labels[label_index]);
             AddLabel(labels[label_index]);
-            const auto type{AttrTypes(*this, static_cast<u32>(index))};
-            if (!type) {
+            const auto& generic{input_generics.at(index)};
+            const Id generic_id{generic.id};
+            if (!ValidId(generic_id)) {
                 OpReturnValue(Const(0.0f));
                 OpReturnValue(Const(0.0f));
                 ++label_index;
                 ++label_index;
                 continue;
                 continue;
             }
             }
-            const Id generic_id{input_generics.at(index)};
-            const Id pointer{is_array
-                                 ? OpAccessChain(type->pointer, generic_id, vertex, masked_index)
-                                 : OpAccessChain(type->pointer, generic_id, masked_index)};
-            const Id value{OpLoad(type->id, pointer)};
-            const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value};
+            const Id pointer{
+                is_array ? OpAccessChain(generic.pointer_type, generic_id, vertex, masked_index)
+                         : OpAccessChain(generic.pointer_type, generic_id, masked_index)};
+            const Id value{OpLoad(generic.component_type, pointer)};
+            const Id result{[this, generic, value]() {
+                switch (generic.load_op) {
+                case InputGenericLoadOp::Bitcast:
+                    return OpBitcast(F32[1], value);
+                case InputGenericLoadOp::SToF:
+                    return OpConvertSToF(F32[1], value);
+                case InputGenericLoadOp::UToF:
+                    return OpConvertUToF(F32[1], value);
+                default:
+                    return value;
+                };
+            }()};
             OpReturnValue(result);
             OpReturnValue(result);
             ++label_index;
             ++label_index;
         }
         }
@@ -1457,7 +1476,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
         const Id id{DefineInput(*this, type, true)};
         const Id id{DefineInput(*this, type, true)};
         Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
         Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
         Name(id, fmt::format("in_attr{}", index));
         Name(id, fmt::format("in_attr{}", index));
-        input_generics[index] = id;
+        input_generics[index] = GetAttributeInfo(*this, input_type, id);
 
 
         if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) {
         if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) {
             Decorate(id, spv::Decoration::PassthroughNV);
             Decorate(id, spv::Decoration::PassthroughNV);

+ 15 - 1
src/shader_recompiler/backend/spirv/spirv_emit_context.h

@@ -95,6 +95,20 @@ struct StorageDefinitions {
     Id U32x4{};
     Id U32x4{};
 };
 };
 
 
+enum class InputGenericLoadOp {
+    None,
+    Bitcast,
+    SToF,
+    UToF,
+};
+
+struct InputGenericInfo {
+    Id id;
+    Id pointer_type;
+    Id component_type;
+    InputGenericLoadOp load_op;
+};
+
 struct GenericElementInfo {
 struct GenericElementInfo {
     Id id{};
     Id id{};
     u32 first_element{};
     u32 first_element{};
@@ -283,7 +297,7 @@ public:
 
 
     bool need_input_position_indirect{};
     bool need_input_position_indirect{};
     Id input_position{};
     Id input_position{};
-    std::array<Id, 32> input_generics{};
+    std::array<InputGenericInfo, 32> input_generics{};
 
 
     Id output_point_size{};
     Id output_point_size{};
     Id output_position{};
     Id output_position{};

+ 1 - 0
src/shader_recompiler/profile.h

@@ -43,6 +43,7 @@ struct Profile {
     bool support_gl_variable_aoffi{};
     bool support_gl_variable_aoffi{};
     bool support_gl_sparse_textures{};
     bool support_gl_sparse_textures{};
     bool support_gl_derivative_control{};
     bool support_gl_derivative_control{};
+    bool support_scaled_attributes{};
 
 
     bool warp_size_potentially_larger_than_guest{};
     bool warp_size_potentially_larger_than_guest{};
 
 

+ 2 - 0
src/shader_recompiler/runtime_info.h

@@ -17,6 +17,8 @@ enum class AttributeType : u8 {
     Float,
     Float,
     SignedInt,
     SignedInt,
     UnsignedInt,
     UnsignedInt,
+    SignedScaled,
+    UnsignedScaled,
     Disabled,
     Disabled,
 };
 };
 
 

+ 8 - 0
src/video_core/renderer_vulkan/maxwell_to_vk.cpp

@@ -347,6 +347,14 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
 
 
 VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
 VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
                       Maxwell::VertexAttribute::Size size) {
                       Maxwell::VertexAttribute::Size size) {
+    if (device.MustEmulateScaledFormats()) {
+        if (type == Maxwell::VertexAttribute::Type::SScaled) {
+            type = Maxwell::VertexAttribute::Type::SInt;
+        } else if (type == Maxwell::VertexAttribute::Type::UScaled) {
+            type = Maxwell::VertexAttribute::Type::UInt;
+        }
+    }
+
     const VkFormat format{([&]() {
     const VkFormat format{([&]() {
         switch (type) {
         switch (type) {
         case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
         case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:

+ 5 - 2
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@@ -114,14 +114,16 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut
         return Shader::AttributeType::Disabled;
         return Shader::AttributeType::Disabled;
     case Maxwell::VertexAttribute::Type::SNorm:
     case Maxwell::VertexAttribute::Type::SNorm:
     case Maxwell::VertexAttribute::Type::UNorm:
     case Maxwell::VertexAttribute::Type::UNorm:
-    case Maxwell::VertexAttribute::Type::UScaled:
-    case Maxwell::VertexAttribute::Type::SScaled:
     case Maxwell::VertexAttribute::Type::Float:
     case Maxwell::VertexAttribute::Type::Float:
         return Shader::AttributeType::Float;
         return Shader::AttributeType::Float;
     case Maxwell::VertexAttribute::Type::SInt:
     case Maxwell::VertexAttribute::Type::SInt:
         return Shader::AttributeType::SignedInt;
         return Shader::AttributeType::SignedInt;
     case Maxwell::VertexAttribute::Type::UInt:
     case Maxwell::VertexAttribute::Type::UInt:
         return Shader::AttributeType::UnsignedInt;
         return Shader::AttributeType::UnsignedInt;
+    case Maxwell::VertexAttribute::Type::UScaled:
+        return Shader::AttributeType::UnsignedScaled;
+    case Maxwell::VertexAttribute::Type::SScaled:
+        return Shader::AttributeType::SignedScaled;
     }
     }
     return Shader::AttributeType::Float;
     return Shader::AttributeType::Float;
 }
 }
@@ -331,6 +333,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
         .support_derivative_control = true,
         .support_derivative_control = true,
         .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
         .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
         .support_native_ndc = device.IsExtDepthClipControlSupported(),
         .support_native_ndc = device.IsExtDepthClipControlSupported(),
+        .support_scaled_attributes = !device.MustEmulateScaledFormats(),
 
 
         .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
         .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
 
 

+ 4 - 0
src/video_core/vulkan_common/vulkan_device.cpp

@@ -363,6 +363,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
 
 
 #ifdef ANDROID
 #ifdef ANDROID
     if (is_adreno) {
     if (is_adreno) {
+        must_emulate_scaled_formats = true;
+
         LOG_WARNING(Render_Vulkan, "Adreno drivers have broken VK_EXT_extended_dynamic_state");
         LOG_WARNING(Render_Vulkan, "Adreno drivers have broken VK_EXT_extended_dynamic_state");
         extensions.extended_dynamic_state = false;
         extensions.extended_dynamic_state = false;
         loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
         loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
@@ -391,6 +393,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
     }
     }
 
 
     if (is_arm) {
     if (is_arm) {
+        must_emulate_scaled_formats = true;
+
         LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
         LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
         extensions.extended_dynamic_state = false;
         extensions.extended_dynamic_state = false;
         loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
         loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);

+ 5 - 0
src/video_core/vulkan_common/vulkan_device.h

@@ -551,6 +551,10 @@ public:
         return cant_blit_msaa;
         return cant_blit_msaa;
     }
     }
 
 
+    bool MustEmulateScaledFormats() const {
+        return must_emulate_scaled_formats;
+    }
+
     bool MustEmulateBGR565() const {
     bool MustEmulateBGR565() const {
         return must_emulate_bgr565;
         return must_emulate_bgr565;
     }
     }
@@ -666,6 +670,7 @@ private:
     bool has_nsight_graphics{};             ///< Has Nsight Graphics attached
     bool has_nsight_graphics{};             ///< Has Nsight Graphics attached
     bool supports_d24_depth{};              ///< Supports D24 depth buffers.
     bool supports_d24_depth{};              ///< Supports D24 depth buffers.
     bool cant_blit_msaa{};                  ///< Does not support MSAA<->MSAA blitting.
     bool cant_blit_msaa{};                  ///< Does not support MSAA<->MSAA blitting.
+    bool must_emulate_scaled_formats{};     ///< Requires scaled vertex format emulation
     bool must_emulate_bgr565{};             ///< Emulates BGR565 by swizzling RGB565 format.
     bool must_emulate_bgr565{};             ///< Emulates BGR565 by swizzling RGB565 format.
     bool dynamic_state3_blending{};         ///< Has all blending features of dynamic_state3.
     bool dynamic_state3_blending{};         ///< Has all blending features of dynamic_state3.
     bool dynamic_state3_enables{};          ///< Has all enables features of dynamic_state3.
     bool dynamic_state3_enables{};          ///< Has all enables features of dynamic_state3.