日別アーカイブ: 2009年8月20日

OpenGLES2.0 Direct3D とのフォーマット変換

Direct3D10 以降、Pixel フォーマットと頂点の各 Element のフォーマット形式は
統一されています。DXGI_FORMAT はまた Direct3D からも独立しており、
バージョンに依存せず同じものが使えるようになりました。
つまり Direct3D10 と Direct3D11 のフォーマットは同一です。

●Direct3D9

・ピクセル、インデックス用フォーマット D3DFORMAT
     D3DFMT_A8R8G8B8
     D3DFMT_R5G6B5
     D3DFMT_A16B16G16R16F
     D3DFMT_INDEX16    (IndexBuffer 用)
     等

・頂点フォーマット D3DDECLTYPE
     D3DDECLTYPE_FLOAT3
     D3DDECLTYPE_UBYTE4
     他


●Direct3D10/Direct3D11

・ピクセル、頂点、インデックス兼用  DXGI_FORMAT
     DXGI_FORMAT_R8G8B8A8_UNORM
     DXGI_FORMAT_B5G6R5_UNORM
     DXGI_FORMAT_R32G32B32_FLOAT  (従来の D3DDECLTYPE_FLOAT3 も兼ねる)
     DXGI_FORMAT_R16_UINT         (従来の D3DFMT_INDEX16 も兼ねる)
     ~

Direct3D11 ではライブラリ独自の頂点形式から ID3D11InputLayout を作るために
下記の関数を用意していました。
これをそのまま OpenGLES.20 用に置き換えたのが、先日の GLInputLayout の関数です。

// DXInputLayout.cpp
static struct _FormatType {
    DXGI_FORMAT   dxgi_format;
} formatTable[]= {

{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_UNKNOWN,			},

{	DXGI_FORMAT_R32_FLOAT,			},
{	DXGI_FORMAT_R32G32_FLOAT,		},
{	DXGI_FORMAT_R32G32B32_FLOAT,		},
{	DXGI_FORMAT_R32G32B32A32_FLOAT,		},

{	DXGI_FORMAT_R16_FLOAT,			},
{	DXGI_FORMAT_R16G16_FLOAT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_FLOAT,		},

{	DXGI_FORMAT_R32_SINT,			},
{	DXGI_FORMAT_R32G32_SINT,		},
{	DXGI_FORMAT_R32G32B32_SINT,		},
{	DXGI_FORMAT_R32G32B32A32_SINT,		},

{	DXGI_FORMAT_R16_SINT,			},
{	DXGI_FORMAT_R16G16_SINT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_SINT,		},

{	DXGI_FORMAT_R8_SINT,			},
{	DXGI_FORMAT_R8G8_SINT,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_SINT,		},

{	DXGI_FORMAT_R32_UINT,			},
{	DXGI_FORMAT_R32G32_UINT,		},
{	DXGI_FORMAT_R32G32B32_UINT,		},
{	DXGI_FORMAT_R32G32B32A32_UINT,		},

{	DXGI_FORMAT_R16_UINT,			},
{	DXGI_FORMAT_R16G16_UINT,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_UINT,		},

{	DXGI_FORMAT_R8_UINT,			},
{	DXGI_FORMAT_R8G8_UINT,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_UINT,		},

{	DXGI_FORMAT_R16_SNORM,			},
{	DXGI_FORMAT_R16G16_SNORM,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_SNORM,		},

{	DXGI_FORMAT_R8_SNORM,			},
{	DXGI_FORMAT_R8G8_SNORM,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_SNORM,		},

{	DXGI_FORMAT_R16_UNORM,			},
{	DXGI_FORMAT_R16G16_UNORM,		},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R16G16B16A16_UNORM,		},

{	DXGI_FORMAT_R8_UNORM,			},
{	DXGI_FORMAT_R8G8_UNORM,			},
{	DXGI_FORMAT_UNKNOWN,			},
{	DXGI_FORMAT_R8G8B8A8_UNORM,		},
};


DXGI_FORMAT _flFASTCALL
PixelToFormat( unsigned int func )
{
    assert( func < Image::FUNC_END );
    return  formatTable[ func ].dxgi_format;
}

void _flFASTCALL
SetElementDesc(
	D3D11_INPUT_ELEMENT_DESC* desc,
	unsigned int desccount,
	const a5::InputLayout& layout
	)
{
    unsigned int    ecount= layout.GetElementCount();
    assert( ecount < desccount );
    for( unsigned int ei= 0 ; ei< ecount ; ei++, desc++ ){
        const a5::InputElement*  ep= layout.GetElement( ei );
        desc->SemanticName= ep->SemanticName;
        desc->SemanticIndex= ep->SemanticIndex;
        desc->Format= PixelToFormat( ep->pixf.Func );
        desc->InputSlot= 0;
        desc->AlignedByteOffset= ep->ByteOffset;
        desc->InputSlotClass= D3D11_INPUT_PER_VERTEX_DATA;
        desc->InstanceDataStepRate= 0;
    }
}

このように、独自の頂点形式をテーブルで DXGI_FORMAT に変換しています。
DXGI そのままでないのが幸いして、OpenGLES2.0 への変換も比較的うまく
いっています。OpenGLES2.0 で用意したのが下記のコードです。
このテーブルはそのまま上の DXGI_FORMAT に対応しています。

static struct _FormatType {
    GLenum    dxgi_format;
    unsigned int   component;
    unsigned int   normalize;
} formatTable[]= {

{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },
{    0,                  0,    FALSE,    },

{    GL_FLOAT,           1,    FALSE,	},
{    GL_FLOAT,           2,    FALSE,	},
{    GL_FLOAT,           3,    FALSE,	},
{    GL_FLOAT,           4,    FALSE,	},

{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float
{    0,                  0,    FALSE,	},  // half float

{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32
{    0,                  0,    FALSE,	},  // signed int 32

{    GL_SHORT,	         1,    FALSE,	},  // signed int 16
{    GL_SHORT,	         2,    FALSE,	},  // signed int 16
{    GL_SHORT,	         3,    FALSE,	},  // signed int 16
{    GL_SHORT,	         4,    FALSE,	},  // signed int 16

{    GL_BYTE,	         1,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         2,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         3,    FALSE,	},  // signed byte 8
{    GL_BYTE,	         4,    FALSE,	},  // signed byte 8

{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32
{    0,		         0,    FALSE,	},  // unsigned int 32

{    GL_UNSIGNED_SHORT,  1,    FALSE,	},
{    GL_UNSIGNED_SHORT,  2,    FALSE,	},
{    GL_UNSIGNED_SHORT,  3,    FALSE,	},
{    GL_UNSIGNED_SHORT,  4,    FALSE,	},

{    GL_UNSIGNED_BYTE,   1,    FALSE,	},
{    GL_UNSIGNED_BYTE,   2,    FALSE,	},
{    GL_UNSIGNED_BYTE,   3,    FALSE,	},
{    GL_UNSIGNED_BYTE,   4,    FALSE,	},

{    GL_SHORT,           1,    TRUE,	},
{    GL_SHORT,           2,    TRUE,	},
{    GL_SHORT,           3,    TRUE,	},
{    GL_SHORT,           4,    TRUE,	},

{    GL_BYTE,            1,    TRUE,	},
{    GL_BYTE,            2,    TRUE,	},
{    GL_BYTE,            3,    TRUE,	},
{    GL_BYTE,            4,    TRUE,	},

{    GL_UNSIGNED_SHORT,  1,    TRUE,	},
{    GL_UNSIGNED_SHORT,  2,    TRUE,	},
{    GL_UNSIGNED_SHORT,  3,    TRUE,	},
{    GL_UNSIGNED_SHORT,  4,    TRUE,	},

{    GL_UNSIGNED_BYTE,   1,    TRUE,	},
{    GL_UNSIGNED_BYTE,   2,    TRUE,	},
{    GL_UNSIGNED_BYTE,   3,    TRUE,	},
{    GL_UNSIGNED_BYTE,   4,    TRUE,	},
};


GLenum _flFASTCALL
PixelToFormat( unsigned int func, unsigned int* component, unsigned int* normal )
{
    assert( func < Image::FUNC_END );
    const _FormatType*   fp= &formatTable[ func ];
    if( component ){
        *component= fp->component;
    }
    if( normal ){
        *normal= fp->normalize;
    }
    return  fp->dxgi_format;
}

前回 GLInputLayout の CreateInputLayout() の中で呼び出していた
PixelToFormat() がこれです。
まだ FLOAT しか使っていないので本当に互換性があるかどうかは検証してません。
BYTE の並びは D3D9 の D3DDECLTYPE_UBYTE4 と D3DDECLTYPE_D3DCOLOR
のように順番が異なっている可能性があります。

同じく前回「ELEMENTFLAG_NORMALIZE の定義値が 1 なのは念のため。」と書いた
のはコンパイラの最適化で三項演算子が完全に消えることを期待していたからです。

(ep->Flag & ELEMENTFLAG_NORMALIZE) ? GL_TRUE : GL_FALSE

GL_TRUE / GL_FALSE の定義は 1 と 0 なので、最終的には
「ep->Flag & ELEMENTFLAG_NORMALIZE」だけが残ります。
予想通り分岐は消えますが、シンボルが 1 以外でも shr が一つ追加されるだけでした。

; ELEMENTFLAG_NORMALIZE == 4 の場合 (x64 + VC)
000000013F4D6697  shr         ebx,2 
000000013F4D66A1  and         ebx,1 
000000013F4D66A4  mov         edx,ebx 

シフトを命令に埋め込める ARM だったら全く意識する必要ないのかもしれません。

関連エントリ
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理