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
16 changed files with 337 additions and 73 deletions
Showing only changes of commit 797320c96b - Show all commits

View File

@ -55,6 +55,7 @@ GPU_SHADER_CREATE_INFO(gpu_compute_ssbo_binding_test)
.compute_source("gpu_compute_dummy_test.glsl")
.do_static_compilation(true);
/* Push constants*/
GPU_SHADER_CREATE_INFO(gpu_push_constants_base_test)
.local_group_size(1)
.storage_buf(0, Qualifier::WRITE, "float", "data_out[]")
@ -68,6 +69,23 @@ GPU_SHADER_CREATE_INFO(gpu_push_constants_packed_test)
.push_constant(Type::VEC4, "vec4_in")
.do_static_compilation(true);
/* Push constants size test. */
GPU_SHADER_CREATE_INFO(gpu_push_constants_128bytes_test)
.additional_info("gpu_push_constants_packed_test")
.push_constant(Type::FLOAT, "filler", 20)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_push_constants_256bytes_test)
.additional_info("gpu_push_constants_128bytes_test")
.push_constant(Type::FLOAT, "filler2", 32)
.do_static_compilation(true);
/* It is expected that this shader will use uniform buffers and not push constants.*/
Jeroen-Bakker marked this conversation as resolved
Review

Remove line as this is vulkan specific.

Remove line as this is vulkan specific.
GPU_SHADER_CREATE_INFO(gpu_push_constants_512bytes_test)
.additional_info("gpu_push_constants_256bytes_test")
.push_constant(Type::FLOAT, "filler3", 64)
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_shadow_test)
.fragment_source("eevee_shadow_test.glsl")
.additional_info("gpu_shader_test")

View File

@ -77,4 +77,22 @@ static void test_push_constants_packed()
}
GPU_TEST(push_constants_packed)
static void test_push_constants_128bytes()
{
push_constants("gpu_push_constants_128bytes_test");
}
GPU_TEST(push_constants_128bytes)
static void test_push_constants_256bytes()
{
push_constants("gpu_push_constants_256bytes_test");
}
GPU_TEST(push_constants_256bytes)
static void test_push_constants_512bytes()
{
push_constants("gpu_push_constants_512bytes_test");
}
GPU_TEST(push_constants_512bytes)
} // namespace blender::gpu::tests

View File

@ -67,11 +67,27 @@ void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_
VKCommandBuffer &command_buffer = context.command_buffer_get();
VKPipeline &pipeline = shader->pipeline_get();
VKDescriptorSet &descriptor_set = pipeline.descriptor_set_get();
VKPushConstants &push_constants = pipeline.push_constants_get();
/* Update push constants based on their storage type.*/
switch (push_constants.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:
break;
case VKPushConstantsLayout::StorageType::PUSH_CONSTANTS:
command_buffer.push_constants(
push_constants, shader->vk_pipeline_layout_get(), VK_SHADER_STAGE_ALL);
break;
case VKPushConstantsLayout::StorageType::STORAGE_BUFFER:
push_constants.update_storage_buffer(context.device_get());
descriptor_set.bind(push_constants.storage_buffer_get(),
push_constants.storage_buffer_binding_get());
break;
}
descriptor_set.update(context.device_get());
command_buffer.bind(
descriptor_set, shader->vk_pipeline_layout_get(), VK_PIPELINE_BIND_POINT_COMPUTE);
command_buffer.push_constants(
pipeline.push_constants_get(), shader->vk_pipeline_layout_get(), VK_SHADER_STAGE_ALL);
command_buffer.dispatch(groups_x_len, groups_y_len, groups_z_len);
}

View File

@ -6,6 +6,7 @@
*/
#include "vk_buffer.hh"
#include "vk_context.hh"
namespace blender::gpu {

View File

@ -10,11 +10,11 @@
#include "gpu_context_private.hh"
#include "vk_common.hh"
#include "vk_context.hh"
#include "vk_mem_alloc.h"
namespace blender::gpu {
class VKContext;
/**
* Class for handing vulkan buffers (allocation/updating/binding).

View File

@ -10,6 +10,7 @@
#include "vk_context.hh"
#include "vk_memory.hh"
#include "vk_texture.hh"
#include "vk_pipeline.hh"
#include "BLI_assert.h"
@ -75,7 +76,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
const VkPipelineLayout vk_pipeline_layout,
const VkShaderStageFlags vk_shader_stages)
{
if (push_constants.size_in_bytes() == 0) {
if (push_constants.storage_type_get() != VKPushConstantsLayout::StorageType::PUSH_CONSTANTS) {
return;
}
vkCmdPushConstants(vk_command_buffer_,

View File

@ -8,12 +8,15 @@
#pragma once
#include "vk_common.hh"
#include "vk_pipeline.hh"
#include "BLI_utility_mixins.hh"
namespace blender::gpu {
class VKBuffer;
class VKTexture;
class VKPushConstants;
class VKPipeline;
class VKDescriptorSet;
/** Command buffer to keep track of the life-time of a command buffer.*/
class VKCommandBuffer : NonCopyable, NonMovable {

View File

@ -50,9 +50,8 @@ class VKDescriptorSet : NonCopyable {
*/
uint32_t binding;
Location() = default;
public:
Location() = default;
Location(const ShaderInput *shader_input) : binding(shader_input->location)
{
}

View File

@ -33,7 +33,7 @@ VKPipeline VKPipeline::create_compute_pipeline(VKContext &context,
VkShaderModule compute_module,
VkDescriptorSetLayout &descriptor_set_layout,
VkPipelineLayout &pipeline_layout,
VKPushConstantsLayout &push_constants_layout)
const VKPushConstantsLayout &push_constants_layout)
{
VK_ALLOCATION_CALLBACKS
VkDevice vk_device = context.device_get();
@ -52,13 +52,12 @@ VKPipeline VKPipeline::create_compute_pipeline(VKContext &context,
if (vkCreateComputePipelines(
vk_device, nullptr, 1, &pipeline_info, vk_allocation_callbacks, &vk_pipeline) !=
VK_SUCCESS) {
return VKPipeline();
//return VKPipeline();
Jeroen-Bakker marked this conversation as resolved
Review

This should be fixed.

This should be fixed.
}
VKDescriptorSet descriptor_set = context.descriptor_pools_get().allocate(descriptor_set_layout);
VKPushConstants push_constants(push_constants_layout);
return VKPipeline(
vk_pipeline, std::move(descriptor_set), std::move(push_constants));
VKPushConstants push_constants(&push_constants_layout);
return VKPipeline(vk_pipeline, std::move(descriptor_set), std::move(push_constants));
}
VkPipeline VKPipeline::vk_handle() const

View File

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

View File

@ -6,15 +6,17 @@
*/
#include "vk_push_constants.hh"
#include "vk_backend.hh"
#include "vk_shader_interface.hh"
namespace blender::gpu {
static uint32_t to_component_size(const shader::Type /*type*/)
constexpr uint32_t to_component_size(const shader::Type /*type*/)
{
return 4;
}
static uint32_t to_num_components(const shader::Type type)
constexpr uint32_t to_num_components(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
@ -43,7 +45,7 @@ static uint32_t to_num_components(const shader::Type type)
return 0;
}
static uint32_t to_alignment(const shader::Type type)
constexpr uint32_t to_alignment(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
@ -70,38 +72,71 @@ static uint32_t to_alignment(const shader::Type type)
return 0;
}
static void align(const shader::ShaderCreateInfo::PushConst &push_constant, uint32_t *r_offset)
{
uint32_t alignment = to_alignment(push_constant.type);
uint32_t alignment_mask = alignment - 1;
uint32_t offset = *r_offset;
if ((offset & alignment_mask) != 0) {
offset &= ~alignment_mask;
offset += alignment;
*r_offset = offset;
}
}
static void reserve(const shader::ShaderCreateInfo::PushConst &push_constant, uint32_t *r_offset)
{
uint32_t size = to_num_components(push_constant.type) * to_component_size(push_constant.type);
if (push_constant.array_size != 0) {
size *= push_constant.array_size;
}
*r_offset += size;
}
static VKPushConstantsLayout::PushConstantLayout init_constant(
const shader::ShaderCreateInfo::PushConst &push_constant,
const ShaderInput &shader_input,
uint32_t *r_offset)
{
align(push_constant, r_offset);
VKPushConstantsLayout::PushConstantLayout layout;
layout.location = shader_input.location;
layout.type = push_constant.type;
layout.array_size = push_constant.array_size;
layout.offset = *r_offset;
/* Perform alignment. */
uint32_t alignment = to_alignment(push_constant.type);
uint32_t alignment_mask = alignment - 1;
if ((layout.offset & alignment_mask) != 0) {
layout.offset &= ~alignment_mask;
layout.offset += alignment;
}
uint32_t size = to_num_components(push_constant.type) * to_component_size(push_constant.type);
if (push_constant.array_size != 0) {
size *= push_constant.array_size;
}
*r_offset = layout.offset + size;
reserve(push_constant, r_offset);
return layout;
}
VKPushConstantsLayout::StorageType VKPushConstantsLayout::determine_storage_type(
const shader::ShaderCreateInfo &info, const VkPhysicalDeviceLimits &vk_physical_device_limits)
{
if (info.push_constants_.is_empty()) {
return StorageType::NONE;
}
uint32_t offset = 0;
for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
align(push_constant, &offset);
reserve(push_constant, &offset);
}
return offset <= vk_physical_device_limits.maxPushConstantsSize ? StorageType::PUSH_CONSTANTS :
StorageType::STORAGE_BUFFER;
}
void VKPushConstantsLayout::init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface)
const VKShaderInterface &interface,
const StorageType storage_type,
const ShaderInput *shader_input)
{
BLI_assert(push_constants.is_empty());
storage_type_ = storage_type;
if (storage_type == StorageType::STORAGE_BUFFER) {
BLI_assert(shader_input);
storage_buffer_binding_ = VKDescriptorSet::Location(shader_input);
}
uint32_t offset = 0;
for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
const ShaderInput *shader_input = interface.uniform_get(push_constant.name.c_str());
@ -121,16 +156,22 @@ const VKPushConstantsLayout::PushConstantLayout *VKPushConstantsLayout::find(
}
return nullptr;
}
VKPushConstants::VKPushConstants(VKPushConstantsLayout &layout) : layout_(layout)
VKPushConstants::VKPushConstants() = default;
VKPushConstants::VKPushConstants(const VKPushConstantsLayout *layout) : layout_(layout)
{
data_ = MEM_mallocN(layout.size_in_bytes(), __func__);
data_ = MEM_mallocN(layout->size_in_bytes(), __func__);
if (storage_type_get() == VKPushConstantsLayout::StorageType::STORAGE_BUFFER) {
storage_buffer_ = new VKStorageBuffer(size_in_bytes(), GPU_USAGE_DYNAMIC, __func__);
}
}
VKPushConstants::VKPushConstants(VKPushConstants &&other) : layout_(other.layout_)
{
data_ = other.data_;
other.data_ = nullptr;
storage_buffer_ = other.storage_buffer_;
other.storage_buffer_ = nullptr;
}
VKPushConstants::~VKPushConstants()
@ -139,15 +180,37 @@ VKPushConstants::~VKPushConstants()
MEM_freeN(data_);
data_ = nullptr;
}
delete storage_buffer_;
storage_buffer_ = nullptr;
}
VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other)
{
layout_ = other.layout_;
data_ = other.data_;
other.data_ = nullptr;
storage_buffer_ = other.storage_buffer_;
other.storage_buffer_ = nullptr;
return *this;
}
void VKPushConstants::update_storage_buffer(VkDevice /*vk_device*/)
{
BLI_assert(storage_type_get() == VKPushConstantsLayout::StorageType::STORAGE_BUFFER);
BLI_assert(storage_buffer_ != nullptr);
BLI_assert(data_ != nullptr);
storage_buffer_->update(data_);
}
VKStorageBuffer &VKPushConstants::storage_buffer_get()
{
BLI_assert(storage_type_get() == VKPushConstantsLayout::StorageType::STORAGE_BUFFER);
BLI_assert(storage_buffer_ != nullptr);
return *storage_buffer_;
}
} // namespace blender::gpu

View File

@ -3,6 +3,17 @@
/** \file
* \ingroup gpu
*
* Push constants is a way to quickly provide a small amount of uniform data to shaders. It should
* be much quicker than UBOs but a huge limitation is the size of data - spec requires 128 bytes to
* be available for a push constant range. Hardware vendors may support more, but compared to other
* means it is still very little (for example 256 bytes).
*
* Due to this size requirements we try to use push constants when it fits on the device. If it
* doesn't fit we fallback to use an uniform buffer.
*
* Shader developers are responsible to fine-tune the performance of the shader. One way to do this
* is to tailor what will be sent as a push constant to keep the push constants within the limits.
*/
#pragma once
@ -10,12 +21,27 @@
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "gpu_shader_create_info.hh"
#include "vk_common.hh"
#include "vk_shader_interface.hh"
//#include "vk_context.hh"
#include "vk_descriptor_set.hh"
#include "vk_storage_buffer.hh"
namespace blender::gpu {
class VKShaderInterface;
/**
* Describe the layout of the push constants and the storage type that should be used.
*/
struct VKPushConstantsLayout {
/* Should the push constant use regular push constants or a buffer.*/
enum class StorageType {
NONE,
PUSH_CONSTANTS,
STORAGE_BUFFER,
};
struct PushConstantLayout {
/* TODO: location requires sequential lookups, we should make the location index based for
* quicker access. */
@ -29,10 +55,34 @@ struct VKPushConstantsLayout {
private:
Vector<PushConstantLayout> push_constants;
uint32_t size_in_bytes_;
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:
void init(const shader::ShaderCreateInfo &info, const VKShaderInterface &interface);
static StorageType determine_storage_type(
const shader::ShaderCreateInfo &info,
const VkPhysicalDeviceLimits &vk_physical_device_limits);
void init(const shader::ShaderCreateInfo &info,
const VKShaderInterface &interface,
StorageType storage_type,
const ShaderInput *shader_input);
/**
* Return the storage type that is used.
*/
StorageType storage_type_get() const
{
return storage_type_;
}
VKDescriptorSet::Location storage_buffer_binding_get() const
{
return storage_buffer_binding_;
}
uint32_t size_in_bytes() const
{
@ -42,16 +92,16 @@ struct VKPushConstantsLayout {
const PushConstantLayout *find(int32_t location) const;
};
static VKPushConstantsLayout dummy_layout;
class VKPushConstants : NonCopyable {
private:
VKPushConstantsLayout &layout_ = dummy_layout;
const VKPushConstantsLayout *layout_ = nullptr;
void *data_ = nullptr;
VKStorageBuffer *storage_buffer_ = nullptr;
public:
VKPushConstants() = default;
VKPushConstants(VKPushConstantsLayout &layout);
VKPushConstants();
VKPushConstants(const VKPushConstantsLayout *layout);
VKPushConstants(VKPushConstants &&other);
virtual ~VKPushConstants();
@ -64,9 +114,22 @@ class VKPushConstants : NonCopyable {
size_t size_in_bytes() 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 layout_.size_in_bytes();
return layout_->size_in_bytes();
}
VKPushConstantsLayout::StorageType storage_type_get() const
{
return layout_->storage_type_get();
}
VKDescriptorSet::Location storage_buffer_binding_get() const
{
return layout_->storage_buffer_binding_get();
}
void update_storage_buffer(VkDevice vk_device);
VKStorageBuffer &storage_buffer_get();
const void *data() const
{
return data_;
@ -78,14 +141,15 @@ class VKPushConstants : NonCopyable {
int32_t array_size,
const T *input_data)
{
const VKPushConstantsLayout::PushConstantLayout *push_constant_layout = layout_.find(location);
const VKPushConstantsLayout::PushConstantLayout *push_constant_layout = layout_->find(
location);
if (push_constant_layout == nullptr) {
/* Currently the builtin uniforms are set using a predefined location each time a shader is
* bound.*/
/* TODO: Currently the builtin uniforms are set using a predefined location each time a
* shader is bound. This needs to be fixed in the VKShaderInterface.*/
return;
}
BLI_assert_msg(push_constant_layout->offset + comp_len * array_size * sizeof(T) <=
layout_.size_in_bytes(),
layout_->size_in_bytes(),
"Tried to write outside the push constant allocated memory.");
uint8_t *bytes = static_cast<uint8_t *>(data_);
T *dst = static_cast<T *>(static_cast<void *>(&bytes[push_constant_layout->offset]));

View File

@ -680,8 +680,7 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
if (!finalize_descriptor_set_layouts(vk_device, *vk_interface, *info)) {
return false;
}
push_constants_layout_.init(*info, *vk_interface);
if (!finalize_pipeline_layout(vk_device, *info)) {
if (!finalize_pipeline_layout(vk_device, *vk_interface)) {
return false;
}
@ -700,7 +699,11 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
BLI_assert(fragment_module_ == VK_NULL_HANDLE);
BLI_assert(compute_module_ != VK_NULL_HANDLE);
compute_pipeline_ = VKPipeline::create_compute_pipeline(
*context_, compute_module_, layout_, pipeline_layout_, push_constants_layout_);
*context_,
compute_module_,
layout_,
pipeline_layout_,
vk_interface->push_constants_layout_get());
result = compute_pipeline_.is_valid();
}
@ -744,7 +747,7 @@ bool VKShader::finalize_graphics_pipeline(VkDevice /*vk_device */)
}
bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
const shader::ShaderCreateInfo & /*info*/)
const VKShaderInterface &shader_interface)
{
VK_ALLOCATION_CALLBACKS
@ -755,9 +758,14 @@ bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
pipeline_info.flags = 0;
pipeline_info.setLayoutCount = layout_count;
pipeline_info.pSetLayouts = &layout_;
if (push_constants_layout_.size_in_bytes() != 0) {
/* Setup push constants. */
const VKPushConstantsLayout &push_constants_layout =
shader_interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::PUSH_CONSTANTS) {
push_constant_range.offset = 0;
push_constant_range.size = push_constants_layout_.size_in_bytes();
push_constant_range.size = push_constants_layout.size_in_bytes();
push_constant_range.stageFlags = VK_SHADER_STAGE_ALL;
pipeline_info.pushConstantRangeCount = 1;
pipeline_info.pPushConstantRanges = &push_constant_range;
@ -881,6 +889,21 @@ static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
return binding;
}
static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
const VKPushConstantsLayout &push_constants_layout)
{
BLI_assert(push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::STORAGE_BUFFER);
VkDescriptorSetLayoutBinding binding = {};
binding.binding = push_constants_layout.storage_buffer_binding_get();
binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_ALL;
binding.pImmutableSamplers = nullptr;
return binding;
}
static void add_descriptor_set_layout_bindings(
const VKShaderInterface &interface,
const Vector<shader::ShaderCreateInfo::Resource> &resources,
@ -895,6 +918,13 @@ static void add_descriptor_set_layout_bindings(
r_bindings.append(create_descriptor_set_layout_binding(*shader_input, resource));
}
/* Add push constants to the descriptor when push constants are stored in a storage buffer.*/
const VKPushConstantsLayout &push_constants_layout = interface.push_constants_layout_get();
if (push_constants_layout.storage_type_get() ==
VKPushConstantsLayout::StorageType::STORAGE_BUFFER) {
r_bindings.append(create_descriptor_set_layout_binding(push_constants_layout));
}
}
Jeroen-Bakker marked this conversation as resolved
Review

Also check for uniform_buffer

Also check for uniform_buffer
static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
@ -912,11 +942,19 @@ static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
return set_info;
}
static bool descriptor_sets_needed(const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info)
{
return !info.pass_resources_.is_empty() || !info.batch_resources_.is_empty() ||
shader_interface.push_constants_layout_get().storage_type_get() ==
VKPushConstantsLayout::StorageType::STORAGE_BUFFER;
}
bool VKShader::finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info)
{
if (info.pass_resources_.is_empty() && info.batch_resources_.is_empty()) {
if (!descriptor_sets_needed(shader_interface, info)) {
return true;
}
@ -981,18 +1019,13 @@ void VKShader::uniform_float(int location, int comp_len, int array_size, const f
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
}
void VKShader::uniform_int(int /*location*/,
int /*comp_len*/,
int /*array_size*/,
const int * /*data*/)
void VKShader::uniform_int(int location, int comp_len, int array_size, const int *data)
{
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
}
std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const
{
if (info.name_ == "workbench_next_prepass_ptcloud_opaque_flat_texture_clip") {
printf("%s\n", info.name_.c_str());
}
VKShaderInterface interface;
interface.init(info);
std::stringstream ss;
@ -1013,9 +1046,19 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co
print_resource_alias(ss, res);
}
if (!info.push_constants_.is_empty()) {
/* Push constants. */
const VKPushConstantsLayout &push_constants_layout = interface.push_constants_layout_get();
const VKPushConstantsLayout::StorageType push_constants_storage =
push_constants_layout.storage_type_get();
if (push_constants_storage != VKPushConstantsLayout::StorageType::NONE) {
ss << "\n/* Push Constants. */\n";
ss << "layout(push_constant) uniform constants\n";
if (push_constants_storage == VKPushConstantsLayout::StorageType::PUSH_CONSTANTS) {
ss << "layout(push_constant) uniform constants\n";
}
else /* VKPushConstantsLayout::StorageType::STORAGE_BUFFER*/ {
ss << "layout(binding = " << push_constants_layout.storage_buffer_binding_get()
<< ", std430) buffer constants\n";
}
ss << "{\n";
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
ss << " " << to_string(uniform.type) << " pc_" << uniform.name;
@ -1044,12 +1087,6 @@ std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &i
ss << "layout(location = " << attr.index << ") ";
ss << "in " << to_string(attr.type) << " " << attr.name << ";\n";
}
/* NOTE(D4490): Fix a bug where shader without any vertex attributes do not behave correctly.
*/
if (GPU_type_matches_ex(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) &&
info.vertex_inputs_.is_empty()) {
ss << "in float gpu_dummy_workaround;\n";
}
ss << "\n/* Interfaces. */\n";
int location = 0;
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {
@ -1223,6 +1260,9 @@ VKPipeline &VKShader::pipeline_get()
const VKShaderInterface &VKShader::interface_get() const
{
BLI_assert_msg(interface != nullptr,
"Unable to access the shader interface when finalizing the shader, use the "
"instance created in the finalize method.");
return *static_cast<const VKShaderInterface *>(interface);
}

View File

@ -11,7 +11,7 @@
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_push_constants.hh"
#include "vk_pipeline.hh"
#include "BLI_string_ref.hh"
@ -28,7 +28,6 @@ class VKShader : public Shader {
bool compilation_failed_ = false;
VkDescriptorSetLayout layout_ = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
VKPushConstantsLayout push_constants_layout_;
VKPipeline compute_pipeline_;
public:
@ -80,7 +79,7 @@ class VKShader : public Shader {
bool finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface);
bool finalize_graphics_pipeline(VkDevice vk_device);
bool is_graphics_shader() const

View File

@ -6,6 +6,7 @@
*/
#include "vk_shader_interface.hh"
#include "vk_context.hh"
namespace blender::gpu {
@ -40,15 +41,26 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
break;
}
}
/* 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::STORAGE_BUFFER) {
ssbo_len_++;
names_size += PUSH_CONSTANTS_FALLBACK_NAME_LEN + 1;
}
/* Make sure that the image slots don't overlap with the sampler slots.*/
image_offset_ += 1;
image_offset_++;
int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_;
inputs_ = static_cast<ShaderInput *>(
MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__));
ShaderInput *input = inputs_;
name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer");
name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
uint32_t name_buffer_offset = 0;
int32_t location = 0;
@ -101,6 +113,23 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
}
}
/* Push constant post initialization.*/
/*
* When using storage buffer storage type we need to add it to the the name list here.
* Also determine the location as this is used inside the descriptor set as its binding number.
*/
int32_t push_constants_fallback_location = -1;
ShaderInput *push_constants_fallback_input = nullptr;
if (push_constants_storage_type == VKPushConstantsLayout::StorageType::STORAGE_BUFFER) {
copy_input_name(input, PUSH_CONSTANTS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
input->location = push_constants_fallback_location = location++;
input->binding = -1;
push_constants_fallback_input = input;
input++;
}
push_constants_layout_.init(
info, *this, push_constants_storage_type, push_constants_fallback_input);
sort_inputs();
}

View File

@ -10,6 +10,8 @@
#include "gpu_shader_create_info.hh"
#include "gpu_shader_interface.hh"
#include "vk_push_constants.hh"
namespace blender::gpu {
class VKShaderInterface : public ShaderInterface {
private:
@ -22,6 +24,13 @@ class VKShaderInterface : public ShaderInterface {
*/
uint32_t image_offset_ = 0;
VKPushConstantsLayout push_constants_layout_;
public:
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();
public:
VKShaderInterface() = default;
@ -35,5 +44,10 @@ class VKShaderInterface : public ShaderInterface {
const ShaderInput *shader_input_get(const shader::ShaderCreateInfo::Resource &resource) const;
const ShaderInput *shader_input_get(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
const VKPushConstantsLayout &push_constants_layout_get() const
{
return push_constants_layout_;
}
};
} // namespace blender::gpu