1 #include "dxil_validator.h"
2
3 #include <windows.h>
4 #include <unknwn.h>
5
6 #include "util/ralloc.h"
7 #include "util/u_debug.h"
8 #include "util/compiler.h"
9
10 #include "dxcapi.h"
11
12 #include <wrl/client.h>
13 using Microsoft::WRL::ComPtr;
14
15 struct dxil_validator {
16 HMODULE dxil_mod;
17 HMODULE dxcompiler_mod;
18
19 IDxcValidator *dxc_validator;
20 IDxcLibrary *dxc_library;
21 IDxcCompiler *dxc_compiler;
22
23 enum dxil_validator_version version;
24 };
25
26 extern "C" {
27 extern IMAGE_DOS_HEADER __ImageBase;
28 }
29
30 static HMODULE
load_dxil_mod()31 load_dxil_mod()
32 {
33 /* First, try to load DXIL.dll from the default search-path */
34 #if defined(_GAMING_XBOX_SCARLETT)
35 HMODULE mod = LoadLibraryA("dxcompiler_xs.dll");
36 #elif defined (_GAMING_XBOX)
37 HMODULE mod = LoadLibraryA("dxcompiler_x.dll");
38 #else
39 HMODULE mod = LoadLibraryA("DXIL.dll");
40 #endif
41 if (mod)
42 return mod;
43
44 /* If that fails, try to load it next to the current module, so we can
45 * ship DXIL.dll next to the GLon12 DLL.
46 */
47
48 char self_path[MAX_PATH];
49 uint32_t path_size = GetModuleFileNameA((HINSTANCE)&__ImageBase,
50 self_path, sizeof(self_path));
51 if (!path_size || path_size == sizeof(self_path)) {
52 debug_printf("DXIL: Unable to get path to self");
53 return NULL;
54 }
55
56 auto last_slash = strrchr(self_path, '\\');
57 if (!last_slash) {
58 debug_printf("DXIL: Unable to get path to self");
59 return NULL;
60 }
61
62 *(last_slash + 1) = '\0';
63 if (strcat_s(self_path, "DXIL.dll") != 0) {
64 debug_printf("DXIL: Unable to get path to DXIL.dll next to self");
65 return NULL;
66 }
67
68 return LoadLibraryA(self_path);
69 }
70
71 static IDxcValidator *
create_dxc_validator(HMODULE dxil_mod)72 create_dxc_validator(HMODULE dxil_mod)
73 {
74 DxcCreateInstanceProc dxil_create_func =
75 (DxcCreateInstanceProc)GetProcAddress(dxil_mod, "DxcCreateInstance");
76 if (!dxil_create_func) {
77 debug_printf("DXIL: Failed to load DxcCreateInstance from DXIL.dll\n");
78 return NULL;
79 }
80
81 IDxcValidator *dxc_validator;
82 HRESULT hr = dxil_create_func(CLSID_DxcValidator,
83 IID_PPV_ARGS(&dxc_validator));
84 if (FAILED(hr)) {
85 debug_printf("DXIL: Failed to create validator\n");
86 return NULL;
87 }
88
89 return dxc_validator;
90 }
91
92 static enum dxil_validator_version
get_validator_version(IDxcValidator * val)93 get_validator_version(IDxcValidator *val)
94 {
95 ComPtr<IDxcVersionInfo> version_info;
96 if (FAILED(val->QueryInterface(version_info.ReleaseAndGetAddressOf())))
97 return NO_DXIL_VALIDATION;
98
99 UINT32 major, minor;
100 if (FAILED(version_info->GetVersion(&major, &minor)))
101 return NO_DXIL_VALIDATION;
102
103 if (major == 1)
104 return (enum dxil_validator_version)(DXIL_VALIDATOR_1_0 + MIN2(minor, 8));
105 if (major > 1)
106 return DXIL_VALIDATOR_1_8;
107 return NO_DXIL_VALIDATION;
108 }
109
110 #ifndef _GAMING_XBOX
111 static uint64_t
get_dll_version(HMODULE mod)112 get_dll_version(HMODULE mod)
113 {
114 WCHAR filename[MAX_PATH];
115 DWORD filename_length = GetModuleFileNameW(mod, filename, ARRAY_SIZE(filename));
116
117 if (filename_length == 0 || filename_length == ARRAY_SIZE(filename))
118 return 0;
119
120 DWORD version_handle = 0;
121 DWORD version_size = GetFileVersionInfoSizeW(filename, &version_handle);
122 if (version_size == 0)
123 return 0;
124
125 void *version_data = malloc(version_size);
126 if (!version_data)
127 return 0;
128
129 if (!GetFileVersionInfoW(filename, version_handle, version_size, version_data)) {
130 free(version_data);
131 return 0;
132 }
133
134 UINT value_size = 0;
135 VS_FIXEDFILEINFO *version_info = nullptr;
136 if (!VerQueryValueW(version_data, L"\\", reinterpret_cast<void **>(&version_info), &value_size) ||
137 !value_size ||
138 version_info->dwSignature != VS_FFI_SIGNATURE) {
139 free(version_data);
140 return 0;
141 }
142
143 uint64_t ret =
144 ((uint64_t)version_info->dwFileVersionMS << 32ull) |
145 (uint64_t)version_info->dwFileVersionLS;
146 free(version_data);
147 return ret;
148 }
149 #endif
150
151 static enum dxil_validator_version
get_filtered_validator_version(HMODULE mod,enum dxil_validator_version raw)152 get_filtered_validator_version(HMODULE mod, enum dxil_validator_version raw)
153 {
154 switch (raw) {
155 #ifndef _GAMING_XBOX
156 case DXIL_VALIDATOR_1_6: {
157 uint64_t dxil_version = get_dll_version(mod);
158 static constexpr uint64_t known_bad_version =
159 // 101.5.2005.60
160 (101ull << 48ull) | (5ull << 32ull) | (2005ull << 16ull) | 60ull;
161 if (dxil_version == known_bad_version)
162 return DXIL_VALIDATOR_1_5;
163 FALLTHROUGH;
164 }
165 #endif
166 default:
167 return raw;
168 }
169 }
170
171 struct dxil_validator *
dxil_create_validator(const void * ctx)172 dxil_create_validator(const void *ctx)
173 {
174 struct dxil_validator *val = rzalloc(ctx, struct dxil_validator);
175 if (!val)
176 return NULL;
177
178 /* Load DXIL.dll. This is a hard requirement on Windows, so we error
179 * out if this fails.
180 */
181 val->dxil_mod = load_dxil_mod();
182 if (!val->dxil_mod) {
183 debug_printf("DXIL: Failed to load DXIL.dll\n");
184 goto fail;
185 }
186
187 /* Create IDxcValidator. This is a hard requirement on Windows, so we
188 * error out if this fails.
189 */
190 val->dxc_validator = create_dxc_validator(val->dxil_mod);
191 if (!val->dxc_validator)
192 goto fail;
193
194 val->version = get_filtered_validator_version(
195 val->dxil_mod,
196 get_validator_version(val->dxc_validator));
197
198 /* Try to load dxcompiler.dll. This is just used for diagnostics, and
199 * will fail on most end-users install. So we do not error out if this
200 * fails.
201 */
202 val->dxcompiler_mod = LoadLibraryA("dxcompiler.dll");
203 if (val->dxcompiler_mod) {
204 /* If we managed to load dxcompiler.dll, but either don't find
205 * DxcCreateInstance, or fail to create IDxcLibrary or
206 * IDxcCompiler, this is a good indication that the user wants
207 * diagnostics, but something went wrong. Print warnings to help
208 * figuring out what's wrong, but do not treat it as an error.
209 */
210 DxcCreateInstanceProc compiler_create_func =
211 (DxcCreateInstanceProc)GetProcAddress(val->dxcompiler_mod,
212 "DxcCreateInstance");
213 if (!compiler_create_func) {
214 debug_printf("DXIL: Failed to load DxcCreateInstance from "
215 "dxcompiler.dll\n");
216 } else {
217 if (FAILED(compiler_create_func(CLSID_DxcLibrary,
218 IID_PPV_ARGS(&val->dxc_library))))
219 debug_printf("DXIL: Unable to create IDxcLibrary instance\n");
220
221 if (FAILED(compiler_create_func(CLSID_DxcCompiler,
222 IID_PPV_ARGS(&val->dxc_compiler))))
223 debug_printf("DXIL: Unable to create IDxcCompiler instance\n");
224 }
225 }
226
227 return val;
228
229 fail:
230 if (val->dxil_mod)
231 FreeLibrary(val->dxil_mod);
232
233 ralloc_free(val);
234 return NULL;
235 }
236
237 void
dxil_destroy_validator(struct dxil_validator * val)238 dxil_destroy_validator(struct dxil_validator *val)
239 {
240 if (!val)
241 return;
242
243 /* if we have a validator, we have these */
244 val->dxc_validator->Release();
245 FreeLibrary(val->dxil_mod);
246
247 if (val->dxcompiler_mod) {
248 if (val->dxc_library)
249 val->dxc_library->Release();
250
251 if (val->dxc_compiler)
252 val->dxc_compiler->Release();
253
254 FreeLibrary(val->dxcompiler_mod);
255 }
256
257 ralloc_free(val);
258 }
259
260 class ShaderBlob : public IDxcBlob {
261 public:
ShaderBlob(void * data,size_t size)262 ShaderBlob(void *data, size_t size) :
263 m_data(data),
264 m_size(size)
265 {
266 }
267
268 LPVOID STDMETHODCALLTYPE
GetBufferPointer(void)269 GetBufferPointer(void) override
270 {
271 return m_data;
272 }
273
274 SIZE_T STDMETHODCALLTYPE
GetBufferSize()275 GetBufferSize() override
276 {
277 return m_size;
278 }
279
280 HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID,void **)281 QueryInterface(REFIID, void **) override
282 {
283 return E_NOINTERFACE;
284 }
285
286 ULONG STDMETHODCALLTYPE
AddRef()287 AddRef() override
288 {
289 return 1;
290 }
291
292 ULONG STDMETHODCALLTYPE
Release()293 Release() override
294 {
295 return 0;
296 }
297
298 void *m_data;
299 size_t m_size;
300 };
301
302 bool
dxil_validate_module(struct dxil_validator * val,void * data,size_t size,char ** error)303 dxil_validate_module(struct dxil_validator *val, void *data, size_t size, char **error)
304 {
305 if (!val)
306 return false;
307
308 ShaderBlob source(data, size);
309
310 ComPtr<IDxcOperationResult> result;
311 val->dxc_validator->Validate(&source, DxcValidatorFlags_InPlaceEdit,
312 &result);
313
314 HRESULT hr;
315 result->GetStatus(&hr);
316
317 if (FAILED(hr) && error) {
318 /* try to resolve error message */
319 *error = NULL;
320 if (!val->dxc_library) {
321 debug_printf("DXIL: validation failed, but lacking IDxcLibrary"
322 "from dxcompiler.dll for proper diagnostics.\n");
323 return false;
324 }
325
326 ComPtr<IDxcBlobEncoding> blob, blob_utf8;
327
328 if (FAILED(result->GetErrorBuffer(&blob)))
329 fprintf(stderr, "DXIL: IDxcOperationResult::GetErrorBuffer() failed\n");
330 else if (FAILED(val->dxc_library->GetBlobAsUtf8(blob.Get(),
331 blob_utf8.GetAddressOf())))
332 fprintf(stderr, "DXIL: IDxcLibrary::GetBlobAsUtf8() failed\n");
333 else {
334 char *str = reinterpret_cast<char *>(blob_utf8->GetBufferPointer());
335 str[blob_utf8->GetBufferSize() - 1] = 0;
336 *error = ralloc_strdup(val, str);
337 }
338 }
339
340 return SUCCEEDED(hr);
341 }
342
343 char *
dxil_disasm_module(struct dxil_validator * val,void * data,size_t size)344 dxil_disasm_module(struct dxil_validator *val, void *data, size_t size)
345 {
346 if (!val)
347 return NULL;
348
349 if (!val->dxc_compiler || !val->dxc_library) {
350 fprintf(stderr, "DXIL: disassembly requires IDxcLibrary and "
351 "IDxcCompiler from dxcompiler.dll\n");
352 return NULL;
353 }
354
355 ShaderBlob source(data, size);
356 ComPtr<IDxcBlobEncoding> blob, blob_utf8;
357
358 if (FAILED(val->dxc_compiler->Disassemble(&source, &blob))) {
359 fprintf(stderr, "DXIL: IDxcCompiler::Disassemble() failed\n");
360 return NULL;
361 }
362
363 if (FAILED(val->dxc_library->GetBlobAsUtf8(blob.Get(), blob_utf8.GetAddressOf()))) {
364 fprintf(stderr, "DXIL: IDxcLibrary::GetBlobAsUtf8() failed\n");
365 return NULL;
366 }
367
368 char *str = reinterpret_cast<char*>(blob_utf8->GetBufferPointer());
369 str[blob_utf8->GetBufferSize() - 1] = 0;
370 return ralloc_strdup(val, str);
371 }
372
373 enum dxil_validator_version
dxil_get_validator_version(struct dxil_validator * val)374 dxil_get_validator_version(struct dxil_validator *val)
375 {
376 return val ? val->version : NO_DXIL_VALIDATION;
377 }
378