26
DirectX9 OpenGL の隠蔽実装例 TECO

Direct xとopenglの隠蔽実装例

Embed Size (px)

Citation preview

DirectX9とOpenGLの隠蔽実装例

TECO

まず始めに

 プレゼンターが早口になっていたら

 指摘してあげてください。

アジェンダ

設計方針 隠蔽方法 3D 描画で必要な機能 DirectX9 と OpenGL の違い Q & A

設計方針 (1) 優先順位を明確にする  ( 実行速度 ) >   ( 移植性 ) (≒ 拡張性 ) (≒ 可読性 ) >   ( コンパイル速度 ) → 移植性を上げるために、アプリケーションからは基本的に依存

部分は触らなくて済むこと。 →  拡張性を上げるために、無駄なリンクはされないようにする。 → 可読性が下がりそうだったら、 cpp ファイルを依存別に分ける

ことも検討

設計方針 (2)

依存部分にアクセスできること → ミドルウェアに依存部分のデータを渡す必要があることがある

。 → ライブラリ側に実装されてない機能を、アプリケーション側で

テストしやすい環境を作る。

クラスの隠蔽方法 (1) 継承 #if 〜 #endif 動的リンク pImpl イディオム Typedef pImpl イディオムと Typedef の思想融合型

クラスの隠蔽方法 (2) 継承 実行速度: ×  コンパイル速度:◯ 可読性:◯ 依存部分アクセス性: ×  動的切替:◯ コーディングしやすさ:◯ #if 〜 #endif 実行速度:◯ コンパイル速度:◯ 可読性: × or or △ ◯ 依存部分アクセス性:書き方次第 動的切替: × コーディングしやすさ:◯ 動的リンク 実行速度:? コンパイル速度:◯ 可読性:◯ 依存部分アクセス性: ×  動的切替: コーディングしやすさ:△  その他: Ogre3D で採用

クラスの隠蔽方法 (3) pImpl イディオム 概要:依存部分と共通部分の型を分けて、共通部分の型が依存部分の型のポ

インタを持つ。 依存種類の数だけ cpp ファイルを用意する。 (cpp 側でRenderDeviceDepend を定義する )

  class RenderDevice {  public:    RenderDevice();

void Draw();  private:    RenderDeviceDepend* m_Depend;  } 実行速度:△(キャッシュミスの恐れ)コンパイル速度:◯  可読性:◯ 依存部分アクセス性: ×  動的切替: × コーディングしやすさ:◯ その他: boost のスマートポインタ参照

クラスの隠蔽方法 (4) Typedef 概要:型名と関数名は統一して、自由に型を定義して typedef する。  class RenderDeviceDX9 { ・・・ }  class RenderDeviceGL{ ・・・ }  typedef RenderDeviceGL RenderDevice;

 実行速度:◯ コンパイル速度:△ 可読性:◯ 依存部分アクセス性:◯ 動的切替: × コーディングしやすさ: ×

 おしい。 これだけでは依存部分で同じコードを各部分が発生する。  

クラスの隠蔽方法 (5) pImpl イディオムと Typedef の思想融合型 概要:依存部分と共通部分の型を分けて、共通部分の型が依存部分の型を実体

を持つ。 依存種類の数だけ cpp ファイルを用意する。 RenderDeviceDependは typedef で切り替える。

  #include <render_device_depend.h> // この中で RenderDeviceDepend を typedef する。

  class RenderDevice {  public:

void Draw();  private:    RenderDeviceDepend m_Depend;  } 実行速度:◯ コンパイル速度:△ 可読性:◯ 依存部分アクセス性:◯ 動的切替: × コーディングしやすさ:◯

クラスの隠蔽方法 (6)

【結論】  pImpl イディオムと Typedef の            思想融合型採用

【余談】 グローバル関数の場合は、可読性が落ちないなら #if~#endif 、可読

性が落ちそうだったら機種依存別に cpp を用意する。

3D描画で必要な機能 デバイス ビュー 頂点バッファ 頂点フォーマット 座標変換行列 ラスタライズ設定 テクスチャ テクスチャサンプラ シェーダ

DX9とGLの隠蔽方針

• 基本的には DirectX 風のオブジェクト指向。

• 依存部分には依存クラスを介してアクセス可能。各クラスは依存コードを集約させたDepend クラスの実体をメンバとして持つ。

DX9とGLの違い (デバイス )定義:リソース ( 頂点バッファ、テクスチャなど)管理やレンダ

リング用設定管理、コマンドバッファの発行を行う。   注 ) ウィンドウに 1:1 の関係ではない。【 DX9 】・オブジェクト指向・生成方法が一つ。【 OpenGL 】・非オブジェクト指向 → カレントのデバイスコンテキストを切り替える。・生成方法がプラットフォーム毎に異なる。

隠蔽方針: Deviceクラスを作る。 OpenGL版ではカレントのデバイスコンテキスト切り替えは自動で行う。 DirectX版のみ、 Dependクラスにデバイスロスト用コードを仕込む。

DX9とGLの違い (ビュー )定義:ウィンドウに対して 1対 1 の関係にあるもの。絵を描く紙のよ

うな概念(バックバッファを持つ)。 1 つのデバイスが複数のビューを持てる。(マルチウィンドウで描画する時に考える必要がある)

【 DX9 】 ・ SwapChain を利用。【 GL 】 ・ 1 つのデバイスで複数のビューを作る場合は新しいデバイスコン

テキストを作り、親となるデバイスのリソースや設定を共有できるようにする (Windows だと wglSharedList 関数を利用 )

隠蔽方針: Deviceクラスから Viewクラスとして複数生成できるようにする。 Viewから RenderTarget情報を取得・設定することで描画を切り替える。

DX9とGLの違い (頂点バッファ /フォーマット)固定シェーダ編【 DX9 】 ・頂点バッファと頂点フォーマットの設定が別々   ( SetStreamSource 、 SetVertexDeclaration )

【 GL 】 ・頂点バッファと頂点フォーマットの設定が同時   ( glVertexPointer 、 glColorPointer ・・・ )

隠蔽方針: VertexBuffer、 VertexFormatクラス、 SetVertexBuffer関数、 SetVertexFormat関数を定義。 OpenGL版は、 Draw関数を呼ばれた時に初めて gl Pointer◯◯ 関数で頂点バッファとフォーマットを設定する(それまで組み合わせが確定できないので)

DX9とGLの違い (デカルト座標系 1)

【 DX9 】 ヘルパー関数 (D3DXMatrixPerspectiveFovLH など )

を使うと右手座標系の行列、左手座標系の行列を選べる。(最終的な z/w=0.0f 〜 1.0f )

【 GL 】 ヘルパー関数 (gluPerspective 、 gluLookAt) を使うと

、右手座標系(最終的な z/w=-1.0f 〜 1.0f)

隠蔽方針: 3DAPI依存のヘルパー関数は一切使わず、自前で左手、右手座標系用関数を用意する。 DX9と GLでは座標系が同じでも行列は違うのと、座標系が違うと表面と裏面が逆になるのを注意。

DX9とGLの違い (デカルト座標系 2)

ビュー変換行列 (vec eye,vec at, vec up)

DX9とGLの違い (デカルト座標系 3) 透視射影行列 (f32 fovy , f32 aspect , f32 znear , f32 zfar )

DX9とGLの違い (テクスチャ )

【 DX9 】     【 GL 】

隠蔽方針: Textureクラスを作って隠蔽。 DirectX版のみ Dependクラスは LostResourceクラスを継承して、デバイスロスト時にリセットが必要なリソースとして管理する (頂点バッファも同様 )。座標系が違うのは、テクスチャ座標変換行列で対応する。

Dx9とGLの違い (テクスチャサンプラ )定義:テクスチャからどのように画素情報を取得する

か。フィルター設定など【 DX9 】  SetSamplerState 関数を利用。テクスチャステージ毎に反映。【 GL 】  glTexParameter 関数を利用。テクスチャ毎に反映。

隠蔽方針: SamplerState クラスを作って、 Device にテクスチャステージ毎に設定できるようにした。 GL 版では設定するたびに、テクスチャステージのテクスチャに設定し直す。

DX9とGLの違い(ラスタライザ・ブレンド設定 )

定義:カリング設定、ブレンド設定など【 DX9 】  SetRenderState 関数で全て設定可能。【 GL 】 それぞれにグローバル関数が用意されている。

隠蔽方針: Deviceクラスにそれぞれの設定関数を持たせる。( DX11が要素別に設定するようになったので)

DX9とGLの違い(シェーダ:全般 )

【 HLSL 】 ・エフェクトファイルがある。 ・頂点、ピクセルシェーダ毎に設定可能。

(SetVertexShader 、 SetPixelShader)

【 GLSL 】 ・エフェクトファイルが無い。 ・頂点、ピクセルシェーダを一つのプログラムにバインドして利用

( glGenProgram 、 glBindProgram 、 glLinkProgram 、 glUseProgram)

隠蔽方針:エフェクトファイル処理は GL版では関数はあるが、何も処理はしない。組み合わせの切り替えは容易にできるようにしたいので、 DirectX風に作る。頂点シェーダ毎にglGenProgramを呼ぶ(理由は次のページで)

DX9とGLの違い    (シェーダ:頂点フォーマット )【 HLSL 】 ・どの入力変数がどの要素(位置、カラー)かはセマンティ

ックスによって管理されている (POSITION0 、 COLOR0等 )

【 GLSL 】 (attribute 変数 ) ・どの入力変数がどの要素かは CPU 側から変数名に対応した番号で指定する (glGetAttribLocation 、 glBindAttribLocation 、glVertexAttribPointer) 。 glBindProgramで頂点シェーダを切り替えると glBindAttribLocationの設定はリセットされる(頂点シェーダ毎に glGenProgramを発行している理由)

隠蔽手法: DirectXの頂点フォーマットと同じ考え。GL版では各セマンティクスに対応した変数名を用意しておく(名前はカスタマイズ可能に)

DX9とGLの違い (シェーダ:Uniform変数 )定義: CPU 側から設定できて、シェーダ側からでは変更

できないグローバル変数。【 DX9 】 ・定数レジスタ番号から値を設定 ・シェーダの組み合わせが変更されても有効【 GL 】 ・変数名から取得できるハンドルから値を設定 ・シェーダの組み合わせが変更されると無効

隠蔽方針: DX9版は定数レジスタ番号、 GL版は変数名(指定の命名規則)に紐づいた共通 enumを使いハンドルを取得して、値を設定できるように。

Q&A

何かありましたら、以下のメールアドレスへ  [email protected]