summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/hwdec_d3d11egl.c
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 /video/out/opengl/hwdec_d3d11egl.c
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.
Diffstat (limited to 'video/out/opengl/hwdec_d3d11egl.c')
-rw-r--r--video/out/opengl/hwdec_d3d11egl.c239
1 files changed, 220 insertions, 19 deletions
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,
};