2015-11-13 13:04:30 +00:00
|
|
|
/*
|
|
|
|
* This file is part of mpv.
|
|
|
|
*
|
2016-01-19 17:36:34 +00:00
|
|
|
* mpv is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2015-11-13 13:04:30 +00:00
|
|
|
*
|
|
|
|
* mpv is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2016-01-19 17:36:34 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2015-11-13 13:04:30 +00:00
|
|
|
*
|
2016-01-19 17:36:34 +00:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
2015-11-13 13:04:30 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <EGL/egl.h>
|
|
|
|
#include <EGL/eglext.h>
|
2016-05-30 09:40:10 +00:00
|
|
|
#include <d3d11.h>
|
2017-02-04 08:16:02 +00:00
|
|
|
#include <dxgi1_2.h>
|
|
|
|
#include <dwmapi.h>
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2016-05-11 10:33:49 +00:00
|
|
|
#include "angle_dynamic.h"
|
2017-02-04 08:16:02 +00:00
|
|
|
#include "egl_helpers.h"
|
vo_gpu: d3d11: initial implementation
This is a new RA/vo_gpu backend that uses Direct3D 11. The GLSL
generated by vo_gpu is cross-compiled to HLSL with SPIRV-Cross.
What works:
- All of mpv's internal shaders should work, including compute shaders.
- Some external shaders have been tested and work, including RAVU and
adaptive-sharpen.
- Non-dumb mode works, even on very old hardware. Most features work at
feature level 9_3 and all features work at feature level 10_0. Some
features also work at feature level 9_1 and 9_2, but without high-bit-
depth FBOs, it's not very useful. (Hardware this old is probably not
fast enough for advanced features anyway.)
Note: This is more compatible than ANGLE, which requires 9_3 to work
at all (GLES 2.0,) and 10_1 for non-dumb-mode (GLES 3.0.)
- Hardware decoding with D3D11VA, including decoding of 10-bit formats
without truncation to 8-bit.
What doesn't work / can be improved:
- PBO upload and direct rendering does not work yet. Direct rendering
requires persistent-mapped PBOs because the decoder needs to be able
to read data from images that have already been decoded and uploaded.
Unfortunately, it seems like persistent-mapped PBOs are fundamentally
incompatible with D3D11, which requires all resources to use driver-
managed memory and requires memory to be unmapped (and hence pointers
to be invalidated) when a resource is used in a draw or copy
operation.
However it might be possible to use D3D11's limited multithreading
capabilities to emulate some features of PBOs, like asynchronous
texture uploading.
- The blit() and clear() operations don't have equivalents in the D3D11
API that handle all cases, so in most cases, they have to be emulated
with a shader. This is currently done inside ra_d3d11, but ideally it
would be done in generic code, so it can take advantage of mpv's
shader generation utilities.
- SPIRV-Cross is used through a NIH C-compatible wrapper library, since
it does not expose a C interface itself.
The library is available here: https://github.com/rossy/crossc
- The D3D11 context could be made to support more modern DXGI features
in future. For example, it should be possible to add support for
high-bit-depth and HDR output with DXGI 1.5/1.6.
2017-09-07 10:18:06 +00:00
|
|
|
#include "video/out/gpu/d3d11_helpers.h"
|
2016-05-11 10:33:49 +00:00
|
|
|
|
2015-11-13 13:04:30 +00:00
|
|
|
#include "common/common.h"
|
2017-01-20 12:38:28 +00:00
|
|
|
#include "options/m_config.h"
|
2015-11-13 13:04:30 +00:00
|
|
|
#include "video/out/w32_common.h"
|
2017-02-04 08:16:02 +00:00
|
|
|
#include "osdep/windows_utils.h"
|
2015-12-19 11:59:07 +00:00
|
|
|
#include "context.h"
|
2017-09-15 15:37:28 +00:00
|
|
|
#include "utils.h"
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
#ifndef EGL_D3D_TEXTURE_ANGLE
|
|
|
|
#define EGL_D3D_TEXTURE_ANGLE 0x33A3
|
|
|
|
#endif
|
2016-05-05 16:44:41 +00:00
|
|
|
#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
|
|
|
|
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
|
|
|
|
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
|
|
|
|
#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
|
|
|
|
#endif
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
enum {
|
|
|
|
RENDERER_AUTO,
|
|
|
|
RENDERER_D3D9,
|
|
|
|
RENDERER_D3D11,
|
|
|
|
};
|
|
|
|
|
2017-01-20 12:38:28 +00:00
|
|
|
struct angle_opts {
|
2017-02-04 08:16:02 +00:00
|
|
|
int renderer;
|
|
|
|
int d3d11_warp;
|
|
|
|
int d3d11_feature_level;
|
|
|
|
int egl_windowing;
|
2017-03-25 14:49:19 +00:00
|
|
|
int flip;
|
2017-01-20 12:38:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct angle_opts
|
|
|
|
const struct m_sub_options angle_conf = {
|
|
|
|
.opts = (const struct m_option[]) {
|
2017-02-04 08:16:02 +00:00
|
|
|
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})),
|
2017-03-25 14:49:19 +00:00
|
|
|
OPT_FLAG("angle-flip", flip, 0),
|
2017-09-15 15:37:28 +00:00
|
|
|
OPT_REPLACED("angle-max-frame-latency", "swapchain-depth"),
|
|
|
|
OPT_REMOVED("angle-swapchain-length", "controlled by --swapchain-depth"),
|
2017-01-20 12:38:28 +00:00
|
|
|
{0}
|
|
|
|
},
|
|
|
|
.defaults = &(const struct angle_opts) {
|
2017-02-04 08:16:02 +00:00
|
|
|
.renderer = RENDERER_AUTO,
|
|
|
|
.d3d11_warp = -1,
|
|
|
|
.d3d11_feature_level = D3D_FEATURE_LEVEL_11_0,
|
|
|
|
.egl_windowing = -1,
|
2017-03-25 14:49:19 +00:00
|
|
|
.flip = 1,
|
2017-01-20 12:38:28 +00:00
|
|
|
},
|
|
|
|
.size = sizeof(struct angle_opts),
|
|
|
|
};
|
|
|
|
|
2015-11-13 13:04:30 +00:00
|
|
|
struct priv {
|
2017-09-15 15:37:28 +00:00
|
|
|
GL gl;
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
IDXGISwapChain *dxgi_swapchain;
|
|
|
|
|
|
|
|
ID3D11Device *d3d11_device;
|
|
|
|
ID3D11DeviceContext *d3d11_context;
|
|
|
|
ID3D11Texture2D *d3d11_backbuffer;
|
|
|
|
|
|
|
|
EGLConfig egl_config;
|
2015-11-13 13:04:30 +00:00
|
|
|
EGLDisplay egl_display;
|
2017-02-04 08:16:02 +00:00
|
|
|
EGLDeviceEXT egl_device;
|
2015-11-13 13:04:30 +00:00
|
|
|
EGLContext egl_context;
|
2017-02-04 08:16:02 +00:00
|
|
|
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;
|
2017-09-15 15:37:28 +00:00
|
|
|
bool flipped;
|
2017-02-04 08:16:02 +00:00
|
|
|
|
2017-01-20 12:38:28 +00:00
|
|
|
struct angle_opts *opts;
|
2015-11-13 13:04:30 +00:00
|
|
|
};
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static __thread struct ra_ctx *current_ctx;
|
2017-02-04 08:16:02 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void update_sizes(struct ra_ctx *ctx)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-02-04 08:16:02 +00:00
|
|
|
p->sc_width = ctx->vo->dwidth ? ctx->vo->dwidth : 1;
|
|
|
|
p->sc_height = ctx->vo->dheight ? ctx->vo->dheight : 1;
|
|
|
|
}
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d11_backbuffer_release(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
|
|
|
if (p->egl_backbuffer) {
|
2015-11-13 13:04:30 +00:00
|
|
|
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
|
|
EGL_NO_CONTEXT);
|
2017-02-04 08:16:02 +00:00
|
|
|
eglDestroySurface(p->egl_display, p->egl_backbuffer);
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
2017-02-04 08:16:02 +00:00
|
|
|
p->egl_backbuffer = EGL_NO_SURFACE;
|
|
|
|
|
|
|
|
SAFE_RELEASE(p->d3d11_backbuffer);
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool d3d11_backbuffer_get(struct ra_ctx *ctx)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-02-04 08:16:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
EGLint pbuffer_attributes[] = {
|
|
|
|
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
|
|
|
|
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
|
|
|
|
EGL_NONE,
|
2015-11-13 13:04:30 +00:00
|
|
|
};
|
2017-02-04 08:16:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d11_backbuffer_resize(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
|
|
|
HRESULT hr;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
int old_sc_width = p->sc_width;
|
|
|
|
int old_sc_height = p->sc_height;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
update_sizes(ctx);
|
|
|
|
// Avoid unnecessary resizing
|
|
|
|
if (old_sc_width == p->sc_width && old_sc_height == p->sc_height)
|
|
|
|
return;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
// 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");
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d11_device_destroy(struct ra_ctx *ctx)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
PFNEGLRELEASEDEVICEANGLEPROC eglReleaseDeviceANGLE =
|
|
|
|
(PFNEGLRELEASEDEVICEANGLEPROC)eglGetProcAddress("eglReleaseDeviceANGLE");
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
if (p->egl_display)
|
|
|
|
eglTerminate(p->egl_display);
|
|
|
|
p->egl_display = EGL_NO_DISPLAY;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
if (p->egl_device && eglReleaseDeviceANGLE)
|
|
|
|
eglReleaseDeviceANGLE(p->egl_device);
|
|
|
|
p->egl_device = 0;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
SAFE_RELEASE(p->d3d11_device);
|
2016-07-11 12:23:00 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool d3d11_device_create(struct ra_ctx *ctx)
|
2016-05-30 09:40:10 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
2017-02-04 08:16:02 +00:00
|
|
|
struct angle_opts *o = p->opts;
|
|
|
|
|
2017-08-15 13:39:55 +00:00
|
|
|
struct d3d11_device_opts device_opts = {
|
|
|
|
.allow_warp = o->d3d11_warp != 0,
|
|
|
|
.force_warp = o->d3d11_warp == 1,
|
|
|
|
.max_feature_level = o->d3d11_feature_level,
|
|
|
|
.min_feature_level = D3D_FEATURE_LEVEL_9_3,
|
2017-09-15 15:37:28 +00:00
|
|
|
.max_frame_latency = ctx->opts.swapchain_depth,
|
2017-02-04 08:16:02 +00:00
|
|
|
};
|
2017-08-15 13:39:55 +00:00
|
|
|
if (!mp_d3d11_create_present_device(vo->log, &device_opts, &p->d3d11_device))
|
2017-02-04 08:16:02 +00:00
|
|
|
return false;
|
2017-08-15 13:39:55 +00:00
|
|
|
ID3D11Device_GetImmediateContext(p->d3d11_device, &p->d3d11_context);
|
2017-02-04 08:16:02 +00:00
|
|
|
|
|
|
|
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;
|
2016-05-30 09:40:10 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d11_swapchain_surface_destroy(struct ra_ctx *ctx)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
2017-02-04 08:16:02 +00:00
|
|
|
struct priv *p = ctx->priv;
|
2017-08-15 13:39:55 +00:00
|
|
|
|
|
|
|
bool had_swapchain = p->dxgi_swapchain;
|
2017-02-04 08:16:02 +00:00
|
|
|
SAFE_RELEASE(p->dxgi_swapchain);
|
|
|
|
d3d11_backbuffer_release(ctx);
|
2017-01-20 12:38:28 +00:00
|
|
|
|
2017-08-15 13:39:55 +00:00
|
|
|
// Ensure the swapchain is destroyed by flushing the D3D11 immediate
|
|
|
|
// context. This is needed because the HWND may be reused. See:
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ff476425.aspx
|
|
|
|
if (had_swapchain && p->d3d11_context)
|
|
|
|
ID3D11DeviceContext_Flush(p->d3d11_context);
|
2017-02-04 08:16:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool d3d11_swapchain_surface_create(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
2017-08-15 13:39:55 +00:00
|
|
|
struct angle_opts *o = p->opts;
|
2017-02-04 08:16:02 +00:00
|
|
|
|
2017-08-15 13:39:55 +00:00
|
|
|
if (!p->d3d11_device)
|
2015-11-13 13:04:30 +00:00
|
|
|
goto fail;
|
2017-02-04 08:16:02 +00:00
|
|
|
|
2017-08-15 13:39:55 +00:00
|
|
|
update_sizes(ctx);
|
|
|
|
struct d3d11_swapchain_opts swapchain_opts = {
|
|
|
|
.window = vo_w32_hwnd(vo),
|
|
|
|
.width = p->sc_width,
|
|
|
|
.height = p->sc_height,
|
|
|
|
.flip = o->flip,
|
2017-09-15 15:37:28 +00:00
|
|
|
// Add one frame for the backbuffer and one frame of "slack" to reduce
|
|
|
|
// contention with the window manager when acquiring the backbuffer
|
|
|
|
.length = ctx->opts.swapchain_depth + 2,
|
2017-08-15 13:39:55 +00:00
|
|
|
.usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT,
|
|
|
|
};
|
|
|
|
if (!mp_d3d11_create_swapchain(p->d3d11_device, vo->log, &swapchain_opts,
|
|
|
|
&p->dxgi_swapchain))
|
|
|
|
goto fail;
|
2017-02-04 08:16:02 +00:00
|
|
|
if (!d3d11_backbuffer_get(ctx))
|
|
|
|
goto fail;
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
p->flipped = true;
|
2017-02-04 08:16:02 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
d3d11_swapchain_surface_destroy(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d9_device_destroy(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
|
|
|
if (p->egl_display)
|
|
|
|
eglTerminate(p->egl_display);
|
|
|
|
p->egl_display = EGL_NO_DISPLAY;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool d3d9_device_create(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
|
|
|
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
|
|
|
|
(PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
|
|
|
if (!eglGetPlatformDisplayEXT) {
|
|
|
|
MP_FATAL(vo, "Missing EGL_EXT_platform_base\n");
|
2017-02-04 08:16:02 +00:00
|
|
|
return false;
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
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);
|
2015-11-13 13:04:30 +00:00
|
|
|
if (p->egl_display == EGL_NO_DISPLAY) {
|
|
|
|
MP_FATAL(vo, "Couldn't get display\n");
|
2017-02-04 08:16:02 +00:00
|
|
|
return false;
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-05-28 15:21:29 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void egl_window_surface_destroy(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
if (p->egl_window) {
|
|
|
|
eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
|
|
EGL_NO_CONTEXT);
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
2017-02-04 08:16:02 +00:00
|
|
|
}
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool egl_window_surface_create(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2016-05-28 15:21:29 +00:00
|
|
|
int window_attribs_len = 0;
|
2016-05-05 16:44:41 +00:00
|
|
|
EGLint *window_attribs = NULL;
|
2016-05-28 15:21:29 +00:00
|
|
|
|
2016-05-05 16:44:41 +00:00
|
|
|
EGLint flip_val;
|
2017-02-04 08:16:02 +00:00
|
|
|
if (eglGetConfigAttrib(p->egl_display, p->egl_config,
|
2016-05-05 16:44:41 +00:00
|
|
|
EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &flip_val))
|
|
|
|
{
|
|
|
|
if (flip_val == EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE) {
|
2016-05-28 15:21:29 +00:00
|
|
|
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len,
|
|
|
|
EGL_SURFACE_ORIENTATION_ANGLE);
|
|
|
|
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len,
|
|
|
|
EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
|
2017-09-15 15:37:28 +00:00
|
|
|
p->flipped = true;
|
2016-05-05 16:44:41 +00:00
|
|
|
MP_VERBOSE(vo, "Rendering flipped.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-28 15:21:29 +00:00
|
|
|
MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_NONE);
|
2017-02-04 08:16:02 +00:00
|
|
|
p->egl_window = eglCreateWindowSurface(p->egl_display, p->egl_config,
|
|
|
|
vo_w32_hwnd(vo), window_attribs);
|
2016-05-28 15:21:29 +00:00
|
|
|
talloc_free(window_attribs);
|
2017-02-04 08:16:02 +00:00
|
|
|
if (!p->egl_window) {
|
|
|
|
MP_FATAL(vo, "Could not create EGL surface!\n");
|
2015-11-21 19:29:07 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
eglMakeCurrent(p->egl_display, p->egl_window, p->egl_window,
|
|
|
|
p->egl_context);
|
|
|
|
return true;
|
|
|
|
fail:
|
|
|
|
egl_window_surface_destroy(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void context_destroy(struct ra_ctx *ctx)
|
2017-05-16 11:10:09 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool context_init(struct ra_ctx *ctx)
|
2017-05-16 11:10:09 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
struct vo *vo = ctx->vo;
|
|
|
|
|
|
|
|
if (!eglInitialize(p->egl_display, NULL, NULL)) {
|
|
|
|
MP_FATAL(vo, "Couldn't initialize EGL\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
|
|
|
|
if (exts)
|
|
|
|
MP_DBG(vo, "EGL extensions: %s\n", exts);
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
if (!mpegl_create_context(ctx, p->egl_display, &p->egl_context,
|
|
|
|
&p->egl_config))
|
2017-05-16 11:10:09 +00:00
|
|
|
{
|
|
|
|
MP_FATAL(vo, "Could not create EGL context!\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
fail:
|
|
|
|
context_destroy(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void angle_uninit(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
ra_gl_ctx_uninit(ctx);
|
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
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);
|
|
|
|
|
2017-05-16 11:10:09 +00:00
|
|
|
context_destroy(ctx);
|
2017-02-04 08:16:02 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d11_swap_buffers(struct ra_ctx *ctx)
|
2017-02-04 08:16:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
|
|
|
|
// 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 < MP_ARRAY_SIZE(rtvs); i++)
|
|
|
|
SAFE_RELEASE(rtvs[i]);
|
|
|
|
SAFE_RELEASE(dsv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void egl_swap_buffers(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
eglSwapBuffers(p->egl_display, p->egl_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void angle_swap_buffers(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
if (p->dxgi_swapchain)
|
|
|
|
d3d11_swap_buffers(ctx);
|
|
|
|
else
|
|
|
|
egl_swap_buffers(ctx);
|
|
|
|
}
|
|
|
|
|
2017-09-22 11:38:05 +00:00
|
|
|
|
|
|
|
static int angle_color_depth(struct ra_swapchain *sw)
|
|
|
|
{
|
|
|
|
// Only 8-bit output is supported at the moment
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool angle_submit_frame(struct ra_swapchain *sw,
|
|
|
|
const struct vo_frame *frame)
|
|
|
|
{
|
|
|
|
struct priv *p = sw->ctx->priv;
|
|
|
|
bool ret = ra_gl_ctx_submit_frame(sw, frame);
|
|
|
|
if (p->d3d11_context) {
|
|
|
|
// DXGI Present doesn't flush the immediate context, which can make
|
|
|
|
// timers inaccurate, since the end queries might not be sent until the
|
|
|
|
// next frame. Fix this by flushing the context now.
|
|
|
|
ID3D11DeviceContext_Flush(p->d3d11_context);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool angle_init(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
2017-02-04 08:16:02 +00:00
|
|
|
struct vo *vo = ctx->vo;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2017-02-04 08:16:02 +00:00
|
|
|
|
|
|
|
p->opts = mp_get_config_group(ctx, ctx->global, &angle_conf);
|
|
|
|
struct angle_opts *o = p->opts;
|
|
|
|
|
|
|
|
if (!angle_load()) {
|
|
|
|
MP_VERBOSE(vo, "Failed to load LIBEGL.DLL\n");
|
2015-11-13 13:04:30 +00:00
|
|
|
goto fail;
|
2015-11-21 19:29:07 +00:00
|
|
|
}
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
// Create the underlying EGL device implementation
|
2017-05-16 11:10:09 +00:00
|
|
|
bool context_ok = false;
|
|
|
|
if ((!context_ok && !o->renderer) || o->renderer == RENDERER_D3D11) {
|
2017-09-15 15:37:28 +00:00
|
|
|
context_ok = d3d11_device_create(ctx);
|
2017-05-16 11:10:09 +00:00
|
|
|
if (context_ok) {
|
2017-09-15 15:37:28 +00:00
|
|
|
context_ok = context_init(ctx);
|
2017-05-16 11:10:09 +00:00
|
|
|
if (!context_ok)
|
|
|
|
d3d11_device_destroy(ctx);
|
2017-02-11 15:08:15 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-16 11:10:09 +00:00
|
|
|
if ((!context_ok && !o->renderer) || o->renderer == RENDERER_D3D9) {
|
2017-09-15 15:37:28 +00:00
|
|
|
context_ok = d3d9_device_create(ctx);
|
2017-05-16 11:10:09 +00:00
|
|
|
if (context_ok) {
|
2017-02-11 15:08:15 +00:00
|
|
|
MP_VERBOSE(vo, "Using Direct3D 9\n");
|
2016-05-30 09:40:10 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
context_ok = context_init(ctx);
|
2017-05-16 11:10:09 +00:00
|
|
|
if (!context_ok)
|
|
|
|
d3d9_device_destroy(ctx);
|
|
|
|
}
|
2017-02-04 08:16:02 +00:00
|
|
|
}
|
2017-05-16 11:10:09 +00:00
|
|
|
if (!context_ok)
|
2017-02-04 08:16:02 +00:00
|
|
|
goto fail;
|
|
|
|
|
2017-05-16 11:10:09 +00:00
|
|
|
if (!vo_w32_init(vo))
|
2017-02-04 08:16:02 +00:00
|
|
|
goto fail;
|
2016-07-04 11:38:36 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
// Create the underlying EGL surface implementation
|
|
|
|
bool surface_ok = false;
|
2017-02-11 15:08:15 +00:00
|
|
|
if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 0) {
|
2017-09-15 15:37:28 +00:00
|
|
|
surface_ok = d3d11_swapchain_surface_create(ctx);
|
2017-02-11 15:08:15 +00:00
|
|
|
}
|
|
|
|
if ((!surface_ok && o->egl_windowing == -1) || o->egl_windowing == 1) {
|
2017-09-15 15:37:28 +00:00
|
|
|
surface_ok = egl_window_surface_create(ctx);
|
2017-02-11 15:08:15 +00:00
|
|
|
if (surface_ok)
|
|
|
|
MP_VERBOSE(vo, "Using EGL windowing\n");
|
|
|
|
}
|
2017-02-04 08:16:02 +00:00
|
|
|
if (!surface_ok)
|
|
|
|
goto fail;
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
mpegl_load_functions(gl, vo->log);
|
2015-11-13 13:04:30 +00:00
|
|
|
|
2017-02-04 08:16:02 +00:00
|
|
|
current_ctx = ctx;
|
2017-09-15 15:37:28 +00:00
|
|
|
gl->SwapInterval = angle_swap_interval;
|
|
|
|
|
2017-09-22 11:38:05 +00:00
|
|
|
// Custom swapchain impl for the D3D11 swapchain-based surface
|
2017-09-23 08:33:33 +00:00
|
|
|
static const struct ra_swapchain_fns dxgi_swapchain_fns = {
|
2017-09-22 11:38:05 +00:00
|
|
|
.color_depth = angle_color_depth,
|
|
|
|
.submit_frame = angle_submit_frame,
|
|
|
|
};
|
2017-09-15 15:37:28 +00:00
|
|
|
struct ra_gl_ctx_params params = {
|
|
|
|
.swap_buffers = angle_swap_buffers,
|
|
|
|
.flipped = p->flipped,
|
2017-09-23 08:33:33 +00:00
|
|
|
.external_swapchain = p->dxgi_swapchain ? &dxgi_swapchain_fns : NULL,
|
2017-09-15 15:37:28 +00:00
|
|
|
};
|
2017-02-04 08:16:02 +00:00
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
if (!ra_gl_ctx_init(ctx, gl, params))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
DwmEnableMMCSS(TRUE); // DWM MMCSS cargo-cult. The dxgl backend also does this.
|
|
|
|
|
|
|
|
return true;
|
2015-11-13 13:04:30 +00:00
|
|
|
fail:
|
|
|
|
angle_uninit(ctx);
|
2017-09-15 15:37:28 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resize(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
if (p->dxgi_swapchain)
|
|
|
|
d3d11_backbuffer_resize(ctx);
|
|
|
|
else
|
|
|
|
eglWaitClient(); // Should get ANGLE to resize its swapchain
|
|
|
|
ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, 0);
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool angle_reconfig(struct ra_ctx *ctx)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
|
|
|
vo_w32_config(ctx->vo);
|
2017-09-15 15:37:28 +00:00
|
|
|
resize(ctx);
|
|
|
|
return true;
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static int angle_control(struct ra_ctx *ctx, int *events, int request, void *arg)
|
2015-11-13 13:04:30 +00:00
|
|
|
{
|
2017-09-15 15:37:28 +00:00
|
|
|
int ret = vo_w32_control(ctx->vo, events, request, arg);
|
|
|
|
if (*events & VO_EVENT_RESIZE)
|
|
|
|
resize(ctx);
|
|
|
|
return ret;
|
2015-11-13 13:04:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
const struct ra_ctx_fns ra_ctx_angle = {
|
|
|
|
.type = "opengl",
|
2015-11-13 13:04:30 +00:00
|
|
|
.name = "angle",
|
|
|
|
.init = angle_init,
|
|
|
|
.reconfig = angle_reconfig,
|
|
|
|
.control = angle_control,
|
|
|
|
.uninit = angle_uninit,
|
|
|
|
};
|