83d9dbfdb0945ce46429b17b146ed57b7d8a377b
[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 "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_trace_implementation.h"
32 #include "gpu/command_buffer/client/transfer_buffer.h"
33 #include "gpu/command_buffer/common/constants.h"
34 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
35 #include "gpu/command_buffer/common/mailbox.h"
36 #include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h"
37 #include "third_party/skia/include/core/SkTypes.h"
38
39 namespace content {
40
41 namespace {
42
43 static base::LazyInstance<base::Lock>::Leaky
44     g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER;
45
46 typedef std::map<GpuChannelHost*,
47     scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> >
48     ShareGroupMap;
49 static base::LazyInstance<ShareGroupMap> g_default_share_groups =
50     LAZY_INSTANCE_INITIALIZER;
51
52 scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>
53     GetDefaultShareGroupForHost(GpuChannelHost* host) {
54   base::AutoLock lock(g_default_share_groups_lock.Get());
55
56   ShareGroupMap& share_groups = g_default_share_groups.Get();
57   ShareGroupMap::iterator it = share_groups.find(host);
58   if (it == share_groups.end()) {
59     scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> group =
60         new WebGraphicsContext3DCommandBufferImpl::ShareGroup();
61     share_groups[host] = group;
62     return group;
63   }
64   return it->second;
65 }
66
67 } // namespace anonymous
68
69 WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits::SharedMemoryLimits()
70     : command_buffer_size(kDefaultCommandBufferSize),
71       start_transfer_buffer_size(kDefaultStartTransferBufferSize),
72       min_transfer_buffer_size(kDefaultMinTransferBufferSize),
73       max_transfer_buffer_size(kDefaultMaxTransferBufferSize),
74       mapped_memory_reclaim_limit(gpu::gles2::GLES2Implementation::kNoLimit) {}
75
76 WebGraphicsContext3DCommandBufferImpl::ShareGroup::ShareGroup() {
77 }
78
79 WebGraphicsContext3DCommandBufferImpl::ShareGroup::~ShareGroup() {
80   DCHECK(contexts_.empty());
81 }
82
83 WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
84     int surface_id,
85     const GURL& active_url,
86     GpuChannelHost* host,
87     const Attributes& attributes,
88     bool lose_context_when_out_of_memory,
89     const SharedMemoryLimits& limits,
90     WebGraphicsContext3DCommandBufferImpl* share_context)
91     : lose_context_when_out_of_memory_(lose_context_when_out_of_memory),
92       attributes_(attributes),
93       visible_(false),
94       host_(host),
95       surface_id_(surface_id),
96       active_url_(active_url),
97       gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
98                                                    : gfx::PreferIntegratedGpu),
99       mem_limits_(limits),
100       weak_ptr_factory_(this) {
101   if (share_context) {
102     DCHECK(!attributes_.shareResources);
103     share_group_ = share_context->share_group_;
104   } else {
105     share_group_ = attributes_.shareResources
106         ? GetDefaultShareGroupForHost(host)
107         : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
108             new ShareGroup());
109   }
110 }
111
112 WebGraphicsContext3DCommandBufferImpl::
113     ~WebGraphicsContext3DCommandBufferImpl() {
114   if (real_gl_) {
115     real_gl_->SetErrorMessageCallback(NULL);
116   }
117
118   Destroy();
119 }
120
121 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
122   if (initialized_)
123     return true;
124
125   if (initialize_failed_)
126     return false;
127
128   TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
129
130   if (!CreateContext(surface_id_ != 0)) {
131     Destroy();
132     initialize_failed_ = true;
133     return false;
134   }
135
136   if (gl_ && attributes_.webGL)
137     gl_->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
138
139   command_buffer_->SetChannelErrorCallback(
140       base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost,
141                  weak_ptr_factory_.GetWeakPtr()));
142
143   command_buffer_->SetOnConsoleMessageCallback(
144       base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnErrorMessage,
145                  weak_ptr_factory_.GetWeakPtr()));
146
147   real_gl_->SetErrorMessageCallback(getErrorMessageCallback());
148
149   visible_ = true;
150   initialized_ = true;
151   return true;
152 }
153
154 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
155     bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
156   if (!host_.get())
157     return false;
158
159   CommandBufferProxyImpl* share_group_command_buffer = NULL;
160
161   if (share_context) {
162     share_group_command_buffer = share_context->command_buffer_.get();
163   }
164
165   ::gpu::gles2::ContextCreationAttribHelper attribs_for_gles2;
166   ConvertAttributes(attributes_, &attribs_for_gles2);
167   attribs_for_gles2.lose_context_when_out_of_memory =
168       lose_context_when_out_of_memory_;
169   DCHECK(attribs_for_gles2.buffer_preserved);
170   std::vector<int32> attribs;
171   attribs_for_gles2.Serialize(&attribs);
172
173   // Create a proxy to a command buffer in the GPU process.
174   if (onscreen) {
175     command_buffer_.reset(host_->CreateViewCommandBuffer(
176         surface_id_,
177         share_group_command_buffer,
178         attribs,
179         active_url_,
180         gpu_preference_));
181   } else {
182     command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
183         gfx::Size(1, 1),
184         share_group_command_buffer,
185         attribs,
186         active_url_,
187         gpu_preference_));
188   }
189
190   if (!command_buffer_) {
191     DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
192     return false;
193   }
194
195   DVLOG_IF(1, gpu::error::IsError(command_buffer_->GetLastError()))
196       << "Context dead on arrival. Last error: "
197       << command_buffer_->GetLastError();
198   // Initialize the command buffer.
199   bool result = command_buffer_->Initialize();
200   LOG_IF(ERROR, !result) << "CommandBufferProxy::Initialize failed.";
201   return result;
202 }
203
204 bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) {
205   TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::CreateContext");
206   scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group;
207
208   scoped_ptr<base::AutoLock> share_group_lock;
209   bool add_to_share_group = false;
210   if (!command_buffer_) {
211     WebGraphicsContext3DCommandBufferImpl* share_context = NULL;
212
213     share_group_lock.reset(new base::AutoLock(share_group_->lock()));
214     share_context = share_group_->GetAnyContextLocked();
215
216     if (!InitializeCommandBuffer(onscreen, share_context)) {
217       LOG(ERROR) << "Failed to initialize command buffer.";
218       return false;
219     }
220
221     if (share_context)
222       gles2_share_group = share_context->GetImplementation()->share_group();
223
224     add_to_share_group = true;
225   }
226
227   // Create the GLES2 helper, which writes the command buffer protocol.
228   gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_.get()));
229   if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) {
230     LOG(ERROR) << "Failed to initialize GLES2CmdHelper.";
231     return false;
232   }
233
234   if (attributes_.noAutomaticFlushes)
235     gles2_helper_->SetAutomaticFlushes(false);
236   // Create a transfer buffer used to copy resources between the renderer
237   // process and the GPU process.
238   transfer_buffer_ .reset(new gpu::TransferBuffer(gles2_helper_.get()));
239
240   DCHECK(host_.get());
241
242   // Create the object exposing the OpenGL API.
243   bool bind_generates_resources = false;
244   real_gl_.reset(
245       new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
246                                           gles2_share_group.get(),
247                                           transfer_buffer_.get(),
248                                           bind_generates_resources,
249                                           lose_context_when_out_of_memory_,
250                                           command_buffer_.get()));
251   setGLInterface(real_gl_.get());
252
253   if (!real_gl_->Initialize(
254       mem_limits_.start_transfer_buffer_size,
255       mem_limits_.min_transfer_buffer_size,
256       mem_limits_.max_transfer_buffer_size,
257       mem_limits_.mapped_memory_reclaim_limit)) {
258     LOG(ERROR) << "Failed to initialize GLES2Implementation.";
259     return false;
260   }
261
262   if (add_to_share_group)
263     share_group_->AddContextLocked(this);
264
265   if (CommandLine::ForCurrentProcess()->HasSwitch(
266       switches::kEnableGpuClientTracing)) {
267     trace_gl_.reset(new gpu::gles2::GLES2TraceImplementation(GetGLInterface()));
268     setGLInterface(trace_gl_.get());
269   }
270   return true;
271 }
272
273 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() {
274   if (!MaybeInitializeGL()) {
275     DLOG(ERROR) << "Failed to initialize context.";
276     return false;
277   }
278   if (gpu::error::IsError(command_buffer_->GetLastError())) {
279     LOG(ERROR) << "Context dead on arrival. Last error: "
280                << command_buffer_->GetLastError();
281     return false;
282   }
283
284   return true;
285 }
286
287 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
288   share_group_->RemoveContext(this);
289
290   gpu::gles2::GLES2Interface* gl = GetGLInterface();
291   if (gl) {
292     // First flush the context to ensure that any pending frees of resources
293     // are completed. Otherwise, if this context is part of a share group,
294     // those resources might leak. Also, any remaining side effects of commands
295     // issued on this context might not be visible to other contexts in the
296     // share group.
297     gl->Flush();
298     setGLInterface(NULL);
299   }
300
301   trace_gl_.reset();
302   real_gl_.reset();
303   transfer_buffer_.reset();
304   gles2_helper_.reset();
305   real_gl_.reset();
306
307   if (command_buffer_) {
308     if (host_.get())
309       host_->DestroyCommandBuffer(command_buffer_.release());
310     command_buffer_.reset();
311   }
312
313   host_ = NULL;
314 }
315
316 gpu::ContextSupport*
317 WebGraphicsContext3DCommandBufferImpl::GetContextSupport() {
318   return real_gl_.get();
319 }
320
321 bool WebGraphicsContext3DCommandBufferImpl::isContextLost() {
322   return initialize_failed_ ||
323       (command_buffer_ && IsCommandBufferContextLost()) ||
324       context_lost_reason_ != GL_NO_ERROR;
325 }
326
327 WGC3Denum WebGraphicsContext3DCommandBufferImpl::getGraphicsResetStatusARB() {
328   if (IsCommandBufferContextLost() &&
329       context_lost_reason_ == GL_NO_ERROR) {
330     return GL_UNKNOWN_CONTEXT_RESET_ARB;
331   }
332
333   return context_lost_reason_;
334 }
335
336 bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() {
337   // If the channel shut down unexpectedly, let that supersede the
338   // command buffer's state.
339   if (host_.get() && host_->IsLost())
340     return true;
341   gpu::CommandBuffer::State state = command_buffer_->GetLastState();
342   return state.error == gpu::error::kLostContext;
343 }
344
345 // static
346 WebGraphicsContext3DCommandBufferImpl*
347 WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
348     GpuChannelHost* host,
349     const WebGraphicsContext3D::Attributes& attributes,
350     bool lose_context_when_out_of_memory,
351     const GURL& active_url,
352     const SharedMemoryLimits& limits,
353     WebGraphicsContext3DCommandBufferImpl* share_context) {
354   if (!host)
355     return NULL;
356
357   if (share_context && share_context->IsCommandBufferContextLost())
358     return NULL;
359
360   return new WebGraphicsContext3DCommandBufferImpl(
361       0,
362       active_url,
363       host,
364       attributes,
365       lose_context_when_out_of_memory,
366       limits,
367       share_context);
368 }
369
370 namespace {
371
372 WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
373   switch (reason) {
374   case gpu::error::kGuilty:
375     return GL_GUILTY_CONTEXT_RESET_ARB;
376   case gpu::error::kInnocent:
377     return GL_INNOCENT_CONTEXT_RESET_ARB;
378   case gpu::error::kUnknown:
379     return GL_UNKNOWN_CONTEXT_RESET_ARB;
380   }
381
382   NOTREACHED();
383   return GL_UNKNOWN_CONTEXT_RESET_ARB;
384 }
385
386 }  // anonymous namespace
387
388 void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
389   context_lost_reason_ = convertReason(
390       command_buffer_->GetLastState().context_lost_reason);
391   if (context_lost_callback_) {
392     context_lost_callback_->onContextLost();
393   }
394
395   share_group_->RemoveAllContexts();
396
397   DCHECK(host_.get());
398   {
399     base::AutoLock lock(g_default_share_groups_lock.Get());
400     g_default_share_groups.Get().erase(host_.get());
401   }
402 }
403
404 }  // namespace content