DxilShaderFlags.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilShaderFlags.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. ///////////////////////////////////////////////////////////////////////////////
  9. #include "dxc/DXIL/DxilModule.h"
  10. #include "dxc/DXIL/DxilShaderFlags.h"
  11. #include "dxc/DXIL/DxilOperations.h"
  12. #include "dxc/DXIL/DxilResource.h"
  13. #include "dxc/Support/Global.h"
  14. #include "llvm/IR/LLVMContext.h"
  15. #include "llvm/IR/Instructions.h"
  16. #include "llvm/IR/Constants.h"
  17. #include "llvm/Support/Casting.h"
  18. #include "dxc/DXIL/DxilEntryProps.h"
  19. using namespace hlsl;
  20. using namespace llvm;
  21. ShaderFlags::ShaderFlags():
  22. m_bDisableOptimizations(false)
  23. , m_bDisableMathRefactoring(false)
  24. , m_bEnableDoublePrecision(false)
  25. , m_bForceEarlyDepthStencil(false)
  26. , m_bEnableRawAndStructuredBuffers(false)
  27. , m_bLowPrecisionPresent(false)
  28. , m_bEnableDoubleExtensions(false)
  29. , m_bEnableMSAD(false)
  30. , m_bAllResourcesBound(false)
  31. , m_bViewportAndRTArrayIndex(false)
  32. , m_bInnerCoverage(false)
  33. , m_bStencilRef(false)
  34. , m_bTiledResources(false)
  35. , m_bUAVLoadAdditionalFormats(false)
  36. , m_bLevel9ComparisonFiltering(false)
  37. , m_b64UAVs(false)
  38. , m_UAVsAtEveryStage(false)
  39. , m_bCSRawAndStructuredViaShader4X(false)
  40. , m_bROVS(false)
  41. , m_bWaveOps(false)
  42. , m_bInt64Ops(false)
  43. , m_bViewID(false)
  44. , m_bBarycentrics(false)
  45. , m_bUseNativeLowPrecision(false)
  46. , m_bShadingRate(false)
  47. , m_align0(0)
  48. , m_align1(0)
  49. {}
  50. uint64_t ShaderFlags::GetFeatureInfo() const {
  51. uint64_t Flags = 0;
  52. Flags |= m_bEnableDoublePrecision ? hlsl::DXIL::ShaderFeatureInfo_Doubles : 0;
  53. Flags |= m_bLowPrecisionPresent && !m_bUseNativeLowPrecision
  54. ? hlsl::DXIL::ShaderFeatureInfo_MinimumPrecision
  55. : 0;
  56. Flags |= m_bLowPrecisionPresent && m_bUseNativeLowPrecision
  57. ? hlsl::DXIL::ShaderFeatureInfo_NativeLowPrecision
  58. : 0;
  59. Flags |= m_bEnableDoubleExtensions
  60. ? hlsl::DXIL::ShaderFeatureInfo_11_1_DoubleExtensions
  61. : 0;
  62. Flags |= m_bWaveOps ? hlsl::DXIL::ShaderFeatureInfo_WaveOps : 0;
  63. Flags |= m_bInt64Ops ? hlsl::DXIL::ShaderFeatureInfo_Int64Ops : 0;
  64. Flags |= m_bROVS ? hlsl::DXIL::ShaderFeatureInfo_ROVs : 0;
  65. Flags |=
  66. m_bViewportAndRTArrayIndex
  67. ? hlsl::DXIL::
  68. ShaderFeatureInfo_ViewportAndRTArrayIndexFromAnyShaderFeedingRasterizer
  69. : 0;
  70. Flags |= m_bInnerCoverage ? hlsl::DXIL::ShaderFeatureInfo_InnerCoverage : 0;
  71. Flags |= m_bStencilRef ? hlsl::DXIL::ShaderFeatureInfo_StencilRef : 0;
  72. Flags |= m_bTiledResources ? hlsl::DXIL::ShaderFeatureInfo_TiledResources : 0;
  73. Flags |=
  74. m_bEnableMSAD ? hlsl::DXIL::ShaderFeatureInfo_11_1_ShaderExtensions : 0;
  75. Flags |=
  76. m_bCSRawAndStructuredViaShader4X
  77. ? hlsl::DXIL::
  78. ShaderFeatureInfo_ComputeShadersPlusRawAndStructuredBuffersViaShader4X
  79. : 0;
  80. Flags |=
  81. m_UAVsAtEveryStage ? hlsl::DXIL::ShaderFeatureInfo_UAVsAtEveryStage : 0;
  82. Flags |= m_b64UAVs ? hlsl::DXIL::ShaderFeatureInfo_64UAVs : 0;
  83. Flags |= m_bLevel9ComparisonFiltering
  84. ? hlsl::DXIL::ShaderFeatureInfo_LEVEL9ComparisonFiltering
  85. : 0;
  86. Flags |= m_bUAVLoadAdditionalFormats
  87. ? hlsl::DXIL::ShaderFeatureInfo_TypedUAVLoadAdditionalFormats
  88. : 0;
  89. Flags |= m_bViewID ? hlsl::DXIL::ShaderFeatureInfo_ViewID : 0;
  90. Flags |= m_bBarycentrics ? hlsl::DXIL::ShaderFeatureInfo_Barycentrics : 0;
  91. Flags |= m_bShadingRate ? hlsl::DXIL::ShaderFeatureInfo_ShadingRate : 0;
  92. return Flags;
  93. }
  94. uint64_t ShaderFlags::GetShaderFlagsRaw() const {
  95. union Cast {
  96. Cast(const ShaderFlags &flags) {
  97. shaderFlags = flags;
  98. }
  99. ShaderFlags shaderFlags;
  100. uint64_t rawData;
  101. };
  102. static_assert(sizeof(uint64_t) == sizeof(ShaderFlags),
  103. "size must match to make sure no undefined bits when cast");
  104. Cast rawCast(*this);
  105. return rawCast.rawData;
  106. }
  107. void ShaderFlags::SetShaderFlagsRaw(uint64_t data) {
  108. union Cast {
  109. Cast(uint64_t data) {
  110. rawData = data;
  111. }
  112. ShaderFlags shaderFlags;
  113. uint64_t rawData;
  114. };
  115. Cast rawCast(data);
  116. *this = rawCast.shaderFlags;
  117. }
  118. uint64_t ShaderFlags::GetShaderFlagsRawForCollection() {
  119. // This should be all the flags that can be set by DxilModule::CollectShaderFlags.
  120. ShaderFlags Flags;
  121. Flags.SetEnableDoublePrecision(true);
  122. Flags.SetInt64Ops(true);
  123. Flags.SetLowPrecisionPresent(true);
  124. Flags.SetEnableDoubleExtensions(true);
  125. Flags.SetWaveOps(true);
  126. Flags.SetTiledResources(true);
  127. Flags.SetEnableMSAD(true);
  128. Flags.SetUAVLoadAdditionalFormats(true);
  129. Flags.SetStencilRef(true);
  130. Flags.SetInnerCoverage(true);
  131. Flags.SetViewportAndRTArrayIndex(true);
  132. Flags.Set64UAVs(true);
  133. Flags.SetUAVsAtEveryStage(true);
  134. Flags.SetEnableRawAndStructuredBuffers(true);
  135. Flags.SetCSRawAndStructuredViaShader4X(true);
  136. Flags.SetViewID(true);
  137. Flags.SetBarycentrics(true);
  138. Flags.SetShadingRate(true);
  139. return Flags.GetShaderFlagsRaw();
  140. }
  141. unsigned ShaderFlags::GetGlobalFlags() const {
  142. unsigned Flags = 0;
  143. Flags |= m_bDisableOptimizations ? DXIL::kDisableOptimizations : 0;
  144. Flags |= m_bDisableMathRefactoring ? DXIL::kDisableMathRefactoring : 0;
  145. Flags |= m_bEnableDoublePrecision ? DXIL::kEnableDoublePrecision : 0;
  146. Flags |= m_bForceEarlyDepthStencil ? DXIL::kForceEarlyDepthStencil : 0;
  147. Flags |= m_bEnableRawAndStructuredBuffers ? DXIL::kEnableRawAndStructuredBuffers : 0;
  148. Flags |= m_bLowPrecisionPresent && !m_bUseNativeLowPrecision? DXIL::kEnableMinPrecision : 0;
  149. Flags |= m_bEnableDoubleExtensions ? DXIL::kEnableDoubleExtensions : 0;
  150. Flags |= m_bEnableMSAD ? DXIL::kEnableMSAD : 0;
  151. Flags |= m_bAllResourcesBound ? DXIL::kAllResourcesBound : 0;
  152. return Flags;
  153. }
  154. // Given a CreateHandle call, returns arbitrary ConstantInt rangeID
  155. // Note: HLSL is currently assuming that rangeID is a constant value, but this code is assuming
  156. // that it can be either constant, phi node, or select instruction
  157. static ConstantInt *GetArbitraryConstantRangeID(CallInst *handleCall) {
  158. Value *rangeID =
  159. handleCall->getArgOperand(DXIL::OperandIndex::kCreateHandleResIDOpIdx);
  160. ConstantInt *ConstantRangeID = dyn_cast<ConstantInt>(rangeID);
  161. while (ConstantRangeID == nullptr) {
  162. if (ConstantInt *CI = dyn_cast<ConstantInt>(rangeID)) {
  163. ConstantRangeID = CI;
  164. } else if (PHINode *PN = dyn_cast<PHINode>(rangeID)) {
  165. rangeID = PN->getIncomingValue(0);
  166. } else if (SelectInst *SI = dyn_cast<SelectInst>(rangeID)) {
  167. rangeID = SI->getTrueValue();
  168. } else {
  169. return nullptr;
  170. }
  171. }
  172. return ConstantRangeID;
  173. }
  174. static bool IsResourceSingleComponent(llvm::Type *Ty) {
  175. if (llvm::ArrayType *arrType = llvm::dyn_cast<llvm::ArrayType>(Ty)) {
  176. if (arrType->getArrayNumElements() > 1) {
  177. return false;
  178. }
  179. return IsResourceSingleComponent(arrType->getArrayElementType());
  180. } else if (llvm::StructType *structType =
  181. llvm::dyn_cast<llvm::StructType>(Ty)) {
  182. if (structType->getStructNumElements() > 1) {
  183. return false;
  184. }
  185. return IsResourceSingleComponent(structType->getStructElementType(0));
  186. } else if (llvm::VectorType *vectorType =
  187. llvm::dyn_cast<llvm::VectorType>(Ty)) {
  188. if (vectorType->getNumElements() > 1) {
  189. return false;
  190. }
  191. return IsResourceSingleComponent(vectorType->getVectorElementType());
  192. }
  193. return true;
  194. }
  195. // Given a handle type, find an arbitrary call instructions to create handle
  196. static CallInst *FindCallToCreateHandle(Value *handleType) {
  197. Value *curVal = handleType;
  198. CallInst *CI = dyn_cast<CallInst>(handleType);
  199. while (CI == nullptr) {
  200. if (PHINode *PN = dyn_cast<PHINode>(curVal)) {
  201. curVal = PN->getIncomingValue(0);
  202. }
  203. else if (SelectInst *SI = dyn_cast<SelectInst>(curVal)) {
  204. curVal = SI->getTrueValue();
  205. }
  206. else {
  207. return nullptr;
  208. }
  209. CI = dyn_cast<CallInst>(curVal);
  210. }
  211. return CI;
  212. }
  213. ShaderFlags ShaderFlags::CollectShaderFlags(const Function *F,
  214. const hlsl::DxilModule *M) {
  215. ShaderFlags flag;
  216. // Module level options
  217. flag.SetUseNativeLowPrecision(!M->GetUseMinPrecision());
  218. flag.SetDisableOptimizations(M->GetDisableOptimization());
  219. flag.SetAllResourcesBound(M->GetAllResourcesBound());
  220. bool hasDouble = false;
  221. // ddiv dfma drcp d2i d2u i2d u2d.
  222. // fma has dxil op. Others should check IR instruction div/cast.
  223. bool hasDoubleExtension = false;
  224. bool has64Int = false;
  225. bool has16 = false;
  226. bool hasWaveOps = false;
  227. bool hasCheckAccessFully = false;
  228. bool hasMSAD = false;
  229. bool hasStencilRef = false;
  230. bool hasInnerCoverage = false;
  231. bool hasViewID = false;
  232. bool hasMulticomponentUAVLoads = false;
  233. bool hasViewportOrRTArrayIndex = false;
  234. bool hasShadingRate = false;
  235. // Try to maintain compatibility with a v1.0 validator if that's what we have.
  236. uint32_t valMajor, valMinor;
  237. M->GetValidatorVersion(valMajor, valMinor);
  238. bool hasMulticomponentUAVLoadsBackCompat = valMajor == 1 && valMinor == 0;
  239. bool hasViewportOrRTArrayIndexBackCombat = valMajor == 1 && valMinor < 4;
  240. Type *int16Ty = Type::getInt16Ty(F->getContext());
  241. Type *int64Ty = Type::getInt64Ty(F->getContext());
  242. for (const BasicBlock &BB : F->getBasicBlockList()) {
  243. for (const Instruction &I : BB.getInstList()) {
  244. // Skip none dxil function call.
  245. if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
  246. if (!OP::IsDxilOpFunc(CI->getCalledFunction()))
  247. continue;
  248. }
  249. Type *Ty = I.getType();
  250. bool isDouble = Ty->isDoubleTy();
  251. bool isHalf = Ty->isHalfTy();
  252. bool isInt16 = Ty == int16Ty;
  253. bool isInt64 = Ty == int64Ty;
  254. if (isa<ExtractElementInst>(&I) ||
  255. isa<InsertElementInst>(&I))
  256. continue;
  257. for (Value *operand : I.operands()) {
  258. Type *Ty = operand->getType();
  259. isDouble |= Ty->isDoubleTy();
  260. isHalf |= Ty->isHalfTy();
  261. isInt16 |= Ty == int16Ty;
  262. isInt64 |= Ty == int64Ty;
  263. }
  264. if (isDouble) {
  265. hasDouble = true;
  266. switch (I.getOpcode()) {
  267. case Instruction::FDiv:
  268. case Instruction::UIToFP:
  269. case Instruction::SIToFP:
  270. case Instruction::FPToUI:
  271. case Instruction::FPToSI:
  272. hasDoubleExtension = true;
  273. break;
  274. }
  275. }
  276. has16 |= isHalf;
  277. has16 |= isInt16;
  278. has64Int |= isInt64;
  279. if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
  280. if (!OP::IsDxilOpFunc(CI->getCalledFunction()))
  281. continue;
  282. Value *opcodeArg = CI->getArgOperand(DXIL::OperandIndex::kOpcodeIdx);
  283. ConstantInt *opcodeConst = dyn_cast<ConstantInt>(opcodeArg);
  284. DXASSERT(opcodeConst, "DXIL opcode arg must be immediate");
  285. unsigned opcode = opcodeConst->getLimitedValue();
  286. DXASSERT(opcode < static_cast<unsigned>(DXIL::OpCode::NumOpCodes),
  287. "invalid DXIL opcode");
  288. DXIL::OpCode dxilOp = static_cast<DXIL::OpCode>(opcode);
  289. if (hlsl::OP::IsDxilOpWave(dxilOp))
  290. hasWaveOps = true;
  291. switch (dxilOp) {
  292. case DXIL::OpCode::CheckAccessFullyMapped:
  293. hasCheckAccessFully = true;
  294. break;
  295. case DXIL::OpCode::Msad:
  296. hasMSAD = true;
  297. break;
  298. case DXIL::OpCode::BufferLoad:
  299. case DXIL::OpCode::TextureLoad: {
  300. if (hasMulticomponentUAVLoads) continue;
  301. // This is the old-style computation (overestimating requirements).
  302. Value *resHandle = CI->getArgOperand(DXIL::OperandIndex::kBufferStoreHandleOpIdx);
  303. CallInst *handleCall = FindCallToCreateHandle(resHandle);
  304. // Check if this is a library handle or general create handle
  305. if (handleCall) {
  306. ConstantInt *HandleOpCodeConst = cast<ConstantInt>(
  307. handleCall->getArgOperand(DXIL::OperandIndex::kOpcodeIdx));
  308. DXIL::OpCode handleOp = static_cast<DXIL::OpCode>(HandleOpCodeConst->getLimitedValue());
  309. if (handleOp == DXIL::OpCode::CreateHandle) {
  310. if (ConstantInt *resClassArg =
  311. dyn_cast<ConstantInt>(handleCall->getArgOperand(
  312. DXIL::OperandIndex::kCreateHandleResClassOpIdx))) {
  313. DXIL::ResourceClass resClass = static_cast<DXIL::ResourceClass>(
  314. resClassArg->getLimitedValue());
  315. if (resClass == DXIL::ResourceClass::UAV) {
  316. // Validator 1.0 assumes that all uav load is multi component load.
  317. if (hasMulticomponentUAVLoadsBackCompat) {
  318. hasMulticomponentUAVLoads = true;
  319. continue;
  320. }
  321. else {
  322. ConstantInt *rangeID = GetArbitraryConstantRangeID(handleCall);
  323. if (rangeID) {
  324. DxilResource resource = M->GetUAV(rangeID->getLimitedValue());
  325. if ((resource.IsTypedBuffer() ||
  326. resource.IsAnyTexture()) &&
  327. !IsResourceSingleComponent(resource.GetRetType())) {
  328. hasMulticomponentUAVLoads = true;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. else {
  335. DXASSERT(false, "Resource class must be constant.");
  336. }
  337. }
  338. else if (handleOp == DXIL::OpCode::CreateHandleForLib) {
  339. // If library handle, find DxilResource by checking the name
  340. if (LoadInst *LI = dyn_cast<LoadInst>(handleCall->getArgOperand(
  341. DXIL::OperandIndex::
  342. kCreateHandleForLibResOpIdx))) {
  343. Value *resType = LI->getOperand(0);
  344. for (auto &&res : M->GetUAVs()) {
  345. if (res->GetGlobalSymbol() == resType) {
  346. if ((res->IsTypedBuffer() || res->IsAnyTexture()) &&
  347. !IsResourceSingleComponent(res->GetRetType())) {
  348. hasMulticomponentUAVLoads = true;
  349. }
  350. }
  351. }
  352. }
  353. }
  354. }
  355. } break;
  356. case DXIL::OpCode::Fma:
  357. hasDoubleExtension |= isDouble;
  358. break;
  359. case DXIL::OpCode::InnerCoverage:
  360. hasInnerCoverage = true;
  361. break;
  362. case DXIL::OpCode::ViewID:
  363. hasViewID = true;
  364. break;
  365. default:
  366. // Normal opcodes.
  367. break;
  368. }
  369. }
  370. }
  371. }
  372. // If this function is a shader, add flags based on signatures
  373. if (M->HasDxilEntryProps(F)) {
  374. const DxilEntryProps &entryProps = M->GetDxilEntryProps(F);
  375. // Val ver < 1.4 has a bug where input case was always clobbered by the
  376. // output check. The only case where it made a difference such that an
  377. // incorrect flag would be set was for the HS and DS input cases.
  378. // It was also checking PS input and output, but PS output could not have
  379. // the semantic, and since it was clobbering the result, it would always
  380. // clear it. Since this flag should not be set for PS at all,
  381. // it produced the correct result for PS by accident.
  382. bool checkInputRTArrayIndex = entryProps.props.IsGS();
  383. if (!hasViewportOrRTArrayIndexBackCombat)
  384. checkInputRTArrayIndex |= entryProps.props.IsDS() ||
  385. entryProps.props.IsHS();
  386. bool checkOutputRTArrayIndex =
  387. entryProps.props.IsVS() || entryProps.props.IsDS() ||
  388. entryProps.props.IsHS();
  389. for (auto &&E : entryProps.sig.InputSignature.GetElements()) {
  390. switch (E->GetKind()) {
  391. case Semantic::Kind::ViewPortArrayIndex:
  392. case Semantic::Kind::RenderTargetArrayIndex:
  393. if (checkInputRTArrayIndex)
  394. hasViewportOrRTArrayIndex = true;
  395. break;
  396. case Semantic::Kind::ShadingRate:
  397. hasShadingRate = true;
  398. break;
  399. default:
  400. break;
  401. }
  402. }
  403. for (auto &&E : entryProps.sig.OutputSignature.GetElements()) {
  404. switch (E->GetKind()) {
  405. case Semantic::Kind::ViewPortArrayIndex:
  406. case Semantic::Kind::RenderTargetArrayIndex:
  407. if (checkOutputRTArrayIndex)
  408. hasViewportOrRTArrayIndex = true;
  409. break;
  410. case Semantic::Kind::StencilRef:
  411. if (entryProps.props.IsPS())
  412. hasStencilRef = true;
  413. break;
  414. case Semantic::Kind::InnerCoverage:
  415. if (entryProps.props.IsPS())
  416. hasInnerCoverage = true;
  417. break;
  418. case Semantic::Kind::ShadingRate:
  419. hasShadingRate = true;
  420. break;
  421. default:
  422. break;
  423. }
  424. }
  425. }
  426. flag.SetEnableDoublePrecision(hasDouble);
  427. flag.SetStencilRef(hasStencilRef);
  428. flag.SetInnerCoverage(hasInnerCoverage);
  429. flag.SetInt64Ops(has64Int);
  430. flag.SetLowPrecisionPresent(has16);
  431. flag.SetEnableDoubleExtensions(hasDoubleExtension);
  432. flag.SetWaveOps(hasWaveOps);
  433. flag.SetTiledResources(hasCheckAccessFully);
  434. flag.SetEnableMSAD(hasMSAD);
  435. flag.SetUAVLoadAdditionalFormats(hasMulticomponentUAVLoads);
  436. flag.SetViewID(hasViewID);
  437. flag.SetViewportAndRTArrayIndex(hasViewportOrRTArrayIndex);
  438. flag.SetShadingRate(hasShadingRate);
  439. return flag;
  440. }
  441. void ShaderFlags::CombineShaderFlags(const ShaderFlags &other) {
  442. SetShaderFlagsRaw(GetShaderFlagsRaw() | other.GetShaderFlagsRaw());
  443. }