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:
parent
cb78a1b494
commit
ea038d6653
11 changed files with 293 additions and 118 deletions
|
@ -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,15 +94,34 @@ 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) {
|
||||||
|
// Dirty flag will be reset by the command buffer update
|
||||||
|
static constexpr std::array LUT{
|
||||||
|
0u, // Invalid
|
||||||
|
1u, // SignedNorm
|
||||||
|
1u, // UnsignedNorm
|
||||||
|
2u, // SignedInt
|
||||||
|
3u, // UnsignedInt
|
||||||
|
1u, // UnsignedScaled
|
||||||
|
1u, // SignedScaled
|
||||||
|
1u, // Float
|
||||||
|
};
|
||||||
|
const auto& attrs = regs.vertex_attrib_format;
|
||||||
|
attribute_types = 0;
|
||||||
|
for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
|
||||||
|
const u32 mask = attrs[i].constant != 0 ? 0 : 3;
|
||||||
|
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) {
|
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||||
const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
|
const bool is_enabled = regs.instanced_arrays.IsInstancingEnabled(index);
|
||||||
binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
|
binding_divisors[index] = is_enabled ? regs.vertex_array[index].divisor : 0;
|
||||||
|
enabled_divisors |= (is_enabled ? u64{1} : 0) << index;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (maxwell3d.dirty.flags[Dirty::VertexAttributes]) {
|
|
||||||
maxwell3d.dirty.flags[Dirty::VertexAttributes] = false;
|
|
||||||
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||||
const auto& input = regs.vertex_attrib_format[index];
|
const auto& input = regs.vertex_attrib_format[index];
|
||||||
auto& attribute = attributes[index];
|
auto& attribute = attributes[index];
|
||||||
|
@ -113,6 +133,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
|
||||||
attribute.size.Assign(static_cast<u32>(input.size.Value()));
|
attribute.size.Assign(static_cast<u32>(input.size.Value()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (maxwell3d.dirty.flags[Dirty::Blending]) {
|
if (maxwell3d.dirty.flags[Dirty::Blending]) {
|
||||||
maxwell3d.dirty.flags[Dirty::Blending] = false;
|
maxwell3d.dirty.flags[Dirty::Blending] = false;
|
||||||
for (size_t index = 0; index < attachments.size(); ++index) {
|
for (size_t index = 0; index < attachments.size(); ++index) {
|
||||||
|
@ -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,14 +472,40 @@ 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;
|
||||||
|
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
||||||
|
if (key.state.dynamic_vertex_input) {
|
||||||
|
const auto& input_attributes = stage_infos[0].input_generics;
|
||||||
|
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
|
||||||
|
const u32 type = key.state.DynamicAttributeType(index);
|
||||||
|
if (!input_attributes[index].used || type == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vertex_attributes.push_back({
|
||||||
|
.location = static_cast<u32>(index),
|
||||||
|
.binding = 0,
|
||||||
|
.format = type == 1 ? VK_FORMAT_R32_SFLOAT
|
||||||
|
: type == 2 ? VK_FORMAT_R32_SINT
|
||||||
|
: VK_FORMAT_R32_UINT,
|
||||||
|
.offset = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!vertex_attributes.empty()) {
|
||||||
|
vertex_bindings.push_back({
|
||||||
|
.binding = 0,
|
||||||
|
.stride = 4,
|
||||||
|
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||||
const bool instanced = key.state.binding_divisors[index] != 0;
|
const bool instanced = key.state.binding_divisors[index] != 0;
|
||||||
const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
const auto rate =
|
||||||
|
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
vertex_bindings.push_back({
|
vertex_bindings.push_back({
|
||||||
.binding = static_cast<u32>(index),
|
.binding = static_cast<u32>(index),
|
||||||
.stride = dynamic.vertex_strides[index],
|
.stride = dynamic.vertex_strides[index],
|
||||||
|
@ -492,7 +518,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
|
||||||
const auto& input_attributes = stage_infos[0].input_generics;
|
const auto& input_attributes = stage_infos[0].input_generics;
|
||||||
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
|
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
|
||||||
const auto& attribute = key.state.attributes[index];
|
const auto& attribute = key.state.attributes[index];
|
||||||
|
@ -506,6 +531,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||||
.offset = attribute.offset,
|
.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,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
if (key.state.dynamic_vertex_input) {
|
||||||
|
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(),
|
std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
|
||||||
&CastAttributeType);
|
&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 {
|
||||||
|
|
Loading…
Reference in a new issue