dxc_batch.cpp 34 KB

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