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/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/metrics/field_trial.h"
23 #include "base/metrics/histogram.h"
24 #include "base/profiler/scoped_tracker.h"
25 #include "base/trace_event/trace_event.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"
40 using blink::WGC3Denum
;
46 static base::LazyInstance
<base::Lock
>::Leaky
47 g_default_share_groups_lock
= LAZY_INSTANCE_INITIALIZER
;
49 typedef std::map
<GpuChannelHost
*,
50 scoped_refptr
<WebGraphicsContext3DCommandBufferImpl::ShareGroup
> >
52 static base::LazyInstance
<ShareGroupMap
> g_default_share_groups
=
53 LAZY_INSTANCE_INITIALIZER
;
55 scoped_refptr
<WebGraphicsContext3DCommandBufferImpl::ShareGroup
>
56 GetDefaultShareGroupForHost(GpuChannelHost
* host
) {
57 base::AutoLock
lock(g_default_share_groups_lock
.Get());
59 ShareGroupMap
& share_groups
= g_default_share_groups
.Get();
60 ShareGroupMap::iterator it
= share_groups
.find(host
);
61 if (it
== share_groups
.end()) {
62 scoped_refptr
<WebGraphicsContext3DCommandBufferImpl::ShareGroup
> group
=
63 new WebGraphicsContext3DCommandBufferImpl::ShareGroup();
64 share_groups
[host
] = group
;
70 } // namespace anonymous
72 WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits::SharedMemoryLimits()
73 : command_buffer_size(kDefaultCommandBufferSize
),
74 start_transfer_buffer_size(kDefaultStartTransferBufferSize
),
75 min_transfer_buffer_size(kDefaultMinTransferBufferSize
),
76 max_transfer_buffer_size(kDefaultMaxTransferBufferSize
),
77 mapped_memory_reclaim_limit(gpu::gles2::GLES2Implementation::kNoLimit
) {}
79 WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
82 WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
83 DCHECK(contexts_
.empty());
86 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
88 const GURL
& active_url
,
90 const Attributes
& attributes
,
91 bool lose_context_when_out_of_memory
,
92 const SharedMemoryLimits
& limits
,
93 WebGraphicsContext3DCommandBufferImpl
* share_context
)
94 : lose_context_when_out_of_memory_(lose_context_when_out_of_memory
),
95 attributes_(attributes
),
98 surface_id_(surface_id
),
99 active_url_(active_url
),
100 gpu_preference_(attributes
.preferDiscreteGPU
? gfx::PreferDiscreteGpu
101 : gfx::PreferIntegratedGpu
),
103 weak_ptr_factory_(this) {
105 DCHECK(!attributes_
.shareResources
);
106 share_group_
= share_context
->share_group_
;
108 share_group_
= attributes_
.shareResources
109 ? GetDefaultShareGroupForHost(host
)
110 : scoped_refptr
<WebGraphicsContext3DCommandBufferImpl::ShareGroup
>(
115 WebGraphicsContext3DCommandBufferImpl::
116 ~WebGraphicsContext3DCommandBufferImpl() {
118 real_gl_
->SetErrorMessageCallback(NULL
);
124 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
128 if (initialize_failed_
)
131 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
133 // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
134 tracked_objects::ScopedTracker
tracking_profile(
135 FROM_HERE_WITH_EXPLICIT_FUNCTION(
136 "125248 WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL"));
138 if (!CreateContext(surface_id_
!= 0)) {
141 initialize_failed_
= true;
145 if (gl_
&& attributes_
.webGL
)
146 gl_
->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
148 command_buffer_
->SetChannelErrorCallback(
149 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost
,
150 weak_ptr_factory_
.GetWeakPtr()));
152 command_buffer_
->SetOnConsoleMessageCallback(
153 base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage
,
154 weak_ptr_factory_
.GetWeakPtr()));
156 real_gl_
->SetErrorMessageCallback(getErrorMessageCallback());
163 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
164 bool onscreen
, WebGraphicsContext3DCommandBufferImpl
* share_context
) {
168 CommandBufferProxyImpl
* share_group_command_buffer
= NULL
;
171 share_group_command_buffer
= share_context
->command_buffer_
.get();
174 ::gpu::gles2::ContextCreationAttribHelper attribs_for_gles2
;
175 ConvertAttributes(attributes_
, &attribs_for_gles2
);
176 attribs_for_gles2
.lose_context_when_out_of_memory
=
177 lose_context_when_out_of_memory_
;
178 DCHECK(attribs_for_gles2
.buffer_preserved
);
179 std::vector
<int32
> attribs
;
180 attribs_for_gles2
.Serialize(&attribs
);
182 // Create a proxy to a command buffer in the GPU process.
184 command_buffer_
.reset(host_
->CreateViewCommandBuffer(
186 share_group_command_buffer
,
191 command_buffer_
.reset(host_
->CreateOffscreenCommandBuffer(
193 share_group_command_buffer
,
199 if (!command_buffer_
) {
200 DLOG(ERROR
) << "GpuChannelHost failed to create command buffer.";
204 DVLOG_IF(1, gpu::error::IsError(command_buffer_
->GetLastError()))
205 << "Context dead on arrival. Last error: "
206 << command_buffer_
->GetLastError();
207 // Initialize the command buffer.
208 bool result
= command_buffer_
->Initialize();
209 LOG_IF(ERROR
, !result
) << "CommandBufferProxy::Initialize failed.";
213 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen
) {
214 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
215 scoped_refptr
<gpu::gles2::ShareGroup
> gles2_share_group
;
217 scoped_ptr
<base::AutoLock
> share_group_lock
;
218 bool add_to_share_group
= false;
219 if (!command_buffer_
) {
220 WebGraphicsContext3DCommandBufferImpl
* share_context
= NULL
;
222 share_group_lock
.reset(new base::AutoLock(share_group_
->lock()));
223 share_context
= share_group_
->GetAnyContextLocked();
225 if (!InitializeCommandBuffer(onscreen
, share_context
)) {
226 LOG(ERROR
) << "Failed to initialize command buffer.";
231 gles2_share_group
= share_context
->GetImplementation()->share_group();
233 add_to_share_group
= true;
236 // Create the GLES2 helper, which writes the command buffer protocol.
237 gles2_helper_
.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_
.get()));
238 if (!gles2_helper_
->Initialize(mem_limits_
.command_buffer_size
)) {
239 LOG(ERROR
) << "Failed to initialize GLES2CmdHelper.";
243 if (attributes_
.noAutomaticFlushes
)
244 gles2_helper_
->SetAutomaticFlushes(false);
245 // Create a transfer buffer used to copy resources between the renderer
246 // process and the GPU process.
247 transfer_buffer_
.reset(new gpu::TransferBuffer(gles2_helper_
.get()));
251 // Create the object exposing the OpenGL API.
252 const bool bind_generates_resources
= false;
253 const bool support_client_side_arrays
= false;
256 new gpu::gles2::GLES2Implementation(gles2_helper_
.get(),
257 gles2_share_group
.get(),
258 transfer_buffer_
.get(),
259 bind_generates_resources
,
260 lose_context_when_out_of_memory_
,
261 support_client_side_arrays
,
262 command_buffer_
.get()));
263 setGLInterface(real_gl_
.get());
265 if (!real_gl_
->Initialize(
266 mem_limits_
.start_transfer_buffer_size
,
267 mem_limits_
.min_transfer_buffer_size
,
268 mem_limits_
.max_transfer_buffer_size
,
269 mem_limits_
.mapped_memory_reclaim_limit
)) {
270 LOG(ERROR
) << "Failed to initialize GLES2Implementation.";
274 if (add_to_share_group
)
275 share_group_
->AddContextLocked(this);
277 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
278 switches::kEnableGpuClientTracing
)) {
279 trace_gl_
.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface()));
280 setGLInterface(trace_gl_
.get());
285 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() {
286 if (!MaybeInitializeGL()) {
287 DLOG(ERROR
) << "Failed to initialize context.";
290 if (gpu::error::IsError(command_buffer_
->GetLastError())) {
291 LOG(ERROR
) << "Context dead on arrival. Last error: "
292 << command_buffer_
->GetLastError();
299 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
300 share_group_
->RemoveContext(this);
302 gpu::gles2::GLES2Interface
* gl
= GetGLInterface();
304 // First flush the context to ensure that any pending frees of resources
305 // are completed. Otherwise, if this context is part of a share group,
306 // those resources might leak. Also, any remaining side effects of commands
307 // issued on this context might not be visible to other contexts in the
310 setGLInterface(NULL
);
315 transfer_buffer_
.reset();
316 gles2_helper_
.reset();
319 if (command_buffer_
) {
321 host_
->DestroyCommandBuffer(command_buffer_
.release());
322 command_buffer_
.reset();
329 WebGraphicsContext3DCommandBufferImpl::GetContextSupport() {
330 return real_gl_
.get();
333 bool WebGraphicsContext3DCommandBufferImpl::isContextLost() {
334 return initialize_failed_
||
335 (command_buffer_
&& IsCommandBufferContextLost()) ||
336 context_lost_reason_
!= GL_NO_ERROR
;
339 WGC3Denum
WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() {
340 if (IsCommandBufferContextLost() &&
341 context_lost_reason_
== GL_NO_ERROR
) {
342 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
345 return context_lost_reason_
;
348 bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() {
349 // If the channel shut down unexpectedly, let that supersede the
350 // command buffer's state.
351 if (host_
.get() && host_
->IsLost())
353 gpu::CommandBuffer::State state
= command_buffer_
->GetLastState();
354 return state
.error
== gpu::error::kLostContext
;
358 WebGraphicsContext3DCommandBufferImpl
*
359 WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
360 GpuChannelHost
* host
,
361 const WebGraphicsContext3D::Attributes
& attributes
,
362 bool lose_context_when_out_of_memory
,
363 const GURL
& active_url
,
364 const SharedMemoryLimits
& limits
,
365 WebGraphicsContext3DCommandBufferImpl
* share_context
) {
369 if (share_context
&& share_context
->IsCommandBufferContextLost())
372 return new WebGraphicsContext3DCommandBufferImpl(
377 lose_context_when_out_of_memory
,
384 WGC3Denum
convertReason(gpu::error::ContextLostReason reason
) {
386 case gpu::error::kGuilty
:
387 return GL_GUILTY_CONTEXT_RESET_ARB
;
388 case gpu::error::kInnocent
:
389 return GL_INNOCENT_CONTEXT_RESET_ARB
;
390 case gpu::error::kUnknown
:
391 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
395 return GL_UNKNOWN_CONTEXT_RESET_ARB
;
398 } // anonymous namespace
400 void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
401 context_lost_reason_
= convertReason(
402 command_buffer_
->GetLastState().context_lost_reason
);
403 if (context_lost_callback_
) {
404 context_lost_callback_
->onContextLost();
407 share_group_
->RemoveAllContexts();
411 base::AutoLock
lock(g_default_share_groups_lock
.Get());
412 g_default_share_groups
.Get().erase(host_
.get());
416 } // namespace content