# GPU同期 in Chrome Chromeで使われている、GPUのsynchronizationのためのメカニズムの概要を解読する。 想定されるシナリオとして、GPUのリソースに対して読みアクセスと書きアクセスが来たときが考えられる。 例えば新しいimageをdraw中にそれを含む最終描画をcompositするようときがこれにあたり、drawはcompositの前に終了している必要がある。そうでないと、composit時に未完成あるいは以前のイメージを参照してしまう。 同期問題に関してChromeがどう処理しているかをなんとなく理解したい。 ## GL Context [GL Context](https://www.khronos.org/opengl/wiki/OpenGL_Context)とはフレームバッファに対応するコンセプト。 各コンテキストはひとつのプロセスの中で独立したSurfaceを描画する。 1つのプロセスで複数のコンテキストをつくることができるので、ひとつしかないか複数あるかで変わる。 ### 単一のGLコンテキスト ひとつのコンテキストの中で全ての共有リソースが使われているならそもそも同期問題は発生しない。 draw->compositの順で実装されているなら、submitされた順番を守れば良い。 ### 複数のGLコンテキスト 同じshare groupの中に作られたGLコンテキストたちは、同プロセス別スレッドにありリソースを互いに共有できる。 この場合[GLFence](https://source.chromium.org/chromium/chromium/src/+/main:ui/gl/gl_fence.h;l=15;drc=3e1a26c44c024d97dc9a4c09bbc6a2365398ca2c)を使う。 Context A: draw -> GLFenceを作る Context B: GLFenceを待つ -> 読む 実際に使われているところとして[SurfacelssGlRenderer](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/demo/surfaceless_gl_renderer.cc;l=221;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)を見てみる。 ```cpp= void SurfacelessGlRenderer::RenderFrame() { ...; std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); presenter_->ScheduleOverlayPlane( buffers_[back_buffer_]->image(), gl_fence->GetGpuFence(), gfx::OverlayPlaneData(...)); ...; } ``` ここではprimary planeだけでしかGLFenceを使っていない。 Primary planeは毎フレームで動的に更新されているが、non-primary planeでは初期化時に作られるだけなので、描画時には準備完了している。 [Initialize](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/demo/surfaceless_gl_renderer.cc;l=158;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)でsurfaceをつくり、imageが用意できたらRenderFrameが呼ばれて`buffer_[back_buffer_]->image()`とともにGpuFenceを作って送りつける。 なおGLFenceとGpuFenceはペアのオブジェクトで Waylandのコードスタックを追ってみる。 [GbmSrfacelessWayland::ScheduleOverlayPlane](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc;l=134;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)が呼ばれると`gpu_fence`はacquireされ、[QueueWaylandOverlayConfig](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc;l=129;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)でPendingFrameとしてqueueされる。 ```cpp= std::vector<gfx::GpuFence> acquire_fences; if (gpu_fence && (buffer_manager_->supports_acquire_fence() || use_egl_fence_sync_)) { acquire_fences.push_back(std::move(*gpu_fence)); } frame->schedule_planes_succeeded = image->ScheduleOverlayPlane( widget_, overlay_plane_data, std::move(acquire_fences), {}); ``` Fenceはこの[WaylandOverlayConfig](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/common/wayland_overlay_config.h;l=21;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)の中に渡されており、これが読み終わるまで解放されない。 ## GPUFence と GPUFenceHandle [gfx::GpuFence](https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/gpu_fence.h;l=22;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)は上述の通り同期処理をするためのガードみたいなもの。 GpuFenceは[gfx::GpuFenceHandle](https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/gpu_fence_handle.h;l=27;drc=fdeb7047d9f017fa5f8ed73681acd3ea8e5409e5)を所有し、GpuFenceのオブジェクトが破壊されると共にGpuFenceHandleも破壊されてリソースが開放されるという仕組み。 プラットフォームによって違うが、POSIXでは[base::ScopedFD](https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/gpu_fence_handle.h;l=29;drc=fdeb7047d9f017fa5f8ed73681acd3ea8e5409e5)がFenceとして使われる。 このHandleクラスは[`smart_fence_`](https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/gpu_fence_handle.h;l=89;drc=fdeb7047d9f017fa5f8ed73681acd3ea8e5409e5)というscoped_refptrすなわちshared pointerのfenceを持っているが、これはFenceを複数に渡す用途をサポートしている。 複数のクライアントがリソースにアクセスしたい時もref countしてくれているのでみんなに同時に渡して良い。全員がリリースしたタイミングでリソースも解放される。 ## Wayland上での explicit synchronization ところで似たような同期処理をwaylandも頑張っている。 wayland connection上でbufferのやり取りをするときも、書き込み・読み込みをblockするためにfenceが用いられる。 [LinuxBufferRelease](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/wayland/zwp_linux_explicit_synchronization.cc;l=32;drc=5ecc98a60b27ea6f23813fafa3e65948e92e406e)は[HandleExplicitRelease](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/wayland/zwp_linux_explicit_synchronization.cc;l=46;drc=5ecc98a60b27ea6f23813fafa3e65948e92e406e)を`release_callback_`として指定しており、このcallbackに渡されるGpuFenceHandleが空なら`zwp_linux_buffer_release_v1_send_immediate_release`、からでなければ`zwp_linux_buffer_release_v1_send_fenced_release`を通じて、wayland client側に`resource_`の所有権を返している。 この`release_callback_`は[Surface::SetPerCommitBufferReleaseCallback](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/surface.cc;l=890;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)によってexo::Surfaceに渡されており、なんか変なことが起きたときは[ImmediateExplicitRelease](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/surface.cc;l=294;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)によってGpuFenceHandleなしに緊急releaseし、うまく進んでいるときは[exo::Buffer](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/buffer.cc;l=529;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)に渡ったあと[Buffer::ReleaseContentsTexture](https://source.chromium.org/chromium/chromium/src/+/main:components/exo/buffer.cc;l=751;drc=7b432aeb14353cb2e0f6696c6558c2d0dcca6786)によって呼び出される。 上記で確認したコンセプト通りに動いていそう。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up