1// Copyright (c) 2012 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 "gpu/command_buffer/client/gl_in_process_context.h"
6
7#include <set>
8#include <utility>
9#include <vector>
10
11#include <GLES2/gl2.h>
12#ifndef GL_GLEXT_PROTOTYPES
13#define GL_GLEXT_PROTOTYPES 1
14#endif
15#include <GLES2/gl2ext.h>
16#include <GLES2/gl2extchromium.h>
17
18#include "base/bind.h"
19#include "base/bind_helpers.h"
20#include "base/lazy_instance.h"
21#include "base/logging.h"
22#include "base/memory/scoped_ptr.h"
23#include "base/memory/weak_ptr.h"
24#include "base/message_loop/message_loop.h"
25#include "gpu/command_buffer/client/gles2_implementation.h"
26#include "gpu/command_buffer/client/transfer_buffer.h"
27#include "gpu/command_buffer/common/command_buffer.h"
28#include "gpu/command_buffer/common/constants.h"
29#include "ui/gfx/size.h"
30#include "ui/gl/gl_image.h"
31
32#if defined(OS_ANDROID)
33#include "ui/gl/android/surface_texture.h"
34#endif
35
36namespace gpu {
37
38namespace {
39
40const int32 kDefaultCommandBufferSize = 1024 * 1024;
41const unsigned int kDefaultStartTransferBufferSize = 4 * 1024 * 1024;
42const unsigned int kDefaultMinTransferBufferSize = 1 * 256 * 1024;
43const unsigned int kDefaultMaxTransferBufferSize = 16 * 1024 * 1024;
44
45class GLInProcessContextImpl
46    : public GLInProcessContext,
47      public base::SupportsWeakPtr<GLInProcessContextImpl> {
48 public:
49  explicit GLInProcessContextImpl(
50      const GLInProcessContextSharedMemoryLimits& mem_limits);
51  virtual ~GLInProcessContextImpl();
52
53  bool Initialize(
54      scoped_refptr<gfx::GLSurface> surface,
55      bool is_offscreen,
56      bool use_global_share_group,
57      GLInProcessContext* share_context,
58      gfx::AcceleratedWidget window,
59      const gfx::Size& size,
60      const gpu::gles2::ContextCreationAttribHelper& attribs,
61      gfx::GpuPreference gpu_preference,
62      const scoped_refptr<InProcessCommandBuffer::Service>& service);
63
64  // GLInProcessContext implementation:
65  virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE;
66  virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE;
67  virtual size_t GetMappedMemoryLimit() OVERRIDE;
68
69#if defined(OS_ANDROID)
70  virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
71      uint32 stream_id) OVERRIDE;
72#endif
73
74 private:
75  void Destroy();
76  void OnContextLost();
77  void OnSignalSyncPoint(const base::Closure& callback);
78
79  scoped_ptr<gles2::GLES2CmdHelper> gles2_helper_;
80  scoped_ptr<TransferBuffer> transfer_buffer_;
81  scoped_ptr<gles2::GLES2Implementation> gles2_implementation_;
82  scoped_ptr<InProcessCommandBuffer> command_buffer_;
83
84  const GLInProcessContextSharedMemoryLimits mem_limits_;
85  bool context_lost_;
86  base::Closure context_lost_callback_;
87
88  DISALLOW_COPY_AND_ASSIGN(GLInProcessContextImpl);
89};
90
91base::LazyInstance<base::Lock> g_all_shared_contexts_lock =
92    LAZY_INSTANCE_INITIALIZER;
93base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts =
94    LAZY_INSTANCE_INITIALIZER;
95
96GLInProcessContextImpl::GLInProcessContextImpl(
97    const GLInProcessContextSharedMemoryLimits& mem_limits)
98    : mem_limits_(mem_limits), context_lost_(false) {
99}
100
101GLInProcessContextImpl::~GLInProcessContextImpl() {
102  {
103    base::AutoLock lock(g_all_shared_contexts_lock.Get());
104    g_all_shared_contexts.Get().erase(this);
105  }
106  Destroy();
107}
108
109gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() {
110  return gles2_implementation_.get();
111}
112
113size_t GLInProcessContextImpl::GetMappedMemoryLimit() {
114  return mem_limits_.mapped_memory_reclaim_limit;
115}
116
117void GLInProcessContextImpl::SetContextLostCallback(
118    const base::Closure& callback) {
119  context_lost_callback_ = callback;
120}
121
122void GLInProcessContextImpl::OnContextLost() {
123  context_lost_ = true;
124  if (!context_lost_callback_.is_null()) {
125    context_lost_callback_.Run();
126  }
127}
128
129bool GLInProcessContextImpl::Initialize(
130    scoped_refptr<gfx::GLSurface> surface,
131    bool is_offscreen,
132    bool use_global_share_group,
133    GLInProcessContext* share_context,
134    gfx::AcceleratedWidget window,
135    const gfx::Size& size,
136    const gles2::ContextCreationAttribHelper& attribs,
137    gfx::GpuPreference gpu_preference,
138    const scoped_refptr<InProcessCommandBuffer::Service>& service) {
139  DCHECK(!use_global_share_group || !share_context);
140  DCHECK(size.width() >= 0 && size.height() >= 0);
141
142  std::vector<int32> attrib_vector;
143  attribs.Serialize(&attrib_vector);
144
145  base::Closure wrapped_callback =
146      base::Bind(&GLInProcessContextImpl::OnContextLost, AsWeakPtr());
147  command_buffer_.reset(new InProcessCommandBuffer(service));
148
149  scoped_ptr<base::AutoLock> scoped_shared_context_lock;
150  scoped_refptr<gles2::ShareGroup> share_group;
151  InProcessCommandBuffer* share_command_buffer = NULL;
152  if (use_global_share_group) {
153    scoped_shared_context_lock.reset(
154        new base::AutoLock(g_all_shared_contexts_lock.Get()));
155    for (std::set<GLInProcessContextImpl*>::const_iterator it =
156             g_all_shared_contexts.Get().begin();
157         it != g_all_shared_contexts.Get().end();
158         it++) {
159      const GLInProcessContextImpl* context = *it;
160      if (!context->context_lost_) {
161        share_group = context->gles2_implementation_->share_group();
162        share_command_buffer = context->command_buffer_.get();
163        DCHECK(share_group.get());
164        DCHECK(share_command_buffer);
165        break;
166      }
167    }
168  } else if (share_context) {
169    GLInProcessContextImpl* impl =
170        static_cast<GLInProcessContextImpl*>(share_context);
171    share_group = impl->gles2_implementation_->share_group();
172    share_command_buffer = impl->command_buffer_.get();
173    DCHECK(share_group.get());
174    DCHECK(share_command_buffer);
175  }
176
177  if (!command_buffer_->Initialize(surface,
178                                   is_offscreen,
179                                   window,
180                                   size,
181                                   attrib_vector,
182                                   gpu_preference,
183                                   wrapped_callback,
184                                   share_command_buffer)) {
185    LOG(ERROR) << "Failed to initialize InProcessCommmandBuffer";
186    return false;
187  }
188
189  // Create the GLES2 helper, which writes the command buffer protocol.
190  gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get()));
191  if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) {
192    LOG(ERROR) << "Failed to initialize GLES2CmdHelper";
193    Destroy();
194    return false;
195  }
196
197  // Create a transfer buffer.
198  transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get()));
199
200  // Check for consistency.
201  DCHECK(!attribs.bind_generates_resource);
202  bool bind_generates_resource = false;
203
204  // Create the object exposing the OpenGL API.
205  gles2_implementation_.reset(
206      new gles2::GLES2Implementation(gles2_helper_.get(),
207                                     share_group.get(),
208                                     transfer_buffer_.get(),
209                                     bind_generates_resource,
210                                     attribs.lose_context_when_out_of_memory,
211                                     command_buffer_.get()));
212
213  if (use_global_share_group) {
214    g_all_shared_contexts.Get().insert(this);
215    scoped_shared_context_lock.reset();
216  }
217
218  if (!gles2_implementation_->Initialize(
219          mem_limits_.start_transfer_buffer_size,
220          mem_limits_.min_transfer_buffer_size,
221          mem_limits_.max_transfer_buffer_size,
222          mem_limits_.mapped_memory_reclaim_limit)) {
223    return false;
224  }
225
226  return true;
227}
228
229void GLInProcessContextImpl::Destroy() {
230  if (gles2_implementation_) {
231    // First flush the context to ensure that any pending frees of resources
232    // are completed. Otherwise, if this context is part of a share group,
233    // those resources might leak. Also, any remaining side effects of commands
234    // issued on this context might not be visible to other contexts in the
235    // share group.
236    gles2_implementation_->Flush();
237
238    gles2_implementation_.reset();
239  }
240
241  transfer_buffer_.reset();
242  gles2_helper_.reset();
243  command_buffer_.reset();
244}
245
246#if defined(OS_ANDROID)
247scoped_refptr<gfx::SurfaceTexture>
248GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) {
249  return command_buffer_->GetSurfaceTexture(stream_id);
250}
251#endif
252
253}  // anonymous namespace
254
255GLInProcessContextSharedMemoryLimits::GLInProcessContextSharedMemoryLimits()
256    : command_buffer_size(kDefaultCommandBufferSize),
257      start_transfer_buffer_size(kDefaultStartTransferBufferSize),
258      min_transfer_buffer_size(kDefaultMinTransferBufferSize),
259      max_transfer_buffer_size(kDefaultMaxTransferBufferSize),
260      mapped_memory_reclaim_limit(gles2::GLES2Implementation::kNoLimit) {
261}
262
263// static
264GLInProcessContext* GLInProcessContext::Create(
265    scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
266    scoped_refptr<gfx::GLSurface> surface,
267    bool is_offscreen,
268    gfx::AcceleratedWidget window,
269    const gfx::Size& size,
270    GLInProcessContext* share_context,
271    bool use_global_share_group,
272    const ::gpu::gles2::ContextCreationAttribHelper& attribs,
273    gfx::GpuPreference gpu_preference,
274    const GLInProcessContextSharedMemoryLimits& memory_limits) {
275  DCHECK(!use_global_share_group || !share_context);
276  if (surface.get()) {
277    DCHECK_EQ(surface->IsOffscreen(), is_offscreen);
278    DCHECK(surface->GetSize() == size);
279    DCHECK_EQ(gfx::kNullAcceleratedWidget, window);
280  }
281
282  scoped_ptr<GLInProcessContextImpl> context(
283      new GLInProcessContextImpl(memory_limits));
284  if (!context->Initialize(surface,
285                           is_offscreen,
286                           use_global_share_group,
287                           share_context,
288                           window,
289                           size,
290                           attribs,
291                           gpu_preference,
292                           service))
293    return NULL;
294
295  return context.release();
296}
297
298}  // namespace gpu
299