summaryrefslogtreecommitdiffstats
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
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.)
-rw-r--r--DOCS/man/options.rst68
-rw-r--r--osdep/windows_utils.c8
-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
-rw-r--r--wscript3
7 files changed, 724 insertions, 240 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 3c205b9da5..13fd4351ca 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -4331,16 +4331,70 @@ The following video options are currently all specific to ``--vo=opengl`` and
Windows only.
-``--opengl-dcomposition=<yes|no>``
- Allows DirectComposition when using the ANGLE backend (default: yes).
- DirectComposition implies flip-model presentation, which can improve
- rendering efficiency on Windows 8+ by avoiding a copy of the video frame.
- mpv uses it by default where possible, but it can cause poor behaviour with
- some drivers, such as a black screen or graphical corruption when leaving
- full-screen mode. Use "no" to disable it.
+``--angle-d3d11-feature-level=<11_0|10_1|10_0|9_3>``
+ Selects a specific feature level when using the ANGLE backend with D3D11.
+ By default, the highest available feature level is used. This option can be
+ used to select a lower feature level, which is mainly useful for debugging.
+ Note that OpenGL ES 3.0 is only supported at feature level 10_1 or higher.
+ Most extended OpenGL features will not work at lower feature levels
+ (similar to ``--opengl-dumb-mode``).
Windows with ANGLE only.
+``--angle-d3d11-warp=<yes|no|auto>``
+ Use WARP (Windows Advanced Rasterization Platform) when using the ANGLE
+ backend with D3D11 (default: auto). This is a high performance software
+ renderer. By default, it is used when the Direct3D hardware does not
+ support Direct3D 11 feature level 9_3. While the extended OpenGL features
+ will work with WARP, they can be very slow.
+
+ Windows with ANGLE only.
+
+``--angle-egl-windowing=<yes|no|auto>``
+ Use ANGLE's built in EGL windowing functions to create a swap chain
+ (default: auto). If this is set to ``no`` and the D3D11 renderer is in use,
+ ANGLE's built in swap chain will not be used and a custom swap chain that
+ is optimized for video rendering will be created instead. If set to
+ ``auto``, a custom swap chain will be used for D3D11 and the built in swap
+ chain will be used for D3D9. This option is mainly for debugging purposes,
+ in case the custom swap chain has poor performance or does not work.
+
+ If set to ``yes``, the ``--angle-max-frame-latency`` and
+ ``--angle-swapchain-length`` options will have no effect.
+
+ Windows with ANGLE only.
+
+``--angle-max-frame-latency=<1-16>``
+ Sets the maximum number of frames that the system is allowed to queue for
+ rendering with the ANGLE backend (default: 3). Lower values should make
+ VSync timing more accurate, but a value of ``1`` requires powerful
+ hardware, since the CPU will not be able to "render ahead" of the GPU.
+
+ Windows with ANGLE only.
+
+``--angle-renderer=<d3d9|d3d11|auto>``
+ Forces a specific renderer when using the ANGLE backend (default: auto). In
+ auto mode this will pick D3D11 for systems that support Direct3D 11 feature
+ level 9_3 or higher, and D3D9 otherwise. This option is mainly for
+ debugging purposes. Normally there is no reason to force a specific
+ renderer, though ``--angle-renderer=d3d9`` may give slightly better
+ performance on old hardware. Note that the D3D9 renderer only supports
+ OpenGL ES 2.0, so most extended OpenGL features will not work if this
+ renderer is selected (similar to ``--opengl-dumb-mode``).
+
+ Windows with ANGLE only.
+
+``--angle-swapchain-length=<2-16>``
+ Sets the number of buffers in the D3D11 presentation queue when using the
+ ANGLE backend (default: 6). At least 2 are required, since one is the back
+ buffer that mpv renders to and the other is the front buffer that is
+ presented by the DWM. Additional buffers can improve performance, because
+ for example, mpv will not have to wait on the DWM to release the front
+ buffer before rendering a new frame to it. For this reason, Microsoft
+ recommends at least 4.
+
+ Windows 8+ with ANGLE only.
+
``--opengl-sw``
Continue even if a software renderer is detected.
diff --git a/osdep/windows_utils.c b/osdep/windows_utils.c
index a1ea32191a..a60eba3d26 100644
--- a/osdep/windows_utils.c
+++ b/osdep/windows_utils.c
@@ -22,6 +22,7 @@
#include <errors.h>
#include <audioclient.h>
#include <d3d9.h>
+#include <dxgi1_2.h>
#include "windows_utils.h"
@@ -118,6 +119,13 @@ static char *hresult_to_str(const HRESULT hr)
E(D3DERR_CANNOTPROTECTCONTENT)
E(D3DERR_UNSUPPORTEDCRYPTO)
E(D3DERR_PRESENT_STATISTICS_DISJOINT)
+ E(DXGI_ERROR_DEVICE_HUNG)
+ E(DXGI_ERROR_DEVICE_REMOVED)
+ E(DXGI_ERROR_DEVICE_RESET)
+ E(DXGI_ERROR_DRIVER_INTERNAL_ERROR)
+ E(DXGI_ERROR_INVALID_CALL)
+ E(DXGI_ERROR_WAS_STILL_DRAWING)
+ E(DXGI_STATUS_OCCLUDED)
default:
return "<Unknown>";
}
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 c