123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- //===--- CapabilityVisitor.cpp - Capability Visitor --------------*- C++ -*-==//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- #include "CapabilityVisitor.h"
- #include "clang/SPIRV/SpirvBuilder.h"
- namespace clang {
- namespace spirv {
- void CapabilityVisitor::addExtension(Extension ext, llvm::StringRef target,
- SourceLocation loc) {
- featureManager.requestExtension(ext, target, loc);
- // Do not emit OpExtension if the given extension is natively supported in
- // the target environment.
- if (featureManager.isExtensionRequiredForTargetEnv(ext))
- spvBuilder.requireExtension(featureManager.getExtensionName(ext), loc);
- }
- void CapabilityVisitor::addCapability(spv::Capability cap, SourceLocation loc) {
- if (cap != spv::Capability::Max) {
- spvBuilder.requireCapability(cap, loc);
- }
- }
- void CapabilityVisitor::addCapabilityForType(const SpirvType *type,
- SourceLocation loc,
- spv::StorageClass sc) {
- // Defent against instructions that do not have a return type.
- if (!type)
- return;
- // Integer-related capabilities
- if (const auto *intType = dyn_cast<IntegerType>(type)) {
- switch (intType->getBitwidth()) {
- case 16: {
- // Usage of a 16-bit integer type.
- addCapability(spv::Capability::Int16);
- // Usage of a 16-bit integer type as stage I/O.
- if (sc == spv::StorageClass::Input || sc == spv::StorageClass::Output) {
- addExtension(Extension::KHR_16bit_storage, "16-bit stage IO variables",
- loc);
- addCapability(spv::Capability::StorageInputOutput16);
- }
- break;
- }
- case 64: {
- addCapability(spv::Capability::Int64);
- break;
- }
- default:
- break;
- }
- }
- // Float-related capabilities
- else if (const auto *floatType = dyn_cast<FloatType>(type)) {
- switch (floatType->getBitwidth()) {
- case 16: {
- // Usage of a 16-bit float type.
- addCapability(spv::Capability::Float16);
- // Usage of a 16-bit float type as stage I/O.
- if (sc == spv::StorageClass::Input || sc == spv::StorageClass::Output) {
- addExtension(Extension::KHR_16bit_storage, "16-bit stage IO variables",
- loc);
- addCapability(spv::Capability::StorageInputOutput16);
- }
- break;
- }
- case 64: {
- addCapability(spv::Capability::Float64);
- break;
- }
- default:
- break;
- }
- }
- // Vectors
- else if (const auto *vecType = dyn_cast<VectorType>(type)) {
- addCapabilityForType(vecType->getElementType(), loc, sc);
- }
- // Matrices
- else if (const auto *matType = dyn_cast<MatrixType>(type)) {
- addCapabilityForType(matType->getElementType(), loc, sc);
- }
- // Arrays
- else if (const auto *arrType = dyn_cast<ArrayType>(type)) {
- addCapabilityForType(arrType->getElementType(), loc, sc);
- }
- // Runtime array of resources requires additional capability.
- else if (const auto *raType = dyn_cast<RuntimeArrayType>(type)) {
- if (SpirvType::isResourceType(raType->getElementType())) {
- // the elements inside the runtime array are resources
- addExtension(Extension::EXT_descriptor_indexing,
- "runtime array of resources", loc);
- addCapability(spv::Capability::RuntimeDescriptorArrayEXT);
- }
- addCapabilityForType(raType->getElementType(), loc, sc);
- }
- // Image types
- else if (const auto *imageType = dyn_cast<ImageType>(type)) {
- switch (imageType->getDimension()) {
- case spv::Dim::Buffer: {
- addCapability(spv::Capability::SampledBuffer);
- if (imageType->withSampler() == ImageType::WithSampler::No) {
- addCapability(spv::Capability::ImageBuffer);
- }
- break;
- }
- case spv::Dim::Dim1D: {
- if (imageType->withSampler() == ImageType::WithSampler::No) {
- addCapability(spv::Capability::Image1D);
- } else {
- addCapability(spv::Capability::Sampled1D);
- }
- break;
- }
- case spv::Dim::SubpassData: {
- addCapability(spv::Capability::InputAttachment);
- break;
- }
- default:
- break;
- }
- switch (imageType->getImageFormat()) {
- case spv::ImageFormat::Rg32f:
- case spv::ImageFormat::Rg16f:
- case spv::ImageFormat::R11fG11fB10f:
- case spv::ImageFormat::R16f:
- case spv::ImageFormat::Rgba16:
- case spv::ImageFormat::Rgb10A2:
- case spv::ImageFormat::Rg16:
- case spv::ImageFormat::Rg8:
- case spv::ImageFormat::R16:
- case spv::ImageFormat::R8:
- case spv::ImageFormat::Rgba16Snorm:
- case spv::ImageFormat::Rg16Snorm:
- case spv::ImageFormat::Rg8Snorm:
- case spv::ImageFormat::R16Snorm:
- case spv::ImageFormat::R8Snorm:
- case spv::ImageFormat::Rg32i:
- case spv::ImageFormat::Rg16i:
- case spv::ImageFormat::Rg8i:
- case spv::ImageFormat::R16i:
- case spv::ImageFormat::R8i:
- case spv::ImageFormat::Rgb10a2ui:
- case spv::ImageFormat::Rg32ui:
- case spv::ImageFormat::Rg16ui:
- case spv::ImageFormat::Rg8ui:
- case spv::ImageFormat::R16ui:
- case spv::ImageFormat::R8ui:
- addCapability(spv::Capability::StorageImageExtendedFormats);
- break;
- default:
- // Only image formats requiring extended formats are relevant. The rest
- // just pass through.
- break;
- }
- if (imageType->isArrayedImage() && imageType->isMSImage())
- addCapability(spv::Capability::ImageMSArray);
- addCapabilityForType(imageType->getSampledType(), loc, sc);
- }
- // Sampled image type
- else if (const auto *sampledImageType = dyn_cast<SampledImageType>(type)) {
- addCapabilityForType(sampledImageType->getImageType(), loc, sc);
- }
- // Pointer type
- else if (const auto *ptrType = dyn_cast<SpirvPointerType>(type)) {
- addCapabilityForType(ptrType->getPointeeType(), loc, sc);
- }
- // Struct type
- else if (const auto *structType = dyn_cast<StructType>(type)) {
- if (SpirvType::isOrContainsType<NumericalType, 16>(structType)) {
- addExtension(Extension::KHR_16bit_storage, "16-bit types in resource",
- loc);
- if (sc == spv::StorageClass::PushConstant) {
- addCapability(spv::Capability::StoragePushConstant16);
- } else if (structType->getInterfaceType() ==
- StructInterfaceType::UniformBuffer) {
- addCapability(spv::Capability::StorageUniform16);
- } else if (structType->getInterfaceType() ==
- StructInterfaceType::StorageBuffer) {
- addCapability(spv::Capability::StorageUniformBufferBlock16);
- }
- }
- for (auto field : structType->getFields())
- addCapabilityForType(field.type, loc, sc);
- }
- }
- bool CapabilityVisitor::visit(SpirvDecoration *decor) {
- const auto loc = decor->getSourceLocation();
- switch (decor->getDecoration()) {
- case spv::Decoration::Sample: {
- addCapability(spv::Capability::SampleRateShading, loc);
- break;
- }
- case spv::Decoration::NonUniformEXT: {
- addExtension(Extension::EXT_descriptor_indexing, "NonUniformEXT", loc);
- addCapability(spv::Capability::ShaderNonUniformEXT);
- break;
- }
- case spv::Decoration::HlslSemanticGOOGLE:
- case spv::Decoration::HlslCounterBufferGOOGLE: {
- addExtension(Extension::GOOGLE_hlsl_functionality1, "SPIR-V reflection",
- loc);
- break;
- }
- // Capabilities needed for built-ins
- case spv::Decoration::BuiltIn: {
- assert(decor->getParams().size() == 1);
- const auto builtin = static_cast<spv::BuiltIn>(decor->getParams()[0]);
- switch (builtin) {
- case spv::BuiltIn::SampleId:
- case spv::BuiltIn::SamplePosition: {
- addCapability(spv::Capability::SampleRateShading, loc);
- break;
- }
- case spv::BuiltIn::SubgroupSize:
- case spv::BuiltIn::NumSubgroups:
- case spv::BuiltIn::SubgroupId:
- case spv::BuiltIn::SubgroupLocalInvocationId: {
- addCapability(spv::Capability::GroupNonUniform, loc);
- break;
- }
- case spv::BuiltIn::BaseVertex: {
- addExtension(Extension::KHR_shader_draw_parameters, "BaseVertex Builtin",
- loc);
- addCapability(spv::Capability::DrawParameters);
- break;
- }
- case spv::BuiltIn::BaseInstance: {
- addExtension(Extension::KHR_shader_draw_parameters,
- "BaseInstance Builtin", loc);
- addCapability(spv::Capability::DrawParameters);
- break;
- }
- case spv::BuiltIn::DrawIndex: {
- addExtension(Extension::KHR_shader_draw_parameters, "DrawIndex Builtin",
- loc);
- addCapability(spv::Capability::DrawParameters);
- break;
- }
- case spv::BuiltIn::DeviceIndex: {
- addExtension(Extension::KHR_device_group, "DeviceIndex Builtin", loc);
- addCapability(spv::Capability::DeviceGroup);
- break;
- }
- case spv::BuiltIn::FragStencilRefEXT: {
- addExtension(Extension::EXT_shader_stencil_export, "SV_StencilRef", loc);
- addCapability(spv::Capability::StencilExportEXT);
- break;
- }
- case spv::BuiltIn::ViewIndex: {
- addExtension(Extension::KHR_multiview, "SV_ViewID", loc);
- addCapability(spv::Capability::MultiView);
- break;
- }
- case spv::BuiltIn::FullyCoveredEXT: {
- addExtension(Extension::EXT_fragment_fully_covered, "SV_InnerCoverage",
- loc);
- addCapability(spv::Capability::FragmentFullyCoveredEXT);
- break;
- }
- case spv::BuiltIn::PrimitiveId: {
- // PrimitiveID can be used as PSIn
- if (shaderModel == spv::ExecutionModel::Fragment)
- addCapability(spv::Capability::Geometry);
- break;
- }
- case spv::BuiltIn::Layer: {
- if (shaderModel == spv::ExecutionModel::Vertex ||
- shaderModel == spv::ExecutionModel::TessellationControl ||
- shaderModel == spv::ExecutionModel::TessellationEvaluation) {
- addExtension(Extension::EXT_shader_viewport_index_layer,
- "SV_RenderTargetArrayIndex", loc);
- addCapability(spv::Capability::ShaderViewportIndexLayerEXT);
- } else if (shaderModel == spv::ExecutionModel::Fragment) {
- // SV_RenderTargetArrayIndex can be used as PSIn.
- addCapability(spv::Capability::Geometry);
- }
- break;
- }
- case spv::BuiltIn::ViewportIndex: {
- if (shaderModel == spv::ExecutionModel::Vertex ||
- shaderModel == spv::ExecutionModel::TessellationControl ||
- shaderModel == spv::ExecutionModel::TessellationEvaluation) {
- addExtension(Extension::EXT_shader_viewport_index_layer,
- "SV_ViewPortArrayIndex", loc);
- addCapability(spv::Capability::ShaderViewportIndexLayerEXT);
- } else if (shaderModel == spv::ExecutionModel::Fragment ||
- shaderModel == spv::ExecutionModel::Geometry) {
- // SV_ViewportArrayIndex can be used as PSIn.
- addCapability(spv::Capability::MultiViewport);
- }
- break;
- }
- case spv::BuiltIn::ClipDistance: {
- addCapability(spv::Capability::ClipDistance);
- break;
- }
- case spv::BuiltIn::CullDistance: {
- addCapability(spv::Capability::CullDistance);
- break;
- }
- case spv::BuiltIn::BaryCoordNoPerspAMD:
- case spv::BuiltIn::BaryCoordNoPerspCentroidAMD:
- case spv::BuiltIn::BaryCoordNoPerspSampleAMD:
- case spv::BuiltIn::BaryCoordSmoothAMD:
- case spv::BuiltIn::BaryCoordSmoothCentroidAMD:
- case spv::BuiltIn::BaryCoordSmoothSampleAMD:
- case spv::BuiltIn::BaryCoordPullModelAMD: {
- addExtension(Extension::AMD_shader_explicit_vertex_parameter,
- "SV_Barycentrics", loc);
- break;
- }
- case spv::BuiltIn::FragSizeEXT: {
- addExtension(Extension::EXT_fragment_invocation_density, "SV_ShadingRate",
- loc);
- addCapability(spv::Capability::FragmentDensityEXT);
- break;
- }
- default:
- break;
- }
- break;
- }
- default:
- break;
- }
- return true;
- }
- spv::Capability
- CapabilityVisitor::getNonUniformCapability(const SpirvType *type) {
- if (!type)
- return spv::Capability::Max;
- if (const auto *arrayType = dyn_cast<ArrayType>(type)) {
- return getNonUniformCapability(arrayType->getElementType());
- }
- if (SpirvType::isTexture(type) || SpirvType::isSampler(type)) {
- return spv::Capability::SampledImageArrayNonUniformIndexingEXT;
- }
- if (SpirvType::isRWTexture(type)) {
- return spv::Capability::StorageImageArrayNonUniformIndexingEXT;
- }
- if (SpirvType::isBuffer(type)) {
- return spv::Capability::UniformTexelBufferArrayNonUniformIndexingEXT;
- }
- if (SpirvType::isRWBuffer(type)) {
- return spv::Capability::StorageTexelBufferArrayNonUniformIndexingEXT;
- }
- if (SpirvType::isSubpassInput(type) || SpirvType::isSubpassInputMS(type)) {
- return spv::Capability::InputAttachmentArrayNonUniformIndexingEXT;
- }
- return spv::Capability::Max;
- }
- bool CapabilityVisitor::visit(SpirvImageQuery *instr) {
- addCapabilityForType(instr->getResultType(), instr->getSourceLocation(),
- instr->getStorageClass());
- addCapability(spv::Capability::ImageQuery);
- return true;
- }
- bool CapabilityVisitor::visit(SpirvImageSparseTexelsResident *instr) {
- addCapabilityForType(instr->getResultType(), instr->getSourceLocation(),
- instr->getStorageClass());
- addCapability(spv::Capability::ImageGatherExtended);
- addCapability(spv::Capability::SparseResidency);
- return true;
- }
- bool CapabilityVisitor::visit(SpirvImageOp *instr) {
- addCapabilityForType(instr->getResultType(), instr->getSourceLocation(),
- instr->getStorageClass());
- if (instr->hasOffset() || instr->hasConstOffsets())
- addCapability(spv::Capability::ImageGatherExtended);
- if (instr->hasMinLod())
- addCapability(spv::Capability::MinLod);
- if (instr->isSparse())
- addCapability(spv::Capability::SparseResidency);
- return true;
- }
- bool CapabilityVisitor::visitInstruction(SpirvInstruction *instr) {
- const SpirvType *resultType = instr->getResultType();
- const auto opcode = instr->getopcode();
- const auto loc = instr->getSourceLocation();
- // Add result-type-specific capabilities
- addCapabilityForType(resultType, loc, instr->getStorageClass());
- // Add NonUniform capabilities if necessary
- if (instr->isNonUniform()) {
- addExtension(Extension::EXT_descriptor_indexing, "NonUniformEXT", loc);
- addCapability(spv::Capability::ShaderNonUniformEXT);
- addCapability(getNonUniformCapability(resultType));
- }
- // Add opcode-specific capabilities
- switch (opcode) {
- case spv::Op::OpDPdxCoarse:
- case spv::Op::OpDPdyCoarse:
- case spv::Op::OpFwidthCoarse:
- case spv::Op::OpDPdxFine:
- case spv::Op::OpDPdyFine:
- case spv::Op::OpFwidthFine:
- addCapability(spv::Capability::DerivativeControl);
- break;
- case spv::Op::OpGroupNonUniformElect:
- addCapability(spv::Capability::GroupNonUniform);
- break;
- case spv::Op::OpGroupNonUniformAny:
- case spv::Op::OpGroupNonUniformAll:
- case spv::Op::OpGroupNonUniformAllEqual:
- addCapability(spv::Capability::GroupNonUniformVote);
- break;
- case spv::Op::OpGroupNonUniformBallot:
- case spv::Op::OpGroupNonUniformInverseBallot:
- case spv::Op::OpGroupNonUniformBallotBitExtract:
- case spv::Op::OpGroupNonUniformBallotBitCount:
- case spv::Op::OpGroupNonUniformBallotFindLSB:
- case spv::Op::OpGroupNonUniformBallotFindMSB:
- case spv::Op::OpGroupNonUniformBroadcast:
- case spv::Op::OpGroupNonUniformBroadcastFirst:
- addCapability(spv::Capability::GroupNonUniformBallot);
- break;
- case spv::Op::OpGroupNonUniformIAdd:
- case spv::Op::OpGroupNonUniformFAdd:
- case spv::Op::OpGroupNonUniformIMul:
- case spv::Op::OpGroupNonUniformFMul:
- case spv::Op::OpGroupNonUniformSMax:
- case spv::Op::OpGroupNonUniformUMax:
- case spv::Op::OpGroupNonUniformFMax:
- case spv::Op::OpGroupNonUniformSMin:
- case spv::Op::OpGroupNonUniformUMin:
- case spv::Op::OpGroupNonUniformFMin:
- case spv::Op::OpGroupNonUniformBitwiseAnd:
- case spv::Op::OpGroupNonUniformBitwiseOr:
- case spv::Op::OpGroupNonUniformBitwiseXor:
- case spv::Op::OpGroupNonUniformLogicalAnd:
- case spv::Op::OpGroupNonUniformLogicalOr:
- case spv::Op::OpGroupNonUniformLogicalXor:
- addCapability(spv::Capability::GroupNonUniformArithmetic);
- break;
- case spv::Op::OpGroupNonUniformQuadBroadcast:
- case spv::Op::OpGroupNonUniformQuadSwap:
- addCapability(spv::Capability::GroupNonUniformQuad);
- break;
- case spv::Op::OpVariable: {
- if (spvOptions.enableReflect &&
- !cast<SpirvVariable>(instr)->getHlslUserType().empty()) {
- addExtension(Extension::GOOGLE_user_type, "HLSL User Type", loc);
- addExtension(Extension::GOOGLE_hlsl_functionality1, "HLSL User Type",
- loc);
- }
- break;
- }
- default:
- break;
- }
- return true;
- }
- bool CapabilityVisitor::visit(SpirvEntryPoint *entryPoint) {
- shaderModel = entryPoint->getExecModel();
- switch (shaderModel) {
- case spv::ExecutionModel::Fragment:
- case spv::ExecutionModel::Vertex:
- case spv::ExecutionModel::GLCompute:
- addCapability(spv::Capability::Shader);
- break;
- case spv::ExecutionModel::Geometry:
- addCapability(spv::Capability::Geometry);
- break;
- case spv::ExecutionModel::TessellationControl:
- case spv::ExecutionModel::TessellationEvaluation:
- addCapability(spv::Capability::Tessellation);
- break;
- case spv::ExecutionModel::RayGenerationNV:
- case spv::ExecutionModel::IntersectionNV:
- case spv::ExecutionModel::ClosestHitNV:
- case spv::ExecutionModel::AnyHitNV:
- case spv::ExecutionModel::MissNV:
- case spv::ExecutionModel::CallableNV:
- addCapability(spv::Capability::RayTracingNV);
- addExtension(Extension::NV_ray_tracing, "SPV_NV_ray_tracing", {});
- break;
- default:
- llvm_unreachable("found unknown shader model");
- break;
- }
- return true;
- }
- bool CapabilityVisitor::visit(SpirvExecutionMode *execMode) {
- if (execMode->getExecutionMode() == spv::ExecutionMode::PostDepthCoverage) {
- addCapability(spv::Capability::SampleMaskPostDepthCoverage,
- execMode->getEntryPoint()->getSourceLocation());
- addExtension(Extension::KHR_post_depth_coverage,
- "[[vk::post_depth_coverage]]", execMode->getSourceLocation());
- }
- return true;
- }
- bool CapabilityVisitor::visit(SpirvExtInst *instr) {
- // OpExtInst using the GLSL extended instruction allows only 32-bit types by
- // default. The AMD_gpu_shader_half_float extension adds support for 16-bit
- // floating-point component types for the following instructions described in
- // the GLSL.std.450 extended instruction set:
- // Acos, Acosh, Asin, Asinh, Atan2, Atanh, Atan, Cos, Cosh, Degrees, Exp,
- // Exp2, InterpolateAtCentroid, InterpolateAtSample, InterpolateAtOffset, Log,
- // Log2, Pow, Radians, Sin, Sinh, Tan, Tanh
- if (SpirvType::isOrContainsType<FloatType, 16>(instr->getResultType()))
- switch (instr->getInstruction()) {
- case GLSLstd450::GLSLstd450Acos:
- case GLSLstd450::GLSLstd450Acosh:
- case GLSLstd450::GLSLstd450Asin:
- case GLSLstd450::GLSLstd450Asinh:
- case GLSLstd450::GLSLstd450Atan2:
- case GLSLstd450::GLSLstd450Atanh:
- case GLSLstd450::GLSLstd450Atan:
- case GLSLstd450::GLSLstd450Cos:
- case GLSLstd450::GLSLstd450Cosh:
- case GLSLstd450::GLSLstd450Degrees:
- case GLSLstd450::GLSLstd450Exp:
- case GLSLstd450::GLSLstd450Exp2:
- case GLSLstd450::GLSLstd450InterpolateAtCentroid:
- case GLSLstd450::GLSLstd450InterpolateAtSample:
- case GLSLstd450::GLSLstd450InterpolateAtOffset:
- case GLSLstd450::GLSLstd450Log:
- case GLSLstd450::GLSLstd450Log2:
- case GLSLstd450::GLSLstd450Pow:
- case GLSLstd450::GLSLstd450Radians:
- case GLSLstd450::GLSLstd450Sin:
- case GLSLstd450::GLSLstd450Sinh:
- case GLSLstd450::GLSLstd450Tan:
- case GLSLstd450::GLSLstd450Tanh:
- addExtension(Extension::AMD_gpu_shader_half_float, "16-bit float",
- instr->getSourceLocation());
- default:
- break;
- }
- return visitInstruction(instr);
- }
- } // end namespace spirv
- } // end namespace clang
|