blob: 7d01858c1e1380d4dfd307374edca516e9abc68c [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.
#pragma once
#include <array>
#include <atomic>
#include <climits>
#include <cstdint>
#include <fstream>
#include <map>
#include <unordered_map>
#include <vector>
#include <vulkan/vulkan.h>
#include "spirv-tools/libspirv.h"
#include "spirv/1.0/spirv.hpp"
#include "config.hpp"
#include "init.hpp"
#include "log.hpp"
#include "memory.hpp"
#include "objects.hpp"
#include "printf.hpp"
#include "utils.hpp"
const int SPIR_WORD_SIZE = 4;
enum class kernel_argument_kind
{
buffer,
buffer_ubo,
pod,
pod_ubo,
pod_pushconstant,
pointer_ubo,
pointer_pushconstant,
sampled_image,
storage_image,
storage_texel_buffer,
uniform_texel_buffer,
sampler,
local,
unused,
};
struct kernel_argument_info {
std::string name;
bool extended_valid = false;
std::string type_name;
uint32_t address_qualifier;
uint32_t access_qualifier;
uint32_t type_qualifier;
bool is_vec3() const {
return extended_valid && type_name[type_name.length() - 1] == '3';
}
};
struct kernel_argument {
kernel_argument_info info;
uint32_t pos;
uint32_t descriptorSet;
uint32_t binding;
uint32_t offset;
uint32_t size;
kernel_argument_kind kind;
uint32_t local_spec_id;
uint32_t local_elem_size;
bool is_pod() const {
return (kind == kernel_argument_kind::pod) ||
(kind == kernel_argument_kind::pod_ubo) ||
(kind == kernel_argument_kind::pod_pushconstant) ||
(kind == kernel_argument_kind::pointer_ubo) ||
(kind == kernel_argument_kind::pointer_pushconstant);
}
bool is_pod_buffer() const {
return (kind == kernel_argument_kind::pod) ||
(kind == kernel_argument_kind::pod_ubo) ||
(kind == kernel_argument_kind::pointer_ubo);
}
bool is_pod_pointer() const {
return (kind == kernel_argument_kind::pointer_pushconstant) ||
(kind == kernel_argument_kind::pointer_ubo);
}
bool is_vec3() const { return info.is_vec3(); }
bool is_mem_object_backed() const {
return (kind == kernel_argument_kind::buffer) ||
(kind == kernel_argument_kind::buffer_ubo) ||
(kind == kernel_argument_kind::sampled_image) ||
(kind == kernel_argument_kind::storage_image) ||
(kind == kernel_argument_kind::storage_texel_buffer) ||
(kind == kernel_argument_kind::uniform_texel_buffer);
}
bool is_unused() const { return kind == kernel_argument_kind::unused; }
};
struct sampler_desc {
uint32_t descriptorSet;
uint32_t binding;
bool normalized_coords;
cl_addressing_mode addressing_mode;
cl_filter_mode filter_mode;
};
enum class pushconstant
{
global_offset,
enqueued_local_size,
global_size,
region_offset,
num_workgroups,
region_group_offset,
image_metadata,
module_constants_pointer,
printf_buffer_pointer,
normalized_sampler_mask,
};
struct pushconstant_desc {
uint32_t offset;
uint32_t size;
};
enum class spec_constant
{
workgroup_size_x,
workgroup_size_y,
workgroup_size_z,
work_dim,
global_offset_x,
global_offset_y,
global_offset_z,
subgroup_max_size,
};
enum class module_buffer_type
{
storage_buffer,
pointer_push_constant,
};
struct user_spec_constant_data {
std::string type;
uint32_t size;
bool set;
union {
uint8_t i8;
uint16_t i16;
uint32_t i32;
uint64_t i64;
} data;
user_spec_constant_data(std::string type, uint32_t size)
: type(type), size(size), set(false) {}
void init_data(size_t size, const void* value) {
if (type == "i8" || type == "i1") {
std::memcpy(&data.i8, value, size);
} else if (type == "i16" || type == "f16") {
std::memcpy(&data.i16, value, size);
} else if (type == "i32" || type == "f32") {
std::memcpy(&data.i32, value, size);
} else if (type == "i64" || type == "f64") {
std::memcpy(&data.i64, value, size);
} else {
CVK_ASSERT(false && "Unexpected specialisation constant type");
}
set = true;
}
};
struct constant_data_buffer_info {
module_buffer_type type;
uint32_t set;
uint32_t binding;
uint32_t pc_offset;
std::vector<char> data;
};
struct printf_buffer_desc_info {
module_buffer_type type;
uint32_t set;
uint32_t binding;
uint32_t pc_offset;
uint32_t size = 0;
};
struct spirv_validation_options {
bool uniform_buffer_std_layout = false;
};
struct image_metadata {
image_metadata() : order_offset(UINT_MAX), data_type_offset(UINT_MAX) {}
uint32_t order_offset;
uint32_t data_type_offset;
void set_order(uint32_t order) { order_offset = order; }
void set_data_type(uint32_t data_type) { data_type_offset = data_type; }
bool has_valid_order() const { return order_offset != UINT_MAX; }
bool has_valid_data_type() const { return data_type_offset != UINT_MAX; }
};
using kernel_image_metadata_map =
std::unordered_map<uint32_t, struct image_metadata>;
using image_metadata_map =
std::unordered_map<std::string, kernel_image_metadata_map>;
using kernel_sampler_metadata_map = std::unordered_map<uint32_t, uint32_t>;
using sampler_metadata_map =
std::unordered_map<std::string, kernel_sampler_metadata_map>;
class spir_binary {
using kernels_arguments_map =
std::unordered_map<std::string, std::vector<kernel_argument>>;
using kernels_reqd_work_group_size_map =
std::unordered_map<std::string, std::array<uint32_t, 3>>;
using kernels_flags_map = std::unordered_map<std::string, uint32_t>;
public:
spir_binary(spv_target_env env)
: m_loaded_from_binary(false), m_target_env(env),
m_workgroup_variables_size(0) {
m_context = spvContextCreate(env);
}
~spir_binary() { spvContextDestroy(m_context); }
CHECK_RETURN bool load(const char* fname);
CHECK_RETURN bool load(std::istream& istream, uint32_t size);
CHECK_RETURN bool load_descriptor_map();
CHECK_RETURN bool save(std::ostream& ostream) const;
CHECK_RETURN bool save(const char* fname) const;
CHECK_RETURN bool read(const unsigned char* src, size_t size);
CHECK_RETURN bool write(unsigned char* dst) const;
size_t size() const;
bool loaded_from_binary() const { return m_loaded_from_binary; }
size_t spir_size() const { return m_code.size() * sizeof(uint32_t); }
const uint32_t* spir_data() const { return m_code.data(); }
void use(std::vector<uint32_t>&& src);
void set_target_env(spv_target_env env);
const std::vector<uint32_t>& code() const { return m_code; };
CHECK_RETURN bool validate(const spirv_validation_options&) const;
size_t num_kernels() const { return m_dmaps.size(); }
const kernels_arguments_map& kernels_arguments() const { return m_dmaps; }
const sampler_metadata_map& sampler_metadata() const {
return m_sampler_metadata;
}
const image_metadata_map& image_metadata() const {
return m_image_metadata;
}
std::vector<uint32_t>* raw_binary() { return &m_code; }
const std::vector<sampler_desc>& literal_samplers() {
return m_literal_samplers;
}
const std::array<uint32_t, 3>&
required_work_group_size(const std::string& kernel) const {
return m_reqd_work_group_sizes.at(kernel);
}
CHECK_RETURN bool
get_capabilities(std::vector<spv::Capability>& capabilities) const;
static constexpr uint32_t MAX_DESCRIPTOR_SETS = 3;
const std::unordered_map<pushconstant, pushconstant_desc>&
push_constants() const {
return m_push_constants;
}
const std::unordered_map<spec_constant, uint32_t>& spec_constants() const {
return m_spec_constants;
}
CHECK_RETURN const pushconstant_desc* push_constant(pushconstant pc) const {
if (m_push_constants.count(pc) != 0) {
return &m_push_constants.at(pc);
} else {
return nullptr;
}
}
const printf_descriptor_map_t& printf_descriptors() const {
return m_printf_descriptors;
}
void add_kernel(const std::string& name, uint32_t num_args,
const std::string& attributes, uint32_t flags) {
m_flags[name] = flags;
auto& args = m_dmaps[name];
kernel_argument unused = {
{}, 0, 0, 0, 0, 0, kernel_argument_kind::unused, 0, 0};
// Generate a placeholder for each argument in the kernel.
args.resize(num_args, unused);
uint32_t pos = 0;
// Assign the argument ordinals. Any used argument will overwrite these,
// but they are necessary for unused arguments.
for (auto& arg : args) {
arg.pos = pos++;
}
m_reqd_work_group_sizes[name] = {0, 0, 0};
m_kernels_attributes[name] = attributes;
}
const std::unordered_map<std::string, std::string>&
kernels_attributes() const {
return m_kernels_attributes;
}
void add_kernel_argument(const std::string& name, kernel_argument&& arg) {
// Overwrite the placeholder argument.
m_dmaps[name][arg.pos] = std::move(arg);
}
void add_spec_constant(spec_constant constant, uint32_t spec_id) {
m_spec_constants[constant] = spec_id;
}
void add_push_constant(pushconstant pc, pushconstant_desc&& desc) {
m_push_constants[pc] = desc;
}
void add_literal_sampler(sampler_desc&& desc) {
m_literal_samplers.push_back(desc);
}
void set_required_work_group_size(const std::string& kernel, uint32_t x,
uint32_t y, uint32_t z) {
m_reqd_work_group_sizes[kernel] = {x, y, z};
}
bool strip_reflection(std::vector<uint32_t>* stripped);
const constant_data_buffer_info* constant_data_buffer() const {
return m_constant_data_buffer.get();
}
void set_constant_data_buffer(const constant_data_buffer_info& info) {
m_constant_data_buffer.reset(new constant_data_buffer_info(info));
}
void add_sampler_metadata(const std::string& name, uint32_t ordinal,
uint32_t offset) {
m_sampler_metadata[name][ordinal] = offset;
}
void add_image_channel_order_metadata(const std::string& name,
uint32_t ordinal, uint32_t offset) {
m_image_metadata[name][ordinal].set_order(offset);
}
void add_image_channel_data_type_metadata(const std::string& name,
uint32_t ordinal,
uint32_t offset) {
m_image_metadata[name][ordinal].set_data_type(offset);
}
void add_printf_descriptor(printf_descriptor&& desc) {
m_printf_descriptors[desc.printf_id] = desc;
}
void set_printf_buffer_info(const printf_buffer_desc_info& info) {
m_printf_buffer_info = info;
}
const printf_buffer_desc_info& printf_buffer_info() const {
return m_printf_buffer_info;
}
const printf_descriptor_map_t& get_printf_descriptors() const {
return m_printf_descriptors;
}
const kernels_flags_map& kernels_flags() const { return m_flags; }
void add_workgroup_variable_size(uint32_t size) {
m_workgroup_variables_size += size;
}
uint32_t get_workgroup_variables_size() const {
return m_workgroup_variables_size;
}
private:
spv_context m_context;
std::vector<uint32_t> m_code;
std::vector<sampler_desc> m_literal_samplers;
std::unordered_map<pushconstant, pushconstant_desc> m_push_constants;
std::unordered_map<spec_constant, uint32_t> m_spec_constants;
sampler_metadata_map m_sampler_metadata;
image_metadata_map m_image_metadata;
std::unordered_map<uint32_t, printf_descriptor> m_printf_descriptors;
printf_buffer_desc_info m_printf_buffer_info;
std::unique_ptr<constant_data_buffer_info> m_constant_data_buffer;
kernels_arguments_map m_dmaps;
kernels_reqd_work_group_size_map m_reqd_work_group_sizes;
std::unordered_map<std::string, std::string> m_kernels_attributes;
kernels_flags_map m_flags;
bool m_loaded_from_binary;
spv_target_env m_target_env;
uint32_t m_workgroup_variables_size;
};
enum class build_operation
{
build,
build_binary,
compile,
link
};
using cvk_program_callback = void(CL_CALLBACK*)(cl_program, void*);
using cvk_spec_constant_map = std::map<uint32_t, uint32_t>;
struct cvk_program;
class cvk_entry_point {
public:
cvk_entry_point(cvk_device* dev, cvk_program* program,
const std::string& name);
~cvk_entry_point() {
VkDevice vkdev = m_device->vulkan_device();
for (auto pipeline : m_pipelines) {
cvk_info("destroying pipeline %p for kernel %s", pipeline.second,
m_name.c_str());
vkDestroyPipeline(vkdev, pipeline.second, nullptr);
}
if (m_descriptor_pool != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(vkdev, m_descriptor_pool, nullptr);
}
if (m_pipeline_layout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(vkdev, m_pipeline_layout, nullptr);
}
for (auto layout : m_descriptor_set_layouts) {
vkDestroyDescriptorSetLayout(vkdev, layout, nullptr);
}
}
CHECK_RETURN cl_int init();
CHECK_RETURN VkPipeline
create_pipeline(const cvk_spec_constant_map& spec_constants);
CHECK_RETURN bool allocate_descriptor_sets(VkDescriptorSet* ds);
void free_descriptor_set(VkDescriptorSet ds) {
TRACE_FUNCTION();
std::lock_guard<std::mutex> lock(m_descriptor_pool_lock);
vkFreeDescriptorSets(m_device->vulkan_device(), m_descriptor_pool, 1,
&ds);
m_nb_descriptor_set_allocated--;
TRACE_CNT(descriptor_set_allocated_counter,
m_nb_descriptor_set_allocated);
}
uint32_t num_set_layouts() const { return m_descriptor_set_layouts.size(); }
std::unique_ptr<cvk_buffer> allocate_pod_buffer();
const std::vector<kernel_argument>& args() const { return m_args; }
const kernel_sampler_metadata_map* sampler_metadata() const {
return m_sampler_metadata;
}
const kernel_image_metadata_map* image_metadata() const {
return m_image_metadata;
}
bool has_pod_arguments() const { return m_has_pod_arguments; }
bool has_pod_buffer_arguments() const { return m_has_pod_buffer_arguments; }
bool has_sampler_metadata() const { return m_sampler_metadata != nullptr; }
bool has_image_metadata() const { return m_image_metadata != nullptr; }
uint32_t pod_buffer_size() const { return m_pod_buffer_size; }
uint32_t num_resource_slots() const { return m_num_resource_slots; }
VkPipelineLayout pipeline_layout() const { return m_pipeline_layout; }
VkDescriptorType pod_descriptor_type() const {
return m_pod_descriptor_type;
}
cvk_program* program() const { return m_program; }
bool uses_printf() const;
private:
const uint32_t MAX_INSTANCES = config.max_entry_points_instances;
cvk_device* m_device;
cvk_context* m_context;
cvk_program* m_program;
std::string m_name;
VkDescriptorType m_pod_descriptor_type;
uint32_t m_pod_buffer_size;
bool m_has_pod_arguments;
bool m_has_pod_buffer_arguments;
std::vector<kernel_argument> m_args;
const kernel_sampler_metadata_map* m_sampler_metadata;
const kernel_image_metadata_map* m_image_metadata;
uint32_t m_num_resource_slots;
VkDescriptorPool m_descriptor_pool;
std::vector<VkDescriptorSetLayout> m_descriptor_set_layouts;
VkPipelineLayout m_pipeline_layout;
std::mutex m_pipeline_cache_lock;
std::mutex m_descriptor_pool_lock;
using binding_stat_map = std::unordered_map<VkDescriptorType, uint32_t>;
bool build_descriptor_set_layout(
const std::vector<VkDescriptorSetLayoutBinding>& bindings);
bool build_descriptor_sets_layout_bindings_for_arguments(
binding_stat_map& smap, uint32_t& num_resource_slots);
bool build_descriptor_sets_layout_bindings_for_literal_samplers(
binding_stat_map& smap);
bool build_descriptor_sets_layout_bindings_for_program_scope_buffers(
binding_stat_map& smap);
bool build_descriptor_sets_layout_bindings_for_printf_buffer(
binding_stat_map& smap);
// Structures for caching pipelines based on specialization constants
struct SpecConstantMapHash {
size_t operator()(const cvk_spec_constant_map& spec_constants) const {
// TODO: better hash?
size_t result = 0;
for (auto& entry : spec_constants) {
result ^= std::hash<uint32_t>{}(entry.first) * 31;
result ^= std::hash<uint32_t>{}(entry.second) * 59;
}
return result;
}
};
struct SpecConstantMapEqual {
bool operator()(const cvk_spec_constant_map& lhs,
const cvk_spec_constant_map& rhs) const {
if (lhs.size() != rhs.size())
return false;
for (auto& lhs_entry : lhs) {
if (!rhs.count(lhs_entry.first))
return false;
if (lhs_entry.second != rhs.at(lhs_entry.first))
return false;
}
return true;
}
};
std::unordered_map<cvk_spec_constant_map, VkPipeline, SpecConstantMapHash,
SpecConstantMapEqual>
m_pipelines;
uint32_t m_nb_descriptor_set_allocated;
TRACE_CNT_VAR(descriptor_set_allocated_counter);
bool m_first_allocation_failure;
};
struct cvk_program : public _cl_program, api_object<object_magic::program> {
cvk_program(cvk_context* ctx)
: api_object(ctx), m_num_devices(1U),
m_binary_type(CL_PROGRAM_BINARY_TYPE_NONE),
m_shader_module(VK_NULL_HANDLE),
m_binary(m_context->device()->vulkan_spirv_env()) {
m_dev_status[m_context->device()] = CL_BUILD_NONE;
}
cvk_program(cvk_context* ctx, const void* il, size_t length)
: cvk_program(ctx) {
m_il.resize(length);
memcpy(m_il.data(), il, length);
}
virtual ~cvk_program() {
if (m_shader_module != VK_NULL_HANDLE) {
auto vkdev = m_context->device()->vulkan_device();
vkDestroyShaderModule(vkdev, m_shader_module, nullptr);
}
for (auto& s : m_literal_samplers) {
s->release();
}
}
void append_source(const char* src, size_t len) {
if (len != 0) {
m_source.append(src, len);
} else {
m_source.append(src);
}
}
const std::string& source() const { return m_source; }
const std::vector<uint8_t>& il() const { return m_il; }
uint32_t num_devices() const { return m_num_devices; }
cl_program_binary_type binary_type(const cvk_device*) const {
return m_binary_type;
}
bool can_be_linked() const {
auto dev = m_context->device();
return ((build_status() == CL_BUILD_SUCCESS) &&
((binary_type(dev) == CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT) ||
(binary_type(dev) == CL_PROGRAM_BINARY_TYPE_LIBRARY)));
}
CHECK_RETURN cl_int build(build_operation operation, cl_uint num_devices,
const cl_device_id* device_list,
const char* options, cl_uint num_input_programs,
const cl_program* input_programs,
const char** header_include_names,
cvk_program_callback cb, void* data);
cl_int set_user_spec_constant(uint32_t spec_id, size_t spec_size,
const void* spec_value) {
auto spec_const_iter = m_user_spec_constants.find(spec_id);
if (spec_const_iter == m_user_spec_constants.end()) {
return CL_INVALID_SPEC_ID;
}
if (spec_const_iter->second.size != spec_size) {
return CL_INVALID_VALUE;
}
spec_const_iter->second.init_data(spec_size, spec_value);
return CL_SUCCESS;
}
const std::string& build_options() const { return m_build_options; }
cl_build_status build_status(const cvk_device* device) const {
return m_dev_status.at(device);
}
cl_build_status build_status() const {
for (auto& dev_st : m_dev_status) {
if (dev_st.second != CL_BUILD_SUCCESS) {
return dev_st.second;
}
}
return CL_BUILD_SUCCESS;
}
const std::string& build_log(const cvk_device* device) const {
UNUSED(device); // TODO support per-device build log
return m_build_log;
}
std::vector<const cvk_device*> devices() const {
std::vector<const cvk_device*> ret;
for (auto& dev_st : m_dev_status) {
ret.push_back(dev_st.first);
}
return ret;
}
VkShaderModule shader_module() const { return m_shader_module; }
void complete_operation(cvk_device* device, cl_build_status status) {
m_dev_status[device] = status;
if (m_operation_callback != nullptr) {
m_operation_callback(this, m_operation_callback_data);
}
release();
}
unsigned num_kernels() const { return m_binary.num_kernels(); }
bool loaded_from_binary() const { return m_binary.loaded_from_binary(); }
bool uses_printf() { return !m_binary.printf_descriptors().empty(); }
const std::unordered_map<uint32_t, printf_descriptor>&
printf_descriptors() {
return m_binary.get_printf_descriptors();
}
const std::vector<kernel_argument>* args_for_kernel(std::string& name) {
auto const& args = m_binary.kernels_arguments().find(name);
if (args != m_binary.kernels_arguments().end()) {
return &args->second;
} else {
return nullptr;
}
}
const kernel_sampler_metadata_map* sampler_metadata(std::string& name) {
auto const& md = m_binary.sampler_metadata().find(name);
if (md != m_binary.sampler_metadata().end()) {
return &md->second;
} else {
return nullptr;
}
}
const kernel_image_metadata_map* image_metadata(std::string& name) {
auto const& md = m_binary.image_metadata().find(name);
if (md != m_binary.image_metadata().end()) {
return &md->second;
} else {
return nullptr;
}
}
private:
bool read_llvm_bitcode(const unsigned char* src, size_t size);
void write_binary_header(unsigned char* dst) const;
CHECK_RETURN cl_program_binary_type
read_binary_header(const unsigned char* src, size_t size);
public:
CHECK_RETURN bool read(const unsigned char* src, size_t size);
CHECK_RETURN bool write(unsigned char* dst) const;
size_t binary_size() const;
std::vector<const char*> kernel_names() const {
std::vector<const char*> ret;
for (auto& kname_args : m_binary.kernels_arguments()) {
ret.push_back(kname_args.first.c_str());
}
return ret;
}
const std::vector<sampler_desc>& literal_sampler_descs() {
return m_binary.literal_samplers();
}
const std::vector<cvk_sampler_holder>& literal_samplers() {
return m_literal_samplers;
}
const VkPushConstantRange& push_constant_range() const {
return m_push_constant_range;
}
CHECK_RETURN const pushconstant_desc* push_constant(pushconstant pc) const {
return m_binary.push_constant(pc);
}
CHECK_RETURN const std::unordered_map<spec_constant, uint32_t>&
spec_constants() const {
return m_binary.spec_constants();
}
const std::array<uint32_t, 3>&
required_work_group_size(const std::string& kernel) const {
return m_binary.required_work_group_size(kernel);
}
uint32_t required_sub_group_size(std::string& kernel) const {
auto forced_subgroup_size_or_zero = []() {
if (config.force_subgroup_size.set) {
return config.force_subgroup_size();
}
return 0u;
};
const char* sub_group_size_attr = "intel_reqd_sub_group_size(";
auto attrs = kernel_attributes(kernel);
auto it = attrs.find(sub_group_size_attr);
if (it == std::string::npos) {
return forced_subgroup_size_or_zero();
}
it += strlen(sub_group_size_attr);
auto it2 = attrs.substr(it).find(")");
if (it2 == std::string::npos) {
return forced_subgroup_size_or_zero();
}
uint32_t kernel_subgroup_size = atoi(attrs.substr(it, it2).c_str());
if (config.force_subgroup_size.set) {
uint32_t force_subgroup_size = config.force_subgroup_size();
if (force_subgroup_size != kernel_subgroup_size) {
cvk_warn_fn("overriding subgroup size specified inside kernel "
"'%s', using '%u' instead of '%u'",
kernel.c_str(), force_subgroup_size,
kernel_subgroup_size);
}
return config.force_subgroup_size();
}
return kernel_subgroup_size;
}
const VkPipelineCache& pipeline_cache() const { return m_pipeline_cache; }
CHECK_RETURN std::shared_ptr<cvk_entry_point>
get_entry_point(std::string& name, cl_int* errcode_ret);
bool create_module_constant_data_buffer() {
cl_int err;
if (m_binary.constant_data_buffer() != nullptr) {
auto& init_data = m_binary.constant_data_buffer()->data;
void* init_data_ptr =
reinterpret_cast<void*>(const_cast<char*>(init_data.data()));
m_module_constant_data_buffer =
cvk_buffer::create(m_context, CL_MEM_COPY_HOST_PTR,
init_data.size(), init_data_ptr, &err);
if (m_module_constant_data_buffer == nullptr) {
return false;
}
}
return true;
}
const cvk_buffer* module_constant_data_buffer() const {
return m_module_constant_data_buffer.get();
}
const constant_data_buffer_info* module_constant_data_buffer_info() const {
return m_binary.constant_data_buffer();
}
const printf_buffer_desc_info& printf_buffer_info() const {
return m_binary.printf_buffer_info();
}
bool options_allow_split_region(std::string options) {
if (options.find("-uniform-workgroup-size") != std::string::npos)
return false;
return true;
}
bool can_split_region() {
int status = options_allow_split_region(m_build_options);
#if COMPILER_AVAILABLE
status &= options_allow_split_region(config.clspv_options);
#endif
return status;
}
CHECK_RETURN cl_int parse_user_spec_constants();
const std::string& kernel_attributes(const std::string& kernel_name) const {
return m_binary.kernels_attributes().at(kernel_name);
}
uint32_t kernel_flags(const std::string& kernel) const {
return m_binary.kernels_flags().at(kernel);
}
uint32_t workgroup_variables_size() const {
return m_binary.get_workgroup_variables_size();
}
private:
void do_build();
void do_build_in_separate_thread() {
cvk_set_current_thread_name_if_supported("clvk-build");
do_build();
};
std::string prepare_build_options(const cvk_device* device) const;
CHECK_RETURN cl_build_status do_build_inner(const cvk_device* device);
#if COMPILER_AVAILABLE
#ifndef CLSPV_ONLINE_COMPILER
CHECK_RETURN cl_build_status
do_build_inner_offline(bool build_to_ir, bool build_from_il,
std::string& build_options, std::string& tmp_folder);
#else
CHECK_RETURN cl_build_status do_build_inner_online(
bool build_to_ir, bool build_from_il, std::string& build_options);
#endif
#endif
void prepare_push_constant_range();
/// Check if all of the capabilities required by the SPIR-V module are
/// supported by `device`.
CHECK_RETURN bool check_capabilities(const cvk_device* device);
uint32_t m_num_devices;
cl_uint m_num_input_programs;
std::vector<const cvk_program*> m_input_programs;
std::vector<const char*> m_header_include_names;
build_operation m_operation;
cl_program_binary_type m_binary_type;
cvk_program_callback m_operation_callback;
void* m_operation_callback_data;
std::mutex m_lock;
std::unique_ptr<std::thread> m_thread;
std::string m_source;
std::vector<uint8_t> m_ir;
std::vector<uint8_t> m_il;
VkShaderModule m_shader_module;
std::unordered_map<const cvk_device*, std::atomic<cl_build_status>>
m_dev_status;
std::string m_build_options;
spir_binary m_binary;
std::string m_build_log;
std::vector<cvk_sampler_holder> m_literal_samplers;
VkPushConstantRange m_push_constant_range;
std::unordered_map<std::string, std::shared_ptr<cvk_entry_point>>
m_entry_points;
std::vector<uint32_t> m_stripped_binary;
VkPipelineCache m_pipeline_cache;
std::unique_ptr<cvk_buffer> m_module_constant_data_buffer;
std::unordered_map<uint32_t, user_spec_constant_data> m_user_spec_constants;
};
static inline cvk_program* icd_downcast(cl_program program) {
return static_cast<cvk_program*>(program);
}
using cvk_program_holder = refcounted_holder<cvk_program>;