WWDC Quick Look 💓 By SwiftGGTeam
Boost performance with MetalFX Upscaling

Boost performance with MetalFX Upscaling

元の動画を見る

ハイライト

MetalFX は、Metal 3 で導入された画像のアップスケーリングおよびアンチエイリアス フレームワークです。これは、空間的アップスケーリングと時間的アンチエイリアスおよびアップスケーリングの 2 つのモードを提供します。

主要内容

高解像度レンダリングのコストは単純明快です。ピクセルが増えると、GPU 時間が長くなります。ゲームのフレーム レートを上げるには、内部レンダリング解像度を下げてから、画面をディスプレイ解像度まで拡大するのが一般的です。問題も単純で、解像度が低いと細部が失われ、エッジがぼやけてしまいます。 (00:34)

MetalFX Upscaling は、このパイプラインの最後のステップを解決します。アプリは引き続き、最初は低い解像度でフレームをレンダリングし、MetalFX は結果をターゲット解像度にアップスケールします。 Apple はセッションの冒頭で、この API は画質を維持しながらレンダリング時間を短縮することを目的として、Apple デバイス向けにプラットフォームが最適化されていると強調しました。 (00:45)

入口が2つあります。空間アップスケーリングは、現在のフレームのアンチエイリアス カラー入力のみを必要とし、統合が簡単で、すでにアンチエイリアス ソリューションを備えているエンジンに適しています。時間的アンチエイリアシングとアップスケーリングはマルチフレーム情報を使用するため、より多くの入力とより高い品質制限が可能になります。 (01:04)

選択基準も実用的です。エンジンがジッターのあるカラー、モーション、深度バッファーをすでに出力できる場合は、時間的ソリューションが最初に評価されます。これらの入力が利用できない場合、または成熟した時間的 AA がすでに利用可能な場合、空間ソリューションを既存のレンダリング パイプラインに統合する方が簡単です。 (17:55)

詳細

空間アップスケーリング: トーン マッピング後に現在のフレームを増幅します。

空間アップスケーリングは、入力画像の空間情報を分析し、アップスケールされた出力を生成します。アンチエイリアス処理されたカラー テクスチャのみが必要です。 Apple は、入力がすでに知覚色空間に入っているときに最適に機能するため、ゲーム トーン マッピングが完了した後に配置することを推奨しています。 (01:36)

初期化中に最初に作成されるMTLFXSpatialScalerDescriptor、入力および出力の寸法、テクスチャ形式、色処理モードを書き込みます。スケーラー オブジェクトの作成にはコストがかかるため、Apple では、アプリの起動時またはディスプレイ解像度の変更時にのみ作成することを推奨しています。 (03:17)

// Spatial upscaling (initialization)

let desc = MTLFXSpatialScalerDescriptor()
desc.inputWidth = 1280
desc.inputHeight = 720
desc.outputWidth = 2560
desc.outputHeight = 1440
desc.colorTextureFormat = .bgra8Unorm_srgb
desc.outputTextureFormat = .bgra8Unorm_srgb
desc.colorProcessingMode = .perceptual

spatialScaler = desc.makeSpatialScaler(device: mtlDevice)

キーポイント:

  • MTLFXSpatialScalerDescriptor()空間アンプの説明オブジェクトを作成します。 -inputWidthそしてinputHeight低解像度のレンダリング結果の寸法を書き込みます。 -outputWidthそしてoutputHeight最終的な目標サイズを書き込みます。 -colorTextureFormat後で渡される現在のフレーム カラー テクスチャ形式に対応します。 -outputTextureFormatMetalFXで記述された増幅結果フォーマットに対応します。 -colorProcessingMode = .perceptual入力と出力が知覚色空間内にあることを示します。セッションでは、空間スキームのパフォーマンスのために知覚モードを使用することを推奨しています。 -makeSpatialScaler(device:)再利用可能なスケーラー オブジェクトを作成します。

各フレームをレンダリングするときは、まず通常どおり低解像度の画像を描画し、次に現在のフレームのカラー テクスチャと出力テクスチャをスケーラーに渡し、最後にエフェクトを同じコマンド バッファにエンコードします。 (04:08)

// Spatial upscaling (per frame)

// Encode Metal commands to draw game frame here...

// Begin setting per frame properties for effect
spatialScaler.colorTexture = currentFrameColor
spatialScaler.outputTexture = currentFrameUpscaledColor

// Encode scaling effect into command buffer
spatialScaler.encode(commandBuffer: cmdBuffer)

// Encode Metal commands for particle/noise effects and game UI drawing for frame here...

キーポイント:

  • 以前の描画パスは低解像度を生成する原因となっていますcurrentFrameColor
  • colorTextureMetalFX の入力カラーマップへのポインタ。 -outputTexture拡大されたターゲット テクスチャを指します。 -encode(commandBuffer:)MetalFX 増幅作業を現在のコマンド バッファに書き込みます。
  • 後続のパーティクル、ノイズ、UI の描画は、アップスケールされた結果に基づいて続行できます。

空間スキームは入力品質に影響されます。 Apple の推奨事項は次のとおりです。入力はアンチエイリアス処理され、ノイズが発生しないようにする必要があります。知覚モードでの入力は、0 ~ 1 の値でトーン マッピングを完了し、sRGB カラー スペースを使用する必要があります。 (12:25)

時間的 AA とアップスケーリング: マルチフレーム サンプルによる出力品質の向上

時間的 AA とアップスケーリングは、以前のフレームのデータを使用して、より高品質の出力を生成します。その中心的なアイデアはスーパーサンプリングから来ています。各ピクセルには複数のサンプリング ポイントが必要ですが、これを単一フレームで実行するには非常にコストがかかります。サンプリングを複数のフレームに分散すると、コストを削減できます。 (04:39)

画面のコンテンツが移動するため、時間的ソリューションには、ジッターのある色、動き、深度、および前のフレームの出力など、より多くの入力が必要になります。動きは前のフレームの対応する位置を再トレースするために使用され、深度は前景、背景、および新たに露出した領域を決定するために使用されます。 MetalFX は前のフレームのアップスケールされた出力を追跡し、アプリは各フレームで現在のフレームの色、動き、深度を渡します。 (05:56)

テンポラル スケーラーを初期化するとき、説明オブジェクトは色、深度、動き、および出力のテクスチャ形式を宣言する必要があります。後で設定するmotionVectorScaleこれにより、MetalFX がエンジン内のモーション データをレンダリング解像度のピクセル空間に解釈できるようになります。 (09:05)

// Temporal antialiasing and upscaling (initialization)

let desc = MTLFXTemporalScalerDescriptor()
desc.inputWidth = 1280
desc.inputHeight = 720
desc.outputWidth = 2560
desc.outputHeight = 1440
desc.colorTextureFormat = .rgba16Float
desc.depthTextureFormat = .depth32Float
desc.motionTextureFormat = .rg16Float
desc.outputTextureFormat = .rgba16Float

temporalScaler = desc.makeTemporalScaler(device: mtlDevice)
temporalScaler.motionVectorScale = CGPoint(x: 1280, y: 720)

キーポイント:

  • MTLFXTemporalScalerDescriptor()時間スキームの説明オブジェクトを作成します。 -inputWidthそしてinputHeight内部レンダリング サイズです。 -outputWidthそしてoutputHeightMetalFX の出力サイズです。 -colorTextureFormatジッターのあるカラー入力に対応します。 -depthTextureFormat現在のフレームの深度入力に対応します。 -motionTextureFormat現在のフレームのモーション入力に対応します。 -outputTextureFormatアップスケール出力に対応します。 -makeTemporalScaler(device:)テンポラル スケーラーを作成します。 -motionVectorScaleMetalFX が期待するピクセル空間に合わせてアプリのモーション データをスケーリングするために使用されます。

各フレーム呼び出しには、空間ソリューションよりもさらにいくつかの状態があります。resetHistory最初のフレームまたはシーン カットで true に設定します。reversedDepth深さに逆Zを使用するかどうかを宣言します。jitterOffset現在のフレームのサンプリング オフセットを渡します。 (10:35)

// Temporal antialiasing and upscaling (per frame)

// Encode Metal commands to draw game frame here...

// Setup per frame effect properties
temporalScaler.resetHistory = firstFrameOrSceneCut
temporalScaler.colorTexture = currentFrameColor
temporalScaler.depthTexture = currentFrameDepth
temporalScaler.motionTexture = currentFrameMotion
temporalScaler.outputTexture = currentFrameUpscaledColor
temporalScaler.reversedDepth = reversedDepth
temporalScaler.jitterOffset = currentFrameJitterOffset

// Encode scaling effect into commandBuffer
temporalScaler.encode(commandBuffer: cmdBuffer)

// Encode Metal commands for post processing/game UI drawing for frame here...

キーポイント:

  • resetHistory明確な過去のフレームの蓄積。最初のフレームとレンズの切り替えに適用されます。 -colorTexture現在のフレームのジッターのあるカラー入力です。 -depthTexture前景、背景、エッジを判断するためのヒントを提供します。 -motionTexture現在のフレーム位置から前のフレーム位置までのモーション情報を提供します。 -outputTextureMetalFX によって書き込まれた高解像度の結果を受け取ります。 -reversedDepthMetalFX に深度値に逆 Z マッピングを使用するかどうかを指示します。 -jitterOffset現在のフレームのサブピクセル サンプリング オフセットを渡します。 -encode(commandBuffer:)テンポラル AA のエンコードとアップスケーリング。

ジッター オフセット: 静的なオブジェクトはドリフトできません

時間的ソリューションの品質は、ジッター オフセットに大きく依存します。 session によって与えられた例では、ピクセルの中心が位置します。(0.5, 0.5)、実際のサンプリング ポイントは次の場所にあります。(0.625, 0.78)、対応するジッター オフセットは次のとおりです。(-0.125, -0.28)。 Apple は、ジッター オフセットは常に-0.5到着0.5範囲内です。 (11:11)

// Conceptual jitter validation based on the session example.
let pixelCenter = SIMD2<Float>(0.5, 0.5)
let sampleLocation = SIMD2<Float>(0.625, 0.78)
let currentFrameJitterOffset = pixelCenter - sampleLocation

temporalScaler.jitterOffset = CGPoint(
    x: CGFloat(currentFrameJitterOffset.x),
    y: CGFloat(currentFrameJitterOffset.y)
)

キーポイント:

  • この段落は、セッションにおけるジッター オフセットの計算関係を表現するために使用される概念的な例です。 -pixelCenterピクセルの中心を表します。 -sampleLocation現在のフレームの実際のサンプリング ポイントを示します。 -pixelCenter - sampleLocationMetalFX に渡されるオフセットを取得します。 -temporalScaler.jitterOffset現在のフレームをレンダリングするときに使用されるサンプリング オフセットと一致している必要があります。

検証方法も非常に簡単です。さまざまなジッター オフセットのセットを使用して、カメラやオブジェクトの動きのない静的なシーンをレンダリングします。オフセットが間違っていると、静的なオブジェクトが移動し、細い線がぼやけてしまいます。オフセットが正しい場合、オブジェクトはその場で停止し、細い線は徐々に解決されます。 (11:40)

Apple はジッター シーケンスの提案も提供しています。 2x アップスケーリングの場合、出力ピクセルあたり 8 サンプルにほぼ等しい、Halton (2,3) シーケンスの 32 ジッターを使用することをお勧めします。 (13:36)

ミップバイアス: 安定性のために明瞭さをトレードする

どちらの MetalFX ソリューションでも、低解像度でレンダリングするときにより詳細なテクスチャをキャプチャするには、負のミップ バイアスを設定することをお勧めします。空間スキームに推奨される式は次のとおりです。log2(renderResolutionWidth / targetResolutionWidth); 各次元を 2 倍に拡大すると、ミップ バイアスは次のようになります。-1。(12:53

// Conceptual mip-bias calculation from the session guidance.
let spatialMipBias = log2(renderResolutionWidth / targetResolutionWidth)
let temporalMipBias = log2(renderResolutionWidth / targetResolutionWidth) - 1

キーポイント:

・本項はセッションごとに与えられる計算ルールを表現した概念的な例です。 -spatialMipBias空間アップスケーリングに対応。 -temporalMipBiasTemporal AAとUpscalingに対応。

  • 各次元を 2 倍に拡大すると、空間結果は次のようになります。-1、一時的な結果は次のようになります。-2
  • 高周波テクスチャは 1 つずつ調整する必要があります。ミップレベルが低すぎるとちらつきやモアレが発生する場合があります。

時間スキームで推奨される式は、空間式から 1 を減算することです。セッションではボードのテクスチャが表示されます。-2最も鮮明ですが、高周波の細い線にはちらつきやモアレが発生します。-1これらの問題を軽減します。0それらは排除することができます。結論としては、推奨値を出発点として使用し、テクスチャの内容に応じて調整することです。 (14:15)

誤った依存関係: リソース バインディングによって独立したパスの速度が低下しないようにする

MetalFX のパフォーマンスは、誤った依存関係によっても影響を受けます。セッションの例としては、シャドウ パスとポスト処理パスに依存関係がなく、フレーム間で実行を重複させることができます。 2 つのパスが同じバッファーの読み取りと書き込みを行う場合、Metal は潜在的な危険を回避するために並列処理を防止します。 (16:00)

MetalFX シーンでは、このクロスフレームの誤った依存関係には、待機パスへの MetalFX アップスケーリングの実行時間が含まれます。 Apple が提供した修正は、後処理とシャドウ パスに別個のバッファを使用し、独立したパスで並列実行を再開できるようにすることです。 (17:01)

// Conceptual resource layout for avoiding false dependencies.
let postProcessingBuffer = makePostProcessingBuffer()
let shadowBuffer = makeShadowBuffer()

encodePostProcessingPass(using: postProcessingBuffer)
encodeShadowPass(using: shadowBuffer)

キーポイント:

  • この段落は、セッション内のリソース分割のアイデアを表現するために使用される概念的な例です。 -postProcessingBufferサービス後処理のみパスします。 -shadowBufferシャドウパスのみの提供となります。
  • 2 つの独立したパスが同じリソースの読み取りと書き込みを行わなくなり、GPU が実行をオーバーラップする可能性が高くなります。
  • 誤った依存関係のチェックは、MetalFX にプラグインした後のパフォーマンス検証ステップとして行う必要があります。

重要ポイント

  • 解像度スライダーを作成: プレーヤーが内部レンダリング解像度とターゲット解像度を切り替えられるようにします。実行する価値がある理由: MetalFX の基本的な価値は、低解像度のレンダリングと高解像度の出力です。開始方法: まずログインしますMTLFXSpatialScalerDescriptor、1280×720を2560×1440に拡大し、フレーム時間と画質の変化を観察します。

  • すでに時間的 AA を備えたエンジンに空間モードを追加します: 既存の AA を保持し、トーン マッピングの後に MetalFX 空間アップスケーリングを配置します。実行する価値がある理由: 空間ソリューションにはアンチエイリアス処理されたカラー入力のみが必要であり、アクセス コストが低いです。開始方法: 既存のカラー テクスチャを再利用し、設定しますcolorProcessingMode = .perceptual、フレームごとに呼び出されますspatialScaler.encode(commandBuffer:)

  • ジッター検証シーンを作成: 静的な細い線、チェッカーボード、または三角形をレンダリングし、一連のジッター オフセットをループします。実行する価値がある理由: セッションは、オフセット エラーにより静的なオブジェクトがドリフトしたり、細い線がぼやけたりする原因となることを明確に指摘しています。開始方法: カメラとオブジェクトのモーションをオフにして、temporalScaler.jitterOffset投影行列ジッターと同期してデバッグ HUD に記録します。

  • マテリアルのミップ バイアス例外テーブルを作成: デフォルトでは、負のミップ バイアスは MetalFX 推奨の式に従って設定され、高周波テクスチャが個別にコールバックされます。実行する価値がある理由: ミップ レベルが低いとディテールが向上しますが、回路基板などの高周波パターンではちらつきやモアレが発生する可能性もあります。開始方法: ミップ バイアス オーバーライド フィールドをマテリアルに追加し、最初にヘアライン、グリッド、布のテクスチャを確認します。

  • フレーム間依存性チェック パスを追加: 隣接するフレーム内の独立したパスのリソース読み取りおよび書き込み関係を記録します。実行する価値がある理由: false の依存関係により、シャドウ、後処理、MetalFX などの作業が重複するのを防ぎます。開始方法: 最も重いレンダリング/コンピューティング パスから開始し、独立している必要があるがバッファを共有するパスを見つけます。

関連セッション

コメント

GitHub Issues · utterances