1// Copyright (c) 2013 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/common/gpu/client/context_provider_command_buffer.h"
6
7#include <set>
8#include <vector>
9
10#include "base/callback_helpers.h"
11#include "base/strings/stringprintf.h"
12#include "cc/output/managed_memory_policy.h"
13#include "gpu/command_buffer/client/gles2_implementation.h"
14#include "webkit/common/gpu/grcontext_for_webgraphicscontext3d.h"
15
16namespace content {
17
18class ContextProviderCommandBuffer::LostContextCallbackProxy
19    : public blink::WebGraphicsContext3D::WebGraphicsContextLostCallback {
20 public:
21  explicit LostContextCallbackProxy(ContextProviderCommandBuffer* provider)
22      : provider_(provider) {
23    provider_->context3d_->setContextLostCallback(this);
24  }
25
26  virtual ~LostContextCallbackProxy() {
27    provider_->context3d_->setContextLostCallback(NULL);
28  }
29
30  virtual void onContextLost() {
31    provider_->OnLostContext();
32  }
33
34 private:
35  ContextProviderCommandBuffer* provider_;
36};
37
38scoped_refptr<ContextProviderCommandBuffer>
39ContextProviderCommandBuffer::Create(
40    scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
41    const std::string& debug_name) {
42  if (!context3d)
43    return NULL;
44
45  return new ContextProviderCommandBuffer(context3d.Pass(), debug_name);
46}
47
48ContextProviderCommandBuffer::ContextProviderCommandBuffer(
49    scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
50    const std::string& debug_name)
51    : context3d_(context3d.Pass()),
52      debug_name_(debug_name),
53      destroyed_(false) {
54  DCHECK(main_thread_checker_.CalledOnValidThread());
55  DCHECK(context3d_);
56  context_thread_checker_.DetachFromThread();
57}
58
59ContextProviderCommandBuffer::~ContextProviderCommandBuffer() {
60  DCHECK(main_thread_checker_.CalledOnValidThread() ||
61         context_thread_checker_.CalledOnValidThread());
62
63  base::AutoLock lock(main_thread_lock_);
64
65  // Destroy references to the context3d_ before leaking it.
66  if (context3d_->GetCommandBufferProxy()) {
67    context3d_->GetCommandBufferProxy()->SetMemoryAllocationChangedCallback(
68        CommandBufferProxyImpl::MemoryAllocationChangedCallback());
69  }
70  lost_context_callback_proxy_.reset();
71}
72
73
74CommandBufferProxyImpl* ContextProviderCommandBuffer::GetCommandBufferProxy() {
75  return context3d_->GetCommandBufferProxy();
76}
77
78WebGraphicsContext3DCommandBufferImpl*
79ContextProviderCommandBuffer::WebContext3D() {
80  DCHECK(context3d_);
81  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
82  DCHECK(context_thread_checker_.CalledOnValidThread());
83
84  return context3d_.get();
85}
86
87bool ContextProviderCommandBuffer::BindToCurrentThread() {
88  // This is called on the thread the context will be used.
89  DCHECK(context_thread_checker_.CalledOnValidThread());
90
91  if (lost_context_callback_proxy_)
92    return true;
93
94  if (!context3d_->InitializeOnCurrentThread())
95    return false;
96
97  InitializeCapabilities();
98
99  std::string unique_context_name =
100      base::StringPrintf("%s-%p", debug_name_.c_str(), context3d_.get());
101  context3d_->pushGroupMarkerEXT(unique_context_name.c_str());
102
103  lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this));
104  context3d_->GetCommandBufferProxy()->SetMemoryAllocationChangedCallback(
105      base::Bind(&ContextProviderCommandBuffer::OnMemoryAllocationChanged,
106                 base::Unretained(this)));
107  return true;
108}
109
110gpu::gles2::GLES2Interface* ContextProviderCommandBuffer::ContextGL() {
111  DCHECK(context3d_);
112  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
113  DCHECK(context_thread_checker_.CalledOnValidThread());
114
115  return context3d_->GetImplementation();
116}
117
118gpu::ContextSupport* ContextProviderCommandBuffer::ContextSupport() {
119  return context3d_->GetContextSupport();
120}
121
122class GrContext* ContextProviderCommandBuffer::GrContext() {
123  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
124  DCHECK(context_thread_checker_.CalledOnValidThread());
125
126  if (gr_context_)
127    return gr_context_->get();
128
129  gr_context_.reset(
130      new webkit::gpu::GrContextForWebGraphicsContext3D(context3d_.get()));
131  return gr_context_->get();
132}
133
134cc::ContextProvider::Capabilities
135ContextProviderCommandBuffer::ContextCapabilities() {
136  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
137  DCHECK(context_thread_checker_.CalledOnValidThread());
138
139  return capabilities_;
140}
141
142bool ContextProviderCommandBuffer::IsContextLost() {
143  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
144  DCHECK(context_thread_checker_.CalledOnValidThread());
145
146  return context3d_->isContextLost();
147}
148
149void ContextProviderCommandBuffer::VerifyContexts() {
150  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
151  DCHECK(context_thread_checker_.CalledOnValidThread());
152
153  if (context3d_->isContextLost())
154    OnLostContext();
155}
156
157void ContextProviderCommandBuffer::DeleteCachedResources() {
158  DCHECK(context_thread_checker_.CalledOnValidThread());
159
160  if (gr_context_)
161    gr_context_->FreeGpuResources();
162}
163
164void ContextProviderCommandBuffer::OnLostContext() {
165  DCHECK(context_thread_checker_.CalledOnValidThread());
166  {
167    base::AutoLock lock(main_thread_lock_);
168    if (destroyed_)
169      return;
170    destroyed_ = true;
171  }
172  if (!lost_context_callback_.is_null())
173    base::ResetAndReturn(&lost_context_callback_).Run();
174  if (gr_context_)
175    gr_context_->OnLostContext();
176}
177
178void ContextProviderCommandBuffer::OnMemoryAllocationChanged(
179    const gpu::MemoryAllocation& allocation) {
180  DCHECK(context_thread_checker_.CalledOnValidThread());
181
182  if (memory_policy_changed_callback_.is_null())
183    return;
184
185  memory_policy_changed_callback_.Run(cc::ManagedMemoryPolicy(allocation));
186}
187
188void ContextProviderCommandBuffer::InitializeCapabilities() {
189  Capabilities caps;
190  caps.gpu = context3d_->GetImplementation()->capabilities();
191
192  size_t mapped_memory_limit = context3d_->GetMappedMemoryLimit();
193  caps.max_transfer_buffer_usage_bytes =
194      mapped_memory_limit == WebGraphicsContext3DCommandBufferImpl::kNoLimit
195      ? std::numeric_limits<size_t>::max() : mapped_memory_limit;
196
197  capabilities_ = caps;
198}
199
200bool ContextProviderCommandBuffer::DestroyedOnMainThread() {
201  DCHECK(main_thread_checker_.CalledOnValidThread());
202
203  base::AutoLock lock(main_thread_lock_);
204  return destroyed_;
205}
206
207void ContextProviderCommandBuffer::SetLostContextCallback(
208    const LostContextCallback& lost_context_callback) {
209  DCHECK(context_thread_checker_.CalledOnValidThread());
210  DCHECK(lost_context_callback_.is_null() ||
211         lost_context_callback.is_null());
212  lost_context_callback_ = lost_context_callback;
213}
214
215void ContextProviderCommandBuffer::SetMemoryPolicyChangedCallback(
216    const MemoryPolicyChangedCallback& memory_policy_changed_callback) {
217  DCHECK(context_thread_checker_.CalledOnValidThread());
218  DCHECK(memory_policy_changed_callback_.is_null() ||
219         memory_policy_changed_callback.is_null());
220  memory_policy_changed_callback_ = memory_policy_changed_callback;
221}
222
223}  // namespace content
224