[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / content / common / gpu / client / webgraphicscontext3d_command_buffer_impl.cc
blobe7c945f3ceafbf059efc90d19cc6576516979369
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
10 #endif
11 #include "third_party/khronos/GLES2/gl2ext.h"
13 #include <algorithm>
14 #include <map>
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"
40 namespace content {
42 namespace {
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> >
49 ShareGroupMap;
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;
63 return group;
65 return it->second;
68 // Singleton used to initialize and terminate the gles2 library.
69 class GLES2Initializer {
70 public:
71 GLES2Initializer() {
72 gles2::Initialize();
75 ~GLES2Initializer() {
76 gles2::Terminate();
79 private:
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(
107 int surface_id,
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),
116 visible_(false),
117 host_(host),
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) {
124 if (share_context) {
125 DCHECK(!attributes_.shareResources);
126 share_group_ = share_context->share_group_;
127 } else {
128 share_group_ = attributes_.shareResources
129 ? GetDefaultShareGroupForHost(host)
130 : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
131 new ShareGroup());
135 WebGraphicsContext3DCommandBufferImpl::
136 ~WebGraphicsContext3DCommandBufferImpl() {
137 if (real_gl_) {
138 real_gl_->SetErrorMessageCallback(NULL);
141 Destroy();
144 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
145 if (initialized_)
146 return true;
148 if (initialize_failed_)
149 return false;
151 TRACE_EVENT0("gpu", "WebGfxCtx3DCmdBfrImpl::MaybeInitializeGL");
153 if (!CreateContext(surface_id_ != 0)) {
154 Destroy();
155 initialize_failed_ = true;
156 return false;
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());
174 visible_ = true;
175 initialized_ = true;
176 return true;
179 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
180 bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
181 if (!host_.get())
182 return false;
184 CommandBufferProxyImpl* share_group_command_buffer = NULL;
186 if (share_context) {
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.
210 if (onscreen) {
211 command_buffer_.reset(host_->CreateViewCommandBuffer(
212 surface_id_,
213 share_group_command_buffer,
214 attribs,
215 active_url_,
216 gpu_preference_));
217 } else {
218 command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
219 gfx::Size(1, 1),
220 share_group_command_buffer,
221 attribs,
222 active_url_,
223 gpu_preference_));
226 if (!command_buffer_) {
227 DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
228 return false;
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.";
237 return result;
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.";
257 return false;
260 if (share_context)
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.";
270 return false;
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()));
279 DCHECK(host_.get());
281 // Create the object exposing the OpenGL API.
282 bool bind_generates_resources = false;
283 real_gl_.reset(
284 new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
285 gles2_share_group,
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.";
298 return false;
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());
309 return true;
312 bool WebGraphicsContext3DCommandBufferImpl::makeContextCurrent() {
313 if (!MaybeInitializeGL()) {
314 DLOG(ERROR) << "Failed to initialize context.";
315 return false;
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();
321 return false;
324 return true;
327 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
328 share_group_->RemoveContext(this);
330 gpu::gles2::GLES2Interface* gl = GetGLInterface();
331 if (gl) {
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
336 // share group.
337 gl->Flush();
338 setGLInterface(NULL);
341 trace_gl_.reset();
342 real_gl_.reset();
343 transfer_buffer_.reset();
344 gles2_helper_.reset();
345 real_gl_.reset();
347 if (command_buffer_) {
348 if (host_.get())
349 host_->DestroyCommandBuffer(command_buffer_.release());
350 command_buffer_.reset();
353 host_ = NULL;
356 gpu::ContextSupport*
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())
380 return true;
381 gpu::CommandBuffer::State state = command_buffer_->GetLastState();
382 return state.error == gpu::error::kLostContext;
385 // static
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) {
394 if (!host)
395 return NULL;
397 if (share_context && share_context->IsCommandBufferContextLost())
398 return NULL;
400 return new WebGraphicsContext3DCommandBufferImpl(
402 active_url,
403 host,
404 attributes,
405 lose_context_when_out_of_memory,
406 limits,
407 share_context);
410 namespace {
412 WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
413 switch (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;
422 NOTREACHED();
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();
437 DCHECK(host_.get());
439 base::AutoLock lock(g_default_share_groups_lock.Get());
440 g_default_share_groups.Get().erase(host_.get());
444 } // namespace content