Skip to content

feat: Vulkanバックエンド実装 - #99

Open
EbiseLutica wants to merge 16 commits into
v2from
feature/vulkan-backend-phase1
Open

feat: Vulkanバックエンド実装#99
EbiseLutica wants to merge 16 commits into
v2from
feature/vulkan-backend-phase1

Conversation

@EbiseLutica

@EbiseLutica EbiseLutica commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

概要

OpenGL 3.3 のみだったバックエンドに Vulkan を追加する対応。コードベースと Promete.Docs をファクトチェックした上で移植計画を立案し、基盤整備から実装を進めた結果、GL バックエンドの描画機能はすべて Vulkan でも動作する状態まで到達しました。

詳細な調査結果・課題一覧・フェーズ計画は VULKAN_PORTING_PLAN.md を参照してください。

変更内容

計画書・基盤整備

  • VULKAN_PORTING_PLAN.md: GL依存 16ファイル約2,600行の分析、抽象の漏れ 9 件、技術選定(Silk.NET.Vulkan / shaderc / SPIRVリフレクション)、フェーズ計画
  • RenderCommandQueue.PushTrim が GL 固有の左下原点Y反転をバックエンド非依存層で行っていた問題を修正。トリム座標を左上原点に統一し、Y反転は GLBeginTrim/GLEndTrimCommandRunner 側へ移動(Vulkan は左上原点のためそのまま使える)
  • docs 誤記修正: rendercommand.mdCommandRunner.Execute シグネチャ

Vulkan バックエンド実装

  • VulkanContext: instance(DEBUG時 validation layers)/ surface / physical device 選択 / logical device / swapchain / レンダーパス / frames-in-flight=2 の同期・リサイズ時のスワップチェーン再構築
  • VulkanResourceManager: Texture2D.Handle (int) 互換のリソーステーブルで VkImage / ImageView / DescriptorSet を管理
  • VulkanPipelineProvider: パイプライン遅延生成+キャッシュ、標準シェーダー (Vulkan GLSL 450) を shaderc でランタイムコンパイル
  • SpirvReflector: SPIR-V バイナリから uniform ブロック・サンプラー変数を抽出する自前の最小リフレクタ(SPIRV-Cross のネイティブ依存なし)
  • VulkanShaderManager / VulkanShaderFactory / VulkanMaterialSystem: カスタムシェーダーの shaderc コンパイルと、リフレクション結果に基づく Material の Uniform 解決(uniform ブロック・Texture2D 双方)
  • VulkanRenderTextureProvider: レンダーパスの中断/再開により BeginCapture のネストセマンティクスを再現。FrameBuffer の上下反転は V反転 UV で GL と挙動を統一
  • マスク: 全オフスクリーン RT にステンシルアタッチメントを追加し、ステンシルマスク・アルファマスク双方に対応
  • VulkanImGuiController: Silk.NET 公式コントローラが OpenGL 専用のため自作。ImGui.ImageTexture2D.Handle をそのまま渡せる形で統一
  • その他: PieSprite、ポストプロセス(ピンポンバッファ)、Pie/Primitive カスタムマテリアル
  • VulkanDesktopBackend / VulkanDesktopGameView / BuildWithVulkanDesktop()
  • Promete.Example: --vulkan フラグでバックエンド選択可能に
  • Promete.Experimental.Vulkan: 実機起動・ピクセル自動検証用プロジェクト

スコープ外(計画書に明記・今後の課題)

  • 線幅 >1 (wideLines): MoltenVK 非対応のため不採用。必要になれば線分のクワッド展開で GL/Vulkan 共通実装する方針

検証

  • dotnet test Promete.Test: 147件全合格(トリム座標変更含め回帰なし)
  • 全C#プロジェクトビルド成功(Promete / Example / ImGui / MeltySynth)
  • 実機での Vulkan 起動確認済み(instance/device/swapchain初期化、クリアカラー描画、シーンライフサイクル、正常終了)
  • ピクセル単位の自動検証を20項目まで拡張し全パス(通常描画・PieSprite・カスタムマテリアル・Texture2D Uniform・ポストプロセス・FrameBuffer上下反転・ステンシル/アルファマスク)
  • ImGui: デモウィンドウのオーバーレイ描画をピクセル検証20項目全パスで確認、Example での GL/Vulkan 両モードスモークテストでクラッシュなし
  • Promete.Docs の esproj は既存の npm 依存衝突(astro peer dependency)でビルド不可 — 本PRとは無関係

備考

  • docs shader.md のカスタム頂点シェーダー例が v2 のインスタンシング頂点レイアウト(location 2〜7 / uProjection)と不整合の疑いあり。計画書のリスク 2 に記載。GL 側で仕様確定を推奨

🤖 Generated with Claude Code

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 23, 2026

Copy link
Copy Markdown

Deploying promete with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2d412a9
Status:🚫  Build failed.

View logs

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

起動確認用の Promete.Experimental.Vulkan プロジェクトを追加し、実機での動作確認を実施しました。

[MainScene] OnStart: Vulkan バックエンド起動成功
[MainScene] Window: (640, 480) (actual: (640, 480))
[MainScene] クリアカラーを DarkOrange に変更
[MainScene] 300 フレーム描画完了、終了します
[MainScene] OnDestroy
exit=0

確認できた項目:

  • Vulkan instance / device / swapchain の初期化
  • クリアカラーの描画とフレーム毎の切り替え(CornflowerBlue → DarkOrange)
  • シーンライフサイクル(OnStart / OnUpdate / OnDestroy)の駆動
  • App.Exit() による正常終了と VulkanContext.Dispose()(DeviceWaitIdle 含む)のクリーンな実行

PR本文の「Vulkan の実機起動確認は未実施」は本コメントで解消済みです。

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

Phase 2/3 実装完了: Vulkan バックエンドで実際の描画に対応

スプライト・プリミティブの描画がVulkanで動作するようになりました。ピクセル単位の自動検証(6項目)が全てパスしています。

[MainScene] OK 背景 (左上) (10,10): expected=(72,61,139) actual=(72,61,139)
[MainScene] OK 赤スプライト中心 (100,100): expected=(255,0,0) actual=(255,0,0)
[MainScene] OK ライム矩形中心 (300,150): expected=(0,255,0) actual=(0,255,0)
[MainScene] OK 青ティントスプライト (475,75): expected=(0,0,255) actual=(0,0,255)
[MainScene] OK 背景 (下部, Y軸反転検出) (100,400): expected=(72,61,139) actual=(72,61,139)
[MainScene] OK 背景 (右下) (620,460): expected=(72,61,139) actual=(72,61,139)
[MainScene] ✅ 全ピクセル検証パス

実装内容

  • VulkanResourceManager: Texture2D.Handle (int) 互換のリソーステーブルで VkImage / ImageView / DescriptorSet を管理。stagingバッファ経由アップロード
  • VulkanPipelineProvider: パイプライン遅延生成+キャッシュ。標準シェーダー (Vulkan GLSL 450) を shaderc でランタイムコンパイル(Silk.NET.Shaderc / .Native を追加)
  • VulkanRenderTextureProvider: レンダーパスの中断/再開により BeginCapture のネストセマンティクスを再現
  • VulkanContext: フレームライフサイクル、レンダーターゲットスタック、frames-in-flight 対応の per-frame 頂点アリーナ、遅延リソース破棄
  • ランナー: テクスチャバッチ(インスタンシング + push constant 射影行列)、プリミティブ(LineLoop は LineStrip で閉路化)、トリム(左上原点座標がそのまま vkCmdSetScissor に使える — Phase 0 の布石が機能)
  • スクリーンショット: vkCmdCopyImageToBuffer によるピクセル読み出し(自動検証にも使用)

回帰確認

  • dotnet test Promete.Test: 147件全合格
  • 全C#プロジェクトビルド成功

未対応(今後のフェーズ)

カスタムシェーダー / Material、マスク (stencil/alpha)、PieSprite、ポストプロセス、線幅>1、ImGui

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

FrameBuffer のテクスチャ上下反転を修正しました。

原因: FrameBuffer は GL の RT テクスチャが bottom-up 格納であることを前提に、子ノードを Scale = (1, -1) で反転補正している。Vulkan の RT は top-down のため二重反転になっていた。

修正: VulkanRenderTextureProvider が返す Texture2D に V 反転 UV (UvStart=(0,1), UvEnd=(1,0)) を持たせ、RT テクスチャのサンプリング挙動を GL と同一に統一。FrameBuffer 側の補正ロジックはバックエンド非依存のまま変更不要。

検証項目に FrameBuffer の上下向きチェック(非対称コンテンツのピクセル検証)を追加し、8 項目全パスを確認済みです。

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

カスタムシェーダー・PieSprite・ポストプロセス対応完了

ピクセル自動検証を 13 項目・2 フェーズに拡張し、全てパスしています。

--- フェーズ1: 通常描画 ---
OK 背景/赤スプライト/ライム矩形/青ティント/FrameBuffer上下/右下背景
OK PieSprite 右上 1/4 (シアン) … 0%→25% の扇形が正しい象限に描画
OK PieSprite 左下 (背景=切り抜き) … discard が機能
OK カスタムマテリアル (uOverrideColor) … UBO 経由の Uniform が反映
--- フェーズ2: ポストプロセス (色反転) ---
OK 背景 反転 / 赤→シアン / ライム→マゼンタ
✅ 全ピクセル検証パス

実装内容

  • SpirvReflector: SPIR-V バイナリから uniform ブロックのメンバー名・オフセット・set/binding を抽出する自前の最小リフレクタ。SPIRV-Cross のネイティブ依存を追加せずに Material の名前ベース Uniform を解決
  • VulkanShaderManager + VulkanShaderFactory: カスタムシェーダー (Vulkan 方言 GLSL 450) の shaderc コンパイルと ShaderProgram.Handle 互換 ID 管理
  • VulkanMaterialSystem: リフレクション結果に基づき Material の Uniform 値を per-material UBO (set=1, binding=0) へ書き込み
  • シェーダー規約: スプライト用は texture_instanced 互換の頂点レイアウト + push_constant の mat4、ポストプロセス用は頂点入力なし + set=0 に入力テクスチャ(VulkanShaderFactory の XML ドキュメントに記載)
  • PieSprite: push constant (MVP + tint + 開始/終了角) による扇形描画
  • ポストプロセス: ピンポンバッファでマテリアルチェーンを適用し最終ブリット。スクリーンショットはポストプロセス適用後の内容を読むよう変更(GL の表示内容一致セマンティクスに準拠)

回帰確認

  • dotnet test Promete.Test: 147 件全合格

残タスク

マスク系 (stencil/alpha) — オフスクリーンパスへのステンシルアタッチメント追加が必要で、次の大きめの作業。ほか: PieSprite のカスタムマテリアル、Material の Texture2D Uniform、線幅 >1、ImGui

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

マスク対応完了 — 主要機能の移植が一巡

ステンシルマスク・アルファマスクの両方式に対応しました。ピクセル自動検証は 17 項目に拡大し、全てパスしています。

OK ステンシルマスク 左半分 (表示)   … マスク白領域で子が描画される
OK ステンシルマスク 右半分 (非表示) … マスク黒領域で子が切り抜かれる
OK アルファマスク 左半分 (表示)
OK アルファマスク 右半分 (非表示)
✅ 全ピクセル検証パス (17項目)

実装内容

  • ステンシルアタッチメント: 全オフスクリーン RT に D24S8/D32S8 (フォーマット照会で選択) を追加。パスの中断/再開 (BeginCapture ネスト) をまたいでステンシル内容を保持 (stencilLoadOp=Load/Store)
  • パイプラインバリアント: GL のグローバルなステンシル状態切替に相当するものとして、全描画パイプラインに None / WriteMask (Always+Replace, カラー書き込み無効) / TestEqual (Equal ref=1) の3バリアントを導入。ランナーが StencilMaskActive に応じて選択
  • ステンシルクリア: レンダーパス内で vkCmdClearAttachments を使用 (GL の glClear(STENCIL) 相当)
  • アルファマスク: VulkanMaskedContainerHelper が子要素を専用 RenderTexture にサブレンダリング後、masked シェーダーで content × mask 合成。ディスクリプタは set0=content / set1=mask として既存のテクスチャ毎セットを流用し、新規のディスクリプタ管理なし
  • GL 版と異なり Vulkan の RT は top-down のため、サブレンダリング時の Y 反転トリックは不要

回帰確認

  • dotnet test Promete.Test: 147 件全合格 / 全ソリューションビルド成功

これで残るタスク(いずれも小物)

PieSprite のカスタムマテリアル、Material の Texture2D Uniform、線幅 >1 (wideLines)、ImGui の Vulkan 対応、docs 更新

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

GL 機能パリティ完了

GL バックエンドが持つ機能のうち Vulkan 未対応だった残り 3 点を実装しました。ピクセル自動検証は 20 項目、全てパスしています。

OK Primitive カスタムマテリアル (uFillColor) … Shape + Material
OK Texture2D Uniform (uExtraTexture)        … Material への追加テクスチャ
OK Pie カスタムマテリアル (uPieColor)        … PieSprite + Material
✅ 全ピクセル検証パス (20項目)

実装内容

  • SpirvReflector 拡張: uniform ブロックに加えてサンプラー変数(名前 / set / binding)も抽出
  • Material の Texture2D Uniform: シェーダー規約「追加テクスチャは set=2 以降に同名の sampler2D を宣言」。リフレクションで名前→セット番号を解決し、既存のテクスチャ毎ディスクリプタセットをそのままバインド(GL のテクスチャスロット自動割当に相当)
  • シェーダー毎パイプラインレイアウト: 追加テクスチャ数が可変になったため、カスタムレイアウトは (シェーダー, 種別) 毎に生成・キャッシュ。シェーダー破棄時に遅延解放
  • Pie / Primitive のカスタムマテリアル: GL 同様に Material 設定時はカスタムシェーダーで描画(Pie は push constant で MVP/tint/角度を供給、Primitive は NDC パススルー + push constant 色)

スコープ外の整理(計画書に明記)

  • 線幅 >1: Vulkan の wideLines はオプション機能で MoltenVK 非対応のため不採用。必要になったら線分のクワッド展開(CPU 側)で GL/Vulkan 共通実装する方針
  • ImGui: プラグイン側の課題として別トラック

回帰確認

dotnet test Promete.Test: 147 件全合格

これで GL バックエンドの描画機能はすべて Vulkan でも動作します

@EbiseLutica

Copy link
Copy Markdown
Collaborator Author

ImGui の Vulkan 対応完了

Promete.ImGui が Vulkan バックエンドでも動作するようになりました。

実装内容

  • VulkanImGuiController: Silk.NET 公式の ImGuiController は OpenGL 専用のため、Vulkan 用のレンダラ + 入力処理を自作
    • フォントアトラスを VulkanResourceManager に登録し、ImGui の TexID = Promete のテクスチャ ID として統一。ImGui.ImageTexture2D.Handle をそのまま渡せる(GL と同じ使用感)
    • frames-in-flight 対応の頂点/インデックスバッファ、クリップ矩形→シザー変換、push constant による射影
    • キーボード(キーマップ + 文字入力 + 修飾キー)・マウス・ホイールを Silk.NET.Input から ImGui IO へ変換
  • スワップチェーンパスの開放: ブリット後もパスを閉じず EndFrame で閉じるよう変更。PostRender でスワップチェーンイメージへオーバーレイを直接記録でき、GL の描画順(ブリット後に ImGui)と同一セマンティクス
  • ImGuiPlugin: app.View の型で GL/Vulkan のコントローラを自動選択。GL パスは従来どおり Silk.NET のコントローラを使用
  • Example の Vulkan 時 ImGui 無効化を解除(--vulkan でも ImGui デモが動く)

検証

  • Experimental: ImGui デモウィンドウをオーバーレイ描画した状態でピクセル検証 20 項目全パス・例外なし
  • Example: GL / Vulkan 両モードで 8〜10 秒スモークテスト、クラッシュなし
  • dotnet test Promete.Test: 147 件全合格

dotnet run --project Promete.Example -- --vulkan から /plugins/imgui デモで動作を確認できます。

@EbiseLutica EbiseLutica changed the title feat: Vulkanバックエンド移植 — 計画書 + Phase 0/1 (基盤整備・骨格) feat: Vulkanバックエンド実装 Jul 23, 2026
@EbiseLutica
EbiseLutica marked this pull request as ready for review July 23, 2026 18:19
EbiseLutica and others added 16 commits August 3, 2026 19:45
これまで Scene 派生クラスの自動登録は Assembly.GetEntryAssembly() のみを
走査していたため、ゲームをエンジン層とコンテンツ層の2プロジェクトに分割し、
シーンをエンジン側のアセンブリに置くと LoadScene が
「The scene "Xxx" is not registered.」で落ちていた。

PrometeAppBuilder に UseScenesFrom(Assembly) と UseScenesFrom<T>() を足し、
エントリアセンブリに加えて任意のアセンブリを探索対象にできるようにした。

    PrometeApp.Create()
        .UseScenesFrom(typeof(LoadingScene).Assembly)
        .BuildWithVulkanDesktop(opts);

既定の挙動は変えていないので、単一アセンブリの構成には影響しない。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019eZmjGkVajSwKumgLsMwrA
Scene 派生クラスの自動登録がエントリアセンブリに限られることは、
これまでドキュメントに書かれていなかった。エントリアセンブリ外に
シーンを置いたときの対処とあわせて記載する。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019eZmjGkVajSwKumgLsMwrA
RenderCommandQueue.PushTrim がバックエンド非依存層で OpenGL 固有の
左下原点変換を行っていたため、キューは左上原点で座標を保持し、
Y反転は GLBeginTrim/GLEndTrimCommandRunner 側で行うよう変更。
Vulkan など左上原点のバックエンド追加への布石。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
- VulkanContext: instance / surface / device / swapchain / フレーム同期
  (frames-in-flight=2)・クリアカラー描画・リサイズ時の再構築
- VulkanDesktopBackend / VulkanDesktopGameView / BuildWithVulkanDesktop()
- テクスチャ・シェーダー等は暫定的に Headless 実装を流用 (Phase 2 で置換)
- VULKAN_PORTING_PLAN.md: コードベース調査に基づく移植計画書

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
BuildWithVulkanDesktop() でウィンドウを起動し、クリアカラーの描画と
切り替え・自動終了までを確認する最小プログラム。実機で起動確認済み。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
- VulkanResourceManager: int IDテーブルでVkImage/ImageView/DescriptorSetを管理
  (Texture2D.Handle 互換)、stagingバッファ経由アップロード
- VulkanPipelineProvider: パイプライン遅延生成・キャッシュ、shadercによる
  GLSL450→SPIR-Vランタイムコンパイル (Silk.NET.Shaderc 追加)
- VulkanRenderTextureProvider: オフスクリーンパスの中断/再開でBeginCapture
  のネストセマンティクスを再現、Resize対応
- VulkanContext: フレームライフサイクル (BeginFrame/EndFrame)、ターゲット
  スタック、per-frame頂点アリーナ、遅延破棄、ピクセル読み出し
- ランナー: テクスチャバッチ (インスタンシング)、プリミティブ、トリム
- VulkanScreenBlitter: フルスクリーントライアングルでスワップチェーンへブリット
- スクリーンショット実装 (TakeScreenshot / SaveScreenshotAsync)
- Promete.Experimental.Vulkan: ピクセル単位の自動検証 (6項目全パス)

未対応: カスタムシェーダー・マスク・PieSprite・ポストプロセス

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
FrameBuffer は GL の RT テクスチャが bottom-up 格納であることを前提に
子ノードを上下反転して補正しているが、Vulkan の RT は top-down のため
二重反転になっていた。RenderTexture 用の Texture2D に V 反転 UV を
持たせ、GL と同じ見え方に統一する。

検証: Experimental に FrameBuffer の上下向きピクセル検証を追加 (8項目全パス)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
dotnet run --project Promete.Example -- --vulkan で実験的な
Vulkanバックエンドを試せる。ImGuiプラグインはOpenGL専用のため
Vulkan時は無効化する。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
- SpirvReflector: SPIR-Vから uniform ブロックのメンバー名/オフセットを抽出する
  最小リフレクタ (追加ネイティブ依存なし)
- VulkanShaderManager / VulkanShaderFactory: GLSL450カスタムシェーダーの
  コンパイルと ShaderProgram.Handle 互換のIDテーブル管理
- VulkanMaterialSystem: Material の名前ベース Uniform をリフレクション結果に
  基づき per-material UBO (set=1, binding=0) へ書き込み
- スプライトのカスタムマテリアル描画パス (texture_instanced 互換レイアウト規約)
- VulkanDrawPieTextureCommandRunner: push constant (MVP/tint/角度) で扇形描画
- VulkanScreenBlitter: ピンポンバッファによるポストプロセスチェーン
- スクリーンショットをポストプロセス適用後の最終ブリット元から読むよう変更
- Experimental: 検証を13項目に拡張 (PieSprite/カスタムマテリアル/色反転
  ポストプロセスの2フェーズ検証) — 全パス

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
- オフスクリーンパス・RenderTexture にステンシルアタッチメントを追加
  (D24S8/D32S8 をフォーマット照会で選択、パス中断/再開をまたいで保持)
- パイプラインにステンシルバリアント (None/WriteMask/TestEqual) を追加し、
  ランナーは StencilMaskActive に応じて選択
- ステンシルマスク: vkCmdClearAttachments でクリア → マスク形状書き込み
  (カラー書き込み無効 + Always/Replace) → 子を Equal テストで描画
- アルファマスク: VulkanMaskedContainerHelper が子を RenderTexture へ
  サブレンダリングし、masked シェーダーで content×mask 合成
  (set0=content, set1=mask として既存のテクスチャ毎ディスクリプタを流用)
- Experimental: マスク両方式の検証を追加、全17項目パス

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
GL バックエンドとの機能パリティを完成させる:
- SpirvReflector: サンプラー変数 (名前/set/binding) の抽出を追加
- カスタムパイプラインレイアウトをシェーダー毎に生成
  (追加テクスチャのセット数をリフレクションから決定)
- Material の Texture2D Uniform: set=2 以降の同名 sampler2D に
  既存のテクスチャ毎ディスクリプタセットをバインド
- VulkanDrawPieTextureCommandRunner / VulkanDrawPrimitiveCommandRunner に
  カスタムマテリアル描画パスを追加
- Experimental: 検証を20項目に拡張 — 全パス

スコープ外と決定: 線幅>1 (wideLines非依存の展開は将来検討)、ImGui

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
- VulkanImGuiController: Vulkan用のImGuiレンダラと入力処理を実装
  - フォントアトラスは VulkanResourceManager に登録し、ImGui の TexID として
    Promete のテクスチャ ID をそのまま使用 (ImGui.Image に Texture2D.Handle が渡せる)
  - frames-in-flight 対応の頂点/インデックスバッファ、シザー・push constant射影
  - Silk.NET.Input からのキーボード/マウス/ホイール入力を ImGui IO へ変換
- スワップチェーンパスを EndFrame まで開放し、PostRender でオーバーレイを
  直接記録できるように変更 (GLの描画順と同一セマンティクス)
- ImGuiPlugin: バックエンドで GL/Vulkan のコントローラを自動選択
- InternalsVisibleTo Promete.ImGui を追加
- Example: Vulkan時のImGuiPlugin無効化を解除
- Experimental: ImGuiデモウィンドウをオーバーレイした状態で20項目検証全パス

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016A9ZxvQBn4vAu8Erah3rHr
@EbiseLutica
EbiseLutica force-pushed the feature/vulkan-backend-phase1 branch from e27071d to 2d412a9 Compare August 3, 2026 11:41
@EbiseLutica
EbiseLutica requested a balanced review from Copilot August 23, 2026 17:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Vulkan デスクトップバックエンドを追加し、OpenGL 相当の描画機能・ImGui・検証基盤を整備する PR です。

Changes:

  • Vulkan のリソース、パイプライン、シェーダー、描画ランナーを実装
  • トリム座標を左上原点へ統一し、GL 側の変換を修正
  • ImGui、validation、サンプル、テスト、複数アセンブリの Scene 登録を追加

Reviewed changes

Copilot reviewed 69 out of 69 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
VULKAN_PORTING_PLAN.md 移植計画と進捗を記録
Promete/VulkanDesktop/VulkanDesktopAppExtension.cs Vulkan ビルド API を追加
Promete/Backends/Vulkan/VulkanDesktopBackend.cs Vulkan バックエンドを構成
Promete/Backends/Vulkan/VulkanDesktopGameView.cs Vulkan 用 GameView とスクリーンショットを実装
Promete/Graphics/Rendering/Vulkan/VulkanContext.cs Vulkan コンテキストと同期処理を実装
Promete/Graphics/Rendering/Vulkan/VulkanPipelineProvider.cs パイプライン生成・キャッシュを実装
Promete/Graphics/Rendering/Vulkan/VulkanResourceManager.cs テクスチャリソースを管理
Promete/Graphics/Rendering/Vulkan/VulkanTextureFactory.cs Vulkan テクスチャ生成を実装
Promete/Graphics/Rendering/Vulkan/VulkanShaderCompiler.cs shaderc コンパイルを実装
Promete/Graphics/Rendering/Vulkan/VulkanShaderManager.cs シェーダーモジュールを管理
Promete/Graphics/Rendering/Vulkan/VulkanShaderFactory.cs ShaderProgram の Vulkan 実装を追加
Promete/Graphics/Rendering/Vulkan/SpirvReflector.cs SPIR-V リフレクションを実装
Promete/Graphics/Rendering/Vulkan/VulkanMaterialSystem.cs Uniform と追加テクスチャを適用
Promete/Graphics/Rendering/Vulkan/VulkanFrameArena.cs フレーム用動的バッファを実装
Promete/Graphics/Rendering/Vulkan/VulkanRenderTarget.cs Vulkan RT リソースを定義
Promete/Graphics/Rendering/Vulkan/VulkanRenderTextureProvider.cs RenderTexture を実装
Promete/Graphics/Rendering/Vulkan/VulkanScreenBlitter.cs ポストプロセスと画面転送を実装
Promete/Graphics/Rendering/Vulkan/VulkanMaskedContainerHelper.cs マスク描画を実装
Promete/Graphics/Rendering/Vulkan/IVulkanInstanceHook.cs Instance 拡張フックを公開
Promete/Graphics/Rendering/Vulkan/Runners/VulkanDrawTextureBatchedCommandRunner.cs スプライト描画を実装
Promete/Graphics/Rendering/Vulkan/Runners/VulkanDrawPrimitiveCommandRunner.cs プリミティブ描画を実装
Promete/Graphics/Rendering/Vulkan/Runners/VulkanDrawPieTextureCommandRunner.cs PieSprite 描画を実装
Promete/Graphics/Rendering/Vulkan/Runners/VulkanTrimCommandRunners.cs Vulkan シザーを実装
Promete/Graphics/Rendering/Vulkan/Runners/VulkanMaskCommandRunners.cs マスクコマンドを実装
Promete/Resources/shaders/vulkan/texture_instanced.vert スプライト頂点シェーダー
Promete/Resources/shaders/vulkan/texture_instanced.frag スプライト断片シェーダー
Promete/Resources/shaders/vulkan/stencil_mask.frag ステンシルマスクシェーダー
Promete/Resources/shaders/vulkan/primitive.vert プリミティブ頂点シェーダー
Promete/Resources/shaders/vulkan/primitive.frag プリミティブ断片シェーダー
Promete/Resources/shaders/vulkan/pie.vert PieSprite 頂点シェーダー
Promete/Resources/shaders/vulkan/pie.frag PieSprite 断片シェーダー
Promete/Resources/shaders/vulkan/masked.vert マスク合成頂点シェーダー
Promete/Resources/shaders/vulkan/masked.frag マスク合成断片シェーダー
Promete/Resources/shaders/vulkan/blit.vert 全画面転送頂点シェーダー
Promete/Resources/shaders/vulkan/blit.frag 全画面転送断片シェーダー
Promete/PrometeApp.cs 外部アセンブリの Scene 登録を追加
Promete/Promete.csproj Vulkan 依存関係とリソースを追加
Promete/Graphics/Rendering/RenderCommandQueue.cs トリム計算を左上原点へ統一
Promete/Graphics/Rendering/Commands/TrimCommands.cs トリム座標の文書を更新
Promete/Graphics/Rendering/GL/Runners/GLBeginTrimCommandRunner.cs GL 開始シザーで Y 反転
Promete/Graphics/Rendering/GL/Runners/GLEndTrimCommandRunner.cs GL 終了シザーを復元
Promete.Vulkan.Validation/Promete.Vulkan.Validation.csproj Validation パッケージを追加
Promete.Vulkan.Validation/VulkanValidationHook.cs Debug Utils フックを実装
Promete.Vulkan.Validation/VulkanValidationExtension.cs Validation 登録 API を追加
Promete.Vulkan.Validation/VulkanValidationMessage.cs Validation メッセージ型を追加
Promete.ImGui/Promete.ImGui.csproj ImGui パッケージ版を更新
Promete.ImGui/ImGuiPlugin.cs GL/Vulkan コントローラを選択
Promete.ImGui/IImGuiController.cs ImGui 共通インターフェースを追加
Promete.ImGui/VulkanImGuiController.cs Vulkan ImGui 描画を実装
Promete.Experimental.Vulkan/Promete.Experimental.Vulkan.csproj Vulkan 検証プロジェクトを追加
Promete.Experimental.Vulkan/Program.cs 検証アプリの起動処理
Promete.Experimental.Vulkan/MainScene.cs ピクセル検証シーンを実装
Promete.Example/Promete.Example.csproj Validation プロジェクトを参照
Promete.Example/Program.cs Vulkan 起動フラグを追加
Promete.Example/MainScene.cs 使用バックエンドを表示
Promete.Example/Kernel/DemoKernel.cs Vulkan 選択状態を保持
Promete.Example/examples/debug/VulkanShaderReflectionDebugScene.cs シェーダー境界検証シーン
Promete.Example/examples/debug/VulkanMaterialSlotLeakDebugScene.cs Material 枯渇再現シーン
Promete.Example/examples/debug/VulkanMaskRestoreDebugScene.cs マスク例外再現シーン
Promete.Example/examples/debug/VulkanFrameArenaGrowthDebugScene.cs Arena 拡張検証シーン
Promete.Example/examples/debug/TrimClampDebugScene.cs トリム境界検証シーン
Promete.Test/VulkanDemoShaderCompileTests.cs Vulkan GLSL コンパイルを検証
Promete.Test/SpirvReflectorTests.cs SPIR-V パーサーを検証
Promete.Test/TrimCommandQueueTests.cs トリム計算を検証
Promete.Test/TrimSceneShapeTests.cs 入れ子トリムを検証
Promete.Test/SceneRegistrationTests.cs 外部 Scene 登録を検証
Promete.Docs/src/content/docs/guide/extends/rendercommand.md Runner シグネチャを訂正
docs-llm.md Scene 登録 API を文書化
Promete.sln Vulkan 関連プロジェクトを追加
Suppressed comments (2)

Promete/Graphics/Rendering/Vulkan/VulkanMaterialSystem.cs:67

  • Material は Uniform 値を含む可変の GetHashCode() を実装しているため、このキーを登録後に material["uTime"] などを更新すると既存エントリを検索できなくなります。通常のアニメーションでも毎フレーム新しい UBO/descriptor set が確保され、固定 1024 セットのプールが枯渇するので、参照同一性に基づく安定したキーを使ってください。
    Promete/Graphics/Rendering/Vulkan/VulkanMaterialSystem.cs:221
  • 新規スロットのホストメモリは初期化されておらず、Apply は Material に指定されたメンバーだけを書き込みます。Uniform ブロック内の未指定メンバーが未定義値になり、GL のリンク直後のゼロ初期値とも挙動が一致しません。マップ直後にブロック全体をゼロクリアしてください。

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +103 to +106
private void DestroyBuffer(Buffer buffer, DeviceMemory memory)
{
_ctx.Vk.DestroyBuffer(_ctx.Device, buffer, null);
_ctx.Vk.FreeMemory(_ctx.Device, memory, null);
private readonly VulkanContext _ctx;
private readonly VulkanShaderManager _shaders;
private readonly VulkanResourceManager _resources;
private readonly Dictionary<(Material Material, int Slot), MaterialSlot> _slots = [];
Comment on lines +61 to +64
var opcode = words[index] & 0xFFFF;
var wordCount = (int)(words[index] >> 16);
if (wordCount == 0)
break;
Binding = 0,
DescriptorType = DescriptorType.CombinedImageSampler,
DescriptorCount = 1,
StageFlags = ShaderStageFlags.FragmentBit,
Comment on lines +109 to +113
SpirvReflector.SamplerBinding? sampler = null;
foreach (var s in entry.Samplers)
{
if (s.Set >= 2 && s.Name == name)
{
Comment on lines +78 to +81
queue.PushScope();
foreach (var child in sorted)
app.CollectNode(child, queue, renderContext);
queue.PopScopeAndFlush();
Comment on lines +662 to +668
private static void DestroyBuffer(VulkanContext ctx, VkBuffer buffer, DeviceMemory memory)
{
if (buffer.Handle == 0)
return;
ctx.Vk.DestroyBuffer(ctx.Device, buffer, null);
ctx.Vk.FreeMemory(ctx.Device, memory, null);
}
Comment on lines +154 to +158
foreach (var slot in _slots.Values)
{
vk.DestroyBuffer(device, slot.Buffer, null);
vk.FreeMemory(device, slot.Memory, null);
}
/// 全描画をスクリーンサイズの <see cref="RenderTexture"/> にキャプチャし、
/// ポストプロセスマテリアルを適用した後、スワップチェーンイメージへブリットするクラスです。
/// </summary>
internal sealed class VulkanScreenBlitter : IScreenBlitter
Comment on lines +67 to +69
// インスタンスフックが登録されていれば使う (バリデーションレイヤー等)
_app.TryGetPlugin<IVulkanInstanceHook>(out var instanceHook);
_context = new VulkanContext(_nativeWindow, instanceHook);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants