From b3ae6362eae622e747cf8ad1ee076898e3536597 Mon Sep 17 00:00:00 2001 From: Nick Sarnie Date: Wed, 23 Aug 2017 20:13:00 -0400 Subject: [PATCH] Wine D3D9 Signed-off-by: Nick Sarnie --- configure.ac | 188 ++++ dlls/d3d9-nine/Makefile.in | 15 + dlls/d3d9-nine/d3d9-nine.spec | 14 + dlls/d3d9-nine/d3d9_main.c | 173 ++++ dlls/d3d9-nine/d3dadapter9.c | 853 ++++++++++++++++++ dlls/d3d9-nine/d3dadapter9.h | 32 + dlls/d3d9-nine/device_wrap.c | 500 +++++++++++ dlls/d3d9-nine/device_wrap.h | 26 + dlls/d3d9-nine/dri3.c | 1426 ++++++++++++++++++++++++++++++ dlls/d3d9-nine/dri3.h | 91 ++ dlls/d3d9-nine/present.c | 1748 +++++++++++++++++++++++++++++++++++++ dlls/d3d9-nine/present.h | 40 + dlls/d3d9-nine/shader_validator.c | 88 ++ dlls/d3d9-nine/shader_validator.h | 29 + dlls/d3d9-nine/version.rc | 26 + dlls/d3d9-nine/wndproc.c | 277 ++++++ dlls/d3d9-nine/wndproc.h | 41 + 17 files changed, 5567 insertions(+) create mode 100644 dlls/d3d9-nine/Makefile.in create mode 100644 dlls/d3d9-nine/d3d9-nine.spec create mode 100644 dlls/d3d9-nine/d3d9_main.c create mode 100644 dlls/d3d9-nine/d3dadapter9.c create mode 100644 dlls/d3d9-nine/d3dadapter9.h create mode 100644 dlls/d3d9-nine/device_wrap.c create mode 100644 dlls/d3d9-nine/device_wrap.h create mode 100644 dlls/d3d9-nine/dri3.c create mode 100644 dlls/d3d9-nine/dri3.h create mode 100644 dlls/d3d9-nine/present.c create mode 100644 dlls/d3d9-nine/present.h create mode 100644 dlls/d3d9-nine/shader_validator.c create mode 100644 dlls/d3d9-nine/shader_validator.h create mode 100644 dlls/d3d9-nine/version.rc create mode 100644 dlls/d3d9-nine/wndproc.c create mode 100644 dlls/d3d9-nine/wndproc.h diff --git a/configure.ac b/configure.ac index fd05212057..6b96bdd209 100644 --- a/configure.ac +++ b/configure.ac @@ -66,6 +66,14 @@ AC_ARG_WITH(openal, AS_HELP_STRING([--without-openal],[do not use OpenAL]), AC_ARG_WITH(opencl, AS_HELP_STRING([--without-opencl],[do not use OpenCL]), [if test "x$withval" = "xno"; then ac_cv_header_CL_cl_h=no; ac_cv_header_OpenCL_opencl_h=no; fi]) AC_ARG_WITH(opengl, AS_HELP_STRING([--without-opengl],[do not use OpenGL])) +AC_ARG_WITH(d3d9-nine, AS_HELP_STRING([--without-d3d9-nine],[do not build d3d9-nine.dll (Gallium Nine support)]), + [], [with_d3d9_nine=auto]) +AC_ARG_WITH(d3d9-nine-module, AS_HELP_STRING([--with-d3d9-nine-module], + [Gallium Nine module location. Can be 'manual' (path to be filled in a register), 'auto' (default. use pkgconfig to detect the location) or a path]), + [], [with_d3d9_nine_module=auto]) +AC_ARG_WITH(d3d9-nine-headers-path, AS_HELP_STRING([--with-d3d9-nine-headers-path], + [Gallium Nine headers location. Can be 'auto' (default. use pkgconfig to detect the location) or a path]), + [], [with_d3d9_nine_headers_path=auto]) AC_ARG_WITH(osmesa, AS_HELP_STRING([--without-osmesa],[do not use the OSMesa library])) AC_ARG_WITH(oss, AS_HELP_STRING([--without-oss],[do not use the OSS sound support])) AC_ARG_WITH(pcap, AS_HELP_STRING([--without-pcap],[do not use the Packet Capture library]), @@ -390,6 +398,8 @@ AC_CHECK_LIB(ossaudio,_oss_ioctl) AC_SUBST(OPENGL_LIBS,"") +AC_SUBST(D3D9NINE_LIBS,"") + dnl **** Check for header files **** AC_SYS_LARGEFILE() @@ -1247,6 +1257,183 @@ OpenGL and Direct3D won't be supported.]) WINE_NOTICE_WITH(va,[test "x$ac_cv_lib_soname_va" = "x" -o "x$ac_cv_lib_soname_va_x11" = "x" -o "x$ac_cv_lib_soname_va_drm" = "x"], [libva ${notice_platform}development files not found, GPU video acceleration won't be supported.]) + dnl Check for d3d9-nine support + if test "x$with_d3d9_nine" = "xauto" && (test "x$with_d3d9_nine_module" = "xauto" || test "x$with_d3d9_nine_headers_path" = "xauto") + then + AC_MSG_CHECKING([whether d3dadapter9 package (Gallium Nine) is present]) + D3DADAPTER9_MODULEDIR=`$PKG_CONFIG --variable=moduledir d3dadapter9 2>/dev/null` + D3DADAPTER9_LEGACY_MODULEDIR=`$PKG_CONFIG --variable=moduledir d3d 2>/dev/null` + if test "x$D3DADAPTER9_MODULEDIR" = "x" -a "x$D3DADAPTER9_LEGACY_MODULEDIR" = "x" + then + AC_MSG_RESULT([no, disabling support]) + with_d3d9_nine=no + else + AC_MSG_RESULT([yes]) + fi + fi + + if test "x$with_d3d9_nine" != "xno" + then + AC_MSG_NOTICE([Checking for d3dadapter9 library dependencies]) + D3D9NINE_LIBS="" + D3D9NINE_MISSING_LIBS="" + AC_CHECK_LIB(xcb,xcb_request_check,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lxcb"])]) + test "x$ac_cv_lib_xcb_xcb_request_check" != xyes && D3D9NINE_MISSING_LIBS="libxcb " + AC_CHECK_LIB(xcb-dri3,xcb_dri3_open,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lxcb-dri3"])]) + test "x$ac_cv_lib_xcb_dri3_xcb_dri3_open" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libxcb-dri3 " + AC_CHECK_LIB(xcb-present,xcb_present_notify_msc,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lxcb-present"])]) + test "x$ac_cv_lib_xcb_present_xcb_present_notify_msc" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libxcb-present " + AC_CHECK_LIB(xcb-xfixes,xcb_xfixes_create_region,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lxcb-xfixes"])]) + test "x$ac_cv_lib_xcb_xfixes_xcb_xfixes_create_region" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libxcb-xfixes " + AC_CHECK_LIB(X11-xcb,XGetXCBConnection,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lX11-xcb"])]) + test "x$ac_cv_lib_X11_xcb_XGetXCBConnection" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libX11-xcb " + AC_CHECK_LIB(X11,XOpenDisplay,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lX11"])]) + test "x$ac_cv_lib_X11_XOpenDisplay" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libX11 " + AC_CHECK_LIB(Xext,XextRemoveDisplay,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lXext"])]) + test "x$ac_cv_lib_Xext_XextRemoveDisplay" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libXext " + # libs for the dri2 fallback + AC_CHECK_LIB(GL,glGenFramebuffers,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lGL"])]) + test "x$ac_cv_lib_GL_glGenFramebuffers" != xyes && D3D9NINE_MISSING_LIBS="libGL " + AC_CHECK_LIB(EGL,eglCreateContext,[AC_SUBST(D3D9NINE_LIBS,["$D3D9NINE_LIBS -lEGL"])]) + test "x$ac_cv_lib_EGL_eglCreateContext" != xyes && D3D9NINE_MISSING_LIBS="${D3D9NINE_MISSING_LIBS}libEGL" + + if test "x$D3D9NINE_MISSING_LIBS" != x + then + if test "x$with_d3d9_nine" = "xyes" + then + AC_MSG_ERROR([Missing libraries to build d3d9-nine.dll: $D3D9NINE_MISSING_LIBS]) + else + AC_MSG_NOTICE([Missing libraries to build d3d9-nine.dll: $D3D9NINE_MISSING_LIBS . disabling support]) + with_d3d9_nine=no + fi + fi + fi + + if test "x$with_d3d9_nine" != "xno" + then + AC_MSG_NOTICE([Checking d3dadapter9 header dependencies]) + D3D9NINE_MISSING_HEADERS="" + AC_CHECK_HEADERS([X11/Xlib-xcb.h xcb/dri3.h xcb/present.h X11/Xutil.h X11/Xlib.h pthread.h]) + test "x$ac_cv_header_X11_Xlib_xcb_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/Xlib-xcb.h " + test "x$ac_cv_header_xcb_dri3_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}xcb/dri3.h " + test "x$ac_cv_header_xcb_present_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}xcb/present.h " + test "x$ac_cv_header_X11_Xutil_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/Xutil.h " + test "x$ac_cv_header_X11_Xlib_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/Xlib.h " + test "x$ac_cv_header_pthread_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}pthread.h " + # headers for the dri2 fallback + AC_CHECK_HEADERS([X11/Xmd.h X11/Xlibint.h X11/extensions/dri2tokens.h X11/extensions/dri2proto.h X11/extensions/extutil.h GL/gl.h GL/glext.h EGL/egl.h EGL/eglext.h libdrm/drm_fourcc.h libdrm/drm.h], + [],[],[[ +#include +#include +#include +#include +]]) + test "x$ac_cv_header_X11_Xmd_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/Xmd.h " + test "x$ac_cv_header_X11_Xlibint_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/Xlibint.h " + test "x$ac_cv_header_X11_extensions_dri2tokens_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/extensions/dri2tokens.h " + test "x$ac_cv_header_X11_extensions_dri2proto_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/extensions/dri2proto.h " + test "x$ac_cv_header_X11_extensions_extutil_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}X11/extensions/extutil.h " + test "x$ac_cv_header_GL_gl_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}GL/gl.h " + test "x$ac_cv_header_GL_glext_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}GL/glext.h " + test "x$ac_cv_header_EGL_egl_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}EGL/egl.h " + test "x$ac_cv_header_EGL_eglext_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}EGL/eglext.h " + test "x$ac_cv_header_libdrm_drm_fourcc_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}libdrm/drm_fourcc.h " + test "x$ac_cv_header_libdrm_drm_h" != xyes && D3D9NINE_MISSING_HEADERS="${D3D9NINE_MISSING_HEADERS}libdrm/drm.h " + if test "x$D3D9NINE_MISSING_HEADERS" != x + then + if test "x$with_d3d9_nine" = "xyes" + then + AC_MSG_ERROR([Missing headers to build d3d9-nine.dll: $D3D9NINE_MISSING_HEADERS]) + else + AC_MSG_NOTICE([Missing headers to build d3d9-nine.dll: $D3D9NINE_MISSING_HEADERS . disabling support]) + with_d3d9_nine=no + fi + fi + fi + + if test "x$with_d3d9_nine" != "xno" + then + # d3d9-nine.dll will be built and an option to use it + # added. The module path can always be overriden by + # a wine registry setting. If the module isn't found + # at execution time, the dll will refuse to load and + # print an error message to the user. + AC_DEFINE(HAVE_D3D9NINE, 1, [Whether d3d9-nine.dll is built]) + # default: use pkgconfig to find the gallium nine module. + # Check the module is there. + if test "x$with_d3d9_nine_module" = "xauto" + then + D3DADAPTER9_MODULEDIR=`$PKG_CONFIG --variable=moduledir d3dadapter9 2>/dev/null` + if test "x$D3DADAPTER9_MODULEDIR" = x + then + # legacy path + D3DADAPTER9_MODULEDIR=`$PKG_CONFIG --variable=moduledir d3d 2>/dev/null` + if test "x$D3DADAPTER9_MODULEDIR" = x + then + AC_MSG_ERROR([pkg-config couldn't find Gallium Nine module]) + fi + fi + AC_DEFINE_UNQUOTED(D3D9NINE_MODULEPATH, ["`echo ${D3DADAPTER9_MODULEDIR}/d3dadapter9.so.1`"], [Gallium Nine module path]) + # Check module + CPPFLAGSBAK=$CPPFLAGS + # link against libdl + CPPFLAGS="$CPPFLAGS -Wl,--no-as-needed -ldl" + + AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include +#include ]],[[void *handle = dlopen("${D3DADAPTER9_MODULEDIR}/d3dadapter9.so.1", RTLD_GLOBAL | RTLD_NOW); +exit((handle && dlsym(handle, "D3DAdapter9GetProc")) ? 0 : 1)]])], + [echo "d3dadapter9.so.1 found at '${D3DADAPTER9_MODULEDIR}/d3dadapter9.so.1'"], + [AC_MSG_ERROR([Couldn't load Gallium nine module at '${D3DADAPTER9_MODULEDIR}/d3dadapter9.so.1' (found by pkg-config)])]) + CPPFLAGS=$CPPFLAGSBAK + else + # Manual : do not feed any path, the wine registry will have + # to be used to pass a path. Useful for wine builds + # that are distributed and will run on different + # distros. + # Else feed a path directly. Useful for distro maintainers who + # know where the package would be installed, but don't have + # it installed on their build. + # Don't check if the module is there. + if test "x$with_d3d9_nine_module" != "xmanual" + then + AC_DEFINE_UNQUOTED(D3D9NINE_MODULEPATH, ["`echo ${with_d3d9_nine_module}`"], [Gallium Nine module path]) + fi + fi + + # by default the headers are found with pkgconfig, + # but it is possible to pass a path to a specific directory. + # that directory must contain a d3dadapter directory + # with d3dadapter9.h, drm.h and present.h + if test "x$with_d3d9_nine_headers_path" = "xauto" + then + AC_PROG_SED + D3DADAPTER9_INCLUDEDIR=`$PKG_CONFIG --variable=includedir d3dadapter9 2>/dev/null` + D3DADAPTER9_MAJOR=`$PKG_CONFIG --modversion d3dadapter9 2>/dev/null | $SED -n 's/\([[^\.]]*\)\..*$/\1/p'` + if test "x$D3DADAPTER9_INCLUDEDIR" = x + then + # legacy path + D3DADAPTER9_INCLUDEDIR=`$PKG_CONFIG --variable=includedir d3d 2>/dev/null` + D3DADAPTER9_MAJOR=`$PKG_CONFIG --modversion d3d 2>/dev/null | $SED -n 's/\([[^\.]]*\)\..*$/\1/p'` + if test "x$D3DADAPTER9_INCLUDEDIR" = x + then + AC_MSG_ERROR([pkg-config couldn't find Gallium Nine headers]) + fi + fi + # check major version of package. The major version number guarantees header compatibility. + if test "x$D3DADAPTER9_MAJOR" != x1 + then + AC_MSG_ERROR([pkg-config found Gallium Nine Module and Headers, but version is incompatible]) + fi + else + D3DADAPTER9_INCLUDEDIR=`echo ${with_d3d9_nine_headers_path}` + fi + AC_DEFINE_UNQUOTED(D3D9NINE_HEADERS_CFLAGS, ["`echo -I${D3DADAPTER9_INCLUDEDIR}`"], [Gallium Nine headers cflags]) + + AC_DEFINE(D3D9NINE_DRI2, 1, [Whether d3d9-nine DRI2 fallback is compiled]) + else + enable_d3d9_nine=${enable_d3d9_nine:-no} + fi + CPPFLAGS="$ac_save_CPPFLAGS" else X_CFLAGS="" @@ -3096,6 +3283,7 @@ WINE_CONFIG_DLL(d3d8,,[implib]) WINE_CONFIG_TEST(dlls/d3d8/tests) WINE_CONFIG_DLL(d3d9,,[implib]) WINE_CONFIG_TEST(dlls/d3d9/tests) +WINE_CONFIG_DLL(d3d9-nine,,[implib]) WINE_CONFIG_DLL(d3dcompiler_33) WINE_CONFIG_DLL(d3dcompiler_34) WINE_CONFIG_DLL(d3dcompiler_35) diff --git a/dlls/d3d9-nine/Makefile.in b/dlls/d3d9-nine/Makefile.in new file mode 100644 index 0000000000..c6df8d7dbe --- /dev/null +++ b/dlls/d3d9-nine/Makefile.in @@ -0,0 +1,15 @@ +MODULE = d3d9-nine.dll +IMPORTS = dxguid uuid advapi32 gdi32 user32 +EXTRAINCL = $(X_CFLAGS) $(D3D9NINE_HEADERS_CFLAGS) +EXTRALIBS = $(D3D9NINE_LIBS) + +C_SRCS = \ + d3d9_main.c \ + d3dadapter9.c \ + device_wrap.c \ + present.c \ + dri3.c \ + wndproc.c \ + shader_validator.c + +RC_SRCS = version.rc diff --git a/dlls/d3d9-nine/d3d9-nine.spec b/dlls/d3d9-nine/d3d9-nine.spec new file mode 100644 index 0000000000..a33cba51e7 --- /dev/null +++ b/dlls/d3d9-nine/d3d9-nine.spec @@ -0,0 +1,14 @@ +@ stdcall Direct3DShaderValidatorCreate9() +@ stub PSGPError +@ stub PSGPSampleTexture +@ stdcall D3DPERF_BeginEvent(long wstr) +@ stdcall D3DPERF_EndEvent() +@ stdcall D3DPERF_GetStatus() +@ stdcall D3DPERF_QueryRepeatFrame() +@ stdcall D3DPERF_SetMarker(long wstr) +@ stdcall D3DPERF_SetOptions(long) +@ stdcall D3DPERF_SetRegion(long wstr) +@ stub DebugSetLevel +@ stdcall DebugSetMute() +@ stdcall Direct3DCreate9(long) +@ stdcall Direct3DCreate9Ex(long ptr) diff --git a/dlls/d3d9-nine/d3d9_main.c b/dlls/d3d9-nine/d3d9_main.c new file mode 100644 index 0000000000..d1efdf4a71 --- /dev/null +++ b/dlls/d3d9-nine/d3d9_main.c @@ -0,0 +1,173 @@ +/* + * Direct3D 9 + * + * Copyright 2002-2003 Jason Edmeades + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2005 Oliver Stieber + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "wine/debug.h" + +#include +#include + +#include "d3dadapter9.h" +#include "wndproc.h" +#include "shader_validator.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d9nine); + +static int D3DPERF_event_level = 0; +static Display *gdi_display; + +void WINAPI DebugSetMute(void) +{ + /* nothing to do */ +} + +IDirect3D9 * WINAPI DECLSPEC_HOTPATCH Direct3DCreate9(UINT sdk_version) +{ + IDirect3D9 *native; + TRACE("sdk_version %#x.\n", sdk_version); + + if (SUCCEEDED(d3dadapter9_new(gdi_display, FALSE, (IDirect3D9Ex **)&native))) + return native; + + return NULL; +} + +HRESULT WINAPI DECLSPEC_HOTPATCH Direct3DCreate9Ex(UINT sdk_version, IDirect3D9Ex **d3d9ex) +{ + TRACE("sdk_version %#x, d3d9ex %p.\n", sdk_version, d3d9ex); + + return d3dadapter9_new(gdi_display, TRUE, d3d9ex); +} + +/******************************************************************* + * Direct3DShaderValidatorCreate9 (D3D9.@) + * + * No documentation available for this function. + * SDK only says it is internal and shouldn't be used. + */ + +void* WINAPI Direct3DShaderValidatorCreate9(void) +{ + IDirect3DShaderValidator9Impl* object = + HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(IDirect3DShaderValidator9Impl)); + + object->lpVtbl = &IDirect3DShaderValidator9Vtbl; + object->ref = 1; + + FIXME("Returning interface %p\n", object); + return (void*) object; +} + +/******************************************************************* + * DllMain + */ +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + if (!(gdi_display = XOpenDisplay( NULL ))) + { + ERR("Failed to open display\n"); + return FALSE; + } + + fcntl( ConnectionNumber(gdi_display), F_SETFD, 1 ); /* set close on exec flag */ + + nine_dll_init(inst); + break; + case DLL_PROCESS_DETACH: + if (!reserved) + return nine_dll_destroy(inst); + break; + } + + return TRUE; +} + +/*********************************************************************** + * D3DPERF_BeginEvent (D3D9.@) + */ +int WINAPI D3DPERF_BeginEvent(D3DCOLOR color, const WCHAR *name) +{ + TRACE("color 0x%08x, name %s.\n", color, debugstr_w(name)); + + return D3DPERF_event_level++; +} + +/*********************************************************************** + * D3DPERF_EndEvent (D3D9.@) + */ +int WINAPI D3DPERF_EndEvent(void) +{ + TRACE("(void) : stub\n"); + + return --D3DPERF_event_level; +} + +/*********************************************************************** + * D3DPERF_GetStatus (D3D9.@) + */ +DWORD WINAPI D3DPERF_GetStatus(void) +{ + FIXME("(void) : stub\n"); + + return 0; +} + +/*********************************************************************** + * D3DPERF_SetOptions (D3D9.@) + * + */ +void WINAPI D3DPERF_SetOptions(DWORD options) +{ + FIXME("(%#x) : stub\n", options); +} + +/*********************************************************************** + * D3DPERF_QueryRepeatFrame (D3D9.@) + */ +BOOL WINAPI D3DPERF_QueryRepeatFrame(void) +{ + FIXME("(void) : stub\n"); + + return FALSE; +} + +/*********************************************************************** + * D3DPERF_SetMarker (D3D9.@) + */ +void WINAPI D3DPERF_SetMarker(D3DCOLOR color, const WCHAR *name) +{ + FIXME("color 0x%08x, name %s stub!\n", color, debugstr_w(name)); +} + +/*********************************************************************** + * D3DPERF_SetRegion (D3D9.@) + */ +void WINAPI D3DPERF_SetRegion(D3DCOLOR color, const WCHAR *name) +{ + FIXME("color 0x%08x, name %s stub!\n", color, debugstr_w(name)); +} diff --git a/dlls/d3d9-nine/d3dadapter9.c b/dlls/d3d9-nine/d3dadapter9.c new file mode 100644 index 0000000000..daa9524361 --- /dev/null +++ b/dlls/d3d9-nine/d3dadapter9.c @@ -0,0 +1,853 @@ +/* + * Wine IDirect3D9 interface using ID3DAdapter9 + * + * Copyright 2013 Joakim Sindholt + * Christoph Bumiller + * Copyright 2014 David Heidelberger + * Copyright 2014-2015 Axel Davy + * Copyright 2015 Nick Sarnie + * Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d9nine); + +#include +#include "present.h" +#include "device_wrap.h" + +/* this represents a snapshot taken at the moment of creation */ +struct output +{ + D3DDISPLAYROTATION rotation; /* current rotation */ + D3DDISPLAYMODEEX *modes; + unsigned nmodes; + unsigned nmodesalloc; + unsigned current; /* current mode num */ + + HMONITOR monitor; +}; + +struct adapter_group +{ + struct output *outputs; + unsigned noutputs; + unsigned noutputsalloc; + + /* override driver provided DeviceName with this to homogenize device names + * with wine */ + WCHAR devname[32]; + + /* driver stuff */ + ID3DAdapter9 *adapter; +}; + +struct adapter_map +{ + unsigned group; + unsigned master; +}; + +struct d3dadapter9 +{ + /* COM vtable */ + void *vtable; + /* IUnknown reference count */ + LONG refs; + + /* adapter groups and mappings */ + struct adapter_group *groups; + struct adapter_map *map; + unsigned nadapters; + unsigned ngroups; + unsigned ngroupsalloc; + + /* true if it implements IDirect3D9Ex */ + boolean ex; + Display *gdi_display; +}; + +/* convenience wrapper for calls into ID3D9Adapter */ +#define ADAPTER_GROUP \ + This->groups[This->map[Adapter].group] + +#define ADAPTER_PROC(name, ...) \ + ID3DAdapter9_##name(ADAPTER_GROUP.adapter, ## __VA_ARGS__) + +#define ADAPTER_OUTPUT \ + ADAPTER_GROUP.outputs[Adapter-This->map[Adapter].master] + +static HRESULT WINAPI d3dadapter9_CheckDeviceFormat(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, + DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat); + +static ULONG WINAPI d3dadapter9_AddRef(struct d3dadapter9 *This) +{ + ULONG refs = InterlockedIncrement(&This->refs); + TRACE("%p increasing refcount to %u.\n", This, refs); + return refs; +} + +static ULONG WINAPI d3dadapter9_Release(struct d3dadapter9 *This) +{ + ULONG refs = InterlockedDecrement(&This->refs); + TRACE("%p decreasing refcount to %u.\n", This, refs); + if (refs == 0) + { + /* dtor */ + if (This->map) + { + HeapFree(GetProcessHeap(), 0, This->map); + } + + if (This->groups) + { + int i, j; + for (i = 0; i < This->ngroups; ++i) + { + if (This->groups[i].outputs) + { + for (j = 0; j < This->groups[i].noutputs; ++j) + { + if (This->groups[i].outputs[j].modes) + { + HeapFree(GetProcessHeap(), 0, + This->groups[i].outputs[j].modes); + } + } + HeapFree(GetProcessHeap(), 0, This->groups[i].outputs); + } + + if (This->groups[i].adapter) + ID3DAdapter9_Release(This->groups[i].adapter); + } + HeapFree(GetProcessHeap(), 0, This->groups); + } + + HeapFree(GetProcessHeap(), 0, This); + } + return refs; +} + +static HRESULT WINAPI d3dadapter9_QueryInterface(struct d3dadapter9 *This, + REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + if ((IsEqualGUID(&IID_IDirect3D9Ex, riid) && This->ex) || + IsEqualGUID(&IID_IDirect3D9, riid) || + IsEqualGUID(&IID_IUnknown, riid)) + { + *ppvObject = This; + d3dadapter9_AddRef(This); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); + *ppvObject = NULL; + + return E_NOINTERFACE; +} + +static HRESULT WINAPI d3dadapter9_RegisterSoftwareDevice(struct d3dadapter9 *This, + void *pInitializeFunction) +{ + FIXME("(%p, %p), stub!\n", This, pInitializeFunction); + return D3DERR_INVALIDCALL; +} + +static UINT WINAPI d3dadapter9_GetAdapterCount(struct d3dadapter9 *This) +{ + return This->nadapters; +} + +static HRESULT WINAPI d3dadapter9_GetAdapterIdentifier(struct d3dadapter9 *This, + UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9 *pIdentifier) +{ + HRESULT hr; + HKEY regkey; + + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + hr = ADAPTER_PROC(GetAdapterIdentifier, Flags, pIdentifier); + if (SUCCEEDED(hr)) + { + /* Override the driver provided DeviceName with what Wine provided */ + ZeroMemory(pIdentifier->DeviceName, sizeof(pIdentifier->DeviceName)); + if (!WideCharToMultiByte(CP_ACP, 0, ADAPTER_GROUP.devname, -1, + pIdentifier->DeviceName, sizeof(pIdentifier->DeviceName), NULL, NULL)) + return D3DERR_INVALIDCALL; + + TRACE("DeviceName overriden: %s\n", pIdentifier->DeviceName); + + /* Override PCI IDs when wined3d registry keys are set */ + if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Direct3DNine", ®key)) + { + DWORD type, data; + DWORD size = sizeof(DWORD); + + if (!RegQueryValueExA(regkey, "VideoPciDeviceID", 0, &type, (BYTE *)&data, &size) && + (type == REG_DWORD) && (size == sizeof(DWORD))) + pIdentifier->DeviceId = data; + if (size != sizeof(DWORD)) + { + ERR("VideoPciDeviceID is not a DWORD\n"); + size = sizeof(DWORD); + } + if (!RegQueryValueExA(regkey, "VideoPciVendorID", 0, &type, (BYTE *)&data, &size) && + (type == REG_DWORD) && (size == sizeof(DWORD))) + pIdentifier->VendorId = data; + if (size != sizeof(DWORD)) + ERR("VideoPciVendorID is not a DWORD\n"); + RegCloseKey(regkey); + + TRACE("DeviceId:VendorId overridden: %04X:%04X\n", pIdentifier->DeviceId, pIdentifier->VendorId); + } + } + return hr; +} + +static UINT WINAPI d3dadapter9_GetAdapterModeCount(struct d3dadapter9 *This, + UINT Adapter, D3DFORMAT Format) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + if (FAILED(d3dadapter9_CheckDeviceFormat(This, Adapter, D3DDEVTYPE_HAL, + Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, Format))) + { + WARN("DeviceFormat not available.\n"); + return 0; + } + + TRACE("%u modes.\n", ADAPTER_OUTPUT.nmodes); + return ADAPTER_OUTPUT.nmodes; +} + +static HRESULT WINAPI d3dadapter9_EnumAdapterModes(struct d3dadapter9 *This, + UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE *pMode) +{ + HRESULT hr; + + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + hr = d3dadapter9_CheckDeviceFormat(This, Adapter, D3DDEVTYPE_HAL, + Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, Format); + + if (FAILED(hr)) + { + TRACE("DeviceFormat not available.\n"); + return hr; + } + + if (Mode >= ADAPTER_OUTPUT.nmodes) + { + WARN("Mode %u does not exist.\n", Mode); + return D3DERR_INVALIDCALL; + } + + pMode->Width = ADAPTER_OUTPUT.modes[Mode].Width; + pMode->Height = ADAPTER_OUTPUT.modes[Mode].Height; + pMode->RefreshRate = ADAPTER_OUTPUT.modes[Mode].RefreshRate; + pMode->Format = Format; + + return D3D_OK; +} + +static HRESULT WINAPI d3dadapter9_GetAdapterDisplayMode(struct d3dadapter9 *This, + UINT Adapter, D3DDISPLAYMODE *pMode) +{ + UINT Mode; + + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + Mode = ADAPTER_OUTPUT.current; + pMode->Width = ADAPTER_OUTPUT.modes[Mode].Width; + pMode->Height = ADAPTER_OUTPUT.modes[Mode].Height; + pMode->RefreshRate = ADAPTER_OUTPUT.modes[Mode].RefreshRate; + pMode->Format = ADAPTER_OUTPUT.modes[Mode].Format; + + return D3D_OK; +} + +static HRESULT WINAPI d3dadapter9_CheckDeviceType(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DevType, D3DFORMAT AdapterFormat, + D3DFORMAT BackBufferFormat, BOOL bWindowed) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + return ADAPTER_PROC(CheckDeviceType, + DevType, AdapterFormat, BackBufferFormat, bWindowed); +} + +static HRESULT WINAPI d3dadapter9_CheckDeviceFormat(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, + DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + return ADAPTER_PROC(CheckDeviceFormat, + DeviceType, AdapterFormat, Usage, RType, CheckFormat); +} + +static HRESULT WINAPI d3dadapter9_CheckDeviceMultiSampleType(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, + BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD *pQualityLevels) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + return ADAPTER_PROC(CheckDeviceMultiSampleType, DeviceType, SurfaceFormat, + Windowed, MultiSampleType, pQualityLevels); +} + +static HRESULT WINAPI d3dadapter9_CheckDepthStencilMatch(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, + D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + return ADAPTER_PROC(CheckDepthStencilMatch, DeviceType, AdapterFormat, + RenderTargetFormat, DepthStencilFormat); +} + +static HRESULT WINAPI d3dadapter9_CheckDeviceFormatConversion(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + return ADAPTER_PROC(CheckDeviceFormatConversion, + DeviceType, SourceFormat, TargetFormat); +} + +static HRESULT WINAPI d3dadapter9_GetDeviceCaps(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9 *pCaps) +{ + HRESULT hr; + + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + hr = ADAPTER_PROC(GetDeviceCaps, DeviceType, pCaps); + if (FAILED(hr)) + return hr; + + pCaps->MasterAdapterOrdinal = This->map[Adapter].master; + pCaps->AdapterOrdinalInGroup = Adapter-This->map[Adapter].master; + pCaps->NumberOfAdaptersInGroup = ADAPTER_GROUP.noutputs; + + return hr; +} + +static HMONITOR WINAPI d3dadapter9_GetAdapterMonitor(struct d3dadapter9 *This, + UINT Adapter) +{ + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return (HMONITOR)0; + + return (HMONITOR)ADAPTER_OUTPUT.monitor; +} + +static HRESULT WINAPI DECLSPEC_HOTPATCH d3dadapter9_CreateDeviceEx(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS *pPresentationParameters, + D3DDISPLAYMODEEX *pFullscreenDisplayMode, + IDirect3DDevice9Ex **ppReturnedDeviceInterface); + +static HRESULT WINAPI DECLSPEC_HOTPATCH d3dadapter9_CreateDevice(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS *pPresentationParameters, + IDirect3DDevice9 **ppReturnedDeviceInterface) +{ + HRESULT hr; + hr = d3dadapter9_CreateDeviceEx(This, Adapter, DeviceType, hFocusWindow, + BehaviorFlags, pPresentationParameters, NULL, + (IDirect3DDevice9Ex **)ppReturnedDeviceInterface); + if (FAILED(hr)) + return hr; + + return D3D_OK; +} + +static UINT WINAPI d3dadapter9_GetAdapterModeCountEx(struct d3dadapter9 *This, + UINT Adapter, const D3DDISPLAYMODEFILTER *pFilter) +{ + FIXME("(%p, %u, %p), stub!\n", This, Adapter, pFilter); + return 1; +} + +static HRESULT WINAPI d3dadapter9_EnumAdapterModesEx(struct d3dadapter9 *This, + UINT Adapter, const D3DDISPLAYMODEFILTER *pFilter, UINT Mode, + D3DDISPLAYMODEEX *pMode) +{ + FIXME("(%p, %u, %p, %u, %p), stub!\n", This, Adapter, pFilter, Mode, pMode); + return D3DERR_INVALIDCALL; +} + +static HRESULT WINAPI d3dadapter9_GetAdapterDisplayModeEx(struct d3dadapter9 *This, + UINT Adapter, D3DDISPLAYMODEEX *pMode, D3DDISPLAYROTATION *pRotation) +{ + FIXME("(%p, %u, %p, %p), stub!\n", This, Adapter, pMode, pRotation); + return D3DERR_INVALIDCALL; +} + +static HRESULT WINAPI DECLSPEC_HOTPATCH d3dadapter9_CreateDeviceEx(struct d3dadapter9 *This, + UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS *pPresentationParameters, + D3DDISPLAYMODEEX *pFullscreenDisplayMode, + IDirect3DDevice9Ex **ppReturnedDeviceInterface) +{ + ID3DPresentGroup *present; + HRESULT hr; + + if (Adapter >= d3dadapter9_GetAdapterCount(This)) + return D3DERR_INVALIDCALL; + + { + struct adapter_group *group = &ADAPTER_GROUP; + unsigned nparams, ordinal; + + if (BehaviorFlags & D3DCREATE_ADAPTERGROUP_DEVICE) + { + nparams = group->noutputs; + ordinal = 0; + } + else + { + nparams = 1; + ordinal = Adapter - This->map[Adapter].master; + } + hr = present_create_present_group(This->gdi_display, group->devname, ordinal, + hFocusWindow, pPresentationParameters, nparams, &present, This->ex, + BehaviorFlags); + } + + if (FAILED(hr)) + { + WARN("Failed to create PresentGroup.\n"); + return hr; + } + + if (This->ex) + { + hr = ADAPTER_PROC(CreateDeviceEx, Adapter, DeviceType, hFocusWindow, + BehaviorFlags, pPresentationParameters, pFullscreenDisplayMode, + (IDirect3D9Ex *)This, present, ppReturnedDeviceInterface); + } + else + { + /* CreateDevice on non-ex */ + hr = ADAPTER_PROC(CreateDevice, Adapter, DeviceType, hFocusWindow, + BehaviorFlags, pPresentationParameters, (IDirect3D9 *)This, present, + (IDirect3DDevice9 **)ppReturnedDeviceInterface); + } + if (FAILED(hr)) + { + WARN("ADAPTER_PROC failed.\n"); + ID3DPresentGroup_Release(present); + return hr; + } + + /* Nine returns different vtables for Ex, non Ex and + * if you use the multithread flag or not. This prevents + * things like Steam overlay to work, in addition to the problem + * that functions nine side are not recognized by wine as + * hotpatch-able. If possible, we use our vtable wrapper, + * which solves the problem described above. */ + if (enable_device_vtable_wrapper()) + (*ppReturnedDeviceInterface)->lpVtbl = get_device_vtable(); + return hr; +} + +static HRESULT WINAPI d3dadapter9_GetAdapterLUID(struct d3dadapter9 *This, + UINT Adapter, LUID *pLUID) +{ + FIXME("(%p, %u, %p), stub!\n", This, Adapter, pLUID); + return D3DERR_INVALIDCALL; +} + +static struct adapter_group *add_group(struct d3dadapter9 *This) +{ + if (This->ngroups >= This->ngroupsalloc) + { + void *r; + + if (This->ngroupsalloc == 0) + { + This->ngroupsalloc = 2; + r = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + This->ngroupsalloc*sizeof(struct adapter_group)); + } + else + { + This->ngroupsalloc <<= 1; + r = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->groups, + This->ngroupsalloc*sizeof(struct adapter_group)); + } + + if (!r) + return NULL; + This->groups = r; + } + + return &This->groups[This->ngroups++]; +} + +static void remove_group(struct d3dadapter9 *This) +{ + struct adapter_group *group = &This->groups[This->ngroups-1]; + int i; + + for (i = 0; i < group->noutputs; ++i) + { + HeapFree(GetProcessHeap(), 0, group->outputs[i].modes); + } + HeapFree(GetProcessHeap(), 0, group->outputs); + + ZeroMemory(group, sizeof(struct adapter_group)); + This->ngroups--; +} + +static struct output *add_output(struct d3dadapter9 *This) +{ + struct adapter_group *group = &This->groups[This->ngroups-1]; + + if (group->noutputs >= group->noutputsalloc) + { + void *r; + + if (group->noutputsalloc == 0) + { + group->noutputsalloc = 2; + r = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + group->noutputsalloc*sizeof(struct output)); + } + else + { + group->noutputsalloc <<= 1; + r = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, group->outputs, + group->noutputsalloc*sizeof(struct output)); + } + + if (!r) + return NULL; + group->outputs = r; + } + + return &group->outputs[group->noutputs++]; +} + +static void remove_output(struct d3dadapter9 *This) +{ + struct adapter_group *group = &This->groups[This->ngroups-1]; + struct output *out = &group->outputs[group->noutputs-1]; + + HeapFree(GetProcessHeap(), 0, out->modes); + + ZeroMemory(out, sizeof(struct output)); + group->noutputs--; +} + +static D3DDISPLAYMODEEX *add_mode(struct d3dadapter9 *This) +{ + struct adapter_group *group = &This->groups[This->ngroups-1]; + struct output *out = &group->outputs[group->noutputs-1]; + + if (out->nmodes >= out->nmodesalloc) + { + void *r; + + if (out->nmodesalloc == 0) + { + out->nmodesalloc = 8; + r = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + out->nmodesalloc*sizeof(struct D3DDISPLAYMODEEX)); + } + else + { + out->nmodesalloc <<= 1; + r = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, out->modes, + out->nmodesalloc*sizeof(struct D3DDISPLAYMODEEX)); + } + + if (!r) + return NULL; + out->modes = r; + } + + return &out->modes[out->nmodes++]; +} + +static void remove_mode(struct d3dadapter9 *This) +{ + struct adapter_group *group = &This->groups[This->ngroups-1]; + struct output *out = &group->outputs[group->noutputs-1]; + out->nmodes--; +} + +static HRESULT fill_groups(struct d3dadapter9 *This) +{ + DISPLAY_DEVICEW dd; + DEVMODEW dm; + POINT pt; + HDC hdc; + HRESULT hr; + int i, j, k; + + WCHAR wdisp[] = {'D','I','S','P','L','A','Y',0}; + + ZeroMemory(&dd, sizeof(dd)); + ZeroMemory(&dm, sizeof(dm)); + dd.cb = sizeof(dd); + dm.dmSize = sizeof(dm); + + for (i = 0; EnumDisplayDevicesW(NULL, i, &dd, 0); ++i) + { + struct adapter_group *group = add_group(This); + if (!group) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + hdc = CreateDCW(wdisp, dd.DeviceName, NULL, NULL); + if (!hdc) + { + remove_group(This); + WARN("Unable to create DC for display %d.\n", i); + goto end_group; + } + + hr = present_create_adapter9(This->gdi_display, hdc, &group->adapter); + DeleteDC(hdc); + if (FAILED(hr)) + { + remove_group(This); + goto end_group; + } + + CopyMemory(group->devname, dd.DeviceName, sizeof(group->devname)); + for (j = 0; EnumDisplayDevicesW(group->devname, j, &dd, 0); ++j) + { + struct output *out = add_output(This); + boolean orient = FALSE, monit = FALSE; + if (!out) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + for (k = 0; EnumDisplaySettingsExW(dd.DeviceName, k, &dm, 0); ++k) + { + D3DDISPLAYMODEEX *mode = add_mode(This); + if (!out) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + mode->Size = sizeof(D3DDISPLAYMODEEX); + mode->Width = dm.dmPelsWidth; + mode->Height = dm.dmPelsHeight; + mode->RefreshRate = dm.dmDisplayFrequency; + mode->ScanLineOrdering = + (dm.dmDisplayFlags & DM_INTERLACED) ? + D3DSCANLINEORDERING_INTERLACED : + D3DSCANLINEORDERING_PROGRESSIVE; + + switch (dm.dmBitsPerPel) + { + case 32: mode->Format = D3DFMT_X8R8G8B8; break; + case 24: mode->Format = D3DFMT_R8G8B8; break; + case 16: mode->Format = D3DFMT_R5G6B5; break; + case 8: + remove_mode(This); + goto end_mode; + + default: + remove_mode(This); + WARN("Unknown format (%u bpp) in display %d, monitor " + "%d, mode %d.\n", dm.dmBitsPerPel, i, j, k); + goto end_mode; + } + + if (!orient) + { + switch (dm.dmDisplayOrientation) + { + case DMDO_DEFAULT: + out->rotation = D3DDISPLAYROTATION_IDENTITY; + break; + + case DMDO_90: + out->rotation = D3DDISPLAYROTATION_90; + break; + + case DMDO_180: + out->rotation = D3DDISPLAYROTATION_180; + break; + + case DMDO_270: + out->rotation = D3DDISPLAYROTATION_270; + break; + + default: + remove_output(This); + WARN("Unknown display rotation in display %d, " + "monitor %d\n", i, j); + goto end_output; + } + orient = TRUE; + } + + if (!monit) + { + pt.x = dm.dmPosition.x; + pt.y = dm.dmPosition.y; + out->monitor = MonitorFromPoint(pt, 0); + if (!out->monitor) + { + remove_output(This); + WARN("Unable to get monitor handle for display %d, " + "monitor %d.\n", i, j); + goto end_output; + } + monit = TRUE; + } + +end_mode: + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + } + +end_output: + ZeroMemory(&dd, sizeof(dd)); + dd.cb = sizeof(dd); + } + +end_group: + ZeroMemory(&dd, sizeof(dd)); + dd.cb = sizeof(dd); + } + + return D3D_OK; +} + +static IDirect3D9ExVtbl d3dadapter9_vtable = { + (void *)d3dadapter9_QueryInterface, + (void *)d3dadapter9_AddRef, + (void *)d3dadapter9_Release, + (void *)d3dadapter9_RegisterSoftwareDevice, + (void *)d3dadapter9_GetAdapterCount, + (void *)d3dadapter9_GetAdapterIdentifier, + (void *)d3dadapter9_GetAdapterModeCount, + (void *)d3dadapter9_EnumAdapterModes, + (void *)d3dadapter9_GetAdapterDisplayMode, + (void *)d3dadapter9_CheckDeviceType, + (void *)d3dadapter9_CheckDeviceFormat, + (void *)d3dadapter9_CheckDeviceMultiSampleType, + (void *)d3dadapter9_CheckDepthStencilMatch, + (void *)d3dadapter9_CheckDeviceFormatConversion, + (void *)d3dadapter9_GetDeviceCaps, + (void *)d3dadapter9_GetAdapterMonitor, + (void *)d3dadapter9_CreateDevice, + (void *)d3dadapter9_GetAdapterModeCountEx, + (void *)d3dadapter9_EnumAdapterModesEx, + (void *)d3dadapter9_GetAdapterDisplayModeEx, + (void *)d3dadapter9_CreateDeviceEx, + (void *)d3dadapter9_GetAdapterLUID +}; + +HRESULT d3dadapter9_new(Display *gdi_display, boolean ex, IDirect3D9Ex **ppOut) +{ + struct d3dadapter9 *This; + HRESULT hr; + unsigned i, j, k; + + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct d3dadapter9)); + if (!This) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + This->vtable = &d3dadapter9_vtable; + This->refs = 1; + This->ex = ex; + This->gdi_display = gdi_display; + + if (!present_has_d3dadapter(gdi_display)) + { + ERR("Your display driver doesn't support native D3D9 adapters.\n"); + d3dadapter9_Release(This); + return D3DERR_NOTAVAILABLE; + } + + if (FAILED(hr = fill_groups(This))) + { + d3dadapter9_Release(This); + return hr; + } + + /* map absolute adapter IDs with internal adapters */ + for (i = 0; i < This->ngroups; ++i) + { + for (j = 0; j < This->groups[i].noutputs; ++j) + { + This->nadapters++; + } + } + if (This->nadapters == 0) + { + ERR("No available native adapters in system.\n"); + d3dadapter9_Release(This); + return D3DERR_NOTAVAILABLE; + } + + This->map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + This->nadapters * sizeof(struct adapter_map)); + + if (!This->map) + { + d3dadapter9_Release(This); + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + for (i = k = 0; i < This->ngroups; ++i) + { + for (j = 0; j < This->groups[i].noutputs; ++j, ++k) + { + This->map[k].master = k-j; + This->map[k].group = i; + } + } + + *ppOut = (IDirect3D9Ex *)This; + FIXME("\033[1;32m\nNative Direct3D 9 is active." + "\nFor more information visit https://wiki.ixit.cz/d3d9\033[0m\n"); + return D3D_OK; +} diff --git a/dlls/d3d9-nine/d3dadapter9.h b/dlls/d3d9-nine/d3dadapter9.h new file mode 100644 index 0000000000..ad54f67285 --- /dev/null +++ b/dlls/d3d9-nine/d3dadapter9.h @@ -0,0 +1,32 @@ +/* + * D3DAdapter9 interface + * + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_D3D9ADAPTER_H +#define __WINE_D3D9ADAPTER_H + +#include + +void d3dadapter9_init(HINSTANCE hinst); + +void d3dadapter9_destroy(HINSTANCE hinst); + +HRESULT d3dadapter9_new(Display *gdi_display, boolean ex, IDirect3D9Ex **ppOut); + +#endif /* __WINE_D3D9ADAPTER_H */ diff --git a/dlls/d3d9-nine/device_wrap.c b/dlls/d3d9-nine/device_wrap.c new file mode 100644 index 0000000000..e662c6f89a --- /dev/null +++ b/dlls/d3d9-nine/device_wrap.c @@ -0,0 +1,500 @@ +/* + * Copyright 2016 Axel Davy + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include + +#include "device_wrap.h" + +struct IDirect3DDevice9Ex_Minor1 +{ + IDirect3DDevice9ExVtbl *lpVtbl; + IDirect3DDevice9ExVtbl *lpVtbl_internal; +}; + +struct IDirect3DSwapChain9Ex_Minor1 +{ + IDirect3DSwapChain9ExVtbl *lpVtbl; + IDirect3DSwapChain9ExVtbl *lpVtbl_internal; +}; + +typedef struct IDirect3DDevice9Ex_Minor1 IDirect3DDevice9Ex_Minor1; +typedef struct IDirect3DSwapChain9Ex_Minor1 IDirect3DSwapChain9Ex_Minor1; + +#define SWAPCHAIN_WRAP0(ret, func) \ + ret WINAPI WineNineSwapChain9_ ## func(IDirect3DSwapChain9Ex *This) \ + { \ + return ((IDirect3DSwapChain9Ex_Minor1 *)This)->lpVtbl_internal->func(This); \ + } + +#define SWAPCHAIN_WRAP1(ret, func, type1) \ + ret WINAPI WineNineSwapChain9_ ## func(IDirect3DSwapChain9Ex *This, type1 arg1) \ + { \ + return ((IDirect3DSwapChain9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1); \ + } + +#define SWAPCHAIN_WRAP2(ret, func, type1, type2) \ + ret WINAPI WineNineSwapChain9_ ## func(IDirect3DSwapChain9Ex *This, type1 arg1, type2 arg2) \ + { \ + return ((IDirect3DSwapChain9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2); \ + } + +#define SWAPCHAIN_WRAP3(ret, func, type1, type2, type3) \ + ret WINAPI WineNineSwapChain9_ ## func(IDirect3DSwapChain9Ex *This, type1 arg1, type2 arg2, type3 arg3) \ + { \ + return ((IDirect3DSwapChain9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3); \ + } + +#define SWAPCHAIN_H_WRAP5(ret, func, type1, type2, type3, type4, type5) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineSwapChain9_ ## func(IDirect3DSwapChain9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + return ((IDirect3DSwapChain9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5); \ + } + +#define DEVICE_WRAP0(ret, func) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This); \ + } + +#define DEVICE_WRAP1(ret, func, type1) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1); \ + } + +#define DEVICE_WRAP2(ret, func, type1, type2) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2); \ + } + +#define DEVICE_WRAP3(ret, func, type1, type2, type3) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3); \ + } + +#define DEVICE_WRAP4(ret, func, type1, type2, type3, type4) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4); \ + } + +#define DEVICE_WRAP5(ret, func, type1, type2, type3, type4, type5) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5); \ + } + +#define DEVICE_WRAP6(ret, func, type1, type2, type3, type4, type5, type6) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + +#define DEVICE_WRAP7(ret, func, type1, type2, type3, type4, type5, type6, type7) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5, arg6, arg7); \ + } + +#define DEVICE_WRAP8(ret, func, type1, type2, type3, type4, type5, type6, type7, type8) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); \ + } + +#define DEVICE_WRAP9(ret, func, type1, type2, type3, type4, type5, type6, type7, type8, type9) \ + ret WINAPI WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6, type7 arg7, type8 arg8, type9 arg9) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \ + } + +#define DEVICE_H_WRAP0(ret, func) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This); \ + } + +#define DEVICE_H_WRAP1(ret, func, type1) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1); \ + } + +#define DEVICE_H_WRAP2(ret, func, type1, type2) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2); \ + } + +#define DEVICE_H_WRAP3(ret, func, type1, type2, type3) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3); \ + } + +#define DEVICE_H_WRAP4(ret, func, type1, type2, type3, type4) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4); \ + } + +#define DEVICE_H_WRAP5(ret, func, type1, type2, type3, type4, type5) \ + ret WINAPI DECLSPEC_HOTPATCH WineNineDevice9_ ## func(IDirect3DDevice9Ex *This, type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + return ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->func(This, arg1, arg2, arg3, arg4, arg5); \ + } + +SWAPCHAIN_WRAP2(HRESULT, QueryInterface, REFIID, void **) +SWAPCHAIN_WRAP0(ULONG, AddRef) +SWAPCHAIN_WRAP0(ULONG, Release) +SWAPCHAIN_H_WRAP5(HRESULT, Present, const RECT *, const RECT *, HWND, const RGNDATA *, DWORD) +SWAPCHAIN_WRAP1(HRESULT, GetFrontBufferData, IDirect3DSurface9 *) +SWAPCHAIN_WRAP3(HRESULT, GetBackBuffer, UINT, D3DBACKBUFFER_TYPE, IDirect3DSurface9 **) +SWAPCHAIN_WRAP1(HRESULT, GetRasterStatus, D3DRASTER_STATUS *) +SWAPCHAIN_WRAP1(HRESULT, GetDisplayMode, D3DDISPLAYMODE *) +SWAPCHAIN_WRAP1(HRESULT, GetDevice, IDirect3DDevice9 **) +SWAPCHAIN_WRAP1(HRESULT, GetPresentParameters, D3DPRESENT_PARAMETERS *) +SWAPCHAIN_WRAP1(HRESULT, GetLastPresentCount, UINT *) +SWAPCHAIN_WRAP1(HRESULT, GetPresentStats, D3DPRESENTSTATS *) +SWAPCHAIN_WRAP2(HRESULT, GetDisplayModeEx, D3DDISPLAYMODEEX *, D3DDISPLAYROTATION *) + +DEVICE_WRAP2(HRESULT, QueryInterface, REFIID, void **) +DEVICE_WRAP0(ULONG, AddRef) +DEVICE_H_WRAP0(ULONG, Release) +DEVICE_WRAP0(HRESULT, TestCooperativeLevel) +DEVICE_WRAP0(UINT, GetAvailableTextureMem) +DEVICE_WRAP0(HRESULT, EvictManagedResources) +DEVICE_WRAP1(HRESULT, GetDirect3D, IDirect3D9 **) +DEVICE_WRAP1(HRESULT, GetDeviceCaps, D3DCAPS9 *) +DEVICE_WRAP2(HRESULT, GetDisplayMode, UINT, D3DDISPLAYMODE*) +DEVICE_WRAP1(HRESULT, GetCreationParameters, D3DDEVICE_CREATION_PARAMETERS *) +DEVICE_WRAP3(HRESULT, SetCursorProperties, UINT, UINT, IDirect3DSurface9 *) +DEVICE_WRAP3(void, SetCursorPosition, int, int, DWORD) +DEVICE_WRAP1(BOOL, ShowCursor, BOOL) +/*DEVICE_H_WRAP2(HRESULT, CreateAdditionalSwapChain, D3DPRESENT_PARAMETERS *, IDirect3DSwapChain9 **)*/ +/*DEVICE_H_WRAP2(HRESULT, GetSwapChain, UINT, IDirect3DSwapChain9 **)*/ +DEVICE_WRAP0(UINT, GetNumberOfSwapChains) +DEVICE_H_WRAP1(HRESULT, Reset, D3DPRESENT_PARAMETERS *) +DEVICE_H_WRAP4(HRESULT, Present, const RECT *, const RECT *, HWND, const RGNDATA *) +DEVICE_WRAP4(HRESULT, GetBackBuffer, UINT, UINT, D3DBACKBUFFER_TYPE, IDirect3DSurface9 **) +DEVICE_WRAP2(HRESULT, GetRasterStatus, UINT, D3DRASTER_STATUS *) +DEVICE_WRAP1(HRESULT, SetDialogBoxMode, BOOL) +DEVICE_H_WRAP3(void, SetGammaRamp, UINT, DWORD, const D3DGAMMARAMP *) +DEVICE_WRAP2(void, GetGammaRamp, UINT, D3DGAMMARAMP *) +DEVICE_WRAP8(HRESULT, CreateTexture, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DTexture9 **, HANDLE *) +DEVICE_WRAP9(HRESULT, CreateVolumeTexture, UINT, UINT, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DVolumeTexture9 **, HANDLE *) +DEVICE_WRAP7(HRESULT, CreateCubeTexture, UINT, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DCubeTexture9 **, HANDLE *) +DEVICE_WRAP6(HRESULT, CreateVertexBuffer, UINT, DWORD, DWORD, D3DPOOL, IDirect3DVertexBuffer9 **, HANDLE *) +DEVICE_WRAP6(HRESULT, CreateIndexBuffer, UINT, DWORD, D3DFORMAT, D3DPOOL, IDirect3DIndexBuffer9 **, HANDLE *) +DEVICE_WRAP8(HRESULT, CreateRenderTarget, UINT, UINT, D3DFORMAT, D3DMULTISAMPLE_TYPE, DWORD, BOOL, IDirect3DSurface9 **, HANDLE *) +DEVICE_WRAP8(HRESULT, CreateDepthStencilSurface, UINT, UINT, D3DFORMAT, D3DMULTISAMPLE_TYPE, DWORD, BOOL, IDirect3DSurface9 **, HANDLE *) +DEVICE_WRAP4(HRESULT, UpdateSurface, IDirect3DSurface9 *, const RECT *, IDirect3DSurface9 *, const POINT *) +DEVICE_WRAP2(HRESULT, UpdateTexture, IDirect3DBaseTexture9 *, IDirect3DBaseTexture9 *) +DEVICE_WRAP2(HRESULT, GetRenderTargetData, IDirect3DSurface9 *, IDirect3DSurface9 *) +DEVICE_WRAP2(HRESULT, GetFrontBufferData, UINT, IDirect3DSurface9 *) +DEVICE_WRAP5(HRESULT, StretchRect, IDirect3DSurface9 *, const RECT *, IDirect3DSurface9 *, const RECT *, D3DTEXTUREFILTERTYPE) +DEVICE_WRAP3(HRESULT, ColorFill, IDirect3DSurface9 *, const RECT *, D3DCOLOR) +DEVICE_WRAP6(HRESULT, CreateOffscreenPlainSurface, UINT, UINT, D3DFORMAT, D3DPOOL, IDirect3DSurface9 **, HANDLE *) +DEVICE_WRAP2(HRESULT, SetRenderTarget, DWORD, IDirect3DSurface9 *) +DEVICE_WRAP2(HRESULT, GetRenderTarget, DWORD, IDirect3DSurface9 **) +DEVICE_WRAP1(HRESULT, SetDepthStencilSurface, IDirect3DSurface9 *) +DEVICE_WRAP1(HRESULT, GetDepthStencilSurface, IDirect3DSurface9 **) +DEVICE_WRAP0(HRESULT, BeginScene) +DEVICE_H_WRAP0(HRESULT, EndScene) +DEVICE_WRAP6(HRESULT, Clear, DWORD, const D3DRECT *, DWORD, D3DCOLOR, float, DWORD) +DEVICE_WRAP2(HRESULT, SetTransform, D3DTRANSFORMSTATETYPE, const D3DMATRIX *) +DEVICE_WRAP2(HRESULT, GetTransform, D3DTRANSFORMSTATETYPE, D3DMATRIX *) +DEVICE_WRAP2(HRESULT, MultiplyTransform, D3DTRANSFORMSTATETYPE, const D3DMATRIX *) +DEVICE_WRAP1(HRESULT, SetViewport, const D3DVIEWPORT9 *) +DEVICE_WRAP1(HRESULT, GetViewport, D3DVIEWPORT9 *) +DEVICE_WRAP1(HRESULT, SetMaterial, const D3DMATERIAL9 *) +DEVICE_WRAP1(HRESULT, GetMaterial, D3DMATERIAL9 *) +DEVICE_WRAP2(HRESULT, SetLight, DWORD, const D3DLIGHT9 *) +DEVICE_WRAP2(HRESULT, GetLight, DWORD, D3DLIGHT9 *) +DEVICE_WRAP2(HRESULT, LightEnable, DWORD, BOOL) +DEVICE_WRAP2(HRESULT, GetLightEnable, DWORD, BOOL *) +DEVICE_WRAP2(HRESULT, SetClipPlane, DWORD, const float *) +DEVICE_WRAP2(HRESULT, GetClipPlane, DWORD, float *) +DEVICE_H_WRAP2(HRESULT, SetRenderState, D3DRENDERSTATETYPE, DWORD) +DEVICE_WRAP2(HRESULT, GetRenderState, D3DRENDERSTATETYPE, DWORD *) +DEVICE_WRAP2(HRESULT, CreateStateBlock, D3DSTATEBLOCKTYPE, IDirect3DStateBlock9 **) +DEVICE_WRAP0(HRESULT, BeginStateBlock) +DEVICE_WRAP1(HRESULT, EndStateBlock, IDirect3DStateBlock9 **) +DEVICE_WRAP1(HRESULT, SetClipStatus, const D3DCLIPSTATUS9 *) +DEVICE_WRAP1(HRESULT, GetClipStatus, D3DCLIPSTATUS9 *) +DEVICE_WRAP2(HRESULT, GetTexture, DWORD, IDirect3DBaseTexture9 **) +DEVICE_WRAP2(HRESULT, SetTexture, DWORD, IDirect3DBaseTexture9 *) +DEVICE_WRAP3(HRESULT, GetTextureStageState, DWORD, D3DTEXTURESTAGESTATETYPE, DWORD *) +DEVICE_WRAP3(HRESULT, SetTextureStageState, DWORD, D3DTEXTURESTAGESTATETYPE, DWORD) +DEVICE_WRAP3(HRESULT, GetSamplerState, DWORD, D3DSAMPLERSTATETYPE, DWORD *) +DEVICE_H_WRAP3(HRESULT, SetSamplerState, DWORD, D3DSAMPLERSTATETYPE, DWORD) +DEVICE_WRAP1(HRESULT, ValidateDevice, DWORD *) +DEVICE_WRAP2(HRESULT, SetPaletteEntries, UINT, const PALETTEENTRY *) +DEVICE_WRAP2(HRESULT, GetPaletteEntries, UINT, PALETTEENTRY *) +DEVICE_WRAP1(HRESULT, SetCurrentTexturePalette, UINT) +DEVICE_WRAP1(HRESULT, GetCurrentTexturePalette, UINT *) +DEVICE_WRAP1(HRESULT, SetScissorRect, const RECT *) +DEVICE_WRAP1(HRESULT, GetScissorRect, RECT *) +DEVICE_WRAP1(HRESULT, SetSoftwareVertexProcessing, BOOL) +DEVICE_WRAP0(BOOL, GetSoftwareVertexProcessing) +DEVICE_WRAP1(HRESULT, SetNPatchMode, float) +DEVICE_WRAP0(float, GetNPatchMode) +DEVICE_WRAP3(HRESULT, DrawPrimitive, D3DPRIMITIVETYPE, UINT, UINT) +DEVICE_WRAP6(HRESULT, DrawIndexedPrimitive, D3DPRIMITIVETYPE, INT, UINT, UINT, UINT, UINT) +DEVICE_WRAP4(HRESULT, DrawPrimitiveUP, D3DPRIMITIVETYPE, UINT, const void *, UINT) +DEVICE_WRAP8(HRESULT, DrawIndexedPrimitiveUP, D3DPRIMITIVETYPE, UINT, UINT, UINT, const void *, D3DFORMAT, const void *, UINT) +DEVICE_WRAP6(HRESULT, ProcessVertices, UINT, UINT, UINT, IDirect3DVertexBuffer9 *, IDirect3DVertexDeclaration9 *, DWORD) +DEVICE_WRAP2(HRESULT, CreateVertexDeclaration, const D3DVERTEXELEMENT9 *, IDirect3DVertexDeclaration9 **) +DEVICE_WRAP1(HRESULT, SetVertexDeclaration, IDirect3DVertexDeclaration9 *) +DEVICE_WRAP1(HRESULT, GetVertexDeclaration, IDirect3DVertexDeclaration9 **) +DEVICE_WRAP1(HRESULT, SetFVF, DWORD) +DEVICE_WRAP1(HRESULT, GetFVF, DWORD *) +DEVICE_WRAP2(HRESULT, CreateVertexShader, const DWORD *, IDirect3DVertexShader9 **) +DEVICE_WRAP1(HRESULT, SetVertexShader, IDirect3DVertexShader9 *) +DEVICE_WRAP1(HRESULT, GetVertexShader, IDirect3DVertexShader9 **) +DEVICE_WRAP3(HRESULT, SetVertexShaderConstantF, UINT, const float *, UINT) +DEVICE_WRAP3(HRESULT, GetVertexShaderConstantF, UINT, float *, UINT) +DEVICE_WRAP3(HRESULT, SetVertexShaderConstantI, UINT, const int *, UINT) +DEVICE_WRAP3(HRESULT, GetVertexShaderConstantI, UINT, int *, UINT) +DEVICE_WRAP3(HRESULT, SetVertexShaderConstantB, UINT, const BOOL *, UINT) +DEVICE_WRAP3(HRESULT, GetVertexShaderConstantB, UINT, BOOL *, UINT) +DEVICE_WRAP4(HRESULT, SetStreamSource, UINT, IDirect3DVertexBuffer9 *, UINT, UINT) +DEVICE_WRAP4(HRESULT, GetStreamSource, UINT, IDirect3DVertexBuffer9 **, UINT *, UINT *) +DEVICE_WRAP2(HRESULT, SetStreamSourceFreq, UINT, UINT) +DEVICE_WRAP2(HRESULT, GetStreamSourceFreq, UINT, UINT *) +DEVICE_WRAP1(HRESULT, SetIndices, IDirect3DIndexBuffer9 *) +DEVICE_WRAP1(HRESULT, GetIndices, IDirect3DIndexBuffer9 **) +DEVICE_WRAP2(HRESULT, CreatePixelShader, const DWORD *, IDirect3DPixelShader9 **) +DEVICE_WRAP1(HRESULT, SetPixelShader, IDirect3DPixelShader9 *) +DEVICE_WRAP1(HRESULT, GetPixelShader, IDirect3DPixelShader9 **) +DEVICE_WRAP3(HRESULT, SetPixelShaderConstantF, UINT, const float *, UINT) +DEVICE_WRAP3(HRESULT, GetPixelShaderConstantF, UINT, float *, UINT) +DEVICE_WRAP3(HRESULT, SetPixelShaderConstantI, UINT, const int *, UINT) +DEVICE_WRAP3(HRESULT, GetPixelShaderConstantI, UINT, int *, UINT) +DEVICE_WRAP3(HRESULT, SetPixelShaderConstantB, UINT, const BOOL *, UINT) +DEVICE_WRAP3(HRESULT, GetPixelShaderConstantB, UINT, BOOL *, UINT) +DEVICE_WRAP3(HRESULT, DrawRectPatch, UINT, const float *, const D3DRECTPATCH_INFO *) +DEVICE_WRAP3(HRESULT, DrawTriPatch, UINT, const float *, const D3DTRIPATCH_INFO *) +DEVICE_WRAP1(HRESULT, DeletePatch, UINT) +DEVICE_WRAP2(HRESULT, CreateQuery, D3DQUERYTYPE, IDirect3DQuery9 **) +DEVICE_WRAP4(HRESULT, SetConvolutionMonoKernel, UINT, UINT, float *, float *) +DEVICE_WRAP8(HRESULT, ComposeRects, IDirect3DSurface9 *, IDirect3DSurface9 *, IDirect3DVertexBuffer9 *, UINT, IDirect3DVertexBuffer9 *, D3DCOMPOSERECTSOP, int, int) +DEVICE_H_WRAP5(HRESULT, PresentEx, const RECT *, const RECT *, HWND, const RGNDATA *, DWORD) +DEVICE_WRAP1(HRESULT, GetGPUThreadPriority, INT *) +DEVICE_WRAP1(HRESULT, SetGPUThreadPriority, INT) +DEVICE_WRAP1(HRESULT, WaitForVBlank, UINT) +DEVICE_WRAP2(HRESULT, CheckResourceResidency, IDirect3DResource9 **, UINT32) +DEVICE_WRAP1(HRESULT, SetMaximumFrameLatency, UINT) +DEVICE_WRAP1(HRESULT, GetMaximumFrameLatency, UINT *) +DEVICE_WRAP1(HRESULT, CheckDeviceState, HWND) +DEVICE_WRAP9(HRESULT, CreateRenderTargetEx, UINT, UINT, D3DFORMAT, D3DMULTISAMPLE_TYPE, DWORD, BOOL, IDirect3DSurface9 **, HANDLE *, DWORD) +DEVICE_WRAP7(HRESULT, CreateOffscreenPlainSurfaceEx, UINT, UINT, D3DFORMAT, D3DPOOL, IDirect3DSurface9 **, HANDLE *, DWORD) +DEVICE_WRAP9(HRESULT, CreateDepthStencilSurfaceEx, UINT, UINT, D3DFORMAT, D3DMULTISAMPLE_TYPE, DWORD, BOOL, IDirect3DSurface9 **, HANDLE *, DWORD) +DEVICE_H_WRAP2(HRESULT, ResetEx, D3DPRESENT_PARAMETERS *, D3DDISPLAYMODEEX *) +DEVICE_WRAP3(HRESULT, GetDisplayModeEx, UINT, D3DDISPLAYMODEEX *, D3DDISPLAYROTATION *) + +IDirect3DSwapChain9ExVtbl WineNineSwapChain9Ex_vtable = { + WineNineSwapChain9_QueryInterface, + WineNineSwapChain9_AddRef, + WineNineSwapChain9_Release, + WineNineSwapChain9_Present, + WineNineSwapChain9_GetFrontBufferData, + WineNineSwapChain9_GetBackBuffer, + WineNineSwapChain9_GetRasterStatus, + WineNineSwapChain9_GetDisplayMode, + WineNineSwapChain9_GetDevice, + WineNineSwapChain9_GetPresentParameters, + WineNineSwapChain9_GetLastPresentCount, + WineNineSwapChain9_GetPresentStats, + WineNineSwapChain9_GetDisplayModeEx +}; + +HRESULT WINAPI DECLSPEC_HOTPATCH WineNineDevice9_CreateAdditionalSwapChain(IDirect3DDevice9Ex *This, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DSwapChain9 **pSwapChain) +{ + HRESULT hr; + + hr = ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->CreateAdditionalSwapChain(This, pPresentationParameters, pSwapChain); + if (FAILED(hr)) + return hr; + + (*pSwapChain)->lpVtbl = (IDirect3DSwapChain9Vtbl *)&WineNineSwapChain9Ex_vtable; + + return hr; +} + +HRESULT WINAPI DECLSPEC_HOTPATCH WineNineDevice9_GetSwapChain(IDirect3DDevice9Ex *This, UINT iSwapChain, IDirect3DSwapChain9 **pSwapChain) +{ + HRESULT hr; + + hr = ((IDirect3DDevice9Ex_Minor1 *)This)->lpVtbl_internal->GetSwapChain(This, iSwapChain, pSwapChain); + if (FAILED(hr)) + return hr; + + (*pSwapChain)->lpVtbl = (IDirect3DSwapChain9Vtbl *)&WineNineSwapChain9Ex_vtable; + + return hr; +} + + +IDirect3DDevice9ExVtbl WineNineDevice9_vtable = { + WineNineDevice9_QueryInterface, + WineNineDevice9_AddRef, + WineNineDevice9_Release, + WineNineDevice9_TestCooperativeLevel, + WineNineDevice9_GetAvailableTextureMem, + WineNineDevice9_EvictManagedResources, + WineNineDevice9_GetDirect3D, + WineNineDevice9_GetDeviceCaps, + WineNineDevice9_GetDisplayMode, + WineNineDevice9_GetCreationParameters, + WineNineDevice9_SetCursorProperties, + WineNineDevice9_SetCursorPosition, + WineNineDevice9_ShowCursor, + WineNineDevice9_CreateAdditionalSwapChain, + WineNineDevice9_GetSwapChain, + WineNineDevice9_GetNumberOfSwapChains, + WineNineDevice9_Reset, + WineNineDevice9_Present, + WineNineDevice9_GetBackBuffer, + WineNineDevice9_GetRasterStatus, + WineNineDevice9_SetDialogBoxMode, + WineNineDevice9_SetGammaRamp, + WineNineDevice9_GetGammaRamp, + WineNineDevice9_CreateTexture, + WineNineDevice9_CreateVolumeTexture, + WineNineDevice9_CreateCubeTexture, + WineNineDevice9_CreateVertexBuffer, + WineNineDevice9_CreateIndexBuffer, + WineNineDevice9_CreateRenderTarget, + WineNineDevice9_CreateDepthStencilSurface, + WineNineDevice9_UpdateSurface, + WineNineDevice9_UpdateTexture, + WineNineDevice9_GetRenderTargetData, + WineNineDevice9_GetFrontBufferData, + WineNineDevice9_StretchRect, + WineNineDevice9_ColorFill, + WineNineDevice9_CreateOffscreenPlainSurface, + WineNineDevice9_SetRenderTarget, + WineNineDevice9_GetRenderTarget, + WineNineDevice9_SetDepthStencilSurface, + WineNineDevice9_GetDepthStencilSurface, + WineNineDevice9_BeginScene, + WineNineDevice9_EndScene, + WineNineDevice9_Clear, + WineNineDevice9_SetTransform, + WineNineDevice9_GetTransform, + WineNineDevice9_MultiplyTransform, + WineNineDevice9_SetViewport, + WineNineDevice9_GetViewport, + WineNineDevice9_SetMaterial, + WineNineDevice9_GetMaterial, + WineNineDevice9_SetLight, + WineNineDevice9_GetLight, + WineNineDevice9_LightEnable, + WineNineDevice9_GetLightEnable, + WineNineDevice9_SetClipPlane, + WineNineDevice9_GetClipPlane, + WineNineDevice9_SetRenderState, + WineNineDevice9_GetRenderState, + WineNineDevice9_CreateStateBlock, + WineNineDevice9_BeginStateBlock, + WineNineDevice9_EndStateBlock, + WineNineDevice9_SetClipStatus, + WineNineDevice9_GetClipStatus, + WineNineDevice9_GetTexture, + WineNineDevice9_SetTexture, + WineNineDevice9_GetTextureStageState, + WineNineDevice9_SetTextureStageState, + WineNineDevice9_GetSamplerState, + WineNineDevice9_SetSamplerState, + WineNineDevice9_ValidateDevice, + WineNineDevice9_SetPaletteEntries, + WineNineDevice9_GetPaletteEntries, + WineNineDevice9_SetCurrentTexturePalette, + WineNineDevice9_GetCurrentTexturePalette, + WineNineDevice9_SetScissorRect, + WineNineDevice9_GetScissorRect, + WineNineDevice9_SetSoftwareVertexProcessing, + WineNineDevice9_GetSoftwareVertexProcessing, + WineNineDevice9_SetNPatchMode, + WineNineDevice9_GetNPatchMode, + WineNineDevice9_DrawPrimitive, + WineNineDevice9_DrawIndexedPrimitive, + WineNineDevice9_DrawPrimitiveUP, + WineNineDevice9_DrawIndexedPrimitiveUP, + WineNineDevice9_ProcessVertices, + WineNineDevice9_CreateVertexDeclaration, + WineNineDevice9_SetVertexDeclaration, + WineNineDevice9_GetVertexDeclaration, + WineNineDevice9_SetFVF, + WineNineDevice9_GetFVF, + WineNineDevice9_CreateVertexShader, + WineNineDevice9_SetVertexShader, + WineNineDevice9_GetVertexShader, + WineNineDevice9_SetVertexShaderConstantF, + WineNineDevice9_GetVertexShaderConstantF, + WineNineDevice9_SetVertexShaderConstantI, + WineNineDevice9_GetVertexShaderConstantI, + WineNineDevice9_SetVertexShaderConstantB, + WineNineDevice9_GetVertexShaderConstantB, + WineNineDevice9_SetStreamSource, + WineNineDevice9_GetStreamSource, + WineNineDevice9_SetStreamSourceFreq, + WineNineDevice9_GetStreamSourceFreq, + WineNineDevice9_SetIndices, + WineNineDevice9_GetIndices, + WineNineDevice9_CreatePixelShader, + WineNineDevice9_SetPixelShader, + WineNineDevice9_GetPixelShader, + WineNineDevice9_SetPixelShaderConstantF, + WineNineDevice9_GetPixelShaderConstantF, + WineNineDevice9_SetPixelShaderConstantI, + WineNineDevice9_GetPixelShaderConstantI, + WineNineDevice9_SetPixelShaderConstantB, + WineNineDevice9_GetPixelShaderConstantB, + WineNineDevice9_DrawRectPatch, + WineNineDevice9_DrawTriPatch, + WineNineDevice9_DeletePatch, + WineNineDevice9_CreateQuery, + WineNineDevice9_SetConvolutionMonoKernel, + WineNineDevice9_ComposeRects, + WineNineDevice9_PresentEx, + WineNineDevice9_GetGPUThreadPriority, + WineNineDevice9_SetGPUThreadPriority, + WineNineDevice9_WaitForVBlank, + WineNineDevice9_CheckResourceResidency, + WineNineDevice9_SetMaximumFrameLatency, + WineNineDevice9_GetMaximumFrameLatency, + WineNineDevice9_CheckDeviceState, + WineNineDevice9_CreateRenderTargetEx, + WineNineDevice9_CreateOffscreenPlainSurfaceEx, + WineNineDevice9_CreateDepthStencilSurfaceEx, + WineNineDevice9_ResetEx, + WineNineDevice9_GetDisplayModeEx +}; + +IDirect3DDevice9ExVtbl *get_device_vtable() +{ + return &WineNineDevice9_vtable; +} diff --git a/dlls/d3d9-nine/device_wrap.h b/dlls/d3d9-nine/device_wrap.h new file mode 100644 index 0000000000..8f5e7206a0 --- /dev/null +++ b/dlls/d3d9-nine/device_wrap.h @@ -0,0 +1,26 @@ +/* + * Copyright 2016 Axel Davy + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __NINE_DEVICE_WRAP_H +#define __NINE_DEVICE_WRAP_H + +#include + +IDirect3DDevice9ExVtbl *get_device_vtable(void); + +#endif /* __NINE_DEVICE_WRAP_H */ diff --git a/dlls/d3d9-nine/dri3.c b/dlls/d3d9-nine/dri3.c new file mode 100644 index 0000000000..3946e7f8bf --- /dev/null +++ b/dlls/d3d9-nine/dri3.c @@ -0,0 +1,1426 @@ +/* + * Wine DRI3 interface + * + * Copyright 2014-2015 Axel Davy + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include "config.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d9nine); + +#include "dri3.h" + +#include +#include +#include + +#include +#include +#include + +#include "winbase.h" + +#ifdef D3D9NINE_DRI2 +#include + +#define BOOL X_BOOL +#define BYTE X_BYTE +#define INT8 X_INT8 +#define INT16 X_INT16 +#define INT32 X_INT32 +#define INT64 X_INT64 +#include +#undef BOOL +#undef BYTE +#undef INT8 +#undef INT16 +#undef INT32 +#undef INT64 +#undef LONG64 + +#include +#include +#include +#include +#define GL_GLEXT_PROTOTYPES 1 +#define EGL_EGLEXT_PROTOTYPES 1 +#define GL_GLEXT_LEGACY 1 + +/* workaround for broken ABI on x86_64 due to windef.h */ +#undef APIENTRY +#undef APIENTRYP +#include + +/* workaround gl header bug */ +#define glBlendColor glBlendColorLEV +#define glBlendEquation glBlendEquationLEV +#include +#include +#include +#include +#include + +static EGLDisplay display = NULL; +static int display_ref = 0; + +struct DRI2priv { + Display *dpy; + EGLDisplay display; + EGLContext context; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_func; + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR_func; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR_func; +}; +#endif + +struct PRESENTPriv { + xcb_connection_t *xcb_connection; + xcb_connection_t *xcb_connection_bis; /* to avoid libxcb thread bugs, use a different connection to present pixmaps */ + XID window; + uint64_t last_msc; + uint64_t last_target; + uint32_t last_serial_given; + xcb_special_event_t *special_event; + PRESENTPixmapPriv *first_present_priv; + int pixmap_present_pending; + BOOL idle_notify_since_last_check; + BOOL notify_with_serial_pending; + CRITICAL_SECTION mutex_present; /* protect readind/writing present_priv things */ + CRITICAL_SECTION mutex_xcb_wait; + BOOL xcb_wait; +}; + +struct PRESENTPixmapPriv { + PRESENTpriv *present_priv; + Pixmap pixmap; + BOOL released; + unsigned int width; + unsigned int height; + unsigned int depth; + unsigned int present_complete_pending; + uint32_t serial; +#ifdef D3D9NINE_DRI2 + struct { + BOOL is_dri2; + struct DRI2priv *dri2_priv; + GLuint fbo_read; + GLuint fbo_write; + GLuint texture_read; + GLuint texture_write; + } dri2_info; +#endif + BOOL last_present_was_flip; + PRESENTPixmapPriv *next; +}; + +BOOL DRI3CheckExtension(Display *dpy, int major, int minor) +{ + xcb_connection_t *xcb_connection = XGetXCBConnection(dpy); + xcb_dri3_query_version_cookie_t dri3_cookie; + xcb_dri3_query_version_reply_t *dri3_reply; + xcb_generic_error_t *error; + const xcb_query_extension_reply_t *extension; + int fd; + + xcb_prefetch_extension_data(xcb_connection, &xcb_dri3_id); + + extension = xcb_get_extension_data(xcb_connection, &xcb_dri3_id); + if (!(extension && extension->present)) + { + ERR("DRI3 extension is not present\n"); + return FALSE; + } + + dri3_cookie = xcb_dri3_query_version(xcb_connection, major, minor); + + dri3_reply = xcb_dri3_query_version_reply(xcb_connection, dri3_cookie, &error); + if (!dri3_reply) + { + free(error); + ERR("Issue getting requested version of DRI3: %d,%d\n", major, minor); + return FALSE; + } + + if (!DRI3Open(dpy, DefaultScreen(dpy), &fd)) + { + ERR("DRI3 advertised, but not working\n"); + return FALSE; + } + close(fd); + + TRACE("DRI3 version %d,%d found. %d %d requested\n", major, minor, + (int)dri3_reply->major_version, (int)dri3_reply->minor_version); + free(dri3_reply); + + return TRUE; +} + +#ifdef D3D9NINE_DRI2 + +BOOL DRI2FallbackInit(Display *dpy, struct DRI2priv **priv) +{ + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_func; + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR_func; + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT_func; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR_func; + EGLint major, minor; + EGLConfig config; + EGLContext context; + EGLint i; + EGLBoolean b; + EGLenum current_api = 0; + const char *extensions; + EGLint config_attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + EGLint context_compatibility_attribs[] = { + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, + EGL_NONE + }; + + current_api = eglQueryAPI(); + eglGetPlatformDisplayEXT_func = (PFNEGLGETPLATFORMDISPLAYEXTPROC) + eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if (!eglGetPlatformDisplayEXT_func) + return FALSE; + if (!display) + display = eglGetPlatformDisplayEXT_func(EGL_PLATFORM_X11_EXT, dpy, NULL); + if (!display) + return FALSE; + /* count references on display for multi device setups */ + display_ref++; + + if (eglInitialize(display, &major, &minor) != EGL_TRUE) + goto clean_egl_display; + + extensions = eglQueryString(display, EGL_CLIENT_APIS); + if (!extensions || !strstr(extensions, "OpenGL")) + goto clean_egl_display; + + extensions = eglQueryString(display, EGL_EXTENSIONS); + if (!extensions || !strstr(extensions, "EGL_EXT_image_dma_buf_import") || + !strstr(extensions, "EGL_KHR_create_context") || + !strstr(extensions, "EGL_KHR_surfaceless_context") || + !strstr(extensions, "EGL_KHR_image_base")) + goto clean_egl_display; + + if (!eglChooseConfig(display, config_attribs, &config, 1, &i)) + goto clean_egl_display; + + b = eglBindAPI(EGL_OPENGL_API); + if (b == EGL_FALSE) + goto clean_egl_display; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_compatibility_attribs); + if (context == EGL_NO_CONTEXT) + goto clean_egl_display; + + glEGLImageTargetTexture2DOES_func = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + eglGetProcAddress("glEGLImageTargetTexture2DOES"); + + eglCreateImageKHR_func = (PFNEGLCREATEIMAGEKHRPROC) + eglGetProcAddress("eglCreateImageKHR"); + + eglDestroyImageKHR_func = (PFNEGLDESTROYIMAGEKHRPROC) + eglGetProcAddress("eglDestroyImageKHR"); + + if (!eglCreateImageKHR_func || + !glEGLImageTargetTexture2DOES_func || + !eglDestroyImageKHR_func) + { + ERR("eglGetProcAddress failed !"); + goto clean_egl; + } + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + *priv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct DRI2priv)); + if (!*priv) + goto clean_egl; + (*priv)->dpy = dpy; + (*priv)->display = display; + (*priv)->context = context; + (*priv)->glEGLImageTargetTexture2DOES_func = glEGLImageTargetTexture2DOES_func; + (*priv)->eglCreateImageKHR_func = eglCreateImageKHR_func; + (*priv)->eglDestroyImageKHR_func = eglDestroyImageKHR_func; + eglBindAPI(current_api); + return TRUE; + +clean_egl: + eglDestroyContext(display, context); + +clean_egl_display: + eglTerminate(display); + eglBindAPI(current_api); + return FALSE; +} + +/* hypothesis: at this step all textures, etc are destroyed */ +void DRI2FallbackDestroy(struct DRI2priv *priv) +{ + EGLenum current_api; + current_api = eglQueryAPI(); + eglBindAPI(EGL_OPENGL_API); + eglMakeCurrent(priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(priv->display, priv->context); + if (display) + { + /* destroy display connection with last device */ + display_ref--; + if (!display_ref) + { + eglTerminate(display); + display = NULL; + } + } + eglBindAPI(current_api); + HeapFree(GetProcessHeap(), 0, priv); +} + +BOOL DRI2FallbackCheckSupport(Display *dpy) +{ + struct DRI2priv *priv; + int fd; + if (!DRI2FallbackInit(dpy, &priv)) + return FALSE; + DRI2FallbackDestroy(priv); + if (!DRI2FallbackOpen(dpy, DefaultScreen(dpy), &fd)) + return FALSE; + close(fd); + return TRUE; +} + +#endif + +BOOL PRESENTCheckExtension(Display *dpy, int major, int minor) +{ + xcb_connection_t *xcb_connection = XGetXCBConnection(dpy); + xcb_present_query_version_cookie_t present_cookie; + xcb_present_query_version_reply_t *present_reply; + xcb_generic_error_t *error; + const xcb_query_extension_reply_t *extension; + + xcb_prefetch_extension_data(xcb_connection, &xcb_present_id); + + extension = xcb_get_extension_data(xcb_connection, &xcb_present_id); + if (!(extension && extension->present)) + { + ERR("PRESENT extension is not present\n"); + return FALSE; + } + + present_cookie = xcb_present_query_version(xcb_connection, major, minor); + + present_reply = xcb_present_query_version_reply(xcb_connection, present_cookie, &error); + if (!present_reply) + { + free(error); + ERR("Issue getting requested version of PRESENT: %d,%d\n", major, minor); + return FALSE; + } + + TRACE("PRESENT version %d,%d found. %u %u requested\n", major, minor, + present_reply->major_version, present_reply->minor_version); + + free(present_reply); + + return TRUE; +} + +BOOL DRI3Open(Display *dpy, int screen, int *device_fd) +{ + xcb_dri3_open_cookie_t cookie; + xcb_dri3_open_reply_t *reply; + xcb_connection_t *xcb_connection = XGetXCBConnection(dpy); + int fd; + Window root = RootWindow(dpy, screen); + + cookie = xcb_dri3_open(xcb_connection, root, 0); + + reply = xcb_dri3_open_reply(xcb_connection, cookie, NULL); + if (!reply) + return FALSE; + + if (reply->nfd != 1) + { + free(reply); + return FALSE; + } + + fd = xcb_dri3_open_reply_fds(xcb_connection, reply)[0]; + fcntl(fd, F_SETFD, FD_CLOEXEC); + + *device_fd = fd; + free(reply); + + return TRUE; +} + +#ifdef D3D9NINE_DRI2 + +static XExtensionInfo _dri2_info_data; +static XExtensionInfo *dri2_info = &_dri2_info_data; +static char dri2_name[] = DRI2_NAME; + +#define DRI2CheckExtension(dpy, i, val) \ + XextCheckExtension(dpy, i, dri2_name, val) + +static int close_display(Display *dpy, XExtCodes *codes); +static Bool wire_to_event(Display *dpy, XEvent *re, xEvent *event); +static Status event_to_wire(Display *dpy, XEvent *re, xEvent *event); +static int error( Display *dpy, xError *err, XExtCodes *codes, int *ret_code ); + +static XExtensionHooks dri2_hooks = { + NULL, /* create_gc */ + NULL, /* copy_gc */ + NULL, /* flush_gc */ + NULL, /* free_gc */ + NULL, /* create_font */ + NULL, /* free_font */ + close_display, /* close_display */ + wire_to_event, /* wire_to_event */ + event_to_wire, /* event_to_wire */ + error, /* error */ + NULL, /* error_string */ +}; +static XEXT_GENERATE_CLOSE_DISPLAY(close_display, dri2_info); +static XEXT_GENERATE_FIND_DISPLAY(find_display, dri2_info, + dri2_name, &dri2_hooks, 0, NULL); +static Bool wire_to_event(Display *dpy, XEvent *re, xEvent *event) +{ + XExtDisplayInfo *info = find_display(dpy); + DRI2CheckExtension(dpy, info, False); + TRACE("dri2 wire_to_event\n"); + return False; +} + +static Status event_to_wire(Display *dpy, XEvent *re, xEvent *event) +{ + XExtDisplayInfo *info = find_display(dpy); + DRI2CheckExtension(dpy, info, False); + TRACE("dri2 event_to_wire\n"); + return False; +} + +static int error(Display *dpy, xError *err, XExtCodes *codes, int *ret_code) +{ + TRACE("dri2 error\n"); + return False; +} + +#define XALIGN(x) (((x) + 3) & (~3)) + +static BOOL DRI2Connect(Display *dpy, XID window, unsigned driver_type, char **device) +{ + XExtDisplayInfo *info = find_display(dpy); + xDRI2ConnectReply rep; + xDRI2ConnectReq *req; + int dev_len, driv_len; + char *driver; + + DRI2CheckExtension(dpy, info, False); + + *device = NULL; + + LockDisplay(dpy); + GetReq(DRI2Connect, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Connect; + req->window = window; + req->driverType = driver_type; + if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) + { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + /* check string lengths */ + dev_len = rep.deviceNameLength; + driv_len = rep.driverNameLength; + if (dev_len == 0 || driv_len == 0) + { + _XEatData(dpy, XALIGN(dev_len) + XALIGN(driv_len)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + + /* read out driver */ + driver = HeapAlloc(GetProcessHeap(), 0, driv_len + 1); + if (!driver) + { + _XEatData(dpy, XALIGN(dev_len) + XALIGN(driv_len)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, driver, driv_len); + HeapFree(GetProcessHeap(), 0, driver); /* we don't need the driver */ + + /* read out device */ + *device = HeapAlloc(GetProcessHeap(), 0, dev_len + 1); + if (!*device) + { + _XEatData(dpy, XALIGN(dev_len)); + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + _XReadPad(dpy, *device, dev_len); + (*device)[dev_len] = '\0'; + + UnlockDisplay(dpy); + SyncHandle(); + + return True; +} + +static Bool DRI2Authenticate(Display *dpy, XID window, uint32_t token) +{ + XExtDisplayInfo *info = find_display(dpy); + xDRI2AuthenticateReply rep; + xDRI2AuthenticateReq *req; + + DRI2CheckExtension(dpy, info, False); + + LockDisplay(dpy); + GetReq(DRI2Authenticate, req); + req->reqType = info->codes->major_opcode; + req->dri2ReqType = X_DRI2Authenticate; + req->window = window; + req->magic = token; + if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) + { + UnlockDisplay(dpy); + SyncHandle(); + return False; + } + UnlockDisplay(dpy); + SyncHandle(); + + return rep.authenticated ? True : False; +} + +BOOL DRI2FallbackOpen(Display *dpy, int screen, int *device_fd) +{ + char *device; + int fd; + Window root = RootWindow(dpy, screen); + drm_auth_t auth; + + if (!DRI2Connect(dpy, root, DRI2DriverDRI, &device)) + return FALSE; + + fd = open(device, O_RDWR); + HeapFree(GetProcessHeap(), 0, device); + if (fd < 0) + return FALSE; + + if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth) != 0) + { + close(fd); + return FALSE; + } + + if (!DRI2Authenticate(dpy, root, auth.magic)) + { + close(fd); + return FALSE; + } + + *device_fd = fd; + + return TRUE; +} + +#endif + + +BOOL DRI3PixmapFromDmaBuf(Display *dpy, int screen, int fd, int width, int height, + int stride, int depth, int bpp, Pixmap *pixmap) +{ + xcb_connection_t *xcb_connection = XGetXCBConnection(dpy); + Window root = RootWindow(dpy, screen); + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + + cookie = xcb_dri3_pixmap_from_buffer_checked(xcb_connection, + (*pixmap = xcb_generate_id(xcb_connection)), root, 0, + width, height, stride, depth, bpp, fd); + + error = xcb_request_check(xcb_connection, cookie); /* performs a flush */ + if (error) + { + ERR("Error using DRI3 to convert a DmaBufFd to pixmap\n"); + return FALSE; + } + return TRUE; +} + +BOOL DRI3DmaBufFromPixmap(Display *dpy, Pixmap pixmap, int *fd, int *width, int *height, + int *stride, int *depth, int *bpp) +{ + xcb_connection_t *xcb_connection = XGetXCBConnection(dpy); + xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie; + xcb_dri3_buffer_from_pixmap_reply_t *bp_reply; + + bp_cookie = xcb_dri3_buffer_from_pixmap(xcb_connection, pixmap); + bp_reply = xcb_dri3_buffer_from_pixmap_reply(xcb_connection, bp_cookie, NULL); + if (!bp_reply) + return FALSE; + *fd = xcb_dri3_buffer_from_pixmap_reply_fds(xcb_connection, bp_reply)[0]; + *width = bp_reply->width; + *height = bp_reply->height; + *stride = bp_reply->stride; + *depth = bp_reply->depth; + *bpp = bp_reply->depth; + return TRUE; +} + +static PRESENTPixmapPriv *PRESENTFindPixmapPriv(PRESENTpriv *present_priv, uint32_t serial) +{ + PRESENTPixmapPriv *current = present_priv->first_present_priv; + + while (current) + { + if (current->serial == serial) + return current; + current = current->next; + } + return NULL; +} + +static void PRESENThandle_events(PRESENTpriv *present_priv, xcb_present_generic_event_t *ge) +{ + PRESENTPixmapPriv *present_pixmap_priv = NULL; + + switch (ge->evtype) + { + case XCB_PRESENT_COMPLETE_NOTIFY: + { + xcb_present_complete_notify_event_t *ce = (void *) ge; + if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) + { + if (ce->serial) + present_priv->notify_with_serial_pending = FALSE; + free(ce); + return; + } + present_pixmap_priv = PRESENTFindPixmapPriv(present_priv, ce->serial); + if (!present_pixmap_priv || ce->kind != XCB_PRESENT_COMPLETE_KIND_PIXMAP) + { + ERR("FATAL ERROR: PRESENT handling failed\n"); + free(ce); + return; + } + present_pixmap_priv->present_complete_pending--; + switch (ce->mode) + { + case XCB_PRESENT_COMPLETE_MODE_FLIP: + present_pixmap_priv->last_present_was_flip = TRUE; + break; + case XCB_PRESENT_COMPLETE_MODE_COPY: + present_pixmap_priv->last_present_was_flip = FALSE; + break; + } + present_priv->pixmap_present_pending--; + present_priv->last_msc = ce->msc; + break; + } + case XCB_PRESENT_EVENT_IDLE_NOTIFY: + { + xcb_present_idle_notify_event_t *ie = (void *) ge; + present_pixmap_priv = PRESENTFindPixmapPriv(present_priv, ie->serial); + if (!present_pixmap_priv || present_pixmap_priv->pixmap != ie->pixmap) + { + ERR("FATAL ERROR: PRESENT handling failed\n"); + free(ie); + return; + } + present_pixmap_priv->released = TRUE; + present_priv->idle_notify_since_last_check = TRUE; + break; + } + } + free(ge); +} + +static void PRESENTflush_events(PRESENTpriv *present_priv, BOOL assert_no_other_thread_waiting) +{ + xcb_generic_event_t *ev; + + if ((present_priv->xcb_wait && !assert_no_other_thread_waiting) || /* don't steal events to someone waiting */ + !present_priv->special_event) + return; + + while ((ev = xcb_poll_for_special_event(present_priv->xcb_connection, + present_priv->special_event)) != NULL) + { + PRESENThandle_events(present_priv, (void *) ev); + } +} + +static BOOL PRESENTwait_events(PRESENTpriv *present_priv, BOOL allow_other_threads) +{ + xcb_generic_event_t *ev; + + if (allow_other_threads) + { + present_priv->xcb_wait = TRUE; + EnterCriticalSection(&present_priv->mutex_xcb_wait); + LeaveCriticalSection(&present_priv->mutex_present); + } + ev = xcb_wait_for_special_event(present_priv->xcb_connection, present_priv->special_event); + if (allow_other_threads) + { + LeaveCriticalSection(&present_priv->mutex_xcb_wait); + EnterCriticalSection(&present_priv->mutex_present); + present_priv->xcb_wait = FALSE; + } + if (!ev) + { + ERR("FATAL error: xcb had an error\n"); + return FALSE; + } + + PRESENThandle_events(present_priv, (void *) ev); + return TRUE; +} + +static struct xcb_connection_t *create_xcb_connection(Display *dpy) +{ + int screen_num = DefaultScreen(dpy); + xcb_connection_t *ret; + xcb_xfixes_query_version_cookie_t cookie; + xcb_xfixes_query_version_reply_t *rep; + + ret = xcb_connect(DisplayString(dpy), &screen_num); + cookie = xcb_xfixes_query_version_unchecked(ret, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); + rep = xcb_xfixes_query_version_reply(ret, cookie, NULL); + if (rep) + free(rep); + return ret; +} + +BOOL PRESENTInit(Display *dpy, PRESENTpriv **present_priv) +{ + *present_priv = (PRESENTpriv *) HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(PRESENTpriv)); + + if (!*present_priv) + return FALSE; + + (*present_priv)->xcb_connection = create_xcb_connection(dpy); + (*present_priv)->xcb_connection_bis = create_xcb_connection(dpy); + + InitializeCriticalSection(&(*present_priv)->mutex_present); + InitializeCriticalSection(&(*present_priv)->mutex_xcb_wait); + return TRUE; +} + +static void PRESENTForceReleases(PRESENTpriv *present_priv) +{ + PRESENTPixmapPriv *current = NULL; + + if (!present_priv->window) + return; + + /* There should be no other thread listening for events here. + * This can happen when hDestWindowOverride changes without reset. + * This case should never happen, but can happen in theory.*/ + if (present_priv->xcb_wait) + { + xcb_present_notify_msc(present_priv->xcb_connection, present_priv->window, 0, 0, 0, 0); + xcb_flush(present_priv->xcb_connection); + EnterCriticalSection(&present_priv->mutex_xcb_wait); + LeaveCriticalSection(&present_priv->mutex_xcb_wait); + /* the problem here is that we don't have access to the event the other thread got. + * It is either presented event, idle event or notify event. + */ + while (present_priv->pixmap_present_pending >= 2) + PRESENTwait_events(present_priv, FALSE); + PRESENTflush_events(present_priv, TRUE); + /* Remaining events to come can be a pair of present/idle, + * or an idle, or nothing. To be sure we are after all pixmaps + * have been presented, add an event to the queue that can only + * be after the present event, then if we receive an event more, + * we are sure all pixmaps were presented */ + present_priv->notify_with_serial_pending = TRUE; + xcb_present_notify_msc(present_priv->xcb_connection, present_priv->window, + 1, present_priv->last_target + 5, 0, 0); + + xcb_flush(present_priv->xcb_connection); + while (present_priv->notify_with_serial_pending) + PRESENTwait_events(present_priv, FALSE); + /* Now we are sure we are not expecting any new event */ + } + else + { + while (present_priv->pixmap_present_pending) /* wait all sent pixmaps are presented */ + PRESENTwait_events(present_priv, FALSE); + PRESENTflush_events(present_priv, TRUE); /* may be remaining idle event */ + /* Since idle events are send with the complete events when it is not flips, + * we are not expecting any new event here */ + } + + current = present_priv->first_present_priv; + while (current) + { + if (!current->released) + { + if (!current->last_present_was_flip && !present_priv->xcb_wait) + { + ERR("ERROR: a pixmap seems not released by PRESENT for no reason. Code bug.\n"); + } + else + { + /* Present the same pixmap with a non-valid part to force the copy mode and the releases */ + xcb_xfixes_region_t valid, update; + xcb_rectangle_t rect_update; + rect_update.x = 0; + rect_update.y = 0; + rect_update.width = 8; + rect_update.height = 1; + valid = xcb_generate_id(present_priv->xcb_connection); + update = xcb_generate_id(present_priv->xcb_connection); + xcb_xfixes_create_region(present_priv->xcb_connection, valid, 1, &rect_update); + xcb_xfixes_create_region(present_priv->xcb_connection, update, 1, &rect_update); + /* here we know the pixmap has been presented. Thus if it is on screen, + * the following request can only make it released by the server if it is not */ + xcb_present_pixmap(present_priv->xcb_connection, present_priv->window, + current->pixmap, 0, valid, update, 0, 0, None, None, + None, XCB_PRESENT_OPTION_COPY | XCB_PRESENT_OPTION_ASYNC, 0, 0, 0, 0, NULL); + xcb_flush(present_priv->xcb_connection); + PRESENTwait_events(present_priv, FALSE); /* by assumption this can only be idle event */ + PRESENTflush_events(present_priv, TRUE); /* Shoudln't be needed */ + } + } + current = current->next; + } + /* Now all pixmaps are released (possibility if xcb_wait is true that one is not aware yet), + * and we don't expect any new Present event to come from Xserver */ +} + +static void PRESENTFreeXcbQueue(PRESENTpriv *present_priv) +{ + if (present_priv->window) + { + xcb_unregister_for_special_event(present_priv->xcb_connection, present_priv->special_event); + present_priv->last_msc = 0; + present_priv->last_target = 0; + present_priv->special_event = NULL; + } +} + +static BOOL PRESENTPrivChangeWindow(PRESENTpriv *present_priv, XID window) +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + xcb_present_event_t eid; + + PRESENTForceReleases(present_priv); + PRESENTFreeXcbQueue(present_priv); + present_priv->window = window; + + if (window) + { + cookie = xcb_present_select_input_checked(present_priv->xcb_connection, + (eid = xcb_generate_id(present_priv->xcb_connection)), window, + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY | XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); + + present_priv->special_event = xcb_register_for_special_xge(present_priv->xcb_connection, + &xcb_present_id, eid, NULL); + + error = xcb_request_check(present_priv->xcb_connection, cookie); /* performs a flush */ + if (error || !present_priv->special_event) + { + ERR("FAILED to use the X PRESENT extension. Was the destination a window ?\n"); + if (present_priv->special_event) + xcb_unregister_for_special_event(present_priv->xcb_connection, present_priv->special_event); + present_priv->special_event = NULL; + present_priv->window = 0; + } + } + return (present_priv->window != 0); +} + +/* Destroy the content, except the link and the struct mem */ +static void PRESENTDestroyPixmapContent(Display *dpy, PRESENTPixmapPriv *present_pixmap) +{ + XFreePixmap(dpy, present_pixmap->pixmap); +#ifdef D3D9NINE_DRI2 + if (present_pixmap->dri2_info.is_dri2) + { + struct DRI2priv *dri2_priv = present_pixmap->dri2_info.dri2_priv; + EGLenum current_api; + current_api = eglQueryAPI(); + eglBindAPI(EGL_OPENGL_API); + if (eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, dri2_priv->context)) + { + glDeleteFramebuffers(1, &present_pixmap->dri2_info.fbo_read); + glDeleteFramebuffers(1, &present_pixmap->dri2_info.fbo_write); + glDeleteTextures(1, &present_pixmap->dri2_info.texture_read); + glDeleteTextures(1, &present_pixmap->dri2_info.texture_write); + } + else + ERR("eglMakeCurrent failed with 0x%0X\n", eglGetError()); + + eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglBindAPI(current_api); + } +#endif +} + +void PRESENTDestroy(Display *dpy, PRESENTpriv *present_priv) +{ + PRESENTPixmapPriv *current = NULL; + + EnterCriticalSection(&present_priv->mutex_present); + + PRESENTForceReleases(present_priv); + + current = present_priv->first_present_priv; + while (current) + { + PRESENTPixmapPriv *next = current->next; + PRESENTDestroyPixmapContent(dpy, current); + HeapFree(GetProcessHeap(), 0, current); + current = next; + } + + PRESENTFreeXcbQueue(present_priv); + + xcb_disconnect(present_priv->xcb_connection); + xcb_disconnect(present_priv->xcb_connection_bis); + LeaveCriticalSection(&present_priv->mutex_present); + DeleteCriticalSection(&present_priv->mutex_present); + DeleteCriticalSection(&present_priv->mutex_xcb_wait); + + HeapFree(GetProcessHeap(), 0, present_priv); +} + +BOOL PRESENTPixmapInit(PRESENTpriv *present_priv, Pixmap pixmap, PRESENTPixmapPriv **present_pixmap_priv) +{ + xcb_get_geometry_cookie_t cookie; + xcb_get_geometry_reply_t *reply; + + cookie = xcb_get_geometry(present_priv->xcb_connection, pixmap); + reply = xcb_get_geometry_reply(present_priv->xcb_connection, cookie, NULL); + + if (!reply) + return FALSE; + + *present_pixmap_priv = (PRESENTPixmapPriv *) HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(PRESENTPixmapPriv)); + + if (!*present_pixmap_priv) + { + free(reply); + return FALSE; + } + EnterCriticalSection(&present_priv->mutex_present); + + (*present_pixmap_priv)->released = TRUE; + (*present_pixmap_priv)->pixmap = pixmap; + (*present_pixmap_priv)->present_priv = present_priv; + (*present_pixmap_priv)->next = present_priv->first_present_priv; + (*present_pixmap_priv)->width = reply->width; + (*present_pixmap_priv)->height = reply->height; + (*present_pixmap_priv)->depth = reply->depth; +#ifdef D3D9NINE_DRI2 + (*present_pixmap_priv)->dri2_info.is_dri2 = FALSE; +#endif + free(reply); + + present_priv->last_serial_given++; + (*present_pixmap_priv)->serial = present_priv->last_serial_given; + present_priv->first_present_priv = *present_pixmap_priv; + + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +} + +#ifdef D3D9NINE_DRI2 + +BOOL DRI2FallbackPRESENTPixmap(PRESENTpriv *present_priv, struct DRI2priv *dri2_priv, + int fd, int width, int height, int stride, int depth, + int bpp, PRESENTPixmapPriv **present_pixmap_priv) +{ + Window root = RootWindow(dri2_priv->dpy, DefaultScreen(dri2_priv->dpy)); + Pixmap pixmap; + EGLImageKHR image; + GLuint texture_read, texture_write, fbo_read, fbo_write; + EGLint attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888, + EGL_DMA_BUF_PLANE0_FD_EXT, 0, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, 0, + EGL_NONE + }; + EGLenum current_api = 0; + int status; + + EnterCriticalSection(&present_priv->mutex_present); + + pixmap = XCreatePixmap(dri2_priv->dpy, root, width, height, 24); + if (!pixmap) + goto fail; + + attribs[1] = width; + attribs[3] = height; + attribs[7] = fd; + attribs[11] = stride; + + current_api = eglQueryAPI(); + eglBindAPI(EGL_OPENGL_API); + + /* We bind the dma-buf to a EGLImage, then to a texture, and then to a fbo. + * Note that we can delete the EGLImage, but we shouldn't delete the texture, + * else the fbo is invalid */ + + image = dri2_priv->eglCreateImageKHR_func(dri2_priv->display, + EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); + + if (image == EGL_NO_IMAGE_KHR) + goto fail; + close(fd); + + if (eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, dri2_priv->context)) + { + glGenTextures(1, &texture_read); + glBindTexture(GL_TEXTURE_2D, texture_read); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + dri2_priv->glEGLImageTargetTexture2DOES_func(GL_TEXTURE_2D, image); + glGenFramebuffers(1, &fbo_read); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_read); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture_read, + 0); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + goto fail; + glBindTexture(GL_TEXTURE_2D, 0); + dri2_priv->eglDestroyImageKHR_func(dri2_priv->display, image); + + /* We bind a newly created pixmap (to which we want to copy the content) + * to an EGLImage, then to a texture, then to a fbo. */ + image = dri2_priv->eglCreateImageKHR_func(dri2_priv->display, + dri2_priv->context, + EGL_NATIVE_PIXMAP_KHR, + (void *)pixmap, NULL); + if (image == EGL_NO_IMAGE_KHR) + goto fail; + + glGenTextures(1, &texture_write); + glBindTexture(GL_TEXTURE_2D, texture_write); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + dri2_priv->glEGLImageTargetTexture2DOES_func(GL_TEXTURE_2D, image); + glGenFramebuffers(1, &fbo_write); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_write); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture_write, + 0); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + goto fail; + glBindTexture(GL_TEXTURE_2D, 0); + dri2_priv->eglDestroyImageKHR_func(dri2_priv->display, image); + } + else + ERR("eglMakeCurrent failed with 0x%0X\n", eglGetError()); + + eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + *present_pixmap_priv = (PRESENTPixmapPriv *) HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(PRESENTPixmapPriv)); + + if (!*present_pixmap_priv) + goto fail; + + (*present_pixmap_priv)->released = TRUE; + (*present_pixmap_priv)->pixmap = pixmap; + (*present_pixmap_priv)->present_priv = present_priv; + (*present_pixmap_priv)->next = present_priv->first_present_priv; + (*present_pixmap_priv)->width = width; + (*present_pixmap_priv)->height = height; + (*present_pixmap_priv)->depth = depth; + (*present_pixmap_priv)->dri2_info.is_dri2 = TRUE; + (*present_pixmap_priv)->dri2_info.dri2_priv = dri2_priv; + (*present_pixmap_priv)->dri2_info.fbo_read = fbo_read; + (*present_pixmap_priv)->dri2_info.fbo_write = fbo_write; + (*present_pixmap_priv)->dri2_info.texture_read = texture_read; + (*present_pixmap_priv)->dri2_info.texture_write = texture_write; + + present_priv->last_serial_given++; + (*present_pixmap_priv)->serial = present_priv->last_serial_given; + present_priv->first_present_priv = *present_pixmap_priv; + + eglBindAPI(current_api); + + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +fail: + eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglBindAPI(current_api); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; +} + +#endif + +BOOL PRESENTTryFreePixmap(Display *dpy, PRESENTPixmapPriv *present_pixmap_priv) +{ + PRESENTpriv *present_priv = present_pixmap_priv->present_priv; + PRESENTPixmapPriv *current; + + EnterCriticalSection(&present_priv->mutex_present); + + if (!present_pixmap_priv->released || present_pixmap_priv->present_complete_pending) + { + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + + if (present_priv->first_present_priv == present_pixmap_priv) + { + present_priv->first_present_priv = present_pixmap_priv->next; + goto free_priv; + } + + current = present_priv->first_present_priv; + while (current->next != present_pixmap_priv) + current = current->next; + current->next = present_pixmap_priv->next; +free_priv: + PRESENTDestroyPixmapContent(dpy, present_pixmap_priv); + HeapFree(GetProcessHeap(), 0, present_pixmap_priv); + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +} + +BOOL PRESENTHelperCopyFront(Display *dpy, PRESENTPixmapPriv *present_pixmap_priv) +{ + PRESENTpriv *present_priv = present_pixmap_priv->present_priv; + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + uint32_t v = 0; + xcb_gcontext_t gc; + + EnterCriticalSection(&present_priv->mutex_present); + + if (!present_priv->window) + { + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + + gc = xcb_generate_id(present_priv->xcb_connection); + xcb_create_gc(present_priv->xcb_connection, gc, present_priv->window, + XCB_GC_GRAPHICS_EXPOSURES, &v); + + cookie = xcb_copy_area_checked(present_priv->xcb_connection, + present_priv->window, present_pixmap_priv->pixmap, gc, + 0, 0, 0, 0, present_pixmap_priv->width, present_pixmap_priv->height); + + error = xcb_request_check(present_priv->xcb_connection, cookie); + xcb_free_gc(present_priv->xcb_connection, gc); + LeaveCriticalSection(&present_priv->mutex_present); + return (error != NULL); +} + +BOOL PRESENTPixmap(Display *dpy, XID window, PRESENTPixmapPriv *present_pixmap_priv, + const UINT PresentationInterval, const BOOL PresentAsync, const BOOL SwapEffectCopy, + const RECT *pSourceRect, const RECT *pDestRect, const RGNDATA *pDirtyRegion) +{ + PRESENTpriv *present_priv = present_pixmap_priv->present_priv; +#ifdef D3D9NINE_DRI2 + struct DRI2priv *dri2_priv = present_pixmap_priv->dri2_info.dri2_priv; + EGLenum current_api = 0; +#endif + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + int64_t target_msc, presentationInterval; + xcb_xfixes_region_t valid, update; + int16_t x_off, y_off; + uint32_t options = XCB_PRESENT_OPTION_NONE; + + EnterCriticalSection(&present_priv->mutex_present); + + if (window != present_priv->window) + PRESENTPrivChangeWindow(present_priv, window); + + if (!window) + { + ERR("ERROR: Try to Present a pixmap on a NULL window\n"); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + + PRESENTflush_events(present_priv, FALSE); + /* Note: present_pixmap_priv->present_complete_pending may be non-0, because + * on some paths the Xserver sends the complete event just after the idle + * event. */ + if (!present_pixmap_priv->released) + { + ERR("FATAL ERROR: Trying to Present a pixmap not released\n"); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } +#ifdef D3D9NINE_DRI2 + if (present_pixmap_priv->dri2_info.is_dri2) + { + current_api = eglQueryAPI(); + eglBindAPI(EGL_OPENGL_API); + if (eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, dri2_priv->context)) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, present_pixmap_priv->dri2_info.fbo_read); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, present_pixmap_priv->dri2_info.fbo_write); + + glBlitFramebuffer(0, 0, present_pixmap_priv->width, present_pixmap_priv->height, + 0, 0, present_pixmap_priv->width, present_pixmap_priv->height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glFlush(); /* Perhaps useless */ + } + else + ERR("eglMakeCurrent failed with 0x%0X\n", eglGetError()); + + eglMakeCurrent(dri2_priv->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglBindAPI(current_api); + } +#endif + target_msc = present_priv->last_msc; + + presentationInterval = PresentationInterval; + if (PresentAsync) + options |= XCB_PRESENT_OPTION_ASYNC; + if (SwapEffectCopy) + options |= XCB_PRESENT_OPTION_COPY; + + target_msc += presentationInterval * (present_priv->pixmap_present_pending + 1); + + /* Note: PRESENT defines some way to do partial copy: + * presentproto: + * 'x-off' and 'y-off' define the location in the window where + * the 0,0 location of the pixmap will be presented. valid-area + * and update-area are relative to the pixmap. + */ + if (!pSourceRect && !pDestRect && !pDirtyRegion) + { + valid = 0; + update = 0; + x_off = 0; + y_off = 0; + } + else + { + xcb_rectangle_t rect_update; + xcb_rectangle_t *rect_updates; + int i; + + rect_update.x = 0; + rect_update.y = 0; + rect_update.width = present_pixmap_priv->width; + rect_update.height = present_pixmap_priv->height; + x_off = 0; + y_off = 0; + if (pSourceRect) + { + x_off = -pSourceRect->left; + y_off = -pSourceRect->top; + rect_update.x = pSourceRect->left; + rect_update.y = pSourceRect->top; + rect_update.width = pSourceRect->right - pSourceRect->left; + rect_update.height = pSourceRect->bottom - pSourceRect->top; + } + if (pDestRect) + { + x_off += pDestRect->left; + y_off += pDestRect->top; + rect_update.width = pDestRect->right - pDestRect->left; + rect_update.height = pDestRect->bottom - pDestRect->top; + /* Note: the size of pDestRect and pSourceRect are supposed to be the same size + * because the driver would have done things to assure that. */ + } + valid = xcb_generate_id(present_priv->xcb_connection_bis); + update = xcb_generate_id(present_priv->xcb_connection_bis); + xcb_xfixes_create_region(present_priv->xcb_connection_bis, valid, 1, &rect_update); + if (pDirtyRegion && pDirtyRegion->rdh.nCount) + { + rect_updates = (void *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(xcb_rectangle_t) * pDirtyRegion->rdh.nCount); + + for (i = 0; i < pDirtyRegion->rdh.nCount; i++) + { + RECT rc; + memcpy(&rc, pDirtyRegion->Buffer + i * sizeof(RECT), sizeof(RECT)); + rect_update.x = rc.left; + rect_update.y = rc.top; + rect_update.width = rc.right - rc.left; + rect_update.height = rc.bottom - rc.top; + memcpy(rect_updates + i * sizeof(xcb_rectangle_t), &rect_update, sizeof(xcb_rectangle_t)); + } + xcb_xfixes_create_region(present_priv->xcb_connection_bis, update, pDirtyRegion->rdh.nCount, rect_updates); + HeapFree(GetProcessHeap(), 0, rect_updates); + } else + xcb_xfixes_create_region(present_priv->xcb_connection_bis, update, 1, &rect_update); + } + + cookie = xcb_present_pixmap_checked(present_priv->xcb_connection_bis, + window, present_pixmap_priv->pixmap, present_pixmap_priv->serial, + valid, update, x_off, y_off, None, None, None, options, + target_msc, 0, 0, 0, NULL); + error = xcb_request_check(present_priv->xcb_connection_bis, cookie); /* performs a flush */ + + if (update) + xcb_xfixes_destroy_region(present_priv->xcb_connection_bis, update); + if (valid) + xcb_xfixes_destroy_region(present_priv->xcb_connection_bis, valid); + + if (error) + { + xcb_get_geometry_cookie_t cookie_geom; + xcb_get_geometry_reply_t *reply; + + cookie_geom = xcb_get_geometry(present_priv->xcb_connection_bis, window); + reply = xcb_get_geometry_reply(present_priv->xcb_connection_bis, cookie_geom, NULL); + + ERR("Error using PRESENT. Here some debug info\n"); + if (!reply) + { + ERR("Error querying window info. Perhaps it doesn't exist anymore\n"); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + ERR("Pixmap: width=%d, height=%d, depth=%d\n", + present_pixmap_priv->width, present_pixmap_priv->height, + present_pixmap_priv->depth); + + ERR("Window: width=%d, height=%d, depth=%d, x=%d, y=%d\n", + (int) reply->width, (int) reply->height, + (int) reply->depth, (int) reply->x, (int) reply->y); + + ERR("Present parameter: PresentationInterval=%d, Pending presentations=%d\n", + PresentationInterval, present_priv->pixmap_present_pending); + + if (present_pixmap_priv->depth != reply->depth) + ERR("Depths are different. PRESENT needs the pixmap and the window have same depth\n"); + free(reply); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + present_priv->last_target = target_msc; + present_priv->pixmap_present_pending++; + present_pixmap_priv->present_complete_pending++; + present_pixmap_priv->released = FALSE; + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +} + +BOOL PRESENTWaitPixmapReleased(PRESENTPixmapPriv *present_pixmap_priv) +{ + PRESENTpriv *present_priv = present_pixmap_priv->present_priv; + + EnterCriticalSection(&present_priv->mutex_present); + + PRESENTflush_events(present_priv, FALSE); + + /* The part with present_pixmap_priv->present_complete_pending is legacy behaviour. + * It matters for SwapEffectCopy with swapinterval > 0. */ + while (!present_pixmap_priv->released || present_pixmap_priv->present_complete_pending) + { + /* Note: following if should not happen because we'll never + * use two PRESENTWaitPixmapReleased in parallels on same window. + * However it would make it work in that case */ + if (present_priv->xcb_wait) + { + /* we allow only one thread to dispatch events */ + EnterCriticalSection(&present_priv->mutex_xcb_wait); + /* here the other thread got an event but hasn't treated it yet */ + LeaveCriticalSection(&present_priv->mutex_xcb_wait); + LeaveCriticalSection(&present_priv->mutex_present); + Sleep(10); /* Let it treat the event */ + EnterCriticalSection(&present_priv->mutex_present); + } + else if (!PRESENTwait_events(present_priv, TRUE)) + { + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + } + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +} + +BOOL PRESENTIsPixmapReleased(PRESENTPixmapPriv *present_pixmap_priv) +{ + PRESENTpriv *present_priv = present_pixmap_priv->present_priv; + BOOL ret; + + EnterCriticalSection(&present_priv->mutex_present); + + PRESENTflush_events(present_priv, FALSE); + + ret = present_pixmap_priv->released; + + LeaveCriticalSection(&present_priv->mutex_present); + return ret; +} + +BOOL PRESENTWaitReleaseEvent(PRESENTpriv *present_priv) +{ + + EnterCriticalSection(&present_priv->mutex_present); + + while (!present_priv->idle_notify_since_last_check) + { + /* Note: following if should not happen because we'll never + * use two PRESENTWaitPixmapReleased in parallels on same window. + * However it would make it work in that case */ + if (present_priv->xcb_wait) + { + /* we allow only one thread to dispatch events */ + EnterCriticalSection(&present_priv->mutex_xcb_wait); + /* here the other thread got an event but hasn't treated it yet */ + LeaveCriticalSection(&present_priv->mutex_xcb_wait); + LeaveCriticalSection(&present_priv->mutex_present); + Sleep(10); /* Let it treat the event */ + EnterCriticalSection(&present_priv->mutex_present); + } + else if (!PRESENTwait_events(present_priv, TRUE)) + { + ERR("Issue in PRESENTWaitReleaseEvent\n"); + LeaveCriticalSection(&present_priv->mutex_present); + return FALSE; + } + } + present_priv->idle_notify_since_last_check = FALSE; + + LeaveCriticalSection(&present_priv->mutex_present); + return TRUE; +} diff --git a/dlls/d3d9-nine/dri3.h b/dlls/d3d9-nine/dri3.h new file mode 100644 index 0000000000..3c9309dedb --- /dev/null +++ b/dlls/d3d9-nine/dri3.h @@ -0,0 +1,91 @@ +/* + * Wine X11DRV DRI3 interface + * + * Copyright 2014 Axel Davy + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_DRI3_H +#define __WINE_DRI3_H + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#include +#include + +BOOL DRI3CheckExtension(Display *dpy, int major, int minor); + +#ifdef D3D9NINE_DRI2 +struct DRI2priv; + +BOOL DRI2FallbackInit(Display *dpy, struct DRI2priv **priv); + +void DRI2FallbackDestroy(struct DRI2priv *priv); + +BOOL DRI2FallbackCheckSupport(Display *dpy); +#endif + +BOOL PRESENTCheckExtension(Display *dpy, int major, int minor); + +BOOL DRI3Open(Display *dpy, int screen, int *device_fd); + +#ifdef D3D9NINE_DRI2 +BOOL DRI2FallbackOpen(Display *dpy, int screen, int *device_fd); +#endif + +BOOL DRI3PixmapFromDmaBuf(Display *dpy, int screen, int fd, int width, int height, + int stride, int depth, int bpp, Pixmap *pixmap); + +BOOL DRI3DmaBufFromPixmap(Display *dpy, Pixmap pixmap, int *fd, int *width, int *height, + int *stride, int *depth, int *bpp); + +typedef struct PRESENTPriv PRESENTpriv; +typedef struct PRESENTPixmapPriv PRESENTPixmapPriv; + +BOOL PRESENTInit(Display *dpy, PRESENTpriv **present_priv); + +/* will clean properly and free all PRESENTPixmapPriv associated to PRESENTpriv. + * PRESENTPixmapPriv should not be freed by something else. + * If never a PRESENTPixmapPriv has to be destroyed, + * please destroy the current PRESENTpriv and create a new one. + * This will take care than all pixmaps are released */ +void PRESENTDestroy(Display *dpy, PRESENTpriv *present_priv); + +BOOL PRESENTPixmapInit(PRESENTpriv *present_priv, Pixmap pixmap, PRESENTPixmapPriv **present_pixmap_priv); + +#ifdef D3D9NINE_DRI2 +BOOL DRI2FallbackPRESENTPixmap(PRESENTpriv *present_priv, struct DRI2priv *priv, + int fd, int width, int height, int stride, int depth, + int bpp, PRESENTPixmapPriv **present_pixmap_priv); +#endif + +BOOL PRESENTTryFreePixmap(Display *dpy, PRESENTPixmapPriv *present_pixmap_priv); + +BOOL PRESENTHelperCopyFront(Display *dpy, PRESENTPixmapPriv *present_pixmap_priv); + +BOOL PRESENTPixmap(Display *dpy, XID window, PRESENTPixmapPriv *present_pixmap_priv, + const UINT PresentationInterval, const BOOL PresentAsync, const BOOL SwapEffectCopy, + const RECT *pSourceRect, const RECT *pDestRect, const RGNDATA *pDirtyRegion); + +BOOL PRESENTWaitPixmapReleased(PRESENTPixmapPriv *present_pixmap_priv); + +BOOL PRESENTIsPixmapReleased(PRESENTPixmapPriv *present_pixmap_priv); + +BOOL PRESENTWaitReleaseEvent(PRESENTpriv *present_priv); + +#endif /* __WINE_DRI3_H */ diff --git a/dlls/d3d9-nine/present.c b/dlls/d3d9-nine/present.c new file mode 100644 index 0000000000..2d9390a358 --- /dev/null +++ b/dlls/d3d9-nine/present.c @@ -0,0 +1,1748 @@ +/* + * Wine ID3DAdapter9 support functions + * + * Copyright 2013 Joakim Sindholt + * Christoph Bumiller + * Copyright 2014 Tiziano Bacocco + * David Heidelberger + * Copyright 2014-2015 Axel Davy + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3d9nine); + +#include +#include + +#include "dri3.h" +#include "wndproc.h" + +#include "wine/library.h" // for wine_dl* +#include "wine/unicode.h" // for strcpyW + +#ifndef D3DPRESENT_DONOTWAIT +#define D3DPRESENT_DONOTWAIT 0x00000001 +#endif + +#define WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MAJOR 1 +#if defined (ID3DPresent_SetPresentParameters2) +#define WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR 3 +#elif defined (ID3DPresent_ResolutionMismatch) +#define WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR 2 +#elif defined (ID3DPresent_GetWindowOccluded) +#define WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR 1 +#else +#define WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR 0 +#endif + +static const struct D3DAdapter9DRM *d3d9_drm = NULL; +#ifdef D3D9NINE_DRI2 +static int is_dri2_fallback = 0; +#endif + +/* Start section of x11drv.h */ +#define X11DRV_ESCAPE 6789 +enum x11drv_escape_codes +{ + X11DRV_SET_DRAWABLE, /* set current drawable for a DC */ + X11DRV_GET_DRAWABLE, /* get current drawable for a DC */ + X11DRV_START_EXPOSURES, /* start graphics exposures */ + X11DRV_END_EXPOSURES, /* end graphics exposures */ + X11DRV_FLUSH_GL_DRAWABLE /* flush changes made to the gl drawable */ +}; + +struct x11drv_escape_get_drawable +{ + enum x11drv_escape_codes code; /* escape code (X11DRV_GET_DRAWABLE) */ + Drawable drawable; /* X drawable */ + Drawable gl_drawable; /* GL drawable */ + int pixel_format; /* internal GL pixel format */ +}; +/* End section x11drv.h */ + +static XContext d3d_hwnd_context; +static CRITICAL_SECTION context_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &context_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": context_section") } +}; +static CRITICAL_SECTION context_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + +const GUID IID_ID3DPresent = { 0x77D60E80, 0xF1E6, 0x11DF, { 0x9E, 0x39, 0x95, 0x0C, 0xDF, 0xD7, 0x20, 0x85 } }; +const GUID IID_ID3DPresentGroup = { 0xB9C3016E, 0xF32A, 0x11DF, { 0x9C, 0x18, 0x92, 0xEA, 0xDE, 0xD7, 0x20, 0x85 } }; + +struct d3d_drawable +{ + Drawable drawable; /* X11 drawable */ + HDC hdc; + HWND wnd; /* HWND (for convenience) */ +}; + +struct DRI3Present +{ + /* COM vtable */ + void *vtable; + /* IUnknown reference count */ + LONG refs; + + D3DPRESENT_PARAMETERS params; + HWND focus_wnd; + PRESENTpriv *present_priv; +#ifdef D3D9NINE_DRI2 + struct DRI2priv *dri2_priv; +#endif + + WCHAR devname[32]; + HCURSOR hCursor; + + DEVMODEW initial_mode; + + DWORD style; + DWORD style_ex; + + BOOL reapply_mode; + BOOL ex; + BOOL resolution_mismatch; + BOOL occluded; + BOOL drop_wnd_messages; + BOOL no_window_changes; + Display *gdi_display; + + UINT present_interval; + BOOL present_async; + BOOL present_swapeffectcopy; + BOOL allow_discard_delayed_release; + BOOL tear_free_discard; + struct d3d_drawable *d3d; +}; + +struct D3DWindowBuffer +{ + PRESENTPixmapPriv *present_pixmap_priv; +}; + +static void free_d3dadapter_drawable(struct d3d_drawable *d3d) +{ + ReleaseDC(d3d->wnd, d3d->hdc); + HeapFree(GetProcessHeap(), 0, d3d); +} + +static void destroy_d3dadapter_drawable(Display *gdi_display, HWND hwnd) +{ + struct d3d_drawable *d3d; + + EnterCriticalSection(&context_section); + if (!XFindContext(gdi_display, (XID)hwnd, + d3d_hwnd_context, (char **)&d3d)) + { + XDeleteContext(gdi_display, (XID)hwnd, d3d_hwnd_context); + free_d3dadapter_drawable(d3d); + } + LeaveCriticalSection(&context_section); +} + +static RECT DRI3Present_GetClientRecWindowRelative(HWND hwnd) +{ + RECT rect; + RECT wnd; + + /* Get client space dimensions */ + GetClientRect(hwnd, &rect); + + /* Get window in screen space */ + GetWindowRect(hwnd, &wnd); + + /* Transform to offset */ + MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT) &wnd, 2); + wnd.top *= -1; + wnd.left *= -1; + wnd.bottom = wnd.top + rect.bottom; + wnd.right = wnd.left + rect.right; + + return wnd; +} + +static struct d3d_drawable *create_d3dadapter_drawable(HWND hwnd) +{ + struct x11drv_escape_get_drawable extesc = { X11DRV_GET_DRAWABLE }; + struct d3d_drawable *d3d; + + d3d = HeapAlloc(GetProcessHeap(), 0, sizeof(*d3d)); + if (!d3d) + { + ERR("Couldn't allocate d3d_drawable.\n"); + return NULL; + } + + d3d->hdc = GetDCEx(hwnd, 0, DCX_CACHE | DCX_CLIPSIBLINGS); + if (ExtEscape(d3d->hdc, X11DRV_ESCAPE, sizeof(extesc), (LPCSTR)&extesc, + sizeof(extesc), (LPSTR)&extesc) <= 0) + { + ERR("Unexpected error in X Drawable lookup (hwnd=%p, hdc=%p)\n", hwnd, d3d->hdc); + ReleaseDC(hwnd, d3d->hdc); + HeapFree(GetProcessHeap(), 0, d3d); + return NULL; + } + + d3d->drawable = extesc.drawable; + d3d->wnd = hwnd; + + return d3d; +} + +static struct d3d_drawable *get_d3d_drawable(Display *gdi_display, HWND hwnd) +{ + struct d3d_drawable *d3d, *race; + + EnterCriticalSection(&context_section); + if (!XFindContext(gdi_display, (XID)hwnd, d3d_hwnd_context, (char **)&d3d)) + { + return d3d; + } + LeaveCriticalSection(&context_section); + + TRACE("No d3d_drawable attached to hwnd %p, creating one.\n", hwnd); + + d3d = create_d3dadapter_drawable(hwnd); + if (!d3d) + return NULL; + + EnterCriticalSection(&context_section); + if (!XFindContext(gdi_display, (XID)hwnd, + d3d_hwnd_context, (char **)&race)) + { + /* apparently someone beat us to creating this d3d drawable. Let's not + waste more time with X11 calls and just use theirs instead. */ + free_d3dadapter_drawable(d3d); + return race; + } + XSaveContext(gdi_display, (XID)hwnd, d3d_hwnd_context, (char *)d3d); + return d3d; +} + +static void release_d3d_drawable(struct d3d_drawable *d3d) +{ + if (!d3d) + ERR("Driver internal error: d3d_drawable is NULL\n"); + LeaveCriticalSection(&context_section); +} + +static ULONG WINAPI DRI3Present_AddRef(struct DRI3Present *This) +{ + ULONG refs = InterlockedIncrement(&This->refs); + TRACE("%p increasing refcount to %u.\n", This, refs); + return refs; +} + +static ULONG WINAPI DRI3Present_Release(struct DRI3Present *This) +{ + ULONG refs = InterlockedDecrement(&This->refs); + TRACE("%p decreasing refcount to %u.\n", This, refs); + if (refs == 0) + { + /* dtor */ + (void) nine_unregister_window(This->focus_wnd); + if (This->d3d) + destroy_d3dadapter_drawable(This->gdi_display, This->d3d->wnd); + ChangeDisplaySettingsExW(This->devname, &(This->initial_mode), 0, CDS_FULLSCREEN, NULL); + PRESENTDestroy(This->gdi_display, This->present_priv); +#ifdef D3D9NINE_DRI2 + if (is_dri2_fallback) + DRI2FallbackDestroy(This->dri2_priv); +#endif + HeapFree(GetProcessHeap(), 0, This); + } + return refs; +} + +static HRESULT WINAPI DRI3Present_QueryInterface(struct DRI3Present *This, + REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + if (IsEqualGUID(&IID_ID3DPresent, riid) || + IsEqualGUID(&IID_IUnknown, riid)) + { + *ppvObject = This; + DRI3Present_AddRef(This); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); + *ppvObject = NULL; + + return E_NOINTERFACE; +} + +static HRESULT DRI3Present_ChangePresentParameters(struct DRI3Present *This, + D3DPRESENT_PARAMETERS *params); + +static HRESULT WINAPI DRI3Present_SetPresentParameters(struct DRI3Present *This, + D3DPRESENT_PARAMETERS *pPresentationParameters, + D3DDISPLAYMODEEX *pFullscreenDisplayMode) +{ + if (pFullscreenDisplayMode) + FIXME("Ignoring pFullscreenDisplayMode\n"); + return DRI3Present_ChangePresentParameters(This, pPresentationParameters); +} + +static HRESULT WINAPI DRI3Present_D3DWindowBufferFromDmaBuf(struct DRI3Present *This, + int dmaBufFd, int width, int height, int stride, int depth, + int bpp, struct D3DWindowBuffer **out) +{ + Pixmap pixmap; + +#ifdef D3D9NINE_DRI2 + if (is_dri2_fallback) + { + *out = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct D3DWindowBuffer)); + if (!DRI2FallbackPRESENTPixmap(This->present_priv, This->dri2_priv, + dmaBufFd, width, height, stride, depth, + bpp, &((*out)->present_pixmap_priv))) + { + ERR("DRI2FallbackPRESENTPixmap failed\n"); + HeapFree(GetProcessHeap(), 0, *out); + return D3DERR_DRIVERINTERNALERROR; + } + return D3D_OK; + } +#endif + if (!DRI3PixmapFromDmaBuf(This->gdi_display, DefaultScreen(This->gdi_display), + dmaBufFd, width, height, stride, depth, bpp, &pixmap)) + { + ERR("DRI3PixmapFromDmaBuf failed\n"); + return D3DERR_DRIVERINTERNALERROR; + } + + *out = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct D3DWindowBuffer)); + if (!PRESENTPixmapInit(This->present_priv, pixmap, &((*out)->present_pixmap_priv))) + { + ERR("PRESENTPixmapInit failed\n"); + HeapFree(GetProcessHeap(), 0, *out); + return D3DERR_DRIVERINTERNALERROR; + } + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_DestroyD3DWindowBuffer(struct DRI3Present *This, + struct D3DWindowBuffer *buffer) +{ + /* the pixmap is managed by the PRESENT backend. + * But if it can delete it right away, we may have + * better performance */ + PRESENTTryFreePixmap(This->gdi_display, buffer->present_pixmap_priv); + HeapFree(GetProcessHeap(), 0, buffer); + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_WaitBufferReleased(struct DRI3Present *This, + struct D3DWindowBuffer *buffer) +{ + if(!PRESENTWaitPixmapReleased(buffer->present_pixmap_priv)) + { + ERR("PRESENTWaitPixmapReleased failed\n"); + return D3DERR_DRIVERINTERNALERROR; + } + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_FrontBufferCopy(struct DRI3Present *This, + struct D3DWindowBuffer *buffer) +{ +#ifdef D3D9NINE_DRI2 + if (is_dri2_fallback) + return D3DERR_DRIVERINTERNALERROR; +#endif + if (PRESENTHelperCopyFront(This->gdi_display, buffer->present_pixmap_priv)) + return D3D_OK; + else + return D3DERR_DRIVERINTERNALERROR; +} + +/* Try to detect client side window decorations by walking the X Drawable up. + * In case there's an intermediate Drawable, server side window decorations are used. + * TODO: Find a X11 function to query for window decorations. + */ +static BOOL DRI3Present_HasClientSideWindowDecorations(struct DRI3Present *This, + HWND hwnd) +{ + struct x11drv_escape_get_drawable extesc = { X11DRV_GET_DRAWABLE }; + Window Wroot; + Window Wparent; + Window *Wchildren; + unsigned int numchildren; + HWND parent; + HDC hdc; + BOOL ret = TRUE; + + parent = GetParent(hwnd); + if (!parent) + parent = GetDesktopWindow(); + if (!parent) + { + ERR("Unexpected error getting the parent hwnd (hwnd=%p)\n", hwnd); + return FALSE; + } + + hdc = GetDCEx(hwnd, 0, DCX_CACHE | DCX_CLIPSIBLINGS); + if (!hdc) + return FALSE; + if (ExtEscape(hdc, X11DRV_ESCAPE, sizeof(extesc), (LPCSTR)&extesc, + sizeof(extesc), (LPSTR)&extesc) <= 0) + { + ERR("Unexpected error in X Drawable lookup (hwnd=%p, hdc=%p)\n", hwnd, hdc); + ReleaseDC(hwnd, hdc); + return FALSE; + } + ReleaseDC(hwnd, hdc); + + if (XQueryTree(This->gdi_display, extesc.drawable, &Wroot, &Wparent, &Wchildren, &numchildren)) + { + hdc = GetDCEx(parent, 0, DCX_CACHE | DCX_CLIPSIBLINGS); + if (!hdc) + return FALSE; + + if (ExtEscape(hdc, X11DRV_ESCAPE, sizeof(extesc), (LPCSTR)&extesc, + sizeof(extesc), (LPSTR)&extesc) <= 0) + { + ERR("Unexpected error in X Drawable lookup (hwnd=%p, hdc=%p)\n", parent, hdc); + ReleaseDC(parent, hdc); + return FALSE; + } + ReleaseDC(parent, hdc); + + if (Wparent != extesc.drawable) + { + /* Found at least one intermediate window */ + ret = FALSE; + } + if (Wchildren) + free(Wchildren); + } + + return ret; +} + +static HRESULT WINAPI DRI3Present_PresentBuffer( struct DRI3Present *This, + struct D3DWindowBuffer *buffer, HWND hWndOverride, const RECT *pSourceRect, + const RECT *pDestRect, const RGNDATA *pDirtyRegion, DWORD Flags ) +{ + struct d3d_drawable *d3d; + RECT dest_translate; + RECT offset; + HWND hwnd; + + if (hWndOverride) + hwnd = hWndOverride; + else if (This->params.hDeviceWindow) + hwnd = This->params.hDeviceWindow; + else + hwnd = This->focus_wnd; + + d3d = get_d3d_drawable(This->gdi_display, hwnd); + + if (!d3d) + return D3DERR_DRIVERINTERNALERROR; + + /* TODO: should we use a list here instead ? */ + if (This->d3d && (This->d3d->wnd != d3d->wnd)) + destroy_d3dadapter_drawable(This->gdi_display, This->d3d->wnd); + + This->d3d = d3d; + + /* In case of client side window decorations we need to add an offset within + * the X drawable. + * FIXME: Call once on window style / size change */ + if (DRI3Present_HasClientSideWindowDecorations(This, hwnd)) + { + offset = DRI3Present_GetClientRecWindowRelative(hwnd); + + if ((offset.top != 0) || (offset.left != 0)) + { + if (!pDestRect) + pDestRect = (const RECT *) &offset; + else + { + dest_translate.top = pDestRect->top + offset.top; + dest_translate.left = pDestRect->left + offset.left; + dest_translate.bottom = pDestRect->bottom + offset.bottom; + dest_translate.right = pDestRect->right + offset.right; + pDestRect = (const RECT *) &dest_translate; + } + } + } + + if (!PRESENTPixmap(This->gdi_display, d3d->drawable, buffer->present_pixmap_priv, + This->present_interval, This->present_async, This->present_swapeffectcopy, + pSourceRect, pDestRect, pDirtyRegion)) + { + release_d3d_drawable(d3d); + return D3DERR_DRIVERINTERNALERROR; + } + release_d3d_drawable(d3d); + + return D3D_OK; +} + +/* Based on wine's wined3d_get_adapter_raster_status. */ +static HRESULT WINAPI DRI3Present_GetRasterStatus( struct DRI3Present *This, + D3DRASTER_STATUS *pRasterStatus ) +{ + LONGLONG freq_per_frame, freq_per_line; + LARGE_INTEGER counter, freq_per_sec; + unsigned refresh_rate, height; + + TRACE("This=%p, pRasterStatus=%p\n", This, pRasterStatus); + + if (!QueryPerformanceCounter(&counter) || !QueryPerformanceFrequency(&freq_per_sec)) + return D3DERR_INVALIDCALL; + + if (This->params.Windowed) + { + refresh_rate = This->initial_mode.dmDisplayFrequency; + height = This->initial_mode.dmPelsHeight; + } + else + { + refresh_rate = This->params.FullScreen_RefreshRateInHz; + height = This->params.BackBufferHeight; + } + + if (refresh_rate == 0) + refresh_rate = 60; + + TRACE("refresh_rate=%u, height=%u\n", refresh_rate, height); + + freq_per_frame = freq_per_sec.QuadPart / refresh_rate; + /* Assume 20 scan lines in the vertical blank. */ + freq_per_line = freq_per_frame / (height + 20); + pRasterStatus->ScanLine = (counter.QuadPart % freq_per_frame) / freq_per_line; + if (pRasterStatus->ScanLine < height) + pRasterStatus->InVBlank = FALSE; + else + { + pRasterStatus->ScanLine = 0; + pRasterStatus->InVBlank = TRUE; + } + + TRACE("Returning fake value, InVBlank %u, ScanLine %u.\n", + pRasterStatus->InVBlank, pRasterStatus->ScanLine); + + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_GetDisplayMode( struct DRI3Present *This, + D3DDISPLAYMODEEX *pMode, D3DDISPLAYROTATION *pRotation ) +{ + DEVMODEW dm; + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + EnumDisplaySettingsExW(This->devname, ENUM_CURRENT_SETTINGS, &dm, 0); + pMode->Width = dm.dmPelsWidth; + pMode->Height = dm.dmPelsHeight; + pMode->RefreshRate = dm.dmDisplayFrequency; + pMode->ScanLineOrdering = (dm.dmDisplayFlags & DM_INTERLACED) ? + D3DSCANLINEORDERING_INTERLACED : D3DSCANLINEORDERING_PROGRESSIVE; + + /* XXX This is called "guessing" */ + switch (dm.dmBitsPerPel) + { + case 32: pMode->Format = D3DFMT_X8R8G8B8; break; + case 24: pMode->Format = D3DFMT_R8G8B8; break; + case 16: pMode->Format = D3DFMT_R5G6B5; break; + default: + WARN("Unknown display format with %u bpp.\n", dm.dmBitsPerPel); + pMode->Format = D3DFMT_UNKNOWN; + } + + switch (dm.dmDisplayOrientation) + { + case DMDO_DEFAULT: *pRotation = D3DDISPLAYROTATION_IDENTITY; break; + case DMDO_90: *pRotation = D3DDISPLAYROTATION_90; break; + case DMDO_180: *pRotation = D3DDISPLAYROTATION_180; break; + case DMDO_270: *pRotation = D3DDISPLAYROTATION_270; break; + default: + WARN("Unknown display rotation %u.\n", dm.dmDisplayOrientation); + *pRotation = D3DDISPLAYROTATION_IDENTITY; + } + + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_GetPresentStats( struct DRI3Present *This, D3DPRESENTSTATS *pStats ) +{ + FIXME("(%p, %p), stub!\n", This, pStats); + return D3DERR_INVALIDCALL; +} + +static HRESULT WINAPI DRI3Present_GetCursorPos( struct DRI3Present *This, POINT *pPoint ) +{ + BOOL ok; + HWND draw_window; + + if (!pPoint) + return D3DERR_INVALIDCALL; + + draw_window = This->params.hDeviceWindow ? + This->params.hDeviceWindow : This->focus_wnd; + + ok = GetCursorPos(pPoint); + ok = ok && ScreenToClient(draw_window, pPoint); + return ok ? S_OK : D3DERR_DRIVERINTERNALERROR; +} + +static HRESULT WINAPI DRI3Present_SetCursorPos( struct DRI3Present *This, POINT *pPoint ) +{ + BOOL ok; + POINT real_pos; + + if (!pPoint) + return D3DERR_INVALIDCALL; + + ok = SetCursorPos(pPoint->x, pPoint->y); + if (!ok) + goto error; + + ok = GetCursorPos(&real_pos); + if (!ok || real_pos.x != pPoint->x || real_pos.y != pPoint->y) + goto error; + + return D3D_OK; + +error: + SetCursor(NULL); /* Hide cursor rather than put wrong pos */ + return D3DERR_DRIVERINTERNALERROR; +} + +/* Note: assuming 32x32 cursor */ +static HRESULT WINAPI DRI3Present_SetCursor( struct DRI3Present *This, void *pBitmap, + POINT *pHotspot, BOOL bShow ) +{ + if (pBitmap) + { + ICONINFO info; + HCURSOR cursor; + + DWORD mask[32]; + memset(mask, ~0, sizeof(mask)); + + if (!pHotspot) + return D3DERR_INVALIDCALL; + info.fIcon = FALSE; + info.xHotspot = pHotspot->x; + info.yHotspot = pHotspot->y; + info.hbmMask = CreateBitmap(32, 32, 1, 1, mask); + info.hbmColor = CreateBitmap(32, 32, 1, 32, pBitmap); + + cursor = CreateIconIndirect(&info); + if (info.hbmMask) DeleteObject(info.hbmMask); + if (info.hbmColor) DeleteObject(info.hbmColor); + if (cursor) + DestroyCursor(This->hCursor); + This->hCursor = cursor; + } + SetCursor(bShow ? This->hCursor : NULL); + + return D3D_OK; +} + +static HRESULT WINAPI DRI3Present_SetGammaRamp( struct DRI3Present *This, + const D3DGAMMARAMP *pRamp, HWND hWndOverride ) +{ + HWND hWnd = hWndOverride ? hWndOverride : This->focus_wnd; + HDC hdc; + BOOL ok; + if (!pRamp) + return D3DERR_INVALIDCALL; + + hdc = GetDC(hWnd); + ok = SetDeviceGammaRamp(hdc, (void *)pRamp); + ReleaseDC(hWnd, hdc); + return ok ? D3D_OK : D3DERR_DRIVERINTERNALERROR; +} + +static HRESULT WINAPI DRI3Present_GetWindowInfo( struct DRI3Present *This, + HWND hWnd, int *width, int *height, int *depth ) +{ + HRESULT hr; + RECT pRect; + + if (!hWnd) + hWnd = This->focus_wnd; + hr = GetClientRect(hWnd, &pRect); + if (!hr) + return D3DERR_INVALIDCALL; + *width = pRect.right - pRect.left; + *height = pRect.bottom - pRect.top; + *depth = 24; //TODO + return D3D_OK; +} + +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 1 +static BOOL WINAPI DRI3Present_GetWindowOccluded(struct DRI3Present *This) +{ + return This->occluded; +} +#endif + +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 2 +static BOOL WINAPI DRI3Present_ResolutionMismatch(struct DRI3Present *This) +{ + /* The resolution might change due to a third party app. + * Poll this function to get the device's resolution match. + * A device reset is required to restore the requested resolution. + */ + return This->resolution_mismatch; +} + +static HANDLE WINAPI DRI3Present_CreateThread( struct DRI3Present *This, + void *pThreadfunc, void *pParam ) +{ + LPTHREAD_START_ROUTINE lpStartAddress = + (LPTHREAD_START_ROUTINE) pThreadfunc; + + return CreateThread(NULL, 0, lpStartAddress, pParam, 0, NULL); +} + +static BOOL WINAPI DRI3Present_WaitForThread( struct DRI3Present *This, HANDLE thread ) +{ + DWORD ExitCode = 0; + while (GetExitCodeThread(thread, &ExitCode) && ExitCode == STILL_ACTIVE) + Sleep(10); + + return TRUE; +} +#endif + +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 3 +static HRESULT WINAPI DRI3Present_SetPresentParameters2( struct DRI3Present *This, D3DPRESENT_PARAMETERS2 *pParams ) +{ + This->allow_discard_delayed_release = pParams->AllowDISCARDDelayedRelease; + This->tear_free_discard = pParams->AllowDISCARDDelayedRelease && pParams->TearFreeDISCARD; + return D3D_OK; +} + +static BOOL WINAPI DRI3Present_IsBufferReleased( struct DRI3Present *This, struct D3DWindowBuffer *buffer ) +{ + return PRESENTIsPixmapReleased(buffer->present_pixmap_priv); +} + +static HRESULT WINAPI DRI3Present_WaitBufferReleaseEvent( struct DRI3Present *This ) +{ + PRESENTWaitReleaseEvent(This->present_priv); + return D3D_OK; +} +#endif + +/*----------*/ + +static ID3DPresentVtbl DRI3Present_vtable = { + (void *)DRI3Present_QueryInterface, + (void *)DRI3Present_AddRef, + (void *)DRI3Present_Release, + (void *)DRI3Present_SetPresentParameters, + (void *)DRI3Present_D3DWindowBufferFromDmaBuf, + (void *)DRI3Present_DestroyD3DWindowBuffer, + (void *)DRI3Present_WaitBufferReleased, + (void *)DRI3Present_FrontBufferCopy, + (void *)DRI3Present_PresentBuffer, + (void *)DRI3Present_GetRasterStatus, + (void *)DRI3Present_GetDisplayMode, + (void *)DRI3Present_GetPresentStats, + (void *)DRI3Present_GetCursorPos, + (void *)DRI3Present_SetCursorPos, + (void *)DRI3Present_SetCursor, + (void *)DRI3Present_SetGammaRamp, + (void *)DRI3Present_GetWindowInfo, +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 1 + (void *)DRI3Present_GetWindowOccluded, +#endif +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 2 + (void *)DRI3Present_ResolutionMismatch, + (void *)DRI3Present_CreateThread, + (void *)DRI3Present_WaitForThread, +#endif +#if WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR >= 3 + (void *)DRI3Present_SetPresentParameters2, + (void *)DRI3Present_IsBufferReleased, + (void *)DRI3Present_WaitBufferReleaseEvent, +#endif +}; + +/* The following code is based on WINE's wined3d/device.c and + * wined3d/swapchain.c and WINE's d3d9 files. */ + +static LONG fullscreen_style(LONG style) +{ + /* Make sure the window is managed, otherwise we won't get keyboard input. */ + style |= WS_POPUP | WS_SYSMENU; + style &= ~(WS_CAPTION | WS_THICKFRAME); + + return style; +} + +static LONG fullscreen_exstyle(LONG exstyle) +{ + /* Filter out window decorations. */ + exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE); + + return exstyle; +} + +static HRESULT DRI3Present_ChangeDisplaySettingsIfNeccessary(struct DRI3Present *This, DEVMODEW *new_mode) +{ + DEVMODEW current_mode; + LONG hr; + + /* Filter invalid resolution */ + if (!new_mode->dmPelsWidth || !new_mode->dmPelsHeight) + return D3DERR_INVALIDCALL; + + /* Ignore invalid frequency requested */ + if (new_mode->dmDisplayFrequency > 1000) + new_mode->dmDisplayFrequency = 0; + + ZeroMemory(¤t_mode, sizeof(DEVMODEW)); + current_mode.dmSize = sizeof(DEVMODEW); + /* Only change the mode if necessary. */ + if (!EnumDisplaySettingsW(This->devname, ENUM_CURRENT_SETTINGS, ¤t_mode)) + ERR("Failed to get current display mode.\n"); + else if (current_mode.dmPelsWidth != new_mode->dmPelsWidth + || current_mode.dmPelsHeight != new_mode->dmPelsHeight + || (current_mode.dmDisplayFrequency != new_mode->dmDisplayFrequency + && (new_mode->dmFields & DM_DISPLAYFREQUENCY))) + { + hr = ChangeDisplaySettingsExW(This->devname, new_mode, 0, CDS_FULLSCREEN, NULL); + if (hr != DISP_CHANGE_SUCCESSFUL) + { + /* try again without display RefreshRate */ + if (new_mode->dmFields & DM_DISPLAYFREQUENCY) + { + new_mode->dmFields &= ~DM_DISPLAYFREQUENCY; + new_mode->dmDisplayFrequency = 0; + hr = ChangeDisplaySettingsExW(This->devname, new_mode, 0, CDS_FULLSCREEN, NULL); + if (hr != DISP_CHANGE_SUCCESSFUL) + { + ERR("ChangeDisplaySettingsExW failed with 0x%08X\n", hr); + return D3DERR_INVALIDCALL; + } + } + else + { + ERR("ChangeDisplaySettingsExW failed with 0x%08X\n", hr); + return D3DERR_INVALIDCALL; + } + } + } + return D3D_OK; +} + +LRESULT device_process_message(struct DRI3Present *present, HWND window, BOOL unicode, + UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc) +{ + boolean drop_wnd_messages; + DEVMODEW current_mode; + DEVMODEW new_mode; + + TRACE("Got message: window %p, message %#x, wparam %#lx, lparam %#lx.\n", + window, message, wparam, lparam); + + if (present->drop_wnd_messages && message != WM_DISPLAYCHANGE) + { + TRACE("Filtering message: window %p, message %#x, wparam %#lx, lparam %#lx.\n", + window, message, wparam, lparam); + if (unicode) + return DefWindowProcW(window, message, wparam, lparam); + else + return DefWindowProcA(window, message, wparam, lparam); + } + + if (message == WM_DESTROY) + { + TRACE("unregister window %p.\n", window); + (void) nine_unregister_window(window); + } + else if (message == WM_DISPLAYCHANGE) + { + /* Ex restores display mode, while non Ex requires the + * user to call Device::Reset() */ + ZeroMemory(¤t_mode, sizeof(DEVMODEW)); + current_mode.dmSize = sizeof(current_mode); + if (!present->ex && + !present->params.Windowed && + present->params.hDeviceWindow && + EnumDisplaySettingsW(present->devname, ENUM_CURRENT_SETTINGS, ¤t_mode) && + (current_mode.dmPelsWidth != present->params.BackBufferWidth || + current_mode.dmPelsHeight != present->params.BackBufferHeight)) + { + present->resolution_mismatch = TRUE; + } + else + { + present->resolution_mismatch = FALSE; + } + } + else if (message == WM_ACTIVATEAPP) + { + drop_wnd_messages = present->drop_wnd_messages; + present->drop_wnd_messages = TRUE; + + if (wparam == WA_INACTIVE) + { + present->occluded = TRUE; + present->reapply_mode = TRUE; + + ZeroMemory(&new_mode, sizeof(DEVMODEW)); + new_mode.dmSize = sizeof(new_mode); + if (EnumDisplaySettingsW(present->devname, ENUM_REGISTRY_SETTINGS, &new_mode)) + DRI3Present_ChangeDisplaySettingsIfNeccessary(present, &new_mode); + + if (!present->no_window_changes && + IsWindowVisible(present->params.hDeviceWindow)) + ShowWindow(present->params.hDeviceWindow, SW_MINIMIZE); + } + else + { + present->occluded = FALSE; + + if (!present->no_window_changes) + { + /* restore window */ + SetWindowPos(present->params.hDeviceWindow, NULL, 0, 0, + present->params.BackBufferWidth, present->params.BackBufferHeight, + SWP_NOACTIVATE | SWP_NOZORDER); + } + + if (present->ex) + { + ZeroMemory(&new_mode, sizeof(DEVMODEW)); + new_mode.dmSize = sizeof(new_mode); + new_mode.dmPelsWidth = present->params.BackBufferWidth; + new_mode.dmPelsHeight = present->params.BackBufferHeight; + new_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + if (present->params.FullScreen_RefreshRateInHz) + { + new_mode.dmFields |= DM_DISPLAYFREQUENCY; + new_mode.dmDisplayFrequency = present->params.FullScreen_RefreshRateInHz; + } + DRI3Present_ChangeDisplaySettingsIfNeccessary(present, &new_mode); + } + } + present->drop_wnd_messages = drop_wnd_messages; + } + else if (message == WM_SYSCOMMAND) + { + if (wparam == SC_RESTORE) + { + if (unicode) + DefWindowProcW(window, message, wparam, lparam); + else + DefWindowProcA(window, message, wparam, lparam); + } + } + + if (unicode) + return CallWindowProcW(proc, window, message, wparam, lparam); + else + return CallWindowProcA(proc, window, message, wparam, lparam); +} + +static void setup_fullscreen_window(struct DRI3Present *This, + HWND hwnd, int w, int h) +{ + boolean drop_wnd_messages; + LONG style, style_ex; + + This->style = GetWindowLongW(hwnd, GWL_STYLE); + This->style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE); + + style = fullscreen_style(This->style); + style_ex = fullscreen_exstyle(This->style_ex); + + drop_wnd_messages = This->drop_wnd_messages; + This->drop_wnd_messages = TRUE; + + SetWindowLongW(hwnd, GWL_STYLE, style); + SetWindowLongW(hwnd, GWL_EXSTYLE, style_ex); + + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, w, h, + SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE); + + This->drop_wnd_messages = drop_wnd_messages; +} + +static void move_fullscreen_window(struct DRI3Present *This, + HWND hwnd, int w, int h) +{ + boolean drop_wnd_messages; + LONG style, style_ex; + + /* move draw window back to place */ + + style = GetWindowLongW(hwnd, GWL_STYLE); + style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE); + + style = fullscreen_style(style); + style_ex = fullscreen_exstyle(style_ex); + + drop_wnd_messages = This->drop_wnd_messages; + This->drop_wnd_messages = TRUE; + SetWindowLongW(hwnd, GWL_STYLE, style); + SetWindowLongW(hwnd, GWL_EXSTYLE, style_ex); + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, w, h, + SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE); + This->drop_wnd_messages = drop_wnd_messages; +} + +static void restore_fullscreen_window(struct DRI3Present *This, + HWND hwnd) +{ + boolean drop_wnd_messages; + LONG style, style_ex; + + /* switch from fullscreen to window */ + style = GetWindowLongW(hwnd, GWL_STYLE); + style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE); + /* These flags are set by us, not the + * application, and we want to ignore them in the test below, since it's + * not the application's fault that they changed. Additionally, we want to + * preserve the current status of these flags (i.e. don't restore them) to + * more closely emulate the behavior of Direct3D, which leaves these flags + * alone when returning to windowed mode. */ + This->style ^= (This->style ^ style) & WS_VISIBLE; + This->style_ex ^= (This->style_ex ^ style_ex) & WS_EX_TOPMOST; + + /* Only restore the style if the application didn't modify it during the + * fullscreen phase. Some applications change it before calling Reset() + * when switching between windowed and fullscreen modes (HL2), some + * depend on the original style (Eve Online). */ + drop_wnd_messages = This->drop_wnd_messages; + This->drop_wnd_messages = TRUE; + if (style == fullscreen_style(This->style) && + style_ex == fullscreen_exstyle(This->style_ex)) + { + SetWindowLongW(hwnd, GWL_STYLE, This->style); + SetWindowLongW(hwnd, GWL_EXSTYLE, This->style_ex); + } + SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_NOACTIVATE); + This->drop_wnd_messages = drop_wnd_messages; + + This->style = 0; + This->style_ex = 0; +} + +static void DRI3Present_UpdatePresentationInterval(struct DRI3Present *This) +{ + switch(This->params.PresentationInterval) + { + case D3DPRESENT_INTERVAL_DEFAULT: + case D3DPRESENT_INTERVAL_ONE: + This->present_interval = 1; + This->present_async = FALSE; + break; + case D3DPRESENT_INTERVAL_TWO: + This->present_interval = 2; + This->present_async = FALSE; + break; + case D3DPRESENT_INTERVAL_THREE: + This->present_interval = 3; + This->present_async = FALSE; + break; + case D3DPRESENT_INTERVAL_FOUR: + This->present_interval = 4; + This->present_async = FALSE; + break; + case D3DPRESENT_INTERVAL_IMMEDIATE: + default: + This->present_interval = 0; + This->present_async = + !(This->params.SwapEffect == D3DSWAPEFFECT_DISCARD && + This->tear_free_discard); + break; + } + + /* D3DSWAPEFFECT_COPY: Force Copy. + * This->present_interval == 0: Force Copy to have buffers + * release as soon as possible (the display server/compositor + * won't hold any buffer), unless DISCARD and + * allow_discard_delayed_release */ + This->present_swapeffectcopy = + This->params.SwapEffect == D3DSWAPEFFECT_COPY || + (This->present_interval == 0 && + !(This->params.SwapEffect == D3DSWAPEFFECT_DISCARD && + This->allow_discard_delayed_release)); +} + +static HRESULT DRI3Present_ChangePresentParameters(struct DRI3Present *This, + D3DPRESENT_PARAMETERS *params) +{ + HWND focus_window = This->focus_wnd ? This->focus_wnd : params->hDeviceWindow; + RECT rect; + DEVMODEW new_mode; + HRESULT hr; + boolean drop_wnd_messages; + + TRACE("This=%p, params=%p, focus_window=%p, params->hDeviceWindow=%p\n", + This, params, focus_window, params->hDeviceWindow); + + This->params.SwapEffect = params->SwapEffect; + This->params.AutoDepthStencilFormat = params->AutoDepthStencilFormat; + This->params.Flags = params->Flags; + This->params.FullScreen_RefreshRateInHz = params->FullScreen_RefreshRateInHz; + This->params.PresentationInterval = params->PresentationInterval; + This->params.EnableAutoDepthStencil = params->EnableAutoDepthStencil; + if (!params->hDeviceWindow) + params->hDeviceWindow = This->params.hDeviceWindow; + else + This->params.hDeviceWindow = params->hDeviceWindow; + + if ((This->params.BackBufferWidth != params->BackBufferWidth) || + (This->params.BackBufferHeight != params->BackBufferHeight) || + (This->params.Windowed != params->Windowed) || + This->reapply_mode) + { + This->reapply_mode = FALSE; + + if (!params->Windowed) + { + TRACE("Setting fullscreen mode: %dx%d@%d\n", params->BackBufferWidth, + params->BackBufferHeight, params->FullScreen_RefreshRateInHz); + + /* switch display mode */ + ZeroMemory(&new_mode, sizeof(DEVMODEW)); + new_mode.dmPelsWidth = params->BackBufferWidth; + new_mode.dmPelsHeight = params->BackBufferHeight; + new_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + if (params->FullScreen_RefreshRateInHz) + { + new_mode.dmFields |= DM_DISPLAYFREQUENCY; + new_mode.dmDisplayFrequency = params->FullScreen_RefreshRateInHz; + } + new_mode.dmSize = sizeof(DEVMODEW); + hr = DRI3Present_ChangeDisplaySettingsIfNeccessary(This, &new_mode); + if (FAILED(hr)) + return hr; + + /* Dirty as BackBufferWidth and BackBufferHeight hasn't been set yet */ + This->resolution_mismatch = FALSE; + } + else if(!This->params.Windowed && params->Windowed) + { + TRACE("Setting fullscreen mode: %dx%d@%d\n", This->initial_mode.dmPelsWidth, + This->initial_mode.dmPelsHeight, This->initial_mode.dmDisplayFrequency); + + hr = DRI3Present_ChangeDisplaySettingsIfNeccessary(This, &This->initial_mode); + if (FAILED(hr)) + return hr; + + /* Dirty as BackBufferWidth and BackBufferHeight hasn't been set yet */ + This->resolution_mismatch = FALSE; + } + + if (This->params.Windowed) + { + if (!params->Windowed) + { + /* switch from window to fullscreen */ + if (!nine_register_window(focus_window, This)) + return D3DERR_INVALIDCALL; + + SetWindowPos(focus_window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + + setup_fullscreen_window(This, params->hDeviceWindow, + params->BackBufferWidth, params->BackBufferHeight); + } + } + else + { + if (!params->Windowed) + { + /* switch from fullscreen to fullscreen */ + drop_wnd_messages = This->drop_wnd_messages; + This->drop_wnd_messages = TRUE; + MoveWindow(params->hDeviceWindow, 0, 0, + params->BackBufferWidth, + params->BackBufferHeight, + TRUE); + This->drop_wnd_messages = drop_wnd_messages; + } + else if (This->style || This->style_ex) + { + restore_fullscreen_window(This, params->hDeviceWindow); + } + + if (params->Windowed && !nine_unregister_window(focus_window)) + ERR("Window %p is not registered with nine.\n", focus_window); + } + This->params.Windowed = params->Windowed; + } + else if (!params->Windowed) + { + move_fullscreen_window(This, params->hDeviceWindow, params->BackBufferWidth, params->BackBufferHeight); + } + else + { + TRACE("Nothing changed.\n"); + } + if (!params->BackBufferWidth || !params->BackBufferHeight) { + if (!params->Windowed) + return D3DERR_INVALIDCALL; + + if (!GetClientRect(params->hDeviceWindow, &rect)) + return D3DERR_INVALIDCALL; + + if (params->BackBufferWidth == 0) + params->BackBufferWidth = rect.right - rect.left; + + if (params->BackBufferHeight == 0) + params->BackBufferHeight = rect.bottom - rect.top; + } + + /* Set as last in case of failed reset those aren't updated */ + This->params.BackBufferWidth = params->BackBufferWidth; + This->params.BackBufferHeight = params->BackBufferHeight; + This->params.BackBufferFormat = params->BackBufferFormat; + This->params.BackBufferCount = params->BackBufferCount; + This->params.MultiSampleType = params->MultiSampleType; + This->params.MultiSampleQuality = params->MultiSampleQuality; + + DRI3Present_UpdatePresentationInterval(This); + + return D3D_OK; +} + +/* The following code isn't based on WINE's wined3d or d3d9. */ + +static HRESULT DRI3Present_new(Display *gdi_display, const WCHAR *devname, + D3DPRESENT_PARAMETERS *params, HWND focus_wnd, struct DRI3Present **out, + boolean ex, boolean no_window_changes) +{ + struct DRI3Present *This; + HWND focus_window; + DEVMODEW new_mode; + HRESULT hr; + RECT rect; + + if (!focus_wnd && !params->hDeviceWindow) + { + ERR("No focus HWND specified for presentation backend.\n"); + return D3DERR_INVALIDCALL; + } + + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct DRI3Present)); + if (!This) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + This->gdi_display = gdi_display; + This->vtable = &DRI3Present_vtable; + This->refs = 1; + This->focus_wnd = focus_wnd; + This->ex = ex; + This->no_window_changes = no_window_changes; + + /* store current resolution */ + ZeroMemory(&(This->initial_mode), sizeof(This->initial_mode)); + This->initial_mode.dmSize = sizeof(This->initial_mode); + EnumDisplaySettingsExW(This->devname, ENUM_CURRENT_SETTINGS, &(This->initial_mode), 0); + + if (!params->hDeviceWindow) + params->hDeviceWindow = This->focus_wnd; + + if (!params->Windowed) { + focus_window = This->focus_wnd ? This->focus_wnd : params->hDeviceWindow; + + if (!nine_register_window(focus_window, This)) + return D3DERR_INVALIDCALL; + + SetWindowPos(focus_window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + + /* switch display mode */ + ZeroMemory(&new_mode, sizeof(DEVMODEW)); + new_mode.dmSize = sizeof(DEVMODEW); + new_mode.dmPelsWidth = params->BackBufferWidth; + new_mode.dmPelsHeight = params->BackBufferHeight; + new_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + + if (params->FullScreen_RefreshRateInHz) + { + new_mode.dmFields |= DM_DISPLAYFREQUENCY; + new_mode.dmDisplayFrequency = params->FullScreen_RefreshRateInHz; + } + + hr = DRI3Present_ChangeDisplaySettingsIfNeccessary(This, &new_mode); + if (FAILED(hr)) + { + nine_unregister_window(focus_window); + HeapFree(GetProcessHeap(), 0, This); + return hr; + } + + /* Dirty as BackBufferWidth and BackBufferHeight hasn't been set yet */ + This->resolution_mismatch = FALSE; + + setup_fullscreen_window(This, params->hDeviceWindow, + params->BackBufferWidth, params->BackBufferHeight); + } else { + GetClientRect(params->hDeviceWindow, &rect); + if (!params->BackBufferWidth || !params->BackBufferHeight) { + + if (params->BackBufferWidth == 0) + params->BackBufferWidth = rect.right - rect.left; + + if (params->BackBufferHeight == 0) + params->BackBufferHeight = rect.bottom - rect.top; + } + } + + This->params = *params; + + DRI3Present_UpdatePresentationInterval(This); + + strcpyW(This->devname, devname); + + PRESENTInit(gdi_display, &(This->present_priv)); +#ifdef D3D9NINE_DRI2 + if (is_dri2_fallback && !DRI2FallbackInit(gdi_display, &(This->dri2_priv))) + return D3DERR_INVALIDCALL; +#endif + *out = This; + + return D3D_OK; +} + +struct DRI3PresentGroup +{ + /* COM vtable */ + void *vtable; + /* IUnknown reference count */ + LONG refs; + + boolean ex; + struct DRI3Present **present_backends; + unsigned npresent_backends; + Display *gdi_display; + boolean no_window_changes; +}; + +static ULONG WINAPI DRI3PresentGroup_AddRef(struct DRI3PresentGroup *This) +{ + ULONG refs = InterlockedIncrement(&This->refs); + TRACE("%p increasing refcount to %u.\n", This, refs); + return refs; +} + +static ULONG WINAPI DRI3PresentGroup_Release(struct DRI3PresentGroup *This) +{ + ULONG refs = InterlockedDecrement(&This->refs); + TRACE("%p decreasing refcount to %u.\n", This, refs); + if (refs == 0) + { + unsigned i; + if (This->present_backends) + { + for (i = 0; i < This->npresent_backends; ++i) + { + if (This->present_backends[i]) + DRI3Present_Release(This->present_backends[i]); + } + HeapFree(GetProcessHeap(), 0, This->present_backends); + } + HeapFree(GetProcessHeap(), 0, This); + } + return refs; +} + +static HRESULT WINAPI DRI3PresentGroup_QueryInterface(struct DRI3PresentGroup *This, + REFIID riid, void **ppvObject ) +{ + if (!ppvObject) + return E_POINTER; + if (IsEqualGUID(&IID_ID3DPresentGroup, riid) || + IsEqualGUID(&IID_IUnknown, riid)) + { + *ppvObject = This; + DRI3PresentGroup_AddRef(This); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); + *ppvObject = NULL; + + return E_NOINTERFACE; +} + +static UINT WINAPI DRI3PresentGroup_GetMultiheadCount(struct DRI3PresentGroup *This) +{ + FIXME("(%p), stub!\n", This); + return 1; +} + +static HRESULT WINAPI DRI3PresentGroup_GetPresent(struct DRI3PresentGroup *This, + UINT Index, ID3DPresent **ppPresent) +{ + if (Index >= DRI3PresentGroup_GetMultiheadCount(This)) + { + ERR("Index >= MultiHeadCount\n"); + return D3DERR_INVALIDCALL; + } + DRI3Present_AddRef(This->present_backends[Index]); + *ppPresent = (ID3DPresent *)This->present_backends[Index]; + + return D3D_OK; +} + +static HRESULT WINAPI DRI3PresentGroup_CreateAdditionalPresent(struct DRI3PresentGroup *This, + D3DPRESENT_PARAMETERS *pPresentationParameters, ID3DPresent **ppPresent) +{ + HRESULT hr; + hr = DRI3Present_new(This->gdi_display, This->present_backends[0]->devname, + pPresentationParameters, 0, (struct DRI3Present **)ppPresent, + This->ex, This->no_window_changes); + + return hr; +} + +static void WINAPI DRI3PresentGroup_GetVersion(struct DRI3PresentGroup *This, + int *major, int *minor) +{ + *major = WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MAJOR; + *minor = WINE_D3DADAPTER_DRIVER_PRESENT_VERSION_MINOR; +} + +static ID3DPresentGroupVtbl DRI3PresentGroup_vtable = { + (void *)DRI3PresentGroup_QueryInterface, + (void *)DRI3PresentGroup_AddRef, + (void *)DRI3PresentGroup_Release, + (void *)DRI3PresentGroup_GetMultiheadCount, + (void *)DRI3PresentGroup_GetPresent, + (void *)DRI3PresentGroup_CreateAdditionalPresent, + (void *)DRI3PresentGroup_GetVersion +}; + +HRESULT present_create_present_group(Display *gdi_display, const WCHAR *device_name, + UINT adapter, HWND focus_wnd, D3DPRESENT_PARAMETERS *params, + unsigned nparams, ID3DPresentGroup **group, boolean ex, DWORD BehaviorFlags) +{ + struct DRI3PresentGroup *This; + DISPLAY_DEVICEW dd; + HRESULT hr; + unsigned i; + + This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct DRI3PresentGroup)); + if (!This) + { + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + This->gdi_display = gdi_display; + This->vtable = &DRI3PresentGroup_vtable; + This->refs = 1; + This->ex = ex; + This->npresent_backends = nparams; + This->no_window_changes = !!(BehaviorFlags & D3DCREATE_NOWINDOWCHANGES); + This->present_backends = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + This->npresent_backends * sizeof(struct DRI3Present *)); + if (!This->present_backends) + { + DRI3PresentGroup_Release(This); + ERR("Out of memory.\n"); + return E_OUTOFMEMORY; + } + + if (nparams != 1) + adapter = 0; + + for (i = 0; i < This->npresent_backends; ++i) + { + ZeroMemory(&dd, sizeof(dd)); + dd.cb = sizeof(dd); + /* find final device name */ + if (!EnumDisplayDevicesW(device_name, adapter + i, &dd, 0)) + { + WARN("Couldn't find subdevice %d from `%s'\n", + i, debugstr_w(device_name)); + } + + /* create an ID3DPresent for it */ + hr = DRI3Present_new(gdi_display, dd.DeviceName, ¶ms[i], + focus_wnd, &This->present_backends[i], ex, This->no_window_changes); + if (FAILED(hr)) + { + DRI3PresentGroup_Release(This); + return hr; + } + } + + *group = (ID3DPresentGroup *)This; + TRACE("Returning %p\n", *group); + + return D3D_OK; +} + +HRESULT present_create_adapter9(Display *gdi_display, HDC hdc, ID3DAdapter9 **out) +{ + struct x11drv_escape_get_drawable extesc = { X11DRV_GET_DRAWABLE }; + HRESULT hr; + int fd; + + if (!d3d9_drm) + { + ERR("DRM drivers are not supported on your system.\n"); + return D3DERR_DRIVERINTERNALERROR; + } + + if (ExtEscape(hdc, X11DRV_ESCAPE, sizeof(extesc), (LPCSTR)&extesc, + sizeof(extesc), (LPSTR)&extesc) <= 0) + ERR("X11 drawable lookup failed (hdc=%p)\n", hdc); + +#ifdef D3D9NINE_DRI2 + if (!is_dri2_fallback && !DRI3Open(gdi_display, DefaultScreen(gdi_display), &fd)) +#else + if (!DRI3Open(gdi_display, DefaultScreen(gdi_display), &fd)) +#endif + { + ERR("DRI3Open failed (fd=%d)\n", fd); + return D3DERR_DRIVERINTERNALERROR; + } +#ifdef D3D9NINE_DRI2 + if (is_dri2_fallback && !DRI2FallbackOpen(gdi_display, DefaultScreen(gdi_display), &fd)) + { + ERR("DRI2Open failed (fd=%d)\n", fd); + return D3DERR_DRIVERINTERNALERROR; + } +#endif + hr = d3d9_drm->create_adapter(fd, out); + if (FAILED(hr)) + { + ERR("Unable to create ID3DAdapter9 (fd=%d)\n", fd); + return hr; + } + + TRACE("Created ID3DAdapter9 with fd %d\n", fd); + + return D3D_OK; +} + +BOOL present_has_d3dadapter(Display *gdi_display) +{ + static const void * WINAPI (*pD3DAdapter9GetProc)(const char *); + static void *handle = NULL; + static int done = 0; + HKEY regkey; + LSTATUS rc; + char *path = NULL; + + char errbuf[256]; + char pathbuf[MAX_PATH]; + + /* like in opengl.c (single threaded assumption OK?) */ + if (done) + return handle != NULL; + done = 1; + + if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Direct3DNine", ®key)) + { + DWORD type; + DWORD size = 0; + + rc = RegQueryValueExA(regkey, "ModulePath", 0, &type, NULL, &size); + if (rc == ERROR_FILE_NOT_FOUND) + goto use_default_path; + + TRACE("Reading registry key for module path\n"); + if (rc != ERROR_SUCCESS || type != REG_SZ) + { + ERR("Failed to read Direct3DNine ModulePath registry key: Invalid content\n"); + goto cleanup; + } + + path = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + 1); + if (!path) + { + ERR("Out of memory\n"); + return FALSE; + } + rc = RegQueryValueExA(regkey, "ModulePath", 0, &type, (LPBYTE)path, &size); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to read Direct3DNine registry\n"); + goto cleanup; + } + /* Split colon separated path for multi-arch support */ + if (strstr(path, ":")) + { + char *tmp_path = strstr(path, ":"); + + /* Replace colon by string terminate */ + *tmp_path = 0; + tmp_path ++; + handle = wine_dlopen(path, + RTLD_GLOBAL | RTLD_NOW, errbuf, sizeof(errbuf)); + if (!handle) + { + TRACE("Failed to load '%s': %s\n", path, errbuf); + + handle = wine_dlopen(tmp_path, + RTLD_GLOBAL | RTLD_NOW, errbuf, sizeof(errbuf)); + if (!handle) + { + TRACE("Failed to load '%s': %s\n", tmp_path, errbuf); + ERR("Failed to load '%s' and '%s' set by ModulePath.\n", + path, tmp_path); + goto cleanup; + } + } + } + else + { + handle = wine_dlopen(path, + RTLD_GLOBAL | RTLD_NOW, errbuf, sizeof(errbuf)); + if (!handle) + { + TRACE("Failed to load %s: %s\n", path, errbuf); + ERR("Failed to load '%s' set by ModulePath.\n", path); + goto cleanup; + } + } + memcpy(pathbuf, path, size >= sizeof(pathbuf) ? (sizeof(pathbuf)-1) : size); + pathbuf[sizeof(pathbuf)-1] = 0; + + HeapFree(GetProcessHeap(), 0, path); + } + +use_default_path: +#if !defined(D3D9NINE_MODULEPATH) + if (!handle) + { + ERR("d3d9-nine.dll was built without default module path.\n" + "Setting Software\\Wine\\Direct3DNine ModulePath is required\n"); + goto cleanup; + } +#else + if (!handle) + { + handle = wine_dlopen(D3D9NINE_MODULEPATH, + RTLD_GLOBAL | RTLD_NOW, errbuf, sizeof(errbuf)); + if (!handle) + { + ERR("Failed to load '%s': %s\n", D3D9NINE_MODULEPATH, errbuf); + goto cleanup; + } + memcpy(pathbuf, D3D9NINE_MODULEPATH, + sizeof(D3D9NINE_MODULEPATH) >= sizeof(pathbuf) ? + (sizeof(pathbuf)-1) : sizeof(D3D9NINE_MODULEPATH)); + pathbuf[sizeof(pathbuf)-1] = 0; + } +#endif + /* find our entry point in d3dadapter9 */ + pD3DAdapter9GetProc = wine_dlsym(handle, "D3DAdapter9GetProc", + errbuf, sizeof(errbuf)); + if (!pD3DAdapter9GetProc) + { + ERR("Failed to get the entry point from %s: %s", pathbuf, errbuf); + goto cleanup; + } + + /* get a handle to the drm backend struct */ + d3d9_drm = pD3DAdapter9GetProc("drm"); + if (!d3d9_drm) + { + ERR("%s doesn't support the drm backend.\n", pathbuf); + goto cleanup; + } + + /* verify that we're binary compatible */ + if (d3d9_drm->major_version != 0) + { + ERR("Version mismatch. %s has %d.%d, was expecting 0.x\n", + pathbuf, d3d9_drm->major_version, d3d9_drm->minor_version); + goto cleanup; + } + + /* this will be used to store d3d_drawables */ + d3d_hwnd_context = XUniqueContext(); + + if (!PRESENTCheckExtension(gdi_display, 1, 0)) + { + ERR("Unable to query PRESENT.\n"); + goto cleanup; + } + + if (!DRI3CheckExtension(gdi_display, 1, 0)) + { +#ifndef D3D9NINE_DRI2 + ERR("Unable to query DRI3.\n"); + goto cleanup; +#else + ERR("Unable to query DRI3. Trying DRI2 fallback (slower performance).\n"); + is_dri2_fallback = 1; + if (!DRI2FallbackCheckSupport(gdi_display)) + { + ERR("DRI2 fallback unsupported\n"); + goto cleanup; + } +#endif + } + + return TRUE; + +cleanup: + ERR("\033[1;31m\nNative Direct3D 9 will be unavailable." + "\nFor more information visit https://wiki.ixit.cz/d3d9\033[0m\n"); + if (handle) + { + wine_dlclose(handle, NULL, 0); + handle = NULL; + } + + if (path) + HeapFree(GetProcessHeap(), 0, path); + + return FALSE; +} + +BOOL enable_device_vtable_wrapper(void) +{ + if (!d3d9_drm) + { + ERR("enable_device_vtable_wrapper call before init.\n"); + return FALSE; + } + /* Since minor version 1, we can assume a copy of the internal vtable is stored in second pos. + * For now always enable if possible the wrapper (enables Steam overlay for example), + * we might in the future let user choose. */ + return d3d9_drm->minor_version >= 1; +} diff --git a/dlls/d3d9-nine/present.h b/dlls/d3d9-nine/present.h new file mode 100644 index 0000000000..a5516877f1 --- /dev/null +++ b/dlls/d3d9-nine/present.h @@ -0,0 +1,40 @@ +/* + * Wine present interface + * + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_PRESENT_H +#define __WINE_PRESENT_H + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#include + +HRESULT present_create_present_group(Display *gdi_display, const WCHAR *device_name, UINT adapter, + HWND focus, D3DPRESENT_PARAMETERS *params, unsigned nparams, ID3DPresentGroup **group, + boolean ex, DWORD BehaviorFlags); + +HRESULT present_create_adapter9(Display *gdi_display, HDC hdc, ID3DAdapter9 **adapter); + +BOOL present_has_d3dadapter(Display *gdi_display); + +BOOL enable_device_vtable_wrapper(void); + +#endif /* __WINE_PRESENT_H */ diff --git a/dlls/d3d9-nine/shader_validator.c b/dlls/d3d9-nine/shader_validator.c new file mode 100644 index 0000000000..03848b2aa2 --- /dev/null +++ b/dlls/d3d9-nine/shader_validator.c @@ -0,0 +1,88 @@ +/* + * Direct3D 9 ShaderValidator + * + * Copyright 2016 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ +#include "wine/debug.h" +WINE_DEFAULT_DEBUG_CHANNEL(d3d9nine); + +#include "winbase.h" + +#include "shader_validator.h" + +static HRESULT WINAPI IDirect3DShaderValidator9Impl_QueryInterface(IDirect3DShaderValidator9Impl *This, + REFIID riid, LPVOID* ppobj) +{ + /* TODO: AddRef(iface). */ + *ppobj = This; + TRACE("This=%p, riid=%s, object=%p.\n", This, debugstr_guid(riid), ppobj); + + return S_OK; +} + +static ULONG WINAPI IDirect3DShaderValidator9Impl_AddRef(IDirect3DShaderValidator9Impl *This) +{ + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("This=%p increasing refcount to %u.\n", This, ref); + + return ref; +} + +static ULONG WINAPI IDirect3DShaderValidator9Impl_Release(IDirect3DShaderValidator9Impl *This) +{ + ULONG ref = InterlockedDecrement(&This->ref); + TRACE("This=%p decreasing refcount to %u.\n", This, ref); + + if (ref == 0) + HeapFree(GetProcessHeap(), 0, This); + + return ref; +} + +static LONG WINAPI IDirect3DShaderValidator9Impl_Begin(IDirect3DShaderValidator9Impl *This, + void* callback, void* unknown1, ULONG unknown2) +{ + TRACE("This=%p, callback=%p, unknown1=%p, unknown2=%u\n", This, callback, unknown1, unknown2); + return 1; +} + +static LONG WINAPI IDirect3DShaderValidator9Impl_Instruction(IDirect3DShaderValidator9Impl *This, + const char* unknown1, unsigned int unknown2, const unsigned long* unknown3, unsigned int unknown4) +{ + TRACE("This=%p, unknown1=%p, unknown2=%u, unknown3=%p, unknown4=%u\n", This, unknown1, unknown2, unknown3, unknown4); + return 1; +} + +static LONG WINAPI IDirect3DShaderValidator9Impl_End(IDirect3DShaderValidator9Impl *This) +{ + TRACE("This=%p\n", This); + return 1; +} + +const void *IDirect3DShaderValidator9Vtbl[] = +{ + /* IUnknown */ + IDirect3DShaderValidator9Impl_QueryInterface, + IDirect3DShaderValidator9Impl_AddRef, + IDirect3DShaderValidator9Impl_Release, + /* IDirect3DShaderValidator9 */ + IDirect3DShaderValidator9Impl_Begin, + IDirect3DShaderValidator9Impl_Instruction, + IDirect3DShaderValidator9Impl_End +}; + diff --git a/dlls/d3d9-nine/shader_validator.h b/dlls/d3d9-nine/shader_validator.h new file mode 100644 index 0000000000..07a5e2d2b4 --- /dev/null +++ b/dlls/d3d9-nine/shader_validator.h @@ -0,0 +1,29 @@ +/* + * Direct3D 9 ShaderValidator + * + * Copyright 2016 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +typedef struct IDirect3DShaderValidator9Impl +{ + /* IUnknown fields */ + void *lpVtbl; + LONG ref; +} IDirect3DShaderValidator9Impl; + +const void *IDirect3DShaderValidator9Vtbl[6]; diff --git a/dlls/d3d9-nine/version.rc b/dlls/d3d9-nine/version.rc new file mode 100644 index 0000000000..bfafc2f24a --- /dev/null +++ b/dlls/d3d9-nine/version.rc @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Patrick Rudolph + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WINE_FILEDESCRIPTION_STR "Wine Gallium Nine Direct3D" +#define WINE_FILENAME_STR "d3d9-nine.dll" +#define WINE_FILEVERSION 5,3,1,904 +#define WINE_FILEVERSION_STR "5.3.1.904" +#define WINE_PRODUCTVERSION 5,3,1,904 +#define WINE_PRODUCTVERSION_STR "5.3.1.904" + +#include "wine/wine_common_ver.rc" diff --git a/dlls/d3d9-nine/wndproc.c b/dlls/d3d9-nine/wndproc.c new file mode 100644 index 0000000000..0ed80de7d7 --- /dev/null +++ b/dlls/d3d9-nine/wndproc.c @@ -0,0 +1,277 @@ +/* + * Copyright 2016 Patrick Rudolph + * + * Based on the file wined3d_main.c taken from wined3d: + * All credits go to the original developers: + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Jason Edmeades + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "wingdi.h" +#include "winuser.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "wndproc.h" + +WINE_DEFAULT_DEBUG_CHANNEL(d3dadapter); + +struct nine_wndproc +{ + HWND window; + BOOL unicode; + WNDPROC proc; + struct DRI3Present *present; +}; + +struct nine_wndproc_table +{ + struct nine_wndproc *entries; + unsigned int count; + unsigned int size; +}; + +static struct nine_wndproc_table wndproc_table; + +static CRITICAL_SECTION nine_wndproc_cs; +static CRITICAL_SECTION_DEBUG nine_wndproc_cs_debug = +{ + 0, 0, &nine_wndproc_cs, + {&nine_wndproc_cs_debug.ProcessLocksList, + &nine_wndproc_cs_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": nine_wndproc_cs")} +}; +static CRITICAL_SECTION nine_wndproc_cs = {&nine_wndproc_cs_debug, -1, 0, 0, 0, 0}; + +BOOL nine_dll_init(HINSTANCE hInstDLL) +{ + WNDCLASSA wc; + + /* We need our own window class for a fake window which we use to retrieve GL capabilities */ + /* We might need CS_OWNDC in the future if we notice strange things on Windows. + * Various articles/posts about OpenGL problems on Windows recommend this. */ + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = DefWindowProcA; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstDLL; + wc.hIcon = LoadIconA(NULL, (const char *)IDI_WINLOGO); + wc.hCursor = LoadCursorA(NULL, (const char *)IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = NINE_WINDOW_CLASS_NAME; + + if (!RegisterClassA(&wc)) + { + ERR("Failed to register window class '%s'!\n", NINE_WINDOW_CLASS_NAME); + return FALSE; + } + + DisableThreadLibraryCalls(hInstDLL); + + return TRUE; +} + +BOOL nine_dll_destroy(HINSTANCE hInstDLL) +{ + unsigned int i; + + for (i = 0; i < wndproc_table.count; ++i) + { + /* Trying to unregister these would be futile. These entries can only + * exist if either we skipped them in nine_unregister_window() due + * to the application replacing the wndproc after the entry was + * registered, or if the application still has an active nine + * device. In the latter case the application has bigger problems than + * these entries. */ + WARN("Leftover wndproc table entry %p.\n", &wndproc_table.entries[i]); + } + HeapFree(GetProcessHeap(), 0, wndproc_table.entries); + + UnregisterClassA(NINE_WINDOW_CLASS_NAME, hInstDLL); + + DeleteCriticalSection(&nine_wndproc_cs); + return TRUE; +} + +static void nine_wndproc_mutex_lock(void) +{ + EnterCriticalSection(&nine_wndproc_cs); +} + +static void nine_wndproc_mutex_unlock(void) +{ + LeaveCriticalSection(&nine_wndproc_cs); +} + +static struct nine_wndproc *nine_find_wndproc(HWND window) +{ + unsigned int i; + + for (i = 0; i < wndproc_table.count; ++i) + { + if (wndproc_table.entries[i].window == window) + { + return &wndproc_table.entries[i]; + } + } + + return NULL; +} + +static LRESULT CALLBACK nine_wndproc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) +{ + struct nine_wndproc *entry; + struct DRI3Present *present; + BOOL unicode; + WNDPROC proc; + + nine_wndproc_mutex_lock(); + entry = nine_find_wndproc(window); + + if (!entry) + { + nine_wndproc_mutex_unlock(); + ERR("Window %p is not registered with nine.\n", window); + return DefWindowProcW(window, message, wparam, lparam); + } + + present = entry->present; + unicode = entry->unicode; + proc = entry->proc; + nine_wndproc_mutex_unlock(); + + if (present) + return device_process_message(present, window, unicode, message, wparam, lparam, proc); + if (unicode) + return CallWindowProcW(proc, window, message, wparam, lparam); + return CallWindowProcA(proc, window, message, wparam, lparam); +} + +BOOL nine_register_window(HWND window, struct DRI3Present *present) +{ + struct nine_wndproc *entry; + + nine_wndproc_mutex_lock(); + + if (nine_find_wndproc(window)) + { + nine_wndproc_mutex_unlock(); + WARN("Window %p is already registered with nine.\n", window); + return TRUE; + } + + if (wndproc_table.size == wndproc_table.count) + { + unsigned int new_size = max(1, wndproc_table.size * 2); + struct nine_wndproc *new_entries; + + if (!wndproc_table.entries) new_entries = HeapAlloc(GetProcessHeap(), 0, new_size * sizeof(*new_entries)); + else new_entries = HeapReAlloc(GetProcessHeap(), 0, wndproc_table.entries, new_size * sizeof(*new_entries)); + + if (!new_entries) + { + nine_wndproc_mutex_unlock(); + ERR("Failed to grow table.\n"); + return FALSE; + } + + wndproc_table.entries = new_entries; + wndproc_table.size = new_size; + } + + entry = &wndproc_table.entries[wndproc_table.count++]; + entry->window = window; + entry->unicode = IsWindowUnicode(window); + /* Set a window proc that matches the window. Some applications (e.g. NoX) + * replace the window proc after we've set ours, and expect to be able to + * call the previous one (ours) directly, without using CallWindowProc(). */ + if (entry->unicode) + entry->proc = (WNDPROC)SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)nine_wndproc); + else + entry->proc = (WNDPROC)SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)nine_wndproc); + entry->present = present; + + nine_wndproc_mutex_unlock(); + + return TRUE; +} + +BOOL nine_unregister_window(HWND window) +{ + struct nine_wndproc *entry, *last; + LONG_PTR proc; + + nine_wndproc_mutex_lock(); + + if (!(entry = nine_find_wndproc(window))) + { + nine_wndproc_mutex_unlock(); + return FALSE; + } + + if (entry->unicode) + { + proc = GetWindowLongPtrW(window, GWLP_WNDPROC); + if (proc != (LONG_PTR)nine_wndproc) + { + entry->present = NULL; + nine_wndproc_mutex_unlock(); + WARN("Not unregistering window %p, window proc %#lx doesn't match nine window proc %p.\n", + window, proc, nine_wndproc); + return FALSE; + } + + SetWindowLongPtrW(window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + else + { + proc = GetWindowLongPtrA(window, GWLP_WNDPROC); + if (proc != (LONG_PTR)nine_wndproc) + { + entry->present = NULL; + nine_wndproc_mutex_unlock(); + WARN("Not unregistering window %p, window proc %#lx doesn't match nine window proc %p.\n", + window, proc, nine_wndproc); + return FALSE; + } + + SetWindowLongPtrA(window, GWLP_WNDPROC, (LONG_PTR)entry->proc); + } + + last = &wndproc_table.entries[--wndproc_table.count]; + if (entry != last) *entry = *last; + + nine_wndproc_mutex_unlock(); + return TRUE; +} diff --git a/dlls/d3d9-nine/wndproc.h b/dlls/d3d9-nine/wndproc.h new file mode 100644 index 0000000000..15f26d491a --- /dev/null +++ b/dlls/d3d9-nine/wndproc.h @@ -0,0 +1,41 @@ +/* + * Direct3D wine internal interface main + * + * Copyright 2002-2003 The wine-d3d team + * Copyright 2002-2003 Raphael Junqueira + * Copyright 2004 Jason Edmeades + * Copyright 2007-2008 Stefan Dösinger for CodeWeavers + * Copyright 2009 Henri Verbeet for CodeWeavers + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_NINE_WNDPROC_H +#define __WINE_NINE_WNDPROC_H + +struct DRI3Present; + +BOOL nine_register_window(HWND window, struct DRI3Present *present); +BOOL nine_unregister_window(HWND window); + +BOOL nine_dll_init(HINSTANCE hInstDLL); +BOOL nine_dll_destroy(HINSTANCE hInstDLL); + +LRESULT device_process_message(struct DRI3Present *present, HWND window, BOOL unicode, + UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc); + +#define NINE_WINDOW_CLASS_NAME "Gallium_Nine_Window" + +#endif -- 2.14.1