Newer
Older
SmTIAS-LightSim / src / sm_tias_lightsim / render / runner.py
@rintoHasegawa rintoHasegawa on 28 May 4 KB [add] Speed-1 LLVM JIT 自動検出と診断ログ強化 — runtime/llvm_init.py を新規実装.ensure_drjit_libllvm_path() で DRJIT_LIBLLVM_PATH 未設定時に Windows 標準 4 候補 (C:\Program Files\LLVM 等) + POSIX glob + LLVM_DIR + extra_candidates を順次探索して os.environ 設定(既設定なら上書きしない).log_variant_status() で llvm_/cuda_/scalar_ 経路を INFO 明示,要求 llvm_* で実 scalar_* なら WARNING 追加.render/runner.py で variant=llvm_* 時に自動検出を呼び ImportError 時は原因明示ログ→ re-raise,mitsuba を関数内遅延 import 化.visualize/baker.py の _set_variant_once に LLVM 自動探索分岐を追加し direct/path 両方で log_variant_status を呼ぶ.cli/render.py で import 順を制御し sensors.py/lights.py のモジュールレベル mitsuba import 前に検出を確実に通す.docs/02_ENV/ENV_02 に LLVM インストール手順・DRJIT_LIBLLVM_PATH 説明・動作確認コマンドを追記.新規テスト 51 件 (runtime/test_llvm_init.py 25, render/test_runner_jit_init.py 14, visualize/test_baker_jit_init.py 8, cli/test_render_jit_init.py 4) で仕様 A1-E1 完全カバー,DRJIT_LIBLLVM_PATH 設定下で 606 件全通過.速度は不変(目的は再発防止+診断容易化).数値同等性は MC 内部シードで SHA256 異なるが平均相対誤差 7.23e-05 で統計同等性パス.これで環境変数消失によるサイレントクラッシュが再発しない土台が整備された.TEST_04 Speed-1 セクションを実施結果で更新.CLAUDE.md 開発進捗に Speed-1 完了追記+実装済みモジュール一覧に runtime/llvm_init.py 追加
# pyright: reportCallIssue=none
# Mitsuba 3 は variant ベースの動的属性解決を行うため Pylance/Pyright が
# mi.* を object 型と推論し reportCallIssue を誤検知する(GUIDE_06 の方針).
"""Mitsuba レンダリング実行モジュール.

variant 設定と mi.render() の実行を担う.
`mitsuba.set_variant` はこのモジュールで 1 度だけ呼ぶ.
"""

from __future__ import annotations

import logging
import shutil
from pathlib import Path

import numpy

from sm_tias_lightsim.runtime.llvm_init import ensure_drjit_libllvm_path, log_variant_status
from sm_tias_lightsim.scene.builder import build_scene
from sm_tias_lightsim.scene.schema import SceneConfig

_logger = logging.getLogger(__name__)

# 現在設定されている variant を追跡する(プロセス内 1 度だけ変更可能)
_current_variant: str | None = None


def render_scene(config: SceneConfig, project_root: Path | None = None) -> numpy.ndarray:
    """SceneConfig からシーンを構築してレンダリングし,HDR 画像(H, W, 3)を返す.

    Mitsuba の variant を config.render.variant に設定する(プロセス内初回のみ有効).
    既に異なる variant が設定されている場合は警告ログを出して続行する.

    llvm_* variant 要求時は mi.set_variant 前に DRJIT_LIBLLVM_PATH を自動探索して設定する.
    mitsuba は関数内でインポートするため,モジュール import 時点では LLVM 初期化が走らない.
    set_variant 成功後に variant 状態を INFO ログで出力する.

    Args:
        config: 検証済み SceneConfig.
        project_root: STL 相対パスの基点ディレクトリ.None なら CWD を使用.

    Returns:
        HDR 画像 (H, W, 3) の float32 配列.値域は物理量(0〜∞ も許容).
    """
    global _current_variant  # noqa: PLW0603

    desired_variant = config.render.variant

    if _current_variant is None:
        # llvm_* variant の場合のみ DRJIT_LIBLLVM_PATH を自動探索する
        # scalar_* / cuda_* は LLVM-C.dll 不要なので探索しない
        # mitsuba の import 前に os.environ['DRJIT_LIBLLVM_PATH'] を設定することが必須.
        # mitsuba を先に import すると LLVM 初期化が走り,LLVM-C.dll 未設定で ImportError になる.
        if desired_variant.startswith("llvm_"):
            ensure_drjit_libllvm_path()

    # mitsuba は DRJIT_LIBLLVM_PATH 設定後にインポートする.
    # モジュールレベルで import すると,ensure_drjit_libllvm_path() の前に LLVM 初期化が走り
    # ImportError になるため,この位置に遅延 import する.
    import mitsuba as mi

    if _current_variant is None:
        try:
            mi.set_variant(desired_variant)
        except ImportError as e:
            if desired_variant.startswith("llvm_") and "LLVM" in str(e):
                _logger.error(
                    "Mitsuba variant=%s の JIT 初期化に失敗しました."
                    "LLVM-C.dll が見つかりません."
                    "Windows: C:\\Program Files\\LLVM\\bin\\LLVM-C.dll を配置するか,"
                    "DRJIT_LIBLLVM_PATH 環境変数を設定してください."
                    "詳細は docs/02_ENV/ENV_02_環境構築手順.md を参照.",
                    desired_variant,
                )
            raise

        _current_variant = desired_variant
        log_variant_status(_logger, desired_variant, mi.variant())
    elif _current_variant != desired_variant:
        _logger.warning(
            "Mitsuba variant is already set to %r; "
            "requested variant %r will be ignored. "
            "Restart the process to change the variant.",
            _current_variant,
            desired_variant,
        )
    # 同じ variant が既に設定されている場合は何もしない

    scene_dict, tempdir = build_scene(config, project_root=project_root)
    try:
        scene = mi.load_dict(scene_dict)
        image_tensor = mi.render(scene, spp=config.render.spp)
        arr = numpy.array(image_tensor)
    finally:
        shutil.rmtree(tempdir, ignore_errors=True)

    return arr