この前見たCOMのインターフェースを参考にD言語でD3D11してみる。
DirectX(D3D11.1)再入門 Input-AssemblerのD言語への移植でございます。
CreateWindow
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あたりからせっせとコピペして修正していく。
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をつけてはいかんらしい
out ID3D11Device *pDevice
コンパイルが通りません。
UUID to GUID
std.uuid.UUIDからcore.sys.windows.com.GUIDへの変換。
エンディアンのせいでUUID.data.ptrを直接使えないのだ。
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のサンプルの焼き直しというまさにやろうとしていることそのままの記事を発見。よく見るとlibとのリンクはしないでLoadLibraryしていた。なるほど、Dのリンカ周りはハマりポイントなのだからリンクしなければいいじゃないということか。ということでd3d11.dllのエクスポートをDerelict化することにする。d3dcompiler_47.dllも作った。
Derelict化
{
"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",
}
//////////////////////////////////////////////////////////////////////////////
// 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();
}
{
"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を導入する必要があるんでないのか。