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.
5 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
7 #include "third_party/khronos/GLES2/gl2.h"
8 #ifndef GL_GLEXT_PROTOTYPES
9 #define GL_GLEXT_PROTOTYPES 1
11 #include "third_party/khronos/GLES2/gl2ext.h"
16 #include "base/atomicops.h"
17 #include "base/bind.h"
18 #include "base/command_line.h"
19 #include "base/debug/trace_event.h"
20 #include "base/lazy_instance.h"
21 #include "base/logging.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/field_trial.h"
24 #include "base/metrics/histogram.h"
25 #include "base/tracked_objects.h"
26 #include "content/common/gpu/client/gpu_channel_host.h"
27 #include "content/public/common/content_constants.h"
28 #include "content/public/common/content_switches.h"
29 #include "gpu/GLES2/gl2extchromium.h"
30 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
31 #include "gpu/command_buffer/client/gles2_implementation.h"
32 #include "gpu/command_buffer/client/gles2_trace_implementation.h"
33 #include "gpu/command_buffer/client/transfer_buffer.h"
34 #include "gpu/command_buffer/common/constants.h"
35 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
36 #include "gpu/command_buffer/common/mailbox.h"
37 #include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h"
38 #include "third_party/skia/include/core/SkTypes.h"
44 static base::LazyInstance<base::Lock>::Leaky
45 g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER;
47 typedef std::map<GpuChannelHost*,
48 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> >
50 static base::LazyInstance<ShareGroupMap> g_default_share_groups =
51 LAZY_INSTANCE_INITIALIZER;
53 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>
54 GetDefaultShareGroupForHost(GpuChannelHost* host) {
55 base::AutoLock lock(g_default_share_groups_lock.Get());
57 ShareGroupMap& share_groups = g_default_share_groups.Get();
58 ShareGroupMap::iterator it = share_groups.find(host);
59 if (it == share_groups.end()) {
60 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> group =
61 new WebGraphicsContext3DCommandBufferImpl::ShareGroup();
62 share_groups[host] = group;
68 } // namespace anonymous
70 WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits::SharedMemoryLimits()
71 : command_buffer_size(kDefaultCommandBufferSize),
72 start_transfer_buffer_size(kDefaultStartTransferBufferSize),
73 min_transfer_buffer_size(kDefaultMinTransferBufferSize),
74 max_transfer_buffer_size(kDefaultMaxTransferBufferSize),
75 mapped_memory_reclaim_limit(gpu::gles2::GLES2Implementation::kNoLimit) {}
77 WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
80 WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
81 DCHECK(contexts_.empty());
84 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
86 const GURL& active_url,
88 const Attributes& attributes,
89 bool lose_context_when_out_of_memory,
90 const SharedMemoryLimits& limits,
91 WebGraphicsContext3DCommandBufferImpl* share_context)
92 : lose_context_when_out_of_memory_(lose_context_when_out_of_memory),
93 attributes_(attributes),
96 surface_id_(surface_id),
97 active_url_(active_url),
98 gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
99 : gfx::PreferIntegratedGpu),
101 weak_ptr_factory_(this) {
103 DCHECK(!attributes_.shareResources);
104 share_group_ = share_context->share_group_;
106 share_group_ = attributes_.shareResources
107 ? GetDefaultShareGroupForHost(host)
108 : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
113 WebGraphicsContext3DCommandBufferImpl::
114 ~WebGraphicsContext3DCommandBufferImpl() {
116 real_gl_->SetErrorMessageCallback(NULL);
122 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
126 if (initialize_failed_)
129 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
131 // Below, we perform an expensive one-time initialization that is required to
132 // get first pixels to the screen. This can't be called "jank" since there is
133 // nothing on the screen. Using TaskStopwatch to exclude the operation from
134 // jank calculations.
135 tracked_objects::TaskStopwatch stopwatch;
138 if (!CreateContext(surface_id_ != 0)) {
143 initialize_failed_ = true;
147 if (gl_ && attributes_.webGL)
148 gl_->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
150 command_buffer_->SetChannelErrorCallback(
151 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost,
152 weak_ptr_factory_.GetWeakPtr()));
154 command_buffer_->SetOnConsoleMessageCallback(
155 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage,
156 weak_ptr_factory_.GetWeakPtr()));
158 real_gl_->SetErrorMessageCallback(getErrorMessageCallback());
167 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
168 bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
172 CommandBufferProxyImpl* share_group_command_buffer = NULL;
175 share_group_command_buffer = share_context->command_buffer_.get();
178 ::gpu::gles2::ContextCreationAttribHelper attribs_for_gles2;
179 ConvertAttributes(attributes_, &attribs_for_gles2);
180 attribs_for_gles2.lose_context_when_out_of_memory =
181 lose_context_when_out_of_memory_;
182 DCHECK(attribs_for_gles2.buffer_preserved);
183 std::vector<int32> attribs;
184 attribs_for_gles2.Serialize(&attribs);
186 // Create a proxy to a command buffer in the GPU process.
188 command_buffer_.reset(host_->CreateViewCommandBuffer(
190 share_group_command_buffer,
195 command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
197 share_group_command_buffer,
203 if (!command_buffer_) {
204 DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
208 DVLOG_IF(1, gpu::error::IsError(command_buffer_->GetLastError()))
209 << "Context dead on arrival. Last error: "
210 << command_buffer_->GetLastError();
211 // Initialize the command buffer.
212 bool result = command_buffer_->Initialize();
213 LOG_IF(ERROR, !result) << "CommandBufferProxy::Initialize failed.";
217 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) {
218 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
219 scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group;
221 scoped_ptr<base::AutoLock> share_group_lock;
222 bool add_to_share_group = false;
223 if (!command_buffer_) {
224 WebGraphicsContext3DCommandBufferImpl* share_context = NULL;
226 share_group_lock.reset(new base::AutoLock(share_group_->lock()));
227 share_context = share_group_->GetAnyContextLocked();
229 if (!InitializeCommandBuffer(onscreen, share_context)) {
230 LOG(ERROR) << "Failed to initialize command buffer.";
235 gles2_share_group = share_context->GetImplementation()->share_group();
237 add_to_share_group = true;
240 // Create the GLES2 helper, which writes the command buffer protocol.
241 gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_.get()));
242 if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) {
243 LOG(ERROR) << "Failed to initialize GLES2CmdHelper.";
247 if (attributes_.noAutomaticFlushes)
248 gles2_helper_->SetAutomaticFlushes(false);
249 // Create a transfer buffer used to copy resources between the renderer
250 // process and the GPU process.
251 transfer_buffer_ .reset(new gpu::TransferBuffer(gles2_helper_.get()));
255 // Create the object exposing the OpenGL API.
256 const bool bind_generates_resources = false;
257 const bool support_client_side_arrays = false;
260 new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
261 gles2_share_group.get(),
262 transfer_buffer_.get(),
263 bind_generates_resources,
264 lose_context_when_out_of_memory_,
265 support_client_side_arrays,
266 command_buffer_.get()));
267 setGLInterface(real_gl_.get());
269 if (!real_gl_->Initialize(
270 mem_limits_.start_transfer_buffer_size,
271 mem_limits_.min_transfer_buffer_size,
272 mem_limits_.max_transfer_buffer_size,
273 mem_limits_.mapped_memory_reclaim_limit)) {
274 LOG(ERROR) << "Failed to initialize GLES2Implementation.";
278 if (add_to_share_group)
279 share_group_->AddContextLocked(this);
281 if (CommandLine::ForCurrentProcess()->HasSwitch(
282 switches::kEnableGpuClientTracing)) {
283 trace_gl_.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface()));
284 setGLInterface(trace_gl_.get());
289 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() {
290 if (!MaybeInitializeGL()) {
291 DLOG(ERROR) << "Failed to initialize context.";
294 if (gpu::error::IsError(command_buffer_->GetLastError())) {
295 LOG(ERROR) << "Context dead on arrival. Last error: "
296 << command_buffer_->GetLastError();
303 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
304 share_group_->RemoveContext(this);
306 gpu::gles2::GLES2Interface* gl = GetGLInterface();
308 // First flush the context to ensure that any pending frees of resources
309 // are completed. Otherwise, if this context is part of a share group,
310 // those resources might leak. Also, any remaining side effects of commands
311 // issued on this context might not be visible to other contexts in the
314 setGLInterface(NULL);
319 transfer_buffer_.reset();
320 gles2_helper_.reset();
323 if (command_buffer_) {
325 host_->DestroyCommandBuffer(command_buffer_.release());
326 command_buffer_.reset();
333 WebGraphicsContext3DCommandBufferImpl::GetContextSupport() {
334 return real_gl_.get();
337 bool WebGraphicsContext3DCommandBufferImpl::isContextLost() {
338 return initialize_failed_ ||
339 (command_buffer_ && IsCommandBufferContextLost()) ||
340 context_lost_reason_ != GL_NO_ERROR;
343 WGC3Denum WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() {
344 if (IsCommandBufferContextLost() &&
345 context_lost_reason_ == GL_NO_ERROR) {
346 return GL_UNKNOWN_CONTEXT_RESET_ARB;
349 return context_lost_reason_;
352 bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() {
353 // If the channel shut down unexpectedly, let that supersede the
354 // command buffer's state.
355 if (host_.get() && host_->IsLost())
357 gpu::CommandBuffer::State state = command_buffer_->GetLastState();
358 return state.error == gpu::error::kLostContext;
362 WebGraphicsContext3DCommandBufferImpl*
363 WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
364 GpuChannelHost* host,
365 const WebGraphicsContext3D::Attributes& attributes,
366 bool lose_context_when_out_of_memory,
367 const GURL& active_url,
368 const SharedMemoryLimits& limits,
369 WebGraphicsContext3DCommandBufferImpl* share_context) {
373 if (share_context && share_context->IsCommandBufferContextLost())
376 return new WebGraphicsContext3DCommandBufferImpl(
381 lose_context_when_out_of_memory,
388 WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
390 case gpu::error::kGuilty:
391 return GL_GUILTY_CONTEXT_RESET_ARB;
392 case gpu::error::kInnocent:
393 return GL_INNOCENT_CONTEXT_RESET_ARB;
394 case gpu::error::kUnknown:
395 return GL_UNKNOWN_CONTEXT_RESET_ARB;
399 return GL_UNKNOWN_CONTEXT_RESET_ARB;
402 } // anonymous namespace
404 void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
405 context_lost_reason_ = convertReason(
406 command_buffer_->GetLastState().context_lost_reason);
407 if (context_lost_callback_) {
408 context_lost_callback_->onContextLost();
411 share_group_->RemoveAllContexts();
415 base::AutoLock lock(g_default_share_groups_lock.Get());
416 g_default_share_groups.Get().erase(host_.get());
420 } // namespace content