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