summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-05-10 20:37:03 +0200
committerwm4 <wm4@nowhere>2016-05-10 21:06:34 +0200
commita3d416c3d3f578e9359fcc145989b26ccf58d190 (patch)
tree3bfc4a1fc51e9fc82b74bd5b81b3f45a15d16799
parent4b3faf9dc1487d3a84ecc605e88452b30bc65d13 (diff)
downloadmpv-a3d416c3d3f578e9359fcc145989b26ccf58d190.tar.bz2
mpv-a3d416c3d3f578e9359fcc145989b26ccf58d190.tar.xz
vo_opengl: d3d11egl: native NV12 sampling support
This uses EGL_ANGLE_stream_producer_d3d_texture_nv12 and related extensions to map the D3D textures coming from the hardware decoder directly in GL. In theory this would be trivial to achieve, but unfortunately ANGLE does not have a mechanism to "import" D3D textures as GL textures. Instead, an awkward mechanism via EGL_KHR_stream was implemented, which involves at least 5 extensions and a lot of glue code. (Even worse than VAAPI EGL interop, and very far from the simplicity you get on OSX.) The ANGLE mechanism so far supports only the NV12 texture format, which means 10 bit won't work. It also does not work in ES3 mode yet. For these reasons, the "old" ID3D11VideoProcessor code is kept and used as a fallback.
-rw-r--r--video/decode/d3d11va.c26
-rw-r--r--video/out/opengl/header_fixes.h5
-rw-r--r--video/out/opengl/hwdec_d3d11egl.c239
-rw-r--r--video/out/opengl/utils.c1
4 files changed, 251 insertions, 20 deletions
diff --git a/video/decode/d3d11va.c b/video/decode/d3d11va.c
index 1626626767..9e300b1620 100644
--- a/video/decode/d3d11va.c
+++ b/video/decode/d3d11va.c
@@ -187,6 +187,29 @@ static const struct d3d_decoded_format d3d11_formats[] = {
};
#undef DFMT
+// Update hw_subfmt to the underlying format. Needed because AVFrame does not
+// have such an attribute, so it can't be passed through, and is updated here
+// instead. (But in the future, AVHWFramesContext could be used.)
+static struct mp_image *d3d11va_update_image_attribs(struct lavc_ctx *s,
+ struct mp_image *img)
+{
+ ID3D11Texture2D *texture = (void *)img->planes[1];
+
+ if (!texture)
+ return img;
+
+ D3D11_TEXTURE2D_DESC texture_desc;
+ ID3D11Texture2D_GetDesc(texture, &texture_desc);
+ for (int n = 0; n < MP_ARRAY_SIZE(d3d11_formats); n++) {
+ if (d3d11_formats[n].dxfmt == texture_desc.Format) {
+ img->params.hw_subfmt = d3d11_formats[n].mpfmt;
+ break;
+ }
+ }
+
+ return img;
+}
+
static bool d3d11_format_supported(struct lavc_ctx *s, const GUID *guid,
const struct d3d_decoded_format *format)
{
@@ -274,7 +297,7 @@ static int d3d11va_init_decoder(struct lavc_ctx *s, int w, int h)
.MiscFlags = 0,
.ArraySize = n_surfaces,
.Usage = D3D11_USAGE_DEFAULT,
- .BindFlags = D3D11_BIND_DECODER,
+ .BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE,
.CPUAccessFlags = 0,
};
hr = ID3D11Device_CreateTexture2D(p->device, &tex_desc, NULL, &texture);
@@ -555,6 +578,7 @@ const struct vd_lavc_hwdec mp_vd_lavc_d3d11va = {
.uninit = d3d11va_uninit,
.init_decoder = d3d11va_init_decoder,
.allocate_image = d3d11va_allocate_image,
+ .process_image = d3d11va_update_image_attribs,
};
const struct vd_lavc_hwdec mp_vd_lavc_d3d11va_copy = {
diff --git a/video/out/opengl/header_fixes.h b/video/out/opengl/header_fixes.h
index 9f9b58f8d3..494658a64b 100644
--- a/video/out/opengl/header_fixes.h
+++ b/video/out/opengl/header_fixes.h
@@ -88,6 +88,11 @@
#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
#endif
+// GL_OES_EGL_image_external, GL_NV_EGL_stream_consumer_external
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
#undef MP_GET_GL_WORKAROUNDS
#endif // MP_GET_GL_WORKAROUNDS
diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c
index ae7e35f953..28c5f27bf6 100644
--- a/video/out/opengl/hwdec_d3d11egl.c
+++ b/video/out/opengl/hwdec_d3d11egl.c
@@ -29,6 +29,10 @@
#include "hwdec.h"
#include "video/hwdec.h"
+#ifndef EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE
+#define EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE 0x3AAB
+#endif
+
struct priv {
struct mp_hwdec_ctx hwctx;
@@ -44,9 +48,36 @@ struct priv {
ID3D11VideoProcessor *video_proc;
ID3D11VideoProcessorEnumerator *vp_enum;
ID3D11VideoProcessorOutputView *out_view;
+
+ struct mp_image_params image_params;
int c_w, c_h;
- GLuint gl_texture;
+ EGLStreamKHR egl_stream;
+
+ GLuint gl_textures[3];
+
+ // EGL_KHR_stream
+ EGLStreamKHR (EGLAPIENTRY *CreateStreamKHR)(EGLDisplay dpy,
+ const EGLint *attrib_list);
+ EGLBoolean (EGLAPIENTRY *DestroyStreamKHR)(EGLDisplay dpy,
+ EGLStreamKHR stream);
+
+ // EGL_KHR_stream_consumer_gltexture
+ EGLBoolean (EGLAPIENTRY *StreamConsumerAcquireKHR)
+ (EGLDisplay dpy, EGLStreamKHR stream);
+ EGLBoolean (EGLAPIENTRY *StreamConsumerReleaseKHR)
+ (EGLDisplay dpy, EGLStreamKHR stream);
+
+ // EGL_NV_stream_consumer_gltexture_yuv
+ EGLBoolean (EGLAPIENTRY *StreamConsumerGLTextureExternalAttribsNV)
+ (EGLDisplay dpy, EGLStreamKHR stream, EGLAttrib *attrib_list);
+
+ // EGL_ANGLE_stream_producer_d3d_texture_nv12
+ EGLBoolean (EGLAPIENTRY *CreateStreamProducerD3DTextureNV12ANGLE)
+ (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list);
+ EGLBoolean (EGLAPIENTRY *StreamPostD3DTextureNV12ANGLE)
+ (EGLDisplay dpy, EGLStreamKHR stream, void *texture,
+ const EGLAttrib *attrib_list);
};
static void destroy_video_proc(struct gl_hwdec *hw)
@@ -71,8 +102,14 @@ static void destroy_objects(struct gl_hwdec *hw)
struct priv *p = hw->priv;
GL *gl = hw->gl;
- gl->DeleteTextures(1, &p->gl_texture);
- p->gl_texture = 0;
+ if (p->egl_stream)
+ p->DestroyStreamKHR(p->egl_display, p->egl_stream);
+ p->egl_stream = 0;
+
+ for (int n = 0; n < 3; n++) {
+ gl->DeleteTextures(1, &p->gl_textures[n]);
+ p->gl_textures[n] = 0;
+ }
if (p->egl_display && p->egl_surface) {
eglReleaseTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER);
@@ -135,6 +172,38 @@ static int create(struct gl_hwdec *hw)
p->egl_display = egl_display;
+ // Optional EGLStream stuff for working without video processor.
+ // Note that as long as GL_OES_EGL_image_external_essl3 is not available,
+ // this won't work in ES 3.x mode due to missing GLSL mechanisms.
+ if (strstr(exts, "EGL_ANGLE_stream_producer_d3d_texture_nv12") &&
+ hw->gl->es == 200)
+ {
+ MP_VERBOSE(hw, "Loading EGL_ANGLE_stream_producer_d3d_texture_nv12\n");
+
+ p->CreateStreamKHR = (void *)eglGetProcAddress("eglCreateStreamKHR");
+ p->DestroyStreamKHR = (void *)eglGetProcAddress("eglDestroyStreamKHR");
+ p->StreamConsumerAcquireKHR =
+ (void *)eglGetProcAddress("eglStreamConsumerAcquireKHR");
+ p->StreamConsumerReleaseKHR =
+ (void *)eglGetProcAddress("eglStreamConsumerReleaseKHR");
+ p->StreamConsumerGLTextureExternalAttribsNV =
+ (void *)eglGetProcAddress("eglStreamConsumerGLTextureExternalAttribsNV");
+ p->CreateStreamProducerD3DTextureNV12ANGLE =
+ (void *)eglGetProcAddress("eglCreateStreamProducerD3DTextureNV12ANGLE");
+ p->StreamPostD3DTextureNV12ANGLE =
+ (void *)eglGetProcAddress("eglStreamPostD3DTextureNV12ANGLE");
+
+ if (!p->CreateStreamKHR || !p->DestroyStreamKHR ||
+ !p->StreamConsumerAcquireKHR || !p->StreamConsumerReleaseKHR ||
+ !p->StreamConsumerGLTextureExternalAttribsNV ||
+ !p->CreateStreamProducerD3DTextureNV12ANGLE ||
+ !p->StreamPostD3DTextureNV12ANGLE)
+ {
+ MP_ERR(hw, "Failed to load some EGLStream functions.\n");
+ goto fail;
+ }
+ }
+
EGLAttrib device = 0;
if (!p_eglQueryDisplayAttribEXT(egl_display, EGL_DEVICE_EXT, &device))
goto fail;
@@ -208,6 +277,73 @@ fail:
return -1;
}
+static int create_egl_stream(struct gl_hwdec *hw, struct mp_image_params *params)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+
+ if (params->hw_subfmt != IMGFMT_NV12)
+ return -1;
+
+ if (!p->CreateStreamKHR)
+ return -1; // extensions not available
+
+ MP_VERBOSE(hw, "Using EGL_KHR_stream path.\n");
+
+ // Hope that the given texture unit range is not "in use" by anything.
+ // The texture units need to be bound during init only, and are free for
+ // use again after the initialization here is done.
+ int texunits = 0; // [texunits, texunits + num_planes)
+ int num_planes = 2;
+ int gl_target = GL_TEXTURE_EXTERNAL_OES;
+
+ p->egl_stream = p->CreateStreamKHR(p->egl_display, (EGLint[]){EGL_NONE});
+ if (!p->egl_stream)
+ goto fail;
+
+ for (int n = 0; n < num_planes; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + texunits + n);
+ gl->GenTextures(1, &p->gl_textures[n]);
+ gl->BindTexture(gl_target, p->gl_textures[n]);
+ gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ EGLAttrib attrs[] = {
+ EGL_COLOR_BUFFER_TYPE, EGL_YUV_BUFFER_EXT,
+ EGL_YUV_NUMBER_OF_PLANES_EXT, num_planes,
+ EGL_YUV_PLANE0_TEXTURE_UNIT_NV, texunits + 0,
+ EGL_YUV_PLANE1_TEXTURE_UNIT_NV, texunits + 1,
+ EGL_NONE,
+ };
+
+ if (!p->StreamConsumerGLTextureExternalAttribsNV(p->egl_display, p->egl_stream,
+ attrs))
+ goto fail;
+
+ if (!p->CreateStreamProducerD3DTextureNV12ANGLE(p->egl_display, p->egl_stream,
+ (EGLAttrib[]){EGL_NONE}))
+ goto fail;
+
+ params->imgfmt = params->hw_subfmt;
+
+ for (int n = 0; n < num_planes; n++) {
+ gl->ActiveTexture(GL_TEXTURE0 + texunits + n);
+ gl->BindTexture(gl_target, 0);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
+ return 0;
+fail:
+ MP_ERR(hw, "Failed to create EGLStream\n");
+ if (p->egl_stream)
+ p->DestroyStreamKHR(p->egl_display, p->egl_stream);
+ p->egl_stream = 0;
+ gl->ActiveTexture(GL_TEXTURE0);
+ return -1;
+}
+
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
{
struct priv *p = hw->priv;
@@ -216,6 +352,14 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
destroy_objects(hw);
+ p->image_params = *params;
+
+ // If this does not work, use the video process instead.
+ if (create_egl_stream(hw, params) >= 0)
+ return 0;
+
+ MP_VERBOSE(hw, "Using ID3D11VideoProcessor path.\n");
+
D3D11_TEXTURE2D_DESC texdesc = {
.Width = params->w,
.Height = params->h,
@@ -264,8 +408,8 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
goto fail;
}
- gl->GenTextures(1, &p->gl_texture);
- gl->BindTexture(GL_TEXTURE_2D, p->gl_texture);
+ gl->GenTextures(1, &p->gl_textures[0]);
+ gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[0]);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -355,27 +499,19 @@ fail:
return -1;
}
-static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
- struct gl_hwdec_frame *out_frame)
+static int map_frame_video_proc(struct gl_hwdec *hw, ID3D11Texture2D *d3d_tex,
+ int d3d_subindex, struct gl_hwdec_frame *out_frame)
{
struct priv *p = hw->priv;
HRESULT hr;
ID3D11VideoProcessorInputView *in_view = NULL;
- if (!p->gl_texture)
- return -1;
-
- ID3D11Texture2D *d3d_tex = (void *)hw_image->planes[1];
- int d3d_subindex = (intptr_t)hw_image->planes[2];
- if (!d3d_tex)
- return -1;
-
D3D11_TEXTURE2D_DESC texdesc;
ID3D11Texture2D_GetDesc(d3d_tex, &texdesc);
if (!p->video_proc || p->c_w != texdesc.Width || p->c_h != texdesc.Height) {
p->c_w = texdesc.Width;
p->c_h = texdesc.Height;
- if (create_video_proc(hw, &hw_image->params) < 0)
+ if (create_video_proc(hw, &p->image_params) < 0)
return -1;
}
@@ -410,16 +546,80 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
*out_frame = (struct gl_hwdec_frame){
.planes = {
{
- .gl_texture = p->gl_texture,
+ .gl_texture = p->gl_textures[0],
.gl_target = GL_TEXTURE_2D,
- .tex_w = hw_image->w,
- .tex_h = hw_image->h,
+ .tex_w = p->image_params.w,
+ .tex_h = p->image_params.h,
},
},
};
return 0;
}
+static int map_frame_egl_stream(struct gl_hwdec *hw, ID3D11Texture2D *d3d_tex,
+ int d3d_subindex, struct gl_hwdec_frame *out_frame)
+{
+ struct priv *p = hw->priv;
+
+ EGLAttrib attrs[] = {
+ EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, d3d_subindex,
+ EGL_NONE,
+ };
+ if (!p->StreamPostD3DTextureNV12ANGLE(p->egl_display, p->egl_stream,
+ (void *)d3d_tex, attrs))
+ return -1;
+
+ if (!p->StreamConsumerAcquireKHR(p->egl_display, p->egl_stream))
+ return -1;
+
+ D3D11_TEXTURE2D_DESC texdesc;
+ ID3D11Texture2D_GetDesc(d3d_tex, &texdesc);
+
+ *out_frame = (struct gl_hwdec_frame){
+ .planes = {
+ {
+ .gl_texture = p->gl_textures[0],
+ .gl_target = GL_TEXTURE_EXTERNAL_OES,
+ .tex_w = texdesc.Width,
+ .tex_h = texdesc.Height,
+ },
+ {
+ .gl_texture = p->gl_textures[1],
+ .gl_target = GL_TEXTURE_EXTERNAL_OES,
+ .tex_w = texdesc.Width / 2,
+ .tex_h = texdesc.Height / 2,
+ .swizzle = "rgba", // even in ES2 mode (no LUMINANCE_ALPHA)
+ },
+ },
+ };
+ return 0;
+}
+
+static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
+ struct gl_hwdec_frame *out_frame)
+{
+ struct priv *p = hw->priv;
+
+ if (!p->gl_textures[0])
+ return -1;
+
+ ID3D11Texture2D *d3d_tex = (void *)hw_image->planes[1];
+ int d3d_subindex = (intptr_t)hw_image->planes[2];
+ if (!d3d_tex)
+ return -1;
+
+ return p->egl_stream
+ ? map_frame_egl_stream(hw, d3d_tex, d3d_subindex, out_frame)
+ : map_frame_video_proc(hw, d3d_tex, d3d_subindex, out_frame);
+}
+
+static void unmap(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+ if (p->egl_stream)
+ p->StreamConsumerReleaseKHR(p->egl_display, p->egl_stream);
+}
+
const struct gl_hwdec_driver gl_hwdec_d3d11egl = {
.name = "d3d11-egl",
.api = HWDEC_D3D11VA,
@@ -427,5 +627,6 @@ const struct gl_hwdec_driver gl_hwdec_d3d11egl = {
.create = create,
.reinit = reinit,
.map_frame = map_frame,
+ .unmap = unmap,
.destroy = destroy,
};
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index 40f1beba5f..d93db76869 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -664,6 +664,7 @@ const char* mp_sampler_type(GLenum texture_target)
case GL_TEXTURE_1D: return "sampler1D";
case GL_TEXTURE_2D: return "sampler2D";
case GL_TEXTURE_RECTANGLE: return "sampler2DRect";
+ case GL_TEXTURE_EXTERNAL_OES: return "samplerExternalOES";
case GL_TEXTURE_3D: return "sampler3D";
default: abort();
}