summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/context_angle.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/context_angle.c')
-rw-r--r--video/out/opengl/context_angle.c897
1 files changed, 671 insertions, 226 deletions
diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c
index 44aed340e3..7a011a80a4 100644
--- a/video/out/opengl/context_angle.c
+++ b/video/out/opengl/context_angle.c
@@ -19,14 +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
@@ -36,79 +43,190 @@
// 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 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_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) {
+ .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;
+ D3D_FEATURE_LEVEL d3d11_level;
+
+ 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;
- EGLint attributes[] = {
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_NONE
+ 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 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;
+ }
- EGLint config_count;
- EGLConfig config;
+ eglMakeCurrent(p->egl_display, p->egl_backbuffer, p->egl_backbuffer,
+ p->egl_context);
+ return true;
+}
- eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
+static void d3d11_backbuffer_resize(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+ HRESULT hr;
- if (!config_count) {
- MP_FATAL(ctx->vo, "Could find EGL configuration!\n");
- return NULL;
- }
+ int old_sc_width = p->sc_width;
+ int old_sc_height = p->sc_height;
+
+ update_sizes(ctx);
+ // Avoid unnecessary resizing
+ if (old_sc_width == p->sc_width && old_sc_height == p->sc_height)
+ return;
+
+ // 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));
- return config;
+ 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
- };
-
- p->egl_context = eglCreateContext(p->egl_display, config,
- EGL_NO_CONTEXT, context_attributes);
+ PFNEGLRELEASEDEVICEANGLEPROC eglReleaseDeviceANGLE =
+ (PFNEGLRELEASEDEVICEANGLEPROC)eglGetProcAddress("eglReleaseDeviceANGLE");
- 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)
@@ -117,180 +235,302 @@ 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,
+ };
+ int level_count = 4;
- hr = ID3D11Device_QueryInterface(d3d11_dev, &IID_IDXGIDevice,
- (void**)&dxgi_dev);
- if (FAILED(hr)) {
- MP_ERR(vo, "Device is not a IDXGIDevice\n");
- goto done;
- }
+ // 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 = IDXGIDevice_GetAdapter(dxgi_dev, &dxgi_adapter);
- if (FAILED(hr)) {
- MP_ERR(vo, "Couldn't get IDXGIAdapter\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, &p->d3d11_level,
+ &p->d3d11_context);
+ }
+ // Try WARP if it is forced or if the HW adapter failed
+ if ((FAILED(hr) && o->d3d11_warp == -1) || o->d3d11_warp == 1) {
+ hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, levels,
+ level_count, D3D11_SDK_VERSION, &p->d3d11_device, &p->d3d11_level,
+ &p->d3d11_context);
+ if (SUCCEEDED(hr))
+ show_sw_adapter_msg(ctx);
+ }
+ if (FAILED(hr)) {
+ MP_FATAL(vo, "Couldn't create Direct3D 11 device: %s\n",
+ mp_HRESULT_to_str(hr));
+ return false;
+ }
- // 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;
- if (!angle_load()) {
- MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n");
- goto fail;
+ 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,
+ };
+
+ 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;
+ } else if (p->dxgi_factory) {
+ // Fall back to DXGI 1.1 (Windows 7)
+ if (!d3d11_swapchain_create_1_1(ctx, flags))
+ goto fail;
+ } 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) {
@@ -303,88 +543,303 @@ 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 ((flags & VOFLAG_ANGLE_DCOMP) &&
- 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;
+ }
+
+ // 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) {
+ MP_VERBOSE(vo, "Using Direct3D 11 feature level %u_%u\n",
+ ((unsigned)p->d3d11_level) >> 12,
+ (((unsigned)p->d3d11_level) >> 8) & 0xf);
+ }
+ }
+ if ((!device_ok && !o->renderer) || o->renderer == RENDERER_D3D9) {
+ device_ok = d3d9_device_create(ctx, flags);
+ if (device_ok)
+ MP_VERBOSE(vo, "Using Direct3D 9\n");
+ }
+ if (!device_ok)
goto fail;
+
+ if (!eglInitialize(p->egl_display, NULL, NULL)) {
+ MP_FATAL(vo, "Couldn't initialize EGL\n");
+ return false;
}
- // Configure the underlying Direct3D device
- d3d_init(ctx);
+ 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 (strstr(exts, "EGL_NV_post_sub_buffer")) {
- p->eglPostSubBufferNV =
- (PFNEGLPOSTSUBBUFFERNVPROC)eglGetProcAddress("eglPostSubBufferNV");
+ 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) {
+ if (p->dxgi_swapchain1) {
+ MP_VERBOSE(vo, "Using DXGI 1.2+\n");
+ } else {
+ MP_VERBOSE(vo, "Using DXGI 1.1\n");
+ }
+ }
+ }
+ if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 1) {
+ surface_ok = egl_window_surface_create(ctx, flags);
+ if (surface_ok)
+ MP_VERBOSE(vo, "Using EGL windowing\n");
+ }
+ 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;
+ switch (td.Format) {
+ case DXGI_FORMAT_B8G8R8A8_UNORM: fmt = IMGFMT_BGR0; break;
+ case DXGI_FORMAT_R8G8B8A8_UNORM: fmt = IMGFMT_RGB0; break;
+ default:
+ goto done;
+ }
+
+ // Create a staging texture based on the frontbuffer with CPU access
+ td.BindFlags = 0;
+ td.MiscFlags = 0;
+ td.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ td.Usage = D3D11_USAGE_STAGING;
+ hr = ID3D11Device_CreateTexture2D(p->d3d11_device, &td, 0, &staging);
+ if (FAILED(hr))
+ goto done;
+
+ ID3D11DeviceContext_CopyResource(p->d3d11_context,
+ (ID3D11Resource*)staging, (ID3D11Resource*)frontbuffer);
+
+ // Attempt to map the staging texture to CPU-accessible memory
+ D3D11_MAPPED_SUBRESOURCE lock;
+ hr = ID3D11DeviceContext_Map(p->d3d11_context, (ID3D11Resource*)staging,
+ 0, D3D11_MAP_READ, 0, &lock);
+ if (FAILED(hr))
+ goto done;
+
+ img = mp_image_alloc(fmt, td.Width, td.Height);
+ if (!img)
+ return NULL;
+ for (int i = 0; i < td.Height; i++) {
+ memcpy(img->planes[0] + img->stride[0] * i,
+ (char*)lock.pData + lock.RowPitch * i, td.Width * 4);
+ }
+
+ ID3D11DeviceContext_Unmap(p->d3d11_context, (ID3D11Resource*)staging, 0);
+
+done:
+ SAFE_RELEASE(frontbuffer);
+ SAFE_RELEASE(staging);
+ return img;
+}
+
static int angle_control(MPGLContext *ctx, int *events, int request, void *arg)
{
struct priv *p = ctx->priv;
- int r = vo_w32_control(ctx->vo, events, request, arg);
- // Calling eglPostSubBufferNV with a 0-sized region doesn't present a frame
- // or block, but it does update the swapchain to match the window size
- // See: https://groups.google.com/d/msg/angleproject/RvyVkjRCQGU/gfKfT64IAgAJ
- if ((*events & VO_EVENT_RESIZE) && p->eglPostSubBufferNV)
- p->eglPostSubBufferNV(p->egl_display, p->egl_surface, 0, 0, 0, 0);
+ // Try a D3D11-specific method of taking a window screenshot
+ if (request == VOCTRL_SCREENSHOT_WIN) {
+ struct mp_image *img = d3d11_screenshot(ctx);
+ if (img) {
+ *(struct mp_image **)arg = img;
+ return true;
+ }
+ }
+ int r = vo_w32_control(ctx->vo, events, request, arg);
+ if (*events & VO_EVENT_RESIZE) {
+ if (p->dxgi_swapchain)
+ d3d11_backbuffer_resize(ctx);
+ else
+ eglWaitClient(); // Should get ANGLE to resize its swapchain
+ }
return r;
}
+static void d3d11_swap_buffers(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ // Calling Present() on a flip-sequential swap chain will silently change
+ // the underlying storage of the back buffer to point to the next buffer in
+ // the chain. This results in the RTVs for the back buffer becoming
+ // unbound. Since ANGLE doesn't know we called Present(), it will continue
+ // using the unbound RTVs, so we must save and restore them ourselves.
+ ID3D11RenderTargetView *rtvs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {0};
+ ID3D11DepthStencilView *dsv = NULL;
+ ID3D11DeviceContext_OM