summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
authorJames Ross-Gowan <rossymiles@gmail.com>2017-02-04 19:16:02 +1100
committerJames Ross-Gowan <rossymiles@gmail.com>2017-02-07 22:45:07 +1100
commite0250b9604b24f32d53b409d8c48d16faa2caebc (patch)
tree2cc257cbc78b4d949feace4356d487869de20e3d /video
parent5fbad204a6d059a1ee997567d0ff470384736820 (diff)
downloadmpv-e0250b9604b24f32d53b409d8c48d16faa2caebc.tar.bz2
mpv-e0250b9604b24f32d53b409d8c48d16faa2caebc.tar.xz
vo_opengl: angle: rewrite with custom swap chain
This replaces the old backend that exclusively used EGL windowing with one that can also use ANGLE's ability to render to directly to a texture. The advantage of this is that it allows mpv to create the swap chain itself and this allows mpv to use a flip-mode swap chain on a HWND (which avoids problems with DirectComposition) and to use a longer swap chain that has six backbuffers by default (which reportedly fixes problems with rendering 24fps video on 24Hz monitors.) Also, "screenshot window" should now work on DXGI 1.2 and up (Windows 8 and up.)
Diffstat (limited to 'video')
-rw-r--r--video/out/opengl/angle_dynamic.h7
-rw-r--r--video/out/opengl/context.c1
-rw-r--r--video/out/opengl/context_angle.c871
-rw-r--r--video/out/opengl/egl_helpers.c6
4 files changed, 653 insertions, 232 deletions
diff --git a/video/out/opengl/angle_dynamic.h b/video/out/opengl/angle_dynamic.h
index 87ad85c268..12a0c692eb 100644
--- a/video/out/opengl/angle_dynamic.h
+++ b/video/out/opengl/angle_dynamic.h
@@ -45,9 +45,12 @@
(EGLDisplay, EGLint)) \
FN(eglSwapBuffers, EGLBoolean (*EGLAPIENTRY PFN_eglSwapBuffers) \
(EGLDisplay, EGLSurface)) \
+ FN(eglSwapInterval, EGLBoolean (*EGLAPIENTRY PFN_eglSwapInterval) \
+ (EGLDisplay, EGLint)) \
FN(eglReleaseTexImage, EGLBoolean (*EGLAPIENTRY PFN_eglReleaseTexImage) \
(EGLDisplay, EGLSurface, EGLint)) \
- FN(eglTerminate, EGLBoolean (*EGLAPIENTRY PFN_eglTerminate)(EGLDisplay))
+ FN(eglTerminate, EGLBoolean (*EGLAPIENTRY PFN_eglTerminate)(EGLDisplay)) \
+ FN(eglWaitClient, EGLBoolean (*EGLAPIENTRY PFN_eglWaitClient)(void))
#define ANGLE_EXT_DECL(NAME, VAR) \
extern VAR;
@@ -76,7 +79,9 @@ bool angle_load(void);
#define eglQueryString PFN_eglQueryString
#define eglReleaseTexImage PFN_eglReleaseTexImage
#define eglSwapBuffers PFN_eglSwapBuffers
+#define eglSwapInterval PFN_eglSwapInterval
#define eglTerminate PFN_eglTerminate
+#define eglWaitClient PFN_eglWaitClient
#endif
#endif
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
index 478421385c..f7fce4fae5 100644
--- a/video/out/opengl/context.c
+++ b/video/out/opengl/context.c
@@ -57,7 +57,6 @@ static const struct mpgl_driver *const backends[] = {
#endif
#if HAVE_EGL_ANGLE
&mpgl_driver_angle,
- &mpgl_driver_angle_es2,
#endif
#if HAVE_GL_WIN32
&mpgl_driver_w32,
diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c
index 5af18e9928..9ea7fb43bb 100644
--- a/video/out/opengl/context_angle.c
+++ b/video/out/opengl/context_angle.c
@@ -19,15 +19,21 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <d3d11.h>
-#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <dwmapi.h>
#include "angle_dynamic.h"
+#include "egl_helpers.h"
#include "common/common.h"
#include "options/m_config.h"
#include "video/out/w32_common.h"
+#include "osdep/windows_utils.h"
#include "context.h"
+#ifndef EGL_D3D_TEXTURE_ANGLE
+#define EGL_D3D_TEXTURE_ANGLE 0x33A3
+#endif
#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
@@ -37,96 +43,189 @@
// Windows 8 enum value, not present in mingw-w64 headers
#define DXGI_ADAPTER_FLAG_SOFTWARE (2)
+enum {
+ RENDERER_AUTO,
+ RENDERER_D3D9,
+ RENDERER_D3D11,
+};
+
struct angle_opts {
- int allow_direct_composition;
+ int renderer;
+ int d3d11_warp;
+ int d3d11_feature_level;
+ int egl_windowing;
+ int swapchain_length; // Currently only works with DXGI 1.2+
+ int max_frame_latency;
};
#define OPT_BASE_STRUCT struct angle_opts
const struct m_sub_options angle_conf = {
.opts = (const struct m_option[]) {
- OPT_FLAG("opengl-dcomposition", allow_direct_composition, 0),
+ OPT_CHOICE("angle-renderer", renderer, 0,
+ ({"auto", RENDERER_AUTO},
+ {"d3d9", RENDERER_D3D9},
+ {"d3d11", RENDERER_D3D11})),
+ OPT_CHOICE("angle-d3d11-warp", d3d11_warp, 0,
+ ({"auto", -1},
+ {"no", 0},
+ {"yes", 1})),
+ OPT_CHOICE("angle-d3d11-feature-level", d3d11_feature_level, 0,
+ ({"11_0", D3D_FEATURE_LEVEL_11_0},
+ {"10_1", D3D_FEATURE_LEVEL_10_1},
+ {"10_0", D3D_FEATURE_LEVEL_10_0},
+ {"9_3", D3D_FEATURE_LEVEL_9_3})),
+ OPT_CHOICE("angle-egl-windowing", egl_windowing, 0,
+ ({"auto", -1},
+ {"no", 0},
+ {"yes", 1})),
+ OPT_INTRANGE("angle-swapchain-length", swapchain_length, 0, 2, 16),
+ OPT_INTRANGE("angle-max-frame-latency", max_frame_latency, 0, 1, 16),
{0}
},
.defaults = &(const struct angle_opts) {
- .allow_direct_composition = 1,
+ .renderer = RENDERER_AUTO,
+ .d3d11_warp = -1,
+ .d3d11_feature_level = D3D_FEATURE_LEVEL_11_0,
+ .egl_windowing = -1,
+ .swapchain_length = 6,
+ .max_frame_latency = 3,
},
.size = sizeof(struct angle_opts),
};
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;
+
+ EGLConfig egl_config;
EGLDisplay egl_display;
+ EGLDeviceEXT egl_device;
EGLContext egl_context;
- EGLSurface egl_surface;
- bool use_es2;
+ EGLSurface egl_window; // For the EGL windowing surface only
+ EGLSurface egl_backbuffer; // For the DXGI swap chain based surface
+
+ int sc_width, sc_height; // Swap chain width and height
+ int swapinterval;
+
bool sw_adapter_msg_shown;
- PFNEGLPOSTSUBBUFFERNVPROC eglPostSubBufferNV;
+
struct angle_opts *opts;
};
-static void angle_uninit(MPGLContext *ctx)
+static __thread struct MPGLContext *current_ctx;
+
+static void update_sizes(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
+ p->sc_width = ctx->vo->dwidth ? ctx->vo->dwidth : 1;
+ p->sc_height = ctx->vo->dheight ? ctx->vo->dheight : 1;
+}
- if (p->egl_context) {
+static void d3d11_backbuffer_release(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (p->egl_backbuffer) {
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
- eglDestroyContext(p->egl_display, p->egl_context);
+ eglDestroySurface(p->egl_display, p->egl_backbuffer);
}
- p->egl_context = EGL_NO_CONTEXT;
- if (p->egl_display)
- eglTerminate(p->egl_display);
- vo_w32_uninit(ctx->vo);
+ p->egl_backbuffer = EGL_NO_SURFACE;
+
+ SAFE_RELEASE(p->d3d11_backbuffer);
}
-static EGLConfig select_fb_config_egl(struct MPGLContext *ctx)
+static bool d3d11_backbuffer_get(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+ HRESULT hr;
+
+ hr = IDXGISwapChain_GetBuffer(p->dxgi_swapchain, 0, &IID_ID3D11Texture2D,
+ (void**)&p->d3d11_backbuffer);
+ if (FAILED(hr)) {
+ MP_FATAL(vo, "Couldn't get swap chain back buffer\n");
+ return false;
+ }
- EGLint attributes[] = {
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_NONE
+ EGLint pbuffer_attributes[] = {
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ EGL_NONE,
};
+ p->egl_backbuffer = eglCreatePbufferFromClientBuffer(p->egl_display,
+ EGL_D3D_TEXTURE_ANGLE, p->d3d11_backbuffer, p->egl_config,
+ pbuffer_attributes);
+ if (!p->egl_backbuffer) {
+ MP_FATAL(vo, "Couldn't create EGL pbuffer\n");
+ return false;
+ }
+
+ eglMakeCurrent(p->egl_display, p->egl_backbuffer, p->egl_backbuffer,
+ p->egl_context);
+ return true;
+}
- EGLint config_count;
- EGLConfig config;
+static void d3d11_backbuffer_resize(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+ HRESULT hr;
- eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
+ int old_sc_width = p->sc_width;
+ int old_sc_height = p->sc_height;
- if (!config_count) {
- MP_FATAL(ctx->vo, "Could find EGL configuration!\n");
- return NULL;
- }
+ update_sizes(ctx);
+ // Avoid unnecessary resizing
+ if (old_sc_width == p->sc_width && old_sc_height == p->sc_height)
+ return;
- return config;
+ // All references to backbuffers must be released before ResizeBuffers
+ // (including references held by ANGLE)
+ d3d11_backbuffer_release(ctx);
+
+ // The DirectX runtime may report errors related to the device like
+ // DXGI_ERROR_DEVICE_REMOVED at this point
+ hr = IDXGISwapChain_ResizeBuffers(p->dxgi_swapchain, 0, p->sc_width,
+ p->sc_height, DXGI_FORMAT_UNKNOWN, 0);
+ if (FAILED(hr))
+ MP_FATAL(vo, "Couldn't resize swapchain: %s\n", mp_HRESULT_to_str(hr));
+
+ if (!d3d11_backbuffer_get(ctx))
+ MP_FATAL(vo, "Couldn't get back buffer after resize\n");
}
-static bool create_context_egl(MPGLContext *ctx, EGLConfig config, int version)
+static void d3d11_device_destroy(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
- EGLint context_attributes[] = {
- EGL_CONTEXT_CLIENT_VERSION, version,
- EGL_NONE
- };
+ PFNEGLRELEASEDEVICEANGLEPROC eglReleaseDeviceANGLE =
+ (PFNEGLRELEASEDEVICEANGLEPROC)eglGetProcAddress("eglReleaseDeviceANGLE");
- p->egl_context = eglCreateContext(p->egl_display, config,
- EGL_NO_CONTEXT, context_attributes);
-
- if (p->egl_context == EGL_NO_CONTEXT) {
- MP_VERBOSE(ctx->vo, "Could not create EGL GLES %d context!\n", version);
- return false;
- }
+ if (p->egl_display)
+ eglTerminate(p->egl_display);
+ p->egl_display = EGL_NO_DISPLAY;
- eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
- p->egl_context);
+ if (p->egl_device && eglReleaseDeviceANGLE)
+ eglReleaseDeviceANGLE(p->egl_device);
+ p->egl_device = 0;
- return true;
+ 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(struct MPGLContext *ctx)
+static void show_sw_adapter_msg(MPGLContext *ctx)
{
struct priv *p = ctx->priv;
if (p->sw_adapter_msg_shown)
@@ -135,182 +234,309 @@ static void show_sw_adapter_msg(struct MPGLContext *ctx)
p->sw_adapter_msg_shown = true;
}
-static void d3d_init(struct MPGLContext *ctx)
+static bool d3d11_device_create(MPGLContext *ctx, int flags)
{
- HRESULT hr;
struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
- IDXGIDevice *dxgi_dev = NULL;
- IDXGIAdapter *dxgi_adapter = NULL;
- IDXGIAdapter1 *dxgi_adapter1 = NULL;
- IDXGIFactory *dxgi_factory = NULL;
-
- PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
- (PFNEGLQUERYDISPLAYATTRIBEXTPROC)eglGetProcAddress("eglQueryDisplayAttribEXT");
- PFNEGLQUERYDEVICEATTRIBEXTPROC eglQueryDeviceAttribEXT =
- (PFNEGLQUERYDEVICEATTRIBEXTPROC)eglGetProcAddress("eglQueryDeviceAttribEXT");
- if (!eglQueryDisplayAttribEXT || !eglQueryDeviceAttribEXT) {
- MP_VERBOSE(vo, "Missing EGL_EXT_device_query\n");
- goto done;
+ 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;
}
- EGLAttrib dev_attr;
- if (!eglQueryDisplayAttribEXT(p->egl_display, EGL_DEVICE_EXT, &dev_attr)) {
- MP_VERBOSE(vo, "Missing EGL_EXT_device_query\n");
- goto done;
+ 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;
}
- // If ANGLE is in D3D11 mode, get the underlying ID3D11Device
- EGLDeviceEXT dev = (EGLDeviceEXT)dev_attr;
- EGLAttrib d3d11_dev_attr;
- if (eglQueryDeviceAttribEXT(dev, EGL_D3D11_DEVICE_ANGLE, &d3d11_dev_attr)) {
- ID3D11Device *d3d11_dev = (ID3D11Device*)d3d11_dev_attr;
+ 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,
+ };
+ D3D_FEATURE_LEVEL selected_level;
+ 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--;
+ }
- hr = ID3D11Device_QueryInterface(d3d11_dev, &IID_IDXGIDevice,
- (void**)&dxgi_dev);
- if (FAILED(hr)) {
- MP_ERR(vo, "Device is not a IDXGIDevice\n");
- goto done;
- }
+ // 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, &selected_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, &selected_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));
+ return false;
+ }
- hr = IDXGIDevice_GetAdapter(dxgi_dev, &dxgi_adapter);
- if (FAILED(hr)) {
- MP_ERR(vo, "Couldn't get IDXGIAdapter\n");
- goto done;
- }
+ MP_VERBOSE(vo, "Using Direct3D 11 feature level %u_%u\n",
+ ((unsigned)selected_level) >> 12,
+ (((unsigned)selected_level) >> 8) & 0xf);
- // Windows 8 can choose a software adapter even if mpv didn't ask for
- // one. If this is the case, show a warning message.
- hr = IDXGIAdapter_QueryInterface(dxgi_adapter, &IID_IDXGIAdapter1,
- (void**)&dxgi_adapter1);
- if (SUCCEEDED(hr)) {
- DXGI_ADAPTER_DESC1 desc;
- hr = IDXGIAdapter1_GetDesc1(dxgi_adapter1, &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 = 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;
+ }
- hr = IDXGIAdapter_GetParent(dxgi_adapter, &IID_IDXGIFactory,
- (void**)&dxgi_factory);
- if (FAILED(hr)) {
- MP_ERR(vo, "Couldn't get IDXGIFactory\n");
- goto done;
- }
+ IDXGIDevice1_SetMaximumFrameLatency(p->dxgi_device, o->max_frame_latency);
- // Prevent DXGI from making changes to the VO window, otherwise in
- // non-DirectComposition mode 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(dxgi_factory, vo_w32_hwnd(vo),
- DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER |
- DXGI_MWA_NO_PRINT_SCREEN);
+ 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;
}
-done:
- if (dxgi_dev)
- IDXGIDevice_Release(dxgi_dev);
- if (dxgi_adapter)
- IDXGIAdapter_Release(dxgi_adapter);
- if (dxgi_adapter1)
- IDXGIAdapter1_Release(dxgi_adapter1);
- if (dxgi_factory)
- IDXGIFactory_Release(dxgi_factory);
+ // 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);
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
+ (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+ if (!eglGetPlatformDisplayEXT) {
+ MP_FATAL(vo, "Missing EGL_EXT_platform_base\n");
+ return false;
+ }
+ PFNEGLCREATEDEVICEANGLEPROC eglCreateDeviceANGLE =
+ (PFNEGLCREATEDEVICEANGLEPROC)eglGetProcAddress("eglCreateDeviceANGLE");
+ if (!eglCreateDeviceANGLE) {
+ MP_FATAL(vo, "Missing EGL_EXT_platform_device\n");
+ return false;
+ }
+
+ p->egl_device = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE,
+ p->d3d11_device, NULL);
+ if (!p->egl_device) {
+ MP_FATAL(vo, "Couldn't create EGL device\n");
+ return false;
+ }
+
+ p->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT,
+ p->egl_device, NULL);
+ if (!p->egl_display) {
+ MP_FATAL(vo, "Couldn't get EGL display\n");
+ return false;
+ }
+
+ return true;
}
-static void *get_proc_address(const GLubyte *proc_name)
+static void d3d11_swapchain_surface_destroy(MPGLContext *ctx)
{
- return eglGetProcAddress(proc_name);
+ struct priv *p = ctx->priv;
+ SAFE_RELEASE(p->dxgi_swapchain);
+ SAFE_RELEASE(p->dxgi_swapchain1);
+ d3d11_backbuffer_release(ctx);
}
-static int angle_init(struct MPGLContext *ctx, int flags)
+static bool d3d11_swapchain_create_1_2(MPGLContext *ctx, int flags)
{
struct priv *p = ctx->priv;
struct vo *vo = ctx->vo;
+ HRESULT hr;
- p->opts = mp_get_config_group(ctx, ctx->global, &angle_conf);
+ 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,
+ .BufferCount = p->opts->swapchain_length,
+ .SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
+ };
- if (!angle_load()) {
- MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n");
- goto fail;
+ 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;
}
- if (!vo_w32_init(vo))
- goto fail;
+ 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;
- HDC dc = GetDC(vo_w32_hwnd(vo));
- if (!dc) {
- MP_FATAL(vo, "Couldn't get DC\n");
+ 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;
+}
+
+static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ 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;
+ MP_VERBOSE(vo, "Using DXGI 1.2+\n");
+ } else if (p->dxgi_factory) {
+ // Fall back to DXGI 1.1 (Windows 7)
+ if (!d3d11_swapchain_create_1_1(ctx, flags))
+ goto fail;
+ MP_VERBOSE(vo, "Using DXGI 1.1\n");
+ } else {
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);
+
+ if (!d3d11_backbuffer_get(ctx))
+ goto fail;
+
+ // EGL_D3D_TEXTURE_ANGLE pbuffers are always flipped vertically
+ ctx->flip_v = true;
+ return true;
+
+fail:
+ d3d11_swapchain_surface_destroy(ctx);
+ return false;
+}
+
+static void d3d9_device_destroy(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (p->egl_display)
+ eglTerminate(p->egl_display);
+ p->egl_display = EGL_NO_DISPLAY;
+}
+
+static bool d3d9_device_create(MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
if (!eglGetPlatformDisplayEXT) {
MP_FATAL(vo, "Missing EGL_EXT_platform_base\n");
- goto fail;
+ return false;
}
- EGLint d3d_types[] = {EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
- EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
- EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE};
- EGLint d3d_dev_types[] = {EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
- EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
- EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE};
- for (int i = 0; i < MP_ARRAY_SIZE(d3d_types); i++) {
- EGLint display_attributes[] = {
- EGL_PLATFORM_ANGLE_TYPE_ANGLE,
- d3d_types[i],
- EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
- d3d_dev_types[i],
- EGL_NONE,
- };
-
- p->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc,
- display_attributes);
- if (p->egl_display == EGL_NO_DISPLAY)
- continue;
-
- if (!eglInitialize(p->egl_display, NULL, NULL)) {
- p->egl_display = EGL_NO_DISPLAY;
- continue;
- }
-
- if (d3d_dev_types[i] == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE)
- show_sw_adapter_msg(ctx);
- break;
- }
+ EGLint display_attributes[] = {
+ EGL_PLATFORM_ANGLE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
+ EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
+ EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
+ EGL_NONE,
+ };
+ p->egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
+ EGL_DEFAULT_DISPLAY, display_attributes);
if (p->egl_display == EGL_NO_DISPLAY) {
MP_FATAL(vo, "Couldn't get display\n");
- goto fail;
+ return false;
}
- const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
- if (exts)
- MP_DBG(ctx->vo, "EGL extensions: %s\n", exts);
+ return true;
+}
- eglBindAPI(EGL_OPENGL_ES_API);
- if (eglGetError() != EGL_SUCCESS) {
- MP_FATAL(vo, "Couldn't bind GLES API\n");
- goto fail;
+static void egl_window_surface_destroy(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ if (p->egl_window) {
+ eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
}
+}
- EGLConfig config = select_fb_config_egl(ctx);
- if (!config)
- goto fail;
+static bool egl_window_surface_create(MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
int window_attribs_len = 0;
EGLint *window_attribs = NULL;
EGLint flip_val;
- if (eglGetConfigAttrib(p->egl_display, config,
+ if (eglGetConfigAttrib(p->egl_display, p->egl_config,
EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &flip_val))
{
if (flip_val == EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) {
@@ -323,88 +549,283 @@ static int angle_init(struct MPGLContext *ctx, int flags)
}
}
- // EGL_DIRECT_COMPOSITION_ANGLE enables the use of flip-mode present, which
- // avoids a copy of the video image and lowers vsync jitter, though the
- // extension is only present on Windows 8 and up, and might have subpar
- // behavior with some drivers (Intel? symptom - whole desktop is black for
- // some seconds after spending some minutes in fullscreen and then leaving
- // fullscreen).
- if (p->opts->allow_direct_composition &&
- strstr(exts, "EGL_ANGLE_direct_composition"))
- {
- MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len,
- EGL_DIRECT_COMPOSITION_ANGLE);
- MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_TRUE);
- MP_VERBOSE(vo, "Using DirectComposition.\n");
- }
-
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_NONE);
- p->egl_surface = eglCreateWindowSurface(p->egl_display, config,
- vo_w32_hwnd(vo), window_attribs);
+ p->egl_window = eglCreateWindowSurface(p->egl_display, p->egl_config,
+ vo_w32_hwnd(vo), window_attribs);
talloc_free(window_attribs);
- if (p->egl_surface == EGL_NO_SURFACE) {
- MP_FATAL(ctx->vo, "Could not create EGL surface!\n");
+ if (!p->egl_window) {
+ MP_FATAL(vo, "Could not create EGL surface!\n");
goto fail;
}
- if (!(!p->use_es2 && create_context_egl(ctx, config, 3)) &&
- !create_context_egl(ctx, config, 2))
- {
- MP_FATAL(ctx->vo, "Could not create EGL context!\n");
+ eglMakeCurrent(p->egl_display, p->egl_window, p->egl_window,
+ p->egl_context);
+ return true;
+fail:
+ egl_window_surface_destroy(ctx);
+ return false;
+}
+
+static void angle_uninit(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ DwmEnableMMCSS(FALSE);
+
+ // Uninit the EGL surface implementation that is being used. Note: This may
+ // result in the *_destroy function being called twice since it is also
+ // called when the surface create function fails. This is fine because the
+ // *_destroy functions are idempotent.
+ if (p->dxgi_swapchain)
+ d3d11_swapchain_surface_destroy(ctx);
+ else
+ egl_window_surface_destroy(ctx);
+
+ if (p->egl_context) {
+ eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglDestroyContext(p->egl_display, p->egl_context);
+ }
+ p->egl_context = EGL_NO_CONTEXT;
+
+ // Uninit the EGL device implementation that is being used
+ if (p->d3d11_device)
+ d3d11_device_destroy(ctx);
+ else
+ d3d9_device_destroy(ctx);
+
+ vo_w32_uninit(ctx->vo);
+}
+
+static int GLAPIENTRY angle_swap_interval(int interval)
+{
+ if (!current_ctx)
+ return 0;
+ struct priv *p = current_ctx->priv;
+
+ if (p->dxgi_swapchain) {
+ p->swapinterval = MPCLAMP(interval, 0, 4);
+ return 1;
+ } else {
+ return eglSwapInterval(p->egl_display, interval);
+ }
+}
+
+static void *get_proc_address(const GLubyte *proc_name)
+{
+ return eglGetProcAddress(proc_name);
+}
+
+static int angle_init(struct MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ p->opts = mp_get_config_group(ctx, ctx->global, &angle_conf);
+ struct angle_opts *o = p->opts;
+
+ // DWM MMCSS cargo-cult. The dxinterop backend also does this.
+ DwmEnableMMCSS(TRUE);
+
+ if (!angle_load()) {
+ MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n");
goto fail;
}
- // Configure the underlying Direct3D device
- d3d_init(ctx);
+ // Create the underlying EGL device implementation
+ bool device_ok = false;
+ if ((!device_ok && !o->renderer) || o->renderer == RENDERER_D3D11)
+ device_ok = d3d11_device_create(ctx, flags);
+ if ((!device_ok && !o->renderer) || o->renderer == RENDERER_D3D9)
+ device_ok = d3d9_device_create(ctx, flags);
+ if (!device_ok)
+ goto fail;
- if (strstr(exts, "EGL_NV_post_sub_buffer")) {
- p->eglPostSubBufferNV =
- (PFNEGLPOSTSUBBUFFERNVPROC)eglGetProcAddress("eglPostSubBufferNV");
+ if (!eglInitialize(p->egl_display, NULL, NULL)) {
+ MP_FATAL(vo, "Couldn't initialize EGL\n");
+ return false;
+ }
+
+ if (!vo_w32_init(vo))
+ goto fail;
+
+ const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
+ if (exts)
+ MP_DBG(ctx->vo, "EGL extensions: %s\n", exts);
+
+ if (!mpegl_create_context(p->egl_display, vo->log, flags | VOFLAG_GLES,
+ &p->egl_context, &p->egl_config))
+ {
+ MP_FATAL(vo, "Could not create EGL context!\n");
+ goto fail;
}
+ // Create the underlying EGL surface implementation
+ 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 && o->egl_windowing == -1) || o->egl_windowing == 1)
+ surface_ok = egl_window_surface_create(ctx, flags);
+ if (!surface_ok)
+ goto fail;
+
mpgl_load_functions(ctx->gl, get_proc_address, NULL, vo->log);
- return 0;
+ current_ctx = ctx;
+ ctx->gl->SwapInterval = angle_swap_interval;
+
+ return 0;
fail:
angle_uninit(ctx);
return -1;
}
-static int angle_init_es2(struct MPGLContext *ctx, int flags)
-{
- struct priv *p = ctx->priv;
- p->use_es2 = true;
- if (ctx->vo->probing) {
- MP_VERBOSE(ctx->vo, "Not using this by default.\n");
- return -1;
- }
- return angle_init(ctx, flags);
-}
-
static int angle_reconfig(struct MPGLContext *ctx)
{
vo_w32_config(ctx->vo);
return 0;
}
+static struct mp_image *d3d11_screenshot(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ ID3D11Texture2D *frontbuffer = NULL;
+ ID3D11Texture2D *staging = NULL;
+ struct mp_image *img = NULL;
+ HRESULT hr;
+
+ if (!p->dxgi_swapchain1)
+ 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);
+ if (FAILED(hr))
+ goto done;
+ if (scd.SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL)
+ goto done;
+
+ // Get the last buffer that was presented with Present(). This should be
+ // the n-1th buffer for a swap chain of length n.
+ hr = IDXGISwapChain_GetBuffer(p->dxgi_swapchain, scd.BufferCount - 1,
+ &IID_ID3D11Texture2D, (void**)&frontbuffer);
+ if (FAILED(hr))
+ goto done;
+
+ D3D11_TEXTURE2D_DESC td;
+ ID3D11Texture2D_GetDesc(frontbuffer, &td);
+ if (td.SampleDesc.Count > 1)
+ goto done;
+
+ // Validate the backbuffer format and convert to an mpv IMGFMT
+ enum mp_imgfmt fmt;