xref: /aosp_15_r20/external/mesa3d/src/microsoft/compiler/dxil_validator.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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