2015-11-30 13:56:02 +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-30 13:56:02 +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-30 13:56:02 +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-30 13:56:02 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <windows.h>
|
2016-02-25 15:04:39 +00:00
|
|
|
#include <versionhelpers.h>
|
2015-11-30 13:56:02 +00:00
|
|
|
#include <d3d9.h>
|
|
|
|
#include <dwmapi.h>
|
2016-02-17 11:34:03 +00:00
|
|
|
#include "osdep/windows_utils.h"
|
2015-11-30 13:56:02 +00:00
|
|
|
#include "video/out/w32_common.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-30 13:56:02 +00:00
|
|
|
|
|
|
|
// For WGL_ACCESS_WRITE_DISCARD_NV, etc.
|
|
|
|
#include <GL/wglext.h>
|
|
|
|
|
2016-06-10 12:37:29 +00:00
|
|
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
|
|
|
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
|
|
|
|
|
2015-11-30 13:56:02 +00:00
|
|
|
// mingw-w64 header typo?
|
|
|
|
#ifndef IDirect3DSwapChain9Ex_GetBackBuffer
|
|
|
|
#define IDirect3DSwapChain9Ex_GetBackBuffer IDirect3DSwapChain9EX_GetBackBuffer
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct priv {
|
2017-09-15 15:37:28 +00:00
|
|
|
GL gl;
|
|
|
|
|
2015-11-30 13:56:02 +00:00
|
|
|
HMODULE d3d9_dll;
|
|
|
|
HRESULT (WINAPI *Direct3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D);
|
|
|
|
|
|
|
|
// Direct3D9 device and resources
|
|
|
|
IDirect3D9Ex *d3d9ex;
|
|
|
|
IDirect3DDevice9Ex *device;
|
|
|
|
HANDLE device_h;
|
|
|
|
IDirect3DSwapChain9Ex *swapchain;
|
|
|
|
IDirect3DSurface9 *backbuffer;
|
|
|
|
IDirect3DSurface9 *rtarget;
|
|
|
|
HANDLE rtarget_h;
|
|
|
|
|
|
|
|
// OpenGL offscreen context
|
|
|
|
HWND os_wnd;
|
|
|
|
HDC os_dc;
|
|
|
|
HGLRC os_ctx;
|
|
|
|
|
|
|
|
// OpenGL resources
|
|
|
|
GLuint texture;
|
2017-09-15 15:37:28 +00:00
|
|
|
GLuint main_fb;
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
// Did we lose the device?
|
|
|
|
bool lost_device;
|
|
|
|
|
|
|
|
// Requested and current parameters
|
|
|
|
int requested_swapinterval;
|
|
|
|
int width, height, swapinterval;
|
|
|
|
};
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static __thread struct ra_ctx *current_ctx;
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
static void pump_message_loop(void)
|
|
|
|
{
|
|
|
|
// We have a hidden window on this thread (for the OpenGL context,) so pump
|
|
|
|
// its message loop at regular intervals to be safe
|
|
|
|
MSG message;
|
|
|
|
while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE))
|
|
|
|
DispatchMessageW(&message);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *w32gpa(const GLubyte *procName)
|
|
|
|
{
|
|
|
|
HMODULE oglmod;
|
|
|
|
void *res = wglGetProcAddress(procName);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
oglmod = GetModuleHandleW(L"opengl32.dll");
|
|
|
|
return GetProcAddress(oglmod, procName);
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static int os_ctx_create(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
static const wchar_t os_wnd_class[] = L"mpv offscreen gl";
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
HGLRC legacy_context = NULL;
|
|
|
|
|
|
|
|
RegisterClassExW(&(WNDCLASSEXW) {
|
|
|
|
.cbSize = sizeof(WNDCLASSEXW),
|
|
|
|
.style = CS_OWNDC,
|
|
|
|
.lpfnWndProc = DefWindowProc,
|
2016-06-10 12:37:29 +00:00
|
|
|
.hInstance = HINST_THISCOMPONENT,
|
2015-11-30 13:56:02 +00:00
|
|
|
.lpszClassName = os_wnd_class,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Create a hidden window for an offscreen OpenGL context. It might also be
|
|
|
|
// possible to use the VO window, but MSDN recommends against drawing to
|
|
|
|
// the same window with flip mode present and other APIs, so play it safe.
|
|
|
|
p->os_wnd = CreateWindowExW(0, os_wnd_class, os_wnd_class, 0, 0, 0, 200,
|
2016-06-10 12:37:29 +00:00
|
|
|
200, NULL, NULL, HINST_THISCOMPONENT, NULL);
|
2015-11-30 13:56:02 +00:00
|
|
|
p->os_dc = GetDC(p->os_wnd);
|
|
|
|
if (!p->os_dc) {
|
|
|
|
MP_FATAL(ctx->vo, "Couldn't create window for offscreen rendering\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Choose a pixel format. It probably doesn't matter what this is because
|
|
|
|
// the primary framebuffer will not be used.
|
|
|
|
PIXELFORMATDESCRIPTOR pfd = {
|
|
|
|
.nSize = sizeof pfd,
|
|
|
|
.nVersion = 1,
|
|
|
|
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
|
|
|
.iPixelType = PFD_TYPE_RGBA,
|
|
|
|
.cColorBits = 24,
|
|
|
|
.iLayerType = PFD_MAIN_PLANE,
|
|
|
|
};
|
|
|
|
int pf = ChoosePixelFormat(p->os_dc, &pfd);
|
|
|
|
if (!pf) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo,
|
|
|
|
"Couldn't choose pixelformat for offscreen rendering: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
SetPixelFormat(p->os_dc, pf, &pfd);
|
|
|
|
|
|
|
|
legacy_context = wglCreateContext(p->os_dc);
|
|
|
|
if (!legacy_context || !wglMakeCurrent(p->os_dc, legacy_context)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo, "Couldn't create OpenGL context for offscreen rendering: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *(GLAPIENTRY *wglGetExtensionsStringARB)(HDC hdc)
|
|
|
|
= w32gpa((const GLubyte*)"wglGetExtensionsStringARB");
|
|
|
|
if (!wglGetExtensionsStringARB) {
|
|
|
|
MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *wgl_exts = wglGetExtensionsStringARB(p->os_dc);
|
|
|
|
if (!strstr(wgl_exts, "WGL_ARB_create_context")) {
|
|
|
|
MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
HGLRC (GLAPIENTRY *wglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext,
|
|
|
|
const int *attribList)
|
|
|
|
= w32gpa((const GLubyte*)"wglCreateContextAttribsARB");
|
|
|
|
if (!wglCreateContextAttribsARB) {
|
|
|
|
MP_FATAL(ctx->vo, "The OpenGL driver does not support OpenGL 3.x\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
int attribs[] = {
|
|
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
|
|
|
|
WGL_CONTEXT_FLAGS_ARB, 0,
|
|
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
|
|
|
|
if (!p->os_ctx) {
|
|
|
|
// NVidia, instead of ignoring WGL_CONTEXT_FLAGS_ARB, will error out if
|
|
|
|
// it's present on pre-3.2 contexts.
|
|
|
|
// Remove it from attribs and retry the context creation.
|
|
|
|
attribs[6] = attribs[7] = 0;
|
|
|
|
p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
|
|
|
|
}
|
|
|
|
if (!p->os_ctx) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo,
|
|
|
|
"Couldn't create OpenGL 3.x context for offscreen rendering: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
wglMakeCurrent(p->os_dc, NULL);
|
|
|
|
wglDeleteContext(legacy_context);
|
|
|
|
legacy_context = NULL;
|
|
|
|
|
|
|
|
if (!wglMakeCurrent(p->os_dc, p->os_ctx)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo,
|
|
|
|
"Couldn't activate OpenGL 3.x context for offscreen rendering: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
mpgl_load_functions(gl, w32gpa, wgl_exts, ctx->vo->log);
|
|
|
|
if (!(gl->mpgl_caps & MPGL_CAP_DXINTEROP)) {
|
2015-11-30 13:56:02 +00:00
|
|
|
MP_FATAL(ctx->vo, "WGL_NV_DX_interop is not supported\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
if (legacy_context) {
|
|
|
|
wglMakeCurrent(p->os_dc, NULL);
|
|
|
|
wglDeleteContext(legacy_context);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void os_ctx_destroy(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
|
|
|
if (p->os_ctx) {
|
|
|
|
wglMakeCurrent(p->os_dc, NULL);
|
|
|
|
wglDeleteContext(p->os_ctx);
|
|
|
|
}
|
|
|
|
if (p->os_dc)
|
|
|
|
ReleaseDC(p->os_wnd, p->os_dc);
|
|
|
|
if (p->os_wnd)
|
|
|
|
DestroyWindow(p->os_wnd);
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static int d3d_size_dependent_create(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
IDirect3DSwapChain9 *sw9;
|
|
|
|
hr = IDirect3DDevice9Ex_GetSwapChain(p->device, 0, &sw9);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't get swap chain: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirect3DSwapChain9_QueryInterface(sw9, &IID_IDirect3DSwapChain9Ex,
|
|
|
|
(void**)&p->swapchain);
|
|
|
|
if (FAILED(hr)) {
|
2017-01-29 13:13:19 +00:00
|
|
|
SAFE_RELEASE(sw9);
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Obtained swap chain is not IDirect3DSwapChain9Ex: %s\n",
|
|
|
|
mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2017-01-29 13:13:19 +00:00
|
|
|
SAFE_RELEASE(sw9);
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
hr = IDirect3DSwapChain9Ex_GetBackBuffer(p->swapchain, 0,
|
|
|
|
D3DBACKBUFFER_TYPE_MONO, &p->backbuffer);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't get backbuffer: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the format of the backbuffer
|
|
|
|
D3DSURFACE_DESC bb_desc = { 0 };
|
|
|
|
IDirect3DSurface9_GetDesc(p->backbuffer, &bb_desc);
|
|
|
|
|
|
|
|
MP_VERBOSE(ctx->vo, "DX_interop backbuffer size: %ux%u\n",
|
|
|
|
(unsigned)bb_desc.Width, (unsigned)bb_desc.Height);
|
|
|
|
MP_VERBOSE(ctx->vo, "DX_interop backbuffer format: %u\n",
|
|
|
|
(unsigned)bb_desc.Format);
|
|
|
|
|
|
|
|
// Create a rendertarget with the same format as the backbuffer for
|
|
|
|
// rendering from OpenGL
|
|
|
|
HANDLE share_handle = NULL;
|
|
|
|
hr = IDirect3DDevice9Ex_CreateRenderTarget(p->device, bb_desc.Width,
|
|
|
|
bb_desc.Height, bb_desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE,
|
|
|
|
&p->rtarget, &share_handle);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't create rendertarget: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register the share handle with WGL_NV_DX_interop. Nvidia does not
|
|
|
|
// require the use of share handles, but Intel does.
|
|
|
|
if (share_handle)
|
|
|
|
gl->DXSetResourceShareHandleNV(p->rtarget, share_handle);
|
|
|
|
|
|
|
|
// Create the OpenGL-side texture
|
|
|
|
gl->GenTextures(1, &p->texture);
|
|
|
|
|
|
|
|
// Now share the rendertarget with OpenGL as a texture
|
|
|
|
p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture,
|
|
|
|
GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
|
|
|
|
if (!p->rtarget_h) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't share rendertarget with OpenGL: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lock the rendertarget for use from OpenGL. This will only be unlocked in
|
|
|
|
// swap_buffers() when it is blitted to the real Direct3D backbuffer.
|
|
|
|
if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't lock rendertarget: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
gl->BindFramebuffer(GL_FRAMEBUFFER, p->main_fb);
|
2016-06-18 13:41:23 +00:00
|
|
|
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
|
GL_TEXTURE_2D, p->texture, 0);
|
|
|
|
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d_size_dependent_destroy(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
if (p->rtarget_h) {
|
|
|
|
gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h);
|
|
|
|
gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h);
|
|
|
|
}
|
2015-12-13 13:54:57 +00:00
|
|
|
p->rtarget_h = 0;
|
2015-11-30 13:56:02 +00:00
|
|
|
if (p->texture)
|
|
|
|
gl->DeleteTextures(1, &p->texture);
|
2015-12-13 13:54:57 +00:00
|
|
|
p->texture = 0;
|
2017-01-29 13:13:19 +00:00
|
|
|
|
|
|
|
SAFE_RELEASE(p->rtarget);
|
|
|
|
SAFE_RELEASE(p->backbuffer);
|
|
|
|
SAFE_RELEASE(p->swapchain);
|
2015-11-30 13:56:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void fill_presentparams(struct ra_ctx *ctx,
|
|
|
|
D3DPRESENT_PARAMETERS *pparams)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
|
|
|
|
// Present intervals other than IMMEDIATE and ONE don't seem to work. It's
|
|
|
|
// possible that they're not compatible with FLIPEX.
|
|
|
|
UINT presentation_interval;
|
|
|
|
switch (p->requested_swapinterval) {
|
|
|
|
case 0: presentation_interval = D3DPRESENT_INTERVAL_IMMEDIATE; break;
|
|
|
|
case 1: presentation_interval = D3DPRESENT_INTERVAL_ONE; break;
|
|
|
|
default: presentation_interval = D3DPRESENT_INTERVAL_ONE; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pparams = (D3DPRESENT_PARAMETERS) {
|
|
|
|
.Windowed = TRUE,
|
2015-12-13 13:45:49 +00:00
|
|
|
.BackBufferWidth = ctx->vo->dwidth ? ctx->vo->dwidth : 1,
|
|
|
|
.BackBufferHeight = ctx->vo->dheight ? ctx->vo->dheight : 1,
|
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
|
|
|
|
.BackBufferCount = ctx->opts.swapchain_depth + 2,
|
2016-02-25 15:04:39 +00:00
|
|
|
.SwapEffect = IsWindows7OrGreater() ? D3DSWAPEFFECT_FLIPEX : D3DSWAPEFFECT_FLIP,
|
2015-12-13 13:45:49 +00:00
|
|
|
// Automatically get the backbuffer format from the display format
|
2015-11-30 13:56:02 +00:00
|
|
|
.BackBufferFormat = D3DFMT_UNKNOWN,
|
|
|
|
.PresentationInterval = presentation_interval,
|
|
|
|
.hDeviceWindow = vo_w32_hwnd(ctx->vo),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static int d3d_create(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
p->d3d9_dll = LoadLibraryW(L"d3d9.dll");
|
|
|
|
if (!p->d3d9_dll) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo, "Failed to load \"d3d9.dll\": %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WGL_NV_dx_interop requires Direct3D 9Ex on WDDM systems. Direct3D 9Ex
|
|
|
|
// also enables flip mode present for efficient rendering with the DWM.
|
|
|
|
p->Direct3DCreate9Ex = (void*)GetProcAddress(p->d3d9_dll,
|
|
|
|
"Direct3DCreate9Ex");
|
|
|
|
if (!p->Direct3DCreate9Ex) {
|
|
|
|
MP_FATAL(ctx->vo, "Direct3D 9Ex not supported\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = p->Direct3DCreate9Ex(D3D_SDK_VERSION, &p->d3d9ex);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo, "Couldn't create Direct3D9Ex: %s\n",
|
|
|
|
mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
D3DPRESENT_PARAMETERS pparams;
|
|
|
|
fill_presentparams(ctx, &pparams);
|
|
|
|
|
|
|
|
hr = IDirect3D9Ex_CreateDeviceEx(p->d3d9ex, D3DADAPTER_DEFAULT,
|
|
|
|
D3DDEVTYPE_HAL, vo_w32_hwnd(ctx->vo),
|
|
|
|
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE |
|
|
|
|
D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED |
|
|
|
|
D3DCREATE_NOWINDOWCHANGES,
|
|
|
|
&pparams, NULL, &p->device);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo, "Couldn't create device: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
IDirect3DDevice9Ex_SetMaximumFrameLatency(p->device, ctx->opts.swapchain_depth);
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
// Register the Direct3D device with WGL_NV_dx_interop
|
|
|
|
p->device_h = gl->DXOpenDeviceNV(p->device);
|
|
|
|
if (!p->device_h) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_FATAL(ctx->vo, "Couldn't open Direct3D device from OpenGL: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void d3d_destroy(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
|
|
|
|
if (p->device_h)
|
|
|
|
gl->DXCloseDeviceNV(p->device_h);
|
2017-01-29 13:13:19 +00:00
|
|
|
SAFE_RELEASE(p->device);
|
|
|
|
SAFE_RELEASE(p->d3d9ex);
|
2015-11-30 13:56:02 +00:00
|
|
|
if (p->d3d9_dll)
|
|
|
|
FreeLibrary(p->d3d9_dll);
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void dxgl_uninit(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
2017-09-15 15:37:28 +00:00
|
|
|
ra_gl_ctx_uninit(ctx);
|
2015-11-30 13:56:02 +00:00
|
|
|
d3d_size_dependent_destroy(ctx);
|
|
|
|
d3d_destroy(ctx);
|
|
|
|
os_ctx_destroy(ctx);
|
|
|
|
vo_w32_uninit(ctx->vo);
|
|
|
|
DwmEnableMMCSS(FALSE);
|
|
|
|
pump_message_loop();
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void dxgl_reset(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
// Check if the device actually needs to be reset
|
|
|
|
if (ctx->vo->dwidth == p->width && ctx->vo->dheight == p->height &&
|
|
|
|
p->requested_swapinterval == p->swapinterval && !p->lost_device)
|
|
|
|
return;
|
|
|
|
|
|
|
|
d3d_size_dependent_destroy(ctx);
|
|
|
|
|
|
|
|
D3DPRESENT_PARAMETERS pparams;
|
|
|
|
fill_presentparams(ctx, &pparams);
|
|
|
|
|
|
|
|
hr = IDirect3DDevice9Ex_ResetEx(p->device, &pparams, NULL);
|
|
|
|
if (FAILED(hr)) {
|
2015-12-13 13:54:57 +00:00
|
|
|
p->lost_device = true;
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't reset device: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d3d_size_dependent_create(ctx) < 0) {
|
2015-12-13 13:54:57 +00:00
|
|
|
p->lost_device = true;
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't recreate Direct3D objects after reset\n");
|
2015-11-30 13:56:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MP_VERBOSE(ctx->vo, "Direct3D device reset\n");
|
|
|
|
p->width = ctx->vo->dwidth;
|
|
|
|
p->height = ctx->vo->dheight;
|
|
|
|
p->swapinterval = p->requested_swapinterval;
|
|
|
|
p->lost_device = false;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static int GLAPIENTRY dxgl_swap_interval(int interval)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
if (!current_ctx)
|
|
|
|
return 0;
|
|
|
|
struct priv *p = current_ctx->priv;
|
|
|
|
|
|
|
|
p->requested_swapinterval = interval;
|
2017-09-15 15:37:28 +00:00
|
|
|
dxgl_reset(current_ctx);
|
2015-11-30 13:56:02 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void * GLAPIENTRY dxgl_get_native_display(const char *name)
|
2016-03-14 00:35:42 +00:00
|
|
|
{
|
|
|
|
if (!current_ctx || !name)
|
|
|
|
return NULL;
|
|
|
|
struct priv *p = current_ctx->priv;
|
|
|
|
|
|
|
|
if (p->device && strcmp("IDirect3DDevice9Ex", name) == 0) {
|
|
|
|
return p->device;
|
|
|
|
} else if (p->device_h && strcmp("dxinterop_device_HANDLE", name) == 0) {
|
|
|
|
return p->device_h;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static void dxgl_swap_buffers(struct ra_ctx *ctx)
|
2015-11-30 13:56:02 +00:00
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
2017-09-15 15:37:28 +00:00
|
|
|
GL *gl = &p->gl;
|
2015-11-30 13:56:02 +00:00
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
pump_message_loop();
|
|
|
|
|
2015-12-13 13:54:57 +00:00
|
|
|
// If the device is still lost, try to reset it again
|
|
|
|
if (p->lost_device)
|
2017-09-15 15:37:28 +00:00
|
|
|
dxgl_reset(ctx);
|
2015-12-13 13:54:57 +00:00
|
|
|
if (p->lost_device)
|
|
|
|
return;
|
|
|
|
|
2015-11-30 13:56:02 +00:00
|
|
|
if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't unlock rendertarget for present: %s\n",
|
|
|
|
mp_LastError_to_str());
|
2015-11-30 13:56:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Blit the OpenGL rendertarget to the backbuffer
|
|
|
|
hr = IDirect3DDevice9Ex_StretchRect(p->device, p->rtarget, NULL,
|
|
|
|
p->backbuffer, NULL, D3DTEXF_NONE);
|
|
|
|
if (FAILED(hr)) {
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Couldn't stretchrect for present: %s\n",
|
|
|
|
mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirect3DDevice9Ex_PresentEx(p->device, NULL, NULL, NULL, NULL, 0);
|
|
|
|
switch (hr) {
|
|
|
|
case D3DERR_DEVICELOST:
|
|
|
|
case D3DERR_DEVICEHUNG:
|
|
|
|
MP_VERBOSE(ctx->vo, "Direct3D device lost! Resetting.\n");
|
|
|
|
p->lost_device = true;
|
2017-09-15 15:37:28 +00:00
|
|
|
dxgl_reset(ctx);
|
2017-06-16 09:51:12 +00:00
|
|
|
return;
|
2015-11-30 13:56:02 +00:00
|
|
|
default:
|
|
|
|
if (FAILED(hr))
|
2016-02-17 11:34:03 +00:00
|
|
|
MP_ERR(ctx->vo, "Failed to present: %s\n", mp_HRESULT_to_str(hr));
|
2015-11-30 13:56:02 +00:00
|
|
|
}
|
2017-06-16 09:51:12 +00:00
|
|
|
|
|
|
|
if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
|
|
|
|
MP_ERR(ctx->vo, "Couldn't lock rendertarget after present: %s\n",
|
|
|
|
mp_LastError_to_str());
|
|
|
|
}
|
2015-11-30 13:56:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
static bool dxgl_init(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
|
|
|
GL *gl = &p->gl;
|
|
|
|
|
|
|
|
p->requested_swapinterval = 1;
|
|
|
|
|
|
|
|
if (!vo_w32_init(ctx->vo))
|
|
|
|
goto fail;
|
|
|
|
if (os_ctx_create(ctx) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
// Create the shared framebuffer
|
|
|
|
gl->GenFramebuffers(1, &p->main_fb);
|
|
|
|
|
|
|
|
current_ctx = ctx;
|
|
|
|
gl->SwapInterval = dxgl_swap_interval;
|
|
|
|
gl->MPGetNativeDisplay = dxgl_get_native_display;
|
|
|
|
|
|
|
|
if (d3d_create(ctx) < 0)
|
|
|
|
goto fail;
|
|
|
|
if (d3d_size_dependent_create(ctx) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
static const struct ra_swapchain_fns empty_swapchain_fns = {0};
|
|
|
|
struct ra_gl_ctx_params params = {
|
|
|
|
.swap_buffers = dxgl_swap_buffers,
|
|
|
|
.flipped = true,
|
|
|
|
.external_swapchain = &empty_swapchain_fns,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!ra_gl_ctx_init(ctx, gl, params))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
DwmEnableMMCSS(TRUE);
|
|
|
|
return true;
|
|
|
|
fail:
|
|
|
|
dxgl_uninit(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resize(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct priv *p = ctx->priv;
|
|
|
|
dxgl_reset(ctx);
|
|
|
|
ra_gl_ctx_resize(ctx->swapchain, ctx->vo->dwidth, ctx->vo->dheight, p->main_fb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dxgl_reconfig(struct ra_ctx *ctx)
|
|
|
|
{
|
|
|
|
vo_w32_config(ctx->vo);
|
|
|
|
resize(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dxgl_control(struct ra_ctx *ctx, int *events, int request,
|
2015-11-30 13:56:02 +00:00
|
|
|
void *arg)
|
|
|
|
{
|
2017-09-15 15:37:28 +00:00
|
|
|
int ret = vo_w32_control(ctx->vo, events, request, arg);
|
2015-11-30 13:56:02 +00:00
|
|
|
if (*events & VO_EVENT_RESIZE)
|
2017-09-15 15:37:28 +00:00
|
|
|
resize(ctx);
|
|
|
|
return ret;
|
2015-11-30 13:56:02 +00:00
|
|
|
}
|
|
|
|
|
2017-09-15 15:37:28 +00:00
|
|
|
const struct ra_ctx_fns ra_ctx_dxgl = {
|
|
|
|
.type = "opengl",
|
2015-11-30 13:56:02 +00:00
|
|
|
.name = "dxinterop",
|
2017-09-15 15:37:28 +00:00
|
|
|
.init = dxgl_init,
|
|
|
|
.reconfig = dxgl_reconfig,
|
|
|
|
.control = dxgl_control,
|
|
|
|
.uninit = dxgl_uninit,
|
2015-11-30 13:56:02 +00:00
|
|
|
};
|