gpu_process_transport_factory.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/compositor/gpu_process_transport_factory.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/location.h"
12#include "base/message_loop/message_loop.h"
13#include "base/metrics/histogram.h"
14#include "base/threading/thread.h"
15#include "cc/output/compositor_frame.h"
16#include "cc/output/output_surface.h"
17#include "cc/surfaces/surface_manager.h"
18#include "content/browser/compositor/browser_compositor_output_surface.h"
19#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
20#include "content/browser/compositor/gpu_browser_compositor_output_surface.h"
21#include "content/browser/compositor/onscreen_display_client.h"
22#include "content/browser/compositor/reflector_impl.h"
23#include "content/browser/compositor/software_browser_compositor_output_surface.h"
24#include "content/browser/compositor/surface_display_output_surface.h"
25#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
26#include "content/browser/gpu/gpu_data_manager_impl.h"
27#include "content/browser/gpu/gpu_surface_tracker.h"
28#include "content/browser/renderer_host/render_widget_host_impl.h"
29#include "content/common/gpu/client/context_provider_command_buffer.h"
30#include "content/common/gpu/client/gl_helper.h"
31#include "content/common/gpu/client/gpu_channel_host.h"
32#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
33#include "content/common/gpu/gpu_process_launch_causes.h"
34#include "content/common/host_shared_bitmap_manager.h"
35#include "content/public/common/content_switches.h"
36#include "gpu/GLES2/gl2extchromium.h"
37#include "gpu/command_buffer/client/gles2_interface.h"
38#include "gpu/command_buffer/common/mailbox.h"
39#include "third_party/khronos/GLES2/gl2.h"
40#include "ui/compositor/compositor.h"
41#include "ui/compositor/compositor_constants.h"
42#include "ui/compositor/compositor_switches.h"
43#include "ui/gfx/native_widget_types.h"
44#include "ui/gfx/size.h"
45
46#if defined(OS_WIN)
47#include "content/browser/compositor/software_output_device_win.h"
48#elif defined(USE_OZONE)
49#include "content/browser/compositor/overlay_candidate_validator_ozone.h"
50#include "content/browser/compositor/software_output_device_ozone.h"
51#include "ui/ozone/public/surface_factory_ozone.h"
52#elif defined(USE_X11)
53#include "content/browser/compositor/software_output_device_x11.h"
54#elif defined(OS_MACOSX)
55#include "content/browser/compositor/software_output_device_mac.h"
56#endif
57
58using cc::ContextProvider;
59using gpu::gles2::GLES2Interface;
60
61namespace content {
62
63struct GpuProcessTransportFactory::PerCompositorData {
64  int surface_id;
65  scoped_refptr<ReflectorImpl> reflector;
66  scoped_ptr<OnscreenDisplayClient> display_client;
67};
68
69GpuProcessTransportFactory::GpuProcessTransportFactory()
70    : callback_factory_(this) {
71  output_surface_proxy_ = new BrowserCompositorOutputSurfaceProxy(
72      &output_surface_map_);
73#if defined(OS_CHROMEOS)
74  bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch(
75      switches::kUIDisableThreadedCompositing);
76#else
77  bool use_thread = false;
78#endif
79  if (use_thread) {
80    compositor_thread_.reset(new base::Thread("Browser Compositor"));
81    compositor_thread_->Start();
82  }
83  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
84          switches::kUseSurfaces)) {
85    surface_manager_ = make_scoped_ptr(new cc::SurfaceManager);
86  }
87}
88
89GpuProcessTransportFactory::~GpuProcessTransportFactory() {
90  DCHECK(per_compositor_data_.empty());
91
92  // Make sure the lost context callback doesn't try to run during destruction.
93  callback_factory_.InvalidateWeakPtrs();
94}
95
96scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
97GpuProcessTransportFactory::CreateOffscreenCommandBufferContext() {
98  return CreateContextCommon(0);
99}
100
101scoped_ptr<cc::SoftwareOutputDevice> CreateSoftwareOutputDevice(
102    ui::Compositor* compositor) {
103#if defined(OS_WIN)
104  return scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareOutputDeviceWin(
105      compositor));
106#elif defined(USE_OZONE)
107  return scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareOutputDeviceOzone(
108      compositor));
109#elif defined(USE_X11)
110  return scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareOutputDeviceX11(
111      compositor));
112#elif defined(OS_MACOSX)
113  return scoped_ptr<cc::SoftwareOutputDevice>(
114      new SoftwareOutputDeviceMac(compositor));
115#else
116  NOTREACHED();
117  return scoped_ptr<cc::SoftwareOutputDevice>();
118#endif
119}
120
121scoped_ptr<cc::OverlayCandidateValidator> CreateOverlayCandidateValidator(
122    gfx::AcceleratedWidget widget) {
123#if defined(USE_OZONE)
124  ui::OverlayCandidatesOzone* overlay_candidates =
125      ui::SurfaceFactoryOzone::GetInstance()->GetOverlayCandidates(widget);
126  if (overlay_candidates && CommandLine::ForCurrentProcess()->HasSwitch(
127                                switches::kEnableHardwareOverlays)) {
128    return scoped_ptr<cc::OverlayCandidateValidator>(
129        new OverlayCandidateValidatorOzone(widget, overlay_candidates));
130  }
131#endif
132  return scoped_ptr<cc::OverlayCandidateValidator>();
133}
134
135scoped_ptr<cc::OutputSurface> GpuProcessTransportFactory::CreateOutputSurface(
136    ui::Compositor* compositor, bool software_fallback) {
137  PerCompositorData* data = per_compositor_data_[compositor];
138  if (!data)
139    data = CreatePerCompositorData(compositor);
140
141  bool create_software_renderer = software_fallback;
142#if defined(OS_CHROMEOS)
143  // Software fallback does not happen on Chrome OS.
144  create_software_renderer = false;
145#elif defined(OS_WIN)
146  if (::GetProp(compositor->widget(), kForceSoftwareCompositor)) {
147    if (::RemoveProp(compositor->widget(), kForceSoftwareCompositor))
148      create_software_renderer = true;
149  }
150#endif
151
152  scoped_refptr<ContextProviderCommandBuffer> context_provider;
153
154  if (!create_software_renderer) {
155    context_provider = ContextProviderCommandBuffer::Create(
156        GpuProcessTransportFactory::CreateContextCommon(data->surface_id),
157        "Compositor");
158  }
159
160  UMA_HISTOGRAM_BOOLEAN("Aura.CreatedGpuBrowserCompositor", !!context_provider);
161
162  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
163          switches::kUseSurfaces)) {
164    // This gets a bit confusing. Here we have a ContextProvider configured to
165    // render directly to this widget. We need to make an OnscreenDisplayClient
166    // associated with this context, then return a SurfaceDisplayOutputSurface
167    // set up to draw to the display's surface.
168    cc::SurfaceManager* manager = surface_manager_.get();
169    scoped_ptr<cc::OutputSurface> software_surface;
170    if (!context_provider) {
171      software_surface =
172          make_scoped_ptr(new SoftwareBrowserCompositorOutputSurface(
173              output_surface_proxy_,
174              CreateSoftwareOutputDevice(compositor),
175              per_compositor_data_[compositor]->surface_id,
176              &output_surface_map_,
177              compositor->vsync_manager()));
178    }
179    scoped_ptr<OnscreenDisplayClient> display_client(new OnscreenDisplayClient(
180        context_provider, software_surface.Pass(), manager));
181    // TODO(jamesr): Need to set up filtering for the
182    // GpuHostMsg_UpdateVSyncParameters message.
183
184    scoped_refptr<cc::ContextProvider> offscreen_context_provider;
185    if (context_provider) {
186      offscreen_context_provider = ContextProviderCommandBuffer::Create(
187          GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
188          "Offscreen-Compositor");
189    }
190    scoped_ptr<SurfaceDisplayOutputSurface> output_surface(
191        new SurfaceDisplayOutputSurface(
192            display_client->display(), manager, offscreen_context_provider));
193    data->display_client = display_client.Pass();
194    return output_surface.PassAs<cc::OutputSurface>();
195  }
196
197  if (!context_provider.get()) {
198    if (compositor_thread_.get()) {
199      LOG(FATAL) << "Failed to create UI context, but can't use software"
200                 " compositing with browser threaded compositing. Aborting.";
201    }
202
203    scoped_ptr<SoftwareBrowserCompositorOutputSurface> surface(
204        new SoftwareBrowserCompositorOutputSurface(
205            output_surface_proxy_,
206            CreateSoftwareOutputDevice(compositor),
207            per_compositor_data_[compositor]->surface_id,
208            &output_surface_map_,
209            compositor->vsync_manager()));
210    return surface.PassAs<cc::OutputSurface>();
211  }
212
213  scoped_refptr<base::SingleThreadTaskRunner> compositor_thread_task_runner =
214      GetCompositorMessageLoop();
215  if (!compositor_thread_task_runner.get())
216    compositor_thread_task_runner = base::MessageLoopProxy::current();
217
218  // Here we know the GpuProcessHost has been set up, because we created a
219  // context.
220  output_surface_proxy_->ConnectToGpuProcessHost(
221      compositor_thread_task_runner.get());
222
223  scoped_ptr<BrowserCompositorOutputSurface> surface(
224      new GpuBrowserCompositorOutputSurface(
225          context_provider,
226          per_compositor_data_[compositor]->surface_id,
227          &output_surface_map_,
228          compositor->vsync_manager(),
229          CreateOverlayCandidateValidator(compositor->widget())));
230  if (data->reflector.get())
231    data->reflector->ReattachToOutputSurfaceFromMainThread(surface.get());
232
233  return surface.PassAs<cc::OutputSurface>();
234}
235
236scoped_refptr<ui::Reflector> GpuProcessTransportFactory::CreateReflector(
237    ui::Compositor* source,
238    ui::Layer* target) {
239  PerCompositorData* data = per_compositor_data_[source];
240  DCHECK(data);
241
242  data->reflector = new ReflectorImpl(source,
243                                      target,
244                                      &output_surface_map_,
245                                      GetCompositorMessageLoop(),
246                                      data->surface_id);
247  return data->reflector;
248}
249
250void GpuProcessTransportFactory::RemoveReflector(
251    scoped_refptr<ui::Reflector> reflector) {
252  ReflectorImpl* reflector_impl =
253      static_cast<ReflectorImpl*>(reflector.get());
254  PerCompositorData* data =
255      per_compositor_data_[reflector_impl->mirrored_compositor()];
256  DCHECK(data);
257  data->reflector->Shutdown();
258  data->reflector = NULL;
259}
260
261void GpuProcessTransportFactory::RemoveCompositor(ui::Compositor* compositor) {
262  PerCompositorDataMap::iterator it = per_compositor_data_.find(compositor);
263  if (it == per_compositor_data_.end())
264    return;
265  PerCompositorData* data = it->second;
266  DCHECK(data);
267  GpuSurfaceTracker::Get()->RemoveSurface(data->surface_id);
268  delete data;
269  per_compositor_data_.erase(it);
270  if (per_compositor_data_.empty()) {
271    // Destroying the GLHelper may cause some async actions to be cancelled,
272    // causing things to request a new GLHelper. Due to crbug.com/176091 the
273    // GLHelper created in this case would be lost/leaked if we just reset()
274    // on the |gl_helper_| variable directly. So instead we call reset() on a
275    // local scoped_ptr.
276    scoped_ptr<GLHelper> helper = gl_helper_.Pass();
277
278    // If there are any observer left at this point, make sure they clean up
279    // before we destroy the GLHelper.
280    FOR_EACH_OBSERVER(
281        ImageTransportFactoryObserver, observer_list_, OnLostResources());
282
283    helper.reset();
284    DCHECK(!gl_helper_) << "Destroying the GLHelper should not cause a new "
285                           "GLHelper to be created.";
286  }
287}
288
289bool GpuProcessTransportFactory::DoesCreateTestContexts() { return false; }
290
291cc::SharedBitmapManager* GpuProcessTransportFactory::GetSharedBitmapManager() {
292  return HostSharedBitmapManager::current();
293}
294
295ui::ContextFactory* GpuProcessTransportFactory::GetContextFactory() {
296  return this;
297}
298
299base::MessageLoopProxy* GpuProcessTransportFactory::GetCompositorMessageLoop() {
300  if (!compositor_thread_)
301    return NULL;
302  return compositor_thread_->message_loop_proxy();
303}
304
305gfx::GLSurfaceHandle GpuProcessTransportFactory::GetSharedSurfaceHandle() {
306  gfx::GLSurfaceHandle handle = gfx::GLSurfaceHandle(
307      gfx::kNullPluginWindow, gfx::TEXTURE_TRANSPORT);
308  handle.parent_client_id =
309      BrowserGpuChannelHostFactory::instance()->GetGpuChannelId();
310  return handle;
311}
312
313GLHelper* GpuProcessTransportFactory::GetGLHelper() {
314  if (!gl_helper_ && !per_compositor_data_.empty()) {
315    scoped_refptr<cc::ContextProvider> provider =
316        SharedMainThreadContextProvider();
317    if (provider.get())
318      gl_helper_.reset(new GLHelper(provider->ContextGL(),
319                                    provider->ContextSupport()));
320  }
321  return gl_helper_.get();
322}
323
324void GpuProcessTransportFactory::AddObserver(
325    ImageTransportFactoryObserver* observer) {
326  observer_list_.AddObserver(observer);
327}
328
329void GpuProcessTransportFactory::RemoveObserver(
330    ImageTransportFactoryObserver* observer) {
331  observer_list_.RemoveObserver(observer);
332}
333
334#if defined(OS_MACOSX)
335void GpuProcessTransportFactory::OnSurfaceDisplayed(int surface_id) {
336  BrowserCompositorOutputSurface* surface = output_surface_map_.Lookup(
337      surface_id);
338  if (surface)
339    surface->OnSurfaceDisplayed();
340}
341#endif
342
343scoped_refptr<cc::ContextProvider>
344GpuProcessTransportFactory::SharedMainThreadContextProvider() {
345  if (shared_main_thread_contexts_.get())
346    return shared_main_thread_contexts_;
347
348  // In threaded compositing mode, we have to create our own context for the
349  // main thread since the compositor's context will be bound to the
350  // compositor thread. When not in threaded mode, we still need a separate
351  // context so that skia and gl_helper don't step on each other.
352  shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create(
353      GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
354      "Offscreen-MainThread");
355
356  if (shared_main_thread_contexts_) {
357    shared_main_thread_contexts_->SetLostContextCallback(
358        base::Bind(&GpuProcessTransportFactory::
359                        OnLostMainThreadSharedContextInsideCallback,
360                   callback_factory_.GetWeakPtr()));
361    if (!shared_main_thread_contexts_->BindToCurrentThread())
362      shared_main_thread_contexts_ = NULL;
363  }
364  return shared_main_thread_contexts_;
365}
366
367GpuProcessTransportFactory::PerCompositorData*
368GpuProcessTransportFactory::CreatePerCompositorData(
369    ui::Compositor* compositor) {
370  DCHECK(!per_compositor_data_[compositor]);
371
372  gfx::AcceleratedWidget widget = compositor->widget();
373  GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get();
374
375  PerCompositorData* data = new PerCompositorData;
376  data->surface_id = tracker->AddSurfaceForNativeWidget(widget);
377  tracker->SetSurfaceHandle(
378      data->surface_id,
379      gfx::GLSurfaceHandle(widget, gfx::NATIVE_DIRECT));
380
381  per_compositor_data_[compositor] = data;
382
383  return data;
384}
385
386scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
387GpuProcessTransportFactory::CreateContextCommon(int surface_id) {
388  if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor())
389    return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>();
390  blink::WebGraphicsContext3D::Attributes attrs;
391  attrs.shareResources = true;
392  attrs.depth = false;
393  attrs.stencil = false;
394  attrs.antialias = false;
395  attrs.noAutomaticFlushes = true;
396  bool lose_context_when_out_of_memory = true;
397  CauseForGpuLaunch cause =
398      CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;
399  scoped_refptr<GpuChannelHost> gpu_channel_host(
400      BrowserGpuChannelHostFactory::instance()->EstablishGpuChannelSync(cause));
401  if (!gpu_channel_host) {
402    LOG(ERROR) << "Failed to establish GPU channel.";
403    return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>();
404  }
405  GURL url("chrome://gpu/GpuProcessTransportFactory::CreateContextCommon");
406  scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context(
407      new WebGraphicsContext3DCommandBufferImpl(
408          surface_id,
409          url,
410          gpu_channel_host.get(),
411          attrs,
412          lose_context_when_out_of_memory,
413          WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
414          NULL));
415  return context.Pass();
416}
417
418void GpuProcessTransportFactory::OnLostMainThreadSharedContextInsideCallback() {
419  base::MessageLoop::current()->PostTask(
420      FROM_HERE,
421      base::Bind(&GpuProcessTransportFactory::OnLostMainThreadSharedContext,
422                 callback_factory_.GetWeakPtr()));
423}
424
425void GpuProcessTransportFactory::OnLostMainThreadSharedContext() {
426  LOG(ERROR) << "Lost UI shared context.";
427
428  // Keep old resources around while we call the observers, but ensure that
429  // new resources are created if needed.
430  // Kill shared contexts for both threads in tandem so they are always in
431  // the same share group.
432  scoped_refptr<cc::ContextProvider> lost_shared_main_thread_contexts =
433      shared_main_thread_contexts_;
434  shared_main_thread_contexts_  = NULL;
435
436  scoped_ptr<GLHelper> lost_gl_helper = gl_helper_.Pass();
437
438  FOR_EACH_OBSERVER(ImageTransportFactoryObserver,
439                    observer_list_,
440                    OnLostResources());
441
442  // Kill things that use the shared context before killing the shared context.
443  lost_gl_helper.reset();
444  lost_shared_main_thread_contexts  = NULL;
445}
446
447}  // namespace content
448