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