/* * This file is part of mpv. * * mpv 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. * * mpv 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 mpv. If not, see . */ #include #include #include #include #include "common/common.h" #include "common/msg.h" #include "misc/bstr.h" #include "osdep/io.h" #include "osdep/threads.h" #include "osdep/windows_utils.h" #include "d3d11_helpers.h" // Windows 8 enum value, not present in mingw-w64 headers #define DXGI_ADAPTER_FLAG_SOFTWARE (2) typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); typedef HRESULT(WINAPI *PFN_DXGI_GET_DEBUG_INTERFACE)(REFIID riid, void **ppDebug); static mp_once d3d11_once = MP_STATIC_ONCE_INITIALIZER; static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL; static PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory1 = NULL; static PFN_DXGI_GET_DEBUG_INTERFACE pDXGIGetDebugInterface = NULL; static void d3d11_load(void) { HMODULE d3d11 = LoadLibraryW(L"d3d11.dll"); HMODULE dxgilib = LoadLibraryW(L"dxgi.dll"); HMODULE dxgidebuglib = LoadLibraryW(L"dxgidebug.dll"); if (!d3d11 || !dxgilib) return; pD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3d11, "D3D11CreateDevice"); pCreateDXGIFactory1 = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory1"); if (dxgidebuglib) { pDXGIGetDebugInterface = (PFN_DXGI_GET_DEBUG_INTERFACE) GetProcAddress(dxgidebuglib, "DXGIGetDebugInterface"); } } static bool load_d3d11_functions(struct mp_log *log) { mp_exec_once(&d3d11_once, d3d11_load); if (!pD3D11CreateDevice || !pCreateDXGIFactory1) { mp_fatal(log, "Failed to load base d3d11 functionality: " "CreateDevice: %s, CreateDXGIFactory1: %s\n", pD3D11CreateDevice ? "success" : "failure", pCreateDXGIFactory1 ? "success": "failure"); return false; } return true; } #define D3D11_DXGI_ENUM(prefix, define) { case prefix ## define: return #define; } static const char *d3d11_get_format_name(DXGI_FORMAT fmt) { switch (fmt) { D3D11_DXGI_ENUM(DXGI_FORMAT_, UNKNOWN); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32A32_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32A32_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32A32_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32A32_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32B32_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16B16A16_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G32_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32G8X24_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, D32_FLOAT_S8X24_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32_FLOAT_X8X24_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, X32_TYPELESS_G8X24_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R10G10B10A2_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R10G10B10A2_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R10G10B10A2_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R11G11B10_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8B8A8_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16G16_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, D32_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R32_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R24G8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, D24_UNORM_S8_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R24_UNORM_X8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, X24_TYPELESS_G8_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_FLOAT); D3D11_DXGI_ENUM(DXGI_FORMAT_, D16_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R16_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8_UINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8_SINT); D3D11_DXGI_ENUM(DXGI_FORMAT_, A8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R1_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R9G9B9E5_SHAREDEXP); D3D11_DXGI_ENUM(DXGI_FORMAT_, R8G8_B8G8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, G8R8_G8B8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC1_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC1_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC1_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC2_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC2_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC2_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC3_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC3_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC3_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC4_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC4_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC4_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC5_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC5_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC5_SNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, B5G6R5_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, B5G5R5A1_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8A8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8X8_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, R10G10B10_XR_BIAS_A2_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8A8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8A8_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8X8_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, B8G8R8X8_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC6H_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC6H_UF16); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC6H_SF16); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC7_TYPELESS); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC7_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, BC7_UNORM_SRGB); D3D11_DXGI_ENUM(DXGI_FORMAT_, AYUV); D3D11_DXGI_ENUM(DXGI_FORMAT_, Y410); D3D11_DXGI_ENUM(DXGI_FORMAT_, Y416); D3D11_DXGI_ENUM(DXGI_FORMAT_, NV12); D3D11_DXGI_ENUM(DXGI_FORMAT_, P010); D3D11_DXGI_ENUM(DXGI_FORMAT_, P016); D3D11_DXGI_ENUM(DXGI_FORMAT_, 420_OPAQUE); D3D11_DXGI_ENUM(DXGI_FORMAT_, YUY2); D3D11_DXGI_ENUM(DXGI_FORMAT_, Y210); D3D11_DXGI_ENUM(DXGI_FORMAT_, Y216); D3D11_DXGI_ENUM(DXGI_FORMAT_, NV11); D3D11_DXGI_ENUM(DXGI_FORMAT_, AI44); D3D11_DXGI_ENUM(DXGI_FORMAT_, IA44); D3D11_DXGI_ENUM(DXGI_FORMAT_, P8); D3D11_DXGI_ENUM(DXGI_FORMAT_, A8P8); D3D11_DXGI_ENUM(DXGI_FORMAT_, B4G4R4A4_UNORM); D3D11_DXGI_ENUM(DXGI_FORMAT_, P208); D3D11_DXGI_ENUM(DXGI_FORMAT_, V208); D3D11_DXGI_ENUM(DXGI_FORMAT_, V408); D3D11_DXGI_ENUM(DXGI_FORMAT_, FORCE_UINT); default: return ""; } } static const char *d3d11_get_csp_name(DXGI_COLOR_SPACE_TYPE csp) { switch (csp) { D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_FULL_G22_NONE_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_FULL_G10_NONE_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_STUDIO_G22_NONE_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_STUDIO_G22_NONE_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RESERVED); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_FULL_G22_NONE_P709_X601); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G22_LEFT_P601); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_FULL_G22_LEFT_P601); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G22_LEFT_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_FULL_G22_LEFT_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G22_LEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_FULL_G22_LEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_FULL_G2084_NONE_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G2084_LEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_STUDIO_G2084_NONE_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G22_TOPLEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G2084_TOPLEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_FULL_G22_NONE_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_GHLG_TOPLEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_FULL_GHLG_TOPLEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_STUDIO_G24_NONE_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, RGB_STUDIO_G24_NONE_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G24_LEFT_P709); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G24_LEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, YCBCR_STUDIO_G24_TOPLEFT_P2020); D3D11_DXGI_ENUM(DXGI_COLOR_SPACE_, CUSTOM); default: return ""; } } static bool d3d11_get_mp_csp(DXGI_COLOR_SPACE_TYPE csp, struct pl_color_space *pl_color_system) { if (!pl_color_system) return false; // Colorspaces utilizing gamma 2.2 (G22) are set to // AUTO as that keeps the current default flow regarding // SDR transfer function handling. // (no adjustment is done unless the user has a CMS LUT). // // Additionally, only set primary information with colorspaces // utilizing non-709 primaries to keep the current behavior // regarding not doing conversion from BT.601 to BT.709. switch (csp) { case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709: *pl_color_system = (struct pl_color_space){ .transfer = PL_COLOR_TRC_UNKNOWN, .primaries = PL_COLOR_PRIM_UNKNOWN, }; break; case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709: *pl_color_system = (struct pl_color_space) { .transfer = PL_COLOR_TRC_LINEAR, .primaries = PL_COLOR_PRIM_UNKNOWN, }; break; case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020: *pl_color_system = (struct pl_color_space) { .transfer = PL_COLOR_TRC_PQ, .primaries = PL_COLOR_PRIM_BT_2020, }; break; case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020: *pl_color_system = (struct pl_color_space) { .transfer = PL_COLOR_TRC_UNKNOWN, .primaries = PL_COLOR_PRIM_BT_2020, }; break; default: return false; } return true; } static bool query_output_format_and_colorspace(struct mp_log *log, IDXGISwapChain *swapchain, DXGI_FORMAT *out_fmt, DXGI_COLOR_SPACE_TYPE *out_cspace) { DXGI_OUTPUT_DESC1 desc = { 0 }; char *monitor_name = NULL; bool success = false; if (!out_fmt || !out_cspace) return false; if (!mp_get_dxgi_output_desc(swapchain, &desc)) { mp_err(log, "Failed to query swap chain's output information\n"); goto done; } monitor_name = mp_to_utf8(NULL, desc.DeviceName); mp_verbose(log, "Queried output: %s, %ldx%ld @ %d bits, colorspace: %s (%d)\n", monitor_name, desc.DesktopCoordinates.right - desc.DesktopCoordinates.left, desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top, desc.BitsPerColor, d3d11_get_csp_name(desc.ColorSpace), desc.ColorSpace); *out_cspace = desc.ColorSpace; // limit ourselves to the 8bit and 10bit formats for now. // while the 16bit float format would be preferable as something // to default to, it seems to be hard-coded to linear transfer // in windowed mode, and follows configured colorspace in full screen. *out_fmt = desc.BitsPerColor > 8 ? DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM; success = true; done: talloc_free(monitor_name); return success; } // 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 = MP_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; } IDXGIAdapter1 *mp_get_dxgi_adapter(struct mp_log *log, bstr requested_adapter_name, bstr *listing) { HRESULT hr = S_OK; IDXGIFactory1 *factory; IDXGIAdapter1 *picked_adapter = NULL; hr = pCreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&factory); if (FAILED(hr)) { mp_fatal(log, "Failed to create a DXGI factory: %s\n", mp_HRESULT_to_str(hr)); return NULL; } for (unsigned int adapter_num = 0; hr != DXGI_ERROR_NOT_FOUND; adapter_num++) { IDXGIAdapter1 *adapter = NULL; DXGI_ADAPTER_DESC1 desc = { 0 }; char *adapter_description = NULL; hr = IDXGIFactory1_EnumAdapters1(factory, adapter_num, &adapter); if (FAILED(hr)) { if (hr != DXGI_ERROR_NOT_FOUND) { mp_fatal(log, "Failed to enumerate at adapter %u\n", adapter_num); } continue; } if (FAILED(IDXGIAdapter1_GetDesc1(adapter, &desc))) { mp_fatal(log, "Failed to get adapter description when listing at adapter %u\n", adapter_num); continue; } adapter_description = mp_to_utf8(NULL, desc.Description); if (listing) { bstr_xappend_asprintf(NULL, listing, "Adapter %u: vendor: %u, description: %s\n", adapter_num, desc.VendorId, adapter_description); } if (requested_adapter_name.len && bstr_case_startswith(bstr0(adapter_description), requested_adapter_name)) { picked_adapter = adapter; } talloc_free(adapter_description); if (picked_adapter) { break; } SAFE_RELEASE(adapter); } SAFE_RELEASE(factory); return picked_adapter; } int mp_dxgi_validate_adapter(struct mp_log *log, const struct m_option *opt, struct bstr name, const char **value) { struct bstr param = bstr0(*value); bool help = bstr_equals0(param, "help"); bool adapter_matched = false; struct bstr listing = { 0 }; if (bstr_equals0(param, "")) { return 0; } adapter_matched = mp_dxgi_list_or_verify_adapters(log, help ? bstr0(NULL) : param, help ? &listing : NULL); if (help) { mp_info(log, "Available DXGI adapters:\n%.*s", BSTR_P(listing)); talloc_free(listing.start); return M_OPT_EXIT; } if (!adapter_matched) { mp_err(log, "No adapter matching '%.*s'!\n", BSTR_P(param)); } return adapter_matched ? 0 : M_OPT_INVALID; } static HRESULT create_device(struct mp_log *log, IDXGIAdapter1 *adapter, bool warp, bool debug, int max_fl, int min_fl, ID3D11Device **dev) { const D3D_FEATURE_LEVEL *levels; int levels_len = get_feature_levels(max_fl, min_fl, &levels); if (!levels_len) { mp_fatal(log, "No suitable Direct3D feature level found\n"); return E_FAIL; } D3D_DRIVER_TYPE type = warp ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_HARDWARE; UINT flags = debug ? D3D11_CREATE_DEVICE_DEBUG : 0; return pD3D11CreateDevice((IDXGIAdapter *)adapter, adapter ? D3D_DRIVER_TYPE_UNKNOWN : type, NULL, flags, levels, levels_len, D3D11_SDK_VERSION, dev, NULL, NULL); } bool mp_dxgi_list_or_verify_adapters(struct mp_log *log, bstr adapter_name, bstr *listing) { IDXGIAdapter1 *picked_adapter = NULL; if (!load_d3d11_functions(log)) { return false; } if ((picked_adapter = mp_get_dxgi_adapter(log, adapter_name, listing))) { SAFE_RELEASE(picked_adapter); return true; } return false; } // Create a Direct3D 11 device for rendering and presentation. This is meant to // reduce boilerplate in backends that D3D11, while also making sure they share // the same device creation logic and log the same information. bool mp_d3d11_create_present_device(struct mp_log *log, struct d3d11_device_opts *opts, ID3D11Device **dev_out) { bool debug = opts->debug; bool warp = opts->force_warp; int max_fl = opts->max_feature_level; int min_fl = opts->min_feature_level; // Normalize nullptr and an empty string to nullptr to simplify handling. char *adapter_name = (opts->adapter_name && *(opts->adapter_name)) ? opts->adapter_name : NULL; ID3D11Device *dev = NULL; IDXGIDevice1 *dxgi_dev = NULL; IDXGIAdapter1 *adapter = NULL; bool success = false; HRESULT hr; if (!load_d3d11_functions(log)) { goto done; } adapter = mp_get_dxgi_adapter(log, bstr0(adapter_name), NULL); if (adapter_name && !adapter) { mp_warn(log, "Adapter matching '%s' was not found in the system! " "Will fall back to the default adapter.\n", adapter_name); } // Return here to retry creating the device do { // Use these default feature levels if they are not set max_fl = max_fl ? max_fl : D3D_FEATURE_LEVEL_11_0; min_fl = min_fl ? min_fl : D3D_FEATURE_LEVEL_9_1; hr = create_device(log, adapter, warp, debug, max_fl, min_fl, &dev); // Retry without debug, if SDK is not available if (debug && hr == DXGI_ERROR_SDK_COMPONENT_MISSING) { mp_warn(log, "gpu-debug disabled due to error: %s\n", mp_HRESULT_to_str(hr)); debug = false; continue; } if (SUCCEEDED(hr)) break; // 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 (max_fl >= D3D_FEATURE_LEVEL_12_0 && min_fl <= D3D_FEATURE_LEVEL_11_1) { mp_dbg(log, "Failed to create 12_0+ device, trying 11_1\n"); 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 (max_fl >= D3D_FEATURE_LEVEL_11_1 && min_fl <= D3D_FEATURE_LEVEL_11_0) { mp_dbg(log, "Failed to create 11_1+ device, trying 11_0\n"); max_fl = D3D_FEATURE_LEVEL_11_0; continue; } // Retry with WARP if allowed if (!warp && opts->allow_warp) { mp_dbg(log, "Failed to create hardware device, trying WARP\n"); warp = true; max_fl = opts->max_feature_level; min_fl = opts->min_feature_level; continue; } mp_fatal(log, "Failed to create Direct3D 11 device: %s\n", mp_HRESULT_to_str(hr)); goto done; } while (true); // if we picked an adapter, release it here - we're taking another // from the device. SAFE_RELEASE(adapter); hr = ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1, (void**)&dxgi_dev); if (FAILED(hr)) { mp_fatal(log, "Failed to get DXGI device\n"); goto done; } hr = IDXGIDevice1_GetParent(dxgi_dev, &IID_IDXGIAdapter1, (void**)&adapter); if (FAILED(hr)) { mp_fatal(log, "Failed to get DXGI adapter\n"); goto done; } IDXGIDevice1_SetMaximumFrameLatency(dxgi_dev, opts->max_frame_latency); DXGI_ADAPTER_DESC1 desc; hr = IDXGIAdapter1_GetDesc1(adapter, &desc); if (FAILED(hr)) { mp_fatal(log, "Failed to get adapter description\n"); goto done; } D3D_FEATURE_LEVEL selected_level = ID3D11Device_GetFeatureLevel(dev); mp_verbose(log, "Using Direct3D 11 feature level %u_%u\n", ((unsigned)selected_level) >> 12, (((unsigned)selected_level) >> 8) & 0xf); char *dev_name = mp_to_utf8(NULL, desc.Description); mp_verbose(log, "Device Name: %s\n" "Device ID: %04x:%04x (rev %02x)\n" "Subsystem ID: %04x:%04x\n" "LUID: %08lx%08lx\n", dev_name, desc.VendorId, desc.DeviceId, desc.Revision, LOWORD(desc.SubSysId), HIWORD(desc.SubSysId), desc.AdapterLuid.HighPart, desc.AdapterLuid.LowPart); talloc_free(dev_name); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) warp = 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 (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) warp = true; if (warp) { mp_msg(log, opts->force_warp ? MSGL_V : MSGL_WARN, "Using a software adapter\n"); } *dev_out = dev; dev = NULL; success = true; done: SAFE_RELEASE(adapter); SAFE_RELEASE(dxgi_dev); SAFE_RELEASE(dev); return success; } static HRESULT create_swapchain_1_2(ID3D11Device *dev, IDXGIFactory2 *factory, struct mp_log *log, struct d3d11_swapchain_opts *opts, bool flip, DXGI_FORMAT format, IDXGISwapChain **swapchain_out) { IDXGISwapChain *swapchain = NULL; IDXGISwapChain1 *swapchain1 = NULL; HRESULT hr; DXGI_SWAP_CHAIN_DESC1 desc = { .Width = opts->width ? opts->width : 1, .Height = opts->height ? opts->height : 1, .Format = format, .SampleDesc = { .Count = 1 }, .BufferUsage = opts->usage, }; if (flip) { // UNORDERED_ACCESS with FLIP_SEQUENTIAL seems to be buggy with // Windows 7 drivers if ((desc.BufferUsage & DXGI_USAGE_UNORDERED_ACCESS) && !IsWindows8OrGreater()) { mp_verbose(log, "Disabling UNORDERED_ACCESS for flip-model " "swapchain backbuffers in Windows 7\n"); desc.BufferUsage &= ~DXGI_USAGE_UNORDERED_ACCESS; } if (IsWindows10OrGreater()) { desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; } else { desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; } desc.BufferCount = opts->length; } else { desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; desc.BufferCount = 1; } hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown*)dev, opts->window, &desc, NULL, NULL, &swapchain1); if (FAILED(hr)) goto done; hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain, (void**)&swapchain); if (FAILED(hr)) goto done; *swapchain_out = swapchain; swapchain = NULL; done: SAFE_RELEASE(swapchain1); SAFE_RELEASE(swapchain); return hr; } static HRESULT create_swapchain_1_1(ID3D11Device *dev, IDXGIFactory1 *factory, struct mp_log *log, struct d3d11_swapchain_opts *opts, DXGI_FORMAT format, IDXGISwapChain **swapchain_out) { DXGI_SWAP_CHAIN_DESC desc = { .BufferDesc = { .Width = opts->width ? opts->width : 1, .Height = opts->height ? opts->height : 1, .Format = format, }, .SampleDesc = { .Count = 1 }, .BufferUsage = opts->usage, .BufferCount = 1, .OutputWindow = opts->window, .Windowed = TRUE, .SwapEffect = DXGI_SWAP_EFFECT_DISCARD, }; return IDXGIFactory1_CreateSwapChain(factory, (IUnknown*)dev, &desc, swapchain_out); } static bool update_swapchain_format(struct mp_log *log, IDXGISwapChain *swapchain, DXGI_FORMAT format) { DXGI_SWAP_CHAIN_DESC desc; HRESULT hr = IDXGISwapChain_GetDesc(swapchain, &desc); if (FAILED(hr)) { mp_fatal(log, "Failed to query swap chain's current state: %s\n", mp_HRESULT_to_str(hr)); return false; } hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, desc.BufferDesc.Width, desc.BufferDesc.Height, format, 0); if (FAILED(hr)) { mp_fatal(log, "Couldn't update swapchain format: %s\n", mp_HRESULT_to_str(hr)); return false; } return true; } static bool update_swapchain_color_space(struct mp_log *log, IDXGISwapChain *swapchain, DXGI_COLOR_SPACE_TYPE color_space) { IDXGISwapChain4 *swapchain4 = NULL; const char *csp_name = d3d11_get_csp_name(color_space); bool success = false; HRESULT hr = E_FAIL; unsigned int csp_support_flags; hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain4, (void *)&(swapchain4)); if (FAILED(hr)) { mp_err(log, "Failed to create v4 swapchain for color space " "configuration (%s)!\n", mp_HRESULT_to_str(hr)); goto done; } hr = IDXGISwapChain4_CheckColorSpaceSupport(swapchain4, color_space, &csp_support_flags); if (FAILED(hr)) { mp_err(log, "Failed to check color space support for color space " "%s (%d): %s!\n", csp_name, color_space, mp_HRESULT_to_str(hr)); goto done; } mp_verbose(log, "Swapchain capabilities for color space %s (%d): " "normal: %s, overlay: %s\n", csp_name, color_space, (csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ? "yes" : "no", (csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_OVERLAY_PRESENT) ? "yes" : "no"); if (!(csp_support_flags & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { mp_err(log, "Color space %s (%d) is not supported by this swapchain!\n", csp_name, color_space); goto done; } hr = IDXGISwapChain4_SetColorSpace1(swapchain4, color_space); if (FAILED(hr)) { mp_err(log, "Failed to set color space %s (%d) for this swapchain " "(%s)!\n", csp_name, color_space, mp_HRESULT_to_str(hr)); goto done; } mp_verbose(log, "Swapchain successfully configured to color space %s (%d)!\n", csp_name, color_space); success = true; done: SAFE_RELEASE(swapchain4); return success; } static bool configure_created_swapchain(struct mp_log *log, IDXGISwapChain *swapchain, DXGI_FORMAT requested_format, DXGI_COLOR_SPACE_TYPE requested_csp, struct pl_color_space *configured_csp) { DXGI_FORMAT probed_format = DXGI_FORMAT_UNKNOWN; DXGI_FORMAT selected_format = DXGI_FORMAT_UNKNOWN; DXGI_COLOR_SPACE_TYPE probed_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; DXGI_COLOR_SPACE_TYPE selected_colorspace; const char *format_name = NULL; const char *csp_name = NULL; struct pl_color_space pl_color_system = { 0 }; bool mp_csp_mapped = false; query_output_format_and_colorspace(log, swapchain, &probed_format, &probed_colorspace); selected_format = requested_format != DXGI_FORMAT_UNKNOWN ? requested_format : (probed_format != DXGI_FORMAT_UNKNOWN ? probed_format : DXGI_FORMAT_R8G8B8A8_UNORM); selected_colorspace = requested_csp != -1 ? requested_csp : probed_colorspace; format_name = d3d11_get_format_name(selected_format); csp_name = d3d11_get_csp_name(selected_colorspace); mp_csp_mapped = d3d11_get_mp_csp(selected_colorspace, &pl_color_system); mp_verbose(log, "Selected swapchain format %s (%d), attempting " "to utilize it.\n", format_name, selected_format); if (!update_swapchain_format(log, swapchain, selected_format)) { return false; } if (!IsWindows10OrGreater()) { // On older than Windows 10, query_output_format_and_colorspace // will not change probed_colorspace, and even if a user sets // a colorspace it will not get applied. Thus warn user in case a // value was specifically set and finish. if (requested_csp != -1) { mp_warn(log, "User selected a D3D11 color space %s (%d), " "but configuration of color spaces is only supported" "from Windows 10! The default configuration has been " "left as-is.\n", csp_name, selected_colorspace); } return true; } if (!mp_csp_mapped) { mp_warn(log, "Color space %s (%d) does not have an mpv color space " "mapping! Overriding to standard sRGB!\n", csp_name, selected_colorspace); selected_colorspace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; d3d11_get_mp_csp(selected_colorspace, &pl_color_system); } mp_verbose(log, "Selected swapchain color space %s (%d), attempting to " "utilize it.\n", csp_name, selected_colorspace); if (!update_swapchain_color_space(log, swapchain, selected_colorspace)) { return false; } if (configured_csp) { *configured_csp = pl_color_system; } return true; } // Create a Direct3D 11 swapchain bool mp_d3d11_create_swapchain(ID3D11Device *dev, struct mp_log *log, struct d3d11_swapchain_opts *opts, IDXGISwapChain **swapchain_out) { IDXGIDevice1 *dxgi_dev = NULL; IDXGIAdapter1 *adapter = NULL; IDXGIFactory1 *factory = NULL; IDXGIFactory2 *factory2 = NULL; IDXGISwapChain *swapchain = NULL; bool success = false; HRESULT hr; hr = ID3D11Device_QueryInterface(dev, &IID_IDXGIDevice1, (void**)&dxgi_dev); if (FAILED(hr)) { mp_fatal(log, "Failed to get DXGI device\n"); goto done; } hr = IDXGIDevice1_GetParent(dxgi_dev, &IID_IDXGIAdapter1, (void**)&adapter); if (FAILED(hr)) { mp_fatal(log, "Failed to get DXGI adapter\n"); goto done; } hr = IDXGIAdapter1_GetParent(adapter, &IID_IDXGIFactory1, (void**)&factory); if (FAILED(hr)) { mp_fatal(log, "Failed to get DXGI factory\n"); goto done; } hr = IDXGIFactory1_QueryInterface(factory, &IID_IDXGIFactory2, (void**)&factory2); if (FAILED(hr)) factory2 = NULL; bool flip = factory2 && opts->flip; // Return here to retry creating the swapchain do { if (factory2) { // Create a DXGI 1.2+ (Windows 8+) swap chain if possible hr = create_swapchain_1_2(dev, factory2, log, opts, flip, DXGI_FORMAT_R8G8B8A8_UNORM, &swapchain); } else { // Fall back to DXGI 1.1 (Windows 7) hr = create_swapchain_1_1(dev, factory, log, opts, DXGI_FORMAT_R8G8B8A8_UNORM, &swapchain); } if (SUCCEEDED(hr)) break; if (flip) { mp_dbg(log, "Failed to create flip-model swapchain, trying bitblt\n"); flip = false; continue; } mp_fatal(log, "Failed to create swapchain: %s\n", mp_HRESULT_to_str(hr)); goto done; } while (true); // Prevent DXGI from making changes to the VO window, otherwise it will // hook the Alt+Enter keystroke and make it trigger an ugly transition to // exclusive fullscreen mode instead of running the user-set command. IDXGIFactory_MakeWindowAssociation(factory, opts->window, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN); if (factory2) { mp_verbose(log, "Using DXGI 1.2+\n"); } else { mp_verbose(log, "Using DXGI 1.1\n"); } configure_created_swapchain(log, swapchain, opts->format, opts->color_space, opts->configured_csp); DXGI_SWAP_CHAIN_DESC scd = {0}; IDXGISwapChain_GetDesc(swapchain, &scd); if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) { mp_verbose(log, "Using flip-model presentation\n"); } else { mp_verbose(log, "Using bitblt-model presentation\n"); } *swapchain_out = swapchain; swapchain = NULL; success = true; done: SAFE_RELEASE(swapchain); SAFE_RELEASE(factory2); SAFE_RELEASE(factory); SAFE_RELEASE(adapter); SAFE_RELEASE(dxgi_dev); return success; } bool mp_get_dxgi_output_desc(IDXGISwapChain *swapchain, DXGI_OUTPUT_DESC1 *desc) { bool ret = false; IDXGIOutput *output = NULL; IDXGIOutput6 *output6 = NULL; if (FAILED(IDXGISwapChain_GetContainingOutput(swapchain, &output))) goto done; if (FAILED(IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6, (void**)&output6))) goto done; ret = SUCCEEDED(IDXGIOutput6_GetDesc1(output6, desc)); done: SAFE_RELEASE(output); SAFE_RELEASE(output6); return ret; } void mp_d3d11_get_debug_interfaces(struct mp_log *log, IDXGIDebug **debug, IDXGIInfoQueue **iqueue) { load_d3d11_functions(log); *iqueue = NULL; *debug = NULL; if (!pDXGIGetDebugInterface) return; HRESULT hr; hr = pDXGIGetDebugInterface(&IID_IDXGIInfoQueue, (void **) iqueue); if (FAILED(hr)) { mp_fatal(log, "Failed to get info queue: %s\n", mp_HRESULT_to_str(hr)); return; } // Store an unlimited amount of messages in the buffer. This is fine // because we flush stored messages regularly (in debug_marker.) IDXGIInfoQueue_SetMessageCountLimit(*iqueue, DXGI_DEBUG_D3D11, -1); IDXGIInfoQueue_SetMessageCountLimit(*iqueue, DXGI_DEBUG_DXGI, -1); // Push empty filter to get everything DXGI_INFO_QUEUE_FILTER filter = {0}; IDXGIInfoQueue_PushStorageFilter(*iqueue, DXGI_DEBUG_ALL, &filter); hr = pDXGIGetDebugInterface(&IID_IDXGIDebug, (void **) debug); if (FAILED(hr)) { mp_fatal(log, "Failed to get debug device: %s\n", mp_HRESULT_to_str(hr)); return; } }