1 /*
2  * This file is part of libplacebo.
3  *
4  * libplacebo is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * libplacebo is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "gpu.h"
19 
20 // Windows 8 enum value, not present in mingw-w64 v7
21 #define DXGI_ADAPTER_FLAG_SOFTWARE (2)
22 
23 const struct pl_d3d11_params pl_d3d11_default_params = {
24     .allow_software = true,
25 };
26 
27 static INIT_ONCE d3d11_once = INIT_ONCE_STATIC_INIT;
28 static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL;
29 static PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory1 = NULL;
d3d11_load(void)30 static void d3d11_load(void)
31 {
32     BOOL bPending = FALSE;
33     InitOnceBeginInitialize(&d3d11_once, 0, &bPending, NULL);
34 
35     if (bPending)
36     {
37         HMODULE d3d11 = LoadLibraryW(L"d3d11.dll");
38         if (d3d11) {
39             pD3D11CreateDevice = (void *)
40                 GetProcAddress(d3d11, "D3D11CreateDevice");
41         }
42 
43         HMODULE dxgi = LoadLibraryW(L"dxgi.dll");
44         if (dxgi) {
45             pCreateDXGIFactory1 = (void *)
46                 GetProcAddress(dxgi, "CreateDXGIFactory1");
47         }
48     }
49 
50     InitOnceComplete(&d3d11_once, 0, NULL);
51 }
52 
53 // Get a const array of D3D_FEATURE_LEVELs from max_fl to min_fl (inclusive)
get_feature_levels(int max_fl,int min_fl,const D3D_FEATURE_LEVEL ** out)54 static int get_feature_levels(int max_fl, int min_fl,
55                               const D3D_FEATURE_LEVEL **out)
56 {
57     static const D3D_FEATURE_LEVEL levels[] = {
58         D3D_FEATURE_LEVEL_12_1,
59         D3D_FEATURE_LEVEL_12_0,
60         D3D_FEATURE_LEVEL_11_1,
61         D3D_FEATURE_LEVEL_11_0,
62         D3D_FEATURE_LEVEL_10_1,
63         D3D_FEATURE_LEVEL_10_0,
64         D3D_FEATURE_LEVEL_9_3,
65         D3D_FEATURE_LEVEL_9_2,
66         D3D_FEATURE_LEVEL_9_1,
67     };
68     static const int levels_len = PL_ARRAY_SIZE(levels);
69 
70     int start = 0;
71     for (; start < levels_len; start++) {
72         if (levels[start] <= max_fl)
73             break;
74     }
75     int len = 0;
76     for (; start + len < levels_len; len++) {
77         if (levels[start + len] < min_fl)
78             break;
79     }
80     *out = &levels[start];
81     return len;
82 }
83 
is_null_luid(LUID luid)84 static bool is_null_luid(LUID luid)
85 {
86     return luid.LowPart == 0 && luid.HighPart == 0;
87 }
88 
get_adapter(pl_d3d11 d3d11,LUID adapter_luid)89 static IDXGIAdapter *get_adapter(pl_d3d11 d3d11, LUID adapter_luid)
90 {
91     struct d3d11_ctx *ctx = PL_PRIV(d3d11);
92     IDXGIFactory1 *factory = NULL;
93     IDXGIAdapter1 *adapter1 = NULL;
94     IDXGIAdapter *adapter = NULL;
95     HRESULT hr;
96 
97     if (!pCreateDXGIFactory1) {
98         PL_FATAL(ctx, "Failed to load dxgi.dll");
99         goto error;
100     }
101     pCreateDXGIFactory1(&IID_IDXGIFactory1, (void **) &factory);
102 
103     for (int i = 0;; i++) {
104         hr = IDXGIFactory1_EnumAdapters1(factory, i, &adapter1);
105         if (hr == DXGI_ERROR_NOT_FOUND)
106             break;
107         if (FAILED(hr)) {
108             PL_FATAL(ctx, "Failed to enumerate adapters");
109             goto error;
110         }
111 
112         DXGI_ADAPTER_DESC1 desc;
113         D3D(IDXGIAdapter1_GetDesc1(adapter1, &desc));
114         if (desc.AdapterLuid.LowPart == adapter_luid.LowPart &&
115             desc.AdapterLuid.HighPart == adapter_luid.HighPart)
116         {
117             break;
118         }
119 
120         SAFE_RELEASE(adapter1);
121     }
122     if (!adapter1) {
123         PL_FATAL(ctx, "Adapter with LUID %08lx%08lx not found",
124                  adapter_luid.HighPart, adapter_luid.LowPart);
125         goto error;
126     }
127 
128     D3D(IDXGIAdapter1_QueryInterface(adapter1, &IID_IDXGIAdapter,
129                                      (void **) &adapter));
130 
131 error:
132     SAFE_RELEASE(factory);
133     SAFE_RELEASE(adapter1);
134     return adapter;
135 }
136 
create_device(struct pl_d3d11 * d3d11,const struct pl_d3d11_params * params)137 static ID3D11Device *create_device(struct pl_d3d11 *d3d11,
138                                    const struct pl_d3d11_params *params)
139 {
140     struct d3d11_ctx *ctx = PL_PRIV(d3d11);
141     bool debug = params->debug;
142     bool warp = params->force_software;
143     int max_fl = params->max_feature_level;
144     int min_fl = params->min_feature_level;
145     ID3D11Device *dev = NULL;
146     IDXGIDevice1 *dxgi_dev = NULL;
147     IDXGIAdapter *adapter = NULL;
148     bool release_adapter = false;
149     HRESULT hr;
150 
151     d3d11_load();
152 
153     if (!pD3D11CreateDevice) {
154         PL_FATAL(ctx, "Failed to load d3d11.dll");
155         goto error;
156     }
157 
158     if (params->adapter) {
159         adapter = params->adapter;
160     } else if (!is_null_luid(params->adapter_luid)) {
161         adapter = get_adapter(d3d11, params->adapter_luid);
162         release_adapter = true;
163     }
164 
165     // Return here to retry creating the device
166     do {
167         // Use these default feature levels if they are not set
168         max_fl = PL_DEF(max_fl, D3D_FEATURE_LEVEL_12_1);
169         min_fl = PL_DEF(min_fl, D3D_FEATURE_LEVEL_9_1);
170 
171         // Get a list of feature levels from min_fl to max_fl
172         const D3D_FEATURE_LEVEL *levels;
173         int levels_len = get_feature_levels(max_fl, min_fl, &levels);
174         if (!levels_len) {
175             PL_FATAL(ctx, "No suitable Direct3D feature level found");
176             goto error;
177         }
178 
179         D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_UNKNOWN;
180         if (!adapter) {
181             if (warp) {
182                 type = D3D_DRIVER_TYPE_WARP;
183             } else {
184                 type = D3D_DRIVER_TYPE_HARDWARE;
185             }
186         }
187 
188         UINT flags = params->flags;
189         if (debug)
190             flags |= D3D11_CREATE_DEVICE_DEBUG;
191 
192         hr = pD3D11CreateDevice(adapter, type, NULL, flags, levels, levels_len,
193                                 D3D11_SDK_VERSION, &dev, NULL, NULL);
194         if (SUCCEEDED(hr))
195             break;
196 
197         if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING && debug) {
198             PL_DEBUG(ctx, "Debug layer not available, removing debug flag");
199             debug = false;
200             continue;
201         }
202 
203         // Trying to create a D3D_FEATURE_LEVEL_12_0 device on Windows 8.1 or
204         // below will not succeed. Try an 11_1 device.
205         if (max_fl >= D3D_FEATURE_LEVEL_12_0 &&
206             min_fl <= D3D_FEATURE_LEVEL_11_1)
207         {
208             PL_DEBUG(ctx, "Failed to create 12_0+ device, trying 11_1");
209             max_fl = D3D_FEATURE_LEVEL_11_1;
210             continue;
211         }
212 
213         // Trying to create a D3D_FEATURE_LEVEL_11_1 device on Windows 7
214         // without the platform update will not succeed. Try an 11_0 device.
215         if (max_fl >= D3D_FEATURE_LEVEL_11_1 &&
216             min_fl <= D3D_FEATURE_LEVEL_11_0)
217         {
218             PL_DEBUG(ctx, "Failed to create 11_1+ device, trying 11_0");
219             max_fl = D3D_FEATURE_LEVEL_11_0;
220             continue;
221         }
222 
223         // Retry with WARP if allowed
224         if (!adapter && !warp && params->allow_software) {
225             PL_DEBUG(ctx, "Failed to create hardware device, trying WARP");
226             warp = true;
227             max_fl = params->max_feature_level;
228             min_fl = params->min_feature_level;
229             continue;
230         }
231 
232         PL_FATAL(ctx, "Failed to create Direct3D 11 device: %s",
233                  pl_hresult_to_str(hr));
234         goto error;
235     } while (true);
236 
237     if (params->max_frame_latency) {
238         D3D(ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1,
239                                         (void **) &dxgi_dev));
240         IDXGIDevice1_SetMaximumFrameLatency(dxgi_dev, params->max_frame_latency);
241     }
242 
243     d3d11->software = warp;
244 
245 error:
246     if (release_adapter)
247         SAFE_RELEASE(adapter);
248     SAFE_RELEASE(dxgi_dev);
249     return dev;
250 }
251 
init_debug_layer(struct d3d11_ctx * ctx)252 static void init_debug_layer(struct d3d11_ctx *ctx)
253 {
254     D3D(ID3D11Device_QueryInterface(ctx->dev, &IID_ID3D11Debug,
255                                     (void **) &ctx->debug));
256     D3D(ID3D11Device_QueryInterface(ctx->dev, &IID_ID3D11InfoQueue,
257                                     (void **) &ctx->iqueue));
258 
259     // Filter some annoying messages
260     D3D11_MESSAGE_ID deny_ids[] = {
261         // This false-positive error occurs every time we Draw() with a shader
262         // that samples from a texture format that only supports point sampling.
263         // Since we already use CheckFormatSupport to know which formats can be
264         // linearly sampled from, we shouldn't ever bind a non-point sampler to
265         // a format that doesn't support it.
266         D3D11_MESSAGE_ID_DEVICE_DRAW_RESOURCE_FORMAT_SAMPLE_UNSUPPORTED,
267 
268         // These warnings can happen when a pl_timer is used too often before a
269         // blocking pl_swapchain_swap_buffers() or pl_gpu_finish(), overflowing
270         // its internal ring buffer and causing older query objects to be reused
271         // before their results are read. This is expected behavior, so ignore
272         // these warnings to prevent log spam.
273         D3D11_MESSAGE_ID_QUERY_BEGIN_ABANDONING_PREVIOUS_RESULTS,
274         D3D11_MESSAGE_ID_QUERY_END_ABANDONING_PREVIOUS_RESULTS,
275     };
276     D3D11_INFO_QUEUE_FILTER filter = {
277         .DenyList = {
278             .NumIDs = PL_ARRAY_SIZE(deny_ids),
279             .pIDList = deny_ids,
280         },
281     };
282     ID3D11InfoQueue_PushStorageFilter(ctx->iqueue, &filter);
283 
284 error:
285     return;
286 }
287 
pl_d3d11_destroy(pl_d3d11 * ptr)288 void pl_d3d11_destroy(pl_d3d11 *ptr)
289 {
290     pl_d3d11 d3d11 = *ptr;
291     if (!d3d11)
292         return;
293     struct d3d11_ctx *ctx = PL_PRIV(d3d11);
294 
295     pl_gpu_destroy(d3d11->gpu);
296 
297     SAFE_RELEASE(ctx->dev);
298     SAFE_RELEASE(ctx->dxgi_dev);
299 
300     if (ctx->debug) {
301         // Report any leaked objects
302         pl_d3d11_flush_message_queue(ctx, "After destroy");
303         ID3D11Debug_ReportLiveDeviceObjects(ctx->debug, D3D11_RLDO_DETAIL);
304         pl_d3d11_flush_message_queue(ctx, "After leak check");
305         ID3D11Debug_ReportLiveDeviceObjects(ctx->debug, D3D11_RLDO_SUMMARY);
306         pl_d3d11_flush_message_queue(ctx, "After leak summary");
307     }
308 
309     SAFE_RELEASE(ctx->debug);
310     SAFE_RELEASE(ctx->iqueue);
311 
312     pl_free_ptr((void **) ptr);
313 }
314 
pl_d3d11_create(pl_log log,const struct pl_d3d11_params * params)315 pl_d3d11 pl_d3d11_create(pl_log log, const struct pl_d3d11_params *params)
316 {
317     params = PL_DEF(params, &pl_d3d11_default_params);
318     IDXGIAdapter1 *adapter = NULL;
319     IDXGIAdapter2 *adapter2 = NULL;
320     bool success = false;
321     HRESULT hr;
322 
323     struct pl_d3d11 *d3d11 = pl_zalloc_obj(NULL, d3d11, struct d3d11_ctx);
324     struct d3d11_ctx *ctx = PL_PRIV(d3d11);
325     ctx->log = log;
326 
327     if (params->device) {
328         d3d11->device = params->device;
329         ID3D11Device_AddRef(d3d11->device);
330     } else {
331         d3d11->device = create_device(d3d11, params);
332         if (!d3d11->device)
333             goto error;
334     }
335     ctx->dev = d3d11->device;
336 
337     D3D(ID3D11Device_QueryInterface(d3d11->device, &IID_IDXGIDevice1,
338                                     (void **) &ctx->dxgi_dev));
339     D3D(IDXGIDevice1_GetParent(ctx->dxgi_dev, &IID_IDXGIAdapter1,
340                                (void **) &adapter));
341 
342     hr = IDXGIAdapter1_QueryInterface(adapter, &IID_IDXGIAdapter2,
343                                       (void **) &adapter2);
344     if (FAILED(hr))
345         adapter2 = NULL;
346 
347     if (adapter2) {
348         PL_INFO(ctx, "Using DXGI 1.2+");
349     } else {
350         PL_INFO(ctx, "Using DXGI 1.1");
351     }
352 
353     D3D_FEATURE_LEVEL fl = ID3D11Device_GetFeatureLevel(d3d11->device);
354     PL_INFO(ctx, "Using Direct3D 11 feature level %u_%u",
355             ((unsigned) fl) >> 12, (((unsigned) fl) >> 8) & 0xf);
356 
357     char *dev_name = NULL;
358     UINT vendor_id, device_id, revision, subsys_id;
359     LUID adapter_luid;
360     UINT flags;
361 
362     if (adapter2) {
363         // DXGI 1.2 IDXGIAdapter2::GetDesc2 is preferred over the DXGI 1.1
364         // version because it reports the real adapter information when using
365         // feature level 9 hardware
366         DXGI_ADAPTER_DESC2 desc;
367         D3D(IDXGIAdapter2_GetDesc2(adapter2, &desc));
368 
369         dev_name = pl_to_utf8(NULL, desc.Description);
370         vendor_id = desc.VendorId;
371         device_id = desc.DeviceId;
372         revision = desc.Revision;
373         subsys_id = desc.SubSysId;
374         adapter_luid = desc.AdapterLuid;
375         flags = desc.Flags;
376     } else {
377         DXGI_ADAPTER_DESC1 desc;
378         D3D(IDXGIAdapter1_GetDesc1(adapter, &desc));
379 
380         dev_name = pl_to_utf8(NULL, desc.Description);
381         vendor_id = desc.VendorId;
382         device_id = desc.DeviceId;
383         revision = desc.Revision;
384         subsys_id = desc.SubSysId;
385         adapter_luid = desc.AdapterLuid;
386         flags = desc.Flags;
387     }
388 
389     PL_INFO(ctx, "Direct3D 11 device properties:");
390     PL_INFO(ctx, "    Device Name: %s", dev_name);
391     PL_INFO(ctx, "    Device ID: %04x:%04x (rev %02x)",
392             vendor_id, device_id, revision);
393     PL_INFO(ctx, "    Subsystem ID: %04x:%04x",
394             LOWORD(subsys_id), HIWORD(subsys_id));
395     PL_INFO(ctx, "    LUID: %08lx%08lx",
396             adapter_luid.HighPart, adapter_luid.LowPart);
397     pl_free(dev_name);
398 
399     LARGE_INTEGER version;
400     hr = IDXGIAdapter1_CheckInterfaceSupport(adapter, &IID_IDXGIDevice, &version);
401     if (SUCCEEDED(hr)) {
402         PL_INFO(ctx, "    Driver version: %u.%u.%u.%u",
403                 HIWORD(version.HighPart), LOWORD(version.HighPart),
404                 HIWORD(version.LowPart), LOWORD(version.LowPart));
405     }
406 
407     // Note: DXGI_ADAPTER_FLAG_SOFTWARE doesn't exist before Windows 8, but we
408     // also set d3d11->software in create_device if we pick WARP ourselves
409     if (flags & DXGI_ADAPTER_FLAG_SOFTWARE)
410         d3d11->software = true;
411 
412     // If the primary display adapter is a software adapter, the
413     // DXGI_ADAPTER_FLAG_SOFTWARE flag won't be set, but the device IDs should
414     // still match the Microsoft Basic Render Driver
415     if (vendor_id == 0x1414 && device_id == 0x8c)
416         d3d11->software = true;
417 
418     if (d3d11->software) {
419         bool external_adapter = params->device || params->adapter ||
420                                 !is_null_luid(params->adapter_luid);
421 
422         // The allow_software flag only applies if the API user didn't manually
423         // specify an adapter or a device
424         if (!params->allow_software && !external_adapter) {
425             // If we got this far with allow_software set, the primary adapter
426             // must be a software adapter
427             PL_ERR(ctx, "Primary adapter is a software adapter");
428             goto error;
429         }
430 
431         // If a software adapter was manually specified, don't show a warning
432         enum pl_log_level level = PL_LOG_WARN;
433         if (external_adapter || params->force_software)
434             level = PL_LOG_INFO;
435 
436         PL_MSG(ctx, level, "Using a software adapter");
437     }
438 
439     // Init debug layer
440     if (ID3D11Device_GetCreationFlags(d3d11->device) & D3D11_CREATE_DEVICE_DEBUG)
441         init_debug_layer(ctx);
442 
443     d3d11->gpu = pl_gpu_create_d3d11(ctx);
444     if (!d3d11->gpu)
445         goto error;
446 
447     success = true;
448 error:
449     if (!success) {
450         PL_FATAL(ctx, "Failed initializing Direct3D 11 device");
451         pl_d3d11_destroy((const struct pl_d3d11 **) &d3d11);
452     }
453     SAFE_RELEASE(adapter);
454     SAFE_RELEASE(adapter2);
455     return d3d11;
456 }
457