Fadis
banner
fadis0.bsky.social
Fadis
@fadis0.bsky.social
25 followers 11 following 230 posts
組み込みからHPCまで、計算機が好きだ。 本: https://techbookfest.org/organization/5465190594248704 丼 : @[email protected] X: @fadis_ み2: @fadis 発表 : https://speakerdeck.com/fadis 動画 : https://www.youtube.com/@fadis_ 芋: https://www.amazon.co.jp/hz/wishlist/ls/2H9VDVP96XBXV/
Posts Media Videos Starter Packs
11月15日から始まる #技術書典 19 で FADIS PRESS は新刊「もふもふの描き方」を頒布する予定です。この本では毛がびっしり生えた物体をリアルタイムレンダリングで描く手法を解説します。頒布価格は500円、PDF版のみ、オンラインのみの出展になります techbookfest.org/organization...
今回KosmicKrispがVulkan 1.3の規格を完全に満たした事で、Vulkan 1.3で標準でサポートされている機能の範囲で書かれたアプリケーションであれば、コードを変更せずにAppleのデバイスにそのまま持っていけるようになった。KosmicKrispを動かすにはmacOS 15以上が必要でiOSはサポートされていない
時が流れてMetalでも比較的新しいバージョンのmacOSではVulkanが標準で要求しているのと同等の機能が一通り備わるようになった。そこで要求するOSのバージョンを引き上げてVulkanの規格を完全に満たすように新しい変換レイヤーを実装するKosmicKrispの開発が始まった
これは辛いので、Appleのデバイス向けのコードをVulkanで書けるように、VulkanをMetalに翻訳する変換レイヤーMoltenVKが開発された。ただMoltenVKが作られた当時のMetalはVulkanが標準で備えているいくつかの機能を欠いていた為Vulkanの規格を完全に満たす事ができなかった。
MoltenVKを使う場合Appleのデバイスで動かない機能を踏まないように注意してVulkanのコードを書く必要があり、クロスプラットフォーム辛い問題を完全に解決する事はできなかった
GPUを使うソフトウェアを作る為のランタイムVulkanは多様なプラットフォームでサポートされていて、GPUを使うアプリケーションをクロスプラットフォームで開発したい場合に最も適した選択肢になっている。
ただAppleがAppleのデバイス向けに用意するGPUのドライバはMetalのAPIしか生えていない為、クロスプラットフォームの対象にAppleのデバイスが含まれている場合、Appleのデバイス用のコードだけ専用に書き直す必要があった。
LunarG Achieves Vulkan 1.3 Conformance with KosmicKrisp on Apple Silicon : KosmicKrispがVulkan 1.3の規格を満たしたというプレスリリース。KosmicKrispはMacでVulkanを使うソフトウェアを動かす為のドライバ
www.lunarg.com/lunarg-achie...
LunarG Achieves Vulkan 1.3 Conformance with KosmicKrisp on Apple Silicon - LunarG
KosmicKrisp, LunarG’s Vulkan-to-Metal driver for Apple Silicon, has passed the Vulkan Conformance Test Suite (CTS), a rigorous, Khronos-mandated benchmark of API correctness.
www.lunarg.com
今回Vulkanに追加された拡張はGDEFLATEの展開を自前のシェーダーで書かなくても専用のコマンド一発で行えるようにする。厳密には以前からNVIDIAがベンダ拡張として提供していた物がマルチベンダ拡張になった物のようなので、NVIDIA以外のGPUでも使えるようになるのではないか、という期待が持てる。
専用のコマンドになったという事はGPU側は圧縮されたデータの展開を汎用の演算器ではなく専用のハードウェアで行っても良いという事になる。GDEFLATEは専用のハードウェアが無くても展開できるように作られた形式だが、今後もっと手の込んだ圧縮アルゴリズムが追加されるのかもしれない
GDEFLATEでは圧縮対象を64KiBのページ単位で分割して圧縮する。1ページを1つのSubgroupが展開し、複数のSubgroupを使って圧縮されたデータを展開する。NVIDIAはこの方法で3.5GB/s程度しか出ないPCIe Gen3のSSDから12GB/sでデータを読めたとしている developer.nvidia.com/blog/acceler...
Accelerating Load Times for DirectX Games and Apps with GDeflate for DirectStorage | NVIDIA Technical Blog
Load times. They are the bane of any developer trying to construct a seamless experience. Trying to hide loading in a game by forcing a player to shimmy through narrow passages or take extremely slow…
developer.nvidia.com
以前読んだビット列のバッファはストリーム毎に独立して持ち、他のストリームで既に出てきたビット列だったとしても自身のストリームで初登場のビット列は新出としてエンコードされる。この結果GDEFLATEはすっぴんのLZSSより少し圧縮率が悪くなる。
GDEFLATEの32本のストリームは32スレッドのSubgroupで処理する事を意図している。個々のスレッドが個々のストリームからトークンを1つ処理し、Subgroup演算で展開結果を書き込むオフセットを求める
通常のLZSSはビット列を読んで、それが以前読んだビット列と同じなら以前出現した位置と長さに再登場を表す最上位ビット1を付けて出力する。新出のビット列なら最上位ビット0を付けてビット列をそのまま記録する。同じパターンが何度も現れていると再登場で表現できる部分が増えて圧縮率が上がる。
GDEFLATEはこのLZSSのトークンを32本のストリームに分けて記録する。展開時には32本のストリームがトークンを1つ展開する度に展開結果がストリームのID順にシリアライズされるので、そのように展開して正しい並び順でデータが展開されるように32本のストリームに順番にトークンを追加する
GPU側でのデータの展開はPCI-Expressの帯域を使い切る勢いで圧縮されたデータが飛んできたとしてもボトルネックにならないくらいの速さで行える必要がある。従ってGPU上で沢山のスレッドで並列で展開してスケールするような形式で圧縮されている必要がある。
既存の可逆圧縮アルゴリズムの多くはシーケンシャルに展開する前提で作られていてスケーラビリティに難があった。2022年にNVIDIAが発表したGDEFLATEはLZSSをベースにGPU上で高速に展開できるように工夫した可逆圧縮形式で、MicrosoftのDirectStorage 1.1と組み合わせて利用可能だった
GPUの計算能力に対してPCI-Express x16の帯域は非常に細い為、現代のGPUの性能を引き出すにはいかにPCI-Expressにでかいデータを流さずに計算をするかが重要になっている。データをストレージから読む必要がある場合NVMe SSDはPCI-Express x4に繋がっている為この問題はより深刻になる。
この為GPUに送る必要があるデータの中でも大きくなりがちな画像に関しては非可逆圧縮をかけた状態でPCI-Expressに流し、GPU側で展開する圧縮テクスチャが古くから用いられてきた。ただこの手法は非可逆なのでデータが少しくらい化けても問題ないケースにしか適用できない
Vulkan 1.4.330で仕様に記載が追加された VK_EXT_memory_decompression 拡張について。この拡張はGPUからアクセス可能なメモリに置かれた可逆圧縮されたデータを展開するコマンドを追加する。今の所具体的な圧縮形式としてGDEFLATEがサポートされている
docs.vulkan.org/refpages/lat...
VK_NV_memory_decompression(3) :: Vulkan Documentation Project
docs.vulkan.org
Linux 6.18ではsuccess_hookコールバックが追加された。このコールバックはvm_area_structがプロセスの仮想メモリに追加された後で、双方向リストの中にあるvm_area_structがconstで渡ってくる。/dev/zero等一部のドライバがこの仕組みが無いとmmap_prepareに移行できないらしい
openコールバックでメモリをマップしてvm_area_structに必要な情報を書いて返すと、プロセスの仮想メモリへの追加が試みられる。追加が失敗するか、munmapされるとcloseコールバックが呼ばれる。他のvm_area_structへの操作はできず、メモリマネージャが知らない所での書き換えもできない
mmap_prepareはプロセスの仮想メモリに実際にvm_area_structを追加する前に呼ばれる。引数のvm_area_descに含まれるvm_operations_structにopenコールバックをセットしておくとvm_area_structが渡ってくるが、この時点でこのvm_area_structはプロセスの仮想メモリに追加されていない
そして、mmapが失敗した時に正しく仮想メモリの状態を元に戻せていなかった事に起因する脆弱性が見つかった。これを受けてLinux 6.17ではfile_operationsに自由すぎるmmapコールバックを置き換える新しいコールバックmmap_prepareが追加された
bugzilla.redhat.com/show_bug.cgi...
2328791 – (CVE-2024-53096) CVE-2024-53096 kernel: mm: resolve faulty mmap_region() error path behaviour
bugzilla.redhat.com
コールバックが失敗を返した場合、Linuxのメモリマネージャはコールバックに渡していたvm_area_structをリストから外し、プロセスの仮想メモリの状態をmmapを試みる前の状態に戻さなければならない。
ここで厄介なのがvm_area_structを触れるmmapのコールバックは、渡ってきたvm_area_structに限らずそのプロセスのvm_area_structのリスト全体を書き換え可能である、という点。この結果メモリマネージャ側が何をmmapしているのか把握して処理を切り替えないと正しく状態を戻せなくなっている
file_operationsにはファイルに対してmmapが要求された時に呼ばれるコールバックmmapが用意されている。Linuxではプロセスに割り当てたメモリはvm_area_struct構造体の双方向リストで管理される。mmapが要求されるとまず新しいvm_area_structがリストに追加され、それがコールバックに渡ってくる。
コールバックはプロセスが要求されたファイルの内容にアクセスできるように必要な操作を行った上で、ファイルの内容をマップした領域の情報を渡ってきたvm_area_structに記録する事が求められている
Linuxカーネルではユーザ空間プロセスがファイルを操作するとfile_operations構造体にセットされたカーネル空間のコールバックが実行される。この構造体のインスタンスはデバイスファイルであればそれを生やしたデバイスドライバが持ち、それ以外のファイルであればファイルシステムのドライバが持つ
The phaseout of the mmap() file operation : Linuxカーネルのfile_operations構造体のコールバックmmapの廃止に向けた取り組みの話。このコールバックは何でも出来すぎて脆弱性の源になっているので、もう少し制限されたコールバックに置き換えようという物
lwn.net/Articles/103...
The phaseout of the mmap() file operation
The file_operations structure in the kernel is a set of function pointers implementing, as the [...]
lwn.net
またアニメーションが滑らかな場合キーフレーム間の頂点座標の変化は限られた大きさになる筈なので、指数部とオフセットを共有しても品質を著しく落とす事無く頂点座標を記録できる。結果としてこの手法は2つの独立したメッシュより効率よくアニメーションを記録する事ができる。

頂点モーフアニメーションはリグを使ったアニメーションと比べてマイナーな手法ではあるが、テッセレーションを使わないメッシュシェーダーにおいてはLoDのポッピングを目立たなくする為に多用するテクニックな為、この改良が役に立つ状況は少なくない
記事では1つのDGFブロックに対して複数の頂点配列部を結び付けられるようにする拡張を提案している。アニメーションの全てのキーフレームを記録できるように頂点配列の指数部とオフセットの値を決め, 仮数部の配列だけを複数用意する。

アニメーションのキーフレーム間ではトポロジは変化しない為頂点インデックスは同じになる。そこで記事の手法では全てのキーフレームで頂点インデックスを共有する
頂点インデックスはリスタート用の2bitの制御ビットが付いたtriangle stripを使ってできるだけ同じ頂点を何度も指さないように記録する。メッシュレット内の最大頂点数は64で6bitあれば全ての頂点を指せる為頂点インデックスの1要素は8bit、1つの三角形は24bitで表現される。

頂点数が同じメッシュが2つある時、両者の頂点を一対一で対応付け、双方の座標を線形補間する事で一方の形から他方の形への変形のアニメーションを行う手法を頂点モーフアニメーションと呼ぶ。線形補間で滑らかにアニメーションするためには2つのメッシュのトポロジは一致していなければならない
メッシュシェーダーの登場によって3Dシーンの頂点は任意のデータ構造から読めるようになった。今日のGPUは計算の性能に対してメモリの帯域が不足しがちな為、シェーダーで圧縮された頂点配列を展開する事でグローバルメモリから読むデータの量を抑えると性能向上が狙える。
そのような頂点配列の新しいフォーマットとしてAMDが提案しているのがDense Geometry Format略してDGF。DGFは128バイトのブロックに1メッシュレット分の頂点座標と頂点インデックスを記録する。頂点座標は共通のオフセットと浮動小数点数の指数部をヘッダに記録し、個々の頂点は仮数部だけを持つ