diff options
Diffstat (limited to 'video/out/gpu/d3d11_helpers.c')
-rw-r--r-- | video/out/gpu/d3d11_helpers.c | 966 |
1 files changed, 966 insertions, 0 deletions
diff --git a/video/out/gpu/d3d11_helpers.c b/video/out/gpu/d3d11_helpers.c new file mode 100644 index 0000000..30d9eae --- /dev/null +++ b/video/out/gpu/d3d11_helpers.c @@ -0,0 +1,966 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <windows.h> +#include <d3d11.h> +#include <dxgi1_6.h> +#include <versionhelpers.h> + +#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); + +static mp_once d3d11_once = MP_STATIC_ONCE_INITIALIZER; +static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL; +static PFN_CREATE_DXGI_FACTORY pCreateDXGIFactory1 = NULL; +static void d3d11_load(void) +{ + HMODULE d3d11 = LoadLibraryW(L"d3d11.dll"); + HMODULE dxgilib = LoadLibraryW(L"dxgi.dll"); + if (!d3d11 || !dxgilib) + return; + + pD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) + GetProcAddress(d3d11, "D3D11CreateDevice"); + pCreateDXGIFactory1 = (PFN_CREATE_DXGI_FACTORY) + GetProcAddress(dxgilib, "CreateDXGIFactory1"); +} + +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 "<Unknown>"; + } +} + +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 "<Unknown>"; + } +} + +static bool d3d11_get_mp_csp(DXGI_COLOR_SPACE_TYPE csp, + struct mp_colorspace *mp_csp) +{ + if (!mp_csp) + 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: + *mp_csp = (struct mp_colorspace){ + .gamma = MP_CSP_TRC_AUTO, + .primaries = MP_CSP_PRIM_AUTO, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_LINEAR, + .primaries = MP_CSP_PRIM_AUTO, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_PQ, + .primaries = MP_CSP_PRIM_BT_2020, + }; + break; + case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020: + *mp_csp = (struct mp_colorspace) { + .gamma = MP_CSP_TRC_AUTO, + .primaries = MP_CSP_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) +{ + IDXGIOutput *output = NULL; + IDXGIOutput6 *output6 = NULL; + DXGI_OUTPUT_DESC1 desc = { 0 }; + char *monitor_name = NULL; + bool success = false; + + if (!out_fmt || !out_cspace) + return false; + + HRESULT hr = IDXGISwapChain_GetContainingOutput(swapchain, &output); + if (FAILED(hr)) { + mp_err(log, "Failed to get swap chain's containing output: %s!\n", + mp_HRESULT_to_str(hr)); + goto done; + } + + hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6, + (void**)&output6); + if (FAILED(hr)) { + // point where systems older than Windows 10 would fail, + // thus utilizing error log level only with windows 10+ + mp_msg(log, IsWindows10OrGreater() ? MSGL_ERR : MSGL_V, + "Failed to create a DXGI 1.6 output interface: %s\n", + mp_HRESULT_to_str(hr)); + goto done; + } + + hr = IDXGIOutput6_GetDesc1(output6, &desc); + if (FAILED(hr)) { + mp_err(log, "Failed to query swap chain's output information: %s\n", + mp_HRESULT_to_str(hr)); + 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); + SAFE_RELEASE(output6); + SAFE_RELEASE(output); + 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; +} + +static IDXGIAdapter1 *get_d3d11_adapter(struct mp_log *log, + struct bstr requested_adapter_name, + struct 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; +} + +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_d3d11_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 = get_d3d11_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 = get_d3d11_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 mp_colorspace *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 mp_colorspace mp_csp = { 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, &mp_csp); + + 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, &mp_csp); + } + + 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 = mp_csp; + } + + 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; +} |