GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / content / common / gpu / client / webgraphicscontext3d_command_buffer_impl.cc
blob0cf028f773b3487a220b846db908d1b4c7cd3067
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/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;
42 namespace content {
44 namespace {
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> >
51 ShareGroupMap;
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;
65 return group;
67 return it->second;
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(
87 int surface_id,
88 const GURL& active_url,
89 GpuChannelHost* host,
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),
96 visible_(false),
97 host_(host),
98 surface_id_(surface_id),
99 active_url_(active_url),
100 gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
101 : gfx::PreferIntegratedGpu),
102 mem_limits_(limits),
103 weak_ptr_factory_(this) {
104 if (share_context) {
105 DCHECK(!attributes_.shareResources);
106 share_group_ = share_context->share_group_;
107 } else {
108 share_group_ = attributes_.shareResources
109 ? GetDefaultShareGroupForHost(host)
110 : scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
111 new ShareGroup());
115 WebGraphicsContext3DCommandBufferImpl::
116 ~WebGraphicsContext3DCommandBufferImpl() {
117 if (real_gl_) {
118 real_gl_->SetErrorMessageCallback(NULL);
121 Destroy();
124 bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
125 if (initialized_)
126 return true;
128 if (initialize_failed_)
129 return false;
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)) {
139 Destroy();
141 initialize_failed_ = true;
142 return false;
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());
158 visible_ = true;
159 initialized_ = true;
160 return true;
163 bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
164 bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
165 if (!host_.get())
166 return false;
168 CommandBufferProxyImpl* share_group_command_buffer = NULL;
170 if (share_context) {
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.
183 if (onscreen) {
184 command_buffer_.reset(host_->CreateViewCommandBuffer(
185 surface_id_,
186 share_group_command_buffer,
187 attribs,
188 active_url_,
189 gpu_preference_));
190 } else {
191 command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
192 gfx::Size(1, 1),
193 share_group_command_buffer,
194 attribs,
195 active_url_,
196 gpu_preference_));
199 if (!command_buffer_) {
200 DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
201 return false;
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.";
210 return result;
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.";
227 return false;
230 if (share_context)
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.";
240 return false;
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()));
249 DCHECK(host_.get());
251 // Create the object exposing the OpenGL API.
252 const bool bind_generates_resources = false;
253 const bool support_client_side_arrays = false;
255 real_gl_.reset(
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.";
271 return false;
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());
282 return true;
285 bool WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread() {
286 if (!MaybeInitializeGL()) {
287 DLOG(ERROR) << "Failed to initialize context.";
288 return false;
290 if (gpu::error::IsError(command_buffer_->GetLastError())) {
291 LOG(ERROR) << "Context dead on arrival. Last error: "
292 << command_buffer_->GetLastError();
293 return false;
296 return true;
299 void WebGraphicsContext3DCommandBufferImpl::Destroy() {
300 share_group_->RemoveContext(this);
302 gpu::gles2::GLES2Interface* gl = GetGLInterface();
303 if (gl) {
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
308 // share group.
309 gl->Flush();
310 setGLInterface(NULL);
313 trace_gl_.reset();
314 real_gl_.reset();
315 transfer_buffer_.reset();
316 gles2_helper_.reset();
317 real_gl_.reset();
319 if (command_buffer_) {
320 if (host_.get())
321 host_->DestroyCommandBuffer(command_buffer_.release());
322 command_buffer_.reset();
325 host_ = NULL;
328 gpu::ContextSupport*
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())
352 return true;
353 gpu::CommandBuffer::State state = command_buffer_->GetLastState();
354 return state.error == gpu::error::kLostContext;
357 // static
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) {
366 if (!host)
367 return NULL;
369 if (share_context && share_context->IsCommandBufferContextLost())
370 return NULL;
372 return new WebGraphicsContext3DCommandBufferImpl(
374 active_url,
375 host,
376 attributes,
377 lose_context_when_out_of_memory,
378 limits,
379 share_context);
382 namespace {
384 WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
385 switch (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;
394 NOTREACHED();
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();
409 DCHECK(host_.get());
411 base::AutoLock lock(g_default_share_groups_lock.Get());
412 g_default_share_groups.Get().erase(host_.get());
416 } // namespace content