/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2023 Blender Foundation. All rights reserved. */ /** \file * \ingroup gpu */ #include "vk_push_constants.hh" #include "vk_backend.hh" #include "vk_context.hh" #include "vk_memory_layout.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" #include "vk_storage_buffer.hh" #include "vk_uniform_buffer.hh" namespace blender::gpu { template static VKPushConstants::Layout::PushConstant init_constant( const shader::ShaderCreateInfo::PushConst &push_constant, const ShaderInput &shader_input, uint32_t *r_offset) { align(push_constant.type, push_constant.array_size, r_offset); VKPushConstants::Layout::PushConstant layout; layout.location = shader_input.location; layout.type = push_constant.type; layout.array_size = push_constant.array_size; layout.offset = *r_offset; reserve(push_constant.type, push_constant.array_size, r_offset); return layout; } template uint32_t struct_size(Span push_constants) { uint32_t offset = 0; for (const shader::ShaderCreateInfo::PushConst &push_constant : push_constants) { align(push_constant.type, push_constant.array_size, &offset); reserve(push_constant.type, push_constant.array_size, &offset); } align_end_of_struct(&offset); return offset; } VKPushConstants::StorageType VKPushConstants::Layout::determine_storage_type( const shader::ShaderCreateInfo &info, const VkPhysicalDeviceLimits &vk_physical_device_limits) { if (info.push_constants_.is_empty()) { return StorageType::NONE; } uint32_t size = struct_size(info.push_constants_); return size <= vk_physical_device_limits.maxPushConstantsSize ? STORAGE_TYPE_DEFAULT : STORAGE_TYPE_FALLBACK; } template void init_struct(const shader::ShaderCreateInfo &info, const VKShaderInterface &interface, Vector &r_struct, uint32_t *r_offset) { for (const shader::ShaderCreateInfo::PushConst &push_constant : info.push_constants_) { const ShaderInput *shader_input = interface.uniform_get(push_constant.name.c_str()); r_struct.append(init_constant(push_constant, *shader_input, r_offset)); } align_end_of_struct(r_offset); } 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; size_in_bytes_ = 0; if (storage_type == StorageType::UNIFORM_BUFFER) { descriptor_set_location_ = location; init_struct(info, interface, push_constants, &size_in_bytes_); } else { init_struct(info, interface, push_constants, &size_in_bytes_); } } const VKPushConstants::Layout::PushConstant *VKPushConstants::Layout::find(int32_t location) const { for (const PushConstant &push_constant : push_constants) { if (push_constant.location == location) { return &push_constant; } } return nullptr; } VKPushConstants::VKPushConstants() = default; VKPushConstants::VKPushConstants(const Layout *layout) : layout_(layout) { data_ = MEM_mallocN(layout->size_in_bytes(), __func__); } VKPushConstants::VKPushConstants(VKPushConstants &&other) : layout_(other.layout_) { data_ = other.data_; other.data_ = nullptr; } VKPushConstants::~VKPushConstants() { if (data_ != nullptr) { MEM_freeN(data_); data_ = nullptr; } } VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other) { layout_ = other.layout_; data_ = other.data_; other.data_ = nullptr; return *this; } void VKPushConstants::update(VKContext &context) { VKShader *shader = static_cast(context.shader); VKCommandBuffer &command_buffer = context.command_buffer_get(); VKPipeline &pipeline = shader->pipeline_get(); BLI_assert_msg(&pipeline.push_constants_get() == this, "Invalid state detected. Push constants doesn't belong to the active shader of " "the given context."); VKDescriptorSetTracker &descriptor_set = pipeline.descriptor_set_get(); switch (layout_get().storage_type_get()) { case VKPushConstants::StorageType::NONE: break; case VKPushConstants::StorageType::PUSH_CONSTANTS: command_buffer.push_constants(*this, shader->vk_pipeline_layout_get(), VK_SHADER_STAGE_ALL); break; case VKPushConstants::StorageType::UNIFORM_BUFFER: update_uniform_buffer(); descriptor_set.bind(*uniform_buffer_get(), layout_get().descriptor_set_location_get()); break; } } void VKPushConstants::update_uniform_buffer() { BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER); BLI_assert(data_ != nullptr); VKContext &context = *VKContext::get(); std::unique_ptr &uniform_buffer = tracked_resource_for(context, is_dirty_); uniform_buffer->update(data_); is_dirty_ = false; } std::unique_ptr &VKPushConstants::uniform_buffer_get() { BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER); return active_resource(); } std::unique_ptr VKPushConstants::create_resource(VKContext & /*context*/) { return std::make_unique(layout_->size_in_bytes(), __func__); } } // namespace blender::gpu