忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

C#でDirectX11 SlimDXチュートリアルその02 デバイスの初期化

前回はこちら

今回はデバイスの初期化についてメモしておきます。

DirectXで3DCGを表示するためには、
CGを表示するための専用ハードウェアをいじる必要があります。
CPUだけでは重くてすぐには計算できないので、GPUという3DCGに特化したハードウェアを使います。
そのハードウェアのことをデバイスといいます。
DirectXアプリを作るにはまずそれを初期化しなければいけません。


デバイス

デバイスを表すクラスはSlimDX.Direct3D11.Deviceクラスです。
public class Device : ComObject
このクラスの初期化にはいくつかメソッドがあるのですが、
3DCGを表示するという目的のためにはDevice.CreateWithSwapChain()というメソッドを使います。
(最近デバイスには3DCG以外の応用が見込まれているので他にそれ用のメソッドもあるのです)

public static Result CreateWithSwapChain(
    DriverType driverType, 
    DeviceCreationFlags flags, 
    SwapChainDescription swapChainDescription, 
    out Device device, 
    out SwapChain swapChain
    );

このメソッドはデバイスを生成するファクトリメソッドです。
これはデバイスと同時に、スワップチェーンも生成します。

このスワップチェーンというのは、3DCGに特有のオブジェクトで、
「デバイスが描いた画像をウィンドウに表示する」という機能を持ちます。
スワップチェーンを使わずデバイスだけを使うというときもありますが、
それはデバイスを3DCGとは関係ない並列計算に使いたいというような時です。
今回は目的がCGなのでスワップチェーンが必要です。
詳しくは後述します。

引数名 説明
driverType 生成するデバイスの種類です。
flags 有効にするランタイムレイヤーのリストです。
swapChainDescription スワップチェーンの生成用設定です。
device 生成されたデバイスが格納されます。
swapChain 生成されたスワップチェーンが格納されます。


driverTypeはSlimDX.Direct3D11.DriverType列挙型です。
次の6つのうちから選びます。
普通はHardwareを選びます。
参考:D3D_DRIVER_TYPE

DeviceTypeの値 説明
Unknown タイプが不明
Hardware Direct3Dの機能を持つハードウェアです。まずほとんどのばあいこれを使ってDeviceクラスを初期化することになります。ハードウェアを使うためかなり速いです。というかこれ以外では遅すぎてまず通常目的では使えません。これを使いましょう。
Reference リファレンスドライバです。リファレンスドライバはソフトウェアで動いているため遅いです。遅いです―が正確で、デバッグ時やDirectXの機能をテストするというようなときには悪くはないのかもしれません。ただ、実際のアプリケーションで使う為のものではありません。
Null Nullドライバです。これはリファレンスドライバから描画機能がなくなったようなものです。これもデバッグ用で、まず普通のアプリケーションで使うためのものではありません。
Software Softwareドライバです。これは完全にソフトウェアで実装されています。やはり遅いため普通のアプリケーションで使うためのものではありません。
Warp WARPドライバです。これもソフトウェアで動きますが、パフォーマンスは高いです。


flagsはSlimDX.Direct3D11.DeviceCreationFlags列挙型です。
次の6つを組み合わせて使います:
普通はNoneを使います。
参考:D3D11_CREATE_DEVICE_FLAG
DeviceCreationFlagsの値 数値 説明
None 0 デバイスに特別な振る舞いはもたせません。
SingleThreaded 1 アプリケーションがシングルスレッドでのみDirect3D11を呼ぶときに使います。もしこのフラグを使わなければ、Direct3D11のAPIを呼んでいる間ロックします。これは複数のスレッドが内部状態を変更しないようにするためです。このフラグを使えばロックは全くされず、パフォーマンスも落ちないでしょう。しかし複数のスレッドから呼ばれたときの振る舞いは未定義となります。
Debug 2 デバイスがDebugレイヤーをサポートするようになります。
SwitchToRef 4 デバイスをソフトウェア(REF)とハードウェア(HAL)両方のバージョンで生成します。これによってアプリケーションはリファレンスデバイスへと変わりデバッグを有効にすることができます。
PreventThreadingOptimization 8 複数のスレッドが生成されないようにします。普通の用途では非推奨です。
BgraSupport 32  Direct3Dリソースとの相互運用をDirect2Dで実現するために必要です。


スワップチェーン

スワップチェーンは「デバイスが描いた画像をウィンドウに表示する」オブジェクトです。
つまり、3DCGを描くとき、デバイスを使ってポリゴンを描くのですが、その描いた内容はスワップチェーンに格納されます。
そこからその内容をウィンドウに持ってくることによってウィンドウにCGを表示できるのです。

スワップチェーンの型はSlimDX.DXGI.SwapChainクラスです。
なお、DXGIというのはDirectX Graphics Infrastructureの略です。
この名前空間にはDirect3D10やDirect3D11で共通する要素が入っています。
デバイスはD3D10やD3D11で機能が違うので別の名前空間にそれぞれ別のクラスとして存在しなければいけませんが、
スワップチェーンは大してやることが変わらないので共通なのです。

public class SwapChain : DeviceChild

生成にはすでに解説したようにDeviceクラスのCreateWithSwapChainメソッドを使います。
そのとき引数として生成するスワップチェーンの情報を与えなければなりません。
その情報はSlimDX.DXGI.SwapChainDescription構造体に格納します。

    public struct SwapChainDescription : IEquatable<SwapChainDescription>
    {
        public int BufferCount { get; set; }
        public SwapChainFlags Flags { get; set; }
        public bool IsWindowed { get; set; }
        public ModeDescription ModeDescription { get; set; }
        public IntPtr OutputHandle { get; set; }
        public SampleDescription SampleDescription { get; set; }
        public SwapEffect SwapEffect { get; set; }
        public Usage Usage { get; set; }
..
    }
それぞれのメンバの意味を以下に記します。
参考:DXGI_SWAP_CHAIN_DESC
 
メンバ名 説明
BufferCount int バッファの数です。なぜこんなモノが存在するのかというと、スワップチェーンはダブルバッファリングを行えるので、そういったときにここに2を指定することになります。これが1だとティアリングが起きて画像が変なことになったりもあるようですが、別に気にならない人は気にならないでしょう。
Flags SwapChainFlags スワップチェーンのオプションフラグです。普通は指定しなくていいでしょう。
IsWindowed bool trueだとウィンドウモード、falseだとフルスクリーンモードです。
ModeDescription ModeDescription ウィンドウの大きさやリフレッシュレート、画像のフォーマットといった情報を表す構造体です。
OutputHandle IntPtr 表示するウィンドウのハンドルです。
SampleDescription SampleDescription スワップチェーンのマルチサンプルの方法をあらわす構造体です。
SwapEffect SwapEffect 表示した後に表示バッファをどうするかを指定する列挙型です。Discard とSequentialの2つがあります。明示的に指定しなくても大丈夫です。
Usage Usage 表示する画像の使われ方を表すフラグです。ShaderInput = 16, RenderTargetOutput = 32, BackBuffer = 64, Shared = 128, ReadOnly = 256, UnorderedAccess = 1024があります。今回は描画を行う対象なので、RenderTargetOutputです。


FlagsはSlimDX.DXGI.SwapChainFlags列挙型です。
以下の3つを組み合わせて使います。
これはNoneでいいでしょう。
参考:DXGI_SWAP_CHAIN_FLAG
数値  
None 0 特別な動作はしません。
NonPrerotated 1 イメージの自動回転をオフにします。前画面モードの時にのみ有効です。
AllowModeSwitch 2 これを指定するとSwapChain.ResizeTarget()メソッドを呼び出してモードを切り替えられるようになります。


ModeDescriptionはSlimDX.DXGI.ModeDescription構造体です。
以下の6つのメンバがあります。
参考:DXGI_MODE_DESC



Width int 表示する画像の横幅です。
Height int 表示する画像の縦幅です。
RefreshRate Rational 一秒間に画像を更新する頻度です。これは60でいいでしょう。このRationalは分数を表す構造体で、Numerator(分子)とDenominator(分母)プロパティがあります。
Format Format 表示する画像バッファのフォーマットです。ひとつのピクセルが具体的にどのようなデータなのか、ということを表します。たとえばR8G8B8A8_UNormは赤8bit緑8bit青8bitアルファ8bitといった具合です。
ScanlineOrdering DisplayModeScanlineOrdering 走査線描画モードです。指定しなくても問題ありません。
Scaling DisplayModeScaling スケーリングモードです。指定しなくても問題ありません。




コード

デバイスとスワップチェーンを初期化するコードは全部でこうなります。


class Program
{
    static void Main()
    {
        using (Game game = new Game())
        {
            game.Run();
        }
    }
}

class Game : System.Windows.Forms.Form
{
    SlimDX.Direct3D11.Device device;
    SlimDX.DXGI.SwapChain swapChain;


    public void Run()
    {
        initDevice();
        SlimDX.Windows.MessagePump.Run(this, Draw);
        disposeDevice();
    }

    private void initDevice()
    {
        SlimDX.Direct3D11.Device.CreateWithSwapChain(
            SlimDX.Direct3D11.DriverType.Hardware,
            SlimDX.Direct3D11.DeviceCreationFlags.None,
            new SlimDX.DXGI.SwapChainDescription
            {
                BufferCount = 1,
                OutputHandle = this.Handle,
                IsWindowed = true,
                SampleDescription = new SlimDX.DXGI.SampleDescription
                {
                    Count = 1,
                    Quality = 0
                },
                ModeDescription = new SlimDX.DXGI.ModeDescription
                {
                    Width = ClientSize.Width,
                    Height = ClientSize.Height,
                    RefreshRate = new SlimDX.Rational(60, 1),
                    Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm
                },
                Usage = SlimDX.DXGI.Usage.RenderTargetOutput
            },
            out device,
            out swapChain
            );
    }

    private void disposeDevice()
    {
        device.Dispose();
        swapChain.Dispose();
    }

    protected virtual void Draw() { }
}
 



実行結果はこうなります。
slimDXWindow00.jpg
つまり単にウィンドウを表示している時と変わりません。
今回はただの初期化処理だからです。














拍手[3回]

PR