D言語でD3D11してみる

この前見た[COMのインターフェース](http://qiita.com/mono_shoo/items/e4a45b7ed12114c04b51)を参考にD言語でD3D11してみる。
[DirectX(D3D11.1)再入門 Input-Assembler](http://qiita.com/ousttrue/items/6e6f09e5ad0c38c4efed)のD言語への移植でございます。

# CreateWindow

```d
module d3d_on_dlang;

import core.runtime;
import core.sys.windows.windows;
//pragma(lib, "gdi32.lib");
import std.string;


extern (Windows)
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    int result;

    try
    {
        Runtime.initialize();

        result = myWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

        Runtime.terminate();
    }
    catch (Throwable o)		// catch any uncaught exceptions
    {
        MessageBoxA(null, cast(char *)o.toString(), "Error", MB_OK | MB_ICONEXCLAMATION);
        result = 0;		// failed
    }

    return result;
}

int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    wstring appName = "HelloWin";

    WNDCLASSW wndclass;
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = &WndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIconW(cast(void*)null, cast(wchar*)IDI_APPLICATION);
    wndclass.hCursor       = LoadCursorW(cast(void*)null, cast(wchar*)IDC_ARROW);
    wndclass.hbrBackground = null; //cast(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName  = null;
    wndclass.lpszClassName = appName.ptr;

    if(!RegisterClassW(&wndclass))
    {
        MessageBoxW(null, "This program requires Windows NT!", appName.ptr, MB_ICONERROR);
        return 0;
    }

    auto hwnd = CreateWindowW(appName.ptr,      // window class name
						"The Hello Program",  // window caption
						WS_OVERLAPPEDWINDOW,  // window style
						CW_USEDEFAULT,        // initial x position
						CW_USEDEFAULT,        // initial y position
						CW_USEDEFAULT,        // initial x size
						CW_USEDEFAULT,        // initial y size
						null,                 // parent window handle
						null,                 // window menu handle
						hInstance,            // program instance handle
						null);                // creation parameters

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG  msg;
    while (GetMessageW(&msg, null, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return cast(int)msg.wParam;
}

extern(Windows)
LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)nothrow
{
    switch (message)
    {
        case WM_CREATE:
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
    }

    return DefWindowProcW(hwnd, message, wParam, lParam);
}
```

特に問題ないがcore.sys.windows.windowsはわりとエクスポートされていない関数があるね。

# d3d11
comのインタフェースをD言語に翻訳していく。

http://lunesu.com/uploads/ModernCOMProgramminginD.pdf
この辺とかにもざっと目を通した。
が、なんかidlからdモジュールに変換する謎ツールを使ってる。
よくわからなかったがinterfaceのstaticメンバーにIIDを保持させるアイデアは頂いた。

他にもcのヘッダからdモジュールに変換するhtodというのがあるが、comで多用される謎マクロやtypedefに勝てない感じだったので手作りすることにした。d3d11.h, d3dcommon.h, dxgi.hあたりからせっせとコピペして修正していく。

```d:d3d11周りのinterfaceを移植していく…
module derelict.windows.kits_8_1.d3d11;
import core.sys.windows.com;

// enumとかstructを書いてゆく
enum D3D11_USAGE
{
	DEFAULT	= 0,
	IMMUTABLE	= 1,
	DYNAMIC	= 2,
	STAGING	= 3
}

// typedefはalias

extern (Windows)
{

interface ID3D11Device: IUnknown
{
    static const IID =
		UUID("db6f6ddb-ac77-4e88-8253-819df9bbf140").GUID;

    HRESULT CreateBuffer( 
            in D3D11_BUFFER_DESC *pDesc,
            in D3D11_SUBRESOURCE_DATA *pInitialData,
            ID3D11Buffer *ppBuffer);

    // メソッドが続く・・・

}
```

## classの引数にoutをつけてはいかんらしい
```d:こういうの
out ID3D11Device *pDevice
```

コンパイルが通りません。

## UUID to GUID
std.uuid.UUIDからcore.sys.windows.com.GUIDへの変換。
エンディアンのせいでUUID.data.ptrを直接使えないのだ。

```diff
GUID toGUID(immutable UUID uuid)
{
	ubyte[8] data=uuid.data[8..$];
	return GUID(
				uuid.data[0] << 24
				|uuid.data[1] << 16
				|uuid.data[2] << 8
				|uuid.data[3],

				uuid.data[4] << 8
				|uuid.data[5],

				uuid.data[6] << 8
				|uuid.data[7],

				data
				);
}
```

## d3d11.libとのリンクでハマる
"Program Files"のようなスペースの入ったパスをエスケープする方法が分からないという事態に遭遇してしまう。あと"C:"とかドライブ名もあかん。2時間くらいがんばったが断念。おのれ・・・

何か良い情報はないかと検索したら[D言語 + DirectXのサンプルの焼き直し](http://www.ku6.jp/report/45.html)というまさにやろうとしていることそのままの記事を発見。よく見るとlibとのリンクはしないでLoadLibraryしていた。なるほど、Dのリンカ周りはハマりポイントなのだからリンクしなければいいじゃないということか。ということでd3d11.dllのエクスポートをDerelict化することにする。d3dcompiler_47.dllも作った。

## Derelict化

```js:dub.json
{
	"targetName": "DerelictD3D11",
    "workingDirectory": "lib",
    "version": "~master",
	"description": "A dynamic binding to the WindowsKits8.1 library.",
	"copyright": "Copyright © 2015, ousttrue",
	"authors": ["ousttrue"],
	"dependencies": {
        "derelict-util": ">=1.9.1",
	},
    "importPaths": [
        "source/",
    ],
    "license": "Boost",
    "configurations":[
        {
            "name": "library",
            "targetType": "library",
        },
    ],
    "targetPath": "lib",
     "sourcePaths": [
        "source/"
    ],
    "name": "derelict-d3d11",
}
```

```d:Derelict部分
//////////////////////////////////////////////////////////////////////////////
// Loader for d3d11.dll
//////////////////////////////////////////////////////////////////////////////
private {
    import derelict.util.loader;
    import derelict.util.system;

    static if( Derelict_OS_Windows )
        enum libNames = "d3d11.dll";
    else
        static assert( 0, "for Windows only." );
}


// signature
extern (Windows) nothrow @nogc
{
    alias da_D3D11CreateDeviceAndSwapChain =HRESULT function(
            in IDXGIAdapter pAdapter,
            D3D_DRIVER_TYPE DriverType,
            HMODULE Software,
            UINT Flags,
            in D3D_FEATURE_LEVEL* pFeatureLevels,
            UINT FeatureLevels,
            UINT SDKVersion,
            in DXGI_SWAP_CHAIN_DESC* pSwapChainDesc,
            /*  */ IDXGISwapChain* ppSwapChain,
            /*  */ ID3D11Device* ppDevice,
            /*  */ D3D_FEATURE_LEVEL* pFeatureLevel,
            /*  */ ID3D11DeviceContext* ppImmediateContext 
            );
}


// function pointer
__gshared {
    da_D3D11CreateDeviceAndSwapChain D3D11CreateDeviceAndSwapChain;
}


class DerelictD3D11Loader : SharedLibLoader {
    public this() {
        super( libNames );
    }

    protected override void loadSymbols() {

        bindFunc(cast(void**)&D3D11CreateDeviceAndSwapChain, "D3D11CreateDeviceAndSwapChain");

    }
}
__gshared DerelictD3D11Loader D3D11;
shared static this() {
    D3D11 = new DerelictD3D11Loader();
}
```

```js:dub.json(ローカルディレクトリへの依存)
{
	"name": "d3d_on_dlang",
	"description": "A minimal D application.",
	"copyright": "Copyright © 2015, ousttrue",
	"authors": ["ousttrue"],
    "targetPath": "bin",
	"dependencies": {
        "derelict-d3d11": {
            "version": "~master",
            "path": "../DerelictD3D"
        }
	},
}
```

# 以上
今回の作業でcomのD言語モジュールの書き方が分かった。作法さえ覚えればC#のDllImportをがんばるより簡単であるしマーシャリングが無いのはよい。これでDirectShowとかKinectのモジュールも書けるし、来るべきD3D12もD言語でできるんでないかと画策。
未確認のポイントとして、IUnknown.Releaseは明示的に呼ぶ必要があるのかということだが多分必要。
となるとsmartポインタ的なstructを導入する必要があるんでないのか。

# 今回のソース
* <https://github.com/ousttrue/DerelictD3D.git>
* <https://github.com/ousttrue/d3d_on_dlang>