In this article, I analyse GPU Process in multi processs.
In Host
Pivotal classes are WebGraphicsContext3DCommandBufferImpl, CommandBufferProxyImpl, CommandBufferHelper.
Store cmd in Ring buffer
stack trace is as followsgpu::CommandBufferHelper::WaitForAvailableEntries() gpu::CommandBufferHelper::GetSpace() gpu::gles2::GLES2Implementation::BindTexture() content::WebGraphicsContext3DCommandBufferImpl::bindTexture() cc::ResourceProvider::BindForSampling() cc::GLRenderer::DrawContentQuad() cc::GLRenderer::DoDrawQuad() cc::DirectRenderer::DrawRenderPass() cc::DirectRenderer::DrawFrame() cc::LayerTreeHostImpl::DrawLayers() cc::SingleThreadProxy::DoComposite() cc::SingleThreadProxy::CommitAndComposite() cc::SingleThreadProxy::CompositeImmediately() content::RenderWidget::Composite() content::RenderWidget::DoDeferredUpdate() content::RenderWidget::DoDeferredUpdateAndSendInputAck() content::RenderWidget::OnViewContextSwapBuffersComplete() base::MessageLoop::RunTask() base::MessageLoop::DeferOrRunPendingTask() base::MessageLoop::DoWork() base::MessagePumpDefault::Run() base::MessageLoop::RunInternal() base::RunLoop::Run() base::MessageLoop::Run() content::RendererMain() content::RunZygote() content::RunNamedProcessTypeMain() content::ContentMainRunnerImpl::Run() content::ContentMain() main
WebGraphicsContext3D::bindTexture() calls GLES2Implementation::BindTexture()
void GLES2Implementation::BindTexture(GLenum target, GLuint texture) { ... if (IsTextureReservedId(texture)) { SetGLError(GL_INVALID_OPERATION, "BindTexture", "texture reserved id"); return; } if (BindTextureHelper(target, texture)) { helper_->BindTexture(target, texture); } CheckGLError(); }
and GLES2CmdHelper::BindTexture is called.
void BindTexture(GLenum target, GLuint texture) { gles2::cmds::BindTexture* c = GetCmdSpace[gles2::cmds::BindTexture](); if (c) { c->Init(target, texture); } }
and CommandBufferHelper::GetSpace is called to alloc msg.
CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) { AllocateRingBuffer(); if (!usable()) { return NULL; } GPU_DCHECK(HaveRingBuffer()); ++commands_issued_; WaitForAvailableEntries(entries); CommandBufferEntry* space = &entries_[put_]; put_ += entries; GPU_DCHECK_LE(put_, total_entry_count_); if (put_ == total_entry_count_) { put_ = 0; } return space; }
WaitForAvailableEntries() calls CommandBufferHelper::FlushSync() if ring buffer is full.
Send cmd to GPU Process
stack trace is as followsIn common, SwapBuffer sends msg to GPU Process.
content::CommandBufferProxyImpl::Flush() content::CompositorOutputSurface::SwapBuffers() cc::GLRenderer::SwapBuffers() cc::LayerTreeHostImpl::SwapBuffers() cc::SingleThreadProxy::CompositeImmediately() content::RenderWidget::Composite() content::RenderWidget::DoDeferredUpdate() content::RenderWidget::DoDeferredUpdateAndSendInputAck() content::RenderWidget::OnViewContextSwapBuffersComplete() base::MessageLoop::RunTask() base::MessageLoop::DeferOrRunPendingTask() base::MessageLoop::DoWork() base::MessagePumpDefault::Run() base::MessageLoop::RunInternal() base::RunLoop::Run() base::MessageLoop::Run() content::RendererMain() content::RunZygote() content::RunNamedProcessTypeMain() content::ContentMainRunnerImpl::Run() content::ContentMain() mainIt is important to remember that getter also sends msg to GPU Process.
content::CommandBufferProxyImpl::Flush() content::CommandBufferProxyImpl::FlushSync() gpu::CommandBufferHelper::FlushSync() gpu::CommandBufferHelper::Finish() gpu::gles2::GLES2Implementation::WaitForCmd() gpu::gles2::GLES2Implementation::GetBucketContents() gpu::gles2::GLES2Implementation::GetBucketAsString() gpu::gles2::GLES2Implementation::GetStringHelper() gpu::gles2::GLES2Implementation::GetString() content::WebGraphicsContext3DCommandBufferImpl::getString() cc::GLRenderer::Initialize() cc::GLRenderer::Create() cc::LayerTreeHostImpl::CreateAndSetRenderer() cc::LayerTreeHostImpl::InitializeRenderer() cc::SingleThreadProxy::CreateAndInitializeOutputSurface() cc::LayerTreeHost::InitializeOutputSurfaceIfNeeded() cc::SingleThreadProxy::CommitAndComposite() cc::SingleThreadProxy::CompositeImmediately() content::RenderWidget::Composite() content::RenderWidget::DoDeferredUpdate() content::RenderWidget::DoDeferredUpdateAndSendInputAck() content::RenderWidget::InvalidationCallback() base::MessageLoop::RunTask() ...
CommandBufferProxyImpl::Flush() send GpuCommandBufferMsg_AsyncFlush to GPU Process to run stored cmds.
When sending GpuCommandBufferMsg_AsyncFlush, send offset on ring buffer and flush count as flush id.
void CommandBufferProxyImpl::Flush(int32 put_offset) { ... Send(new GpuCommandBufferMsg_AsyncFlush(route_id_, put_offset, ++flush_count_)); }
FYI, just see how cmd looks like
GLES2CmdHelper::BindTexture calls gles2::cmds::BindTexture::Init()in gles2_cmd_format_autogen.h
struct BindTexture { typedef BindTexture ValueType; static const CommandId kCmdId = kBindTexture; static const cmd::ArgFlags kArgFlags = cmd::kFixed; static uint32 ComputeSize() { return static_cast(sizeof(ValueType)); // NOLINT } void SetHeader() { header.SetCmd (); } void Init(GLenum _target, GLuint _texture) { SetHeader(); target = _target; texture = _texture; } void* Set(void* cmd, GLenum _target, GLuint _texture) { static_cast (cmd)->Init(_target, _texture); return NextCmdAddress (cmd); } gpu::CommandHeader header; uint32 target; uint32 texture; };
In GPU Process
Pivotal classes are GpuCommandBufferStub, CommandBufferService, GpuScheduler, CommandParser, GLES2DecoderImpl
Run cmd
stacktrace is as followsgpu::gles2::GLES2DecoderImpl::DoBindTexture() gpu::gles2::GLES2DecoderImpl::HandleBindTexture() gpu::gles2::GLES2DecoderImpl::DoCommand() gpu::CommandParser::ProcessCommand() gpu::GpuScheduler::PutChanged() gpu::CommandBufferService::Flush() content::GpuCommandBufferStub::OnAsyncFlush() _ZN30GpuCommandBufferMsg_AsyncFlush8DispatchIN7content20GpuCommandBufferStubES2_MS2_FvijEEEbPKN3IPC7MessageEPT_PT0_T1_.isra.51 content::GpuCommandBufferStub::OnMessageReceived() content::MessageRouter::RouteMessage() content::GpuChannel::HandleMessage() base::MessageLoop::RunTask() base::MessageLoop::DeferOrRunPendingTask() base::MessageLoop::DoWork() base::MessagePumpDefault::Run() base::MessageLoop::RunInternal() base::RunLoop::Run() base::MessageLoop::Run() content::GpuMain() content::RunNamedProcessTypeMain() content::ContentMainRunnerImpl::Run() content::ContentMain() main
As I mentioned in previous article, GLES2DecoderImpl calls real gl call via GLApi. Refer to gl_bindings_autogen_gl.cc and gl_gl_api_implementation.h
Where MakeCurrent()
GpuCommandBufferStub::OnMessageReceived() call GpuCommandBufferStub::MakeCurrent() before handling msg.
GpuCommandBufferStub::MakeCurrent() has two tasks.
1. glMakeCurrent() via decoder
2. If there is a error, handle lostContext.
bool GpuCommandBufferStub::MakeCurrent() { if (decoder_->MakeCurrent()) return true; DLOG(ERROR) << "Context lost because MakeCurrent failed."; command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); command_buffer_->SetParseError(gpu::error::kLostContext); if (gfx::GLContext::LosesAllContextsOnContextLost()) channel_->LoseAllContexts(); return false; }
Compare to InProcess.
CommandBuffer
- Multi Process: WebGraphicsContext3DCommandBufferImpl uses CommandBufferProxyImpl. CommandBufferProxyImpl communicates to CommandBufferService via IPC- InProcess : WebGraphicsContext3DInProcessCommandBufferImpl use directly CommandBufferService
Who control CommandBufferService. They must handle various exceptions (i.e. lostContext)
- Multi Process: GpuCommandBufferStub- InProcess: GLInProcessContext
Both handle exceptions before gpu::GpuScheduler::PutChanged(). How does CommandBufferService::Flush() calls gpu::GpuScheduler::PutChanged() while CommandBufferService does not know GpuScheduler.
in GpuCommandBufferStub::OnInitialize()
command_buffer_->SetPutOffsetChangeCallback( base::Bind(&GpuCommandBufferStub::PutChanged, base::Unretained(this)));
in GLInProcessContext::Initialize()
command_buffer->SetPutOffsetChangeCallback(base::Bind( &GLInProcessContextImpl::PumpCommands, base::Unretained(this)));Both register various callbacks to CommandBufferService as well as OffsetChangeCallback.