vulkan: Add VK_EXT_vertex_input_dynamic_state support
Reduces the number of total pipelines generated on Vulkan. Tested on Super Smash Bros. Ultimate.
This commit is contained in:
		@@ -50,7 +50,7 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
 | 
				
			|||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
					void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
				
			||||||
                                 bool has_extended_dynamic_state) {
 | 
					                                 bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
 | 
				
			||||||
    const Maxwell& regs = maxwell3d.regs;
 | 
					    const Maxwell& regs = maxwell3d.regs;
 | 
				
			||||||
    const std::array enabled_lut{
 | 
					    const std::array enabled_lut{
 | 
				
			||||||
        regs.polygon_offset_point_enable,
 | 
					        regs.polygon_offset_point_enable,
 | 
				
			||||||
@@ -60,7 +60,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
				
			|||||||
    const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
 | 
					    const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    raw1 = 0;
 | 
					    raw1 = 0;
 | 
				
			||||||
    no_extended_dynamic_state.Assign(has_extended_dynamic_state ? 0 : 1);
 | 
					    extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0);
 | 
				
			||||||
 | 
					    dynamic_vertex_input.Assign(has_dynamic_vertex_input ? 1 : 0);
 | 
				
			||||||
    xfb_enabled.Assign(regs.tfb_enabled != 0);
 | 
					    xfb_enabled.Assign(regs.tfb_enabled != 0);
 | 
				
			||||||
    primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
 | 
					    primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
 | 
				
			||||||
    depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
 | 
					    depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
 | 
				
			||||||
@@ -73,11 +74,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
				
			|||||||
    tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
 | 
					    tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
 | 
				
			||||||
    logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
 | 
					    logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
 | 
				
			||||||
    logic_op.Assign(PackLogicOp(regs.logic_op.operation));
 | 
					    logic_op.Assign(PackLogicOp(regs.logic_op.operation));
 | 
				
			||||||
    rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
 | 
					 | 
				
			||||||
    topology.Assign(regs.draw.topology);
 | 
					    topology.Assign(regs.draw.topology);
 | 
				
			||||||
    msaa_mode.Assign(regs.multisample_mode);
 | 
					    msaa_mode.Assign(regs.multisample_mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    raw2 = 0;
 | 
					    raw2 = 0;
 | 
				
			||||||
 | 
					    rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
 | 
				
			||||||
    const auto test_func =
 | 
					    const auto test_func =
 | 
				
			||||||
        regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
 | 
					        regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always;
 | 
				
			||||||
    alpha_test_func.Assign(PackComparisonOp(test_func));
 | 
					    alpha_test_func.Assign(PackComparisonOp(test_func));
 | 
				
			||||||
@@ -93,24 +94,44 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
				
			|||||||
    alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
 | 
					    alpha_test_ref = Common::BitCast<u32>(regs.alpha_test_ref);
 | 
				
			||||||
    point_size = Common::BitCast<u32>(regs.point_size);
 | 
					    point_size = Common::BitCast<u32>(regs.point_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (maxwell3d.dirty.flags[Dirty::InstanceDivisors]) {
 | 
					    if (maxwell3d.dirty.flags[Dirty::VertexInput]) {
 | 
				
			||||||
        maxwell3d.dirty.flags[Dirty::InstanceDivisors] = false;
 | 
					        if (has_dynamic_vertex_input) {
 | 
				
			||||||
        for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
 | 
					            // Dirty flag will be reset by the command buffer update
 | 
				
			||||||
            const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
 | 
					            static constexpr std::array LUT{
 | 
				
			||||||
            binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
 | 
					                0u, // Invalid
 | 
				
			||||||
        }
 | 
					                1u, // SignedNorm
 | 
				
			||||||
    }
 | 
					                1u, // UnsignedNorm
 | 
				
			||||||
    if (maxwell3d.dirty.flags[Dirty::VertexAttributes]) {
 | 
					                2u, // SignedInt
 | 
				
			||||||
        maxwell3d.dirty.flags[Dirty::VertexAttributes] = false;
 | 
					                3u, // UnsignedInt
 | 
				
			||||||
        for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
 | 
					                1u, // UnsignedScaled
 | 
				
			||||||
            const auto& input = regs.vertex_attrib_format[index];
 | 
					                1u, // SignedScaled
 | 
				
			||||||
            auto& attribute = attributes[index];
 | 
					                1u, // Float
 | 
				
			||||||
            attribute.raw = 0;
 | 
					            };
 | 
				
			||||||
            attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
 | 
					            const auto& attrs = regs.vertex_attrib_format;
 | 
				
			||||||
            attribute.buffer.Assign(input.buffer);
 | 
					            attribute_types = 0;
 | 
				
			||||||
            attribute.offset.Assign(input.offset);
 | 
					            for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
 | 
				
			||||||
            attribute.type.Assign(static_cast<u32>(input.type.Value()));
 | 
					                const u32 mask = attrs[i].constant != 0 ? 0 : 3;
 | 
				
			||||||
            attribute.size.Assign(static_cast<u32>(input.size.Value()));
 | 
					                const u32 type = LUT[static_cast<size_t>(attrs[i].type.Value())];
 | 
				
			||||||
 | 
					                attribute_types |= static_cast<u64>(type & mask) << (i * 2);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            maxwell3d.dirty.flags[Dirty::VertexInput] = false;
 | 
				
			||||||
 | 
					            enabled_divisors = 0;
 | 
				
			||||||
 | 
					            for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
 | 
				
			||||||
 | 
					                const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
 | 
				
			||||||
 | 
					                binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
 | 
				
			||||||
 | 
					                enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
 | 
				
			||||||
 | 
					                const auto& input = regs.vertex_attrib_format[index];
 | 
				
			||||||
 | 
					                auto& attribute = attributes[index];
 | 
				
			||||||
 | 
					                attribute.raw = 0;
 | 
				
			||||||
 | 
					                attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
 | 
				
			||||||
 | 
					                attribute.buffer.Assign(input.buffer);
 | 
				
			||||||
 | 
					                attribute.offset.Assign(input.offset);
 | 
				
			||||||
 | 
					                attribute.type.Assign(static_cast<u32>(input.type.Value()));
 | 
				
			||||||
 | 
					                attribute.size.Assign(static_cast<u32>(input.size.Value()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (maxwell3d.dirty.flags[Dirty::Blending]) {
 | 
					    if (maxwell3d.dirty.flags[Dirty::Blending]) {
 | 
				
			||||||
@@ -126,10 +147,10 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
 | 
				
			|||||||
            return static_cast<u16>(viewport.swizzle.raw);
 | 
					            return static_cast<u16>(viewport.swizzle.raw);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (no_extended_dynamic_state != 0) {
 | 
					    if (!extended_dynamic_state) {
 | 
				
			||||||
        dynamic_state.Refresh(regs);
 | 
					        dynamic_state.Refresh(regs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (xfb_enabled != 0) {
 | 
					    if (xfb_enabled) {
 | 
				
			||||||
        RefreshXfbState(xfb_state, regs);
 | 
					        RefreshXfbState(xfb_state, regs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,44 +168,51 @@ struct FixedPipelineState {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
        u32 raw1;
 | 
					        u32 raw1;
 | 
				
			||||||
        BitField<0, 1, u32> no_extended_dynamic_state;
 | 
					        BitField<0, 1, u32> extended_dynamic_state;
 | 
				
			||||||
        BitField<1, 1, u32> xfb_enabled;
 | 
					        BitField<1, 1, u32> dynamic_vertex_input;
 | 
				
			||||||
        BitField<2, 1, u32> primitive_restart_enable;
 | 
					        BitField<2, 1, u32> xfb_enabled;
 | 
				
			||||||
        BitField<3, 1, u32> depth_bias_enable;
 | 
					        BitField<3, 1, u32> primitive_restart_enable;
 | 
				
			||||||
        BitField<4, 1, u32> depth_clamp_disabled;
 | 
					        BitField<4, 1, u32> depth_bias_enable;
 | 
				
			||||||
        BitField<5, 1, u32> ndc_minus_one_to_one;
 | 
					        BitField<5, 1, u32> depth_clamp_disabled;
 | 
				
			||||||
        BitField<6, 2, u32> polygon_mode;
 | 
					        BitField<6, 1, u32> ndc_minus_one_to_one;
 | 
				
			||||||
        BitField<8, 5, u32> patch_control_points_minus_one;
 | 
					        BitField<7, 2, u32> polygon_mode;
 | 
				
			||||||
        BitField<13, 2, u32> tessellation_primitive;
 | 
					        BitField<9, 5, u32> patch_control_points_minus_one;
 | 
				
			||||||
        BitField<15, 2, u32> tessellation_spacing;
 | 
					        BitField<14, 2, u32> tessellation_primitive;
 | 
				
			||||||
        BitField<17, 1, u32> tessellation_clockwise;
 | 
					        BitField<16, 2, u32> tessellation_spacing;
 | 
				
			||||||
        BitField<18, 1, u32> logic_op_enable;
 | 
					        BitField<18, 1, u32> tessellation_clockwise;
 | 
				
			||||||
        BitField<19, 4, u32> logic_op;
 | 
					        BitField<19, 1, u32> logic_op_enable;
 | 
				
			||||||
        BitField<23, 1, u32> rasterize_enable;
 | 
					        BitField<20, 4, u32> logic_op;
 | 
				
			||||||
        BitField<24, 4, Maxwell::PrimitiveTopology> topology;
 | 
					        BitField<24, 4, Maxwell::PrimitiveTopology> topology;
 | 
				
			||||||
        BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
 | 
					        BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
        u32 raw2;
 | 
					        u32 raw2;
 | 
				
			||||||
        BitField<0, 3, u32> alpha_test_func;
 | 
					        BitField<0, 1, u32> rasterize_enable;
 | 
				
			||||||
        BitField<3, 1, u32> early_z;
 | 
					        BitField<1, 3, u32> alpha_test_func;
 | 
				
			||||||
        BitField<4, 1, u32> depth_enabled;
 | 
					        BitField<4, 1, u32> early_z;
 | 
				
			||||||
        BitField<5, 5, u32> depth_format;
 | 
					        BitField<5, 1, u32> depth_enabled;
 | 
				
			||||||
        BitField<10, 1, u32> y_negate;
 | 
					        BitField<6, 5, u32> depth_format;
 | 
				
			||||||
        BitField<11, 1, u32> provoking_vertex_last;
 | 
					        BitField<11, 1, u32> y_negate;
 | 
				
			||||||
 | 
					        BitField<12, 1, u32> provoking_vertex_last;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    std::array<u8, Maxwell::NumRenderTargets> color_formats;
 | 
					    std::array<u8, Maxwell::NumRenderTargets> color_formats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 alpha_test_ref;
 | 
					    u32 alpha_test_ref;
 | 
				
			||||||
    u32 point_size;
 | 
					    u32 point_size;
 | 
				
			||||||
    std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
 | 
					 | 
				
			||||||
    std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
 | 
					 | 
				
			||||||
    std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
 | 
					    std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
 | 
				
			||||||
    std::array<u16, Maxwell::NumViewports> viewport_swizzles;
 | 
					    std::array<u16, Maxwell::NumViewports> viewport_swizzles;
 | 
				
			||||||
 | 
					    union {
 | 
				
			||||||
 | 
					        u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
 | 
				
			||||||
 | 
					        u64 enabled_divisors;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
 | 
				
			||||||
 | 
					    std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DynamicState dynamic_state;
 | 
					    DynamicState dynamic_state;
 | 
				
			||||||
    VideoCommon::TransformFeedbackState xfb_state;
 | 
					    VideoCommon::TransformFeedbackState xfb_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state);
 | 
					    void Refresh(Tegra::Engines::Maxwell3D& maxwell3d, bool has_extended_dynamic_state,
 | 
				
			||||||
 | 
					                 bool has_dynamic_vertex_input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_t Hash() const noexcept;
 | 
					    size_t Hash() const noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -216,16 +223,24 @@ struct FixedPipelineState {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_t Size() const noexcept {
 | 
					    size_t Size() const noexcept {
 | 
				
			||||||
        if (xfb_enabled != 0) {
 | 
					        if (xfb_enabled) {
 | 
				
			||||||
            // When transform feedback is enabled, use the whole struct
 | 
					            // When transform feedback is enabled, use the whole struct
 | 
				
			||||||
            return sizeof(*this);
 | 
					            return sizeof(*this);
 | 
				
			||||||
        } else if (no_extended_dynamic_state != 0) {
 | 
					        }
 | 
				
			||||||
            // Dynamic state is enabled, we can enable more
 | 
					        if (dynamic_vertex_input) {
 | 
				
			||||||
            return offsetof(FixedPipelineState, xfb_state);
 | 
					            // Exclude dynamic state and attributes
 | 
				
			||||||
        } else {
 | 
					            return offsetof(FixedPipelineState, attributes);
 | 
				
			||||||
            // No XFB, extended dynamic state enabled
 | 
					        }
 | 
				
			||||||
 | 
					        if (extended_dynamic_state) {
 | 
				
			||||||
 | 
					            // Exclude dynamic state
 | 
				
			||||||
            return offsetof(FixedPipelineState, dynamic_state);
 | 
					            return offsetof(FixedPipelineState, dynamic_state);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Default
 | 
				
			||||||
 | 
					        return offsetof(FixedPipelineState, xfb_state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u32 DynamicAttributeType(size_t index) const noexcept {
 | 
				
			||||||
 | 
					        return (attribute_types >> (index * 2)) & 0b11;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
 | 
					static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -472,39 +472,65 @@ void GraphicsPipeline::ConfigureDraw() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
 | 
					void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
 | 
				
			||||||
    FixedPipelineState::DynamicState dynamic{};
 | 
					    FixedPipelineState::DynamicState dynamic{};
 | 
				
			||||||
    if (!device.IsExtExtendedDynamicStateSupported()) {
 | 
					    if (key.state.extended_dynamic_state) {
 | 
				
			||||||
        dynamic = key.state.dynamic_state;
 | 
					        dynamic = key.state.dynamic_state;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
 | 
					    static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
 | 
				
			||||||
    static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
 | 
					    static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
 | 
				
			||||||
    for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
 | 
					    static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
 | 
				
			||||||
        const bool instanced = key.state.binding_divisors[index] != 0;
 | 
					    if (key.state.dynamic_vertex_input) {
 | 
				
			||||||
        const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
 | 
					        const auto& input_attributes = stage_infos[0].input_generics;
 | 
				
			||||||
        vertex_bindings.push_back({
 | 
					        for (size_t index = 0; index < key.state.attributes.size(); ++index) {
 | 
				
			||||||
            .binding = static_cast<u32>(index),
 | 
					            const u32 type = key.state.DynamicAttributeType(index);
 | 
				
			||||||
            .stride = dynamic.vertex_strides[index],
 | 
					            if (!input_attributes[index].used || type == 0) {
 | 
				
			||||||
            .inputRate = rate,
 | 
					                continue;
 | 
				
			||||||
        });
 | 
					            }
 | 
				
			||||||
        if (instanced) {
 | 
					            vertex_attributes.push_back({
 | 
				
			||||||
            vertex_binding_divisors.push_back({
 | 
					                .location = static_cast<u32>(index),
 | 
				
			||||||
                .binding = static_cast<u32>(index),
 | 
					                .binding = 0,
 | 
				
			||||||
                .divisor = key.state.binding_divisors[index],
 | 
					                .format = type == 1   ? VK_FORMAT_R32_SFLOAT
 | 
				
			||||||
 | 
					                          : type == 2 ? VK_FORMAT_R32_SINT
 | 
				
			||||||
 | 
					                                      : VK_FORMAT_R32_UINT,
 | 
				
			||||||
 | 
					                .offset = 0,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					        if (!vertex_attributes.empty()) {
 | 
				
			||||||
    static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
 | 
					            vertex_bindings.push_back({
 | 
				
			||||||
    const auto& input_attributes = stage_infos[0].input_generics;
 | 
					                .binding = 0,
 | 
				
			||||||
    for (size_t index = 0; index < key.state.attributes.size(); ++index) {
 | 
					                .stride = 4,
 | 
				
			||||||
        const auto& attribute = key.state.attributes[index];
 | 
					                .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
 | 
				
			||||||
        if (!attribute.enabled || !input_attributes[index].used) {
 | 
					            });
 | 
				
			||||||
            continue;
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
 | 
				
			||||||
 | 
					            const bool instanced = key.state.binding_divisors[index] != 0;
 | 
				
			||||||
 | 
					            const auto rate =
 | 
				
			||||||
 | 
					                instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
 | 
				
			||||||
 | 
					            vertex_bindings.push_back({
 | 
				
			||||||
 | 
					                .binding = static_cast<u32>(index),
 | 
				
			||||||
 | 
					                .stride = dynamic.vertex_strides[index],
 | 
				
			||||||
 | 
					                .inputRate = rate,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            if (instanced) {
 | 
				
			||||||
 | 
					                vertex_binding_divisors.push_back({
 | 
				
			||||||
 | 
					                    .binding = static_cast<u32>(index),
 | 
				
			||||||
 | 
					                    .divisor = key.state.binding_divisors[index],
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const auto& input_attributes = stage_infos[0].input_generics;
 | 
				
			||||||
 | 
					        for (size_t index = 0; index < key.state.attributes.size(); ++index) {
 | 
				
			||||||
 | 
					            const auto& attribute = key.state.attributes[index];
 | 
				
			||||||
 | 
					            if (!attribute.enabled || !input_attributes[index].used) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            vertex_attributes.push_back({
 | 
				
			||||||
 | 
					                .location = static_cast<u32>(index),
 | 
				
			||||||
 | 
					                .binding = attribute.buffer,
 | 
				
			||||||
 | 
					                .format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size()),
 | 
				
			||||||
 | 
					                .offset = attribute.offset,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        vertex_attributes.push_back({
 | 
					 | 
				
			||||||
            .location = static_cast<u32>(index),
 | 
					 | 
				
			||||||
            .binding = attribute.buffer,
 | 
					 | 
				
			||||||
            .format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size()),
 | 
					 | 
				
			||||||
            .offset = attribute.offset,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    VkPipelineVertexInputStateCreateInfo vertex_input_ci{
 | 
					    VkPipelineVertexInputStateCreateInfo vertex_input_ci{
 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
 | 
					        .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
 | 
				
			||||||
@@ -545,27 +571,25 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
 | 
				
			|||||||
        .flags = 0,
 | 
					        .flags = 0,
 | 
				
			||||||
        .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
 | 
					        .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    VkPipelineViewportStateCreateInfo viewport_ci{
 | 
					
 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
 | 
					 | 
				
			||||||
        .pNext = nullptr,
 | 
					 | 
				
			||||||
        .flags = 0,
 | 
					 | 
				
			||||||
        .viewportCount = Maxwell::NumViewports,
 | 
					 | 
				
			||||||
        .pViewports = nullptr,
 | 
					 | 
				
			||||||
        .scissorCount = Maxwell::NumViewports,
 | 
					 | 
				
			||||||
        .pScissors = nullptr,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
 | 
					    std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
 | 
				
			||||||
    std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
 | 
					    std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
 | 
				
			||||||
    VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
 | 
					    const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
 | 
					        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
 | 
				
			||||||
        .pNext = nullptr,
 | 
					        .pNext = nullptr,
 | 
				
			||||||
        .flags = 0,
 | 
					        .flags = 0,
 | 
				
			||||||
        .viewportCount = Maxwell::NumViewports,
 | 
					        .viewportCount = Maxwell::NumViewports,
 | 
				
			||||||
        .pViewportSwizzles = swizzles.data(),
 | 
					        .pViewportSwizzles = swizzles.data(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if (device.IsNvViewportSwizzleSupported()) {
 | 
					    const VkPipelineViewportStateCreateInfo viewport_ci{
 | 
				
			||||||
        viewport_ci.pNext = &swizzle_ci;
 | 
					        .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
 | 
				
			||||||
    }
 | 
					        .pNext = device.IsNvViewportSwizzleSupported() ? &swizzle_ci : nullptr,
 | 
				
			||||||
 | 
					        .flags = 0,
 | 
				
			||||||
 | 
					        .viewportCount = Maxwell::NumViewports,
 | 
				
			||||||
 | 
					        .pViewports = nullptr,
 | 
				
			||||||
 | 
					        .scissorCount = Maxwell::NumViewports,
 | 
				
			||||||
 | 
					        .pScissors = nullptr,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
 | 
					    const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{
 | 
				
			||||||
        .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
 | 
					        .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
 | 
				
			||||||
@@ -660,13 +684,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
 | 
				
			|||||||
        .pAttachments = cb_attachments.data(),
 | 
					        .pAttachments = cb_attachments.data(),
 | 
				
			||||||
        .blendConstants = {},
 | 
					        .blendConstants = {},
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    static_vector<VkDynamicState, 17> dynamic_states{
 | 
					    static_vector<VkDynamicState, 18> dynamic_states{
 | 
				
			||||||
        VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
 | 
					        VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
 | 
				
			||||||
        VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
 | 
					        VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
 | 
				
			||||||
        VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
 | 
					        VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
 | 
				
			||||||
        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
 | 
					        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if (device.IsExtExtendedDynamicStateSupported()) {
 | 
					    if (key.state.extended_dynamic_state) {
 | 
				
			||||||
        static constexpr std::array extended{
 | 
					        static constexpr std::array extended{
 | 
				
			||||||
            VK_DYNAMIC_STATE_CULL_MODE_EXT,
 | 
					            VK_DYNAMIC_STATE_CULL_MODE_EXT,
 | 
				
			||||||
            VK_DYNAMIC_STATE_FRONT_FACE_EXT,
 | 
					            VK_DYNAMIC_STATE_FRONT_FACE_EXT,
 | 
				
			||||||
@@ -678,6 +702,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
 | 
				
			|||||||
            VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
 | 
					            VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
 | 
				
			||||||
            VK_DYNAMIC_STATE_STENCIL_OP_EXT,
 | 
					            VK_DYNAMIC_STATE_STENCIL_OP_EXT,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        if (key.state.dynamic_vertex_input) {
 | 
				
			||||||
 | 
					            dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
 | 
					        dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
 | 
					    const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,6 +109,20 @@ static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexA
 | 
				
			|||||||
    return Shader::AttributeType::Float;
 | 
					    return Shader::AttributeType::Float;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t index) {
 | 
				
			||||||
 | 
					    switch (state.DynamicAttributeType(index)) {
 | 
				
			||||||
 | 
					    case 0:
 | 
				
			||||||
 | 
					        return Shader::AttributeType::Disabled;
 | 
				
			||||||
 | 
					    case 1:
 | 
				
			||||||
 | 
					        return Shader::AttributeType::Float;
 | 
				
			||||||
 | 
					    case 2:
 | 
				
			||||||
 | 
					        return Shader::AttributeType::SignedInt;
 | 
				
			||||||
 | 
					    case 3:
 | 
				
			||||||
 | 
					        return Shader::AttributeType::UnsignedInt;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Shader::AttributeType::Disabled;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
 | 
					Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
 | 
				
			||||||
                                    const Shader::IR::Program& program) {
 | 
					                                    const Shader::IR::Program& program) {
 | 
				
			||||||
    Shader::RuntimeInfo info;
 | 
					    Shader::RuntimeInfo info;
 | 
				
			||||||
@@ -123,13 +137,19 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
 | 
				
			|||||||
            if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
 | 
					            if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
 | 
				
			||||||
                info.fixed_state_point_size = point_size;
 | 
					                info.fixed_state_point_size = point_size;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (key.state.xfb_enabled != 0) {
 | 
					            if (key.state.xfb_enabled) {
 | 
				
			||||||
                info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
 | 
					                info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            info.convert_depth_mode = gl_ndc;
 | 
					            info.convert_depth_mode = gl_ndc;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
 | 
					        if (key.state.dynamic_vertex_input) {
 | 
				
			||||||
                               &CastAttributeType);
 | 
					            for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
 | 
				
			||||||
 | 
					                info.generic_input_types[index] = AttributeType(key.state, index);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
 | 
				
			||||||
 | 
					                                   &CastAttributeType);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case Shader::Stage::TessellationEval:
 | 
					    case Shader::Stage::TessellationEval:
 | 
				
			||||||
        // We have to flip tessellation clockwise for some reason...
 | 
					        // We have to flip tessellation clockwise for some reason...
 | 
				
			||||||
@@ -298,7 +318,8 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
 | 
				
			|||||||
        current_pipeline = nullptr;
 | 
					        current_pipeline = nullptr;
 | 
				
			||||||
        return nullptr;
 | 
					        return nullptr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported());
 | 
					    graphics_key.state.Refresh(maxwell3d, device.IsExtExtendedDynamicStateSupported(),
 | 
				
			||||||
 | 
					                               device.IsExtVertexInputDynamicStateSupported());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (current_pipeline) {
 | 
					    if (current_pipeline) {
 | 
				
			||||||
        GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
 | 
					        GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -551,6 +551,9 @@ void RasterizerVulkan::UpdateDynamicStates() {
 | 
				
			|||||||
        UpdateFrontFace(regs);
 | 
					        UpdateFrontFace(regs);
 | 
				
			||||||
        UpdateStencilOp(regs);
 | 
					        UpdateStencilOp(regs);
 | 
				
			||||||
        UpdateStencilTestEnable(regs);
 | 
					        UpdateStencilTestEnable(regs);
 | 
				
			||||||
 | 
					        if (device.IsExtVertexInputDynamicStateSupported()) {
 | 
				
			||||||
 | 
					            UpdateVertexInput(regs);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -780,4 +783,57 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
 | 
				
			||||||
 | 
					    auto& dirty{maxwell3d.dirty.flags};
 | 
				
			||||||
 | 
					    if (!dirty[Dirty::VertexInput]) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dirty[Dirty::VertexInput] = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings;
 | 
				
			||||||
 | 
					    boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
 | 
				
			||||||
 | 
					        if (!dirty[Dirty::VertexAttribute0 + index]) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
 | 
				
			||||||
 | 
					        const u32 binding{attribute.buffer};
 | 
				
			||||||
 | 
					        dirty[Dirty::VertexAttribute0 + index] = false;
 | 
				
			||||||
 | 
					        dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        attributes.push_back({
 | 
				
			||||||
 | 
					            .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
 | 
				
			||||||
 | 
					            .pNext = nullptr,
 | 
				
			||||||
 | 
					            .location = static_cast<u32>(index),
 | 
				
			||||||
 | 
					            .binding = binding,
 | 
				
			||||||
 | 
					            .format = attribute.IsConstant()
 | 
				
			||||||
 | 
					                          ? VK_FORMAT_A8B8G8R8_UNORM_PACK32
 | 
				
			||||||
 | 
					                          : MaxwellToVK::VertexFormat(attribute.type, attribute.size),
 | 
				
			||||||
 | 
					            .offset = attribute.offset,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
 | 
				
			||||||
 | 
					        if (!dirty[Dirty::VertexBinding0 + index]) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dirty[Dirty::VertexBinding0 + index] = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const u32 binding{static_cast<u32>(index)};
 | 
				
			||||||
 | 
					        const auto& input_binding{regs.vertex_array[binding]};
 | 
				
			||||||
 | 
					        const bool is_instanced{regs.instanced_arrays.IsInstancingEnabled(binding)};
 | 
				
			||||||
 | 
					        bindings.push_back({
 | 
				
			||||||
 | 
					            .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
 | 
				
			||||||
 | 
					            .pNext = nullptr,
 | 
				
			||||||
 | 
					            .binding = binding,
 | 
				
			||||||
 | 
					            .stride = input_binding.stride,
 | 
				
			||||||
 | 
					            .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
 | 
				
			||||||
 | 
					            .divisor = is_instanced ? input_binding.divisor : 1,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
 | 
				
			||||||
 | 
					        cmdbuf.SetVertexInputEXT(bindings, attributes);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Vulkan
 | 
					} // namespace Vulkan
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,6 +135,8 @@ private:
 | 
				
			|||||||
    void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
					    void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
				
			||||||
    void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
					    void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Tegra::GPU& gpu;
 | 
					    Tegra::GPU& gpu;
 | 
				
			||||||
    Tegra::MemoryManager& gpu_memory;
 | 
					    Tegra::MemoryManager& gpu_memory;
 | 
				
			||||||
    Tegra::Engines::Maxwell3D& maxwell3d;
 | 
					    Tegra::Engines::Maxwell3D& maxwell3d;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,9 +29,10 @@ using Flags = Maxwell3D::DirtyState::Flags;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Flags MakeInvalidationFlags() {
 | 
					Flags MakeInvalidationFlags() {
 | 
				
			||||||
    static constexpr int INVALIDATION_FLAGS[]{
 | 
					    static constexpr int INVALIDATION_FLAGS[]{
 | 
				
			||||||
        Viewports,         Scissors,  DepthBias,         BlendConstants,    DepthBounds,
 | 
					        Viewports,       Scissors,          DepthBias,      BlendConstants,
 | 
				
			||||||
        StencilProperties, CullMode,  DepthBoundsEnable, DepthTestEnable,   DepthWriteEnable,
 | 
					        DepthBounds,     StencilProperties, CullMode,       DepthBoundsEnable,
 | 
				
			||||||
        DepthCompareOp,    FrontFace, StencilOp,         StencilTestEnable, VertexBuffers,
 | 
					        DepthTestEnable, DepthWriteEnable,  DepthCompareOp, FrontFace,
 | 
				
			||||||
 | 
					        StencilOp,       StencilTestEnable, VertexBuffers,  VertexInput,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    Flags flags{};
 | 
					    Flags flags{};
 | 
				
			||||||
    for (const int flag : INVALIDATION_FLAGS) {
 | 
					    for (const int flag : INVALIDATION_FLAGS) {
 | 
				
			||||||
@@ -40,6 +41,12 @@ Flags MakeInvalidationFlags() {
 | 
				
			|||||||
    for (int index = VertexBuffer0; index <= VertexBuffer31; ++index) {
 | 
					    for (int index = VertexBuffer0; index <= VertexBuffer31; ++index) {
 | 
				
			||||||
        flags[index] = true;
 | 
					        flags[index] = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    for (int index = VertexAttribute0; index <= VertexAttribute31; ++index) {
 | 
				
			||||||
 | 
					        flags[index] = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (int index = VertexBinding0; index <= VertexBinding31; ++index) {
 | 
				
			||||||
 | 
					        flags[index] = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return flags;
 | 
					    return flags;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,19 +141,6 @@ void SetupDirtyBlending(Tables& tables) {
 | 
				
			|||||||
    FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending);
 | 
					    FillBlock(tables[0], OFF(independent_blend), NUM(independent_blend), Blending);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SetupDirtyInstanceDivisors(Tables& tables) {
 | 
					 | 
				
			||||||
    static constexpr size_t divisor_offset = 3;
 | 
					 | 
				
			||||||
    for (size_t index = 0; index < Regs::NumVertexArrays; ++index) {
 | 
					 | 
				
			||||||
        tables[0][OFF(instanced_arrays) + index] = InstanceDivisors;
 | 
					 | 
				
			||||||
        tables[0][OFF(vertex_array) + index * NUM(vertex_array[0]) + divisor_offset] =
 | 
					 | 
				
			||||||
            InstanceDivisors;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SetupDirtyVertexAttributes(Tables& tables) {
 | 
					 | 
				
			||||||
    FillBlock(tables[0], OFF(vertex_attrib_format), NUM(vertex_attrib_format), VertexAttributes);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SetupDirtyViewportSwizzles(Tables& tables) {
 | 
					void SetupDirtyViewportSwizzles(Tables& tables) {
 | 
				
			||||||
    static constexpr size_t swizzle_offset = 6;
 | 
					    static constexpr size_t swizzle_offset = 6;
 | 
				
			||||||
    for (size_t index = 0; index < Regs::NumViewports; ++index) {
 | 
					    for (size_t index = 0; index < Regs::NumViewports; ++index) {
 | 
				
			||||||
@@ -154,11 +148,31 @@ void SetupDirtyViewportSwizzles(Tables& tables) {
 | 
				
			|||||||
            ViewportSwizzles;
 | 
					            ViewportSwizzles;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SetupDirtyVertexAttributes(Tables& tables) {
 | 
				
			||||||
 | 
					    for (size_t i = 0; i < Regs::NumVertexAttributes; ++i) {
 | 
				
			||||||
 | 
					        const size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]);
 | 
				
			||||||
 | 
					        FillBlock(tables[0], offset, NUM(vertex_attrib_format[0]), VertexAttribute0 + i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    FillBlock(tables[1], OFF(vertex_attrib_format), Regs::NumVertexAttributes, VertexInput);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SetupDirtyVertexBindings(Tables& tables) {
 | 
				
			||||||
 | 
					    // Do NOT include stride here, it's implicit in VertexBuffer
 | 
				
			||||||
 | 
					    static constexpr size_t divisor_offset = 3;
 | 
				
			||||||
 | 
					    for (size_t i = 0; i < Regs::NumVertexArrays; ++i) {
 | 
				
			||||||
 | 
					        const u8 flag = static_cast<u8>(VertexBinding0 + i);
 | 
				
			||||||
 | 
					        tables[0][OFF(instanced_arrays) + i] = VertexInput;
 | 
				
			||||||
 | 
					        tables[1][OFF(instanced_arrays) + i] = flag;
 | 
				
			||||||
 | 
					        tables[0][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = VertexInput;
 | 
				
			||||||
 | 
					        tables[1][OFF(vertex_array) + i * NUM(vertex_array[0]) + divisor_offset] = flag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
StateTracker::StateTracker(Tegra::GPU& gpu)
 | 
					StateTracker::StateTracker(Tegra::GPU& gpu)
 | 
				
			||||||
    : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} {
 | 
					    : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} {
 | 
				
			||||||
    auto& tables = gpu.Maxwell3D().dirty.tables;
 | 
					    auto& tables{gpu.Maxwell3D().dirty.tables};
 | 
				
			||||||
    SetupDirtyFlags(tables);
 | 
					    SetupDirtyFlags(tables);
 | 
				
			||||||
    SetupDirtyViewports(tables);
 | 
					    SetupDirtyViewports(tables);
 | 
				
			||||||
    SetupDirtyScissors(tables);
 | 
					    SetupDirtyScissors(tables);
 | 
				
			||||||
@@ -175,9 +189,9 @@ StateTracker::StateTracker(Tegra::GPU& gpu)
 | 
				
			|||||||
    SetupDirtyStencilOp(tables);
 | 
					    SetupDirtyStencilOp(tables);
 | 
				
			||||||
    SetupDirtyStencilTestEnable(tables);
 | 
					    SetupDirtyStencilTestEnable(tables);
 | 
				
			||||||
    SetupDirtyBlending(tables);
 | 
					    SetupDirtyBlending(tables);
 | 
				
			||||||
    SetupDirtyInstanceDivisors(tables);
 | 
					 | 
				
			||||||
    SetupDirtyVertexAttributes(tables);
 | 
					 | 
				
			||||||
    SetupDirtyViewportSwizzles(tables);
 | 
					    SetupDirtyViewportSwizzles(tables);
 | 
				
			||||||
 | 
					    SetupDirtyVertexAttributes(tables);
 | 
				
			||||||
 | 
					    SetupDirtyVertexBindings(tables);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Vulkan
 | 
					} // namespace Vulkan
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,12 @@ namespace Dirty {
 | 
				
			|||||||
enum : u8 {
 | 
					enum : u8 {
 | 
				
			||||||
    First = VideoCommon::Dirty::LastCommonEntry,
 | 
					    First = VideoCommon::Dirty::LastCommonEntry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VertexInput,
 | 
				
			||||||
 | 
					    VertexAttribute0,
 | 
				
			||||||
 | 
					    VertexAttribute31 = VertexAttribute0 + 31,
 | 
				
			||||||
 | 
					    VertexBinding0,
 | 
				
			||||||
 | 
					    VertexBinding31 = VertexBinding0 + 31,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Viewports,
 | 
					    Viewports,
 | 
				
			||||||
    Scissors,
 | 
					    Scissors,
 | 
				
			||||||
    DepthBias,
 | 
					    DepthBias,
 | 
				
			||||||
@@ -36,8 +42,6 @@ enum : u8 {
 | 
				
			|||||||
    StencilTestEnable,
 | 
					    StencilTestEnable,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Blending,
 | 
					    Blending,
 | 
				
			||||||
    InstanceDivisors,
 | 
					 | 
				
			||||||
    VertexAttributes,
 | 
					 | 
				
			||||||
    ViewportSwizzles,
 | 
					    ViewportSwizzles,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Last
 | 
					    Last
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,6 +239,11 @@ public:
 | 
				
			|||||||
        return ext_extended_dynamic_state;
 | 
					        return ext_extended_dynamic_state;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
 | 
				
			||||||
 | 
					    bool IsExtVertexInputDynamicStateSupported() const {
 | 
				
			||||||
 | 
					        return ext_vertex_input_dynamic_state;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns true if the device supports VK_EXT_shader_stencil_export.
 | 
					    /// Returns true if the device supports VK_EXT_shader_stencil_export.
 | 
				
			||||||
    bool IsExtShaderStencilExportSupported() const {
 | 
					    bool IsExtShaderStencilExportSupported() const {
 | 
				
			||||||
        return ext_shader_stencil_export;
 | 
					        return ext_shader_stencil_export;
 | 
				
			||||||
@@ -349,6 +354,7 @@ private:
 | 
				
			|||||||
    bool ext_transform_feedback{};          ///< Support for VK_EXT_transform_feedback.
 | 
					    bool ext_transform_feedback{};          ///< Support for VK_EXT_transform_feedback.
 | 
				
			||||||
    bool ext_custom_border_color{};         ///< Support for VK_EXT_custom_border_color.
 | 
					    bool ext_custom_border_color{};         ///< Support for VK_EXT_custom_border_color.
 | 
				
			||||||
    bool ext_extended_dynamic_state{};      ///< Support for VK_EXT_extended_dynamic_state.
 | 
					    bool ext_extended_dynamic_state{};      ///< Support for VK_EXT_extended_dynamic_state.
 | 
				
			||||||
 | 
					    bool ext_vertex_input_dynamic_state{};  ///< Support for VK_EXT_vertex_input_dynamic_state.
 | 
				
			||||||
    bool ext_shader_stencil_export{};       ///< Support for VK_EXT_shader_stencil_export.
 | 
					    bool ext_shader_stencil_export{};       ///< Support for VK_EXT_shader_stencil_export.
 | 
				
			||||||
    bool ext_shader_atomic_int64{};         ///< Support for VK_KHR_shader_atomic_int64.
 | 
					    bool ext_shader_atomic_int64{};         ///< Support for VK_KHR_shader_atomic_int64.
 | 
				
			||||||
    bool ext_provoking_vertex{};            ///< Support for VK_EXT_provoking_vertex.
 | 
					    bool ext_provoking_vertex{};            ///< Support for VK_EXT_provoking_vertex.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,6 +123,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
 | 
				
			|||||||
    X(vkCmdSetPrimitiveTopologyEXT);
 | 
					    X(vkCmdSetPrimitiveTopologyEXT);
 | 
				
			||||||
    X(vkCmdSetStencilOpEXT);
 | 
					    X(vkCmdSetStencilOpEXT);
 | 
				
			||||||
    X(vkCmdSetStencilTestEnableEXT);
 | 
					    X(vkCmdSetStencilTestEnableEXT);
 | 
				
			||||||
 | 
					    X(vkCmdSetVertexInputEXT);
 | 
				
			||||||
    X(vkCmdResolveImage);
 | 
					    X(vkCmdResolveImage);
 | 
				
			||||||
    X(vkCreateBuffer);
 | 
					    X(vkCreateBuffer);
 | 
				
			||||||
    X(vkCreateBufferView);
 | 
					    X(vkCreateBufferView);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,6 +238,7 @@ struct DeviceDispatch : InstanceDispatch {
 | 
				
			|||||||
    PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
 | 
					    PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
 | 
				
			||||||
    PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{};
 | 
					    PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{};
 | 
				
			||||||
    PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{};
 | 
					    PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{};
 | 
				
			||||||
 | 
					    PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{};
 | 
				
			||||||
    PFN_vkCmdResolveImage vkCmdResolveImage{};
 | 
					    PFN_vkCmdResolveImage vkCmdResolveImage{};
 | 
				
			||||||
    PFN_vkCreateBuffer vkCreateBuffer{};
 | 
					    PFN_vkCreateBuffer vkCreateBuffer{};
 | 
				
			||||||
    PFN_vkCreateBufferView vkCreateBufferView{};
 | 
					    PFN_vkCreateBufferView vkCreateBufferView{};
 | 
				
			||||||
@@ -1203,6 +1204,13 @@ public:
 | 
				
			|||||||
        dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
 | 
					        dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SetVertexInputEXT(
 | 
				
			||||||
 | 
					        vk::Span<VkVertexInputBindingDescription2EXT> bindings,
 | 
				
			||||||
 | 
					        vk::Span<VkVertexInputAttributeDescription2EXT> attributes) const noexcept {
 | 
				
			||||||
 | 
					        dld->vkCmdSetVertexInputEXT(handle, bindings.size(), bindings.data(), attributes.size(),
 | 
				
			||||||
 | 
					                                    attributes.data());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
 | 
					    void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
 | 
				
			||||||
                                         const VkDeviceSize* offsets,
 | 
					                                         const VkDeviceSize* offsets,
 | 
				
			||||||
                                         const VkDeviceSize* sizes) const noexcept {
 | 
					                                         const VkDeviceSize* sizes) const noexcept {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user