📝

WebGLのレンダリングフローを理解する【OS編】

に公開

執筆理由

こんにちは、フロントエンドのyです。

WebGL難しいですね...。
glslは1行のコードを解読するのに数時間かかることもあります。
JavaScriptの処理は直列で実行されるのに対して、glslは並列で実行される言語なので頂点やフラグメントが一斉に動きます。
JavaScriptの直列の処理、glslの並列の処理の2つを考慮して実装にあたらなければなりません。
それに加えて内積、外積などの線形代数の理解も必須です。
骨が折れるとはこのことですね...

これらを抑えた実装者はそう多くはいないのではないでしょうか。
それゆえWebGLは他の実装者との大きな差別化となり、実装者としての市場価値を大きく高めると思います。

私自身が学習初期に理解できなかったのが、「JavaScriptから直接GPUを動かす言語を扱えない」だとか、「GPUを扱うのにはWebGL APIを叩かなければならない」だとか、「そのためにはOSを経由させる」などです。
下図のような図を学習中に見たことがあるのではないでしょうか。

OSやCPU、GPUなどのハードウェアについてはなんとなくは分かるのですが、説明できるくらい理解できていませんでした...

実際のところThreeを使っている学習レベルのエフェクトや既存のエフェクトは理解できます。ただし、glslの複雑なコードを理解したり、オリジナルのいいものを実装するためにはより深い部分でどうなっているのかを理解する必要があると思います。1つでも理解できていないところがあると後からボディブローのように効いてきて理解が追いつかなくなります。

回り道にはなりますが今後のことも考えて根本的な部分であるOSの基本から調べていこうと思います。
理解していない部分を地道に1つ1つ潰していけば理解は積み重なり、大きな成果へとつながります。

この記事ではOSの基本からWebGLの描画のフローまで書いて行こうと思います。
図はとても簡易的に作ってます、ご了承ください...。

数本の記事になるとは思いますが頑張ります!

当初のOSの役割についてのイメージ

下記の画像が私のOSの役割のイメージです。
PCのどこかにOSいう領域があり、ユーザーがアプリを起動したり、サイトを開いたりなどをすると、それをシェルを仲介させてカーネルに通知する。そしてCPUやGPUに依頼してディスプレイに描画する。
下図のようなイメージでした。

このようなイメージを持たれている方は多いのではないでしょうか。
細かく見ていこうと思います!

OSとは?

OS(Oparating System)とは、アプリケーションとハードウェアとの間に立ち、ハードウェアを管理してアプリが動く環境を提供する「ソフトウェア群」のことです。
OSとは単一のものではなくて、主にカーネル、シェル、ドライバ、システムユーティリティの4つで構成されていて、これらを総称してOSと呼んでいます。

カーネル

OSの中枢・心臓部分で、PCのハードウェア(CPU・メモリなど)を管理し、それをアプリが安全に使えるようにします。ユーザーが直接ハードウェアを操作するのは危険なので、カーネルが「仲介役」をしています。

シェル

ユーザーとカーネルをつなぐ操作窓口で、ユーザーが入力したコマンドや操作をカーネルに伝える役目。
文字ベースで操作するコマンドライン(CLI)のシェルと、マウスやタッチで操作するグラフィカルシェル(GUI)があります。
古くからあるシェルとしてsh、csh、tcshなどがあり、広く使われているシェルとしてbash、zsh、kshなどがあります。

ドライバ

カーネルとハードウェアとの仲介を行うソフトウェアです。
ハードウェアを動かすための翻訳ソフトの役割を担います。
カーネルがハードウェアを直接制御はできないのでドライバがカーネルからの命令(低レベルな形式=バイナリや機械語に近い形式)をわかりやすくハードウェアに通訳します。
(ただし、CPU、メモリにはドライバが存在しません。)

システムユーティリティ

OSの機能を補助したり、ユーザーがOSを便利に使うためのツール群のことです。
ターミナル、タスクマネジャーの他に、ファイルをコピー・削除するツール、ネットワーク設定ツールなどがあります。
カーネルやシェルに比べると必須ではないが、これがないとPCを扱うにあたって不便になります。

ざっとまとめてみると...
カーネル:OSの中核・心臓。会社の社長・経営幹部
シェル:受付、秘書・フロントデスク・窓口担当
ドライバ:現場の専門マネージャー/通訳
システムユーティリティ:総務・庶務・サポート部門

後述しますが、これらはPCの1箇所にあるわけではなくてPC全体を蜘蛛の巣のように張り巡らせて機能しています。

OSの構成要素の動きについてアプリを起動させてからハードウェアに行き着くまでの簡単なフローを確認してみましょう!

アプリ起動からハードウェア起動までのフロー

ここでは、アプリケーション(GUI)をクリック、またはターミナル(CLI)にコマンドを入力してからハードウェアに指示が行き届くまでを見ていきます。

図を見ての通りですが、ユーザー空間・カーネル空間・ハードウェア層 の3つで成り立っています。
3層になっている理由はユーザーが直接ハードウェアを操作するのを防ぐためや、他に効率性の観点からです。

✅ ユーザー空間 … アプリやソフトが動く場所です。私たちが直接触れるのはここで、ゲームやブラウザ、エディタなどはすべてユーザー空間で動いています。ユーザーは、ハードウェアには直接アクセスできないようになっていますね。

✅ カーネル空間 … ユーザーのリクエストを受け取り、ハードウェアに正しく伝える役目を担っています。OSの中核部分であり、ユーザー空間とハードウェア層の橋渡しをしています。

✅ ハードウェア層 … CPU、メモリ、ディスク、ディスプレイなど、実際に動く機械の部分です。

この仕組みでは、ユーザーは直接ハードウェアを操作するのではなく、必ずカーネルを経由して命令を出します。例えば、「ファイルを保存する」ときは、アプリがカーネルに保存を依頼し、カーネルがストレージに正しい信号を送ることで実際に書き込みが行われていきます。

つまり、ユーザー → カーネル → ハードウェアという流れで命令が伝わり、最終的には結果がユーザーに返ってくる仕組みになっています。

次に、カーネルからハードウェアに指示が届いてからからユーザーに結果が返ってくる部分をみていきましょう!

アプリ起動からユーザーに結果が返ってくるまでの流れ

アプリケーションはなんでもいいですが、とりあえずメモ帳アプリとします。
ハードウェアは多くあるのですが、メインのハードウェアであるCPU、GPU、メモリを例に取ります。

① ユーザーがメモ帳アプリをクリック(GUI)
ユーザーの操作は、シェルを通じてカーネルに伝わります。
② シェルを通じて、カーネルに通知がいく
カーネルに「こうしてほしい」とお願いするだけです。
③ カーネルはメモ帳アプリの実行ファイルをメモリに展開。
④ CPUに命令を送り、「このプログラムを実行せよ」と指示を送る
⑤ メモ帳アプリが起動し実行し始める
⑥ GPUに命令し、メモ帳のウィンドウを表示。

このときにはドライバが仲介する
⑦ 映像信号をディスプレイに送信、表示させる

このように、ユーザーのクリックという小さな操作が、カーネルを経由してCPU・メモリ・GPUといったハードウェアに伝わりそれぞれが役割を果たします。最終的に画面上に「メモ帳アプリのウィンドウが開く」という形で結果が返ってくる仕組みとなっています。

OSとはユーザーとハードウェアとが対話できるようにすることが役割だということがわかりますね。

まとめ

PC上で、アプリケーションは複数同時に起動します。そのため、ユーザーとハードウェアの間でOSが絶えず働き、各アプリの動作を管理しています。先にも言いましたが、まるでPC全体に蜘蛛の巣を張り巡らせ、全てのアプリの処理を見張っています。

以下は初めに私が誤って認識していたことや気づきです。

① PCのどこかにOSいう領域がある
👉 PCのどこかにOSがあるのではなくて、カーネルを起点としてシェル、ドライバ、システムユーティリティなどの構成要素がPC全体を張り巡らせているイメージですね。

② OSが立ち上がるとは?
👉 ユーザーとハードウェアとが対話できる状態になるということ

③ なぜJavaScriptコードでは直接GPUを扱うことができないのか?
私が学習初期に抱いていた疑問ですね。これは、JavaScriptのコードからGPUを動かすまでの間にブラウザ、カーネル、ドライバなどを介する必要があり、それぞれの部門では使用言語が違うからです。
ですからそこでAPIを噛ませてGPUにまで伝達させる必要があります。

以下でそれぞれのステージで実装されている言語、そこでの処理を記述しました。

✅ ステージ① JavaScriptコード
実装言語: JavaScript
処理: WebGL APIを呼び出す(gl.drawArraysなど)

👉 そのままの形でブラウザに渡す

✅ ステージ② ブラウザ
実装言語: C、C++
処理: WebGL APIを、OS(カーネル)のグラフィックスAPI(OpenGL, DirectX, Metal, Vulkanなど)に変換する

👉 カーネルにグラフィクスAPIを渡す

✅ ステージ③ カーネル
実装言語: C、C++
処理: グラフィックスAPIを受け取り、必要に応じて権限管理や安全チェックを行う。

👉 安全にGPUドライバに渡す形式(コマンドバッファなど)に変換して、GPUドライバに渡す

✅ ステージ④ GPUドライバ
実装言語: C / C++
処理: グラフィクスAPI命令の翻訳、シェーダのコンパイル(0、1のバイナリコード)、メモリ管理などを行う。

👉 最終的にバイナリ形式(0、1の2進数の形式)でGPUに渡す。

✅ ステージ⑤ GPU
実装言語: 0と1の組み合わせで書かれた機械語(マシンコード)
処理: glsl言語(バイナリに変換されている)を実行します。頂点処理やピクセル処理がGPU内部で並列実行され画面へ描画していきます。

このように、JavaScriptのコードがユーザーの操作から始まり、ブラウザ、カーネル、GPUドライバを経てGPUで描画されるまでには、それぞれのステージで異なる言語で構成され処理が行われていることが分かります。
各ステージが連携することで、私たちは直接GPUを意識することなく、WebGLを使ったリアルタイム描画や高度なグラフィックス表現を安全かつ効率的に行うことができます。

長かったですね...。
次の記事では、この部分とJavaScriptファイル、WebGL APIを兼ねての処理の流れを追ってみようと思います。

個人的にはOSの体系的な理解はできたのではないかと思います。
記事は随時更新していきたいと思います。
読んでいただきありがとうございました!

補足

補足ですが、PCを起動させた時にOSがどう立ち上がるのか気になったので調べました。
あくまで補足ですので理解がつながらない部分があれば適宜調べてください。
あくまで参考までに...

PC電源ONからOSが起動するまでの流れ

PCを起動すると、デスクトップ画面が表示される前に、内部ではさまざまな処理が順序立てて行われています。
まず電源を入れると、マザーボードに組み込まれたBIOS/UEFIのプログラムが立ち上がり、ハードウェアの自己診断や初期化を行います。その後、OSを起動するためのブートローダーが読み込まれ、カーネルに制御が渡されます。そしてメモリやハードウェアの初期化、シェルが起動していく流れになります。

下図では、PCの電源ONからユーザーが操作できる状態になるまでの主要な処理の流れを整理しています。

図で示したように、OSの起動は単なる「画面が出るまでの時間」ではなく、ハードウェアの初期化、メモリの管理、ドライバのロード、カーネル空間からユーザー空間への構築、シェルやGUIの起動といった複数の処理が順番に行われています。
この一連の処理によって、ユーザーは安心してアプリケーションを操作できる状態が整っていきます。

今回紹介した処理は一部分であり、他にも数多く処理があります。
日常的に何気なく行っているPCの起動も、その裏側には膨大な準備と仕組みが隠れているのだと意識すると、OSの役割がより鮮明に見えていくと思います。

以上補足でした。
次回以降の記事も引き続きよろしくお願いします!

Discussion