diff options
Diffstat (limited to 'video/out/opengl/hwdec_d3d11eglrgb.c')
-rw-r--r-- | video/out/opengl/hwdec_d3d11eglrgb.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/video/out/opengl/hwdec_d3d11eglrgb.c b/video/out/opengl/hwdec_d3d11eglrgb.c new file mode 100644 index 0000000000..10470c2bf8 --- /dev/null +++ b/video/out/opengl/hwdec_d3d11eglrgb.c @@ -0,0 +1,293 @@ +/* + * This file is part of mpv. + * + * 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. + * + * 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 + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <initguid.h> +#include <assert.h> +#include <windows.h> +#include <d3d11.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "angle_dynamic.h" + +#include "common/common.h" +#include "osdep/timer.h" +#include "osdep/windows_utils.h" +#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; + + ID3D11Device *d3d11_device; + ID3D11DeviceContext *device_ctx; + ID3D11VideoDevice *video_dev; + ID3D11VideoContext *video_ctx; + + EGLDisplay egl_display; + EGLConfig egl_config; + EGLSurface egl_surface; + + ID3D11Texture2D *texture; + ID3D11VideoProcessor *video_proc; + ID3D11VideoProcessorEnumerator *vp_enum; + ID3D11VideoProcessorOutputView *out_view; + + GLuint gl_texture; +}; + +static void unmap(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + if (p->egl_surface) { + eglReleaseTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); + eglDestroySurface(p->egl_display, p->egl_surface); + } + p->egl_surface = NULL; +} + +static void destroy_objects(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + + unmap(hw); + + gl->DeleteTextures(1, &p->gl_texture); + p->gl_texture = 0; +} + +static void destroy(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + + destroy_objects(hw); + + hwdec_devices_remove(hw->devs, &p->hwctx); + + if (p->video_ctx) + ID3D11VideoContext_Release(p->video_ctx); + p->video_ctx = NULL; + + if (p->video_dev) + ID3D11VideoDevice_Release(p->video_dev); + p->video_dev = NULL; + + if (p->device_ctx) + ID3D11DeviceContext_Release(p->device_ctx); + p->device_ctx = NULL; + + if (p->d3d11_device) + ID3D11Device_Release(p->d3d11_device); + p->d3d11_device = NULL; +} + +static int create(struct gl_hwdec *hw) +{ + if (!angle_load()) + return -1; + + EGLDisplay egl_display = eglGetCurrentDisplay(); + if (!egl_display) + return -1; + + if (!eglGetCurrentContext()) + return -1; + + const char *exts = eglQueryString(egl_display, EGL_EXTENSIONS); + if (!exts || !strstr(exts, "EGL_ANGLE_d3d_share_handle_client_buffer")) + return -1; + + HRESULT hr; + struct priv *p = talloc_zero(hw, struct priv); + hw->priv = p; + + p->egl_display = egl_display; + + HANDLE d3d11_dll = GetModuleHandleW(L"d3d11.dll"); + if (!d3d11_dll) { + MP_ERR(hw, "Failed to load D3D11 library\n"); + goto fail; + } + + PFN_D3D11_CREATE_DEVICE CreateDevice = + (void *)GetProcAddress(d3d11_dll, "D3D11CreateDevice"); + if (!CreateDevice) + goto fail; + + hr = CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, + D3D11_CREATE_DEVICE_VIDEO_SUPPORT, NULL, 0, + D3D11_SDK_VERSION, &p->d3d11_device, NULL, NULL); + if (FAILED(hr)) { + MP_ERR(hw, "Failed to create D3D11 Device: %s\n", + mp_HRESULT_to_str(hr)); + goto fail; + } + + ID3D10Multithread *multithread; + hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_ID3D10Multithread, + (void **)&multithread); + if (FAILED(hr)) { + ID3D10Multithread_Release(multithread); + MP_ERR(hw, "Failed to get Multithread interface: %s\n", + mp_HRESULT_to_str(hr)); + goto fail; + } + ID3D10Multithread_SetMultithreadProtected(multithread, TRUE); + ID3D10Multithread_Release(multithread); + + hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_ID3D11VideoDevice, + (void **)&p->video_dev); + if (FAILED(hr)) + goto fail; + + ID3D11Device_GetImmediateContext(p->d3d11_device, &p->device_ctx); + if (!p->device_ctx) + goto fail; + hr = ID3D11DeviceContext_QueryInterface(p->device_ctx, &IID_ID3D11VideoContext, + (void **)&p->video_ctx); + if (FAILED(hr)) + goto fail; + + EGLint attrs[] = { + EGL_BUFFER_SIZE, 32, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_ALPHA_SIZE, 8, + EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, + EGL_NONE + }; + EGLint count; + if (!eglChooseConfig(p->egl_display, attrs, &p->egl_config, 1, &count) || + !count) { + MP_ERR(hw, "Failed to get EGL surface configuration\n"); + goto fail; + } + + p->hwctx = (struct mp_hwdec_ctx){ + .type = HWDEC_D3D11VA, + .driver_name = hw->driver->name, + .ctx = p->d3d11_device, + }; + hwdec_devices_add(hw->devs, &p->hwctx); + + return 0; +fail: + destroy(hw); + return -1; +} + +static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + + destroy_objects(hw); + + gl->GenTextures(1, &p->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); + 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); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl->BindTexture(GL_TEXTURE_2D, 0); + + params->imgfmt = IMGFMT_RGB0; + 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; + GL *gl = hw->gl; + HRESULT hr; + + if (!p->gl_texture) + return -1; + + ID3D11Texture2D *d3d_tex = (void *)hw_image->planes[1]; + if (!d3d_tex) + return -1; + + IDXGIResource *res; + hr = IUnknown_QueryInterface(d3d_tex, &IID_IDXGIResource, (void **)&res); + if (FAILED(hr)) + return -1; + + HANDLE share_handle = NULL; + hr = IDXGIResource_GetSharedHandle(res, &share_handle); + if (FAILED(hr)) + share_handle = NULL; + + IDXGIResource_Release(res); + + if (!share_handle) + return -1; + + D3D11_TEXTURE2D_DESC texdesc; + ID3D11Texture2D_GetDesc(d3d_tex, &texdesc); + + EGLint attrib_list[] = { + EGL_WIDTH, texdesc.Width, + EGL_HEIGHT, texdesc.Height, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_NONE + }; + p->egl_surface = eglCreatePbufferFromClientBuffer( + p->egl_display, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + share_handle, p->egl_config, attrib_list); + if (p->egl_surface == EGL_NO_SURFACE) { + MP_ERR(hw, "Failed to create EGL surface\n"); + return -1; + } + + gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); + eglBindTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); + gl->BindTexture(GL_TEXTURE_2D, 0); + + *out_frame = (struct gl_hwdec_frame){ + .planes = { + { + .gl_texture = p->gl_texture, + .gl_target = GL_TEXTURE_2D, + .tex_w = texdesc.Width, + .tex_h = texdesc.Height, + }, + }, + }; + return 0; +} + +const struct gl_hwdec_driver gl_hwdec_d3d11eglrgb = { + .name = "d3d11-egl-rgb", + .api = HWDEC_D3D11VA, + .imgfmt = IMGFMT_D3D11RGB, + .create = create, + .reinit = reinit, + .map_frame = map_frame, + .unmap = unmap, + .destroy = destroy, +}; |