vo_opengl: extract non-ANGLE specific D3D11 code

This extracts non-ANGLE specific code to d3d11_helpers.c, which is
modeled after egl_helpers.c. Currently the only consumer is
context_angle.c, but in future this may allow the D3D11 device and
swapchain creation logic to be reused in other backends.

Also includes small improvements to D3D11 device creation. It is now
possible to create feature level 11_1 devices (though ANGLE does not
support these,) and BGRA swapchains, which might be slightly more
efficient than ARGB, since its the same format used by the compositor.
This commit is contained in:
James Ross-Gowan 2017-08-15 23:39:55 +10:00
parent f24612db44
commit 16e0a39482
4 changed files with 490 additions and 232 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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" ),