Who, why, how create HW Surface
Where to create HW Surface and How to use it.
Where need it
Render process
- Where create surface_id()?
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> RenderWidget::CreateGraphicsContext3D( const WebKit::WebGraphicsContext3D::Attributes& attributes) { ... scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( new WebGraphicsContext3DCommandBufferImpl( surface_id(), // <- HERE GetURLForGraphicsContext3D(), gpu_channel_host.get(), swap_client, attributes, false /* bind generates resources */, limits)); return context.Pass(); }
Browser process
- Where create data->surface_id?
scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface( ui::Compositor* compositor) { PerCompositorData* data = per_compositor_data_[compositor]; ... if (!command_line->HasSwitch(switches::kUIEnableSoftwareCompositing)) { context_provider = ContextProviderCommandBuffer::Create( GpuProcessTransportFactory::CreateContextCommon( swap_client_weak_ptr, data->surface_id), // <- HERE "Compositor"); } ... }
Where create surface_id?
render process
- in browser process, creating RenderWidgetHost
- demonstrate how RenderWidget is created.
- How content shell start
- How render process is created
- How GLXSurface is created with GTK_TOOLKIT option
// surface_id and routing_id are just seq.
RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
RenderProcessHost* process,
int routing_id,
bool hidden)
: view_(NULL),
...
last_input_number_(0) {
if (routing_id_ == MSG_ROUTING_NONE) {
routing_id_ = process_->GetNextRoutingID();
surface_id_ = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
process_->GetID(),
routing_id_);
} else {
...
}
// Create HW surface and map to surface_id
void RenderWidgetHostImpl::Init() {
...
GpuSurfaceTracker::Get()->SetSurfaceHandle(
surface_id_, GetCompositingSurface());
...
}
gfx::GLSurfaceHandle RenderWidgetHostImpl::GetCompositingSurface() {
if (view_)
return view_->GetCompositingSurface();
return gfx::GLSurfaceHandle();
}
with Aura
gfx::GLSurfaceHandle RenderWidgetHostViewAura::GetCompositingSurface() {
if (shared_surface_handle_.is_null()) {
ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
shared_surface_handle_ = factory->CreateSharedSurfaceHandle();
if (!shared_surface_handle_.is_null())
factory->AddObserver(this);
}
return shared_surface_handle_;
}
// Make GLSurfaceHandle with gfx::kNullPluginWindow & gfx::TEXTURE_TRANSPORT
gfx::GLSurfaceHandle GpuProcessTransportFactory::CreateSharedSurfaceHandle() {
scoped_refptr<cc::ContextProvider> provider =
SharedMainThreadContextProvider();
if (!provider.get())
return gfx::GLSurfaceHandle();
typedef WebGraphicsContext3DCommandBufferImpl WGC3DCBI;
WGC3DCBI* context = static_cast<WGC3DCBI*>(provider->Context3d());
gfx::GLSurfaceHandle handle = gfx::GLSurfaceHandle(
gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
handle.parent_gpu_process_id = context->GetGPUProcessID();
handle.parent_client_id = context->GetChannelID();
return handle;
}
With GTK_TOOLKIT
gfx::GLSurfaceHandle RenderWidgetHostViewGtk::GetCompositingSurface() {
if (compositing_surface_ == gfx::kNullPluginWindow) {
GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance();
gfx::NativeViewId view_id = GetNativeViewId();
if (!manager->GetPermanentXIDForId(&compositing_surface_, view_id)) {
DLOG(ERROR) << "Can't find XID for view id " << view_id;
}
}
return gfx::GLSurfaceHandle(compositing_surface_, gfx::NATIVE_TRANSPORT);
}
// Time to create RenderView in render process
bool RenderViewHostImpl::CreateRenderView(
const string16& frame_name,
int opener_route_id,
int32 max_page_id) {
...
ViewMsg_New_Params params;
...
params.view_id = GetRoutingID();
params.surface_id = surface_id();
...
Send(new ViewMsg_New(params));
...
return true;
}
In render process. Now RenderViewImpl : RenderWidget knows surface_id.
void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {
EnsureWebKitInitialized();
// When bringing in render_view, also bring in webkit's glue and jsbindings.
RenderViewImpl::Create(
params.opener_route_id,
params.renderer_preferences,
params.web_preferences,
params.view_id,
params.main_frame_routing_id,
params.surface_id,
...
params.allow_partial_swap);
}
- NOTE
- AURA : HW surface of RenderWidget is just texture
- GTK : HW surface of RenderWidget is XWindow
- QUESTION: How Render process create GLSurface by just sequence surface_id
- A: It is because Render process always create CommandBufferProxyImpl via render process -> browser process -> gpu process.
- A: In more detail, see below How WebGraphicsContext3DCommandBufferImpl connects GPU Process.
Browser process
- Only Aura makes HW surface for browser process
// if aura content_shell, (don't explain how chromium work because it's more complex) void DesktopNativeWidgetAura::InitNativeWidget( const Widget::InitParams& params) { ... aura::RootWindow::CreateParams rw_params(params.bounds); desktop_root_window_host_->Init(content_window_, params, &rw_params); root_window_.reset(new aura::RootWindow(rw_params)); ... } RootWindow::RootWindow(const CreateParams& params) : Window(NULL), host_(CreateHost(this, params)), ... held_event_factory_(this) { compositor_.reset(new ui::Compositor(host_->GetAcceleratedWidget())); ... } // if aura chrome gfx::AcceleratedWidget RootWindowHostX11::GetAcceleratedWidget() { return xwindow_; } //or if aura content_shell gfx::AcceleratedWidget DesktopRootWindowHostX11::GetAcceleratedWidget() { return xwindow_; } Compositor::Compositor(gfx::AcceleratedWidget widget) : root_layer_(NULL), widget_(widget), ... schedule_draw_factory_(this) { ... } // Time to create OutputSurface scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface( ui::Compositor* compositor) { PerCompositorData* data = per_compositor_data_[compositor]; if (!data) data = CreatePerCompositorData(compositor); ... CommandLine* command_line = CommandLine::ForCurrentProcess(); if (!command_line->HasSwitch(switches::kUIEnableSoftwareCompositing)) { context_provider = ContextProviderCommandBuffer::Create( GpuProcessTransportFactory::CreateContextCommon( swap_client_weak_ptr, data->surface_id), "Compositor"); } .. } GpuProcessTransportFactory::PerCompositorData* GpuProcessTransportFactory::CreatePerCompositorData( ui::Compositor* compositor) { ... gfx::AcceleratedWidget widget = compositor->widget(); GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); PerCompositorData* data = new PerCompositorData; data->surface_id = tracker->AddSurfaceForNativeWidget(widget); ... tracker->SetSurfaceHandle( data->surface_id, gfx::GLSurfaceHandle(widget, gfx::NATIVE_DIRECT)); per_compositor_data_[compositor] = data; return data; }
- NOTE: HW Surface of aura browser process is XWindow
Summary
- GTK Render Process : XWindow with gfx::NATIVE_TRANSPORT
- Aura Render Process : nothing with gfx::TEXTURE_TRANSPORT
- Aura Browser Process: XWindow with gfx::NATIVE_DIRECT
What's happen near GPU side
How WebGraphicsContext3DCommandBufferImpl connects GPU Process
- Where/How to create GLSurface from HW Surface.
- As a results
- Browser process send the HW Surface handle to GPU process
- GPU process creates GLSurface from HW Surface.
Common part
bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
bool onscreen) {
...
// Create a proxy to a command buffer in the GPU process.
if (onscreen) {
command_buffer_.reset(host_->CreateViewCommandBuffer(
surface_id_,
share_group,
attribs,
active_url_,
gpu_preference_));
} else {
command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
gfx::Size(1, 1),
share_group,
attribs,
active_url_,
gpu_preference_));
}
...
// Initialize the command buffer.
return command_buffer_->Initialize();
}
CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer(
int32 surface_id,
CommandBufferProxyImpl* share_group,
const std::vector<int32>& attribs,
const GURL& active_url,
gfx::GpuPreference gpu_preference) {
...
// connect gpu process and get route_id. This impl is different between browser and render process
int32 route_id = factory_->CreateViewCommandBuffer(surface_id, init_params);
if (route_id == MSG_ROUTING_NONE)
return NULL;
// make CommandBufferProxyImpl using route_id
CommandBufferProxyImpl* command_buffer =
new CommandBufferProxyImpl(this, route_id);
...
}
// Offscreen logic is the same between browser and render process.
CommandBufferProxyImpl* GpuChannelHost::CreateOffscreenCommandBuffer(
const gfx::Size& size,
CommandBufferProxyImpl* share_group,
const std::vector<int32>& attribs,
const GURL& active_url,
gfx::GpuPreference gpu_preference) {
...
int32 route_id;
if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size,
init_params,
&route_id))) {
return NULL;
}
...
CommandBufferProxyImpl* command_buffer =
new CommandBufferProxyImpl(this, route_id);
...
}
in GPU process for offscreen
void GpuChannel::OnCreateOffscreenCommandBuffer(
const gfx::Size& size,
const GPUCreateCommandBufferConfig& init_params,
int32* route_id) {
...
*route_id = GenerateRouteID();
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
this,
share_group,
gfx::GLSurfaceHandle(),
...
*route_id,
...
init_params.active_url));
...
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
...
}
- More detail for GPU process
Render process
Render process need to pass through browser process because render process does not know HW Surface handle.
// in render process
int32 RenderThreadImpl::CreateViewCommandBuffer(
int32 surface_id, const GPUCreateCommandBufferConfig& init_params) {
...
int32 route_id = MSG_ROUTING_NONE;
IPC::Message* message = new GpuHostMsg_CreateViewCommandBuffer(
surface_id,
init_params,
&route_id);
// Allow calling this from the compositor thread.
thread_safe_sender()->Send(message);
return route_id;
}
// in io thread in browser process
void GpuMessageFilter::OnCreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
IPC::Message* reply) {
...
gfx::GLSurfaceHandle compositing_surface;
int renderer_id = 0;
int render_widget_id = 0;
bool result = surface_tracker->GetRenderWidgetIDForSurface(
surface_id, &renderer_id, &render_widget_id);
...
// Get HW Surface via surface_id
compositing_surface = surface_tracker->GetSurfaceHandle(surface_id);
... // error handling for gpu process die
host->CreateViewCommandBuffer(
compositing_surface,
surface_id,
render_process_id_,
init_params,
base::Bind(&GpuMessageFilter::CreateCommandBufferCallback,
weak_ptr_factory_.GetWeakPtr(),
reply));
}
void GpuProcessHost::CreateViewCommandBuffer(
const gfx::GLSurfaceHandle& compositing_surface,
int surface_id,
int client_id,
const GPUCreateCommandBufferConfig& init_params,
const CreateCommandBufferCallback& callback) {
..
// Request GPU process to make route_id for GPUChannel. send surface_id and HW Surface handle!!
// NOTE!! How to receive? this is on io thread. io thread MUST not be blocked. GPU process will send ACK msg.
// At that time, callback will run to notify render process.
Send(new GpuMsg_CreateViewCommandBuffer(
compositing_surface, surface_id, client_id, init_params))) {
create_command_buffer_requests_.push(callback);
surface_refs_.insert(std::make_pair(surface_id,
GpuSurfaceTracker::GetInstance()->GetSurfaceRefForSurface(surface_id)));
..
}
// in GPU process
void GpuChannelManager::OnCreateViewCommandBuffer(
const gfx::GLSurfaceHandle& window,
int32 surface_id,
int32 client_id,
const GPUCreateCommandBufferConfig& init_params) {
DCHECK(surface_id);
int32 route_id = MSG_ROUTING_NONE;
// NOTE!!: get gpu_channel using render_process_id. render process uses only one gpu_channel_host.
GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
if (iter != gpu_channels_.end()) {
iter->second->CreateViewCommandBuffer(
window, surface_id, init_params, &route_id);
}
// NOTE!!: and Send route_id to browser process. remember browser process is waiting for it.
Send(new GpuHostMsg_CommandBufferCreated(route_id));
}
void GpuChannel::CreateViewCommandBuffer(
const gfx::GLSurfaceHandle& window,
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
int32* route_id) {
...
// Similar to GpuChannel::OnCreateOffscreenCommandBuffer()
*route_id = GenerateRouteID();
scoped_ptr<GpuCommandBufferStub> stub(
new GpuCommandBufferStub(this,
share_group,
window,
...
*route_id,
surface_id,
...
init_params.active_url));
...
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
}
// in io thread in browser process
void GpuProcessHost::OnCommandBufferCreated(const int32 route_id) {
...
CreateCommandBufferCallback callback =
create_command_buffer_requests_.front();
create_command_buffer_requests_.pop();
callback.Run(route_id);
}
void GpuMessageFilter::CreateCommandBufferCallback(
IPC::Message* reply, int32 route_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
GpuHostMsg_CreateViewCommandBuffer::WriteReplyParams(reply, route_id);
Send(reply);
}
// in render process
int32 RenderThreadImpl::CreateViewCommandBuffer(
int32 surface_id, const GPUCreateCommandBufferConfig& init_params) {
// NOTE!!: sync GpuHostMsg_CreateViewCommandBuffer msg return!!
int32 route_id = MSG_ROUTING_NONE;
IPC::Message* message = new GpuHostMsg_CreateViewCommandBuffer(
surface_id,
init_params,
&route_id);
// Allow calling this from the compositor thread.
thread_safe_sender()->Send(message);
return route_id;
}
Browser process
// in main thread of browser process
int32 BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params) {
CreateRequest request;
// Request IO thread and wait.
GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
&BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
base::Unretained(this),
&request,
surface_id,
init_params));
...
request.event.Wait();
return request.route_id;
}
// in io thread
void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
CreateRequest* request,
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params) {
GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
...
gfx::GLSurfaceHandle surface =
GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
// gpu_client_id_ is browser process id (seq.)
host->CreateViewCommandBuffer(
surface,
surface_id,
gpu_client_id_,
init_params,
base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
request)); // wow. partial binding!!
}
/*
* Same to render process
* from GpuProcessHost::CreateViewCommandBuffer() to gpu process to receive ACK msg
*/
// in io thread in browser process
void GpuProcessHost::OnCommandBufferCreated(const int32 route_id) {
...
CreateCommandBufferCallback callback =
create_command_buffer_requests_.front();
create_command_buffer_requests_.pop();
callback.Run(route_id);
}
void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO(
CreateRequest* request, int32 route_id) {
request->route_id = route_id;
request->event.Signal();
}
GpuCommandBufferStub
- represent GraphicsContext3D in gpu process
- See How Gpu process draws contents on the surface of Browser process's window in chromium.
- See GPU Command Buffer
gpu::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() ...
How to create GLSurface
void GpuCommandBufferStub::OnInitialize(
base::SharedMemoryHandle shared_state_handle,
IPC::Message* reply_message) {
...
if (!handle_.is_null()) {
...
surface_ = ImageTransportSurface::CreateSurface( // CreateViewCommandBuffer!!!
channel_->gpu_channel_manager(),
this,
handle_);
} else {
GpuChannelManager* manager = channel_->gpu_channel_manager();
surface_ = manager->GetDefaultOffscreenSurface(); // CreateOffscreenCommandBuffer!!!
}
...
}
scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
const gfx::GLSurfaceHandle& handle) {
scoped_refptr<gfx::GLSurface> surface;
if (handle.transport_type == gfx::TEXTURE_TRANSPORT)
surface = new TextureImageTransportSurface(manager, stub, handle); // AURA RENDER PROCESS!!!!! do you remember?
else
surface = CreateNativeSurface(manager, stub, handle); // Aura browser process, GTK Render process!!!
if (!surface.get() || !surface->Initialize())
return NULL;
return surface;
}
scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateNativeSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
const gfx::GLSurfaceHandle& handle) {
DCHECK(handle.handle);
DCHECK(handle.transport_type == gfx::NATIVE_DIRECT || // Aura browser process!!!
handle.transport_type == gfx::NATIVE_TRANSPORT); // GTK render process!!!
scoped_refptr<gfx::GLSurface> surface =
gfx::GLSurface::CreateViewGLSurface(handle.handle);
if (!surface.get())
return surface;
return scoped_refptr<gfx::GLSurface>(new PassThroughImageTransportSurface(
manager, stub, surface.get(), handle.is_transport()));
}
FIN.