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 16 namespace content { 17 18 class 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 38 scoped_refptr<ContextProviderCommandBuffer> 39 ContextProviderCommandBuffer::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 48 ContextProviderCommandBuffer::ContextProviderCommandBuffer( 49 scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d, 50 const std::string& debug_name) 51 : context3d_(context3d.Pass()), 52 debug_name_(debug_name), 53 leak_on_destroy_(false), 54 destroyed_(false) { 55 DCHECK(main_thread_checker_.CalledOnValidThread()); 56 DCHECK(context3d_); 57 context_thread_checker_.DetachFromThread(); 58 } 59 60 ContextProviderCommandBuffer::~ContextProviderCommandBuffer() { 61 DCHECK(main_thread_checker_.CalledOnValidThread() || 62 context_thread_checker_.CalledOnValidThread()); 63 64 base::AutoLock lock(main_thread_lock_); 65 66 // Destroy references to the context3d_ before leaking it. 67 if (context3d_->GetCommandBufferProxy()) { 68 context3d_->GetCommandBufferProxy()->SetMemoryAllocationChangedCallback( 69 CommandBufferProxyImpl::MemoryAllocationChangedCallback()); 70 } 71 lost_context_callback_proxy_.reset(); 72 73 if (leak_on_destroy_) { 74 WebGraphicsContext3DCommandBufferImpl* context3d ALLOW_UNUSED = 75 context3d_.release(); 76 webkit::gpu::GrContextForWebGraphicsContext3D* gr_context ALLOW_UNUSED = 77 gr_context_.release(); 78 } 79 } 80 81 bool ContextProviderCommandBuffer::BindToCurrentThread() { 82 // This is called on the thread the context will be used. 83 DCHECK(context_thread_checker_.CalledOnValidThread()); 84 85 if (lost_context_callback_proxy_) 86 return true; 87 88 if (!context3d_->makeContextCurrent()) 89 return false; 90 91 InitializeCapabilities(); 92 93 std::string unique_context_name = 94 base::StringPrintf("%s-%p", debug_name_.c_str(), context3d_.get()); 95 context3d_->pushGroupMarkerEXT(unique_context_name.c_str()); 96 97 lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this)); 98 context3d_->GetCommandBufferProxy()->SetMemoryAllocationChangedCallback( 99 base::Bind(&ContextProviderCommandBuffer::OnMemoryAllocationChanged, 100 base::Unretained(this))); 101 return true; 102 } 103 104 WebGraphicsContext3DCommandBufferImpl* 105 ContextProviderCommandBuffer::Context3d() { 106 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 107 DCHECK(context_thread_checker_.CalledOnValidThread()); 108 109 return context3d_.get(); 110 } 111 112 gpu::gles2::GLES2Interface* ContextProviderCommandBuffer::ContextGL() { 113 DCHECK(context3d_); 114 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 115 DCHECK(context_thread_checker_.CalledOnValidThread()); 116 117 return context3d_->GetImplementation(); 118 } 119 120 gpu::ContextSupport* ContextProviderCommandBuffer::ContextSupport() { 121 return context3d_->GetContextSupport(); 122 } 123 124 class GrContext* ContextProviderCommandBuffer::GrContext() { 125 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 126 DCHECK(context_thread_checker_.CalledOnValidThread()); 127 128 if (gr_context_) 129 return gr_context_->get(); 130 131 gr_context_.reset( 132 new webkit::gpu::GrContextForWebGraphicsContext3D(context3d_.get())); 133 return gr_context_->get(); 134 } 135 136 void ContextProviderCommandBuffer::MakeGrContextCurrent() { 137 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 138 DCHECK(context_thread_checker_.CalledOnValidThread()); 139 DCHECK(gr_context_); 140 141 context3d_->makeContextCurrent(); 142 } 143 144 cc::ContextProvider::Capabilities 145 ContextProviderCommandBuffer::ContextCapabilities() { 146 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 147 DCHECK(context_thread_checker_.CalledOnValidThread()); 148 149 return capabilities_; 150 } 151 152 bool ContextProviderCommandBuffer::IsContextLost() { 153 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 154 DCHECK(context_thread_checker_.CalledOnValidThread()); 155 156 return context3d_->isContextLost(); 157 } 158 159 void ContextProviderCommandBuffer::VerifyContexts() { 160 DCHECK(lost_context_callback_proxy_); // Is bound to thread. 161 DCHECK(context_thread_checker_.CalledOnValidThread()); 162 163 if (context3d_->isContextLost()) 164 OnLostContext(); 165 } 166 167 void ContextProviderCommandBuffer::OnLostContext() { 168 DCHECK(context_thread_checker_.CalledOnValidThread()); 169 { 170 base::AutoLock lock(main_thread_lock_); 171 if (destroyed_) 172 return; 173 destroyed_ = true; 174 } 175 if (!lost_context_callback_.is_null()) 176 base::ResetAndReturn(&lost_context_callback_).Run(); 177 } 178 179 void ContextProviderCommandBuffer::OnMemoryAllocationChanged( 180 const gpu::MemoryAllocation& allocation) { 181 DCHECK(context_thread_checker_.CalledOnValidThread()); 182 183 if (gr_context_) { 184 bool nonzero_allocation = !!allocation.bytes_limit_when_visible; 185 gr_context_->SetMemoryLimit(nonzero_allocation); 186 } 187 188 if (memory_policy_changed_callback_.is_null()) 189 return; 190 191 memory_policy_changed_callback_.Run(cc::ManagedMemoryPolicy(allocation)); 192 } 193 194 void ContextProviderCommandBuffer::InitializeCapabilities() { 195 Capabilities caps(context3d_->GetImplementation()->capabilities()); 196 197 size_t mapped_memory_limit = context3d_->GetMappedMemoryLimit(); 198 caps.max_transfer_buffer_usage_bytes = 199 mapped_memory_limit == WebGraphicsContext3DCommandBufferImpl::kNoLimit 200 ? std::numeric_limits<size_t>::max() : mapped_memory_limit; 201 202 capabilities_ = caps; 203 } 204 205 206 bool ContextProviderCommandBuffer::DestroyedOnMainThread() { 207 DCHECK(main_thread_checker_.CalledOnValidThread()); 208 209 base::AutoLock lock(main_thread_lock_); 210 return destroyed_; 211 } 212 213 void ContextProviderCommandBuffer::SetLostContextCallback( 214 const LostContextCallback& lost_context_callback) { 215 DCHECK(context_thread_checker_.CalledOnValidThread()); 216 DCHECK(lost_context_callback_.is_null() || 217 lost_context_callback.is_null()); 218 lost_context_callback_ = lost_context_callback; 219 } 220 221 void ContextProviderCommandBuffer::SetMemoryPolicyChangedCallback( 222 const MemoryPolicyChangedCallback& memory_policy_changed_callback) { 223 DCHECK(context_thread_checker_.CalledOnValidThread()); 224 DCHECK(memory_policy_changed_callback_.is_null() || 225 memory_policy_changed_callback.is_null()); 226 memory_policy_changed_callback_ = memory_policy_changed_callback; 227 } 228 229 } // namespace content 230