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 "content/common/gpu/client/gpu_channel_host.h"
26 #include "content/public/common/content_constants.h"
27 #include "content/public/common/content_switches.h"
28 #include "gpu/GLES2/gl2extchromium.h"
29 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
30 #include "gpu/command_buffer/client/gles2_implementation.h"
31 #include "gpu/command_buffer/client/gles2_lib.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 // Singleton used to initialize and terminate the gles2 library.
69 class GLES2Initializer
{
80 DISALLOW_COPY_AND_ASSIGN(GLES2Initializer
);
83 ////////////////////////////////////////////////////////////////////////////////
85 base::LazyInstance
<GLES2Initializer
> g_gles2_initializer
=
86 LAZY_INSTANCE_INITIALIZER
;
88 ////////////////////////////////////////////////////////////////////////////////
90 } // namespace anonymous
92 WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits::SharedMemoryLimits()
93 : command_buffer_size(kDefaultCommandBufferSize
),
94 start_transfer_buffer_size(kDefaultStartTransferBufferSize
),
95 min_transfer_buffer_size(kDefaultMinTransferBufferSize
),
96 max_transfer_buffer_size(kDefaultMaxTransferBufferSize
),
97 mapped_memory_reclaim_limit(gpu::gles2::GLES2Implementation::kNoLimit
) {}
99 WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
102 WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
103 DCHECK(contexts_
.empty());
106 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
108 const GURL
& active_url
,
109 GpuChannelHost
* host
,
110 const Attributes
& attributes
,
111 bool lose_context_when_out_of_memory
,
112 const SharedMemoryLimits
& limits
,
113 WebGraphicsContext3DCommandBufferImpl
* share_context
)
114 : lose_context_when_out_of_memory_(lose_context_when_out_of_memory
),
115 attributes_(attributes
),
118 surface_id_(surface_id
),
119 active_url_(active_url
),
120 gpu_preference_(attributes
.preferDiscreteGPU
? gfx::PreferDiscreteGpu
121 : gfx::PreferIntegratedGpu
),
122 weak_ptr_factory_(this),
123 mem_limits_(limits
) {
125 DCHECK(!attributes_
.shareResources
);
126 share_group_
= share_context
->share_group_
;
128 share_group_
= attributes_
.shareResources
129 ? GetDefaultShareGroupForHost(host
)
130 : scoped_refptr
<WebGraphicsContext3DCommandBufferImpl::ShareGroup
>(
135 WebGraphicsContext3DCommandBufferImpl::
136 ~WebGraphicsContext3DCommandBufferImpl() {
138 real_gl_
->SetErrorMessageCallback(NULL
);
144 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
148 if (initialize_failed_
)
151 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
153 if (!CreateContext(surface_id_
!= 0)) {
155 initialize_failed_
= true;
159 // TODO(twiz): This code is too fragile in that it assumes that only WebGL
160 // contexts will request noExtensions.
161 if (gl_
&& attributes_
.noExtensions
)
162 gl_
->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
164 command_buffer_
->SetChannelErrorCallback(
165 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost
,
166 weak_ptr_factory_
.GetWeakPtr()));
168 command_buffer_
->SetOnConsoleMessageCallback(
169 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage
,
170 weak_ptr_factory_
.GetWeakPtr()));
172 real_gl_
->SetErrorMessageCallback(getErrorMessageCallback());
179 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
180 bool onscreen
, WebGraphicsContext3DCommandBufferImpl
* share_context
) {
184 CommandBufferProxyImpl
* share_group_command_buffer
= NULL
;
187 share_group_command_buffer
= share_context
->command_buffer_
.get();
190 std::vector
<int32
> attribs
;
191 attribs
.push_back(ALPHA_SIZE
);
192 attribs
.push_back(attributes_
.alpha
? 8 : 0);
193 attribs
.push_back(DEPTH_SIZE
);
194 attribs
.push_back(attributes_
.depth
? 24 : 0);
195 attribs
.push_back(STENCIL_SIZE
);
196 attribs
.push_back(attributes_
.stencil
? 8 : 0);
197 attribs
.push_back(SAMPLES
);
198 attribs
.push_back(attributes_
.antialias
? 4 : 0);
199 attribs
.push_back(SAMPLE_BUFFERS
);
200 attribs
.push_back(attributes_
.antialias
? 1 : 0);
201 attribs
.push_back(FAIL_IF_MAJOR_PERF_CAVEAT
);
202 attribs
.push_back(attributes_
.failIfMajorPerformanceCaveat
? 1 : 0);
203 attribs
.push_back(LOSE_CONTEXT_WHEN_OUT_OF_MEMORY
);
204 attribs
.push_back(lose_context_when_out_of_memory_
? 1 : 0);
205 attribs
.push_back(BIND_GENERATES_RESOURCES
);
206 attribs
.push_back(0);
207 attribs
.push_back(NONE
);
209 // Create a proxy to a command buffer in the GPU process.
211 command_buffer_
.reset(host_
->CreateViewCommandBuffer(
213 share_group_command_buffer
,
218 command_buffer_
.reset(host_
->CreateOffscreenCommandBuffer(
220 share_group_command_buffer
,
226 if (!command_buffer_
) {
227 DLOG(ERROR
) << "GpuChannelHost failed to create command buffer.";
231 DVLOG_IF(1, gpu::error::IsError(command_buffer_
->GetLastError()))
232 << "Context dead on arrival. Last error: "
233 << command_buffer_
->GetLastError();
234 // Initialize the command buffer.
235 bool result
= command_buffer_
->Initialize();
236 LOG_IF(ERROR
, !result
) << "CommandBufferProxy::Initialize failed.";
240 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen
) {
241 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
242 // Ensure the gles2 library is initialized first in a thread safe way.
243 g_gles2_initializer
.Get();
245 scoped_refptr
<gpu::gles2::ShareGroup
> gles2_share_group
;
247 scoped_ptr
<base::AutoLock
> share_group_lock
;
248 bool add_to_share_group
= false;
249 if (!command_buffer_
) {
250 WebGraphicsContext3DCommandBufferImpl
* share_context
= NULL
;
252 share_group_lock
.reset(new base::AutoLock(share_group_
->lock()));
253 share_context
= share_group_
->GetAnyContextLocked();
255 if (!InitializeCommandBuffer(onscreen
, share_context
)) {
256 LOG(ERROR
) << "Failed to initialize command buffer.";
261 gles2_share_group
= share_context
->GetImplementation()->share_group();
263 add_to_share_group
= true;
266 // Create the GLES2 helper, which writes the command buffer protocol.
267 gles2_helper_
.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_
.get()));
268 if (!gles2_helper_
->Initialize(mem_limits_
.command_buffer_size
)) {
269 LOG(ERROR
) << "Failed to initialize GLES2CmdHelper.";
273 if (attributes_
.noAutomaticFlushes
)
274 gles2_helper_
->SetAutomaticFlushes(false);
275 // Create a transfer buffer used to copy resources between the renderer
276 // process and the GPU process.
277 transfer_buffer_
.reset(new gpu::TransferBuffer(gles2_helper_
.get()));
281 // Create the object exposing the OpenGL API.
282 bool bind_generates_resources
= false;
284 new gpu::gles2::GLES2Implementation(gles2_helper_
.get(),
286 transfer_buffer_
.get(),
287 bind_generates_resources
,
288 lose_context_when_out_of_memory_
,
289 command_buffer_
.get()));
290 setGLInterface(real_gl_
.get());
292 if (!real_gl_
->Initialize(
293 mem_limits_
.start_transfer_buffer_size
,
294 mem_limits_
.min_transfer_buffer_size
,
295 mem_limits_
.max_transfer_buffer_size
,
296 mem_limits_
.mapped_memory_reclaim_limit
)) {
297 LOG(ERROR
) << "Failed to initialize GLES2Implementation.";
301 if (add_to_share_group
)
302 share_group_
->AddContextLocked(this);
304 if (CommandLine::ForCurrentProcess()->HasSwitch(
305 switches::kEnableGpuClientTracing
)) {
306 trace_gl_
.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface()));
307 setGLInterface(trace_gl_
.get());
312 bool WebGraphicsContext3DCommandBufferImpl::makeContextCurrent() {
313 if (!MaybeInitializeGL()) {
314 DLOG(ERROR
) << "Failed to initialize context.";
317 gles2::SetGLContext(GetGLInterface());
318 if (gpu::error::IsError(command_buffer_
->GetLastError())) {
319 LOG(ERROR
) << "Context dead on arrival. Last error: "
320 << command_buffer_
->GetLastError();
327 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
328 share_group_
->RemoveContext(this);
330 gpu::gles2::GLES2Interface
* gl
= GetGLInterface();
332 // First flush the context to ensure that any pending frees of resources
333 // are completed. Otherwise, if this context is part of a share group,
334 // those resources might leak. Also, any remaining side effects of commands
335 // issued on this context might not be visible to other contexts in the
338 setGLInterface(NULL
);
343 transfer_buffer_
.reset();
344 gles2_helper_
.reset();
347 if (command_buffer_
) {
349 host_
->DestroyCommandBuffer(command_buffer_
.release());
350 command_buffer_
.reset();
357 WebGraphicsContext3DCommandBufferImpl::GetContextSupport() {
358 return real_gl_
.get();
361 bool WebGraphicsContext3DCommandBufferImpl::isContextLost() {
362 return initialize_failed_
||
363 (command_buffer_
&& IsCommandBufferContextLost()) ||
364 context_lost_reason_
!= GL_NO_ERROR
;
367 WGC3Denum
WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() {
368 if (IsCommandBufferContextLost() &&
369 context_lost_reason_
== GL_NO_ERROR
) {
370 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
373 return context_lost_reason_
;
376 bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() {
377 // If the channel shut down unexpectedly, let that supersede the
378 // command buffer's state.
379 if (host_
.get() && host_
->IsLost())
381 gpu::CommandBuffer::State state
= command_buffer_
->GetLastState();
382 return state
.error
== gpu::error::kLostContext
;
386 WebGraphicsContext3DCommandBufferImpl
*
387 WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
388 GpuChannelHost
* host
,
389 const WebGraphicsContext3D::Attributes
& attributes
,
390 bool lose_context_when_out_of_memory
,
391 const GURL
& active_url
,
392 const SharedMemoryLimits
& limits
,
393 WebGraphicsContext3DCommandBufferImpl
* share_context
) {
397 if (share_context
&& share_context
->IsCommandBufferContextLost())
400 return new WebGraphicsContext3DCommandBufferImpl(
405 lose_context_when_out_of_memory
,
412 WGC3Denum
convertReason(gpu::error::ContextLostReason reason
) {
414 case gpu::error::kGuilty
:
415 return GL_GUILTY_CONTEXT_RESET_ARB
;
416 case gpu::error::kInnocent
:
417 return GL_INNOCENT_CONTEXT_RESET_ARB
;
418 case gpu::error::kUnknown
:
419 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
423 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
426 } // anonymous namespace
428 void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
429 context_lost_reason_
= convertReason(
430 command_buffer_
->GetLastState().context_lost_reason
);
431 if (context_lost_callback_
) {
432 context_lost_callback_
->onContextLost();
435 share_group_
->RemoveAllContexts();
439 base::AutoLock
lock(g_default_share_groups_lock
.Get());
440 g_default_share_groups
.Get().erase(host_
.get());
444 } // namespace content