blob: 8355654514e11e8bdaba74cdec1ad84cfa229db7 [file] [edit]
// 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;
}