Rust×windows‑rsでD3D11CreateDeviceAndSwapChainのDEBUG出力がVS Codeで見えない時の完全解決ガイド|Graphics ToolsとOutputDebugStringで直す

Rust+windows-rs で Direct3D 11 のデバッグレイヤーを使いたいのに、D3D11_CREATE_DEVICE_DEBUG を付けて D3D11CreateDeviceAndSwapChain を呼んでも VS Code のターミナルに何も出ない――この悩みは仕組みを知ればすぐ解決できます。本稿は「なぜ見えないのか」「VS Code でどう見えるようにするか」を、環境構築から実装・検証・運用の順で徹底解説します。

目次

問題の本質:出力先は「標準出力」ではなく OutputDebugString

Direct3D 11 のデバッグレイヤーが吐くメッセージは、コンソール(標準出力/標準エラー)ではなく Win32 API の OutputDebugString に流れます。つまり、ターミナルや PowerShell に待ち構えていても何も表示されません。
この出力を受け取れるのは、Visual Studio のデバッガ、VS Code の MSVC デバッガ(cppvsdbg、および DebugView(Sysinternals) などのビューアです。gdb / lldb(CodeLLDB)では既定で拾えない点がハマりどころです。

結論の先出し:最短ルート

  • Graphics Tools を Windows に追加インストールD3D11SDKLayers.dll を得る)。
  • VS Code の launch.jsontype: "cppvsdbg" にする(Rustでも MSVC ターゲットなら利用可)。
  • 必要なら DebugViewOutputDebugString を直接モニタ。
  • アプリ側では ID3D11InfoQueueBreakOnSeverity を設定して ミスを即停止

なぜフラグを立てても静かなのか:前提条件の未充足

D3D11_CREATE_DEVICE_DEBUG を指定しても、システムに D3D11 デバッグレイヤー(D3D11SDKLayers.dll が存在しないと、デバイス生成が失敗または出力が沈黙します。Windows 10/11 では「機能の追加」から Graphics Tools を入れるだけで解決します。

Graphics Tools の導入手順

  1. Windows の 設定アプリオプション機能
  2. 機能の追加 → 検索枠に「Graphics Tools」→ 追加。
  3. インストール後に再起動(求められた場合)。

これで D3D11SDKLayers.dll と関連ユーティリティが入り、デバッグレイヤーが有効化できる土台が整います。

環境別:デバッグメッセージの見え方

環境必要な操作メッセージの表示場所補足
Visual StudioF5 デバッグ実行[出力] ウィンドウ(デバッグ)特別な設定不要。自動で OutputDebugString を捕捉。
VS Code(MSVC)launch.jsontypecppvsdbgデバッグコンソールCodeLLDB / gdb では既定で出ない。Rust でも -msvc ターゲットなら cppvsdbg が使える。
DebugView(Sysinternals)アプリと DebugView を起動DebugView のリストスタンドアロン。昇格(管理者)整合性に注意。

VS Code 設定:Rust でも cppvsdbg で受信する

Rust のターゲットを x86_64-pc-windows-msvc にし、VS Code のデバッガを MSVC に切り替えます。標準出力はターミナル、OutputDebugStringデバッグコンソール に現れます。

推奨構成(抜粋)

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Run Rust (MSVC) with D3D11 debug",
      "type": "cppvsdbg",
      "request": "launch",
      "program": "${workspaceFolder}\\target\\debug\\your_app.exe",
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "stopAtEntry": false,
      "environment": [
        { "name": "RUST_BACKTRACE", "value": "1" }
      ],
      "preLaunchTask": "cargo build"
    }
  ]
}

MSVC ターゲットに切り替える

rustup toolchain install stable-x86_64-pc-windows-msvc
rustup default stable-x86_64-pc-windows-msvc   # あるいは cargo +stable-x86_64-pc-windows-msvc build

最小検証コード:OutputDebugString と D3D11 デバッグレイヤー

まずは OutputDebugString 単体で VS Code のデバッグコンソールに出るか確認します。

use windows::core::PCWSTR;
use windows::Win32::System::Diagnostics::Debug::OutputDebugStringW;

fn ods(s: &str) {
use std::os::windows::ffi::OsStrExt;
let wide: Vec = std::ffi::OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect();
unsafe { OutputDebugStringW(PCWSTR(wide.as_ptr())); }
}

fn main() {
println!("stdout にも出ます");
ods("OutputDebugString にも出ます\n");
} 

次に D3D11 デバイスを D3D11_CREATE_DEVICE_DEBUG で作成し、ID3D11InfoQueue にブレーク条件を設定してみます。

use windows::core::{Interface, Result};
use windows::Win32::Foundation::{BOOL, HINSTANCE, HWND};
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1};
use windows::Win32::Graphics::Direct3D11::*;
use windows::Win32::Graphics::Dxgi::{IDXGISwapChain, DXGI_ERROR_SDK_COMPONENT_MISSING};
use windows::Win32::Graphics::Dxgi::Common::*;

unsafe fn create_device_and_swap_chain(hwnd: HWND, debug: bool)
-> Result<(ID3D11Device, ID3D11DeviceContext, IDXGISwapChain)> {
let mut sc_desc = DXGI_SWAP_CHAIN_DESC::default();
sc_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sc_desc.SampleDesc = DXGI_SAMPLE_DESC { Count: 1, Quality: 0 };
sc_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sc_desc.BufferCount = 2;
sc_desc.OutputWindow = hwnd;
sc_desc.Windowed = BOOL(1);
sc_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;


let levels = [D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0];
let mut attained: D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_11_0;

let mut device: Option<ID3D11Device> = None;
let mut context: Option<ID3D11DeviceContext> = None;
let mut swapchain: Option<IDXGISwapChain> = None;

let mut flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if debug { flags |= D3D11_CREATE_DEVICE_DEBUG; }

// 生成(デバッグレイヤー未導入なら DXGI_ERROR_SDK_COMPONENT_MISSING が返ることがある)
let hr = D3D11CreateDeviceAndSwapChain(
    None,
    D3D_DRIVER_TYPE_HARDWARE,
    HINSTANCE(0),
    flags,
    Some(&levels),
    D3D11_SDK_VERSION,
    Some(&sc_desc),
    Some(&mut swapchain),
    Some(&mut device),
    Some(&mut attained),
    Some(&mut context),
);

if let Err(e) = hr {
    if debug && e.code() == DXGI_ERROR_SDK_COMPONENT_MISSING {
        // デバッグレイヤーが無いのでフラグを外してリトライ
        let hr2 = D3D11CreateDeviceAndSwapChain(
            None, D3D_DRIVER_TYPE_HARDWARE, HINSTANCE(0),
            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
            Some(&levels), D3D11_SDK_VERSION,
            Some(&sc_desc), Some(&mut swapchain),
            Some(&mut device), Some(&mut attained), Some(&mut context),
        );
        hr2?;
    } else {
        e.result()?;
    }
}

let device = device.unwrap();
let context = context.unwrap();
let swapchain = swapchain.unwrap();

// 重要:InfoQueue でブレーク条件を設定(誤用を即座に検出)
if debug {
    if let Ok(q) = device.cast::() {
        q.SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true).ok();
        q.SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true).ok();
        q.SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true).ok();
    }
}

Ok((device, context, swapchain))


} 

注意:上記は説明用に簡略化しています。ウィンドウ作成や COM 初期化、エラーハンドリングは実戦に合わせて補完してください。

Rust(windows クレート)の機能選択

Cargo.toml で必要 API を有効化しておきます。

[dependencies]
windows = { version = "0.58", features = [
  "Win32_Foundation",
  "Win32_Graphics_Direct3D",
  "Win32_Graphics_Direct3D11",
  "Win32_Graphics_Dxgi",
  "Win32_Graphics_Dxgi_Common",
  "Win32_System_Diagnostics_Debug",
] }

よくある誤解の整理

  • 「DEBUG は Visual Studio 専用?」 → いいえ。Windows の標準デバッグ出力(OutputDebugString)に流れる仕組みで、VS はそれを見やすく表示しているだけです。
  • 「VS Code のターミナルに出る?」 → いいえ。コンソールではなく デバッグコンソールcppvsdbg 使用時)に出ます。
  • 「リリースビルドでは無効?」 → いいえ。フラグを立てて生成に成功すれば動作します(最終配布では外すのが一般的)。
  • 「GPU-based validation を D3D11 でも使う?」 → これは 主に D3D12 の検証強化機能です。D3D11 では ID3D11InfoQueue による検証・ブレークが中心です。

動作確認を一段深く:InfoQueue のフィルタとダンプ

警告が多すぎて埋もれる場合は、低重要度を抑制しつつ致命的なメッセージでブレークするのが定石です。

if let Ok(q) = unsafe { device.cast::<ID3D11InfoQueue>() } {
    // Info メッセージをストレージ側で抑制
    let deny = [D3D11_MESSAGE_SEVERITY_INFO];
    let mut filter = D3D11_INFO_QUEUE_FILTER::default();
    filter.DenyList.NumSeverities = deny.len() as u32;
    filter.DenyList.pSeverityList = deny.as_ptr();
    unsafe { q.AddStorageFilterEntries(&filter).ok(); }


// 既存メッセージのダンプ例
unsafe {
    let count = q.GetNumStoredMessagesAllowedByRetrievalFilter();
    for i in 0..count {
        let mut len: usize = 0;
        q.GetMessageA(i, std::ptr::null_mut(), &mut len).ok(); // 長さ取得
        let mut buf = vec![0u8; len];
        let p = buf.as_mut_ptr() as *mut D3D11_MESSAGE;
        q.GetMessageA(i, p, &mut len).ok();
        let msg = &*p;
        // msg.pDescription は C 文字列。必要なら文字列化して OutputDebugString に流す
    }
}


} 

巨大なログはパフォーマンスに響くため、フィルタで間引く・名前を付ける・要所で BreakOnSeverity が実践的です。

DirectX Control Panel(dxcpl.exe)の活用

Graphics Tools に含まれる「DirectX Control Panel」では、アプリ単位で Debug Layer 有効化Break on Error などを指定できます。プロセス起動前に設定しておくと、アプリ側でフラグを付け忘れても検証できます。
ただし D3D11 の検証は主にデバッグレイヤーと InfoQueue が担い、GPU-based validation は D3D12 の機能です。D3D11 アプリなら「Debug Layer」と「Message break」を中心に使いましょう。

権限・整合性レベルに関する注意

  • 昇格の不一致:アプリが管理者権限で走り、ビューアが非昇格だと OutputDebugString を拾えない場合があります。同じ権限レベルで実行しましょう。
  • サンドボックス:一部のセキュリティソフトやサンドボックスがデバッグ出力を抑制します。疑わしい場合は一時的に無効化して切り分けます。

ビルド構成と最適化の落とし穴

  • LTO/最適化:リリースビルドで最適化が強いと、軽微な API 誤用が表に出にくくなります。検証時はデバッグビルド、配布前にリリースビルドで再最終確認が安全です。
  • マルチスレッドログ:複数スレッドで大量出力すると、表示側が追いつかず欠落して見えることがあります。必要に応じて SetMessageCountLimit などで抑制しましょう。

「ターミナルに何も出ない」を潰すチェックリスト

  1. Graphics Tools を導入したか(未導入ならまず導入)。
  2. Rust のターゲットが MSVC になっているか。
  3. VS Code のデバッガが cppvsdbg になっているか(CodeLLDB ではない)。
  4. D3D11_CREATE_DEVICE_DEBUG でデバイス作成が成功しているか(失敗時は DXGI_ERROR_SDK_COMPONENT_MISSING を疑う)。
  5. ID3D11InfoQueueBreakOnSeverity を設定したか。
  6. 権限レベルの不一致がないか(昇格の有無を合わせる)。

トラブルシューティング:症状別・即効表

症状主な原因対処
D3D11_CREATE_DEVICE_DEBUG で失敗デバッグレイヤー未導入Graphics Tools を導入。以後成功するか確認。フォールバック実装も検討。
VS Code で何も出ないcppvsdbg でなく CodeLLDB を使用launch.jsontype: "cppvsdbg" に変更、MSVC ターゲットに切替。
DebugView でも見えない整合性レベル不一致/セキュリティソフト両方を同じ権限で実行。セキュリティソフトを一時無効化して切り分け。
警告が多すぎて埋もれるInfoQueue フィルタ未設定低重要度(INFO)を抑制、BreakOnSeverity を ERROR/ WARNING に設定。
Release で重いデバッグレイヤー常時有効配布ビルドではフラグを外し、開発時のみ有効化。

実運用のコツ:検証を「仕組み化」する

  • ビルドフラグの二段階化cfg!(debug_assertions) でデバッグ時のみ D3D11_CREATE_DEVICE_DEBUG を立てる。
  • 初期化でアサート:起動直後に InfoQueueBreakOnSeverity(ERROR) を仕掛け、重大ミスを即発見。
  • 命名の徹底SetPrivateData(WKPDID_D3DDebugObjectName, ...) でリソースに名前を付けると、ログの読解性が飛躍的に向上。
  • 小さく失敗させる:意図的に不正な引数(例:不一致フォーマット)を与えて、ブレークとメッセージ経路を初日に検証。

サンプル:WKPDID_D3DDebugObjectName で名前を付ける

// 例:バッファ作成直後にデバッグ名を付与
use windows::core::GUID;
use windows::Win32::Graphics::Direct3D11::{ID3D11Buffer, WKPDID_D3DDebugObjectName};

unsafe fn set_debug_name(buf: &ID3D11Buffer, name: &str) {
let bytes = name.as_bytes();
buf.SetPrivateData(&WKPDID_D3DDebugObjectName, bytes.len() as u32, Some(bytes.as_ptr() as _)).ok();
} 

以後のメッセージにリソース名が含まれ、どのオブジェクトが悪さをしているかが追いやすくなります。

FAQ

  • Q. Rust の println! は出るのに D3D のログが出ません。
    A. 出力経路が別です。println! は標準出力、D3D のログは OutputDebugString です。デバッグコンソールや DebugView を確認してください。
  • Q. D3D11CreateDevice 単独でも同様?
    A. はい。D3D11_CREATE_DEVICE_DEBUG を付ける点は同じです。スワップチェーンが絡むかは無関係。
  • Q. WARP/REF ドライバでもログは出ますか?
    A. 出ます。ドライバ種別に関わらずデバッグレイヤーが有効なら同様に動作します。

まとめ

DEBUG モードは Visual Studio 専用ではありません。Windows 標準のデバッグ出力に流れる設計です。したがって、

  • Graphics Tools を導入してデバッグレイヤーを使える状態にする。
  • VS Code では cppvsdbg を使い、デバッグコンソールOutputDebugString を受信する。
  • ID3D11InfoQueue を活用して BreakOnSeverity とフィルタを設定する。

これだけで、VS Code+Rust(windows-rs)環境でも Direct3D 11 のデバッグメッセージを確実に観測でき、ミスを初期段階で潰せます。デバッグ出力の行き先を正しく理解し、環境に合った受け皿(デバッガ/ビューア)を選ぶ――それが最短の解です。


付録:実装テンプレート(要点だけ)

初期化シーケンスの骨子をまとめたテンプレートです。

fn init_d3d11(hwnd: HWND) -> Result<()> {
    // 1) デバイス生成(デバッグ有効)
    let (device, context, swapchain) = unsafe { create_device_and_swap_chain(hwnd, true)? };


// 2) InfoQueue のブレーク設定
if let Ok(q) = unsafe { device.cast::<ID3D11InfoQueue>() } {
    unsafe {
        q.SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true).ok();
        q.SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true).ok();
    }
}

// 3) リソースに名前を付ける(ログの可読性向上)
// 例:レンダーターゲットを作ったら SetPrivateData(...)

Ok(())


} 

付録:運用チェックポイント

  • プロジェクト共有:チーム全員の launch.jsoncppvsdbg で統一し、差分を最小化。
  • CI のスモーク:ヘッドレス起動でも OutputDebugString をインターセプトして異常検知(DebugView のコマンドライン版や独自フック)。
  • メッセージ予算:フレームごとの出力量を抑え、意味のあるワーニングだけを残す。

キーワード早見

キーワード要点
D3D11_CREATE_DEVICE_DEBUGデバッグレイヤー有効化フラグ。未導入なら生成失敗や出力なし。
OutputDebugStringDirect3D のデバッグメッセージの出力先。ターミナルではなくデバッグコンソール等で確認。
ID3D11InfoQueueメッセージの取得・フィルタ・ブレーク条件の設定が可能。
Graphics ToolsD3D11SDKLayers.dll を含むオプション機能。導入必須。
cppvsdbgVS Code の MSVC デバッガ。OutputDebugString を捕捉できる。
MSVC ターゲットRust で VS Code の cppvsdbg を使う前提(x86_64-pc-windows-msvc)。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次