LoginSignup
6
5

More than 5 years have passed since last update.

D言語でD3D11してみる

Posted at

この前見た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あたりからせっせとコピペして修正していく。

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をつけてはいかんらしい

こういうの
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化

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",
}
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();
}
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を導入する必要があるんでないのか。

今回のソース

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5