diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:38:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:38:23 +0000 |
commit | ff6e3c025658a5fa1affd094f220b623e7e1b24b (patch) | |
tree | 9faab72d69c92d24e349d184f5869b9796f17e0c /src/d3d11/context.c | |
parent | Initial commit. (diff) | |
download | libplacebo-ff6e3c025658a5fa1affd094f220b623e7e1b24b.tar.xz libplacebo-ff6e3c025658a5fa1affd094f220b623e7e1b24b.zip |
Adding upstream version 6.338.2.upstream/6.338.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/d3d11/context.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/src/d3d11/context.c b/src/d3d11/context.c new file mode 100644 index 0000000..e0ba90f --- /dev/null +++ b/src/d3d11/context.c @@ -0,0 +1,488 @@ +/* + * This file is part of libplacebo. + * + * libplacebo is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * libplacebo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "gpu.h" + +// Windows 8 enum value, not present in mingw-w64 v7 +#define DXGI_ADAPTER_FLAG_SOFTWARE (2) + +const struct pl_d3d11_params pl_d3d11_default_params = { PL_D3D11_DEFAULTS }; + +static INIT_ONCE d3d11_once = INIT_ONCE_STATIC_INIT; +static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL; +static __typeof__(&CreateDXGIFactory1) pCreateDXGIFactory1 = NULL; +#ifdef PL_HAVE_DXGI_DEBUG +static __typeof__(&DXGIGetDebugInterface) pDXGIGetDebugInterface = NULL; +#endif + +static void d3d11_load(void) +{ + BOOL bPending = FALSE; + InitOnceBeginInitialize(&d3d11_once, 0, &bPending, NULL); + + if (bPending) + { + HMODULE d3d11 = LoadLibraryW(L"d3d11.dll"); + if (d3d11) { + pD3D11CreateDevice = (void *) + GetProcAddress(d3d11, "D3D11CreateDevice"); + } + + HMODULE dxgi = LoadLibraryW(L"dxgi.dll"); + if (dxgi) { + pCreateDXGIFactory1 = (void *) + GetProcAddress(dxgi, "CreateDXGIFactory1"); + } + +#ifdef PL_HAVE_DXGI_DEBUG + HMODULE dxgi_debug = LoadLibraryW(L"dxgidebug.dll"); + if (dxgi_debug) { + pDXGIGetDebugInterface = (void *) + GetProcAddress(dxgi_debug, "DXGIGetDebugInterface"); + } +#endif + } + + InitOnceComplete(&d3d11_once, 0, NULL); +} + +// Get a const array of D3D_FEATURE_LEVELs from max_fl to min_fl (inclusive) +static int get_feature_levels(int max_fl, int min_fl, + const D3D_FEATURE_LEVEL **out) +{ + static const D3D_FEATURE_LEVEL levels[] = { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + }; + static const int levels_len = PL_ARRAY_SIZE(levels); + + int start = 0; + for (; start < levels_len; start++) { + if (levels[start] <= max_fl) + break; + } + int len = 0; + for (; start + len < levels_len; len++) { + if (levels[start + len] < min_fl) + break; + } + *out = &levels[start]; + return len; +} + +static bool is_null_luid(LUID luid) +{ + return luid.LowPart == 0 && luid.HighPart == 0; +} + +static IDXGIAdapter *get_adapter(pl_d3d11 d3d11, LUID adapter_luid) +{ + struct d3d11_ctx *ctx = PL_PRIV(d3d11); + IDXGIFactory1 *factory = NULL; + IDXGIAdapter1 *adapter1 = NULL; + IDXGIAdapter *adapter = NULL; + HRESULT hr; + + if (!pCreateDXGIFactory1) { + PL_FATAL(ctx, "Failed to load dxgi.dll"); + goto error; + } + pCreateDXGIFactory1(&IID_IDXGIFactory1, (void **) &factory); + + for (int i = 0;; i++) { + hr = IDXGIFactory1_EnumAdapters1(factory, i, &adapter1); + if (hr == DXGI_ERROR_NOT_FOUND) + break; + if (FAILED(hr)) { + PL_FATAL(ctx, "Failed to enumerate adapters"); + goto error; + } + + DXGI_ADAPTER_DESC1 desc; + D3D(IDXGIAdapter1_GetDesc1(adapter1, &desc)); + if (desc.AdapterLuid.LowPart == adapter_luid.LowPart && + desc.AdapterLuid.HighPart == adapter_luid.HighPart) + { + break; + } + + SAFE_RELEASE(adapter1); + } + if (!adapter1) { + PL_FATAL(ctx, "Adapter with LUID %08lx%08lx not found", + adapter_luid.HighPart, adapter_luid.LowPart); + goto error; + } + + D3D(IDXGIAdapter1_QueryInterface(adapter1, &IID_IDXGIAdapter, + (void **) &adapter)); + +error: + SAFE_RELEASE(factory); + SAFE_RELEASE(adapter1); + return adapter; +} + +static bool has_sdk_layers(void) +{ + // This will fail if the SDK layers aren't installed + return SUCCEEDED(pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_NULL, NULL, + D3D11_CREATE_DEVICE_DEBUG, NULL, 0, D3D11_SDK_VERSION, NULL, NULL, + NULL)); +} + +static ID3D11Device *create_device(struct pl_d3d11_t *d3d11, + const struct pl_d3d11_params *params) +{ + struct d3d11_ctx *ctx = PL_PRIV(d3d11); + bool debug = params->debug; + bool warp = params->force_software; + int max_fl = params->max_feature_level; + int min_fl = params->min_feature_level; + ID3D11Device *dev = NULL; + IDXGIDevice1 *dxgi_dev = NULL; + IDXGIAdapter *adapter = NULL; + bool release_adapter = false; + HRESULT hr; + + d3d11_load(); + + if (!pD3D11CreateDevice) { + PL_FATAL(ctx, "Failed to load d3d11.dll"); + goto error; + } + + if (params->adapter) { + adapter = params->adapter; + } else if (!is_null_luid(params->adapter_luid)) { + adapter = get_adapter(d3d11, params->adapter_luid); + release_adapter = true; + } + + if (debug && !has_sdk_layers()) { + PL_INFO(ctx, "Debug layer not available, removing debug flag"); + debug = false; + } + + // Return here to retry creating the device + do { + // Use these default feature levels if they are not set + max_fl = PL_DEF(max_fl, D3D_FEATURE_LEVEL_12_1); + min_fl = PL_DEF(min_fl, D3D_FEATURE_LEVEL_9_1); + + // Get a list of feature levels from min_fl to max_fl + const D3D_FEATURE_LEVEL *levels; + int levels_len = get_feature_levels(max_fl, min_fl, &levels); + if (!levels_len) { + PL_FATAL(ctx, "No suitable Direct3D feature level found"); + goto error; + } + + D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_UNKNOWN; + if (!adapter) { + if (warp) { + type = D3D_DRIVER_TYPE_WARP; + } else { + type = D3D_DRIVER_TYPE_HARDWARE; + } + } + + UINT flags = params->flags; + if (debug) + flags |= D3D11_CREATE_DEVICE_DEBUG; + + hr = pD3D11CreateDevice(adapter, type, NULL, flags, levels, levels_len, + D3D11_SDK_VERSION, &dev, NULL, NULL); + if (SUCCEEDED(hr)) + break; + + pl_d3d11_after_error(ctx, hr); + + // Trying to create a D3D_FEATURE_LEVEL_12_0 device on Windows 8.1 or + // below will not succeed. Try an 11_1 device. + if (hr == E_INVALIDARG && max_fl >= D3D_FEATURE_LEVEL_12_0 && + min_fl <= D3D_FEATURE_LEVEL_11_1) { + PL_DEBUG(ctx, "Failed to create 12_0+ device, trying 11_1"); + max_fl = D3D_FEATURE_LEVEL_11_1; + continue; + } + + // Trying to create a D3D_FEATURE_LEVEL_11_1 device on Windows 7 + // without the platform update will not succeed. Try an 11_0 device. + if (hr == E_INVALIDARG && max_fl >= D3D_FEATURE_LEVEL_11_1 && + min_fl <= D3D_FEATURE_LEVEL_11_0) { + PL_DEBUG(ctx, "Failed to create 11_1+ device, trying 11_0"); + max_fl = D3D_FEATURE_LEVEL_11_0; + continue; + } + + // Retry with WARP if allowed + if (!adapter && !warp && params->allow_software) { + PL_DEBUG(ctx, "Failed to create hardware device, trying WARP: %s", + pl_hresult_to_str(hr)); + warp = true; + max_fl = params->max_feature_level; + min_fl = params->min_feature_level; + continue; + } + + PL_FATAL(ctx, "Failed to create Direct3D 11 device: %s", + pl_hresult_to_str(hr)); + goto error; + } while (true); + + if (params->max_frame_latency) { + D3D(ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1, + (void **) &dxgi_dev)); + IDXGIDevice1_SetMaximumFrameLatency(dxgi_dev, params->max_frame_latency); + } + + d3d11->software = warp; + +error: + if (release_adapter) + SAFE_RELEASE(adapter); + SAFE_RELEASE(dxgi_dev); + return dev; +} + +static void init_debug_layer(struct d3d11_ctx *ctx, bool leak_check) +{ +#ifdef PL_HAVE_DXGI_DEBUG + if (!pDXGIGetDebugInterface) + d3d11_load(); + + if (!pDXGIGetDebugInterface) + goto error; + + D3D(pDXGIGetDebugInterface(&IID_IDXGIInfoQueue, (void **) &ctx->iqueue)); + + // Push empty filter to get everything + IDXGIInfoQueue_PushStorageFilter(ctx->iqueue, DXGI_DEBUG_ALL, + &(DXGI_INFO_QUEUE_FILTER){0}); + + // Filter some annoying D3D11 messages + DXGI_INFO_QUEUE_MESSAGE_ID deny_ids[] = { + // This false-positive error occurs every time we Draw() with a shader + // that samples from a texture format that only supports point sampling. + // Since we already use CheckFormatSupport to know which formats can be + // linearly sampled from, we shouldn't ever bind a non-point sampler to + // a format that doesn't support it. + D3D11_MESSAGE_ID_DEVICE_DRAW_RESOURCE_FORMAT_SAMPLE_UNSUPPORTED, + }; + DXGI_INFO_QUEUE_FILTER filter = { + .DenyList = { + .NumIDs = PL_ARRAY_SIZE(deny_ids), + .pIDList = deny_ids, + }, + }; + IDXGIInfoQueue_PushStorageFilter(ctx->iqueue, DXGI_DEBUG_D3D11, &filter); + + IDXGIInfoQueue_SetMessageCountLimit(ctx->iqueue, DXGI_DEBUG_D3D11, -1); + IDXGIInfoQueue_SetMessageCountLimit(ctx->iqueue, DXGI_DEBUG_DXGI, -1); + + if (leak_check) + D3D(pDXGIGetDebugInterface(&IID_IDXGIDebug, (void **) &ctx->debug)); + +error: + return; +#endif +} + +void pl_d3d11_destroy(pl_d3d11 *ptr) +{ + pl_d3d11 d3d11 = *ptr; + if (!d3d11) + return; + struct d3d11_ctx *ctx = PL_PRIV(d3d11); + + pl_gpu_destroy(d3d11->gpu); + + SAFE_RELEASE(ctx->dev); + SAFE_RELEASE(ctx->dxgi_dev); + +#ifdef PL_HAVE_DXGI_DEBUG + if (ctx->debug) { + // Report any leaked objects + pl_d3d11_flush_message_queue(ctx, "After destroy"); + IDXGIDebug_ReportLiveObjects(ctx->debug, DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_DETAIL); + pl_d3d11_flush_message_queue(ctx, "After leak check"); + IDXGIDebug_ReportLiveObjects(ctx->debug, DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_SUMMARY); + pl_d3d11_flush_message_queue(ctx, "After leak summary"); + } + + SAFE_RELEASE(ctx->debug); + SAFE_RELEASE(ctx->iqueue); +#endif + + pl_free_ptr((void **) ptr); +} + +pl_d3d11 pl_d3d11_create(pl_log log, const struct pl_d3d11_params *params) +{ + params = PL_DEF(params, &pl_d3d11_default_params); + IDXGIAdapter1 *adapter = NULL; + IDXGIAdapter2 *adapter2 = NULL; + bool success = false; + HRESULT hr; + + struct pl_d3d11_t *d3d11 = pl_zalloc_obj(NULL, d3d11, struct d3d11_ctx); + struct d3d11_ctx *ctx = PL_PRIV(d3d11); + ctx->log = log; + ctx->d3d11 = d3d11; + + if (params->device) { + d3d11->device = params->device; + ID3D11Device_AddRef(d3d11->device); + } else { + d3d11->device = create_device(d3d11, params); + if (!d3d11->device) + goto error; + } + ctx->dev = d3d11->device; + + if (params->debug || + ID3D11Device_GetCreationFlags(d3d11->device) & D3D11_CREATE_DEVICE_DEBUG) + { + // Do not report live object on pl_d3d11_destroy if device was created + // externally, it makes no sense as there will be a lot of things alive. + init_debug_layer(ctx, !params->device); + } + + D3D(ID3D11Device_QueryInterface(d3d11->device, &IID_IDXGIDevice1, + (void **) &ctx->dxgi_dev)); + D3D(IDXGIDevice1_GetParent(ctx->dxgi_dev, &IID_IDXGIAdapter1, + (void **) &adapter)); + + hr = IDXGIAdapter1_QueryInterface(adapter, &IID_IDXGIAdapter2, + (void **) &adapter2); + if (FAILED(hr)) + adapter2 = NULL; + + if (adapter2) { + PL_INFO(ctx, "Using DXGI 1.2+"); + } else { + PL_INFO(ctx, "Using DXGI 1.1"); + } + + D3D_FEATURE_LEVEL fl = ID3D11Device_GetFeatureLevel(d3d11->device); + PL_INFO(ctx, "Using Direct3D 11 feature level %u_%u", + ((unsigned) fl) >> 12, (((unsigned) fl) >> 8) & 0xf); + + char *dev_name = NULL; + UINT vendor_id, device_id, revision, subsys_id; + LUID adapter_luid; + UINT flags; + + if (adapter2) { + // DXGI 1.2 IDXGIAdapter2::GetDesc2 is preferred over the DXGI 1.1 + // version because it reports the real adapter information when using + // feature level 9 hardware + DXGI_ADAPTER_DESC2 desc; + D3D(IDXGIAdapter2_GetDesc2(adapter2, &desc)); + + dev_name = pl_to_utf8(NULL, desc.Description); + vendor_id = desc.VendorId; + device_id = desc.DeviceId; + revision = desc.Revision; + subsys_id = desc.SubSysId; + adapter_luid = desc.AdapterLuid; + flags = desc.Flags; + } else { + DXGI_ADAPTER_DESC1 desc; + D3D(IDXGIAdapter1_GetDesc1(adapter, &desc)); + + dev_name = pl_to_utf8(NULL, desc.Description); + vendor_id = desc.VendorId; + device_id = desc.DeviceId; + revision = desc.Revision; + subsys_id = desc.SubSysId; + adapter_luid = desc.AdapterLuid; + flags = desc.Flags; + } + + PL_INFO(ctx, "Direct3D 11 device properties:"); + PL_INFO(ctx, " Device Name: %s", dev_name); + PL_INFO(ctx, " Device ID: %04x:%04x (rev %02x)", + vendor_id, device_id, revision); + PL_INFO(ctx, " Subsystem ID: %04x:%04x", + LOWORD(subsys_id), HIWORD(subsys_id)); + PL_INFO(ctx, " LUID: %08lx%08lx", + adapter_luid.HighPart, adapter_luid.LowPart); + pl_free(dev_name); + + LARGE_INTEGER version; + hr = IDXGIAdapter1_CheckInterfaceSupport(adapter, &IID_IDXGIDevice, &version); + if (SUCCEEDED(hr)) { + PL_INFO(ctx, " Driver version: %u.%u.%u.%u", + HIWORD(version.HighPart), LOWORD(version.HighPart), + HIWORD(version.LowPart), LOWORD(version.LowPart)); + } + + // Note: DXGI_ADAPTER_FLAG_SOFTWARE doesn't exist before Windows 8, but we + // also set d3d11->software in create_device if we pick WARP ourselves + if (flags & DXGI_ADAPTER_FLAG_SOFTWARE) + d3d11->software = true; + + // If the primary display adapter is a software adapter, the + // DXGI_ADAPTER_FLAG_SOFTWARE flag won't be set, but the device IDs should + // still match the Microsoft Basic Render Driver + if (vendor_id == 0x1414 && device_id == 0x8c) + d3d11->software = true; + + if (d3d11->software) { + bool external_adapter = params->device || params->adapter || + !is_null_luid(params->adapter_luid); + + // The allow_software flag only applies if the API user didn't manually + // specify an adapter or a device + if (!params->allow_software && !external_adapter) { + // If we got this far with allow_software set, the primary adapter + // must be a software adapter + PL_ERR(ctx, "Primary adapter is a software adapter"); + goto error; + } + + // If a software adapter was manually specified, don't show a warning + enum pl_log_level level = PL_LOG_WARN; + if (external_adapter || params->force_software) + level = PL_LOG_INFO; + + PL_MSG(ctx, level, "Using a software adapter"); + } + + d3d11->gpu = pl_gpu_create_d3d11(ctx); + if (!d3d11->gpu) + goto error; + + success = true; +error: + if (!success) { + PL_FATAL(ctx, "Failed initializing Direct3D 11 device"); + pl_d3d11_destroy((pl_d3d11 *) &d3d11); + } + SAFE_RELEASE(adapter); + SAFE_RELEASE(adapter2); + return d3d11; +} |