blob: f4f7a61def8e906438d00d3428039b42a78e5ce2 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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 "hal/vulkan/pipeline_executable.h"
#include "absl/container/inlined_vector.h"
#include "base/memory.h"
#include "base/source_location.h"
#include "base/status.h"
#include "base/tracing.h"
#include "hal/vulkan/status_util.h"
namespace iree {
namespace hal {
namespace vulkan {
namespace {
// Generates the baked specialization constant data based on the flatbuffer.
// We only support uint32_t right now so this is easy.
// Note that the returned vectors are referenced by pointers in |out_info| and
// must remain valid until the info is no longer in use.
std::pair<std::vector<VkSpecializationMapEntry>, std::vector<uint8_t>>
PopulateSpecializationInfo(const VkSpecializationInfoDef* info_def) {
int entry_count =
info_def && info_def->map_entries() ? info_def->map_entries()->size() : 0;
if (!entry_count) {
return {};
}
std::vector<VkSpecializationMapEntry> entries;
entries.reserve(entry_count);
std::vector<uint8_t> data;
data.resize(entry_count * sizeof(uint32_t));
uint32_t offset = 0;
for (const auto* entry_def : *info_def->map_entries()) {
if (!entry_def) continue;
entries.push_back({});
auto& entry = entries.back();
entry.constantID = entry_def->constant_id();
entry.offset = offset;
entry.size = sizeof(uint32_t);
uint32_t value = entry_def->uint32_value();
std::memcpy(data.data() + offset, &value, sizeof(value));
offset += entry.size;
}
return {std::move(entries), std::move(data)};
}
} // namespace
// static
StatusOr<ref_ptr<PipelineExecutable>> PipelineExecutable::Create(
const ref_ptr<VkDeviceHandle>& logical_device,
VkPipelineCache pipeline_cache, VkPipelineLayout pipeline_layout,
PipelineDescriptorSets descriptor_sets, ExecutableCachingModeBitfield mode,
const SpirVExecutableDef& spirv_executable_def) {
IREE_TRACE_SCOPE0("PipelineExecutable::Create");
const auto& syms = logical_device->syms();
if (!spirv_executable_def.entry_points() ||
spirv_executable_def.entry_points()->size() == 0) {
return InvalidArgumentErrorBuilder(IREE_LOC) << "No entry points defined";
}
if (!spirv_executable_def.code()) {
return InvalidArgumentErrorBuilder(IREE_LOC) << "No SPIR-V code present";
}
const auto& code = *spirv_executable_def.code();
// Create the shader module.
VkShaderModuleCreateInfo shader_module_create_info;
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_module_create_info.pNext = nullptr;
shader_module_create_info.flags = 0;
shader_module_create_info.codeSize = code.size() * sizeof(uint32_t);
shader_module_create_info.pCode = code.data();
VkShaderModule shader_module = VK_NULL_HANDLE;
VK_RETURN_IF_ERROR(
syms->vkCreateShaderModule(*logical_device, &shader_module_create_info,
logical_device->allocator(), &shader_module));
// We only need to keep this around during pipeline creation so ensure we
// always clean it up when we exit this function.
auto shader_module_cleanup = MakeCleanup([&logical_device, shader_module]() {
logical_device->syms()->vkDestroyShaderModule(
*logical_device, shader_module, logical_device->allocator());
});
// Specialization info is currently constant against all entry points.
std::vector<VkSpecializationMapEntry> spec_entries;
std::vector<uint8_t> spec_data;
std::tie(spec_entries, spec_data) =
PopulateSpecializationInfo(spirv_executable_def.specialization_info());
VkSpecializationInfo specialization_info;
specialization_info.mapEntryCount = spec_entries.size();
specialization_info.pMapEntries = spec_entries.data();
specialization_info.dataSize = spec_data.size();
specialization_info.pData = spec_data.data();
// Create pipelines for each entry point.
const auto& entry_points = *spirv_executable_def.entry_points();
absl::InlinedVector<VkComputePipelineCreateInfo, 1> pipeline_create_infos;
pipeline_create_infos.resize(entry_points.size());
for (int entry_ordinal = 0; entry_ordinal < entry_points.size();
++entry_ordinal) {
auto& pipeline_create_info = pipeline_create_infos[entry_ordinal];
pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipeline_create_info.pNext = nullptr;
pipeline_create_info.flags = 0;
if (!AllBitsSet(mode, ExecutableCachingMode::kAllowOptimization)) {
pipeline_create_info.flags |= VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT;
}
if (entry_ordinal == 0) {
pipeline_create_info.flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
} else {
pipeline_create_info.flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
}
pipeline_create_info.layout = pipeline_layout;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.basePipelineIndex = 0;
auto& stage_create_info = pipeline_create_info.stage;
stage_create_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage_create_info.pNext = nullptr;
stage_create_info.flags = 0;
stage_create_info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stage_create_info.module = shader_module;
stage_create_info.pName = entry_points[entry_ordinal]->c_str();
stage_create_info.pSpecializationInfo = &specialization_info;
}
absl::InlinedVector<VkPipeline, 1> pipelines;
pipelines.resize(entry_points.size());
// Some ICDs appear to leak in here, out of our control.
// Warning: leak checks remain disabled if an error is returned.
IREE_DISABLE_LEAK_CHECKS();
VK_RETURN_IF_ERROR(syms->vkCreateComputePipelines(
*logical_device, pipeline_cache, pipeline_create_infos.size(),
pipeline_create_infos.data(), logical_device->allocator(),
pipelines.data()));
IREE_ENABLE_LEAK_CHECKS();
auto executable =
make_ref<PipelineExecutable>(CtorKey{}, logical_device, pipeline_layout,
descriptor_sets, std::move(pipelines));
executable->tag_ =
spirv_executable_def.tag() ? spirv_executable_def.tag()->str() : "";
return executable;
}
PipelineExecutable::PipelineExecutable(
CtorKey ctor_key, const ref_ptr<VkDeviceHandle>& logical_device,
VkPipelineLayout pipeline_layout, PipelineDescriptorSets descriptor_sets,
absl::InlinedVector<VkPipeline, 1> pipelines)
: logical_device_(add_ref(logical_device)),
pipeline_layout_(pipeline_layout),
descriptor_sets_(descriptor_sets),
pipelines_(std::move(pipelines)) {}
PipelineExecutable::~PipelineExecutable() {
IREE_TRACE_SCOPE0("PipelineExecutable::dtor");
for (auto pipeline : pipelines_) {
syms()->vkDestroyPipeline(*logical_device_, pipeline,
logical_device_->allocator());
}
pipelines_.clear();
}
StatusOr<VkPipeline> PipelineExecutable::GetPipelineForEntryPoint(
int entry_ordinal) const {
if (entry_ordinal < 0 || entry_ordinal >= pipelines_.size()) {
return OutOfRangeErrorBuilder(IREE_LOC) << "Invalid entry point ordinal";
}
return pipelines_[entry_ordinal];
}
} // namespace vulkan
} // namespace hal
} // namespace iree