From: Ruslan Kabatsayev Subject: Add a simple 3D APIs demo Message-Id: <57b768f3.9a0f190a.24841.6ca9@mx.google.com> Date: Fri, 19 Aug 2016 22:41:48 +0300 This adds a 3d-demo wine application. It will allow users to check that their system can handle at least basic WGL, D3D8 and D3D9 before they need to look deeper into their problem. Signed-off-by: Ruslan Kabatsayev --- configure | 26 +- configure.ac | 1 + programs/3d-demo/Makefile.in | 14 + programs/3d-demo/common.c | 107 ++++++ programs/3d-demo/common.h | 36 ++ programs/3d-demo/d3d.c | 605 ++++++++++++++++++++++++++++++++ programs/3d-demo/d3d8.c | 2 + programs/3d-demo/d3d9.c | 2 + programs/3d-demo/demo.rc | 61 ++++ programs/3d-demo/main.c | 175 +++++++++ programs/3d-demo/resource.h | 33 ++ programs/3d-demo/wgl.c | 473 +++++++++++++++++++++++++ programs/3d-demo/winehq_logo_glass.png | Bin 0 -> 31512 bytes 13 files changed, 1517 insertions(+), 18 deletions(-) create mode 100644 programs/3d-demo/Makefile.in create mode 100644 programs/3d-demo/common.c create mode 100644 programs/3d-demo/common.h create mode 100644 programs/3d-demo/d3d.c create mode 100644 programs/3d-demo/d3d8.c create mode 100644 programs/3d-demo/d3d9.c create mode 100644 programs/3d-demo/demo.rc create mode 100644 programs/3d-demo/main.c create mode 100644 programs/3d-demo/resource.h create mode 100644 programs/3d-demo/wgl.c create mode 100644 programs/3d-demo/winehq_logo_glass.png diff --git a/configure b/configure index 3eba2d2..efae53e 100755 --- a/configure +++ b/configure @@ -787,7 +787,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -1451,6 +1450,7 @@ enable_libs_wine enable_libs_wpp enable_loader enable_po +enable_3d_demo enable_arp enable_aspnet_regiis enable_attrib @@ -1628,7 +1628,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1881,15 +1880,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -2027,7 +2017,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -2180,7 +2170,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -6253,7 +6242,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6299,7 +6288,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6323,7 +6312,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6368,7 +6357,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6392,7 +6381,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -18229,6 +18218,7 @@ wine_fn_config_makefile libs/wine enable_libs_wine clean,implib,install-dev,inst wine_fn_config_makefile libs/wpp enable_libs_wpp wine_fn_config_makefile loader enable_loader clean,install-lib wine_fn_config_makefile po enable_po clean +wine_fn_config_program 3d-demo enable_3d_demo clean,install,installbin wine_fn_config_program arp enable_arp install wine_fn_config_program aspnet_regiis enable_aspnet_regiis install wine_fn_config_program attrib enable_attrib clean,install diff --git a/configure.ac b/configure.ac index 24e5190..9c1ab55 100644 --- a/configure.ac +++ b/configure.ac @@ -3459,6 +3459,7 @@ WINE_CONFIG_MAKEFILE([libs/wine],,[clean,implib,install-dev,install-lib]) WINE_CONFIG_MAKEFILE([libs/wpp]) WINE_CONFIG_MAKEFILE([loader],,[clean,install-lib]) WINE_CONFIG_MAKEFILE([po],,[clean]) +WINE_CONFIG_PROGRAM(3d-demo,,[clean,install,installbin]) WINE_CONFIG_PROGRAM(arp,,[install]) WINE_CONFIG_PROGRAM(aspnet_regiis,,[install]) WINE_CONFIG_PROGRAM(attrib,,[clean,install]) diff --git a/programs/3d-demo/Makefile.in b/programs/3d-demo/Makefile.in new file mode 100644 index 0000000..4ca5b6c --- /dev/null +++ b/programs/3d-demo/Makefile.in @@ -0,0 +1,14 @@ +MODULE = 3d-demo.exe +APPMODE = -mwindows -municode -mno-cygwin +IMPORTS = user32 gdi32 gdiplus opengl32 glu32 d3d8 d3d9 d3dx9 ole32 shell32 msvcrt + +C_SRCS = \ + d3d8.c \ + d3d9.c \ + wgl.c \ + main.c \ + common.c + +RC_SRCS = demo.rc + +INSTALL_LIB = 3d-demo.exe 3d-demo diff --git a/programs/3d-demo/common.c b/programs/3d-demo/common.c new file mode 100644 index 0000000..0551785 --- /dev/null +++ b/programs/3d-demo/common.c @@ -0,0 +1,107 @@ +/* + * Direct3D 8/9 demo + * + * Copyright 2016 Ruslan Kabatsayev + * + * 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 COBJMACROS +#include +#include +#include +#include +#include +#include "common.h" +#include "resource.h" + +void fatalError(HINSTANCE hInst, int statusId) +{ + WCHAR message[STRING_MAX]; + WCHAR caption[STRING_MAX]; + /* Make sure the strings don't extend indefinitely if we fail to load them */ + message[0]=0; + caption[0]=0; + LoadStringW(hInst,statusId,message,sizeof message/sizeof message[0]); + LoadStringW(hInst,STRING_FATAL_ERROR,caption,sizeof caption/sizeof caption[0]); + MessageBoxW(NULL, message, caption, MB_OK|MB_ICONSTOP); + ExitProcess(statusId); +} + + +int readTexture(HINSTANCE hInst, void** bgraBits, UINT* w, UINT* h) +{ + static const int pixelSize=4; + GpBitmap* bmp; + BitmapData data; + GpRect imgRect; + ULONG_PTR gdiplusToken; + int width, height; + DWORD dataSize; + HRSRC texRes; + HGLOBAL hTexRes, hTexData; + LPSTREAM stream; + LPVOID texResData; + GpStatus status; + + struct GdiplusStartupInput gsi={1,NULL,FALSE,FALSE}; + GdiplusStartup(&gdiplusToken,&gsi,NULL); + + if(!(texRes=FindResourceW(hInst,(LPCWSTR)MAKEINTRESOURCE(IDR_TEXTURE),(LPCWSTR)RT_RCDATA))) + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + if(!(hTexRes=LoadResource(hInst,texRes))) + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + dataSize=SizeofResource(hInst,texRes); + if(!(hTexData=GlobalAlloc(GMEM_MOVEABLE,dataSize))) + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + if(!(texResData=GlobalLock(hTexData))) + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + memcpy(texResData,LockResource(hTexRes),dataSize); + GlobalUnlock(hTexData); + if(FAILED(CreateStreamOnHGlobal(hTexData,TRUE,&stream))) + { + GlobalFree(hTexData); + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + } + status=GdipCreateBitmapFromStream(stream,&bmp); + IStream_Release(stream); + if(status!=Ok) + return DEMO_STATUS_ERR_TEXTURE_FILE_READ; + if(GdipGetImageWidth ((GpImage*)bmp,w) || + GdipGetImageHeight((GpImage*)bmp,h)) + return DEMO_STATUS_ERR_TEXTURE_IMAGE_SIZE_GETTING; + width=*w; + height=*h; + *bgraBits=malloc(width*height*pixelSize); + + data.Width=width; + data.Height=height; + data.Stride=data.Width*pixelSize; + data.PixelFormat=PixelFormat32bppARGB; + data.Scan0=*bgraBits; + imgRect.Width=data.Width; + imgRect.Height=data.Height; + imgRect.X=0; + imgRect.Y=0; + if(GdipBitmapLockBits(bmp,&imgRect, + ImageLockModeRead|ImageLockModeUserInputBuf, + PixelFormat32bppARGB,&data)) + return DEMO_STATUS_ERR_TEXTURE_DATA_READING; + + GdipDisposeImage((GpImage*)bmp); + GdiplusShutdown(gdiplusToken); + + return DEMO_STATUS_SUCCESS; +} diff --git a/programs/3d-demo/common.h b/programs/3d-demo/common.h new file mode 100644 index 0000000..54479d7 --- /dev/null +++ b/programs/3d-demo/common.h @@ -0,0 +1,36 @@ +/* + * 3D APIs demo + * + * Copyright 2016 Ruslan Kabatsayev + * + * 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 _3D_DEMO_COMMON_H +#define _3D_DEMO_COMMON_H + +#include +int readTexture(HINSTANCE hInst, void** bgraBits, UINT* width, UINT* height); +void fatalError(HINSTANCE hInst, int statusId); + +extern BOOL vsync; +extern BOOL setVsyncMode; +extern BOOL fullscreen; +extern DWORD fullscreenWidth, fullscreenHeight, fullscreenFreq, fullscreenBits; +static const int bgRed=85, bgGreen=136, bgBlue=238; +extern UINT_PTR exitTimerId; +#define MATH_PI 3.141592653589793238 + +#endif diff --git a/programs/3d-demo/d3d.c b/programs/3d-demo/d3d.c new file mode 100644 index 0000000..92ee4c3 --- /dev/null +++ b/programs/3d-demo/d3d.c @@ -0,0 +1,605 @@ +/* + * 3D APIs demo + * + * Copyright 2016 Ruslan Kabatsayev + * + * 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 "common.h" +#include +#include +#include +#include "resource.h" + +#if D3D_VER==8 +# include +# define APPEND_VERSION(x) x##8 + typedef BYTE VertexBufferData; + /* Fixup renamed methods and constants */ +# define IDirect3DDevice8_SetSamplerState IDirect3DDevice8_SetTextureStageState +# define IDirect3DDevice8_SetFVF IDirect3DDevice8_SetVertexShader +# define D3DSAMP_MINFILTER D3DTSS_MINFILTER +# define D3DSAMP_MAGFILTER D3DTSS_MAGFILTER +# define PresentationInterval FullScreen_PresentationInterval +#elif D3D_VER==9 +# include +# define APPEND_VERSION(x) x##9 + typedef void VertexBufferData; +#endif + +#define EVALUATE_AND_ATTACH(x,y,z,w) x##y##z##w +#define ATTACH(x,y,z,w) EVALUATE_AND_ATTACH(x,y,z,w) + +#define Direct3DCreate(x) APPEND_VERSION(Direct3DCreate)(x) +typedef APPEND_VERSION(LPDIRECT3D) LPDIRECT3D; +#define ID3D_FUNC(func) ATTACH(IDirect3D,D3D_VER,_,func) +typedef APPEND_VERSION(LPDIRECT3DDEVICE) LPDIRECT3DDEVICE; +#define ID3D_DEV_FUNC(func) ATTACH(IDirect3DDevice,D3D_VER,_,func) +typedef APPEND_VERSION(LPDIRECT3DVERTEXBUFFER) LPDIRECT3DVERTEXBUFFER; +#define ID3D_VERT_BUF_FUNC(func) ATTACH(IDirect3DVertexBuffer,D3D_VER,_,func) +typedef APPEND_VERSION(LPDIRECT3DINDEXBUFFER) LPDIRECT3DINDEXBUFFER; +#define ID3D_IND_BUF_FUNC(func) ATTACH(IDirect3DIndexBuffer,D3D_VER,_,func) +typedef APPEND_VERSION(LPDIRECT3DTEXTURE) LPDIRECT3DTEXTURE; +#define ID3D_TEX_FUNC(func) ATTACH(IDirect3DTexture,D3D_VER,_,func) +typedef APPEND_VERSION(LPDIRECT3DBASETEXTURE) LPDIRECT3DBASETEXTURE; +typedef APPEND_VERSION(D3DMATERIAL) D3DMATERIAL; +typedef APPEND_VERSION(D3DLIGHT) D3DLIGHT; + +#if D3D_VER==9 +#define ID3DDEV_CreateTexture(d,w,h,l,u,f,p,t) ID3D_DEV_FUNC(CreateTexture)(d,w,h,l,u,f,p,t,NULL) +#define ID3DDEV_CreateVertexBuffer(d,l,u,f,p,b) ID3D_DEV_FUNC(CreateVertexBuffer)(d,l,u,f,p,b,NULL) +#define ID3DDEV_CreateIndexBuffer(d,l,u,f,p,b) ID3D_DEV_FUNC(CreateIndexBuffer)(d,l,u,f,p,b,NULL) +#define ID3DDEV_SetStreamSource(d,n,sd,s) ID3D_DEV_FUNC(SetStreamSource)(d,n,sd,0,s) +#define ID3DDEV_DrawIndexedPrimitive(d,t,m,n,s,p) ID3D_DEV_FUNC(DrawIndexedPrimitive)(d,t,0,m,n,s,p) +#define ID3DDEV_SetIndices(d,b) ID3D_DEV_FUNC(SetIndices)(d,b) +#define ID3D_GetAdapterModeCount(d,a,f) ID3D_FUNC(GetAdapterModeCount)(d,a,f) +#define ID3D_EnumAdapterModes(d,a,f,m,dm) ID3D_FUNC(EnumAdapterModes)(d,a,f,m,dm) +#elif D3D_VER==8 +#define ID3DDEV_CreateTexture(d,w,h,l,u,f,p,t) ID3D_DEV_FUNC(CreateTexture)(d,w,h,l,u,f,p,t) +#define ID3DDEV_CreateVertexBuffer(d,l,u,f,p,b) ID3D_DEV_FUNC(CreateVertexBuffer)(d,l,u,f,p,b) +#define ID3DDEV_CreateIndexBuffer(d,l,u,f,p,b) ID3D_DEV_FUNC(CreateIndexBuffer)(d,l,u,f,p,b) +#define ID3DDEV_SetStreamSource(d,n,sd,s) ID3D_DEV_FUNC(SetStreamSource)(d,n,sd,s) +#define ID3DDEV_DrawIndexedPrimitive(d,t,m,n,s,p) ID3D_DEV_FUNC(DrawIndexedPrimitive)(d,t,m,n,s,p) +#define ID3DDEV_SetIndices(d,b) ID3D_DEV_FUNC(SetIndices)(d,b,0) +#define ID3D_GetAdapterModeCount(d,a,f) ID3D_FUNC(GetAdapterModeCount)(d,a) +#define ID3D_EnumAdapterModes(d,a,f,m,dm) ID3D_FUNC(EnumAdapterModes)(d,a,m,dm) +#endif + +// Common D3D 8/9 declarations of D3DX matrix functions. Actual library will be d3dx9.dll. +#define D3DX_PI MATH_PI +typedef D3DVECTOR D3DXVECTOR3; +typedef D3DMATRIX D3DXMATRIX; +D3DXMATRIX* WINAPI D3DXMatrixRotationX(D3DXMATRIX *pout, FLOAT angle); +D3DXMATRIX* WINAPI D3DXMatrixRotationY(D3DXMATRIX *pout, FLOAT angle); +D3DXMATRIX* WINAPI D3DXMatrixMultiply(D3DXMATRIX *pout, const D3DXMATRIX *pm1, const D3DXMATRIX *pm2); +D3DXMATRIX* WINAPI D3DXMatrixLookAtRH(D3DXMATRIX *pout, const D3DXVECTOR3 *peye, const D3DXVECTOR3 *pat, const D3DXVECTOR3 *pup); +D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovRH(D3DXMATRIX *pout, FLOAT fovy, FLOAT aspect, FLOAT zn, FLOAT zf); + + +static LPDIRECT3D d3d = NULL; +static LPDIRECT3DDEVICE device = NULL; +static LPDIRECT3DVERTEXBUFFER vertexBuffer = NULL; +static LPDIRECT3DINDEXBUFFER indexBuffer = NULL; +static LPDIRECT3DTEXTURE texture1 = NULL; +static D3DPRESENT_PARAMETERS presentParms; +static LONG windowWidth=300, windowHeight=300; +static HINSTANCE hInstance; + +/* all allowed formats */ +static const struct +{ + D3DFORMAT format; + const char* name; +} displayFormats[]= +{ + {D3DFMT_A1R5G5B5,"16-bit (1:5:5:5)"}, +#if D3D_VER==9 + {D3DFMT_A2R10G10B10,"32-bit (2:10:10:10)"}, +#endif + {D3DFMT_A8R8G8B8,"32-bit (8:8:8:8)"}, + {D3DFMT_R5G6B5,"16-bit (5:6:5)"}, + {D3DFMT_X1R5G5B5,"15-bit (5:5:5)"}, + {D3DFMT_X8R8G8B8,"24-bit (8:8:8)"}, +}; +static const UINT displayFormatCount=sizeof displayFormats/sizeof displayFormats[0]; + +static D3DDISPLAYMODE getDisplayMode(UINT index) +{ + const int adapter=D3DADAPTER_DEFAULT; + D3DDISPLAYMODE dm={0}; + UINT fmt; + UINT currentIndex=0; +#if D3D_VER==9 + for(fmt=0;fmtperiod) + { + printf("%d frames in %.1f seconds = %.3f FPS\n",frameCount,elapsed,frameCount/elapsed); + fflush(stdout); + oldTime=newTime; + frameCount=0; + } + } +} + +static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch(msg) + { + case WM_DESTROY: + deleteDirect3D(); + PostQuitMessage(0); + return 0; + case WM_KEYDOWN: + if(wparam==VK_ESCAPE) + PostQuitMessage(0); + return 0; + case WM_SIZE: + { + windowWidth=LOWORD(lparam); + windowHeight=HIWORD(lparam); + if(windowWidth==0 || windowHeight==0) + break; + + presentParms.BackBufferWidth=windowWidth; + presentParms.BackBufferHeight=windowHeight; + + if(FAILED(ID3D_DEV_FUNC(Reset)(device,&presentParms))) + fatalError(hInstance,DEMO_STATUS_ERR_RESET_DEVICE); + initScene(); + return 0; + } + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + drawIteration(); + EndPaint(hwnd, &ps); + return 0; + } + } + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +int APPEND_VERSION(run_d3d)(HINSTANCE hInst, int cmdShow) +{ + static const WCHAR caption[]={'W','i','n','e','D','3','D','0'+D3D_VER,' ','D','e','m','o',0}; + static const WCHAR className[]={'W','i','n','e','-','D','e','m','o',0}; + MSG msg={0}; + HWND hwnd; + WNDCLASSEXW windowClass; + hInstance=hInst; + windowClass.cbSize = sizeof windowClass; + windowClass.style = 0; + windowClass.lpfnWndProc = wndProc; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = 0; + windowClass.hInstance = hInst; + windowClass.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); + windowClass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + windowClass.hbrBackground = CreateSolidBrush(RGB(bgRed,bgGreen,bgBlue)); + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = className; + windowClass.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); + if(!RegisterClassExW(&windowClass)) return 1; + + if(!(hwnd = CreateWindowExW(0,windowClass.lpszClassName, + caption,WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT,CW_USEDEFAULT,windowWidth,windowHeight, + NULL,NULL,hInst,NULL))) return 2; + initD3D(hwnd); + + ShowWindow(hwnd,cmdShow); + /* In fullscreen mode we don't get WM_SIZE, so initialize the scene here */ + if(fullscreen) + initScene(); + UpdateWindow(hwnd); + + while(1) + { + if(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) + { + if(msg.message==WM_QUIT || (msg.message==WM_TIMER && msg.wParam==exitTimerId)) + break; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + else + { + // Directly draw here. InvalidateRect can slow things down up to 50%. + drawIteration(); + } + } + + return msg.wParam; +} + +int APPEND_VERSION(listModes_d3d)(HINSTANCE hInst) +{ + int mode; + if(!(d3d = Direct3DCreate(D3D_SDK_VERSION))) + fatalError(hInstance,DEMO_STATUS_ERR_D3D_INTERFACE); + + for(mode=0;;++mode) + { + const D3DDISPLAYMODE dm=getDisplayMode(mode); + UINT fmtLookup; + if(dm.Format==D3DFMT_UNKNOWN) + break; + for(fmtLookup=0;fmtLookup +#include "resource.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + STRING_FATAL_ERROR, "Fatal Error" + STRING_WARNING, "Warning" + STRING_WARN_NO_VSYNC_EXT, "Can't set vsync mode: the necessary extension is not supported" + STRING_WARN_NO_VSYNC_ENTRYPOINT, "Can't set vsync mode: function entry point not found" + STRING_WARN_VSYNC_D3D8, "Direct3D 8 doesn't allow to request vsync mode in windowed mode.\nAttempt to do this anyway (may work in Wine)?" + STRING_USAGE, "Usage: 3d-demo /api:{d3d8|d3d9|wgl} [options...]\nSupported options:\n /vsync:{on|off}\n /fullscreen[:WIDTHxHEIGHT[@HZ][:BITS]]\n /listmodes\n /timeout:SEC" + STRING_USAGE_CAPTION, "Usage" + DEMO_STATUS_SUCCESS, "Success" + DEMO_STATUS_ERR_TEXTURE_FILE_READ, "Failed to read texture file" + DEMO_STATUS_ERR_TEXTURE_IMAGE_SIZE_GETTING, "Failed to get texture dimensions" + DEMO_STATUS_ERR_TEXTURE_DATA_READING, "Failed to read texture data" + DEMO_STATUS_ERR_TEXTURE_ALLOCATION, "Failed to allocate texture" + DEMO_STATUS_ERR_TEXTURE_BINDING, "Failed to bind texture" + DEMO_STATUS_ERR_TEXTURE_UPLOADING, "Failed to upload texture" + DEMO_STATUS_ERR_PIXEL_FORMAT_SETUP, "Failed to setup pixel format" + DEMO_STATUS_ERR_GET_ADAPTER_MODE, "Failed to get adapter video mode" + DEMO_STATUS_ERR_D3D_INTERFACE, "Failed to create Direct3D interface" + DEMO_STATUS_ERR_D3D_DEVICE_INVALID_CALL, "Failed to create Direct3D device: invalid call" + DEMO_STATUS_ERR_D3D_DEVICE_NOT_AVAILABLE, "Failed to create Direct3D device: not available" + DEMO_STATUS_ERR_D3D_DEVICE_OUT_OF_VIDEO_RAM, "Failed to create Direct3D device: out of video memory" + DEMO_STATUS_ERR_D3D_DEVICE_UNKNOWN_ERROR, "Failed to create Direct3D device: unknown error" + DEMO_STATUS_ERR_TEXTURE_UNLOCK, "Failed to unlock texture bits" + DEMO_STATUS_ERR_VERTEX_BUFFER_CREATE, "Failed to create vertex buffer" + DEMO_STATUS_ERR_VERTEX_BUFFER_LOCK, "Failed to lock vertex buffer" + DEMO_STATUS_ERR_INDEX_BUFFER_CREATE, "Failed to create index buffer" + DEMO_STATUS_ERR_INDEX_BUFFER_LOCK, "Failed to lock index buffer" + DEMO_STATUS_ERR_RESET_DEVICE, "Failed to reset the device" + DEMO_STATUS_ERR_SETUP_TEXTURE, "Failed to setup texture" + DEMO_STATUS_ERR_RESOLUTION_CHANGE, "Failed to set resolution" +} + +IDR_TEXTURE RCDATA "winehq_logo_glass.png" diff --git a/programs/3d-demo/main.c b/programs/3d-demo/main.c new file mode 100644 index 0000000..6b72571 --- /dev/null +++ b/programs/3d-demo/main.c @@ -0,0 +1,175 @@ +/* + * 3D APIs demo + * + * Copyright 2016 Ruslan Kabatsayev + * + * 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 "common.h" +#include +#include "resource.h" +int run_d3d8(HINSTANCE hInst, int cmdShow); +int run_d3d9(HINSTANCE hInst, int cmdShow); +int run_wgl(HINSTANCE hInst, int cmdShow); +int listModes_d3d8(HINSTANCE hInst); +int listModes_d3d9(HINSTANCE hInst); +int listModes_wgl(HINSTANCE hInst); + +enum API +{ + API_NONE, + API_WGL, + API_D3D8, + API_D3D9, +} apiToUse=API_NONE; +BOOL listModes=FALSE; + +HINSTANCE hInstance; +BOOL setVsyncMode=FALSE; +BOOL vsync; +BOOL fullscreen=FALSE; +DWORD fullscreenWidth=0, fullscreenHeight=0, fullscreenFreq=0, fullscreenBits=0; +UINT_PTR exitTimerId; + +#include +BOOL processCommandLine(int argc, WCHAR** argv) +{ + static const WCHAR api_wgl[]={'/','a','p','i',':','w','g','l',0}; + static const WCHAR api_d3d8[]={'/','a','p','i',':','d','3','d','8',0}; + static const WCHAR api_d3d9[]={'/','a','p','i',':','d','3','d','9',0}; + static const WCHAR vsync_on[]={'/','v','s','y','n','c',':','o','n',0}; + static const WCHAR vsync_off[]={'/','v','s','y','n','c',':','o','f','f',0}; + static const WCHAR fullscreen_on[]={'/','f','u','l','l','s','c','r','e','e','n',0}; + static const WCHAR listmodes[]={'/','l','i','s','t','m','o','d','e','s',0}; + static const WCHAR fullscreen_modePrefix[]={'/','f','u','l','l','s','c','r','e','e','n',':',0}; + static const WCHAR timeout_secPrefix[]={'/','t','i','m','e','o','u','t',':',0}; + int i=0; + for(;i6) + return FALSE; + if(fieldCount>3) + { + if(op1=='@') + fullscreenFreq=a; + else if(op1==':') + fullscreenBits=a; + else + return FALSE; + } + if(fieldCount>5) + { + if(op2==op1) + return FALSE; + if(op2=='@') + fullscreenFreq=b; + else if(op2==':') + fullscreenBits=b; + else + return FALSE; + } + fullscreen=TRUE; + } + else if(!wcscmp(option,listmodes)) + listModes=TRUE; + else + return FALSE; + } + return TRUE; +} + +int usage(void) +{ + WCHAR message[STRING_MAX]; + WCHAR caption[STRING_MAX]; + message[0]=0; + caption[0]=0; + LoadStringW(hInstance,STRING_USAGE,message,sizeof message/sizeof message[0]); + LoadStringW(hInstance,STRING_USAGE_CAPTION,caption,sizeof caption/sizeof caption[0]); + MessageBoxW(NULL,message,caption,MB_OK|MB_ICONEXCLAMATION); + return -1; +} + +int run(int argc, WCHAR** argv, int cmdShow) +{ + if(!processCommandLine(argc,argv)) + return usage(); + switch(apiToUse) + { + case API_WGL: + if(listModes) + return listModes_wgl(hInstance); + return run_wgl(hInstance,cmdShow); + case API_D3D8: + if(listModes) + return listModes_d3d8(hInstance); + return run_d3d8(hInstance,cmdShow); + case API_D3D9: + if(listModes) + return listModes_d3d9(hInstance); + return run_d3d9(hInstance,cmdShow); + default: + return usage(); + } +} + +int WINAPI wWinMain(HINSTANCE hInst,HINSTANCE hPrev,LPWSTR cmdLine,int cmdShow) +{ + int argc; + LPWSTR*const argv=CommandLineToArgvW(cmdLine,&argc); + hInstance=hInst; + return run(argc,argv,cmdShow); +} diff --git a/programs/3d-demo/resource.h b/programs/3d-demo/resource.h new file mode 100644 index 0000000..3523db2 --- /dev/null +++ b/programs/3d-demo/resource.h @@ -0,0 +1,33 @@ +#define STRING_MAX 1024 + +#define IDR_TEXTURE 10000 +#define STRING_WARN_VSYNC_D3D8 1001 +#define STRING_USAGE 1002 +#define STRING_USAGE_CAPTION 1003 +#define STRING_UNKNOWN_OPTION 1004 +#define STRING_WARN_NO_VSYNC_ENTRYPOINT 1005 +#define STRING_WARN_NO_VSYNC_EXT 1006 +#define STRING_FATAL_ERROR 1007 +#define STRING_WARNING 1008 +#define DEMO_STATUS_SUCCESS 100 +#define DEMO_STATUS_ERR_TEXTURE_FILE_READ 101 +#define DEMO_STATUS_ERR_TEXTURE_IMAGE_SIZE_GETTING 102 +#define DEMO_STATUS_ERR_TEXTURE_DATA_READING 103 +#define DEMO_STATUS_ERR_TEXTURE_ALLOCATION 104 +#define DEMO_STATUS_ERR_TEXTURE_BINDING 105 +#define DEMO_STATUS_ERR_TEXTURE_UPLOADING 106 +#define DEMO_STATUS_ERR_PIXEL_FORMAT_SETUP 107 +#define DEMO_STATUS_ERR_GET_ADAPTER_MODE 108 +#define DEMO_STATUS_ERR_D3D_INTERFACE 109 +#define DEMO_STATUS_ERR_D3D_DEVICE_INVALID_CALL 110 +#define DEMO_STATUS_ERR_D3D_DEVICE_NOT_AVAILABLE 111 +#define DEMO_STATUS_ERR_D3D_DEVICE_OUT_OF_VIDEO_RAM 112 +#define DEMO_STATUS_ERR_D3D_DEVICE_UNKNOWN_ERROR 113 +#define DEMO_STATUS_ERR_TEXTURE_UNLOCK 114 +#define DEMO_STATUS_ERR_VERTEX_BUFFER_CREATE 115 +#define DEMO_STATUS_ERR_VERTEX_BUFFER_LOCK 116 +#define DEMO_STATUS_ERR_INDEX_BUFFER_CREATE 117 +#define DEMO_STATUS_ERR_INDEX_BUFFER_LOCK 118 +#define DEMO_STATUS_ERR_RESET_DEVICE 119 +#define DEMO_STATUS_ERR_SETUP_TEXTURE 120 +#define DEMO_STATUS_ERR_RESOLUTION_CHANGE 121 diff --git a/programs/3d-demo/wgl.c b/programs/3d-demo/wgl.c new file mode 100644 index 0000000..b472e65 --- /dev/null +++ b/programs/3d-demo/wgl.c @@ -0,0 +1,473 @@ +/* + * 3D APIs demo + * + * Copyright 2016 Ruslan Kabatsayev + * + * 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 "common.h" +#include +#include +#include +#include +#include + +#include +#include /* for GL_BGRA */ + +#include "resource.h" + +static LONG windowWidth=300, windowHeight=300; +static GLuint displayList=0; +static GLuint texture1=0; +static HINSTANCE hInstance; +static HDC hdc; + +static void clearOpenGLError(void) +{ + while(glGetError()!=GL_NO_ERROR); +} + +BOOL extensionIsSupported(const char*const exts, const char*const extension) +{ + size_t extsLen; + size_t refLen; + size_t begin=0; + if(!exts) return FALSE; + extsLen=strlen(exts); + refLen=strlen(extension); + + while(beginperiod) + { + printf("%d frames in %.1f seconds = %.3f FPS\n",frameCount,elapsed,frameCount/elapsed); + fflush(stdout); + oldTime=newTime; + frameCount=0; + } +} +static LRESULT APIENTRY wndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + static HGLRC hGLRC; + switch (message) + { + case WM_CREATE: + hdc = GetDC(hwnd); + setupPixelFormat(); + hGLRC = wglCreateContext(hdc); + wglMakeCurrent(hdc, hGLRC); + initScene(); + setupVsync(); + return 0; + case WM_DESTROY: + if(hGLRC) + { + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hGLRC); + } + ReleaseDC(hwnd, hdc); + PostQuitMessage(0); + return 0; + case WM_KEYDOWN: + if(wparam==VK_ESCAPE) + PostQuitMessage(0); + return 0; + case WM_SIZE: + if(hGLRC) + { + windowWidth=LOWORD(lparam); + windowHeight=HIWORD(lparam); + glViewport(0, 0, windowWidth, windowHeight); + return 0; + } + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + drawIteration(); + EndPaint(hwnd, &ps); + return 0; + } + } + return DefWindowProcW(hwnd, message, wparam, lparam); +} + +int run_wgl(HINSTANCE hInst, int cmdShow) +{ + static const WCHAR caption[]={'W','i','n','e','W','G','L',' ','D','e','m','o',0}; + static const WCHAR className[]={'W','i','n','e','-','D','e','m','o',0}; + MSG msg={0}; + HWND hwnd; + WNDCLASSEXW windowClass; + DWORD windowStyle=WS_OVERLAPPEDWINDOW; + hInstance=hInst; + windowClass.cbSize = sizeof windowClass; + windowClass.style = 0; + windowClass.lpfnWndProc = wndProc; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = 0; + windowClass.hInstance = hInst; + windowClass.hIcon = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); + windowClass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + windowClass.hbrBackground = CreateSolidBrush(RGB(bgRed,bgGreen,bgBlue)); + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = className; + windowClass.hIconSm = LoadIconW(NULL, (LPCWSTR)IDI_APPLICATION); + if(!RegisterClassExW(&windowClass)) return 1; + + if(fullscreen) + { + DEVMODEW dm; + dm.dmSize=sizeof dm; + dm.dmFields=0; + if(fullscreenWidth) + { + dm.dmPelsWidth=fullscreenWidth; + dm.dmFields|=DM_PELSWIDTH; + windowWidth =dm.dmPelsWidth; + } + else + windowWidth =GetSystemMetrics(SM_CXSCREEN); + + if(fullscreenHeight) + { + dm.dmPelsHeight=fullscreenHeight; + dm.dmFields|=DM_PELSHEIGHT; + windowHeight=dm.dmPelsHeight; + } + else + windowHeight=GetSystemMetrics(SM_CYSCREEN); + + if(fullscreenBits) + { + dm.dmBitsPerPel=fullscreenBits; + dm.dmFields|=DM_BITSPERPEL; + } + + if(fullscreenFreq) + { + dm.dmDisplayFrequency=fullscreenFreq; + dm.dmFields|=DM_DISPLAYFREQUENCY; + } + + if(fullscreenWidth||fullscreenHeight||fullscreenBits||fullscreenFreq) + { + if(ChangeDisplaySettingsW(&dm,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) + fatalError(hInstance,DEMO_STATUS_ERR_RESOLUTION_CHANGE); + } + windowStyle = WS_POPUP; + } + if(!(hwnd = CreateWindowExW(0,windowClass.lpszClassName, + caption,windowStyle, + CW_USEDEFAULT,CW_USEDEFAULT,windowWidth,windowHeight, + NULL,NULL,hInst,NULL))) return 2; + + ShowWindow(hwnd, cmdShow); + UpdateWindow(hwnd); + + while(1) + { + if(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) + { + if(msg.message==WM_QUIT || (msg.message==WM_TIMER && msg.wParam==exitTimerId)) + break; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + else + { + // Directly draw here. InvalidateRect can slow things down up to 50%. + drawIteration(); + } + } + return msg.wParam; +} + +int listModes_wgl(HINSTANCE hInst) +{ + DEVMODEW dm; + int mode; + /* Since WGL doesn't provide anything related to modesetting, use a generic user32 function */ + for(mode=0;EnumDisplaySettingsW(NULL,mode,&dm);++mode) + printf("%ux%u@%u, %u-bit\n",dm.dmPelsWidth,dm.dmPelsHeight,dm.dmDisplayFrequency,dm.dmBitsPerPel); + return 0; +} diff --git a/programs/3d-demo/winehq_logo_glass.png b/programs/3d-demo/winehq_logo_glass.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb8ce9147be806735c27adf9ba8714f9142d74a GIT binary patch literal 31512 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ξ;zMFfpH|=5 z+%Orn4WY$YlaacjnsH`M`fm1BIoUZo(<->;eTpUu6^y1($}4kf_Vh=UWyDzf>1ia$t_xmtAVbzbqkl2X17RV8 z4qdds#}jUxcfQQT4uM?Zie(%L9pEZE=(Badvllt}a9qpKIS|Pyd@dzh9c)45auQb?i~azVGZX6H1u zBEac5Zr<8|K{i!?T^~{3%_8?NR3KvpJFKa5=LMdzrIYqYuIGwNGCK5Gf4?4d1s2t_uPPqZCoKboB*tL|KAkl?KKli|6;q9>FO9o9QZU{kP2|O_0&oFf@2FLYSe4m)mt0# z3^!0xf!%oe51@%+VpCq@uflGhlp0kh+RXjl#+o zqq-?xE44&^WOJ^>*EsSI&5Vkq#L$BF3fK33bX)s0hFTB4e-gfm9oxFl?xr)jg->56C} z%Re$?0mV1IpKZ%ec$JuMD$U+Vx;+-`1vA5z&_NpZ5tC*Ts;QIFOwFXuH4`DP*C)L5 z#fUpbXYQW&XSGO1PY(R8lebnA&pBj0NYmL>kPMj(CQ2&rCoErDx$eD*Ss*lmlMKXS z;LZs@CKC(R%inla@yMmi1PH(q&02>(#E$m1xcs+c3{rCEyo3M!hIwvIeUV(193TbTpGX6ZoaRd z_nyS~s|JdmM8k;r5y^8~Kb8h=#=)8;p#9j#oyd)HJAl|)yP4tmy_eN(bvo(%zJFc! z3KL3eaZLivEm^!#dfT);I`ER<7p>4=NH$OpVYBKcJ*rg zN|Gd1Ulf!#4ov-W+S_DZuV!nf(ygCvKnp1AO$?tDk?d-$3Mm7lKUE=bd*y_J)W>??B5D;UuF5m>@*q;Ke#TY ir0fj#uEZ>G-94>1-CMBLH+2BW-)lKF*$QdX;Qs^4KT}5l literal 0 HcmV?d00001 -- 1.7.10.2