- ディスプレイの画面は、まずGPUはウィンドウの中身を「サーフェス」と呼ばれる画像の置き場に描き、OSのコンポジターがそれを重ねて1枚の画面にしています。
- 特に、ウィンドウの大きさが変わるとき、サーフェスは作り直されます。
- このとき、録画ソフトが古いサーフェスを見続けると、画面は動いているのに録画だけが止まる、という不具合が発生します。
- GPUを使うと切り替えに対応できない、というわけではありませんが、録画する側が、サーフェスの入れ替わりやサイズの変化を丁寧に追いかける設計になっているかどうかの問題です。
- サーフェスのサイズ変更そのものはCPUで描いても起きますが、ハードウェアアクセラレーションをオフにするとサイズ変更に関わるGPU側の部品と経路が減るため、不具合が出にくいのです。
1. GPUはまず「サーフェス」に描く
画面録画をしていて、ウィンドウの大きさを変えた瞬間に録画が止まったり、真っ黒になったりすることがあります。
このときの対処法でよく出てくるのが「ハードウェアアクセラレーションをオフにする」という設定です1。
たとえば、Chromeなら、「設定」の「システム」にある「グラフィック アクセラレーションが使用可能な場合は使用する」を切ります2。
では、なぜGPUを切ると直るのか。
これを理解するには、GPUが画面をどう描いているかを知る必要があります。
1.1. サーフェスは、ピクセルを並べた画像の置き場
1920×1080の画面は、約207万個のピクセル(画素)で構成されます。
それぞれのピクセルについて、赤・緑・青の強さと透明度が決まって、1枚の画像になります。
CPUだけで画像を作るなら、メモリ上に単純な配列を用意すれば済みます。
struct Pixel {
uint8_t r, g, b, a;
};
Pixel image[1920 * 1080];
image[y * 1920 + x] = {255, 0, 0, 255}; // (x, y) を赤にするCode language: JavaScript (javascript)
GPUでも、この「画像データを置いておく場所」という発想は同じです。
違うのは、置き場がGPUの扱いやすい形式で、GPU側のメモリに用意されること。
この置き場を「サーフェス」(Surface)と呼びます。

厳密には、APIによってテクスチャ(Texture)、レンダーターゲット(Render Target)、フレームバッファ(Framebuffer)など別の名前が付いています。
「サーフェス」は、それらをまとめて「描画の対象になる画像の領域」という意味で使われることが多い言葉です。
たとえばChromeのウィンドウが800×600だとすると、Chromeは800×600のサーフェスを用意して、そこに背景、文字、画像、ボタンを順に描いていきます。
Surface surface = createSurface(800, 600);
gpu.setRenderTarget(surface);
drawBackground();
drawText();
drawImages();
drawButtons();
ここで大事なのは、GPUはモニターに直接表示しているわけではなく、まずサーフェスという1枚の画像を完成させている、ということです。
1.2. ウィンドウを動かしても、描き直しはいらない
ディスプレイ画面には、Chromeのほかに、Zoomのウィンドウや、デスクトップ、マウスカーソルも映っています。
これらを1枚の最終画面にまとめるのが、OSのコンポジター(Compositor:合成器)です。
Windowsでは、DWM(Desktop Window Manager:デスクトップ ウィンドウ マネージャー)がこの役を担っています3。
つまり、全アプリが1枚の巨大な画像を直接書き換えているのではありません。
ChromeはChrome専用のサーフェスに、ZoomはZoom専用のサーフェスに描き、それをOSが最後に重ねています。
この仕組みのおかげで、たとえばウィンドウを右に100ピクセル動かしても、中身を描き直す必要がありません。
コンポジターに「このサーフェスを、この位置に置いてください」と伝えるだけで済むのです。
compositor.draw(chromeSurface, x = 300, y = 100);
画像を動かす、拡大する、半透明にする、重ねる。
GPUはこういう処理がとても得意です。
2. ウィンドウの大きさを変えると、サーフェスは作り直される

ただ、大きさを変えるとなると話が違います。
800×600のサーフェスに、1600×900の絵は入りません。
そこで新しい大きさのサーフェスを作って、古いほうを捨てることになります。
Surface oldSurface = surface;
surface = createSurface(1600, 900);
destroySurface(oldSurface);
ここに録画ソフトとの間に問題が出てくる温床があります。
2.1. 録画ソフトが古いサーフェスを見ていると、録画だけ止まる
録画ソフトが、Chromeのサーフェスから絵を受け取って録画しているとします。
録画を始めた時点で見ているのは、800×600のサーフェスAです。
その後、たとえばChromeのウィンドウを広げると、Chromeは1600×900のサーフェスBに描くようになります。
ところが録画ソフトが、まだサーフェスAを見ていたらどうなるか。
Chromeは新しいBに描き続けているのに、録画ソフトはもう更新されないAを見続けてしまいます。
これが「画面は動いているのに、録画だけ止まった」という現象につながります。
もちろん、今のOSの録画用APIには、大きさの変化に合わせて受け取り側を作り直す手段が用意されています4。
それでも、裏側ではこうした置き場の交換が起きていて、録画側がそれに付いていく必要がある、ということです。
2.2. ダブルバッファリングとスワップチェーンで、描き先は毎回入れ替わる
実は、GPUが使うサーフェスは1枚だけではありません。
画面に表示している最中の画像を書き換えると、上半分が新しい絵、下半分が古い絵、という状態が映ってしまいます。
いわゆる「ティアリング」(Tearing:画面の食い違い)です5。
こうしたチラつきを防ぐ仕組みが、ダブルバッファリング(Double Buffering:2枚の描画領域を交互に使う方法)です。
サーフェスAを表示している間に、GPUはサーフェスBに次の画面を描く。
Bが描き終わったら、Bを表示に回し、Aを次の描き先にする。
この交換をスワップ(Swap)と呼びます。
Direct3Dには、表示用のサーフェスを何枚かまとめて管理する「スワップチェーン」(Swap Chain)という仕組みがあります6。
描画のループは、概念的にはこうなります。
while (running) {
Surface& backBuffer = swapChain.getBackBuffer();
gpu.render(backBuffer);
swapChain.present(); // 描き終わった絵を表示側へ渡す
}Code language: JavaScript (javascript)
ここでウィンドウの大きさを変えると、このスワップチェーンが抱えているサーフェスを全部作り直すことがあります7。
古いAとBを捨てて、新しいCとDを用意する。
録画ソフトから見ると、アプリが描いた絵は、スワップチェーンを通ってコンポジターで重ねられ、そこから録画用のAPIを経てやっと届きます8。
思っていたよりずっと多段の構造で、サイズ変更はその途中の置き場を一斉に入れ替える出来事なのです。
3. ハードウェアアクセラレーションを切ると、合わせる部品が減る
ここで、最初の疑問に戻ります。

3.1. CPUで描いても、サイズ変更は起きる
ハードウェアアクセラレーションを切っても、ウィンドウの大きさが変われば描く先の大きさは変わる、これは同じです。
CPUで描く場合には、アプリはメモリ上に普通の画像を持って、そこに描いてからOSへ渡します。
Bitmap image(width, height);
drawWithCPU(image);
osPresent(image);
サイズが変わったときは、画像の大きさを変えて描き直ます。
image.resize(newWidth, newHeight);
drawWithCPU(image);Code language: CSS (css)
リサイズそのものは、GPUでもCPUでも起きるわけです。
ただ、リサイズのときに一緒に作り直したり、足並みをそろえたりしなければならないものの数に違いがあります。
GPUで描いている場合、サイズ変更のたびに、テクスチャ、サーフェス、スワップチェーン、それに描画の完了を知らせるための同期用の部品までまとめて作り直すことがあります。
ちなみに、CPUで描いている場合でも、描画領域の大きさを変えれば、あとはOSの標準の流れに乗ります。
その後OSがGPUで合成するので、GPUがまったく使われなくなるわけではありません。
アプリ自身が抱えるGPUの部品が減る、ということです。
3.2. GPU固有のサーフェス、オーバーレイ、非同期の3つが減る
ハードウェアアクセラレーションを切ると直る理由は、大きく3つあると考えられます。
1つめは、アプリ独自のGPUサーフェスが減ることです。
GPUで描くChromeは、自分のGPUサーフェスを持ち、それをコンポジターに渡します9。
切った場合は、CPUで描いた結果をコンポジターに渡す形に近くなります。
録画ソフトから見ると、後者のほうがOSの管理する標準的な経路に乗りやすいわけです。
2つめは、オーバーレイ(Overlay)のような特殊な表示経路が使われにくくなることです。
GPUを使っていると、たとえば動画の部分だけを、普通の合成とは別のサーフェスとして画面に重ねることがあります。
Windowsでは「マルチプレーン オーバーレイ」(Multiplane Overlay:複数の面をハードウェアで重ねる仕組み)と呼ばれるものです10。
録画側が普通のウィンドウの中身だけを見ていると、この別経路の部分が取れず、動画のところだけ黒くなったりします。
これもハードウェアアクセラレータを切ると、動画もCPUで描いた普通のウィンドウの画像に戻ることがあり、録画しやすくなります。
3つめは、非同期性が減ることです。
GPUは、CPUから「描いて」と頼まれても、すぐに描くとは限りません。
CPUは返事を待たずに先へ進み、GPUはあとで描きます11。
そのため、GPUが描いている途中でリサイズが起き、サーフェスが交換され、そこへ録画が割り込む、という出来事が重なると、どの時点のどの絵を取ればいいのかの管理が難しくなります。
CPUで描く場合は、リサイズして、描いて、取り込むという命令の順番と、実際に処理が終わる順番がだいたい一致します。
つまり、GPUアクセラレーションを切って直るのは、「リサイズが起きなくなる」という単純な話ではなく、リサイズのときに交換したり同期したりしなければならない、GPU固有の部品と経路が大きく減るから、と言えます。
4. 録画できないのではなく、追いかける設計が難しくなる
では、GPUを使うアプリは録画に向かないのか、というと、そういうことではありません。録画する側が正しく付いていくために扱わなければならない状態が増える、というのが近いです。
CPU中心の単純な経路なら、描いて、できた画像を取って、録画する、で済みやすい。
しかし、GPU中心だと、どのサーフェスに描いているのか、もう描き終わっているのか、サイズは変わっていないか、別のサーフェスに切り替わっていないか、を毎回確かめる必要があります。

4.1. 録画ソフトは毎回、サイズと完成を確かめている
うまく付いていく録画ソフトは、ざっくり言うとこういうループで動いています。
while (recording) {
Frame frame = captureSession.acquireNextFrame();
if (!frame.valid()) continue;
if (frame.sizeChanged()) {
captureBuffer.recreate(frame.width, frame.height);
encoder.recreate(frame.width, frame.height);
}
if (frame.formatChanged()) {
converter.recreate(frame.format);
}
encoder.encode(processFrame(frame));
}Code language: JavaScript (javascript)
ここでは、いくつかのことを同時に気にしています。
まず、描き終わる前の絵を読まないこと。
GPUに描画を頼んだ直後にサーフェスを読むと、まだ半分しか描けていないことがあるので、完了の知らせを待つか、OSが渡してくれる「完成したフレーム」を受け取る形にします。
次に、サイズや色の形式の変化に合わせて、録画用の置き場とエンコーダーを作り直すこと12。
ここの設計が精緻でないと、リサイズした瞬間に固まったり、真っ黒になったりするわけです。
それから、描画と録画の速さの違いを前提にすること。
描画が60fps、録画が30fpsというのは普通にあります。
全部の絵を取る必要はなく、その時点で完成している正しい絵を取れればいい。
ここを間違えると、古い絵を何度も録ったり、描きかけの絵を録ったり、遅れがどんどん増えたりします。
さらに、動画だけ別のサーフェスで重ねるような特殊な経路も考えに入れる必要があります。
普通のウィンドウの中身だけを見ていると、その部分が取れません。
4.2. 最初のサーフェスだけを見る実装は、リサイズに弱い
たとえば、不具合が起きるのはこういう処理の流れです。
Surface* target = getInitialSurface();
while (recording) {
Frame frame = capture(target);
encode(frame);
}Code language: JavaScript (javascript)
このコードでは録画を始めたときに取ったサーフェスを、ずっと見続けています。
ウィンドウの大きさを変える、全画面に切り替える、といった操作でサーフェスが作り直されると、もう更新されない古い置き場を録り続けることになります。
これが、最初に書いた「ウィンドウの大きさを変えた瞬間に録画が止まる」の正体に近いものだと思います13。
ハードウェアアクセラレーションを切るのは、録画側が付いていかなければならない部品を、アプリの側で減らしてあげる方法です。
描画は少し重くなりますが、録画のときだけ切って、終わったら戻す、という使い分けでもいいのかもしれません。
- OBS Studio のフォーラムには、Chromeで講義を録画すると映像が真っ黒になり、ハードウェアアクセラレーションを切ったら録れた、という2024年1月の相談があります。回答でも、切るか別のブラウザを使うのが解決策とされています。 – Screen Recording Woes: Black Screen Issue with Browser Hardware Acceleration|OBS Forums
- 場所は右上の「︙」から「設定」、「システム」です。解説記事では、オフにする場面の例として、画面のちらつきや動画のカクつきと並んで「黒い画面になる」が挙げられています。以前の解説では「ハードウェア アクセラレーション」の名前で紹介されていることが多いです。 – Chrome の高速化設定──グラフィックアクセラレーションの最適解をまとめる|AQlier
- Windows Vista で入ったデスクトップコンポジションでは、各ウィンドウは画面へ直接描かず、ビデオメモリ上の画面外のサーフェスに描きます。それをDWMが1枚のデスクトップ画像にまとめて表示します。 – Desktop Window Manager|Microsoft Learn
- Windows の Windows.Graphics.Capture では、録画対象の大きさが変わったら Direct3D11CaptureFramePool の Recreate を呼ぶのが推奨の方法です。呼ぶと、それまでのフレームはすべて捨てられます。 – Screen capture|Microsoft Learn
- ティアリングは、ディスプレイが画面を更新している最中にバッファが書き換えられ、複数のフレームの一部が同時にモニターへ送られることで起きます。複数のバッファを使い、表示の更新と同期させるのが一般的な対策です。 – Screen tearing|Wikipedia
- Microsoft の説明では、スワップチェーンはバックバッファの持ち回りを制御するもので、グラフィックスのアニメーションの基礎になっています。スワップチェーンが持つバッファの組は、ResizeBuffers などを呼ぶまで変わりません。 – Swap Chains|Microsoft Learn
- Direct3D では、スワップチェーンそのものではなく、IDXGISwapChain::ResizeBuffers でバッファを作り直すのが基本です。その前に、アプリはバックバッファへの参照を直接のものも間接のものもすべて手放しておく必要があります。 – IDXGISwapChain::ResizeBuffers|Microsoft Learn
- 今の Windows で推奨される「フリップモデル」では、バックバッファがアプリ側とDWMの間で受け渡され、DWMはバックバッファから直接合成します。中身をコピーしない分、速く、電力も少なくて済みます。 – For best performance, use DXGI flip model|Microsoft Learn
- Chrome は、ページを層に分けてGPUで合成する仕組みを持っています。GPUへの命令は、描画を受け持つ専用のGPUプロセスを通して実行されます。 – GPU Accelerated Compositing in Chrome|The Chromium Projects
- マルチプレーン オーバーレイは Windows 8.1 で入った機能です。グラフィックスのハードウェアが複数の層を1枚の画像に合成して表示するので、CPUやソフトウェアでの合成を通りません。 – Multiplane overlay support|Microsoft Learn
- Direct3D 12 では、CPUとGPUの同期は「フェンス」という仕組みを使ってアプリが明示的に行う責任を持ちます。Direct3D 11 では、この同期をランタイムが暗黙に行っていました。 – Multi-engine synchronization|Microsoft Learn
- macOS の ScreenCaptureKit は考え方が少し違い、出力の大きさはほぼ固定で、元のウィンドウの大きさが変わっても追いかけません。Apple は、出力の大きさを頻繁に変えるとメモリの割り当てが増えるので勧めない、と説明しています。 – Take ScreenCaptureKit to the next level|WWDC22|Apple Developer
- ここまでの説明は、仕組みを単純にした模式図です。特定の録画ソフトやアプリで、どの部品が原因で止まるのかは確かめていません。フォーラムでも、どこで絵を取り込むかによって症状が変わる、という推測の形で説明されています。 – Screen Recording Woes: Black Screen Issue with Browser Hardware Acceleration|OBS Forums