diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index 13e29c410e..e09945ba2d 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -24,6 +24,7 @@ #include "angle_dynamic.h" #include "egl_helpers.h" +#include "d3d11_helpers.h" #include "common/common.h" #include "options/m_config.h" @@ -40,9 +41,6 @@ #define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002 #endif -// Windows 8 enum value, not present in mingw-w64 headers -#define DXGI_ADAPTER_FLAG_SOFTWARE (2) - enum { RENDERER_AUTO, RENDERER_D3D9, @@ -97,17 +95,11 @@ const struct m_sub_options angle_conf = { }; struct priv { - IDXGIFactory1 *dxgi_factory; - IDXGIFactory2 *dxgi_factory2; - IDXGIAdapter1 *dxgi_adapter; - IDXGIDevice1 *dxgi_device; IDXGISwapChain *dxgi_swapchain; - IDXGISwapChain1 *dxgi_swapchain1; ID3D11Device *d3d11_device; ID3D11DeviceContext *d3d11_context; ID3D11Texture2D *d3d11_backbuffer; - D3D_FEATURE_LEVEL d3d11_level; EGLConfig egl_config; EGLDisplay egl_display; @@ -223,19 +215,6 @@ static void d3d11_device_destroy(MPGLContext *ctx) p->egl_device = 0; SAFE_RELEASE(p->d3d11_device); - SAFE_RELEASE(p->dxgi_device); - SAFE_RELEASE(p->dxgi_adapter); - SAFE_RELEASE(p->dxgi_factory); - SAFE_RELEASE(p->dxgi_factory2); -} - -static void show_sw_adapter_msg(MPGLContext *ctx) -{ - struct priv *p = ctx->priv; - if (p->sw_adapter_msg_shown) - return; - MP_WARN(ctx->vo, "Using a software adapter\n"); - p->sw_adapter_msg_shown = true; } static bool d3d11_device_create(MPGLContext *ctx, int flags) @@ -243,96 +222,17 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) struct priv *p = ctx->priv; struct vo *vo = ctx->vo; struct angle_opts *o = p->opts; - HRESULT hr; - HMODULE d3d11_dll = LoadLibraryW(L"d3d11.dll"); - if (!d3d11_dll) { - MP_FATAL(vo, "Failed to load d3d11.dll\n"); - return false; - } - - PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) - GetProcAddress(d3d11_dll, "D3D11CreateDevice"); - if (!D3D11CreateDevice) { - MP_FATAL(vo, "D3D11CreateDevice entry point not found\n"); - return false; - } - - D3D_FEATURE_LEVEL *levels = (D3D_FEATURE_LEVEL[]) { - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, + struct d3d11_device_opts device_opts = { + .allow_warp = o->d3d11_warp != 0, + .force_warp = o->d3d11_warp == 1, + .max_feature_level = o->d3d11_feature_level, + .min_feature_level = D3D_FEATURE_LEVEL_9_3, + .max_frame_latency = o->max_frame_latency, }; - int level_count = 4; - - // Only try feature levels less than or equal to the user specified level - while (level_count && levels[0] > o->d3d11_feature_level) { - levels++; - level_count--; - } - - // Try a HW adapter first unless WARP is forced - hr = E_FAIL; - if ((FAILED(hr) && o->d3d11_warp == -1) || o->d3d11_warp == 0) { - hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, levels, - level_count, D3D11_SDK_VERSION, &p->d3d11_device, &p->d3d11_level, - &p->d3d11_context); - } - // Try WARP if it is forced or if the HW adapter failed - if ((FAILED(hr) && o->d3d11_warp == -1) || o->d3d11_warp == 1) { - hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, levels, - level_count, D3D11_SDK_VERSION, &p->d3d11_device, &p->d3d11_level, - &p->d3d11_context); - if (SUCCEEDED(hr)) - show_sw_adapter_msg(ctx); - } - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't create Direct3D 11 device: %s\n", - mp_HRESULT_to_str(hr)); + if (!mp_d3d11_create_present_device(vo->log, &device_opts, &p->d3d11_device)) return false; - } - - hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_IDXGIDevice1, - (void**)&p->dxgi_device); - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't get DXGI device\n"); - return false; - } - - IDXGIDevice1_SetMaximumFrameLatency(p->dxgi_device, o->max_frame_latency); - - hr = IDXGIDevice1_GetParent(p->dxgi_device, &IID_IDXGIAdapter1, - (void**)&p->dxgi_adapter); - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't get DXGI adapter\n"); - return false; - } - - // Query some properties of the adapter in order to warn the user if they - // are using a software adapter - DXGI_ADAPTER_DESC1 desc; - hr = IDXGIAdapter1_GetDesc1(p->dxgi_adapter, &desc); - if (SUCCEEDED(hr)) { - if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) - show_sw_adapter_msg(ctx); - - // If the primary display adapter is a software adapter, the - // DXGI_ADAPTER_FLAG_SOFTWARE won't be set, but the device IDs - // should still match the Microsoft Basic Render Driver - if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) - show_sw_adapter_msg(ctx); - } - - hr = IDXGIAdapter1_GetParent(p->dxgi_adapter, &IID_IDXGIFactory1, - (void**)&p->dxgi_factory); - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't get DXGI factory\n"); - return false; - } - - IDXGIFactory1_QueryInterface(p->dxgi_factory, &IID_IDXGIFactory2, - (void**)&p->dxgi_factory2); + ID3D11Device_GetImmediateContext(p->d3d11_device, &p->d3d11_context); PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); @@ -367,119 +267,39 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) static void d3d11_swapchain_surface_destroy(MPGLContext *ctx) { struct priv *p = ctx->priv; + + bool had_swapchain = p->dxgi_swapchain; SAFE_RELEASE(p->dxgi_swapchain); - SAFE_RELEASE(p->dxgi_swapchain1); d3d11_backbuffer_release(ctx); -} -static bool d3d11_swapchain_create_1_2(MPGLContext *ctx, int flags) -{ - struct priv *p = ctx->priv; - struct vo *vo = ctx->vo; - HRESULT hr; - - update_sizes(ctx); - DXGI_SWAP_CHAIN_DESC1 desc1 = { - .Width = p->sc_width, - .Height = p->sc_height, - .Format = DXGI_FORMAT_R8G8B8A8_UNORM, - .SampleDesc = { .Count = 1 }, - .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | - DXGI_USAGE_SHADER_INPUT, - }; - - if (p->opts->flip) { - desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - desc1.BufferCount = p->opts->swapchain_length; - } else { - desc1.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - desc1.BufferCount = 1; - } - - hr = IDXGIFactory2_CreateSwapChainForHwnd(p->dxgi_factory2, - (IUnknown*)p->d3d11_device, vo_w32_hwnd(vo), &desc1, NULL, NULL, - &p->dxgi_swapchain1); - if (FAILED(hr) && p->opts->flip) { - // Try again without DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL - desc1.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - desc1.BufferCount = 1; - - hr = IDXGIFactory2_CreateSwapChainForHwnd(p->dxgi_factory2, - (IUnknown*)p->d3d11_device, vo_w32_hwnd(vo), &desc1, NULL, NULL, - &p->dxgi_swapchain1); - } - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't create DXGI 1.2+ swap chain: %s\n", - mp_HRESULT_to_str(hr)); - return false; - } - - hr = IDXGISwapChain1_QueryInterface(p->dxgi_swapchain1, - &IID_IDXGISwapChain, (void**)&p->dxgi_swapchain); - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't create DXGI 1.2+ swap chain\n"); - return false; - } - - return true; -} - -static bool d3d11_swapchain_create_1_1(MPGLContext *ctx, int flags) -{ - struct priv *p = ctx->priv; - struct vo *vo = ctx->vo; - HRESULT hr; - - update_sizes(ctx); - DXGI_SWAP_CHAIN_DESC desc = { - .BufferDesc = { - .Width = p->sc_width, - .Height = p->sc_height, - .Format = DXGI_FORMAT_R8G8B8A8_UNORM - }, - .SampleDesc = { .Count = 1 }, - .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | - DXGI_USAGE_SHADER_INPUT, - .BufferCount = 1, - .OutputWindow = vo_w32_hwnd(vo), - .Windowed = TRUE, - .SwapEffect = DXGI_SWAP_EFFECT_DISCARD, - }; - - hr = IDXGIFactory1_CreateSwapChain(p->dxgi_factory, - (IUnknown*)p->d3d11_device, &desc, &p->dxgi_swapchain); - if (FAILED(hr)) { - MP_FATAL(vo, "Couldn't create DXGI 1.1 swap chain: %s\n", - mp_HRESULT_to_str(hr)); - return false; - } - - return true; + // Ensure the swapchain is destroyed by flushing the D3D11 immediate + // context. This is needed because the HWND may be reused. See: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476425.aspx + if (had_swapchain && p->d3d11_context) + ID3D11DeviceContext_Flush(p->d3d11_context); } static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags) { struct priv *p = ctx->priv; struct vo *vo = ctx->vo; + struct angle_opts *o = p->opts; - if (p->dxgi_factory2) { - // Create a DXGI 1.2+ (Windows 8+) swap chain if possible - if (!d3d11_swapchain_create_1_2(ctx, flags)) - goto fail; - } else if (p->dxgi_factory) { - // Fall back to DXGI 1.1 (Windows 7) - if (!d3d11_swapchain_create_1_1(ctx, flags)) - goto fail; - } else { + if (!p->d3d11_device) goto fail; - } - // 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(p->dxgi_factory, vo_w32_hwnd(vo), - DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | - DXGI_MWA_NO_PRINT_SCREEN); + update_sizes(ctx); + struct d3d11_swapchain_opts swapchain_opts = { + .window = vo_w32_hwnd(vo), + .width = p->sc_width, + .height = p->sc_height, + .flip = o->flip, + .length = o->swapchain_length, + .usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT, + }; + if (!mp_d3d11_create_swapchain(p->d3d11_device, vo->log, &swapchain_opts, + &p->dxgi_swapchain)) + goto fail; if (!d3d11_backbuffer_get(ctx)) goto fail; @@ -677,10 +497,6 @@ static int angle_init(struct MPGLContext *ctx, int flags) if ((!context_ok && !o->renderer) || o->renderer == RENDERER_D3D11) { context_ok = d3d11_device_create(ctx, flags); if (context_ok) { - MP_VERBOSE(vo, "Using Direct3D 11 feature level %u_%u\n", - ((unsigned)p->d3d11_level) >> 12, - (((unsigned)p->d3d11_level) >> 8) & 0xf); - context_ok = context_init(ctx, flags); if (!context_ok) d3d11_device_destroy(ctx); @@ -706,21 +522,6 @@ static int angle_init(struct MPGLContext *ctx, int flags) bool surface_ok = false; if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 0) { surface_ok = d3d11_swapchain_surface_create(ctx, flags); - if (surface_ok) { - if (p->dxgi_swapchain1) { - MP_VERBOSE(vo, "Using DXGI 1.2+\n"); - - DXGI_SWAP_CHAIN_DESC1 scd = {0}; - IDXGISwapChain1_GetDesc1(p->dxgi_swapchain1, &scd); - if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) { - MP_VERBOSE(vo, "Using flip-model presentation\n"); - } else { - MP_VERBOSE(vo, "Using bitblt-model presentation\n"); - } - } else { - MP_VERBOSE(vo, "Using DXGI 1.1\n"); - } - } } if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 1) { surface_ok = egl_window_surface_create(ctx, flags); @@ -755,15 +556,15 @@ static struct mp_image *d3d11_screenshot(MPGLContext *ctx) struct mp_image *img = NULL; HRESULT hr; - if (!p->dxgi_swapchain1) + if (!p->dxgi_swapchain) goto done; // Validate the swap chain. This screenshot method will only work on DXGI // 1.2+ flip/sequential swap chains. It's probably not possible at all with // discard swap chains, since by definition, the backbuffer contents is // discarded on Present(). - DXGI_SWAP_CHAIN_DESC1 scd; - hr = IDXGISwapChain1_GetDesc1(p->dxgi_swapchain1, &scd); + DXGI_SWAP_CHAIN_DESC scd; + hr = IDXGISwapChain_GetDesc(p->dxgi_swapchain, &scd); if (FAILED(hr)) goto done; if (scd.SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) diff --git a/video/out/opengl/d3d11_helpers.c b/video/out/opengl/d3d11_helpers.c new file mode 100644 index 0000000000..d9b7fc2804 --- /dev/null +++ b/video/out/opengl/d3d11_helpers.c @@ -0,0 +1,383 @@ +/* + * 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 "osdep/io.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) + +static pthread_once_t d3d11_once = PTHREAD_ONCE_INIT; +static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = NULL; +static void d3d11_load(void) +{ + HMODULE d3d11 = LoadLibraryW(L"d3d11.dll"); + if (!d3d11) + return; + pD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) + GetProcAddress(d3d11, "D3D11CreateDevice"); +} + +// 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_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 HRESULT create_device(struct mp_log *log, bool warp, bool bgra, + 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 = bgra ? D3D11_CREATE_DEVICE_BGRA_SUPPORT : 0; + return pD3D11CreateDevice(NULL, type, NULL, flags, levels, levels_len, + D3D11_SDK_VERSION, dev, NULL, NULL); +} + +// 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 warp = opts->force_warp; + bool bgra = true; + int max_fl = opts->max_feature_level; + int min_fl = opts->min_feature_level; + ID3D11Device *dev = NULL; + IDXGIDevice1 *dxgi_dev = NULL; + IDXGIAdapter1 *adapter = NULL; + bool success = false; + HRESULT hr; + + pthread_once(&d3d11_once, d3d11_load); + if (!pD3D11CreateDevice) { + mp_fatal(log, "Failed to load d3d11.dll\n"); + goto done; + } + + // 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, warp, bgra, max_fl, min_fl, &dev); + if (SUCCEEDED(hr)) + break; + + // BGRA is recommended, but FL 10_0 hardware may not support it + if (bgra) { + mp_dbg(log, "Failed to create D3D device with BGRA support\n"); + bgra = false; + continue; + } + + // Trying to create a D3D_FEATURE_LEVEL_11_1 device on Windows 7 + // without the platform update will not succeed. Try a 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; + bgra = true; + 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; + bgra = true; + continue; + } + + mp_fatal(log, "Failed to create Direct3D 11 device: %s\n", + mp_HRESULT_to_str(hr)); + goto done; + } while (true); + + 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: %s\n" + "VendorId: 0x%04d\n" + "DeviceId: 0x%04d\n" + "LUID: %08lx%08lx\n", + dev_name, desc.VendorId, desc.DeviceId, + 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) { + 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); +} + +// 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; + + // Try B8G8R8A8_UNORM first, since at least in Windows 8, it's always the + // format of the desktop image + static const DXGI_FORMAT formats[] = { + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, + }; + static const int formats_len = MP_ARRAY_SIZE(formats); + bool flip = factory2 && opts->flip; + + // Return here to retry creating the swapchain + do { + for (int i = 0; i < formats_len; i++) { + if (factory2) { + // Create a DXGI 1.2+ (Windows 8+) swap chain if possible + hr = create_swapchain_1_2(dev, factory2, log, opts, flip, + formats[i], &swapchain); + } else { + // Fall back to DXGI 1.1 (Windows 7) + hr = create_swapchain_1_1(dev, factory, log, opts, formats[i], + &swapchain); + } + if (SUCCEEDED(hr)) + break; + } + 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"); + } + + DXGI_SWAP_CHAIN_DESC scd = {0}; + IDXGISwapChain_GetDesc(swapchain, &scd); + if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) { + 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; +} diff --git a/video/out/opengl/d3d11_helpers.h b/video/out/opengl/d3d11_helpers.h new file mode 100644 index 0000000000..f34d1d4def --- /dev/null +++ b/video/out/opengl/d3d11_helpers.h @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +#ifndef MP_D3D11_HELPERS_H_ +#define MP_D3D11_HELPERS_H_ + +#include +#include +#include +#include + +struct d3d11_device_opts { + // Allow a software (WARP) adapter. Note, sometimes a software adapter will + // be used even when allow_warp is false. This is because, on Windows 8 and + // up, if there are no hardware adapters, Windows will pretend the WARP + // adapter is the primary hardware adapter. + bool allow_warp; + + // Always use a WARP adapter. This is mainly for testing purposes. + bool force_warp; + + // The maximum number of pending frames allowed to be queued to a swapchain + int max_frame_latency; + + // The maximum Direct3D 11 feature level to attempt to create + // If unset, defaults to D3D_FEATURE_LEVEL_11_0 + int max_feature_level; + + // The minimum Direct3D 11 feature level to attempt to create. If this is + // not supported, device creation will fail. + // If unset, defaults to D3D_FEATURE_LEVEL_9_1 + int min_feature_level; +}; + +bool mp_d3d11_create_present_device(struct mp_log *log, + struct d3d11_device_opts *opts, + ID3D11Device **dev_out); + +struct d3d11_swapchain_opts { + HWND window; + int width; + int height; + + // Use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible + bool flip; + + // Number of surfaces in the swapchain + int length; + + // The BufferUsage value for swapchain surfaces. This should probably + // contain DXGI_USAGE_RENDER_TARGET_OUTPUT. + DXGI_USAGE usage; +}; + +bool mp_d3d11_create_swapchain(ID3D11Device *dev, struct mp_log *log, + struct d3d11_swapchain_opts *opts, + IDXGISwapChain **swapchain_out); + +#endif diff --git a/wscript_build.py b/wscript_build.py index 94ffef3ae4..600c1c93fe 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -397,6 +397,7 @@ def build(ctx): ( "video/out/opengl/context_x11.c", "gl-x11" ), ( "video/out/opengl/context_x11egl.c", "egl-x11" ), ( "video/out/opengl/cuda_dynamic.c", "cuda-hwaccel" ), + ( "video/out/opengl/d3d11_helpers.c", "egl-angle-win32" ), ( "video/out/opengl/egl_helpers.c", "egl-helpers" ), ( "video/out/opengl/formats.c", "gl" ), ( "video/out/opengl/gl_utils.c", "gl" ),