dxc_batch.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // dxc_bach.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 the entry point for the dxc_back console program. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. // dxc_batch is a fork of dxc showing how to build multiple shaders while
  12. // sharing library-fied intermediates
  13. #include "dxc/Support/Global.h"
  14. #include "dxc/Support/Unicode.h"
  15. #include "dxc/Support/WinIncludes.h"
  16. #include <string>
  17. #include <vector>
  18. #include "dxc/DxilContainer/DxilContainer.h"
  19. #include "dxc/DxilRootSignature/DxilRootSignature.h"
  20. #include "dxc/DXIL/DxilShaderModel.h"
  21. #include "dxc/Support/FileIOHelper.h"
  22. #include "dxc/Support/HLSLOptions.h"
  23. #include "dxc/Support/dxcapi.use.h"
  24. #include "dxc/Support/microcom.h"
  25. #include "dxc/dxcapi.h"
  26. #include "dxc/dxcapi.internal.h"
  27. #include "dxc/dxctools.h"
  28. #include "llvm/Option/ArgList.h"
  29. #include "llvm/Option/OptTable.h"
  30. #include "llvm/Support/MemoryBuffer.h"
  31. #include "llvm/Support/raw_ostream.h"
  32. #include <algorithm>
  33. #include <chrono>
  34. #include <comdef.h>
  35. #include <thread>
  36. #include <unordered_map>
  37. #include "llvm/Support//MSFileSystem.h"
  38. #include "llvm/Support/CommandLine.h"
  39. #include "llvm/Support/FileSystem.h"
  40. #include "llvm/Support/Path.h"
  41. inline bool wcseq(LPCWSTR a, LPCWSTR b) {
  42. return (a == nullptr && b == nullptr) ||
  43. (a != nullptr && b != nullptr && wcscmp(a, b) == 0);
  44. }
  45. inline bool wcsieq(LPCWSTR a, LPCWSTR b) { return _wcsicmp(a, b) == 0; }
  46. using namespace dxc;
  47. using namespace llvm;
  48. using namespace llvm::opt;
  49. using namespace hlsl::options;
  50. extern HRESULT WINAPI DxilD3DCompile2(
  51. LPCVOID pSrcData, SIZE_T SrcDataSize, LPCSTR pSourceName,
  52. LPCSTR pEntrypoint, LPCSTR pTarget,
  53. const DxcDefine *pDefines, // Array of defines
  54. UINT32 defineCount, // Number of defines
  55. LPCWSTR *pArguments, // Array of pointers to arguments
  56. UINT32 argCount, // Number of arguments
  57. IDxcIncludeHandler *pInclude, IDxcOperationResult **ppOperationResult);
  58. class DxcContext {
  59. private:
  60. DxcOpts &m_Opts;
  61. DxcDllSupport &m_dxcSupport;
  62. int ActOnBlob(IDxcBlob *pBlob);
  63. int ActOnBlob(IDxcBlob *pBlob, IDxcBlob *pDebugBlob, LPCWSTR pDebugBlobName);
  64. void UpdatePart(IDxcBlob *pBlob, IDxcBlob **ppResult);
  65. bool UpdatePartRequired();
  66. void WriteHeader(IDxcBlobEncoding *pDisassembly, IDxcBlob *pCode,
  67. llvm::Twine &pVariableName, LPCWSTR pPath);
  68. HRESULT ReadFileIntoPartContent(hlsl::DxilFourCC fourCC, LPCWSTR fileName,
  69. IDxcBlob **ppResult);
  70. void ExtractRootSignature(IDxcBlob *pBlob, IDxcBlob **ppResult);
  71. int VerifyRootSignature();
  72. public:
  73. DxcContext(DxcOpts &Opts, DxcDllSupport &dxcSupport)
  74. : m_Opts(Opts), m_dxcSupport(dxcSupport) {}
  75. int Compile(llvm::StringRef path, bool bLibLink);
  76. int DumpBinary();
  77. void Preprocess();
  78. };
  79. static void PrintHlslException(const ::hlsl::Exception &hlslException,
  80. llvm::StringRef stage,
  81. llvm::raw_string_ostream &errorStream) {
  82. errorStream << stage << " failed\n";
  83. try {
  84. errorStream << hlslException.what();
  85. if (hlslException.hr == DXC_E_DUPLICATE_PART) {
  86. errorStream << "dxc_batch failed : DXIL container already contains the given part.";
  87. } else if (hlslException.hr == DXC_E_MISSING_PART) {
  88. errorStream << "dxc_batch failed : DXIL container does not contain the given part.";
  89. } else if (hlslException.hr == DXC_E_CONTAINER_INVALID) {
  90. errorStream << "dxc_batch failed : Invalid DXIL container.";
  91. } else if (hlslException.hr == DXC_E_CONTAINER_MISSING_DXIL) {
  92. errorStream << "dxc_batch failed : DXIL container is missing DXIL part.";
  93. } else if (hlslException.hr == DXC_E_CONTAINER_MISSING_DEBUG) {
  94. errorStream << "dxc_batch failed : DXIL container is missing Debug Info part.";
  95. } else if (hlslException.hr == E_OUTOFMEMORY) {
  96. errorStream << "dxc_batch failed : Out of Memory.";
  97. } else if (hlslException.hr == E_INVALIDARG) {
  98. errorStream << "dxc_batch failed : Invalid argument.";
  99. } else {
  100. errorStream << "dxc_batch failed : error code 0x" << std::hex << hlslException.hr << ".";
  101. }
  102. errorStream << "\n";
  103. } catch (...) {
  104. errorStream << " unable to retrieve error message.\n";
  105. }
  106. }
  107. static int Compile(llvm::StringRef command, DxcDllSupport &dxcSupport,
  108. llvm::StringRef path, bool bLinkLib,
  109. std::string &errorString) {
  110. //llvm::raw_string_ostream &errorStream) {
  111. const OptTable *optionTable = getHlslOptTable();
  112. llvm::SmallVector<llvm::StringRef, 4> args;
  113. command.split(args, " ", /*MaxSplit*/-1, /*KeepEmpty*/false);
  114. if (!path.empty()) {
  115. args.emplace_back("-I");
  116. args.emplace_back(path);
  117. }
  118. MainArgs argStrings(args);
  119. DxcOpts dxcOpts;
  120. llvm::raw_string_ostream errorStream(errorString);
  121. int retVal =
  122. ReadDxcOpts(optionTable, DxcFlags, argStrings, dxcOpts, errorStream);
  123. if (0 == retVal) {
  124. try {
  125. DxcContext context(dxcOpts, dxcSupport);
  126. // TODO: implement all other actions.
  127. if (!dxcOpts.Preprocess.empty()) {
  128. context.Preprocess();
  129. }
  130. else if (dxcOpts.DumpBin) {
  131. retVal = context.DumpBinary();
  132. }
  133. else {
  134. retVal = context.Compile(path, bLinkLib);
  135. }
  136. }
  137. catch (const ::hlsl::Exception &hlslException) {
  138. PrintHlslException(hlslException, command, errorStream);
  139. retVal = 1;
  140. }
  141. catch (std::bad_alloc &) {
  142. errorStream << command << " failed - out of memory.\n";
  143. retVal = 1;
  144. }
  145. catch (...) {
  146. errorStream << command << " failed - unknown error.\n";
  147. retVal = 1;
  148. }
  149. }
  150. errorStream.flush();
  151. return retVal;
  152. }
  153. static void WriteBlobToFile(_In_opt_ IDxcBlob *pBlob, llvm::StringRef FName, UINT32 defaultTextCodePage) {
  154. ::dxc::WriteBlobToFile(pBlob, StringRefUtf16(FName), defaultTextCodePage);
  155. }
  156. static void WritePartToFile(IDxcBlob *pBlob, hlsl::DxilFourCC CC,
  157. llvm::StringRef FName) {
  158. const hlsl::DxilContainerHeader *pContainer = hlsl::IsDxilContainerLike(
  159. pBlob->GetBufferPointer(), pBlob->GetBufferSize());
  160. if (!pContainer) {
  161. throw hlsl::Exception(E_FAIL, "Unable to find required part in blob");
  162. }
  163. hlsl::DxilPartIsType pred(CC);
  164. hlsl::DxilPartIterator it =
  165. std::find_if(hlsl::begin(pContainer), hlsl::end(pContainer), pred);
  166. if (it == hlsl::end(pContainer)) {
  167. throw hlsl::Exception(E_FAIL, "Unable to find required part in blob");
  168. }
  169. const char *pData = hlsl::GetDxilPartData(*it);
  170. DWORD dataLen = (*it)->PartSize;
  171. StringRefUtf16 WideName(FName);
  172. CHandle file(CreateFileW(WideName, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
  173. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
  174. if (file == INVALID_HANDLE_VALUE) {
  175. IFT_Data(HRESULT_FROM_WIN32(GetLastError()), WideName);
  176. }
  177. DWORD written;
  178. if (FALSE == WriteFile(file, pData, dataLen, &written, nullptr)) {
  179. IFT_Data(HRESULT_FROM_WIN32(GetLastError()), WideName);
  180. }
  181. }
  182. // This function is called either after the compilation is done or /dumpbin
  183. // option is provided Performing options that are used to process dxil
  184. // container.
  185. int DxcContext::ActOnBlob(IDxcBlob *pBlob) {
  186. return ActOnBlob(pBlob, nullptr, nullptr);
  187. }
  188. int DxcContext::ActOnBlob(IDxcBlob *pBlob, IDxcBlob *pDebugBlob,
  189. LPCWSTR pDebugBlobName) {
  190. int retVal = 0;
  191. // Text output.
  192. if (m_Opts.AstDump || m_Opts.OptDump) {
  193. WriteBlobToConsole(pBlob);
  194. return retVal;
  195. }
  196. // Write the output blob.
  197. if (!m_Opts.OutputObject.empty()) {
  198. // For backward compatability: fxc requires /Fo for /extractrootsignature
  199. if (!m_Opts.ExtractRootSignature) {
  200. CComPtr<IDxcBlob> pResult;
  201. UpdatePart(pBlob, &pResult);
  202. WriteBlobToFile(pResult, m_Opts.OutputObject, m_Opts.DefaultTextCodePage);
  203. }
  204. }
  205. // Verify Root Signature
  206. if (!m_Opts.VerifyRootSignatureSource.empty()) {
  207. return VerifyRootSignature();
  208. }
  209. // Extract and write the PDB/debug information.
  210. if (!m_Opts.DebugFile.empty()) {
  211. IFTBOOLMSG(m_Opts.DebugInfo, E_INVALIDARG,
  212. "/Fd specified, but no Debug Info was "
  213. "found in the shader, please use the "
  214. "/Zi switch to generate debug "
  215. "information compiling this shader.");
  216. if (pDebugBlob != nullptr) {
  217. IFTBOOLMSG(pDebugBlobName && *pDebugBlobName, E_INVALIDARG,
  218. "/Fd was specified but no debug name was produced");
  219. WriteBlobToFile(pDebugBlob, pDebugBlobName, m_Opts.DefaultTextCodePage);
  220. } else {
  221. WritePartToFile(pBlob, hlsl::DFCC_ShaderDebugInfoDXIL, m_Opts.DebugFile);
  222. }
  223. }
  224. // Extract and write root signature information.
  225. if (m_Opts.ExtractRootSignature) {
  226. CComPtr<IDxcBlob> pRootSignatureContainer;
  227. ExtractRootSignature(pBlob, &pRootSignatureContainer);
  228. WriteBlobToFile(pRootSignatureContainer, m_Opts.OutputObject, m_Opts.DefaultTextCodePage);
  229. }
  230. // Extract and write private data.
  231. if (!m_Opts.ExtractPrivateFile.empty()) {
  232. WritePartToFile(pBlob, hlsl::DFCC_PrivateData, m_Opts.ExtractPrivateFile);
  233. }
  234. // OutputObject suppresses console dump.
  235. bool needDisassembly =
  236. !m_Opts.OutputHeader.empty() || !m_Opts.AssemblyCode.empty() ||
  237. (m_Opts.OutputObject.empty() && m_Opts.DebugFile.empty() &&
  238. m_Opts.ExtractPrivateFile.empty() &&
  239. m_Opts.VerifyRootSignatureSource.empty() &&
  240. !m_Opts.ExtractRootSignature);
  241. if (!needDisassembly)
  242. return retVal;
  243. CComPtr<IDxcBlobEncoding> pDisassembleResult;
  244. if (m_Opts.IsRootSignatureProfile()) {
  245. // keep the same behavior as fxc, people may want to embed the root
  246. // signatures in their code bases.
  247. CComPtr<IDxcLibrary> pLibrary;
  248. IFT(m_dxcSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  249. std::string Message = "Disassembly failed";
  250. IFT(pLibrary->CreateBlobWithEncodingOnHeapCopy(
  251. (LPBYTE)&Message[0], Message.size(), CP_ACP, &pDisassembleResult));
  252. } else {
  253. CComPtr<IDxcCompiler> pCompiler;
  254. IFT(m_dxcSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  255. IFT(pCompiler->Disassemble(pBlob, &pDisassembleResult));
  256. }
  257. if (!m_Opts.OutputHeader.empty()) {
  258. llvm::Twine varName = m_Opts.VariableName.empty()
  259. ? llvm::Twine("g_", m_Opts.EntryPoint)
  260. : m_Opts.VariableName;
  261. WriteHeader(pDisassembleResult, pBlob, varName,
  262. StringRefUtf16(m_Opts.OutputHeader));
  263. } else if (!m_Opts.AssemblyCode.empty()) {
  264. WriteBlobToFile(pDisassembleResult, m_Opts.AssemblyCode, m_Opts.DefaultTextCodePage);
  265. } else {
  266. WriteBlobToConsole(pDisassembleResult);
  267. }
  268. return retVal;
  269. }
  270. // Given a dxil container, update the dxil container by processing container
  271. // specific options.
  272. void DxcContext::UpdatePart(IDxcBlob *pSource, IDxcBlob **ppResult) {
  273. DXASSERT(pSource && ppResult, "otherwise blob cannot be updated");
  274. if (!UpdatePartRequired()) {
  275. *ppResult = pSource;
  276. pSource->AddRef();
  277. return;
  278. }
  279. CComPtr<IDxcContainerBuilder> pContainerBuilder;
  280. CComPtr<IDxcBlob> pResult;
  281. IFT(m_dxcSupport.CreateInstance(CLSID_DxcContainerBuilder,
  282. &pContainerBuilder));
  283. // Load original container and update blob for each given option
  284. IFT(pContainerBuilder->Load(pSource));
  285. // Update parts based on dxc options
  286. if (m_Opts.StripDebug) {
  287. IFT(pContainerBuilder->RemovePart(
  288. hlsl::DxilFourCC::DFCC_ShaderDebugInfoDXIL));
  289. }
  290. if (m_Opts.StripPrivate) {
  291. IFT(pContainerBuilder->RemovePart(hlsl::DxilFourCC::DFCC_PrivateData));
  292. }
  293. if (m_Opts.StripRootSignature) {
  294. IFT(pContainerBuilder->RemovePart(hlsl::DxilFourCC::DFCC_RootSignature));
  295. }
  296. if (!m_Opts.PrivateSource.empty()) {
  297. CComPtr<IDxcBlob> privateBlob;
  298. IFT(ReadFileIntoPartContent(hlsl::DxilFourCC::DFCC_PrivateData,
  299. StringRefUtf16(m_Opts.PrivateSource),
  300. &privateBlob));
  301. // setprivate option can replace existing private part.
  302. // Try removing the private data if exists
  303. pContainerBuilder->RemovePart(hlsl::DxilFourCC::DFCC_PrivateData);
  304. IFT(pContainerBuilder->AddPart(hlsl::DxilFourCC::DFCC_PrivateData,
  305. privateBlob));
  306. }
  307. if (!m_Opts.RootSignatureSource.empty()) {
  308. // set rootsignature assumes that the given input is a dxil container.
  309. // We only want to add RTS0 part to the container builder.
  310. CComPtr<IDxcBlob> RootSignatureBlob;
  311. IFT(ReadFileIntoPartContent(hlsl::DxilFourCC::DFCC_RootSignature,
  312. StringRefUtf16(m_Opts.RootSignatureSource),
  313. &RootSignatureBlob));
  314. // setrootsignature option can replace existing rootsignature part
  315. // Try removing rootsignature if exists
  316. pContainerBuilder->RemovePart(hlsl::DxilFourCC::DFCC_RootSignature);
  317. IFT(pContainerBuilder->AddPart(hlsl::DxilFourCC::DFCC_RootSignature,
  318. RootSignatureBlob));
  319. }
  320. // Get the final blob from container builder
  321. CComPtr<IDxcOperationResult> pBuilderResult;
  322. IFT(pContainerBuilder->SerializeContainer(&pBuilderResult));
  323. if (!m_Opts.OutputWarningsFile.empty()) {
  324. CComPtr<IDxcBlobEncoding> pErrors;
  325. IFT(pBuilderResult->GetErrorBuffer(&pErrors));
  326. if (pErrors != nullptr) {
  327. WriteBlobToFile(pErrors, m_Opts.OutputWarningsFile, m_Opts.DefaultTextCodePage);
  328. }
  329. } else {
  330. WriteOperationErrorsToConsole(pBuilderResult, m_Opts.OutputWarnings);
  331. }
  332. HRESULT status;
  333. IFT(pBuilderResult->GetStatus(&status));
  334. IFT(status);
  335. IFT(pBuilderResult->GetResult(ppResult));
  336. }
  337. bool DxcContext::UpdatePartRequired() {
  338. return m_Opts.StripDebug || m_Opts.StripPrivate ||
  339. m_Opts.StripRootSignature || !m_Opts.PrivateSource.empty() ||
  340. !m_Opts.RootSignatureSource.empty();
  341. }
  342. // This function reads the file from input file and constructs a blob with
  343. // fourCC parts Used for setprivate and setrootsignature option
  344. HRESULT DxcContext::ReadFileIntoPartContent(hlsl::DxilFourCC fourCC,
  345. LPCWSTR fileName,
  346. IDxcBlob **ppResult) {
  347. DXASSERT(fourCC == hlsl::DxilFourCC::DFCC_PrivateData ||
  348. fourCC == hlsl::DxilFourCC::DFCC_RootSignature,
  349. "Otherwise we provided wrong part to read for updating part.");
  350. // Read result, if it's private data, then return the blob
  351. if (fourCC == hlsl::DxilFourCC::DFCC_PrivateData) {
  352. CComPtr<IDxcBlobEncoding> pResult;
  353. ReadFileIntoBlob(m_dxcSupport, fileName, &pResult);
  354. *ppResult = pResult.Detach();
  355. }
  356. // If root signature, check if it's a dxil container that contains
  357. // rootsignature part, then construct a blob of root signature part
  358. if (fourCC == hlsl::DxilFourCC::DFCC_RootSignature) {
  359. CComPtr<IDxcBlob> pResult;
  360. CComHeapPtr<BYTE> pData;
  361. DWORD dataSize;
  362. hlsl::ReadBinaryFile(fileName, (void **)&pData, &dataSize);
  363. DXASSERT(pData != nullptr,
  364. "otherwise ReadBinaryFile should throw an exception");
  365. hlsl::DxilContainerHeader *pHeader =
  366. hlsl::IsDxilContainerLike(pData.m_pData, dataSize);
  367. IFRBOOL(IsValidDxilContainer(pHeader, dataSize), E_INVALIDARG);
  368. hlsl::DxilPartHeader *pPartHeader =
  369. hlsl::GetDxilPartByType(pHeader, hlsl::DxilFourCC::DFCC_RootSignature);
  370. IFRBOOL(pPartHeader != nullptr, E_INVALIDARG);
  371. hlsl::DxcCreateBlobOnHeapCopy(hlsl::GetDxilPartData(pPartHeader),
  372. pPartHeader->PartSize, &pResult);
  373. *ppResult = pResult.Detach();
  374. }
  375. return S_OK;
  376. }
  377. // Constructs a dxil container builder with only root signature part.
  378. // Right now IDxcContainerBuilder assumes that we are building a full dxil
  379. // container, but we are building a container with only rootsignature part
  380. void DxcContext::ExtractRootSignature(IDxcBlob *pBlob, IDxcBlob **ppResult) {
  381. DXASSERT_NOMSG(pBlob != nullptr && ppResult != nullptr);
  382. const hlsl::DxilContainerHeader *pHeader =
  383. (hlsl::DxilContainerHeader *)(pBlob->GetBufferPointer());
  384. IFTBOOL(hlsl::IsValidDxilContainer(pHeader, pHeader->ContainerSizeInBytes),
  385. DXC_E_CONTAINER_INVALID);
  386. const hlsl::DxilPartHeader *pPartHeader =
  387. hlsl::GetDxilPartByType(pHeader, hlsl::DxilFourCC::DFCC_RootSignature);
  388. IFTBOOL(pPartHeader != nullptr, DXC_E_MISSING_PART);
  389. // Get new header and allocate memory for new container
  390. hlsl::DxilContainerHeader newHeader;
  391. uint32_t containerSize =
  392. hlsl::GetDxilContainerSizeFromParts(1, pPartHeader->PartSize);
  393. hlsl::InitDxilContainer(&newHeader, 1, containerSize);
  394. CComPtr<IMalloc> pMalloc;
  395. CComPtr<hlsl::AbstractMemoryStream> pMemoryStream;
  396. IFT(CoGetMalloc(1, &pMalloc));
  397. IFT(hlsl::CreateMemoryStream(pMalloc, &pMemoryStream));
  398. ULONG cbWritten;
  399. // Write Container Header
  400. IFT(pMemoryStream->Write(&newHeader, sizeof(hlsl::DxilContainerHeader),
  401. &cbWritten));
  402. IFTBOOL(cbWritten == sizeof(hlsl::DxilContainerHeader), E_OUTOFMEMORY);
  403. // Write Part Offset
  404. uint32_t offset =
  405. sizeof(hlsl::DxilContainerHeader) + hlsl::GetOffsetTableSize(1);
  406. IFT(pMemoryStream->Write(&offset, sizeof(uint32_t), &cbWritten));
  407. IFTBOOL(cbWritten == sizeof(uint32_t), E_OUTOFMEMORY);
  408. // Write Root Signature Header
  409. IFT(pMemoryStream->Write(pPartHeader, sizeof(hlsl::DxilPartHeader),
  410. &cbWritten));
  411. IFTBOOL(cbWritten == sizeof(hlsl::DxilPartHeader), E_OUTOFMEMORY);
  412. const char *partContent = hlsl::GetDxilPartData(pPartHeader);
  413. // Write Root Signature Content
  414. IFT(pMemoryStream->Write(partContent, pPartHeader->PartSize, &cbWritten));
  415. IFTBOOL(cbWritten == pPartHeader->PartSize, E_OUTOFMEMORY);
  416. // Return Result
  417. CComPtr<IDxcBlob> pResult;
  418. IFT(pMemoryStream->QueryInterface(&pResult));
  419. *ppResult = pResult.Detach();
  420. }
  421. int DxcContext::VerifyRootSignature() {
  422. // Get dxil container from file
  423. CComPtr<IDxcBlobEncoding> pSource;
  424. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile), &pSource);
  425. hlsl::DxilContainerHeader *pSourceHeader =
  426. (hlsl::DxilContainerHeader *)pSource->GetBufferPointer();
  427. IFTBOOLMSG(hlsl::IsValidDxilContainer(pSourceHeader,
  428. pSourceHeader->ContainerSizeInBytes),
  429. E_INVALIDARG, "invalid DXIL container to verify.");
  430. // Get rootsignature from file
  431. CComPtr<IDxcBlob> pRootSignature;
  432. IFTMSG(ReadFileIntoPartContent(
  433. hlsl::DxilFourCC::DFCC_RootSignature,
  434. StringRefUtf16(m_Opts.VerifyRootSignatureSource), &pRootSignature),
  435. "invalid root signature to verify.");
  436. // TODO : Right now we are just going to bild a new blob with updated root
  437. // signature to verify root signature Since dxil container builder will verify
  438. // on its behalf. This does unnecessary memory allocation. We can improve this
  439. // later.
  440. CComPtr<IDxcContainerBuilder> pContainerBuilder;
  441. IFT(m_dxcSupport.CreateInstance(CLSID_DxcContainerBuilder,
  442. &pContainerBuilder));
  443. IFT(pContainerBuilder->Load(pSource));
  444. // Try removing root signature if it already exists
  445. pContainerBuilder->RemovePart(hlsl::DxilFourCC::DFCC_RootSignature);
  446. IFT(pContainerBuilder->AddPart(hlsl::DxilFourCC::DFCC_RootSignature,
  447. pRootSignature));
  448. CComPtr<IDxcOperationResult> pOperationResult;
  449. IFT(pContainerBuilder->SerializeContainer(&pOperationResult));
  450. HRESULT status = E_FAIL;
  451. CComPtr<IDxcBlob> pResult;
  452. IFT(pOperationResult->GetStatus(&status));
  453. if (FAILED(status)) {
  454. if (!m_Opts.OutputWarningsFile.empty()) {
  455. CComPtr<IDxcBlobEncoding> pErrors;
  456. IFT(pOperationResult->GetErrorBuffer(&pErrors));
  457. WriteBlobToFile(pErrors, m_Opts.OutputWarningsFile, m_Opts.DefaultTextCodePage);
  458. } else {
  459. WriteOperationErrorsToConsole(pOperationResult, m_Opts.OutputWarnings);
  460. }
  461. return 1;
  462. } else {
  463. printf("root signature verification succeeded.");
  464. return 0;
  465. }
  466. }
  467. class DxcIncludeHandlerForInjectedSources : public IDxcIncludeHandler {
  468. private:
  469. DXC_MICROCOM_REF_FIELD(m_dwRef)
  470. public:
  471. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  472. DxcIncludeHandlerForInjectedSources() : m_dwRef(0){};
  473. std::unordered_map<std::wstring, CComPtr<IDxcBlob>> includeFiles;
  474. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) {
  475. return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
  476. }
  477. HRESULT insertIncludeFile(_In_ LPCWSTR pFilename,
  478. _In_ IDxcBlobEncoding *pBlob, _In_ UINT32 dataLen) {
  479. try {
  480. includeFiles.try_emplace(std::wstring(pFilename), pBlob);
  481. }
  482. CATCH_CPP_RETURN_HRESULT()
  483. return S_OK;
  484. }
  485. HRESULT STDMETHODCALLTYPE
  486. LoadSource(_In_ LPCWSTR pFilename,
  487. _COM_Outptr_result_maybenull_ IDxcBlob **ppIncludeSource) override {
  488. try {
  489. *ppIncludeSource = includeFiles.at(std::wstring(pFilename));
  490. (*ppIncludeSource)->AddRef();
  491. }
  492. CATCH_CPP_RETURN_HRESULT()
  493. return S_OK;
  494. }
  495. };
  496. int DxcContext::Compile(llvm::StringRef path, bool bLibLink) {
  497. CComPtr<IDxcCompiler> pCompiler;
  498. CComPtr<IDxcOperationResult> pCompileResult;
  499. CComPtr<IDxcBlob> pDebugBlob;
  500. std::wstring debugName;
  501. {
  502. CComPtr<IDxcBlobEncoding> pSource;
  503. std::vector<std::wstring> argStrings;
  504. CopyArgsToWStrings(m_Opts.Args, CoreOption, argStrings);
  505. std::vector<LPCWSTR> args;
  506. args.reserve(argStrings.size());
  507. for (unsigned i = 0; i < argStrings.size(); i++) {
  508. const std::wstring &a = argStrings[i];
  509. if (a == L"-E" || a == L"-T") {
  510. // Skip entry and profile in arg.
  511. i++;
  512. continue;
  513. }
  514. args.push_back(a.data());
  515. }
  516. CComPtr<IDxcLibrary> pLibrary;
  517. IFT(m_dxcSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  518. IFT(m_dxcSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  519. if (path.empty()) {
  520. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile),
  521. &pSource);
  522. } else {
  523. llvm::sys::fs::MSFileSystem *msfPtr;
  524. IFT(CreateMSFileSystemForDisk(&msfPtr));
  525. std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr);
  526. ::llvm::sys::fs::AutoPerThreadSystem pts(msf.get());
  527. IFTLLVM(pts.error_code());
  528. if (llvm::sys::fs::exists(m_Opts.InputFile)) {
  529. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile),
  530. &pSource);
  531. } else {
  532. SmallString<128> pathStr(path.begin(), path.end());
  533. llvm::sys::path::append(pathStr, m_Opts.InputFile.begin(),
  534. m_Opts.InputFile.end());
  535. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(pathStr.str().str()),
  536. &pSource);
  537. }
  538. }
  539. IFTARG(pSource->GetBufferSize() >= 4);
  540. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  541. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  542. // Upgrade profile to 6.0 version from minimum recognized shader model
  543. llvm::StringRef TargetProfile = m_Opts.TargetProfile;
  544. const hlsl::ShaderModel *SM =
  545. hlsl::ShaderModel::GetByName(m_Opts.TargetProfile.str().c_str());
  546. if (SM->IsValid() && SM->GetMajor() < 6) {
  547. TargetProfile = hlsl::ShaderModel::Get(SM->GetKind(), 6, 0)->GetName();
  548. }
  549. if (bLibLink) {
  550. IFT(DxilD3DCompile2(pSource->GetBufferPointer(), pSource->GetBufferSize(),
  551. m_Opts.InputFile.str().c_str(),
  552. m_Opts.EntryPoint.str().c_str(),
  553. TargetProfile.str().c_str(), m_Opts.Defines.data(),
  554. m_Opts.Defines.size(), args.data(), args.size(),
  555. pIncludeHandler, &pCompileResult));
  556. } else {
  557. IFT(pCompiler->Compile(
  558. pSource, StringRefUtf16(m_Opts.InputFile),
  559. StringRefUtf16(m_Opts.EntryPoint), StringRefUtf16(TargetProfile),
  560. args.data(), args.size(), m_Opts.Defines.data(),
  561. m_Opts.Defines.size(), pIncludeHandler, &pCompileResult));
  562. }
  563. }
  564. if (!m_Opts.OutputWarningsFile.empty()) {
  565. CComPtr<IDxcBlobEncoding> pErrors;
  566. IFT(pCompileResult->GetErrorBuffer(&pErrors));
  567. WriteBlobToFile(pErrors, m_Opts.OutputWarningsFile, m_Opts.DefaultTextCodePage);
  568. } else {
  569. WriteOperationErrorsToConsole(pCompileResult, m_Opts.OutputWarnings);
  570. }
  571. HRESULT status;
  572. IFT(pCompileResult->GetStatus(&status));
  573. if (SUCCEEDED(status) || m_Opts.AstDump || m_Opts.OptDump) {
  574. CComPtr<IDxcBlob> pProgram;
  575. IFT(pCompileResult->GetResult(&pProgram));
  576. pCompiler.Release();
  577. pCompileResult.Release();
  578. if (pProgram.p != nullptr) {
  579. ActOnBlob(pProgram.p, pDebugBlob, debugName.c_str());
  580. }
  581. }
  582. return status;
  583. }
  584. int DxcContext::DumpBinary() {
  585. CComPtr<IDxcBlobEncoding> pSource;
  586. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile), &pSource);
  587. return ActOnBlob(pSource.p);
  588. }
  589. void DxcContext::Preprocess() {
  590. DXASSERT(!m_Opts.Preprocess.empty(),
  591. "else option reading should have failed");
  592. CComPtr<IDxcCompiler> pCompiler;
  593. CComPtr<IDxcOperationResult> pPreprocessResult;
  594. CComPtr<IDxcBlobEncoding> pSource;
  595. std::vector<LPCWSTR> args;
  596. CComPtr<IDxcLibrary> pLibrary;
  597. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  598. IFT(m_dxcSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  599. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  600. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile), &pSource);
  601. IFT(m_dxcSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  602. IFT(pCompiler->Preprocess(pSource, StringRefUtf16(m_Opts.InputFile),
  603. args.data(), args.size(), m_Opts.Defines.data(),
  604. m_Opts.Defines.size(), pIncludeHandler,
  605. &pPreprocessResult));
  606. WriteOperationErrorsToConsole(pPreprocessResult, m_Opts.OutputWarnings);
  607. HRESULT status;
  608. IFT(pPreprocessResult->GetStatus(&status));
  609. if (SUCCEEDED(status)) {
  610. CComPtr<IDxcBlob> pProgram;
  611. IFT(pPreprocessResult->GetResult(&pProgram));
  612. WriteBlobToFile(pProgram, m_Opts.Preprocess, m_Opts.DefaultTextCodePage);
  613. }
  614. }
  615. static void WriteString(HANDLE hFile, _In_z_ LPCSTR value, LPCWSTR pFileName) {
  616. DWORD written;
  617. if (FALSE == WriteFile(hFile, value, strlen(value) * sizeof(value[0]),
  618. &written, nullptr))
  619. IFT_Data(HRESULT_FROM_WIN32(GetLastError()), pFileName);
  620. }
  621. void DxcContext::WriteHeader(IDxcBlobEncoding *pDisassembly, IDxcBlob *pCode,
  622. llvm::Twine &pVariableName, LPCWSTR pFileName) {
  623. CHandle file(CreateFileW(pFileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
  624. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
  625. if (file == INVALID_HANDLE_VALUE) {
  626. IFT_Data(HRESULT_FROM_WIN32(GetLastError()), pFileName);
  627. }
  628. {
  629. std::string s;
  630. llvm::raw_string_ostream OS(s);
  631. // Note: with \r\n line endings, writing the disassembly could be a simple
  632. // WriteBlobToHandle with a prior and following WriteString for #ifs
  633. OS << "#if 0\r\n";
  634. const uint8_t *pBytes = (const uint8_t *)pDisassembly->GetBufferPointer();
  635. size_t len = pDisassembly->GetBufferSize();
  636. s.reserve(len + len * 0.1f); // rough estimate
  637. for (size_t i = 0; i < len; ++i) {
  638. if (pBytes[i] == '\n')
  639. OS << '\r';
  640. OS << pBytes[i];
  641. }
  642. OS << "\r\n#endif\r\n";
  643. OS.flush();
  644. WriteString(file, s.c_str(), pFileName);
  645. }
  646. {
  647. std::string s;
  648. llvm::raw_string_ostream OS(s);
  649. OS << "\r\nconst unsigned char " << pVariableName << "[] = {";
  650. const uint8_t *pBytes = (const uint8_t *)pCode->GetBufferPointer();
  651. size_t len = pCode->GetBufferSize();
  652. s.reserve(100 + len * 6 + (len / 12) * 3); // rough estimate
  653. for (size_t i = 0; i < len; ++i) {
  654. if (i != 0)
  655. OS << ',';
  656. if ((i % 12) == 0)
  657. OS << "\r\n ";
  658. OS << " 0x";
  659. if (pBytes[i] < 0x10)
  660. OS << '0';
  661. OS.write_hex(pBytes[i]);
  662. }
  663. OS << "\r\n};\r\n";
  664. OS.flush();
  665. WriteString(file, s.c_str(), pFileName);
  666. }
  667. }
  668. class DxcBatchContext {
  669. public:
  670. DxcBatchContext(DxcOpts &Opts, DxcDllSupport &dxcSupport)
  671. : m_Opts(Opts), m_dxcSupport(dxcSupport) {}
  672. int BatchCompile(bool bMultiThread, bool bLibLink);
  673. private:
  674. DxcOpts &m_Opts;
  675. DxcDllSupport &m_dxcSupport;
  676. };
  677. int DxcBatchContext::BatchCompile(bool bMultiThread, bool bLibLink) {
  678. int retVal = 0;
  679. DxcOpts tmp_Opts;
  680. // tmp_Opts = m_Opts;
  681. m_Opts.InputFile;
  682. SmallString<128> path(m_Opts.InputFile.begin(), m_Opts.InputFile.end());
  683. llvm::sys::path::remove_filename(path);
  684. CComPtr<IDxcBlobEncoding> pSource;
  685. ReadFileIntoBlob(m_dxcSupport, StringRefUtf16(m_Opts.InputFile), &pSource);
  686. llvm::StringRef source((char *)pSource->GetBufferPointer(),
  687. pSource->GetBufferSize());
  688. llvm::SmallVector<llvm::StringRef, 4> commands;
  689. source.split(commands, "\n", /*MaxSplit*/-1, /*KeepEmpty*/false);
  690. if (bMultiThread) {
  691. unsigned int threadNum = std::min<unsigned>(
  692. std::thread::hardware_concurrency(), commands.size());
  693. auto empty_fn = []() {};
  694. std::vector<std::thread> threads(threadNum);
  695. std::vector<std::string> errorStrings(threadNum);
  696. for (unsigned i = 0; i < threadNum; i++)
  697. threads[i] = std::thread(empty_fn);
  698. for (unsigned i = 0; i < commands.size(); i++) {
  699. // trim to remove /r if exist.
  700. llvm::StringRef command = commands[i].trim();
  701. if (command.empty())
  702. continue;
  703. if (command.startswith("//"))
  704. continue;
  705. unsigned threadIdx = i % threadNum;
  706. threads[threadIdx].join();
  707. threads[threadIdx] = std::thread(
  708. ::Compile, command, std::ref(m_dxcSupport), path.str(), bLibLink,
  709. std::ref(errorStrings[threadIdx]));
  710. }
  711. for (auto &th : threads)
  712. th.join();
  713. for (unsigned i = 0; i < threadNum; i++) {
  714. auto &errorString = errorStrings[i];
  715. if (errorString.size()) {
  716. fprintf(stderr, "dxc_batch failed : %s", errorString.c_str());
  717. if (0 == retVal)
  718. retVal = 1;
  719. }
  720. }
  721. } else {
  722. for (llvm::StringRef command : commands) {
  723. // trim to remove /r if exist.
  724. command = command.trim();
  725. if (command.empty())
  726. continue;
  727. if (command.startswith("//"))
  728. continue;
  729. std::string errorString;
  730. int ret = Compile(command, m_dxcSupport, path.str(), bLibLink, errorString);
  731. if (ret && 0 == retVal)
  732. retVal = ret;
  733. if (errorString.size()) {
  734. fprintf(stderr, "dxc_batch failed : %s", errorString.c_str());
  735. if (0 == retVal)
  736. retVal = 1;
  737. }
  738. }
  739. }
  740. return retVal;
  741. }
  742. int __cdecl wmain(int argc, const wchar_t **argv_) {
  743. const char *pStage = "Initialization";
  744. int retVal = 0;
  745. if (llvm::sys::fs::SetupPerThreadFileSystem())
  746. return 1;
  747. llvm::sys::fs::AutoCleanupPerThreadFileSystem auto_cleanup_fs;
  748. try {
  749. auto t_start = std::chrono::high_resolution_clock::now();
  750. std::error_code ec = hlsl::options::initHlslOptTable();
  751. if (ec) {
  752. fprintf(stderr, "%s failed - %s.\n", pStage, ec.message().c_str());
  753. return ec.value();
  754. }
  755. pStage = "Argument processing";
  756. const char *kMultiThreadArg = "-multi-thread";
  757. bool bMultiThread = false;
  758. const char *kLibLinkArg = "-lib-link";
  759. bool bLibLink = false;
  760. // Parse command line options.
  761. const OptTable *optionTable = getHlslOptTable();
  762. MainArgs argStrings(argc, argv_);
  763. llvm::ArrayRef<const char *> tmpArgStrings = argStrings.getArrayRef();
  764. std::vector<std::string> args(tmpArgStrings.begin(), tmpArgStrings.end());
  765. // Add target to avoid fail.
  766. args.emplace_back("-T");
  767. args.emplace_back("lib_6_x");
  768. std::vector<StringRef> refArgs;
  769. refArgs.reserve(args.size());
  770. for (auto &arg : args) {
  771. if (arg != kMultiThreadArg && arg != kLibLinkArg) {
  772. refArgs.emplace_back(arg.c_str());
  773. } else if (arg == kLibLinkArg) {
  774. bLibLink = true;
  775. } else {
  776. bMultiThread = true;
  777. }
  778. }
  779. MainArgs batchArgStrings(refArgs);
  780. DxcOpts dxcOpts;
  781. DxcDllSupport dxcSupport;
  782. // Read options and check errors.
  783. {
  784. std::string errorString;
  785. llvm::raw_string_ostream errorStream(errorString);
  786. int optResult = ReadDxcOpts(optionTable, DxcFlags, batchArgStrings,
  787. dxcOpts, errorStream);
  788. // TODO: validate unused option for dxc_bach.
  789. errorStream.flush();
  790. if (errorString.size()) {
  791. fprintf(stderr, "dxc_batch failed : %s", errorString.data());
  792. }
  793. if (optResult != 0) {
  794. return optResult;
  795. }
  796. }
  797. // Handle help request, which overrides any other processing.
  798. if (dxcOpts.ShowHelp) {
  799. std::string helpString;
  800. llvm::raw_string_ostream helpStream(helpString);
  801. optionTable->PrintHelp(helpStream, "dxc_batch.exe", "HLSL Compiler", "");
  802. helpStream << "multi-thread";
  803. helpStream.flush();
  804. dxc::WriteUtf8ToConsoleSizeT(helpString.data(), helpString.size());
  805. return 0;
  806. }
  807. // Setup a helper DLL.
  808. {
  809. std::string dllErrorString;
  810. llvm::raw_string_ostream dllErrorStream(dllErrorString);
  811. int dllResult = SetupDxcDllSupport(dxcOpts, dxcSupport, dllErrorStream);
  812. dllErrorStream.flush();
  813. if (dllErrorString.size()) {
  814. fprintf(stderr, "%s", dllErrorString.data());
  815. }
  816. if (dllResult)
  817. return dllResult;
  818. }
  819. EnsureEnabled(dxcSupport);
  820. DxcBatchContext context(dxcOpts, dxcSupport);
  821. pStage = "BatchCompilation";
  822. retVal = context.BatchCompile(bMultiThread, bLibLink);
  823. {
  824. auto t_end = std::chrono::high_resolution_clock::now();
  825. double duration_ms =
  826. std::chrono::duration<double, std::milli>(t_end - t_start).count();
  827. fprintf(stderr, "duration: %f sec\n", duration_ms / 1000);
  828. }
  829. } catch (const ::hlsl::Exception &hlslException) {
  830. std::string errorString;
  831. llvm::raw_string_ostream errorStream(errorString);
  832. PrintHlslException(hlslException, pStage, errorStream);
  833. errorStream.flush();
  834. fprintf(stderr, "dxc_batch failed : %s", errorString.c_str());
  835. return 1;
  836. } catch (std::bad_alloc &) {
  837. printf("%s failed - out of memory.\n", pStage);
  838. return 1;
  839. } catch (...) {
  840. printf("%s failed - unknown error.\n", pStage);
  841. return 1;
  842. }
  843. return retVal;
  844. }