| // Copyright 2018 The clvk authors. |
| // |
| // 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. |
| |
| #include <algorithm> |
| |
| #include "clspv/Sampler.h" |
| |
| #include "kernel.hpp" |
| #include "memory.hpp" |
| |
| cl_ulong cvk_kernel::local_mem_size() const { |
| cl_ulong ret = m_program->workgroup_variables_size(); |
| |
| for (uint32_t i = 0; i < m_args.size(); i++) { |
| auto const& arg = m_args[i]; |
| if (arg.kind == kernel_argument_kind::local) { |
| ret += m_argument_values->local_arg_size(i); |
| } |
| } |
| |
| return ret; |
| } |
| |
| cl_int cvk_kernel::init() { |
| cl_int errcode; |
| m_entry_point = m_program->get_entry_point(m_name, &errcode); |
| if (!m_entry_point) { |
| return errcode; |
| } |
| |
| // Store a copy of the arguments |
| m_args = m_entry_point->args(); |
| |
| if (const auto* md = m_entry_point->image_metadata()) { |
| m_image_metadata = md; |
| } |
| |
| if (const auto* md = m_entry_point->sampler_metadata()) { |
| m_sampler_metadata = md; |
| } |
| |
| // Init argument values |
| m_argument_values = cvk_kernel_argument_values::create(m_entry_point); |
| if (m_argument_values == nullptr) { |
| return CL_OUT_OF_RESOURCES; |
| } |
| |
| return CL_SUCCESS; |
| } |
| |
| VkPipeline |
| cvk_kernel::create_pipeline(const cvk_spec_constant_map& spec_constants) { |
| return m_entry_point->create_pipeline(spec_constants); |
| } |
| |
| std::unique_ptr<cvk_kernel> cvk_kernel::clone(cl_int* errcode_ret) const { |
| |
| auto kernel = std::make_unique<cvk_kernel>(m_program, m_name.c_str()); |
| |
| *errcode_ret = kernel->init(); |
| |
| if (*errcode_ret != CL_SUCCESS) { |
| return nullptr; |
| } |
| |
| kernel->m_argument_values = |
| cvk_kernel_argument_values::create(*m_argument_values.get()); |
| |
| return kernel; |
| } |
| |
| void cvk_kernel::set_image_metadata(cl_uint index, const void* image) { |
| if (!m_image_metadata) { |
| return; |
| } |
| auto md = m_image_metadata->find(index); |
| if (md != m_image_metadata->end()) { |
| |
| auto mem = icd_downcast(*reinterpret_cast<const cl_mem*>(image)); |
| assert(mem->is_image_type()); |
| auto format = static_cast<cvk_image*>(mem)->format(); |
| |
| if (md->second.has_valid_order()) { |
| auto order_offset = md->second.order_offset; |
| auto order = format.image_channel_order; |
| m_argument_values->set_pod_data(order_offset, sizeof(order), |
| &order); |
| } |
| if (md->second.has_valid_data_type()) { |
| auto data_type_offset = md->second.data_type_offset; |
| auto data_type = format.image_channel_data_type; |
| m_argument_values->set_pod_data(data_type_offset, sizeof(data_type), |
| &data_type); |
| } |
| } |
| } |
| |
| void cvk_kernel::set_sampler_metadata(cl_uint index, const void* sampler) { |
| if (!m_sampler_metadata) { |
| return; |
| } |
| auto md = m_sampler_metadata->find(index); |
| if (md != m_sampler_metadata->end()) { |
| auto apisampler = *reinterpret_cast<const cl_sampler*>(sampler); |
| auto offset = md->second; |
| auto sampler = icd_downcast(apisampler); |
| uint32_t sampler_mask = (sampler->normalized_coords() |
| ? clspv::CLK_NORMALIZED_COORDS_TRUE |
| : clspv::CLK_NORMALIZED_COORDS_FALSE) | |
| (sampler->filter_mode() == CL_FILTER_NEAREST |
| ? clspv::CLK_FILTER_NEAREST |
| : clspv::CLK_FILTER_LINEAR); |
| switch (sampler->addressing_mode()) { |
| case CL_ADDRESS_NONE: |
| sampler_mask |= clspv::CLK_ADDRESS_NONE; |
| break; |
| case CL_ADDRESS_CLAMP: |
| sampler_mask |= clspv::CLK_ADDRESS_CLAMP; |
| break; |
| case CL_ADDRESS_REPEAT: |
| sampler_mask |= clspv::CLK_ADDRESS_REPEAT; |
| break; |
| case CL_ADDRESS_CLAMP_TO_EDGE: |
| sampler_mask |= clspv::CLK_ADDRESS_CLAMP_TO_EDGE; |
| break; |
| case CL_ADDRESS_MIRRORED_REPEAT: |
| sampler_mask |= clspv::CLK_ADDRESS_MIRRORED_REPEAT; |
| break; |
| default: |
| break; |
| } |
| m_argument_values->set_pod_data(offset, sizeof(sampler_mask), |
| &sampler_mask); |
| } |
| } |
| |
| cl_int cvk_kernel::set_arg(cl_uint index, size_t size, const void* value) { |
| std::lock_guard<std::mutex> lock(m_lock); |
| |
| // Clone argument values if they have been used in an enqueue |
| if (m_argument_values->is_enqueued()) { |
| m_argument_values = |
| cvk_kernel_argument_values::create(*m_argument_values); |
| if (m_argument_values == nullptr) { |
| return CL_OUT_OF_RESOURCES; |
| } |
| } |
| |
| auto const& arg = m_args[index]; |
| |
| cl_int ret = m_argument_values->set_arg(arg, size, value); |
| |
| // if the argument is an image, we need to set its metadata |
| // (channel_order/channel_data_type). |
| if (arg.kind == kernel_argument_kind::sampled_image || |
| arg.kind == kernel_argument_kind::storage_image || |
| arg.kind == kernel_argument_kind::storage_texel_buffer || |
| arg.kind == kernel_argument_kind::uniform_texel_buffer) { |
| set_image_metadata(index, value); |
| } |
| |
| if (arg.kind == kernel_argument_kind::sampler) { |
| set_sampler_metadata(index, value); |
| } |
| |
| return ret; |
| } |
| |
| bool cvk_kernel::args_valid() const { return m_argument_values->args_valid(); } |
| |
| bool cvk_kernel_argument_values::setup_descriptor_sets() { |
| std::lock_guard<std::mutex> lock(m_lock); |
| |
| auto program = m_entry_point->program(); |
| auto dev = program->context()->device()->vulkan_device(); |
| |
| // Do nothing if these argument values have already been used in an enqueue |
| if (m_is_enqueued) { |
| return true; |
| } |
| |
| // Allocate descriptor sets |
| if (!m_entry_point->allocate_descriptor_sets(descriptor_sets())) { |
| return false; |
| } |
| VkDescriptorSet* ds = descriptor_sets(); |
| |
| // Make enough space to store all descriptor write structures |
| size_t max_descriptor_writes = |
| m_args.size() // upper bound that includes POD buffers |
| + program->literal_sampler_descs().size() + |
| 1; // module constant data buffer |
| std::vector<VkWriteDescriptorSet> descriptor_writes; |
| std::vector<VkDescriptorBufferInfo> buffer_info; |
| std::vector<VkDescriptorImageInfo> image_info; |
| std::vector<VkBufferView> buffer_views; |
| descriptor_writes.reserve(max_descriptor_writes); |
| buffer_info.reserve(max_descriptor_writes); |
| image_info.reserve(max_descriptor_writes); |
| buffer_views.reserve(max_descriptor_writes); |
| |
| // Setup module-scope variables |
| if (program->module_constant_data_buffer() != nullptr && |
| program->module_constant_data_buffer_info()->type == |
| module_buffer_type::storage_buffer) { |
| auto buffer = program->module_constant_data_buffer(); |
| auto info = program->module_constant_data_buffer_info(); |
| cvk_debug_fn( |
| "constant data buffer %p, size = %zu @ set = %u, binding = %u", |
| buffer->vulkan_buffer(), buffer->size(), info->set, info->binding); |
| // Update descriptors |
| VkDescriptorBufferInfo bufferInfo = {buffer->vulkan_buffer(), |
| 0, // offset |
| VK_WHOLE_SIZE}; |
| buffer_info.push_back(bufferInfo); |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[info->set], |
| info->binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // descriptorType |
| nullptr, // pImageInfo |
| &buffer_info.back(), |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| } |
| |
| // Setup descriptors for POD arguments |
| if (m_entry_point->has_pod_buffer_arguments()) { |
| // Create POD buffer |
| if (!create_pod_buffer()) { |
| return false; |
| } |
| |
| // Update descriptors |
| cvk_debug_fn("pod buffer %p, size = %zu @ set = %u, binding = %u", |
| m_pod_buffer->vulkan_buffer(), m_pod_buffer->size(), |
| m_pod_arg->descriptorSet, m_pod_arg->binding); |
| VkDescriptorBufferInfo bufferInfo = {m_pod_buffer->vulkan_buffer(), |
| 0, // offset |
| VK_WHOLE_SIZE}; |
| buffer_info.push_back(bufferInfo); |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[m_pod_arg->descriptorSet], |
| m_pod_arg->binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| m_entry_point->pod_descriptor_type(), // descriptorType |
| nullptr, // pImageInfo |
| &buffer_info.back(), |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| } |
| |
| // Setup other kernel argument descriptors |
| for (cl_uint i = 0; i < m_args.size(); i++) { |
| auto const& arg = m_args[i]; |
| |
| switch (arg.kind) { |
| |
| case kernel_argument_kind::buffer: |
| case kernel_argument_kind::buffer_ubo: { |
| auto buffer = static_cast<cvk_buffer*>(get_arg_value(arg)); |
| if (buffer == nullptr) { |
| cvk_debug_fn("ignoring NULL buffer argument"); |
| break; |
| } |
| auto vkbuf = buffer->vulkan_buffer(); |
| cvk_debug_fn( |
| "buffer %p, offset = %zu, size = %zu @ set = %u, binding = %u", |
| buffer->vulkan_buffer(), buffer->vulkan_buffer_offset(), |
| buffer->size(), arg.descriptorSet, arg.binding); |
| VkDescriptorBufferInfo bufferInfo = { |
| vkbuf, |
| buffer->vulkan_buffer_offset(), // offset |
| buffer->size()}; |
| buffer_info.push_back(bufferInfo); |
| |
| auto descriptor_type = arg.kind == kernel_argument_kind::buffer |
| ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER |
| : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[arg.descriptorSet], |
| arg.binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| descriptor_type, |
| nullptr, // pImageInfo |
| &buffer_info.back(), |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| break; |
| } |
| case kernel_argument_kind::sampler: { |
| auto clsampler = static_cast<cvk_sampler*>(get_arg_value(arg)); |
| bool normalized_coord_sampler_required = false; |
| if (auto md = m_entry_point->sampler_metadata()) { |
| normalized_coord_sampler_required = md->find(i) != md->end(); |
| } |
| auto sampler = |
| normalized_coord_sampler_required && |
| !clsampler->normalized_coords() |
| ? clsampler |
| ->get_or_create_vulkan_sampler_with_normalized_coords() |
| : clsampler->vulkan_sampler(); |
| if (sampler == VK_NULL_HANDLE) { |
| cvk_error_fn("Could not set descriptor for sampler"); |
| return false; |
| } |
| |
| cvk_debug_fn("sampler %p @ set = %u, binding = %u", sampler, |
| arg.descriptorSet, arg.binding); |
| VkDescriptorImageInfo imageInfo = { |
| sampler, |
| VK_NULL_HANDLE, // imageView |
| VK_IMAGE_LAYOUT_UNDEFINED // imageLayout |
| }; |
| image_info.push_back(imageInfo); |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[arg.descriptorSet], |
| arg.binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| VK_DESCRIPTOR_TYPE_SAMPLER, |
| &image_info.back(), // pImageInfo |
| nullptr, // pBufferInfo |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| break; |
| } |
| case kernel_argument_kind::sampled_image: |
| case kernel_argument_kind::storage_image: { |
| auto image = static_cast<cvk_image*>(get_arg_value(arg)); |
| bool sampled = arg.kind == kernel_argument_kind::sampled_image; |
| auto view = sampled ? image->vulkan_sampled_view() |
| : image->vulkan_storage_view(); |
| |
| cvk_debug_fn("image view %p @ set = %u, binding = %u", view, |
| arg.descriptorSet, arg.binding); |
| VkDescriptorImageInfo imageInfo = { |
| VK_NULL_HANDLE, |
| view, // imageView |
| VK_IMAGE_LAYOUT_GENERAL // imageLayout |
| }; |
| image_info.push_back(imageInfo); |
| |
| VkDescriptorType dtype = sampled ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE |
| : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[arg.descriptorSet], |
| arg.binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| dtype, |
| &image_info.back(), // pImageInfo |
| nullptr, // pBufferInfo |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| break; |
| } |
| case kernel_argument_kind::storage_texel_buffer: |
| case kernel_argument_kind::uniform_texel_buffer: { |
| auto image = static_cast<cvk_image*>(get_arg_value(arg)); |
| bool uniform = |
| arg.kind == kernel_argument_kind::uniform_texel_buffer; |
| auto view = image->vulkan_buffer_view(); |
| buffer_views.push_back(view); |
| |
| cvk_debug_fn("buffer view %p @ set = %u, binding = %u", view, |
| arg.descriptorSet, arg.binding); |
| |
| VkDescriptorType dtype = |
| uniform ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER |
| : VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[arg.descriptorSet], |
| arg.binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| dtype, |
| nullptr, // pImageInfo |
| nullptr, // pBufferInfo |
| &buffer_views.back(), // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| break; |
| } |
| case kernel_argument_kind::pod: // skip POD arguments |
| case kernel_argument_kind::pod_ubo: |
| case kernel_argument_kind::pod_pushconstant: |
| case kernel_argument_kind::pointer_ubo: |
| case kernel_argument_kind::pointer_pushconstant: |
| break; |
| case kernel_argument_kind::local: // nothing to do? |
| break; |
| case kernel_argument_kind::unused: |
| break; |
| default: |
| cvk_error_fn("unsupported argument type"); |
| return false; |
| } |
| } |
| |
| // Setup literal samplers |
| for (size_t i = 0; i < program->literal_sampler_descs().size(); i++) { |
| auto desc = program->literal_sampler_descs()[i]; |
| auto clsampler = icd_downcast(program->literal_samplers()[i]); |
| auto sampler = clsampler->vulkan_sampler(); |
| |
| VkDescriptorImageInfo imageInfo = { |
| sampler, |
| VK_NULL_HANDLE, // imageView |
| VK_IMAGE_LAYOUT_UNDEFINED // imageLayout |
| }; |
| image_info.push_back(imageInfo); |
| |
| VkWriteDescriptorSet writeDescriptorSet = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| nullptr, |
| ds[desc.descriptorSet], |
| desc.binding, // dstBinding |
| 0, // dstArrayElement |
| 1, // descriptorCount |
| VK_DESCRIPTOR_TYPE_SAMPLER, |
| &image_info.back(), // pImageInfo |
| nullptr, // pBufferInfo |
| nullptr, // pTexelBufferView |
| }; |
| descriptor_writes.push_back(writeDescriptorSet); |
| } |
| |
| m_is_enqueued = true; |
| |
| // Write descriptors to device |
| vkUpdateDescriptorSets(dev, static_cast<uint32_t>(descriptor_writes.size()), |
| descriptor_writes.data(), 0, nullptr); |
| |
| return true; |
| } |