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