blob: f940c871d094f98aae7fa8943c8c170c26b76ee3 [file] [log] [blame]
// Copyright 2019 The Amber 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 "src/clspv_helper.h"
#include <unordered_map>
#include <utility>
#include "clspv/ArgKind.h"
#include "clspv/Compiler.h"
#include "clspv/Sampler.h"
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/optimizer.hpp"
#include "spirv/unified1/NonSemanticClspvReflection.h"
#include "spirv/unified1/spirv.hpp"
using amber::Pipeline;
namespace {
struct ReflectionHelper {
Pipeline::ShaderInfo* shader_info = nullptr;
Pipeline* pipeline = nullptr;
uint32_t uint_id = 0;
std::unordered_map<uint32_t, std::string> strings;
std::unordered_map<uint32_t, uint32_t> constants;
std::string error_message;
};
Pipeline::ShaderInfo::DescriptorMapEntry::Kind GetArgKindFromExtInst(
uint32_t value) {
switch (static_cast<NonSemanticClspvReflectionInstructions>(value)) {
case NonSemanticClspvReflectionArgumentStorageBuffer:
case NonSemanticClspvReflectionConstantDataStorageBuffer:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
case NonSemanticClspvReflectionArgumentUniform:
case NonSemanticClspvReflectionConstantDataUniform:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO;
case NonSemanticClspvReflectionArgumentPodStorageBuffer:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
case NonSemanticClspvReflectionArgumentPodUniform:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO;
case NonSemanticClspvReflectionArgumentPodPushConstant:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
case NonSemanticClspvReflectionArgumentSampledImage:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE;
case NonSemanticClspvReflectionArgumentStorageImage:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE;
case NonSemanticClspvReflectionArgumentSampler:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER;
case NonSemanticClspvReflectionArgumentWorkgroup:
default:
return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
}
}
spv_result_t ParseExtendedInst(ReflectionHelper* helper,
const spv_parsed_instruction_t* inst) {
auto ext_inst = inst->words[inst->operands[3].offset];
switch (ext_inst) {
case NonSemanticClspvReflectionKernel: {
// Remap the name string to the declaration's result id.
const auto& name = helper->strings[inst->words[inst->operands[5].offset]];
helper->strings[inst->result_id] = name;
break;
}
case NonSemanticClspvReflectionArgumentInfo: {
// Remap the name string to the info's result id.
const auto& name = helper->strings[inst->words[inst->operands[4].offset]];
helper->strings[inst->result_id] = name;
break;
}
case NonSemanticClspvReflectionArgumentStorageBuffer:
case NonSemanticClspvReflectionArgumentUniform:
case NonSemanticClspvReflectionArgumentSampledImage:
case NonSemanticClspvReflectionArgumentStorageImage:
case NonSemanticClspvReflectionArgumentSampler: {
// These arguments have descriptor set and binding.
auto kernel_id = inst->words[inst->operands[4].offset];
auto ordinal_id = inst->words[inst->operands[5].offset];
auto ds_id = inst->words[inst->operands[6].offset];
auto binding_id = inst->words[inst->operands[7].offset];
std::string arg_name;
if (inst->num_operands == 9) {
arg_name = helper->strings[inst->words[inst->operands[8].offset]];
}
auto kind = GetArgKindFromExtInst(ext_inst);
Pipeline::ShaderInfo::DescriptorMapEntry entry{
arg_name,
kind,
helper->constants[ds_id],
helper->constants[binding_id],
helper->constants[ordinal_id],
/* offset */ 0,
/* size */ 0};
helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
std::move(entry));
break;
}
case NonSemanticClspvReflectionArgumentPodStorageBuffer:
case NonSemanticClspvReflectionArgumentPodUniform: {
// These arguments have descriptor set, binding, offset and size.
auto kernel_id = inst->words[inst->operands[4].offset];
auto ordinal_id = inst->words[inst->operands[5].offset];
auto ds_id = inst->words[inst->operands[6].offset];
auto binding_id = inst->words[inst->operands[7].offset];
auto offset_id = inst->words[inst->operands[8].offset];
auto size_id = inst->words[inst->operands[9].offset];
std::string arg_name;
if (inst->num_operands == 11) {
arg_name = helper->strings[inst->words[inst->operands[10].offset]];
}
auto kind = GetArgKindFromExtInst(ext_inst);
Pipeline::ShaderInfo::DescriptorMapEntry entry{
arg_name,
kind,
helper->constants[ds_id],
helper->constants[binding_id],
helper->constants[ordinal_id],
helper->constants[offset_id],
helper->constants[size_id]};
helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
std::move(entry));
break;
}
case NonSemanticClspvReflectionArgumentPodPushConstant: {
// These arguments have offset and size.
auto kernel_id = inst->words[inst->operands[4].offset];
auto ordinal_id = inst->words[inst->operands[5].offset];
auto offset_id = inst->words[inst->operands[6].offset];
auto size_id = inst->words[inst->operands[7].offset];
std::string arg_name;
if (inst->num_operands == 9) {
arg_name = helper->strings[inst->words[inst->operands[8].offset]];
}
auto kind = GetArgKindFromExtInst(ext_inst);
Pipeline::ShaderInfo::DescriptorMapEntry entry{
arg_name,
kind,
/* descriptor set */ 0,
/* binding */ 0,
helper->constants[ordinal_id],
helper->constants[offset_id],
helper->constants[size_id]};
helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
std::move(entry));
break;
}
case NonSemanticClspvReflectionArgumentWorkgroup:
helper->error_message = "Workgroup arguments are not currently supported";
return SPV_ERROR_INVALID_DATA;
case NonSemanticClspvReflectionConstantDataStorageBuffer:
case NonSemanticClspvReflectionConstantDataUniform:
helper->error_message =
"Constant descriptor entries are not currently supported";
return SPV_ERROR_INVALID_DATA;
case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
case NonSemanticClspvReflectionSpecConstantGlobalOffset:
case NonSemanticClspvReflectionSpecConstantWorkDim:
// Nothing to do. Amber currently requires script authors to know the
// spec ids and use them directly.
break;
case NonSemanticClspvReflectionPushConstantGlobalOffset: {
auto offset_id = inst->words[inst->operands[4].offset];
auto size_id = inst->words[inst->operands[5].offset];
Pipeline::ShaderInfo::PushConstant push_constant{
Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset,
helper->constants[offset_id], helper->constants[size_id]};
helper->shader_info->AddPushConstant(std::move(push_constant));
break;
}
case NonSemanticClspvReflectionPushConstantRegionOffset: {
auto offset_id = inst->words[inst->operands[4].offset];
auto size_id = inst->words[inst->operands[5].offset];
Pipeline::ShaderInfo::PushConstant push_constant{
Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset,
helper->constants[offset_id], helper->constants[size_id]};
helper->shader_info->AddPushConstant(std::move(push_constant));
break;
}
case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
case NonSemanticClspvReflectionPushConstantGlobalSize:
case NonSemanticClspvReflectionPushConstantNumWorkgroups:
case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
helper->error_message = "Unsupported push constant";
return SPV_ERROR_INVALID_DATA;
case NonSemanticClspvReflectionLiteralSampler: {
auto ds_id = inst->words[inst->operands[4].offset];
auto binding_id = inst->words[inst->operands[5].offset];
auto mask_id = inst->words[inst->operands[6].offset];
helper->pipeline->AddSampler(helper->constants[mask_id],
helper->constants[ds_id],
helper->constants[binding_id]);
break;
} break;
}
return SPV_SUCCESS;
}
spv_result_t ParseReflection(void* user_data,
const spv_parsed_instruction_t* inst) {
auto* helper = reinterpret_cast<ReflectionHelper*>(user_data);
switch (inst->opcode) {
case spv::OpTypeInt:
if (inst->words[inst->operands[1].offset] == 32 &&
inst->words[inst->operands[2].offset] == 0) {
// Track the result id of OpTypeInt 32 0.
helper->uint_id = inst->result_id;
}
break;
case spv::OpConstant:
if (inst->words[inst->operands[0].offset] == helper->uint_id) {
// Record the values for all uint32_t constants.
uint32_t value = inst->words[inst->operands[2].offset];
helper->constants[inst->result_id] = value;
}
break;
case spv::OpString: {
// Track all strings.
std::string value =
reinterpret_cast<const char*>(inst->words + inst->operands[1].offset);
helper->strings[inst->result_id] = value;
break;
}
case spv::OpExtInst:
if (inst->ext_inst_type ==
SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
return ParseExtendedInst(helper, inst);
}
break;
}
return SPV_SUCCESS;
}
} // anonymous namespace
namespace amber {
namespace clspvhelper {
Result Compile(Pipeline::ShaderInfo* shader_info,
Pipeline* pipeline,
spv_target_env env,
std::vector<uint32_t>* generated_binary) {
const auto& src_str = shader_info->GetShader()->GetData();
std::string options;
for (const auto& option : shader_info->GetCompileOptions()) {
options += option + " ";
}
std::string error_log;
if (clspv::CompileFromSourceString(src_str, /* sampler map */ "", options,
generated_binary, &error_log)) {
return Result("Clspv compile error: " + error_log);
}
// Parse the reflection instructions.
ReflectionHelper helper;
helper.shader_info = shader_info;
helper.pipeline = pipeline;
spv_context context(spvContextCreate(env));
if (spvBinaryParse(context, &helper, generated_binary->data(),
generated_binary->size(), nullptr, ParseReflection,
nullptr)) {
return Result(helper.error_message);
}
// Strip the reflection instructions to avoid requiring the implementation to
// support VK_KHR_shader_non_semantic_info.
spvtools::Optimizer opt(env);
opt.RegisterPass(spvtools::CreateStripNonSemanticInfoPass());
std::vector<uint32_t> stripped;
if (!opt.Run(generated_binary->data(), generated_binary->size(), &stripped)) {
return Result("failed to strip reflection instructions");
}
generated_binary->swap(stripped);
return Result();
}
} // namespace clspvhelper
} // namespace amber