202 lines
9.0 KiB
Plaintext
202 lines
9.0 KiB
Plaintext
////
|
|
- Copyright (c) 2022-2023, Mobica Limited
|
|
-
|
|
- SPDX-License-Identifier: Apache-2.0
|
|
-
|
|
- Licensed under the Apache License, Version 2.0 the "License";
|
|
- you may not use this file except in compliance with the License.
|
|
- You may obtain a copy of the License at
|
|
-
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
-
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
- See the License for the specific language governing permissions and
|
|
- limitations under the License.
|
|
-
|
|
////
|
|
= Vertex input dynamic state
|
|
|
|
ifdef::site-gen-antora[]
|
|
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/vertex_dynamic_state[Khronos Vulkan samples github repository].
|
|
endif::[]
|
|
|
|
|
|
image::./images/sample.png[Sample]
|
|
|
|
== Overview
|
|
|
|
This sample demonstrates how to use the `VK_EXT_vertex_input_dynamic_state` extension, which eliminates the need to create multiple pipelines in case of different binding and/or attribute descriptions.
|
|
|
|
This extension changes how attributes and bindings are managed.
|
|
Instead of static description during pipeline creation, this extension allows developers to change attributes and bindings using a function before every draw.
|
|
|
|
Below is a comparison of common Vulkan vertex input description and dynamic one.
|
|
|
|
|===
|
|
| Vertex input static state | Vertex input dynamic state
|
|
|
|
| dynamic_state = {}
|
|
| dynamic_state = \{VK_DYNAMIC_STATE_VERTEX_INPUT_EXT}
|
|
|
|
| pVertexInputState = &vertex_input_state
|
|
| pVertexInputState = VK_NULL_HANDLE
|
|
|
|
| vkCreateGraphicsPipelines(model1) + vkCreateGraphicsPipelines(model2)
|
|
| vkCreateGraphicsPipelines(model)
|
|
|
|
| draw(model1, pipeline1) + draw(model2, pipeline2)
|
|
| vertex_bindings_description.stride = sizeof(Vertex1);
|
|
+ vertex_attribute_description.offset = offsetof(Vertex1, param);
|
|
+ vkCmdSetVertexInputEXT(&vertex1_params) + draw(model1, pipeline) + vertex_bindings_description.stride = sizeof(Vertex2);
|
|
+ vertex_attribute_description.offset = offsetof(Vertex2, param);
|
|
+ vkCmdSetVertexInputEXT(&vertex2_params) + draw(model2, pipeline)
|
|
|===
|
|
|
|
More details are provided in the sections that follow.
|
|
|
|
== Pipelines
|
|
|
|
Previously, developers had to create multiple pipeline's for models with different vertex input data, which would be referenced in `VkGraphicsPipelineCreateInfo`.
|
|
This is illustrated in the non-dynamic version of vertex input.
|
|
|
|
[,C++]
|
|
----
|
|
// First pipeline vertex data
|
|
// Binding description
|
|
std::vector<VkVertexInputBindingDescription> vertex_input_bindings_1 = {
|
|
vkb::initializers::vertex_input_binding_description(
|
|
0, sizeof(Vertex1), VK_VERTEX_INPUT_RATE_VERTEX),
|
|
};
|
|
|
|
// Attribute descriptions
|
|
std::vector<VkVertexInputAttributeDescription> vertex_input_attributes_1 = {
|
|
vkb::initializers::vertex_input_attribute_description(
|
|
0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
|
|
vkb::initializers::vertex_input_attribute_description(
|
|
0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex1, normal)), // Normal
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_state_1 =
|
|
vkb::initializers::pipeline_vertex_input_state_create_info();
|
|
vertex_input_state_1.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings_1.size());
|
|
vertex_input_state_1.pVertexBindingDescriptions = vertex_input_bindings_1.data();
|
|
|
|
vertex_input_state_1.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes_1.size());
|
|
vertex_input_state_1.pVertexAttributeDescriptions = vertex_input_attributes_1.data();
|
|
|
|
graphics_create_info_1.pVertexInputState = &vertex_input_state_1;
|
|
|
|
vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1,
|
|
&graphics_create_info, VK_NULL_HANDLE, &pipeline1);
|
|
|
|
// Second pipeline vertex data
|
|
// Binding description
|
|
std::vector<VkVertexInputBindingDescription> vertex_input_bindings_2 = {
|
|
vkb::initializers::vertex_input_binding_description(
|
|
0, sizeof(Vertex2), VK_VERTEX_INPUT_RATE_VERTEX),
|
|
};
|
|
|
|
// Attribute descriptions
|
|
std::vector<VkVertexInputAttributeDescription> vertex_input_attributes_2 = {
|
|
vkb::initializers::vertex_input_attribute_description(
|
|
0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
|
|
vkb::initializers::vertex_input_attribute_description(
|
|
0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex2, normal)), // Normal (different offset than in vertex_input_attributes_1)
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertex_input_state_2 =
|
|
vkb::initializers::pipeline_vertex_input_state_create_info();
|
|
vertex_input_state_1.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings_2.size());
|
|
vertex_input_state_2.pVertexBindingDescriptions = vertex_input_bindings_2.data();
|
|
|
|
vertex_input_state_2.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes_2.size());
|
|
vertex_input_state_2.pVertexAttributeDescriptions = vertex_input_attributes_2.data();
|
|
|
|
graphics_create_info_2.pVertexInputState = &vertex_input_state_2;
|
|
|
|
vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1,
|
|
&graphics_create_info_2, VK_NULL_HANDLE, &pipeline2);
|
|
|
|
draw_model(model1, pipeline1);
|
|
draw_model(model2, pipeline2);
|
|
};
|
|
----
|
|
|
|
However, with dynamic vertex input state the number of pipelines can be reduced because of possibility to change vertex bindings and attribute descriptions on runtime by calling `vkCmdSetVertexInputEXT` before `draw_model`.
|
|
+ This extension extends 2 structures: + `VkVertexInputBindingDescription` to `VkVertexInputBindingDescription2EXT` + `VkVertexInputAttributeDescription` to `VkVertexInputAttributeDescription2EXT` + An example of 2 sets of different vertex input data architectures `Vertex` and `SampleVertex`.
|
|
First model is loaded from assets (load_model function from framework), second model was created directly in code (have different vertex input data architecture).
|
|
|
|
[,C++]
|
|
----
|
|
VkVertexInputBindingDescription2EXT vertex_bindings_description_ext = {
|
|
vkb::initializers::vertex_input_binding_description2ext(
|
|
0,
|
|
sizeof(Vertex),
|
|
VK_VERTEX_INPUT_RATE_VERTEX,
|
|
1);
|
|
}
|
|
VkVertexInputAttributeDescription2EXT vertex_attribute_description_ext[2] = {
|
|
vkb::initializers::vertex_input_attribute_description2ext(
|
|
0,
|
|
0,
|
|
VK_FORMAT_R32G32B32_SFLOAT,
|
|
offsetof(Vertex, pos)),
|
|
vkb::initializers::vertex_input_attribute_description2ext(
|
|
0,
|
|
1,
|
|
VK_FORMAT_R32G32B32_SFLOAT,
|
|
offsetof(Vertex, normal);
|
|
}
|
|
|
|
graphics_create_info.pVertexInputState = nullptr;
|
|
|
|
vkCreateGraphicsPipelines(get_device().get_handle(), VK_NULL_HANDLE, 1,
|
|
&graphics_create_info, VK_NULL_HANDLE, &pipeline);
|
|
|
|
/* First set of vertex input dynamic data (Vertex structure) */
|
|
vertex_bindings_description_ext[0].stride = sizeof(Vertex);
|
|
vertex_attribute_description_ext[1].offset = offsetof(Vertex, normal);
|
|
vkCmdSetVertexInputEXT(draw_cmd_buffer,
|
|
static_cast<uint32_t>(vertex_bindings_description_ext.size()),
|
|
vertex_bindings_description_ext.data(),
|
|
static_cast<uint32_t>(vertex_attribute_description_ext.size()),
|
|
vertex_attribute_description_ext.data());
|
|
draw_model(model1, pipeline);
|
|
|
|
/* Second set of vertex input dynamic data (SampleVertex structure) */
|
|
vertex_bindings_description_ext[0].stride = sizeof(SampleVertex);
|
|
vertex_attribute_description_ext[1].offset = offsetof(SampleVertex, normal);
|
|
vkCmdSetVertexInputEXT(draw_cmd_buffer,
|
|
static_cast<uint32_t>(vertex_bindings_description_ext.size()),
|
|
vertex_bindings_description_ext.data(),
|
|
static_cast<uint32_t>(vertex_attribute_description_ext.size()),
|
|
vertex_attribute_description_ext.data());
|
|
draw_model(model2, pipeline);
|
|
----
|
|
|
|
== Enabling the Extension
|
|
|
|
The vertex input dynamic state api is provided in Vulkan 1.0 and the appropriate headers / SDK is required.
|
|
|
|
In addition, since vertex input dynamic state is provided as an extension and may have varying levels of support, the developer must query availability for each device used.
|
|
|
|
The device extension is provided by `VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME`.
|
|
It also requires `VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME` instance extension to be enabled:
|
|
|
|
[,C++]
|
|
----
|
|
add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
add_device_extension(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
|
----
|
|
|
|
Additional features are provided by the `VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT` struct:
|
|
|
|
[,C++]
|
|
----
|
|
typedef struct VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT {
|
|
VkStructureType sType;
|
|
void* pNext;
|
|
VkBool32 vertexInputDynamicState;
|
|
} VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT;
|
|
----
|