blob: e412bf6ffb3d0eccc672beb93dd4d3758dabecff [file] [log] [blame] [edit]
///////////////////////////////////////////////////////////////////////////////
// //
// DxilShaderFlags.cpp //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DXIL/DxilShaderFlags.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilResource.h"
#include "dxc/Support/Global.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Constants.h"
#include "llvm/Support/Casting.h"
#include "dxc/DXIL/DxilEntryProps.h"
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DXIL/DxilResourceProperties.h"
#include "dxc/DXIL/DxilUtil.h"
using namespace hlsl;
using namespace llvm;
ShaderFlags::ShaderFlags():
m_bDisableOptimizations(false)
, m_bDisableMathRefactoring(false)
, m_bEnableDoublePrecision(false)
, m_bForceEarlyDepthStencil(false)
, m_bEnableRawAndStructuredBuffers(false)
, m_bLowPrecisionPresent(false)
, m_bEnableDoubleExtensions(false)
, m_bEnableMSAD(false)
, m_bAllResourcesBound(false)
, m_bViewportAndRTArrayIndex(false)
, m_bInnerCoverage(false)
, m_bStencilRef(false)
, m_bTiledResources(false)
, m_bUAVLoadAdditionalFormats(false)
, m_bLevel9ComparisonFiltering(false)
, m_b64UAVs(false)
, m_UAVsAtEveryStage(false)
, m_bCSRawAndStructuredViaShader4X(false)
, m_bROVS(false)
, m_bWaveOps(false)
, m_bInt64Ops(false)
, m_bViewID(false)
, m_bBarycentrics(false)
, m_bUseNativeLowPrecision(false)
, m_bShadingRate(false)
, m_bRaytracingTier1_1(false)
, m_bSamplerFeedback(false)
, m_align0(0)
, m_align1(0)
{}
uint64_t ShaderFlags::GetFeatureInfo() const {
uint64_t Flags = 0;
Flags |= m_bEnableDoublePrecision ? hlsl::DXIL::ShaderFeatureInfo_Doubles : 0;
Flags |= m_bLowPrecisionPresent && !m_bUseNativeLowPrecision
? hlsl::DXIL::ShaderFeatureInfo_MinimumPrecision
: 0;
Flags |= m_bLowPrecisionPresent && m_bUseNativeLowPrecision
? hlsl::DXIL::ShaderFeatureInfo_NativeLowPrecision
: 0;
Flags |= m_bEnableDoubleExtensions
? hlsl::DXIL::ShaderFeatureInfo_11_1_DoubleExtensions
: 0;
Flags |= m_bWaveOps ? hlsl::DXIL::ShaderFeatureInfo_WaveOps : 0;
Flags |= m_bInt64Ops ? hlsl::DXIL::ShaderFeatureInfo_Int64Ops : 0;
Flags |= m_bROVS ? hlsl::DXIL::ShaderFeatureInfo_ROVs : 0;
Flags |=
m_bViewportAndRTArrayIndex
? hlsl::DXIL::
ShaderFeatureInfo_ViewportAndRTArrayIndexFromAnyShaderFeedingRasterizer
: 0;
Flags |= m_bInnerCoverage ? hlsl::DXIL::ShaderFeatureInfo_InnerCoverage : 0;
Flags |= m_bStencilRef ? hlsl::DXIL::ShaderFeatureInfo_StencilRef : 0;
Flags |= m_bTiledResources ? hlsl::DXIL::ShaderFeatureInfo_TiledResources : 0;
Flags |=
m_bEnableMSAD ? hlsl::DXIL::ShaderFeatureInfo_11_1_ShaderExtensions : 0;
Flags |=
m_bCSRawAndStructuredViaShader4X
? hlsl::DXIL::
ShaderFeatureInfo_ComputeShadersPlusRawAndStructuredBuffersViaShader4X
: 0;
Flags |=
m_UAVsAtEveryStage ? hlsl::DXIL::ShaderFeatureInfo_UAVsAtEveryStage : 0;
Flags |= m_b64UAVs ? hlsl::DXIL::ShaderFeatureInfo_64UAVs : 0;
Flags |= m_bLevel9ComparisonFiltering
? hlsl::DXIL::ShaderFeatureInfo_LEVEL9ComparisonFiltering
: 0;
Flags |= m_bUAVLoadAdditionalFormats
? hlsl::DXIL::ShaderFeatureInfo_TypedUAVLoadAdditionalFormats
: 0;
Flags |= m_bViewID ? hlsl::DXIL::ShaderFeatureInfo_ViewID : 0;
Flags |= m_bBarycentrics ? hlsl::DXIL::ShaderFeatureInfo_Barycentrics : 0;
Flags |= m_bShadingRate ? hlsl::DXIL::ShaderFeatureInfo_ShadingRate : 0;
Flags |= m_bRaytracingTier1_1 ? hlsl::DXIL::ShaderFeatureInfo_Raytracing_Tier_1_1 : 0;
Flags |= m_bSamplerFeedback ? hlsl::DXIL::ShaderFeatureInfo_SamplerFeedback : 0;
return Flags;
}
uint64_t ShaderFlags::GetShaderFlagsRaw() const {
union Cast {
Cast(const ShaderFlags &flags) {
shaderFlags = flags;
}
ShaderFlags shaderFlags;
uint64_t rawData;
};
static_assert(sizeof(uint64_t) == sizeof(ShaderFlags),
"size must match to make sure no undefined bits when cast");
Cast rawCast(*this);
return rawCast.rawData;
}
void ShaderFlags::SetShaderFlagsRaw(uint64_t data) {
union Cast {
Cast(uint64_t data) {
rawData = data;
}
ShaderFlags shaderFlags;
uint64_t rawData;
};
Cast rawCast(data);
*this = rawCast.shaderFlags;
}
uint64_t ShaderFlags::GetShaderFlagsRawForCollection() {
// This should be all the flags that can be set by DxilModule::CollectShaderFlags.
ShaderFlags Flags;
Flags.SetEnableDoublePrecision(true);
Flags.SetInt64Ops(true);
Flags.SetLowPrecisionPresent(true);
Flags.SetEnableDoubleExtensions(true);
Flags.SetWaveOps(true);
Flags.SetTiledResources(true);
Flags.SetEnableMSAD(true);
Flags.SetUAVLoadAdditionalFormats(true);
Flags.SetStencilRef(true);
Flags.SetInnerCoverage(true);
Flags.SetViewportAndRTArrayIndex(true);
Flags.Set64UAVs(true);
Flags.SetUAVsAtEveryStage(true);
Flags.SetEnableRawAndStructuredBuffers(true);
Flags.SetCSRawAndStructuredViaShader4X(true);
Flags.SetViewID(true);
Flags.SetBarycentrics(true);
Flags.SetShadingRate(true);
Flags.SetRaytracingTier1_1(true);
Flags.SetSamplerFeedback(true);
return Flags.GetShaderFlagsRaw();
}
unsigned ShaderFlags::GetGlobalFlags() const {
unsigned Flags = 0;
Flags |= m_bDisableOptimizations ? DXIL::kDisableOptimizations : 0;
Flags |= m_bDisableMathRefactoring ? DXIL::kDisableMathRefactoring : 0;
Flags |= m_bEnableDoublePrecision ? DXIL::kEnableDoublePrecision : 0;
Flags |= m_bForceEarlyDepthStencil ? DXIL::kForceEarlyDepthStencil : 0;
Flags |= m_bEnableRawAndStructuredBuffers ? DXIL::kEnableRawAndStructuredBuffers : 0;
Flags |= m_bLowPrecisionPresent && !m_bUseNativeLowPrecision? DXIL::kEnableMinPrecision : 0;
Flags |= m_bEnableDoubleExtensions ? DXIL::kEnableDoubleExtensions : 0;
Flags |= m_bEnableMSAD ? DXIL::kEnableMSAD : 0;
Flags |= m_bAllResourcesBound ? DXIL::kAllResourcesBound : 0;
return Flags;
}
// Given a CreateHandle call, returns arbitrary ConstantInt rangeID
// Note: HLSL is currently assuming that rangeID is a constant value, but this code is assuming
// that it can be either constant, phi node, or select instruction
static ConstantInt *GetArbitraryConstantRangeID(CallInst *handleCall) {
Value *rangeID =
handleCall->getArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx);
ConstantInt *ConstantRangeID = dyn_cast<ConstantInt>(rangeID);
while (ConstantRangeID == nullptr) {
if (ConstantInt *CI = dyn_cast<ConstantInt>(rangeID)) {
ConstantRangeID = CI;
} else if (PHINode *PN = dyn_cast<PHINode>(rangeID)) {
rangeID = PN->getIncomingValue(0);
} else if (SelectInst *SI = dyn_cast<SelectInst>(rangeID)) {
rangeID = SI->getTrueValue();
} else {
return nullptr;
}
}
return ConstantRangeID;
}
// Given a handle type, find an arbitrary call instructions to create handle
static CallInst *FindCallToCreateHandle(Value *handleType) {
Value *curVal = handleType;
CallInst *CI = dyn_cast<CallInst>(handleType);
while (CI == nullptr) {
if (PHINode *PN = dyn_cast<PHINode>(curVal)) {
curVal = PN->getIncomingValue(0);
}
else if (SelectInst *SI = dyn_cast<SelectInst>(curVal)) {
curVal = SI->getTrueValue();
}
else {
return nullptr;
}
CI = dyn_cast<CallInst>(curVal);
}
return CI;
}
ShaderFlags ShaderFlags::CollectShaderFlags(const Function *F,
const hlsl::DxilModule *M) {
ShaderFlags flag;
// Module level options
flag.SetUseNativeLowPrecision(!M->GetUseMinPrecision());
flag.SetDisableOptimizations(M->GetDisableOptimization());
flag.SetAllResourcesBound(M->GetAllResourcesBound());
bool hasDouble = false;
// ddiv dfma drcp d2i d2u i2d u2d.
// fma has dxil op. Others should check IR instruction div/cast.
bool hasDoubleExtension = false;
bool has64Int = false;
bool has16 = false;
bool hasWaveOps = false;
bool hasCheckAccessFully = false;
bool hasMSAD = false;
bool hasStencilRef = false;
bool hasInnerCoverage = false;
bool hasViewID = false;
bool hasMulticomponentUAVLoads = false;
bool hasViewportOrRTArrayIndex = false;
bool hasShadingRate = false;
bool hasSamplerFeedback = false;
bool hasRaytracingTier1_1 = false;
// Try to maintain compatibility with a v1.0 validator if that's what we have.
uint32_t valMajor, valMinor;
M->GetValidatorVersion(valMajor, valMinor);
bool hasMulticomponentUAVLoadsBackCompat = valMajor == 1 && valMinor == 0;
bool hasViewportOrRTArrayIndexBackCombat = valMajor == 1 && valMinor < 4;
Type *int16Ty = Type::getInt16Ty(F->getContext());
Type *int64Ty = Type::getInt64Ty(F->getContext());
for (const BasicBlock &BB : F->getBasicBlockList()) {
for (const Instruction &I : BB.getInstList()) {
// Skip none dxil function call.
if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
if (!OP::IsDxilOpFunc(CI->getCalledFunction()))
continue;
}
Type *Ty = I.getType();
bool isDouble = Ty->isDoubleTy();
bool isHalf = Ty->isHalfTy();
bool isInt16 = Ty == int16Ty;
bool isInt64 = Ty == int64Ty;
if (isa<ExtractElementInst>(&I) ||
isa<InsertElementInst>(&I))
continue;
for (Value *operand : I.operands()) {
Type *Ty = operand->getType();
isDouble |= Ty->isDoubleTy();
isHalf |= Ty->isHalfTy();
isInt16 |= Ty == int16Ty;
isInt64 |= Ty == int64Ty;
}
if (isDouble) {
hasDouble = true;
switch (I.getOpcode()) {
case Instruction::FDiv:
case Instruction::UIToFP:
case Instruction::SIToFP:
case Instruction::FPToUI:
case Instruction::FPToSI:
hasDoubleExtension = true;
break;
}
}
has16 |= isHalf;
has16 |= isInt16;
has64Int |= isInt64;
if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
if (!OP::IsDxilOpFunc(CI->getCalledFunction()))
continue;
Value *opcodeArg = CI->getArgOperand(DXIL::OperandIndex::kOpcodeIdx);
ConstantInt *opcodeConst = dyn_cast<ConstantInt>(opcodeArg);
DXASSERT(opcodeConst, "DXIL opcode arg must be immediate");
unsigned opcode = opcodeConst->getLimitedValue();
DXASSERT(opcode < static_cast<unsigned>(DXIL::OpCode::NumOpCodes),
"invalid DXIL opcode");
DXIL::OpCode dxilOp = static_cast<DXIL::OpCode>(opcode);
if (hlsl::OP::IsDxilOpWave(dxilOp))
hasWaveOps = true;
switch (dxilOp) {
case DXIL::OpCode::CheckAccessFullyMapped:
hasCheckAccessFully = true;
break;
case DXIL::OpCode::Msad:
hasMSAD = true;
break;
case DXIL::OpCode::BufferLoad:
case DXIL::OpCode::TextureLoad: {
if (hasMulticomponentUAVLoads) continue;
// This is the old-style computation (overestimating requirements).
Value *resHandle = CI->getArgOperand(DXIL::OperandIndex::kBufferStoreHandleOpIdx);
CallInst *handleCall = FindCallToCreateHandle(resHandle);
// Check if this is a library handle or general create handle
if (handleCall) {
ConstantInt *HandleOpCodeConst = cast<ConstantInt>(
handleCall->getArgOperand(DXIL::OperandIndex::kOpcodeIdx));
DXIL::OpCode handleOp = static_cast<DXIL::OpCode>(HandleOpCodeConst->getLimitedValue());
if (handleOp == DXIL::OpCode::CreateHandle) {
if (ConstantInt *resClassArg =
dyn_cast<ConstantInt>(handleCall->getArgOperand(
DXIL::OperandIndex::kCreateHandleResClassOpIdx))) {
DXIL::ResourceClass resClass = static_cast<DXIL::ResourceClass>(
resClassArg->getLimitedValue());
if (resClass == DXIL::ResourceClass::UAV) {
// Validator 1.0 assumes that all uav load is multi component load.
if (hasMulticomponentUAVLoadsBackCompat) {
hasMulticomponentUAVLoads = true;
continue;
}
else {
ConstantInt *rangeID = GetArbitraryConstantRangeID(handleCall);
if (rangeID) {
DxilResource resource = M->GetUAV(rangeID->getLimitedValue());
if ((resource.IsTypedBuffer() ||
resource.IsAnyTexture()) &&
!dxilutil::IsResourceSingleComponent(resource.GetRetType())) {
hasMulticomponentUAVLoads = true;
}
}
}
}
}
else {
DXASSERT(false, "Resource class must be constant.");
}
}
else if (handleOp == DXIL::OpCode::CreateHandleForLib) {
// If library handle, find DxilResource by checking the name
if (LoadInst *LI = dyn_cast<LoadInst>(handleCall->getArgOperand(
DXIL::OperandIndex::
kCreateHandleForLibResOpIdx))) {
Value *resType = LI->getOperand(0);
for (auto &&res : M->GetUAVs()) {
if (res->GetGlobalSymbol() == resType) {
if ((res->IsTypedBuffer() || res->IsAnyTexture()) &&
!dxilutil::IsResourceSingleComponent(res->GetRetType())) {
hasMulticomponentUAVLoads = true;
}
}
}
}
} else if (handleOp == DXIL::OpCode::AnnotateHandle) {
DxilInst_AnnotateHandle annotateHandle(handleCall);
Type *ResPropTy = M->GetOP()->GetResourcePropertiesType();
DxilResourceProperties RP =
resource_helper::loadFromAnnotateHandle(
annotateHandle, ResPropTy, *M->GetShaderModel());
if (RP.Class == DXIL::ResourceClass::UAV) {
// Validator 1.0 assumes that all uav load is multi component
// load.
if (hasMulticomponentUAVLoadsBackCompat) {
hasMulticomponentUAVLoads = true;
continue;
} else {
if (DXIL::IsTyped(RP.Kind) &&
!RP.Typed.SingleComponent)
hasMulticomponentUAVLoads = true;
}
}
}
}
} break;
case DXIL::OpCode::Fma:
hasDoubleExtension |= isDouble;
break;
case DXIL::OpCode::InnerCoverage:
hasInnerCoverage = true;
break;
case DXIL::OpCode::ViewID:
hasViewID = true;
break;
case DXIL::OpCode::AllocateRayQuery:
case DXIL::OpCode::GeometryIndex:
hasRaytracingTier1_1 = true;
break;
default:
// Normal opcodes.
break;
}
}
}
}
// If this function is a shader, add flags based on signatures
if (M->HasDxilEntryProps(F)) {
const DxilEntryProps &entryProps = M->GetDxilEntryProps(F);
// Val ver < 1.4 has a bug where input case was always clobbered by the
// output check. The only case where it made a difference such that an
// incorrect flag would be set was for the HS and DS input cases.
// It was also checking PS input and output, but PS output could not have
// the semantic, and since it was clobbering the result, it would always
// clear it. Since this flag should not be set for PS at all,
// it produced the correct result for PS by accident.
bool checkInputRTArrayIndex = entryProps.props.IsGS();
if (!hasViewportOrRTArrayIndexBackCombat)
checkInputRTArrayIndex |= entryProps.props.IsDS() ||
entryProps.props.IsHS();
bool checkOutputRTArrayIndex =
entryProps.props.IsVS() || entryProps.props.IsDS() ||
entryProps.props.IsHS();
for (auto &&E : entryProps.sig.InputSignature.GetElements()) {
switch (E->GetKind()) {
case Semantic::Kind::ViewPortArrayIndex:
case Semantic::Kind::RenderTargetArrayIndex:
if (checkInputRTArrayIndex)
hasViewportOrRTArrayIndex = true;
break;
case Semantic::Kind::ShadingRate:
hasShadingRate = true;
break;
default:
break;
}
}
for (auto &&E : entryProps.sig.OutputSignature.GetElements()) {
switch (E->GetKind()) {
case Semantic::Kind::ViewPortArrayIndex:
case Semantic::Kind::RenderTargetArrayIndex:
if (checkOutputRTArrayIndex)
hasViewportOrRTArrayIndex = true;
break;
case Semantic::Kind::StencilRef:
if (entryProps.props.IsPS())
hasStencilRef = true;
break;
case Semantic::Kind::InnerCoverage:
if (entryProps.props.IsPS())
hasInnerCoverage = true;
break;
case Semantic::Kind::ShadingRate:
hasShadingRate = true;
break;
default:
break;
}
}
}
if (!hasRaytracingTier1_1) {
if (const DxilSubobjects *pSubobjects = M->GetSubobjects()) {
for (const auto &it : pSubobjects->GetSubobjects()) {
switch (it.second->GetKind()) {
case DXIL::SubobjectKind::RaytracingPipelineConfig1:
hasRaytracingTier1_1 = true;
break;
case DXIL::SubobjectKind::StateObjectConfig: {
uint32_t Flags;
if (it.second->GetStateObjectConfig(Flags) &&
((Flags & ~(unsigned)DXIL::StateObjectFlags::ValidMask_1_4) != 0))
hasRaytracingTier1_1 = true;
} break;
default:
break;
}
if (hasRaytracingTier1_1)
break;
}
}
}
flag.SetEnableDoublePrecision(hasDouble);
flag.SetStencilRef(hasStencilRef);
flag.SetInnerCoverage(hasInnerCoverage);
flag.SetInt64Ops(has64Int);
flag.SetLowPrecisionPresent(has16);
flag.SetEnableDoubleExtensions(hasDoubleExtension);
flag.SetWaveOps(hasWaveOps);
flag.SetTiledResources(hasCheckAccessFully);
flag.SetEnableMSAD(hasMSAD);
flag.SetUAVLoadAdditionalFormats(hasMulticomponentUAVLoads);
flag.SetViewID(hasViewID);
flag.SetViewportAndRTArrayIndex(hasViewportOrRTArrayIndex);
flag.SetShadingRate(hasShadingRate);
flag.SetSamplerFeedback(hasSamplerFeedback);
flag.SetRaytracingTier1_1(hasRaytracingTier1_1);
return flag;
}
void ShaderFlags::CombineShaderFlags(const ShaderFlags &other) {
SetShaderFlagsRaw(GetShaderFlagsRaw() | other.GetShaderFlagsRaw());
}