DxilOutputColorBecomesConstant.cpp 10 KB

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