forked from eden-emu/eden
		
	Implement scaled vertex buffer format emulation
These formats are unsupported by mobile GPUs so they need to be emulated in shaders instead.
This commit is contained in:
		
							parent
							
								
									58d420937c
								
							
						
					
					
						commit
						2beb3051c1
					
				
					 9 changed files with 97 additions and 51 deletions
				
			
		|  | @ -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)}; |         const auto& generic{ctx.input_generics.at(index)}; | ||||||
|         if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) { |         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{ | ||||||
|         const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; |             AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(element))}; | ||||||
|         const Id value{ctx.OpLoad(type->id, pointer)}; |         const Id value{ctx.OpLoad(generic.component_type, pointer)}; | ||||||
|         return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; |         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: | ||||||
|  |  | ||||||
|  | @ -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) { | InputGenericInfo GetAttributeInfo(EmitContext& ctx, AttributeType type, Id id) { | ||||||
|     const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; |  | ||||||
|     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))}; |             const auto& generic{input_generics.at(index)}; | ||||||
|             if (!type) { |             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{ | ||||||
|             const Id pointer{is_array |                 is_array ? OpAccessChain(generic.pointer_type, generic_id, vertex, masked_index) | ||||||
|                                  ? OpAccessChain(type->pointer, generic_id, vertex, masked_index) |                          : OpAccessChain(generic.pointer_type, generic_id, masked_index)}; | ||||||
|                                  : OpAccessChain(type->pointer, generic_id, masked_index)}; |             const Id value{OpLoad(generic.component_type, pointer)}; | ||||||
|             const Id value{OpLoad(type->id, pointer)}; |             const Id result{[this, generic, value]() { | ||||||
|             const Id result{type->needs_cast ? OpBitcast(F32[1], value) : 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); | ||||||
|  |  | ||||||
|  | @ -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{}; | ||||||
|  |  | ||||||
|  | @ -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{}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,8 @@ enum class AttributeType : u8 { | ||||||
|     Float, |     Float, | ||||||
|     SignedInt, |     SignedInt, | ||||||
|     UnsignedInt, |     UnsignedInt, | ||||||
|  |     SignedScaled, | ||||||
|  |     UnsignedScaled, | ||||||
|     Disabled, |     Disabled, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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: | ||||||
|  |  | ||||||
|  | @ -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(), | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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.
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Billy Laws
						Billy Laws