diff --git a/Makefile b/Makefile
index 32c13af..248de82 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ FILES = src/debug.c \
src/clipper.c \
src/render.c \
src/render_soft.c \
+ src/render_d3d9.c \
src/render_dummy.c \
src/screenshot.c \
src/opengl.c
diff --git a/cnc-ddraw.vcxproj b/cnc-ddraw.vcxproj
index 585771f..580c416 100644
--- a/cnc-ddraw.vcxproj
+++ b/cnc-ddraw.vcxproj
@@ -18,6 +18,7 @@
+
diff --git a/cnc-ddraw.vcxproj.filters b/cnc-ddraw.vcxproj.filters
index a26ea52..0fda0cd 100644
--- a/cnc-ddraw.vcxproj.filters
+++ b/cnc-ddraw.vcxproj.filters
@@ -48,6 +48,9 @@
Source Files
+
+ Source Files
+
diff --git a/src/main.c b/src/main.c
index 9837ffa..727a1b9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,6 +43,7 @@ IDirectDrawImpl *ddraw = NULL;
DWORD WINAPI render_main(void);
DWORD WINAPI render_soft_main(void);
DWORD WINAPI render_dummy_main(void);
+DWORD WINAPI render_d3d9_main(void);
int WindowPosX;
int WindowPosY;
@@ -1204,7 +1205,7 @@ HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnk
}
GetPrivateProfileStringA("ddraw", "renderer", "auto", tmp, sizeof(tmp), SettingsIniPath);
- if(tolower(tmp[0]) == 'd' || tolower(tmp[0]) == 'd')
+ if(tolower(tmp[0]) == 'd' && tolower(tmp[1]) == 'u')
{
printf("DirectDrawCreate: Using dummy renderer\n");
This->renderer = render_dummy_main;
@@ -1220,6 +1221,11 @@ HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnk
This->renderer = render_main;
This->autorenderer = TRUE;
}
+ else if (tolower(tmp[0]) == 'd')
+ {
+ printf("DirectDrawCreate: Using Direct3D 9 renderer\n");
+ This->renderer = render_d3d9_main;
+ }
else
{
printf("DirectDrawCreate: Using OpenGL renderer\n");
diff --git a/src/render_d3d9.c b/src/render_d3d9.c
new file mode 100644
index 0000000..fde6e05
--- /dev/null
+++ b/src/render_d3d9.c
@@ -0,0 +1,271 @@
+#include
+#include
+#include
+#include "main.h"
+#include "surface.h"
+
+// TO DO:
+// Try to get fullscreen exclusive working
+// Fix maintain aspect ratio / boxing
+// Use different scaling filter (seems to use blurry linear by default)
+// optional vsync
+
+const BYTE PalettePixelShaderSrc[] =
+{
+ 0,2,255,255,254,255,42,0,67,84,65,66,28,0,0,0,115,0,0,0,0,2,255,255,
+ 2,0,0,0,28,0,0,0,0,1,0,0,108,0,0,0,68,0,0,0,3,0,0,0,
+ 1,0,2,0,72,0,0,0,0,0,0,0,88,0,0,0,3,0,1,0,1,0,6,0,
+ 92,0,0,0,0,0,0,0,115,48,0,171,4,0,12,0,1,0,1,0,1,0,0,0,
+ 0,0,0,0,115,49,0,171,4,0,12,0,1,0,1,0,1,0,0,0,0,0,0,0,
+ 112,115,95,50,95,48,0,77,105,99,114,111,115,111,102,116,32,40,82,41,32,72,76,83,
+ 76,32,83,104,97,100,101,114,32,67,111,109,112,105,108,101,114,32,57,46,50,57,46,57,
+ 53,50,46,51,49,49,49,0,81,0,0,5,0,0,15,160,0,0,127,63,0,0,0,59,
+ 0,0,0,0,0,0,0,0,31,0,0,2,0,0,0,128,0,0,3,176,31,0,0,2,
+ 0,0,0,144,0,8,15,160,31,0,0,2,0,0,0,144,1,8,15,160,66,0,0,3,
+ 0,0,15,128,0,0,228,176,0,8,228,160,4,0,0,4,0,0,1,128,0,0,0,128,
+ 0,0,0,160,0,0,85,160,1,0,0,2,0,0,2,128,0,0,170,160,66,0,0,3,
+ 0,0,15,128,0,0,228,128,1,8,228,160,1,0,0,2,0,8,15,128,0,0,228,128,255,255,0,0
+};
+
+LPDIRECT3D9 D3d;
+LPDIRECT3DDEVICE9 D3ddev;
+LPDIRECT3DVERTEXBUFFER9 D3dvb;
+IDirect3DTexture9 *SurfaceTex;
+IDirect3DTexture9 *PaletteTex;
+IDirect3DPixelShader9 *PixelShader;
+D3DPRESENT_PARAMETERS D3dpp;
+
+static void InitDirect3D(BOOL reset)
+{
+ if (reset)
+ {
+ D3dvb->lpVtbl->Release(D3dvb);
+ SurfaceTex->lpVtbl->Release(SurfaceTex);
+ PaletteTex->lpVtbl->Release(PaletteTex);
+ PixelShader->lpVtbl->Release(PixelShader);
+
+ if (FAILED(D3ddev->lpVtbl->Reset(D3ddev, &D3dpp)))
+ return;
+ }
+
+ int width = ddraw->width;
+ int height = ddraw->height;
+
+ int surfaceTexWidth =
+ width <= 1024 ? 1024 : width <= 2048 ? 2048 : width <= 4096 ? 4096 : width;
+
+ int surfaceTexHeight =
+ height <= 512 ? 512 : height <= 1024 ? 1024 : height <= 2048 ? 2048 : height <= 4096 ? 4096 : height;
+
+ float scaleW = (float)width / surfaceTexWidth;;
+ float scaleH = (float)height / surfaceTexHeight;
+
+ typedef struct CUSTOMVERTEX { float x, y, z, rhw, u, v; } CUSTOMVERTEX;
+ CUSTOMVERTEX vertices[] =
+ {
+ { -1.0f, height, 0.0f, 1.0f, 0.0f, scaleH },
+ { -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f },
+ { width, height, 0.0f, 1.0f, scaleW, scaleH },
+ { width, -1.0f, 0.0f, 1.0f, scaleW, 0.0f }
+ };
+
+ D3ddev->lpVtbl->SetFVF(D3ddev, D3DFVF_XYZRHW | D3DFVF_TEX1);
+ D3ddev->lpVtbl->CreateVertexBuffer(
+ D3ddev, sizeof(vertices), 0, D3DFVF_XYZRHW | D3DFVF_TEX1, D3DPOOL_MANAGED, &D3dvb, NULL);
+
+ void *data;
+ if (SUCCEEDED(D3dvb->lpVtbl->Lock(D3dvb, 0, 0, (void**)&data, 0)))
+ {
+ memcpy(data, vertices, sizeof(vertices));
+ D3dvb->lpVtbl->Unlock(D3dvb);
+ }
+
+ D3ddev->lpVtbl->SetStreamSource(D3ddev, 0, D3dvb, 0, sizeof(CUSTOMVERTEX));
+
+ D3ddev->lpVtbl->CreateTexture(
+ D3ddev, surfaceTexWidth, surfaceTexHeight, 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, &SurfaceTex, 0);
+ D3ddev->lpVtbl->SetTexture(D3ddev, 0, (IDirect3DBaseTexture9 *)SurfaceTex);
+
+ D3ddev->lpVtbl->CreateTexture(D3ddev, 256, 256, 1, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &PaletteTex, 0);
+ D3ddev->lpVtbl->SetTexture(D3ddev, 1, (IDirect3DBaseTexture9 *)PaletteTex);
+
+ D3ddev->lpVtbl->CreatePixelShader(D3ddev, (DWORD *)PalettePixelShaderSrc, &PixelShader);
+ D3ddev->lpVtbl->SetPixelShader(D3ddev, PixelShader);
+}
+
+DWORD WINAPI render_d3d9_main(void)
+{
+ Sleep(500);
+
+ HMODULE hD3D9 = LoadLibrary("d3d9.dll");
+ if (hD3D9)
+ {
+ IDirect3D9 *(WINAPI *D3DCreate9)(UINT) =
+ (IDirect3D9 *(WINAPI *)(UINT))GetProcAddress(hD3D9, "Direct3DCreate9");
+
+ if (D3DCreate9 && (D3d = D3DCreate9(D3D_SDK_VERSION)))
+ {
+ D3dpp.Windowed = TRUE;
+ D3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ D3dpp.hDeviceWindow = ddraw->hWnd;
+ D3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ D3dpp.BackBufferWidth = ddraw->width;
+ D3dpp.BackBufferHeight = ddraw->height;
+ D3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
+ D3dpp.BackBufferCount = 1;
+
+ D3d->lpVtbl->CreateDevice(
+ D3d,
+ D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ ddraw->hWnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+ &D3dpp,
+ &D3ddev);
+
+ InitDirect3D(FALSE);
+ }
+ }
+
+ DWORD tick_start = 0;
+ DWORD tick_end = 0;
+ DWORD frame_len = 0;
+
+ int maxfps = ddraw->render.maxfps;
+
+ if (maxfps < 0)
+ maxfps = ddraw->mode.dmDisplayFrequency;
+
+ if (maxfps == 0)
+ maxfps = 125;
+
+ if (maxfps >= 1000)
+ maxfps = 0;
+
+ if (maxfps > 0)
+ frame_len = 1000.0f / maxfps;
+
+ while (D3d && ddraw->render.run && WaitForSingleObject(ddraw->render.sem, INFINITE) != WAIT_FAILED)
+ {
+#if _DEBUG
+ static DWORD tick_fps = 0;
+ static DWORD frame_count = 0;
+ static char debugText[512] = { 0 };
+ static double frameTime = 0;
+ RECT debugrc = { 0, 0, ddraw->width, ddraw->height };
+
+ if (ddraw->primary && ddraw->primary->palette)
+ DrawText(ddraw->primary->hDC, debugText, -1, &debugrc, DT_NOCLIP);
+
+ tick_start = timeGetTime();
+ if (tick_start >= tick_fps)
+ {
+ snprintf(
+ debugText,
+ sizeof(debugText),
+ "FPS: %lu | Time: %2.2f ms ",
+ frame_count,
+ frameTime);
+
+ frame_count = 0;
+ tick_fps = tick_start + 1000;
+
+ CounterStart();
+ }
+ frame_count++;
+#endif
+
+ if (maxfps > 0)
+ tick_start = timeGetTime();
+
+ EnterCriticalSection(&ddraw->cs);
+
+ if (ddraw->primary && ddraw->primary->palette && ddraw->primary->palette->data_rgb)
+ {
+ D3DLOCKED_RECT lock_rc;
+
+ if (InterlockedExchange(&ddraw->render.surfaceUpdated, FALSE))
+ {
+ RECT rc = { 0,0,ddraw->width,ddraw->height };
+ if (SUCCEEDED(SurfaceTex->lpVtbl->LockRect(SurfaceTex, 0, &lock_rc, &rc, 0)))
+ {
+ unsigned char *src = (unsigned char *)ddraw->primary->surface;
+ unsigned char *dst = (unsigned char *)lock_rc.pBits;
+
+ int i;
+ for (i = 0; i < ddraw->height; i++)
+ {
+ memcpy(dst, src, ddraw->width);
+
+ src += ddraw->width;
+ dst += lock_rc.Pitch;
+ }
+
+ SurfaceTex->lpVtbl->UnlockRect(SurfaceTex, 0);
+ }
+ }
+
+ if (InterlockedExchange(&ddraw->render.paletteUpdated, FALSE))
+ {
+ RECT rc = { 0,0,256,1 };
+ if (SUCCEEDED(PaletteTex->lpVtbl->LockRect(PaletteTex, 0, &lock_rc, &rc, 0)))
+ {
+ memcpy(lock_rc.pBits, ddraw->primary->palette->data_rgb, 4 * 256);
+ PaletteTex->lpVtbl->UnlockRect(PaletteTex, 0);
+ }
+ }
+ }
+
+ LeaveCriticalSection(&ddraw->cs);
+
+ HRESULT hr = D3ddev->lpVtbl->TestCooperativeLevel(D3ddev);
+
+ if (hr == D3DERR_DEVICENOTRESET)
+ {
+ InitDirect3D(TRUE);
+ }
+ else if (SUCCEEDED(hr))
+ {
+ D3ddev->lpVtbl->BeginScene(D3ddev);
+ D3ddev->lpVtbl->DrawPrimitive(D3ddev, D3DPT_TRIANGLESTRIP, 0, 2);
+ D3ddev->lpVtbl->EndScene(D3ddev);
+
+ D3ddev->lpVtbl->Present(D3ddev, NULL, NULL, NULL, NULL);
+ }
+
+#if _DEBUG
+ if (frame_count == 1) frameTime = CounterStop();
+#endif
+
+ if (maxfps > 0)
+ {
+ tick_end = timeGetTime();
+
+ if (tick_end - tick_start < frame_len)
+ Sleep(frame_len - (tick_end - tick_start));
+ }
+ }
+
+ if (D3dvb)
+ D3dvb->lpVtbl->Release(D3dvb);
+
+ if (SurfaceTex)
+ SurfaceTex->lpVtbl->Release(SurfaceTex);
+
+ if (PaletteTex)
+ PaletteTex->lpVtbl->Release(PaletteTex);
+
+ if (PixelShader)
+ PixelShader->lpVtbl->Release(PixelShader);
+
+ if (D3ddev)
+ D3ddev->lpVtbl->Release(D3ddev);
+
+ if (D3d)
+ D3d->lpVtbl->Release(D3d);
+
+ if (hD3D9)
+ FreeLibrary(hD3D9);
+
+ return 0;
+}
diff --git a/src/surface.c b/src/surface.c
index d36b276..37e261b 100644
--- a/src/surface.c
+++ b/src/surface.c
@@ -22,7 +22,7 @@
void dump_ddscaps(DWORD dwCaps);
void dump_ddsd(DWORD dwFlags);
-DWORD WINAPI render_main(void);
+DWORD WINAPI render_soft_main(void);
HRESULT __stdcall ddraw_surface_QueryInterface(IDirectDrawSurfaceImpl *This, REFIID riid, void **obj)
{
@@ -172,7 +172,7 @@ HRESULT __stdcall ddraw_surface_Blt(IDirectDrawSurfaceImpl *This, LPRECT lpDestR
{
InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE);
ReleaseSemaphore(ddraw->render.sem, 1, NULL);
- if (ddraw->renderer != render_main)
+ if (ddraw->renderer == render_soft_main)
{
WaitForSingleObject(ddraw->render.ev, INFINITE);
ResetEvent(ddraw->render.ev);
@@ -264,7 +264,7 @@ HRESULT __stdcall ddraw_surface_Flip(IDirectDrawSurfaceImpl *This, LPDIRECTDRAWS
{
InterlockedExchange(&ddraw->render.surfaceUpdated, TRUE);
ReleaseSemaphore(ddraw->render.sem, 1, NULL);
- if (ddraw->renderer != render_main)
+ if (ddraw->renderer == render_soft_main)
{
ResetEvent(ddraw->render.ev);
WaitForSingleObject(ddraw->render.ev, INFINITE);