From d890e0731c0a0c002c32e8be89130e178059d3ba Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 20 Jan 2017 13:38:28 +0100 Subject: options: refactor how --opengl-dcomposition is declared vo_opengl used to have it as sub-option, which made it very hard to pass down option values to backends in a generic way (even if these options were completely backend-specific). For --opengl-dcomposition we used a VOFLAG to deal with this. Fortunately, sub-options are gone, and we can just add it as global option. Move the option to context_angle.c and add it as global option. I thought about adding a mechanism to let backends declare options, which would get magically picked up my m_config instead of having to add them to the global option list manually (similar to VO vo_driver.options), but decided against this complexity just for 1 or 2 backends. Likewise, it could have been added as a single option to avoid the boilerplate of an option struct, but then again there are probably going to be more angle suboptions, and it's cleaner. --- video/out/opengl/context_angle.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'video/out/opengl/context_angle.c') diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index 44aed340e3..5af18e9928 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -24,6 +24,7 @@ #include "angle_dynamic.h" #include "common/common.h" +#include "options/m_config.h" #include "video/out/w32_common.h" #include "context.h" @@ -36,6 +37,22 @@ // Windows 8 enum value, not present in mingw-w64 headers #define DXGI_ADAPTER_FLAG_SOFTWARE (2) +struct angle_opts { + int allow_direct_composition; +}; + +#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), + {0} + }, + .defaults = &(const struct angle_opts) { + .allow_direct_composition = 1, + }, + .size = sizeof(struct angle_opts), +}; + struct priv { EGLDisplay egl_display; EGLContext egl_context; @@ -43,6 +60,7 @@ struct priv { bool use_es2; bool sw_adapter_msg_shown; PFNEGLPOSTSUBBUFFERNVPROC eglPostSubBufferNV; + struct angle_opts *opts; }; static void angle_uninit(MPGLContext *ctx) @@ -217,6 +235,8 @@ 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); + if (!angle_load()) { MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n"); goto fail; @@ -309,7 +329,7 @@ static int angle_init(struct MPGLContext *ctx, int flags) // 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) && + if (p->opts->allow_direct_composition && strstr(exts, "EGL_ANGLE_direct_composition")) { MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, -- cgit v1.2.3 From e0250b9604b24f32d53b409d8c48d16faa2caebc Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Sat, 4 Feb 2017 19:16:02 +1100 Subject: 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.) --- video/out/opengl/context_angle.c | 871 ++++++++++++++++++++++++++++----------- 1 file changed, 641 insertions(+), 230 deletions(-) (limited to 'video/out/opengl/context_angle.c') 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 #include #include -#include +#include +#include #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; + 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_OMGetRenderTargets(p->d3d11_context, + MP_ARRAY_SIZE(rtvs), rtvs, &dsv); + + HRESULT hr = IDXGISwapChain_Present(p->dxgi_swapchain, p->swapinterval, 0); + if (FAILED(hr)) + MP_FATAL(ctx->vo, "Couldn't present: %s\n", mp_HRESULT_to_str(hr)); + + // Restore the RTVs and release the objects + ID3D11DeviceContext_OMSetRenderTargets(p->d3d11_context, + MP_ARRAY_SIZE(rtvs), rtvs, dsv); + for (int i = 0; i < 8; i++) + SAFE_RELEASE(rtvs[i]); + SAFE_RELEASE(dsv); +} + +static void egl_swap_buffers(MPGLContext *ctx) +{ + struct priv *p = ctx->priv; + eglSwapBuffers(p->egl_display, p->egl_window); +} + static void angle_swap_buffers(MPGLContext *ctx) { struct priv *p = ctx->priv; - eglSwapBuffers(p->egl_display, p->egl_surface); + if (p->dxgi_swapchain) + d3d11_swap_buffers(ctx); + else + egl_swap_buffers(ctx); } const struct mpgl_driver mpgl_driver_angle = { @@ -416,13 +837,3 @@ const struct mpgl_driver mpgl_driver_angle = { .control = angle_control, .uninit = angle_uninit, }; - -const struct mpgl_driver mpgl_driver_angle_es2 = { - .name = "angle-es2", - .priv_size = sizeof(struct priv), - .init = angle_init_es2, - .reconfig = angle_reconfig, - .swap_buffers = angle_swap_buffers, - .control = angle_control, - .uninit = angle_uninit, -}; -- cgit v1.2.3 From 8bc335e3db2434ef594e49e8ddff3d6c89a6a009 Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Sun, 12 Feb 2017 02:08:15 +1100 Subject: vo_opengl: angle: log the device/surface implementation This should be useful for debugging, since otherwise it's hard to tell which implementation has been auto-detected or if any failed to init. --- video/out/opengl/context_angle.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'video/out/opengl/context_angle.c') diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index 9ea7fb43bb..7a011a80a4 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -104,6 +104,7 @@ struct priv { ID3D11Device *d3d11_device; ID3D11DeviceContext *d3d11_context; ID3D11Texture2D *d3d11_backbuffer; + D3D_FEATURE_LEVEL d3d11_level; EGLConfig egl_config; EGLDisplay egl_display; @@ -260,7 +261,6 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) 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 @@ -273,13 +273,13 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) 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, + 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, &selected_level, + level_count, D3D11_SDK_VERSION, &p->d3d11_device, &p->d3d11_level, &p->d3d11_context); if (SUCCEEDED(hr)) show_sw_adapter_msg(ctx); @@ -290,10 +290,6 @@ static bool d3d11_device_create(MPGLContext *ctx, int flags) return false; } - MP_VERBOSE(vo, "Using Direct3D 11 feature level %u_%u\n", - ((unsigned)selected_level) >> 12, - (((unsigned)selected_level) >> 8) & 0xf); - hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_IDXGIDevice1, (void**)&p->dxgi_device); if (FAILED(hr)) { @@ -452,12 +448,10 @@ static bool d3d11_swapchain_surface_create(MPGLContext *ctx, int flags) // 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; } @@ -634,10 +628,19 @@ static int angle_init(struct MPGLContext *ctx, int flags) // Create the underlying EGL device implementation bool device_ok = false; - if ((!device_ok && !o->renderer) || o->renderer == RENDERER_D3D11) + 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) + 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; @@ -662,10 +665,21 @@ static int angle_init(struct MPGLContext *ctx, int flags) // Create the underlying EGL surface implementation bool surface_ok = false; - if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 0) + 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) + 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; -- cgit v1.2.3