DxilOutputColorBecomesConstant.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilOutputColorBecomesConstant.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. // Provides a pass to stomp a pixel shader's output color to a given //
  9. // constant value //
  10. // //
  11. ///////////////////////////////////////////////////////////////////////////////
  12. #include "dxc/HLSL/DxilGenerationPass.h"
  13. #include "dxc/HLSL/DxilOperations.h"
  14. #include "dxc/HLSL/DxilSignatureElement.h"
  15. #include "dxc/HLSL/DxilModule.h"
  16. #include "dxc/Support/Global.h"
  17. #include "dxc/HLSL/DxilTypeSystem.h"
  18. #include "dxc/HLSL/DxilInstructions.h"
  19. #include "dxc/HLSL/DxilSpanAllocator.h"
  20. #include "llvm/IR/Instructions.h"
  21. #include "llvm/IR/IntrinsicInst.h"
  22. #include "llvm/IR/InstIterator.h"
  23. #include "llvm/IR/Module.h"
  24. #include "llvm/IR/PassManager.h"
  25. #include "llvm/ADT/BitVector.h"
  26. #include "llvm/Pass.h"
  27. #include "llvm/Transforms/Utils/Local.h"
  28. #include <memory>
  29. #include <unordered_set>
  30. #include <array>
  31. using namespace llvm;
  32. using namespace hlsl;
  33. class DxilOutputColorBecomesConstant : public ModulePass {
  34. enum VisualizerInstrumentationMode
  35. {
  36. FromLiteralConstant,
  37. FromConstantBuffer
  38. };
  39. float Red = 1.f;
  40. float Green = 1.f;
  41. float Blue = 1.f;
  42. float Alpha = 1.f;
  43. VisualizerInstrumentationMode Mode = FromLiteralConstant;
  44. void visitOutputInstructionCallers(
  45. Function * OutputFunction,
  46. const hlsl::DxilSignature &OutputSignature,
  47. OP * HlslOP,
  48. std::function<void(CallInst*)> Visitor);
  49. public:
  50. static char ID; // Pass identification, replacement for typeid
  51. explicit DxilOutputColorBecomesConstant() : ModulePass(ID) {}
  52. const char *getPassName() const override { return "DXIL Constant Color Mod"; }
  53. void applyOptions(PassOptions O) override;
  54. bool runOnModule(Module &M) override;
  55. };
  56. void DxilOutputColorBecomesConstant::applyOptions(PassOptions O)
  57. {
  58. for (const auto & option : O)
  59. {
  60. if (0 == option.first.compare("constant-red"))
  61. {
  62. Red = atof(option.second.data());
  63. }
  64. else if (0 == option.first.compare("constant-green"))
  65. {
  66. Green = atof(option.second.data());
  67. }
  68. else if (0 == option.first.compare("constant-blue"))
  69. {
  70. Blue = atof(option.second.data());
  71. }
  72. else if (0 == option.first.compare("constant-alpha"))
  73. {
  74. Alpha = atof(option.second.data());
  75. }
  76. else if (0 == option.first.compare("mod-mode"))
  77. {
  78. Mode = static_cast<VisualizerInstrumentationMode>(atoi(option.second.data()));
  79. }
  80. }
  81. }
  82. void DxilOutputColorBecomesConstant::visitOutputInstructionCallers(
  83. Function * OutputFunction,
  84. const hlsl::DxilSignature &OutputSignature,
  85. OP * HlslOP,
  86. std::function<void(CallInst*)> Visitor) {
  87. auto OutputFunctionUses = OutputFunction->uses();
  88. for (Use &FunctionUse : OutputFunctionUses) {
  89. iterator_range<Value::user_iterator> FunctionUsers = FunctionUse->users();
  90. for (User * FunctionUser : FunctionUsers) {
  91. if (isa<Instruction>(FunctionUser)) {
  92. auto CallInstruction = cast<CallInst>(FunctionUser);
  93. // Check if the instruction writes to a render target (as opposed to a system-value, such as RenderTargetArrayIndex)
  94. Value *OutputID = CallInstruction->getArgOperand(DXIL::OperandIndex::kStoreOutputIDOpIdx);
  95. unsigned SignatureElementIndex = cast<ConstantInt>(OutputID)->getLimitedValue();
  96. const DxilSignatureElement &SignatureElement = OutputSignature.GetElement(SignatureElementIndex);
  97. // We only modify the output color for RTV0
  98. if (SignatureElement.GetSemantic()->GetKind() == DXIL::SemanticKind::Target &&
  99. SignatureElement.GetSemanticStartIndex() == 0) {
  100. // Replace the source operand with the appropriate constant value
  101. Visitor(CallInstruction);
  102. }
  103. }
  104. }
  105. }
  106. }
  107. bool DxilOutputColorBecomesConstant::runOnModule(Module &M)
  108. {
  109. // This pass finds all users of the "StoreOutput" function, and replaces their source operands with a constant
  110. // value.
  111. DxilModule &DM = M.GetOrCreateDxilModule();
  112. LLVMContext & Ctx = M.getContext();
  113. OP *HlslOP = DM.GetOP();
  114. const hlsl::DxilSignature & OutputSignature = DM.GetOutputSignature();
  115. Function * FloatOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getFloatTy(Ctx));
  116. Function * IntOutputFunction = HlslOP->GetOpFunc(DXIL::OpCode::StoreOutput, Type::getInt32Ty(Ctx));
  117. bool hasFloatOutputs = false;
  118. bool hasIntOutputs = false;
  119. visitOutputInstructionCallers(FloatOutputFunction, OutputSignature, HlslOP, [&hasFloatOutputs](CallInst *) {
  120. hasFloatOutputs = true;
  121. });
  122. visitOutputInstructionCallers(IntOutputFunction, OutputSignature, HlslOP, [&hasIntOutputs](CallInst *) {
  123. hasIntOutputs = true;
  124. });
  125. if (!hasFloatOutputs && !hasIntOutputs)
  126. {
  127. return false;
  128. }
  129. // Otherwise, we assume the shader outputs only one or the other (because the 0th RTV can't have a mixed type)
  130. DXASSERT(!hasFloatOutputs || !hasIntOutputs, "Only one or the other type of output: float or int");
  131. std::array<llvm::Value *, 4> ReplacementColors;
  132. switch (Mode)
  133. {
  134. case FromLiteralConstant: {
  135. if (hasFloatOutputs) {
  136. ReplacementColors[0] = HlslOP->GetFloatConst(Red);
  137. ReplacementColors[1] = HlslOP->GetFloatConst(Green);
  138. ReplacementColors[2] = HlslOP->GetFloatConst(Blue);
  139. ReplacementColors[3] = HlslOP->GetFloatConst(Alpha);
  140. }
  141. if (hasIntOutputs) {
  142. ReplacementColors[0] = HlslOP->GetI32Const(static_cast<int>(Red));
  143. ReplacementColors[1] = HlslOP->GetI32Const(static_cast<int>(Green));
  144. ReplacementColors[2] = HlslOP->GetI32Const(static_cast<int>(Blue));
  145. ReplacementColors[3] = HlslOP->GetI32Const(static_cast<int>(Alpha));
  146. }
  147. }
  148. break;
  149. case FromConstantBuffer: {
  150. // Setup a constant buffer with a single float4 in it:
  151. SmallVector<llvm::Type*, 4> Elements { Type::getFloatTy(Ctx), Type::getFloatTy(Ctx) , Type::getFloatTy(Ctx) , Type::getFloatTy(Ctx) };
  152. llvm::StructType *CBStructTy = llvm::StructType::create(Elements, "PIX_ConstantColorCB_Type");
  153. std::unique_ptr<DxilCBuffer> pCBuf = llvm::make_unique<DxilCBuffer>();
  154. pCBuf->SetGlobalName("PIX_ConstantColorCBName");
  155. pCBuf->SetGlobalSymbol(UndefValue::get(CBStructTy));
  156. pCBuf->SetID(0);
  157. pCBuf->SetSpaceID((unsigned int)-2); // This is the reserved-for-tools register space
  158. pCBuf->SetLowerBound(0);
  159. pCBuf->SetRangeSize(1);
  160. pCBuf->SetSize(4);
  161. ID = DM.AddCBuffer(std::move(pCBuf));
  162. Instruction * entryPointInstruction = &*(DM.GetEntryFunction()->begin()->begin());
  163. IRBuilder<> Builder(entryPointInstruction);
  164. // Create handle for the newly-added constant buffer (which is achieved via a function call)
  165. auto ConstantBufferName = "PIX_Constant_Color_CB_Handle";
  166. Function *createHandle = HlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
  167. Constant *CreateHandleOpcodeArg = HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
  168. Constant *CBVArg = HlslOP->GetI8Const(static_cast<std::underlying_type<DxilResourceBase::Class>::type>(DXIL::ResourceClass::CBuffer));
  169. Constant *MetaDataArg = HlslOP->GetU32Const(ID); // position of the metadata record in the corresponding metadata list
  170. Constant *IndexArg = HlslOP->GetU32Const(0); //
  171. Constant *FalseArg = HlslOP->GetI1Const(0); // non-uniform resource index: false
  172. CallInst *callCreateHandle = Builder.CreateCall(createHandle, { CreateHandleOpcodeArg, CBVArg, MetaDataArg, IndexArg, FalseArg }, ConstantBufferName);
  173. DM.ReEmitDxilResources();
  174. #define PIX_CONSTANT_VALUE "PIX_Constant_Color_Value"
  175. // Insert the Buffer load instruction:
  176. Function *CBLoad = HlslOP->GetOpFunc(OP::OpCode::CBufferLoadLegacy, hasFloatOutputs ? Type::getFloatTy(Ctx) : Type::getInt32Ty(Ctx));
  177. Constant *OpArg = HlslOP->GetU32Const((unsigned)OP::OpCode::CBufferLoadLegacy);
  178. Value * ResourceHandle = callCreateHandle;
  179. Constant *RowIndex = HlslOP->GetU32Const(0);
  180. CallInst *loadLegacy = Builder.CreateCall(CBLoad, { OpArg, ResourceHandle, RowIndex }, PIX_CONSTANT_VALUE);
  181. // Now extract four color values:
  182. ReplacementColors[0] = Builder.CreateExtractValue(loadLegacy, 0, PIX_CONSTANT_VALUE "0");
  183. ReplacementColors[1] = Builder.CreateExtractValue(loadLegacy, 1, PIX_CONSTANT_VALUE "1");
  184. ReplacementColors[2] = Builder.CreateExtractValue(loadLegacy, 2, PIX_CONSTANT_VALUE "2");
  185. ReplacementColors[3] = Builder.CreateExtractValue(loadLegacy, 3, PIX_CONSTANT_VALUE "3");
  186. }
  187. break;
  188. default:
  189. assert(false);
  190. return 0;
  191. }
  192. bool Modified = false;
  193. // The StoreOutput function can store either a float or an integer, depending on the intended output
  194. // render-target resource view.
  195. if (hasFloatOutputs) {
  196. visitOutputInstructionCallers(FloatOutputFunction, OutputSignature, HlslOP,
  197. [&ReplacementColors, &Modified](CallInst * CallInstruction) {
  198. Modified = true;
  199. // The output column is the channel (red, green, blue or alpha) within the output pixel
  200. Value * OutputColumnOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputColOpIdx);
  201. ConstantInt * OutputColumnConstant = cast<ConstantInt>(OutputColumnOperand);
  202. APInt OutputColumn = OutputColumnConstant->getValue();
  203. CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, ReplacementColors[*OutputColumn.getRawData()]);
  204. });
  205. }
  206. if (hasIntOutputs) {
  207. visitOutputInstructionCallers(IntOutputFunction, OutputSignature, HlslOP,
  208. [&ReplacementColors, &Modified](CallInst * CallInstruction) {
  209. Modified = true;
  210. // The output column is the channel (red, green, blue or alpha) within the output pixel
  211. Value * OutputColumnOperand = CallInstruction->getOperand(hlsl::DXIL::OperandIndex::kStoreOutputColOpIdx);
  212. ConstantInt * OutputColumnConstant = cast<ConstantInt>(OutputColumnOperand);
  213. APInt OutputColumn = OutputColumnConstant->getValue();
  214. CallInstruction->setOperand(hlsl::DXIL::OperandIndex::kStoreOutputValOpIdx, ReplacementColors[*OutputColumn.getRawData()]);
  215. });
  216. }
  217. return Modified;
  218. }
  219. char DxilOutputColorBecomesConstant::ID = 0;
  220. ModulePass *llvm::createDxilOutputColorBecomesConstantPass() {
  221. return new DxilOutputColorBecomesConstant();
  222. }
  223. INITIALIZE_PASS(DxilOutputColorBecomesConstant, "hlsl-dxil-constantColor", "DXIL Constant Color Mod", false, false)