Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
3 changed files with 132 additions and 25 deletions
Showing only changes of commit e8ad8d934e - Show all commits

View File

@ -11,20 +11,6 @@
namespace blender::gpu {
/**
* Information about alignment/components and memory size for types when using std430 layout.
*/
struct Std430 {
/** Get the memory size in bytes of a single component using by the given type.*/
static uint32_t component_mem_size(const shader::Type type);
/** Get to alignment of the given type in bytes.*/
static uint32_t element_alignment(const shader::Type type, bool is_array);
/** Get the number of components that should be allocated for the given type.*/
static uint32_t element_components_len(const shader::Type type);
/** Get the number of components of the given type when used in an array.*/
static uint32_t array_components_len(const shader::Type type);
};
/**
* Information about alignment/components and memory size for types when using std140 layout.
*/
@ -39,6 +25,39 @@ struct Std140 {
static uint32_t array_components_len(const shader::Type type);
};
/**
* Information about alignment/components and memory size for types when using std430 layout.
*/
struct Std430 {
/** Get the memory size in bytes of a single component using by the given type.*/
static uint32_t component_mem_size(const shader::Type type);
/** Get to alignment of the given type in bytes.*/
static uint32_t element_alignment(const shader::Type type, bool is_array);
/** Get the number of components that should be allocated for the given type.*/
static uint32_t element_components_len(const shader::Type type);
/** Get the number of components of the given type when used in an array.*/
static uint32_t array_components_len(const shader::Type type);
};
template<typename Layout> static uint32_t element_stride(const shader::Type type)
Jeroen-Bakker marked this conversation as resolved

I think Layout should be LayoutT to avoid thinking it is the layout itself (as in the content of a UBO/SSBO).

I think `Layout` should be `LayoutT` to avoid thinking it is the layout itself (as in the content of a UBO/SSBO).
{
return Layout::element_components_len(type) * Layout::component_mem_size(type);
}
template<typename Layout> static uint32_t array_stride(const shader::Type type)
{
return Layout::array_components_len(type) * Layout::component_mem_size(type);
}
/**
* Move the r_offset to the next alignment where the given type+array_size can be
* reserved.
*
* 'type': The type that needs to be aligned.
* 'array_size': The array_size that needs to be aligned. (0=no array).
* 'r_offset': After the call it will point to the byte where the reservation
* can happen.
*/
template<typename Layout>
static void align(const shader::Type &type, const int32_t array_size, uint32_t *r_offset)
{
@ -52,16 +71,18 @@ static void align(const shader::Type &type, const int32_t array_size, uint32_t *
}
}
template<typename Layout> static uint32_t element_stride(const shader::Type type)
{
return Layout::element_components_len(type) * Layout::component_mem_size(type);
}
template<typename Layout> static uint32_t array_stride(const shader::Type type)
{
return Layout::array_components_len(type) * Layout::component_mem_size(type);
}
/**
* Reserve space for the given type and array size.
*
* This function doesn't handle alignment this needs to be done up front by calling
* 'align<Layout>' function. Caller is responsible for this.
*
* 'type': The type that needs to be reserved.
* 'array_size': The array_size that needs to be reserved. (0=no array).
* 'r_offset': When calling needs to be pointing to the aligned location where to
* reserve space. After the call it will point to the byte just after reserved
* space.
*/
template<typename Layout>
static void reserve(const shader::Type type, int32_t array_size, uint32_t *r_offset)
{
@ -70,6 +91,12 @@ static void reserve(const shader::Type type, int32_t array_size, uint32_t *r_off
*r_offset += size;
}
/**
* Update 'r_offset' to be aligned to the end of the struct.
*
* Call this function when all attributes have been added to make sure that the struct size is
* correct.
*/
template<typename Layout> static void align_end_of_struct(uint32_t *r_offset)
{
align<Layout>(shader::Type::VEC4, 0, r_offset);

View File

@ -70,9 +70,30 @@ struct VKPushConstantsLayout {
VKDescriptorSet::Location storage_buffer_binding_;
public:
/**
* Return the desired storage type that can fit the push constants of the given shader create
* info, matching the device limits.
*
* Returns:
* - StorageType::NONE: No push constants are needed.
* - StorageType::PUSH_CONSTANTS: Regular vulkan push constants can be used.
* - StorageType::UNIFORM_BUFFER: The push constants don't fit in the limits of the given device.
* A uniform buffer should be used as a fallback method.
*/
static StorageType determine_storage_type(
const shader::ShaderCreateInfo &info,
const VkPhysicalDeviceLimits &vk_physical_device_limits);
/**
* Initialize the push constants of the given shader create info with the
* binding location.
*
* interface: Uniform locations of the interface are used as lookup key.
* storage_type: The type of storage for push constants to use.
* location: When storage_type=StorageType::UNIFORM_BUFFER this contains
* the location in the descriptor set where the uniform buffer can be
* bound.
*/
void init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
StorageType storage_type,
@ -86,21 +107,44 @@ struct VKPushConstantsLayout {
return storage_type_;
}
/**
* Get the binding location for the uniform buffer.
*
* Only valid when storage_type=StorageType::UNIFORM_BUFFER.
*/
VKDescriptorSet::Location storage_buffer_binding_get() const
Jeroen-Bakker marked this conversation as resolved
Review

Add const VKPushConstantsLayout& layout_get() const as API and clean up the interface.

Add `const VKPushConstantsLayout& layout_get() const` as API and clean up the interface.
{
return storage_buffer_binding_;
}
/**
* Get the size needed to store the push constants.
*/
uint32_t size_in_bytes() const
{
return size_in_bytes_;
}
/**
* Find the push constant layout for the given location.
* Location = ShaderInput.location.
*/
const PushConstantLayout *find(int32_t location) const;
};
/**
* Container to store push constants in a buffer.
*
* Can handle buffers with different memory layouts (std140/std430)
* Which memory layout is used is based on the storage type.
*
* VKPushConstantsLayout only describes the buffer, an instance of this
* class can handle setting/modifying/duplicating push constants.
*
* It should also keep track of the submissions in order to reuse the allocated
* data.
*/
class VKPushConstants : NonCopyable {
private:
const VKPushConstantsLayout *layout_ = nullptr;
void *data_ = nullptr;
@ -124,14 +168,46 @@ class VKPushConstants : NonCopyable {
return *layout_;
}
/**
* When storage type = StorageType::UNIFORM_BUFFER use this method to update the uniform
* buffer.
*
* It must be called just before adding a draw/compute command to the command queue.
*/
void update_uniform_buffer();
/**
* Get a reference to the uniform buffer.
*
* Only valid when storage type = StorageType::UNIFORM_BUFFER.
*/
VKUniformBuffer &uniform_buffer_get();
/**
* Get the reference to the active data.
*
* Data can get inactive when push constants are modified, after being added to the command
* queue. We still keep track of the old data for reuse and make sure we don't overwrite data
* that is still not on the GPU.
*/
const void *data() const
{
return data_;
}
/**
* Modify a push constant.
*
* location: ShaderInput.location of the push constant to update.
* comp_len: number of components has the data type that is being updated.
* array_size: number of elements when an array to update. (0=no array)
* input_data: packed source data to use.
*
* TODO: this function still needs to convert the input_data layout to that
* what the storage type is expected.
* TODO: Current implementation has a work around for missing implementation
* of builtin uniforms. Builtin uniforms should eventually also be supported.
*/
template<typename T>
void push_constant_set(int32_t location,
int32_t comp_len,

View File

@ -32,6 +32,9 @@ class VKShaderInterface : public ShaderInterface {
VKPushConstantsLayout push_constants_layout_;
public:
/**
* When the push constants fallback is used, this name will be used in the GLSL source.
*/
static constexpr StringRefNull PUSH_CONSTANTS_FALLBACK_NAME = StringRefNull(
"push_constants_fallback", 23);
static constexpr size_t PUSH_CONSTANTS_FALLBACK_NAME_LEN = PUSH_CONSTANTS_FALLBACK_NAME.size();
@ -46,6 +49,7 @@ class VKShaderInterface : public ShaderInterface {
const VKDescriptorSet::Location descriptor_set_location(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
/** Get the VKPushConstantsLayout of the shader.*/
const VKPushConstantsLayout &push_constants_layout_get() const
{
return push_constants_layout_;