クロス コンパイラ

HLSL を GLSL に変換するために使用する HLSLCC ツールに関する情報

このライブラリは High Level Shading Language (HLSL) シェーダー ソースコードを高水準の中間表現に変換し、デバイス非依存の最適化を行い、OpenGL Shading Language (GLSL) 互換のソースコードを生成します。大部分は Mesa の GLSL コンパイラが基本となっています。HLSL をパースし、HLSL Abstract Syntax Tree (AST) から Mesa IR を生成するために、フロントエンドは大部分が再記述されます。このライブラリは、Mesa の中間表現の最適化を利用してコードを単純化し、最終的に Mesa の中間表現から GLSL ソース コードを生成します。GLSL ソースコードの生成は、glsl-optimizer の最適化に基づいています。

GLSL コードの生成の他にも、設定を簡単かつ効率的な設定のためにユニフォーム変数 (グローバル変数でもあるので) を配列にパックし、高水準コードに必要なユニフォーム変数を通知するリフレクション メカニズムを提供し、ラインタイム中に高水準コードがリソースを名前ではなくインデックスでバインドできるようにマッピング情報を提供します。

UnrealBuildTool は、HLSLCC などの外部ライブラリの変更を検出しません。 HLSLCC ライブラリを再ビルドする場合、OpenGLShaders.cpp にスペースを加えて、強制的にこのモジューを再リンクしてください。

メイン ライブラリのエントリポイントは、HLSLCrossCompile です。この関数は、要求されたオプションを使ってソース HLSL からGLSL コードを生成するために必要なすべてのステップを行います。各ステージの概要は以下のとおりです。

操作

説明

Preprocessing

このコードは、C のようなプリプロセッサで実行します。このステージはオプションで、NoPreprocess フラグを使用して省略できます。アンリアルでは、MCPP を使用してコンパイル前にプリプロセッシングを行っているため、このステップをスキップします。

Parsing

HLSL ソースが抽象構文木にパースされます。これは、関数、_mesa_hlsl_parse で行われます。lexer (字句解析器) は flex によって、parser (構文解析器) は bison によってそれぞれ生成されます。詳しい情報は、パースのセクションをご覧ください。

Compilation

AST は Mesa の中間表現にコンパイルされます。このプロセスは関数 _mesa_ast_to_hir で行われます。このステージ中、コンパイラは暗黙の変換、関数のオーバーロードの解決、組み込みに対する命令の生成などの機能を実行します。GLSL のメイン エントリポイントが生成されます。GenerateGlslMain をご覧ください。このステージでは、入出力の変数に対するグローバル宣言を IR に追加し、HLSL エントリポイントの入力を計算し、HLSL エントリポイントを呼び出し、出力をグローバル出力変数に書き込みます。

Optimization

関数のインライン化、デッドコード削除、定数伝搬、共通部分式の削除などの最適化パスが中間表現 (IR) で行われます。詳細は、OptimizeIR、特に do_optimization_pass をご覧ください。

Uniform packing

ユニフォーム変数 (グローバル変数でもあります) が保持されているマッピング情報と共に配列にパックされるので、エンジンはパラメータをユニフォーム配列の関連部分にバインドすることができます。詳細は、PackUniforms をご覧ください。

Final optimization

ユニフォーム変数をパックした後、2 回目の最適化が IR で実行され、ユニフォーム変数をパックした時に生成されたコードを単純化します。

Generate GLSL

最後に、最適化された IR が GLSL ソースコードに変換されます。IR から GLSL への変換は比較的簡単です。すべての構造体、ユニフォーム バッファ、そのソース自体の定義を作るだけでなく、マッピング テーブルがファイルの一番上のコメントに書き出されます。このマッピング テーブルは、アンリアルによってパースされ、パラメータをバインドできるようにします。詳細は、GenerateGlsl、特に ir_gen_glsl_visitor クラスをご覧ください。

パース

HLSL パーサーにはレキサーとパーサーの 2 つの構成要素が組み込まれています。レキサーは、正規表現式を対応するトークンに一致させて HLSL 入力をトークンに分割します。ソース ファイルは hlsl_lexer.ll です。flex によって処理され C コードを生成します。各行は正規表現で始まり、C コードで記述された処理文が続きます。正規表現が一致すると、対応する C コードが実行されます。ステートは接頭辞 "yy" が付いたグローバル変数に格納されます。

パーサーは、言語の文法を解釈しするためにルールをトークン化した入力と一致させて ATS をビルドします。ソース ファイルは hlsl_parser.yy です。bison で処理され C コードを生成します。bison が使用する構文を十分に説明するのは、本ドキュメントの範囲外ですが、HLSL パーサーを見れば、基本的なことがわかるはずです。一般的には、反復的に評価されるトークンのシーケンスに一致するものとしてルールを定義します。ルールと一致すると、対応する C コードが実行され、ATS をビルドできるようになります。C コードブロック内のシンタックスは次のようになります。

$$ = このルールをパースした結果であり、通常は抽象構文木のノードです。 $1, $2, など = 現行ルールと一致したサブルールの出力

レキサーまたはパーサーに変更を加えたら、flex と bison を使用して C コードを再生成しなければなりません。GenerateParsers バッチファイルは、この再生成を行いますが、flex と bison がインストールされているシステムの場所に合わせてディレクトリをセットアップしなければなりません。README ファイルには、使用したバージョンに関する情報と、Windows 用にどこにバイナリをダウンロードできるかについての情報が含まれます。

コンパイル

コンパイル中は、AST はトラバースされ、IR 命令を生成するために使用されます。ここで重要な概念となるのが、IR は演算の細かいシーケンスであるということです。そのため、暗黙の変換またはそれに類するような処理は行いません。すべて明示的に処理しなければなりません。

以下は一般的な関数のいくつかです。

apply_type_conversion - ある型の値を可能であれば別の型の値に変換する関数です。暗黙の型変換と明示的な型変換は、パラメータで制御します。

arithmetic_result_type など。 - 入力値に演算を適用する Rusult Type を判断する関数のセットです。

validate_assignment -特定の型 lvalue にrvalue を代入できるかを判断します。必要があれば、認められた暗黙の型変換が適用されます。

do_assignment - 可能であれば、validate_assignment を使用して rvalue を lvalue に代入します。

ast_expression::hir - AST の表現式ノードを IR 命令一式に変換します。

process_initializer - イニシャライザ表現式を変数に適用します。

ast_struct_specifier::hir - 宣言された構造を表すために集成型をビルドします。

ast_cbuffer_declaration::hir - 定数バッファのレイアウトに合わせた構造体をビルドし、それを uniform ブロックとして格納します。

process_mul - HLSL intrinsic mul を処理するための特別なコードです。

match_function_by_name - 名前と入力パラメータのリストに基づき関数シグネチャを探索します。

rank_parameter_lists - 2 つのパラメータ リストを比較し、リストとの一致度を示す数字のランクを代入します。オーバーロードを解決する時に使うヘルパー関数です。最低ランクのシグネチャが代入されます。もし最低ランクと同等のシグネチャが存在する場合は関数呼び出しが曖昧に宣言されます。ランクがゼロの場合は完全一致を示します。

gen_texture_op - ビルトイン HLSL テクスチャとサンプラのオブジェクトのメソッドの呼び出しを処理します。

_mesa_glsl_initialize_functions - HLSL イントリンシック関数に対して組み込み関数を生成します。sin、cos を始めとするほとんどの関数は IR コードを生成して演算を行いますが、ranspose、determinant などの一部の関数はドライバーの GLSL コンパイラの演算を保留して関数呼び出し内に残ります。

コンパイラを拡張する

以下は機能の型を実装するヒントの一例です。

新しい表現式

  • ir_expression_operation 列挙型変数にエントリを追加します。

  • ir_expression コンストラクタで、新規表現式を処理し、入力オペランドの型に基づき表現式の型付けした結果をセットアップします。

  • 可能であれば、コンパイル中に定数式の評価ができるように ir_expression::constant_expression_value にハンドラを追加します。

  • 表現式が正しいことを検証するために ir_validate::visit_leave(ir_expression *ir) にハンドラを追加します。

  • GLSLExpressionTable にエントリを追加し、表現式を GLSL 表現式にマッピングします。

  • 表現式のトークンを認識するようにレキサーを修正します (該当する場合)。

  • トークンを認識し、適切な ast_expression ノードを作るようにパーサーを修正します (該当する場合)。

組み込み関数 (Intrinsics)

  • 組み込み関数の定義を _mesa_glsl_initialize_functions に追加します。

  • ほとんどの場合、組み込み関数 (Intrinsics) は単一の表現式に直接マッピングします。その場合、単に新しい ir_expression を追加し、make_intrinsic_genType を使用して組み込み関数 (Intrinsics) を生成します。

  • IR 内で型を表現するために Glsl_type を追加します。これを _mesa_glsl_initialize_types に追加するか、例えば glsl_type::builtin_core_types などのビルトイン型テーブルに追加します。テンプレート化された型では、例として glsl_type::get_sampler_instance をご覧ください。

  • レキサーを修正して必要なトークンを認識し、パーサーを修正してトークンを一致させます。例として、Texture2DArray をご覧ください。

  • パーサーを修正してトークンを認識し、必要な型の指定子を作成します。exture_type_specifier_nonarray は良い例です。

  • ast_type_specifier::hir を修正して、ユーザー定義の型を作成するために必要な処理を行います。例として構造体の処理をご覧ください。

  • ast_type_specifier::hir を修正して、ユーザー定義の型を作成するために必要な処理を行います。

  • 型にメソッドを含む場合、_mesa_ast_field_selection_to_hir を修正してメソッドを処理します。例として gen_texture_op をご覧ください。

属性、フラグ、修飾子

  • 必要とする場所で IR および / または AST のノードに属性 / フラグ / 修飾子を追加します。

  • レキサーを修正して必要なトークンを認識します。

  • パーサーを修正して必要に応じて文法ルールを追加します。たとえば、[loop] 属性のサポートを追加するつもりならば、iteration_statement のルールを修正し、その前のオプションの属性をアクセプトするようにします。以下のようなものになります。iteration_statement を base_iteration_statement に変更し、

以下のように iteration_statement を追加します。

    iteration_attr base_iteration_statement
    {
        // 結果は反復処理文です
        $$ = $2;
        // 属性を適用
        $$->attr = $1;
    }
    base_iteration_statement
    {
        //  属性がなければパススルーです
        $$ = $1;
    }

最後に属性について知る必要がある場所でコンパイラで修正します。

このページは Unreal Engine の前のバージョン用です。現在リリースされている Unreal Engine 5.3 に対して更新は行われていません。
Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
キャンセル