summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/out/opengl/context_angle.c265
-rw-r--r--video/out/opengl/d3d11_helpers.c383
-rw-r--r--video/out/opengl/d3d11_helpers.h73
3 files changed, 489 insertions, 232 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#include <pthread.h>
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_D3D11_HELPERS_H_
+#define MP_D3D11_HELPERS_H_
+
+#include <stdbool.h>
+#include <windows.h>
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+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