Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / common / gpu / client / webgraphicscontext3d_command_buffer_impl.cc
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.
4
5 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
6
7 #include "third_party/khronos/GLES2/gl2.h"
8 #ifndef GL_GLEXT_PROTOTYPES
9 #define GL_GLEXT_PROTOTYPES 1
10 #endif
11 #include "third_party/khronos/GLES2/gl2ext.h"
12
13 #include <algorithm>
14 #include <map>
15
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"
39
40 namespace content {
41
42 namespace {
43
44 static base::LazyInstance<base::Lock>::Leaky
45     g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER;
46
47 typedef std::map<GpuChannelHost*,
48     scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> >
49     ShareGroupMap;
50 static base::LazyInstance<ShareGroupMap> g_default_share_groups =
51     LAZY_INSTANCE_INITIALIZER;
52
53 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>
54     GetDefaultShareGroupForHost(GpuChannelHost* host) {
55   base::AutoLock lock(g_default_share_groups_lock.Get());
56
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;
63     return group;
64   }
65   return it->second;
66 }
67
68 } // namespace anonymous
69
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) {}
76
77 WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
78 }
79
80 WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
81   DCHECK(contexts_.empty());
82 }
83
84 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
85     int surface_id,
86     const GURL& active_url,
87     GpuChannelHost* host,
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),
94       visible_(false),
95       host_(host),
96       surface_id_(surface_id),
97       active_url_(active_url),
98       gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
99                                                    : gfx::PreferIntegratedGpu),
100       mem_limits_(limits),
101       weak_ptr_factory_(this) {
102   if (share_context) {
103     DCHECK(!attributes_.shareResources);
104     share_group_ = share_context->share_group_;
105   } else {
106     share_group_ = attributes_.shareResources
107         ? GetDefaultShareGroupForHost(host)
108         : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
109             new ShareGroup());
110   }
111 }
112
113 WebGraphicsContext3DCommandBufferImpl::
114     ~WebGraphicsContext3DCommandBufferImpl() {
115   if (real_gl_) {
116     real_gl_->SetErrorMessageCallback(NULL);
117   }
118
119   Destroy();
120 }
121
122 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
123   if (initialized_)
124     return true;
125
126   if (initialize_failed_)
127     return false;
128
129   TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
130
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;
136   stopwatch.Start();
137
138   if (!CreateContext(surface_id_ != 0)) {
139     Destroy();
140
141     stopwatch.Stop();
142
143     initialize_failed_ = true;
144     return false;
145   }
146
147   if (gl_ && attributes_.webGL)
148     gl_->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
149
150   command_buffer_->SetChannelErrorCallback(
151       base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost,
152                  weak_ptr_factory_.GetWeakPtr()));
153
154   command_buffer_->SetOnConsoleMessageCallback(
155       base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage,
156                  weak_ptr_factory_.GetWeakPtr()));
157
158   real_gl_->SetErrorMessageCallback(getErrorMessageCallback());
159
160   stopwatch.Stop();
161
162   visible_ = true;
163   initialized_ = true;
164   return true;
165 }
166
167 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
168     bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
169   if (!host_.get())
170     return false;
171
172   CommandBufferProxyImpl* share_group_command_buffer = NULL;
173
174   if (share_context) {
175     share_group_command_buffer = share_context->command_buffer_.get();
176   }
177
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);
185
186   // Create a proxy to a command buffer in the GPU process.
187   if (onscreen) {
188     command_buffer_.reset(host_->CreateViewCommandBuffer(
189         surface_id_,
190         share_group_command_buffer,
191         attribs,
192         active_url_,
193         gpu_preference_));
194   } else {
195     command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
196         gfx::Size(1, 1),
197         share_group_command_buffer,
198         attribs,
199         active_url_,
200         gpu_preference_));
201   }
202
203   if (!command_buffer_) {
204     DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
205     return false;
206   }
207
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.";
214   return result;
215 }
216
217 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) {
218   TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
219   scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group;
220
221   scoped_ptr<base::AutoLock> share_group_lock;
222   bool add_to_share_group = false;
223   if (!command_buffer_) {
224     WebGraphicsContext3DCommandBufferImpl* share_context = NULL;
225
226     share_group_lock.reset(new base::AutoLock(share_group_->lock()));
227     share_context = share_group_->GetAnyContextLocked();
228
229     if (!InitializeCommandBuffer(onscreen, share_context)) {
230       LOG(ERROR) << "Failed to initialize command buffer.";
231       return false;
232     }
233
234     if (share_context)
235       gles2_share_group = share_context->GetImplementation()->share_group();
236
237     add_to_share_group = true;
238   }
239
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.";
244     return false;
245   }
246
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()));
252
253   DCHECK(host_.get());
254
255   // Create the object exposing the OpenGL API.
256   const bool bind_generates_resources = false;
257   const bool support_client_side_arrays = false;
258
259   real_gl_.reset(
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());
268
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.";
275     return false;
276   }
277
278   if (add_to_share_group)
279     share_group_->AddContextLocked(this);
280
281   if (CommandLine::ForCurrentProcess()->HasSwitch(
282       switches::kEnableGpuClientTracing)) {
283     trace_gl_.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface()));
284     setGLInterface(trace_gl_.get());
285   }
286   return true;
287 }
288
289 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() {
290   if (!MaybeInitializeGL()) {
291     DLOG(ERROR) << "Failed to initialize context.";
292     return false;
293   }
294   if (gpu::error::IsError(command_buffer_->GetLastError())) {
295     LOG(ERROR) << "Context dead on arrival. Last error: "
296                << command_buffer_->GetLastError();
297     return false;
298   }
299
300   return true;
301 }
302
303 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
304   share_group_->RemoveContext(this);
305
306   gpu::gles2::GLES2Interface* gl = GetGLInterface();
307   if (gl) {
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
312     // share group.
313     gl->Flush();
314     setGLInterface(NULL);
315   }
316
317   trace_gl_.reset();
318   real_gl_.reset();
319   transfer_buffer_.reset();
320   gles2_helper_.reset();
321   real_gl_.reset();
322
323   if (command_buffer_) {
324     if (host_.get())
325       host_->DestroyCommandBuffer(command_buffer_.release());
326     command_buffer_.reset();
327   }
328
329   host_ = NULL;
330 }
331
332 gpu::ContextSupport*
333 WebGraphicsContext3DCommandBufferImpl::GetContextSupport() {
334   return real_gl_.get();
335 }
336
337 bool WebGraphicsContext3DCommandBufferImpl::isContextLost() {
338   return initialize_failed_ ||
339       (command_buffer_ && IsCommandBufferContextLost()) ||
340       context_lost_reason_ != GL_NO_ERROR;
341 }
342
343 WGC3Denum WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() {
344   if (IsCommandBufferContextLost() &&
345       context_lost_reason_ == GL_NO_ERROR) {
346     return GL_UNKNOWN_CONTEXT_RESET_ARB;
347   }
348
349   return context_lost_reason_;
350 }
351
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())
356     return true;
357   gpu::CommandBuffer::State state = command_buffer_->GetLastState();
358   return state.error == gpu::error::kLostContext;
359 }
360
361 // static
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) {
370   if (!host)
371     return NULL;
372
373   if (share_context && share_context->IsCommandBufferContextLost())
374     return NULL;
375
376   return new WebGraphicsContext3DCommandBufferImpl(
377       0,
378       active_url,
379       host,
380       attributes,
381       lose_context_when_out_of_memory,
382       limits,
383       share_context);
384 }
385
386 namespace {
387
388 WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
389   switch (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;
396   }
397
398   NOTREACHED();
399   return GL_UNKNOWN_CONTEXT_RESET_ARB;
400 }
401
402 }  // anonymous namespace
403
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();
409   }
410
411   share_group_->RemoveAllContexts();
412
413   DCHECK(host_.get());
414   {
415     base::AutoLock lock(g_default_share_groups_lock.Get());
416     g_default_share_groups.Get().erase(host_.get());
417   }
418 }
419
420 }  // namespace content