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
9 changed files with 156 additions and 158 deletions
Showing only changes of commit d20bf06825 - Show all commits

View File

@ -71,15 +71,15 @@ void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_
/* Update push constants based on their storage type.*/
switch (push_constants.layout_get().storage_type_get()) {
Jeroen-Bakker marked this conversation as resolved
Review

I may be missing something, but this whole switch block feels like it does not belong here. It looks way too specific to me.

Would rather see that logic as part of the VKPushConstants class itself, but no idea if this is doable in practice... At the very least would have it in a dedicated util function of VKBackend otherwise?

I may be missing something, but this whole `switch` block feels like it does not belong here. It looks way too specific to me. Would rather see that logic as part of the `VKPushConstants` class itself, but no idea if this is doable in practice... At the very least would have it in a dedicated util function of `VKBackend` otherwise?
Review

Yes you're right, will move this part into a method of VKPushConstants.

Yes you're right, will move this part into a method of VKPushConstants.
case VKPushConstantsLayout::StorageType::NONE:
case VKPushConstants::StorageType::NONE:
break;
case VKPushConstantsLayout::StorageType::PUSH_CONSTANTS:
case VKPushConstants::StorageType::PUSH_CONSTANTS:
command_buffer.push_constants(
push_constants, shader->vk_pipeline_layout_get(), VK_SHADER_STAGE_ALL);
break;
case VKPushConstantsLayout::StorageType::UNIFORM_BUFFER:
case VKPushConstants::StorageType::UNIFORM_BUFFER:
push_constants.update_uniform_buffer();
descriptor_set.bind(push_constants.uniform_buffer_get(),
push_constants.layout_get().storage_buffer_binding_get());

View File

@ -77,7 +77,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
const VkShaderStageFlags vk_shader_stages)
{
BLI_assert(push_constants.layout_get().storage_type_get() ==
VKPushConstantsLayout::StorageType::PUSH_CONSTANTS);
VKPushConstants::StorageType::PUSH_CONSTANTS);
vkCmdPushConstants(vk_command_buffer_,
vk_pipeline_layout,
vk_shader_stages,

View File

@ -29,11 +29,12 @@ VKPipeline::~VKPipeline()
}
}
VKPipeline VKPipeline::create_compute_pipeline(VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layout,
const VKPushConstantsLayout &push_constants_layout)
VKPipeline VKPipeline::create_compute_pipeline(
VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layout,
const VKPushConstants::Layout &push_constants_layout)
{
VK_ALLOCATION_CALLBACKS
VkDevice vk_device = context.device_get();

View File

@ -44,7 +44,7 @@ class VKPipeline : NonCopyable {
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layouts,
const VKPushConstantsLayout &push_constants_layout);
const VKPushConstants::Layout &push_constants_layout);
VKDescriptorSet &descriptor_set_get()
{

View File

@ -15,14 +15,14 @@
namespace blender::gpu {
template<typename Layout>
static VKPushConstantsLayout::PushConstantLayout init_constant(
static VKPushConstants::Layout::PushConstant init_constant(
const shader::ShaderCreateInfo::PushConst &push_constant,
const ShaderInput &shader_input,
uint32_t *r_offset)
{
align<Layout>(push_constant.type, push_constant.array_size, r_offset);
VKPushConstantsLayout::PushConstantLayout layout;
VKPushConstants::Layout::PushConstant layout;
layout.location = shader_input.location;
layout.type = push_constant.type;
layout.array_size = push_constant.array_size;
@ -45,7 +45,7 @@ uint32_t struct_size(Span<shader::ShaderCreateInfo::PushConst> push_constants)
return offset;
}
VKPushConstantsLayout::StorageType VKPushConstantsLayout::determine_storage_type(
VKPushConstants::StorageType VKPushConstants::Layout::determine_storage_type(
const shader::ShaderCreateInfo &info, const VkPhysicalDeviceLimits &vk_physical_device_limits)
{
if (info.push_constants_.is_empty()) {
@ -60,7 +60,7 @@ VKPushConstantsLayout::StorageType VKPushConstantsLayout::determine_storage_type
template<typename Layout>
void init_struct(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
Vector<VKPushConstantsLayout::PushConstantLayout> &r_struct,
Vector<VKPushConstants::Layout::PushConstant> &r_struct,
uint32_t *r_offset)
{
for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
@ -70,10 +70,10 @@ void init_struct(const shader::ShaderCreateInfo &info,
align_end_of_struct<Std140>(r_offset);
}
void VKPushConstantsLayout::init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
const StorageType storage_type,
const VKDescriptorSet::Location location)
void VKPushConstants::Layout::init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
const StorageType storage_type,
const VKDescriptorSet::Location location)
{
BLI_assert(push_constants.is_empty());
storage_type_ = storage_type;
@ -88,10 +88,9 @@ void VKPushConstantsLayout::init(const shader::ShaderCreateInfo &info,
}
}
const VKPushConstantsLayout::PushConstantLayout *VKPushConstantsLayout::find(
int32_t location) const
const VKPushConstants::Layout::PushConstant *VKPushConstants::Layout::find(int32_t location) const
{
for (const PushConstantLayout &push_constant : push_constants) {
for (const PushConstant &push_constant : push_constants) {
if (push_constant.location == location) {
return &push_constant;
}
@ -100,16 +99,16 @@ const VKPushConstantsLayout::PushConstantLayout *VKPushConstantsLayout::find(
}
VKPushConstants::VKPushConstants() = default;
VKPushConstants::VKPushConstants(const VKPushConstantsLayout *layout) : layout_(layout)
VKPushConstants::VKPushConstants(const Layout *layout) : layout_(layout)
{
data_ = MEM_mallocN(layout->size_in_bytes(), __func__);
switch (layout_->storage_type_get()) {
case VKPushConstantsLayout::StorageType::UNIFORM_BUFFER:
case StorageType::UNIFORM_BUFFER:
uniform_buffer_ = new VKUniformBuffer(layout_->size_in_bytes(), __func__);
break;
case VKPushConstantsLayout::StorageType::PUSH_CONSTANTS:
case VKPushConstantsLayout::StorageType::NONE:
case StorageType::PUSH_CONSTANTS:
case StorageType::NONE:
break;
}
}
@ -149,7 +148,7 @@ VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other)
void VKPushConstants::update_uniform_buffer()
{
BLI_assert(layout_->storage_type_get() == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER);
BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER);
BLI_assert(uniform_buffer_ != nullptr);
BLI_assert(data_ != nullptr);
uniform_buffer_->update(data_);
@ -157,7 +156,7 @@ void VKPushConstants::update_uniform_buffer()
VKUniformBuffer &VKPushConstants::uniform_buffer_get()
{
BLI_assert(layout_->storage_type_get() == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER);
BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER);
BLI_assert(uniform_buffer_ != nullptr);
return *uniform_buffer_;
}

View File

@ -30,108 +30,6 @@ namespace blender::gpu {
class VKShaderInterface;
class VKUniformBuffer;
/**
* Describe the layout of the push constants and the storage type that should be used.
*/
struct VKPushConstantsLayout {
/* Different methods to store push constants.*/
enum class StorageType {
/** Push constants aren't in use.*/
NONE,
/** Store push constants as regular vulkan push constants.*/
PUSH_CONSTANTS,
/**
* Fallback when push constants doesn't meet the device requirements.
*/
UNIFORM_BUFFER,
};
static constexpr StorageType STORAGE_TYPE_DEFAULT = StorageType::PUSH_CONSTANTS;
static constexpr StorageType STORAGE_TYPE_FALLBACK = StorageType::UNIFORM_BUFFER;
struct PushConstantLayout {
/* Used as lookup based on ShaderInput.*/
int32_t location;
/** Offset in the push constant data (in bytes). */
uint32_t offset;
shader::Type type;
int array_size;
};
private:
Vector<PushConstantLayout> push_constants;
uint32_t size_in_bytes_ = 0;
StorageType storage_type_ = StorageType::NONE;
/**
* Binding index in the descriptor set when the push constants use an uniform buffer.
*/
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,
VKDescriptorSet::Location location);
/**
* Return the storage type that is used.
*/
StorageType storage_type_get() const
{
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
{
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.
*
@ -145,14 +43,118 @@ struct VKPushConstantsLayout {
* data.
*/
class VKPushConstants : NonCopyable {
public:
/** Different methods to store push constants.*/
enum class StorageType {
/** Push constants aren't in use.*/
NONE,
/** Store push constants as regular vulkan push constants.*/
PUSH_CONSTANTS,
/**
* Fallback when push constants doesn't meet the device requirements.
*/
UNIFORM_BUFFER,
};
/**
* Describe the layout of the push constants and the storage type that should be used.
*/
struct Layout {
static constexpr StorageType STORAGE_TYPE_DEFAULT = StorageType::PUSH_CONSTANTS;
static constexpr StorageType STORAGE_TYPE_FALLBACK = StorageType::UNIFORM_BUFFER;
struct PushConstant {
/* Used as lookup based on ShaderInput.*/
int32_t location;
/** Offset in the push constant data (in bytes). */
uint32_t offset;
shader::Type type;
int array_size;
};
private:
Vector<PushConstant> push_constants;
uint32_t size_in_bytes_ = 0;
StorageType storage_type_ = StorageType::NONE;
/**
* Binding index in the descriptor set when the push constants use an uniform buffer.
*/
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,
VKDescriptorSet::Location location);
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 the storage type that is used.
*/
StorageType storage_type_get() const
{
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
{
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 PushConstant *find(int32_t location) const;
};
private:
const VKPushConstantsLayout *layout_ = nullptr;
const Layout *layout_ = nullptr;
void *data_ = nullptr;
VKUniformBuffer *uniform_buffer_ = nullptr;
public:
VKPushConstants();
VKPushConstants(const VKPushConstantsLayout *layout);
VKPushConstants(const Layout *layout);
VKPushConstants(VKPushConstants &&other);
virtual ~VKPushConstants();
@ -163,7 +165,7 @@ class VKPushConstants : NonCopyable {
return 0;
}
const VKPushConstantsLayout &layout_get() const
const Layout &layout_get() const
{
return *layout_;
}
@ -212,14 +214,12 @@ class VKPushConstants : NonCopyable {
int32_t array_size,
const T *input_data)
{
const VKPushConstantsLayout::PushConstantLayout *push_constant_layout = layout_->find(
location);
const Layout::PushConstant *push_constant_layout = layout_->find(location);
BLI_assert(push_constant_layout);
uint8_t *bytes = static_cast<uint8_t *>(data_);
T *dst = static_cast<T *>(static_cast<void *>(&bytes[push_constant_layout->offset]));
if (layout_->storage_type_get() == VKPushConstantsLayout::StorageType::PUSH_CONSTANTS ||
array_size == 0) {
if (layout_->storage_type_get() == StorageType::PUSH_CONSTANTS || array_size == 0) {
BLI_assert_msg(push_constant_layout->offset + comp_len * array_size * sizeof(T) <=
layout_->size_in_bytes(),
"Tried to write outside the push constant allocated memory.");

View File

@ -756,10 +756,9 @@ bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
pipeline_info.pSetLayouts = &layout_;
/* Setup push constants. */
const VKPushConstantsLayout &push_constants_layout =
const VKPushConstants::Layout &push_constants_layout =
shader_interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::PUSH_CONSTANTS) {
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::PUSH_CONSTANTS) {
push_constant_range.offset = 0;
push_constant_range.size = push_constants_layout.size_in_bytes();
push_constant_range.stageFlags = VK_SHADER_STAGE_ALL;
@ -886,10 +885,10 @@ static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
}
static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
const VKPushConstantsLayout &push_constants_layout)
const VKPushConstants::Layout &push_constants_layout)
{
BLI_assert(push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::UNIFORM_BUFFER);
VKPushConstants::StorageType::UNIFORM_BUFFER);
VkDescriptorSetLayoutBinding binding = {};
binding.binding = push_constants_layout.storage_buffer_binding_get();
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@ -911,9 +910,8 @@ static void add_descriptor_set_layout_bindings(
}
/* Add push constants to the descriptor when push constants are stored in an uniform buffer.*/
const VKPushConstantsLayout &push_constants_layout = interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::UNIFORM_BUFFER) {
const VKPushConstants::Layout &push_constants_layout = interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::UNIFORM_BUFFER) {
r_bindings.append(create_descriptor_set_layout_binding(push_constants_layout));
}
}
@ -938,7 +936,7 @@ static bool descriptor_sets_needed(const VKShaderInterface &shader_interface,
{
return !info.pass_resources_.is_empty() || !info.batch_resources_.is_empty() ||
shader_interface.push_constants_layout_get().storage_type_get() ==
VKPushConstantsLayout::StorageType::UNIFORM_BUFFER;
VKPushConstants::StorageType::UNIFORM_BUFFER;
}
bool VKShader::finalize_descriptor_set_layouts(VkDevice vk_device,
@ -1038,15 +1036,15 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co
}
/* Push constants. */
const VKPushConstantsLayout &push_constants_layout = interface.push_constants_layout_get();
const VKPushConstantsLayout::StorageType push_constants_storage =
const VKPushConstants::Layout &push_constants_layout = interface.push_constants_layout_get();
const VKPushConstants::StorageType push_constants_storage =
push_constants_layout.storage_type_get();
if (push_constants_storage != VKPushConstantsLayout::StorageType::NONE) {
if (push_constants_storage != VKPushConstants::StorageType::NONE) {
ss << "\n/* Push Constants. */\n";
if (push_constants_storage == VKPushConstantsLayout::StorageType::PUSH_CONSTANTS) {
if (push_constants_storage == VKPushConstants::StorageType::PUSH_CONSTANTS) {
ss << "layout(push_constant) uniform constants\n";
}
else if (push_constants_storage == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER) {
else if (push_constants_storage == VKPushConstants::StorageType::UNIFORM_BUFFER) {
ss << "layout(binding = " << push_constants_layout.storage_buffer_binding_get()
<< ", std140) uniform constants\n";
}

View File

@ -45,9 +45,9 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
/* Reserve 1 storage buffer for push constants fallback. */
size_t names_size = info.interface_names_size_;
VKContext &context = *VKContext::get();
const VKPushConstantsLayout::StorageType push_constants_storage_type =
VKPushConstantsLayout::determine_storage_type(info, context.physical_device_limits_get());
if (push_constants_storage_type == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER) {
const VKPushConstants::StorageType push_constants_storage_type =
VKPushConstants::Layout::determine_storage_type(info, context.physical_device_limits_get());
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
ubo_len_++;
names_size += PUSH_CONSTANTS_FALLBACK_NAME_LEN + 1;
}
@ -73,7 +73,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
}
/* Add push constant when using uniform buffer as fallback. */
int32_t push_constants_fallback_location = -1;
if (push_constants_storage_type == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER) {
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
copy_input_name(input, PUSH_CONSTANTS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
input->location = input->binding = -1;
input++;
@ -138,7 +138,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
/* Post initializing push constants.*/
/* Determine the binding location of push constants fallback buffer.*/
int32_t push_constant_descriptor_set_location = -1;
if (push_constants_storage_type == VKPushConstantsLayout::StorageType::UNIFORM_BUFFER) {
if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
push_constant_descriptor_set_location = descriptor_set_location++;
const ShaderInput *push_constant_input = ubo_get(PUSH_CONSTANTS_FALLBACK_NAME.c_str());
descriptor_set_location_update(push_constant_input, push_constants_fallback_location);

View File

@ -29,7 +29,7 @@ class VKShaderInterface : public ShaderInterface {
uint32_t image_offset_ = 0;
Array<VKDescriptorSet::Location> descriptor_set_locations_;
VKPushConstantsLayout push_constants_layout_;
VKPushConstants::Layout push_constants_layout_;
public:
/**
@ -49,8 +49,8 @@ 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
/** Get the Layout of the shader.*/
const VKPushConstants::Layout &push_constants_layout_get() const
{
return push_constants_layout_;
}