Thursday, November 7, 2013

GLSurface for both render and browser process

original doc : https://github.com/ds-hwang/wiki/wiki/GLSurface-for-both-render-and-browser-process#who-why-how-create-hw-surface

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

// 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);
  ...
}

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.

No comments:

Post a Comment