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