• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/dxc_helper.h"
16 
17 #include <algorithm>
18 #include <sstream>
19 
20 #include "src/platform.h"
21 #include "src/virtual_file_store.h"
22 
23 #if AMBER_PLATFORM_WINDOWS
24 #pragma warning(push)
25 #pragma warning(disable : 4267)
26 #pragma warning(disable : 4003)
27 #endif  // AMBER_PLATFORM_WINDOWS
28 
29 #pragma clang diagnostic push
30 #pragma clang diagnostic ignored "-Wreserved-id-macro"
31 #pragma clang diagnostic ignored "-Wextra-semi"
32 #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec"
33 #pragma clang diagnostic ignored "-Wold-style-cast"
34 #pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
35 #pragma clang diagnostic ignored "-Wconversion"
36 #pragma clang diagnostic ignored "-Wsign-conversion"
37 #pragma clang diagnostic ignored "-Wshadow"
38 #pragma clang diagnostic ignored "-Wweak-vtables"
39 #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
40 #pragma clang diagnostic ignored "-Wundef"
41 #pragma clang diagnostic ignored "-Wunused-function"
42 #pragma clang diagnostic ignored "-Wunused-parameter"
43 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
44 #pragma clang diagnostic ignored "-Wreserved-identifier"
45 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
46 #pragma clang diagnostic ignored "-Wglobal-constructors"
47 #pragma clang diagnostic ignored "-Wdocumentation-deprecated-sync"
48 #pragma clang diagnostic ignored "-Wsuggest-override"
49 #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
50 #pragma clang diagnostic ignored "-Wdeprecated"
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wunused-function"
53 #pragma GCC diagnostic ignored "-Wunused-parameter"
54 #ifndef __STDC_LIMIT_MACROS
55 #define __STDC_LIMIT_MACROS
56 #endif  // __STDC_LIMIT_MACROS
57 #ifndef __STDC_CONSTANT_MACROS
58 #define __STDC_CONSTANT_MACROS
59 #endif  // __STDC_CONSTANT_MACROS
60 
61 // clang-format off
62 // The order here matters, so don't reformat.
63 #include "dxc/Support/Global.h"
64 #include "dxc/Support/HLSLOptions.h"
65 #include "dxc/dxcapi.h"
66 #include "dxc/Support/microcom.h"
67 // clang-format on
68 
69 namespace amber {
70 namespace dxchelper {
71 namespace {
72 
73 const wchar_t* kDxcFlags[] = {
74     L"-spirv",               // SPIR-V compilation
75     L"-enable-16bit-types",  // Enabling 16bit types
76 };
77 const size_t kDxcFlagsCount = sizeof(kDxcFlags) / sizeof(const wchar_t*);
78 
79 // Converts an IDxcBlob into a vector of 32-bit unsigned integers which
80 // is returned via the 'binaryWords' argument.
ConvertIDxcBlobToUint32(IDxcBlob * blob,std::vector<uint32_t> * binaryWords)81 void ConvertIDxcBlobToUint32(IDxcBlob* blob,
82                              std::vector<uint32_t>* binaryWords) {
83   size_t num32BitWords = (blob->GetBufferSize() + 3) / 4;
84   std::string binaryStr(static_cast<char*>(blob->GetBufferPointer()),
85                         blob->GetBufferSize());
86   binaryStr.resize(num32BitWords * 4, 0);
87   binaryWords->resize(num32BitWords, 0);
88   memcpy(binaryWords->data(), binaryStr.data(), binaryStr.size());
89 }
90 
91 class IncludeHandler : public IDxcIncludeHandler {
92   DXC_MICROCOM_REF_FIELD(dw_ref_)
93 
94  public:
95   DXC_MICROCOM_ADDREF_RELEASE_IMPL(dw_ref_)
96 
IncludeHandler(const VirtualFileStore * file_store,IDxcLibrary * dxc_lib,IDxcIncludeHandler * fallback)97   IncludeHandler(const VirtualFileStore* file_store,
98                  IDxcLibrary* dxc_lib,
99                  IDxcIncludeHandler* fallback)
100       : file_store_(file_store), dxc_lib_(dxc_lib), fallback_(fallback) {}
101 
LoadSource(LPCWSTR pFilename,IDxcBlob ** ppIncludeSource)102   HRESULT STDMETHODCALLTYPE LoadSource(LPCWSTR pFilename,
103                                        IDxcBlob** ppIncludeSource) override {
104     std::wstring wide_path(pFilename);
105     std::string path = std::string(wide_path.begin(), wide_path.end());
106 
107     std::string content;
108     Result r = file_store_->Get(path, &content);
109     if (r.IsSuccess()) {
110       IDxcBlobEncoding* source;
111       auto res = dxc_lib_->CreateBlobWithEncodingOnHeapCopy(
112           content.data(), static_cast<uint32_t>(content.size()), CP_UTF8,
113           &source);
114       if (res != S_OK) {
115         DxcCleanupThreadMalloc();
116         return res;
117       }
118       *ppIncludeSource = source;
119       return S_OK;
120     }
121 
122     return fallback_->LoadSource(pFilename, ppIncludeSource);
123   }
124 
QueryInterface(REFIID iid,void ** ppvObject)125   HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
126                                            void** ppvObject) override {
127     return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
128   }
129 
130  private:
131   const VirtualFileStore* const file_store_;
132   IDxcLibrary* const dxc_lib_;
133   IDxcIncludeHandler* const fallback_;
134 };
135 
136 #pragma GCC diagnostic pop
137 #pragma clang diagnostic pop
138 #if AMBER_PLATFORM_WINDOWS
139 #pragma warning(pop)
140 #endif  // AMBER_PLATFORM_WINDOWS
141 
142 }  // namespace
143 
Compile(const std::string & src,const std::string & entry,const std::string & profile,const std::string & spv_env,const std::string & filename,const VirtualFileStore * virtual_files,std::vector<uint32_t> * generated_binary)144 Result Compile(const std::string& src,
145                const std::string& entry,
146                const std::string& profile,
147                const std::string& spv_env,
148                const std::string& filename,
149                const VirtualFileStore* virtual_files,
150                std::vector<uint32_t>* generated_binary) {
151   if (hlsl::options::initHlslOptTable()) {
152     DxcCleanupThreadMalloc();
153     return Result("DXC compile failure: initHlslOptTable");
154   }
155 
156   IDxcLibrary* dxc_lib;
157   if (DxcCreateInstance(CLSID_DxcLibrary, __uuidof(IDxcLibrary),
158                         reinterpret_cast<void**>(&dxc_lib)) < 0) {
159     DxcCleanupThreadMalloc();
160     return Result("DXCCreateInstance for DXCLibrary failed");
161   }
162 
163   IDxcBlobEncoding* source;
164   if (dxc_lib->CreateBlobWithEncodingOnHeapCopy(
165           src.data(), static_cast<uint32_t>(src.size()), CP_UTF8, &source) <
166       0) {
167     DxcCleanupThreadMalloc();
168     return Result("DXC compile failure: CreateBlobFromFile");
169   }
170 
171   IDxcIncludeHandler* fallback_include_handler;
172   if (dxc_lib->CreateIncludeHandler(&fallback_include_handler) < 0) {
173     DxcCleanupThreadMalloc();
174     return Result("DXC compile failure: CreateIncludeHandler");
175   }
176 
177   CComPtr<IDxcIncludeHandler> include_handler(
178       new IncludeHandler(virtual_files, dxc_lib, fallback_include_handler));
179 
180   IDxcCompiler* compiler;
181   if (DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler),
182                         reinterpret_cast<void**>(&compiler)) < 0) {
183     DxcCleanupThreadMalloc();
184     return Result("DXCCreateInstance for DXCCompiler failed");
185   }
186 
187   std::string filepath = filename.empty() ? ("amber." + profile) : filename;
188 
189   std::vector<const wchar_t*> dxc_flags(kDxcFlags, &kDxcFlags[kDxcFlagsCount]);
190 
191   const wchar_t* target_env = nullptr;
192   if (!spv_env.compare("spv1.3") || !spv_env.compare("vulkan1.1")) {
193     target_env = L"-fspv-target-env=vulkan1.1";
194   } else if (!spv_env.compare("spv1.0") || !spv_env.compare("vulkan1.0")) {
195     target_env = L"-fspv-target-env=vulkan1.0";
196   } else if (!spv_env.empty()) {
197     return Result(
198         "Invalid target environment. Choose spv1.3 or vulkan1.1 for vulkan1.1 "
199         "and spv1.0 or vulkan1.0 for vulkan1.0.");
200   }
201   if (target_env)
202     dxc_flags.push_back(target_env);
203 
204   IDxcOperationResult* result;
205   if (compiler->Compile(
206           source, /* source text */
207           std::wstring(filepath.begin(), filepath.end())
208               .c_str(), /* original file source */
209           std::wstring(entry.begin(), entry.end())
210               .c_str(), /* entry point name */
211           std::wstring(profile.begin(), profile.end())
212               .c_str(),     /* shader profile to compile */
213           dxc_flags.data(), /* arguments */
214           static_cast<uint32_t>(dxc_flags.size()), /* argument count */
215           nullptr,                                 /* defines */
216           0,                                       /* define count */
217           include_handler,                         /* handler for #include */
218           &result /* output status */) < 0) {
219     DxcCleanupThreadMalloc();
220     return Result("DXC compile failure: Compile");
221   }
222 
223   // Compilation is done. We can clean up the HlslOptTable.
224   hlsl::options::cleanupHlslOptTable();
225 
226   // Get compilation results.
227   HRESULT result_status;
228   if (result->GetStatus(&result_status) < 0) {
229     DxcCleanupThreadMalloc();
230     return Result("DXC compile failure: GetStatus");
231   }
232 
233   // Get diagnostics string.
234   IDxcBlobEncoding* error_buffer;
235   if (result->GetErrorBuffer(&error_buffer)) {
236     DxcCleanupThreadMalloc();
237     return Result("DXC compile failure: GetErrorBuffer");
238   }
239 
240   const std::string diagnostics(
241       static_cast<char*>(error_buffer->GetBufferPointer()),
242       error_buffer->GetBufferSize());
243 
244   bool success = true;
245   if (static_cast<HRESULT>(result_status) >= 0) {
246     IDxcBlob* compiled_blob;
247     if (result->GetResult(&compiled_blob) < 0) {
248       DxcCleanupThreadMalloc();
249       return Result("DXC compile failure: GetResult");
250     }
251     ConvertIDxcBlobToUint32(compiled_blob, generated_binary);
252   } else {
253     success = false;
254   }
255 
256   DxcCleanupThreadMalloc();
257   return success ? Result() : Result("DXC compile failure: " + diagnostics);
258 }
259 
260 }  // namespace dxchelper
261 }  // namespace amber
262