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.