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