summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsievers <sievers@chromium.org>2015-04-27 17:45:31 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-28 00:45:49 +0000
commitfbaa5dc2fcb2e482c1daec1b9bcf1d577d53ca30 (patch)
treeb5024f14c70c616a96e9e4254315b7bce287ac27
parent8fbbc669b356f7c25b8c6417738986f5c8525d37 (diff)
downloadchromium_src-fbaa5dc2fcb2e482c1daec1b9bcf1d577d53ca30.zip
chromium_src-fbaa5dc2fcb2e482c1daec1b9bcf1d577d53ca30.tar.gz
chromium_src-fbaa5dc2fcb2e482c1daec1b9bcf1d577d53ca30.tar.bz2
gpu: Fix some context lost marking glitches+leaks and add UMA stats
* This fixes some glitches in the decoder where we were not ending up with the correct 'context lost' reason, such as calling things in the wrong order or calling glGetGraphicsResetStatus() while we might not have the correct context current. * Add context lost reasons for when the context group is forcibly lost when GL_OUT_OF_MEMORY is detected and when MakeCurrent fails. * Also communicate parse errors to the client. * Record UMA histograms for each context type (for example 'browser compositor') with the context lost reason. * Always lose (client-side) share-group if we force-lose a context so we don't leak resources. * Fix a bug where we weren't deleting GL resources if the context was lost although we might still be able to make it current. Add a bunch of tests. BUG=475676 Review URL: https://codereview.chromium.org/1095893002 Cr-Commit-Position: refs/heads/master@{#327197}
-rw-r--r--content/browser/compositor/gpu_process_transport_factory.cc4
-rw-r--r--content/browser/gpu/gpu_ipc_browsertests.cc2
-rw-r--r--content/browser/renderer_host/compositor_impl_android.cc2
-rw-r--r--content/browser/renderer_host/media/video_capture_device_client.cc2
-rw-r--r--content/common/gpu/client/command_buffer_metrics.cc153
-rw-r--r--content/common/gpu/client/command_buffer_metrics.h37
-rw-r--r--content/common/gpu/client/command_buffer_proxy_impl.cc17
-rw-r--r--content/common/gpu/client/command_buffer_proxy_impl.h7
-rw-r--r--content/common/gpu/client/context_provider_command_buffer.cc10
-rw-r--r--content/common/gpu/client/context_provider_command_buffer.h6
-rw-r--r--content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc24
-rw-r--r--content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h7
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.cc12
-rw-r--r--content/common/gpu/gpu_messages.h6
-rw-r--r--content/content_common.gypi2
-rw-r--r--content/renderer/pepper/pepper_video_encoder_host.cc2
-rw-r--r--content/renderer/pepper/ppb_graphics_3d_impl.cc2
-rw-r--r--content/renderer/render_thread_impl.cc4
-rw-r--r--content/renderer/render_widget.cc4
-rw-r--r--gpu/BUILD.gn3
-rw-r--r--gpu/command_buffer/common/constants.h15
-rw-r--r--gpu/command_buffer/service/context_group.cc4
-rw-r--r--gpu/command_buffer/service/context_group.h2
-rw-r--r--gpu/command_buffer/service/gl_context_mock.cc14
-rw-r--r--gpu/command_buffer/service/gl_context_mock.h25
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc175
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_mock.h6
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc23
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc9
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h8
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc273
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc29
-rw-r--r--gpu/command_buffer/tests/gl_manager.cc4
-rw-r--r--gpu/gpu.gyp3
-rw-r--r--tools/metrics/histograms/histograms.xml43
-rw-r--r--ui/gl/gl_context_stub_with_extensions.cc6
-rw-r--r--ui/gl/gl_context_stub_with_extensions.h1
38 files changed, 793 insertions, 159 deletions
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index f2d1b39..7d85348 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -256,7 +256,7 @@ void GpuProcessTransportFactory::EstablishedGpuChannel(
context_provider = ContextProviderCommandBuffer::Create(
GpuProcessTransportFactory::CreateContextCommon(gpu_channel_host,
data->surface_id),
- "Compositor");
+ BROWSER_COMPOSITOR_ONSCREEN_CONTEXT);
if (context_provider && !context_provider->BindToCurrentThread())
context_provider = nullptr;
}
@@ -504,7 +504,7 @@ GpuProcessTransportFactory::SharedMainThreadContextProvider() {
// context so that skia and gl_helper don't step on each other.
shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create(
GpuProcessTransportFactory::CreateOffscreenCommandBufferContext(),
- "Offscreen-MainThread");
+ BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT);
if (shared_main_thread_contexts_.get()) {
shared_main_thread_contexts_->SetLostContextCallback(
diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc
index 1eeb2ee..7154c2e 100644
--- a/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -209,7 +209,7 @@ IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
scoped_refptr<ContextProviderCommandBuffer> provider =
ContextProviderCommandBuffer::Create(CreateContext(),
- "BrowserGpuChannelHostFactoryTest");
+ OFFSCREEN_CONTEXT_FOR_TESTING);
base::RunLoop run_loop;
int counter = 0;
provider->SetLostContextCallback(
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index d1c4d01..7f9f4bf 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -604,7 +604,7 @@ void CompositorImpl::CreateOutputSurface() {
scoped_refptr<ContextProviderCommandBuffer> context_provider(
ContextProviderCommandBuffer::Create(
CreateGpuProcessViewContext(gpu_channel_host, attrs, surface_id_),
- "BrowserCompositor"));
+ BROWSER_COMPOSITOR_ONSCREEN_CONTEXT));
DCHECK(context_provider.get());
scoped_ptr<cc::OutputSurface> real_output_surface(
diff --git a/content/browser/renderer_host/media/video_capture_device_client.cc b/content/browser/renderer_host/media/video_capture_device_client.cc
index 7310963..3fdbca2 100644
--- a/content/browser/renderer_host/media/video_capture_device_client.cc
+++ b/content/browser/renderer_host/media/video_capture_device_client.cc
@@ -90,7 +90,7 @@ void CreateContextOnUIThread(ProcessContextCallback bottom_half) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if !defined(OS_ANDROID)
bottom_half.Run(ContextProviderCommandBuffer::Create(
- CreateOffscreenCommandBufferContext(), "Offscreen-CaptureThread"));
+ CreateOffscreenCommandBufferContext(), OFFSCREEN_VIDEO_CAPTURE_CONTEXT));
return;
#endif
}
diff --git a/content/common/gpu/client/command_buffer_metrics.cc b/content/common/gpu/client/command_buffer_metrics.cc
new file mode 100644
index 0000000..d16f276
--- /dev/null
+++ b/content/common/gpu/client/command_buffer_metrics.cc
@@ -0,0 +1,153 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/common/gpu/client/command_buffer_metrics.h"
+
+#include "base/metrics/histogram.h"
+
+namespace content {
+
+namespace {
+
+enum CommandBufferContextLostReason {
+ // Don't add new values here.
+ CONTEXT_INIT_FAILED,
+ CONTEXT_LOST_GPU_CHANNEL_ERROR,
+ CONTEXT_PARSE_ERROR_INVALID_SIZE,
+ CONTEXT_PARSE_ERROR_OUT_OF_BOUNDS,
+ CONTEXT_PARSE_ERROR_UNKNOWN_COMMAND,
+ CONTEXT_PARSE_ERROR_INVALID_ARGS,
+ CONTEXT_PARSE_ERROR_GENERIC_ERROR,
+ CONTEXT_LOST_GUILTY,
+ CONTEXT_LOST_INNOCENT,
+ CONTEXT_LOST_UNKNOWN,
+ CONTEXT_LOST_OUT_OF_MEMORY,
+ CONTEXT_LOST_MAKECURRENT_FAILED,
+ // Add new values here and update _MAX_ENUM.
+ CONTEXT_LOST_REASON_MAX_ENUM = CONTEXT_LOST_MAKECURRENT_FAILED
+};
+
+CommandBufferContextLostReason GetContextLostReason(
+ gpu::error::Error error,
+ gpu::error::ContextLostReason reason) {
+ if (error == gpu::error::kLostContext) {
+ switch (reason) {
+ case gpu::error::kGuilty:
+ return CONTEXT_LOST_GUILTY;
+ case gpu::error::kInnocent:
+ return CONTEXT_LOST_INNOCENT;
+ case gpu::error::kUnknown:
+ return CONTEXT_LOST_UNKNOWN;
+ case gpu::error::kOutOfMemory:
+ return CONTEXT_LOST_OUT_OF_MEMORY;
+ case gpu::error::kMakeCurrentFailed:
+ return CONTEXT_LOST_MAKECURRENT_FAILED;
+ case gpu::error::kGpuChannelLost:
+ return CONTEXT_LOST_GPU_CHANNEL_ERROR;
+ }
+ }
+ switch (error) {
+ case gpu::error::kInvalidSize:
+ return CONTEXT_PARSE_ERROR_INVALID_SIZE;
+ case gpu::error::kOutOfBounds:
+ return CONTEXT_PARSE_ERROR_OUT_OF_BOUNDS;
+ case gpu::error::kUnknownCommand:
+ return CONTEXT_PARSE_ERROR_UNKNOWN_COMMAND;
+ case gpu::error::kInvalidArguments:
+ return CONTEXT_PARSE_ERROR_INVALID_ARGS;
+ case gpu::error::kGenericError:
+ return CONTEXT_PARSE_ERROR_GENERIC_ERROR;
+ case gpu::error::kDeferCommandUntilLater:
+ case gpu::error::kNoError:
+ case gpu::error::kLostContext:
+ NOTREACHED();
+ return CONTEXT_LOST_UNKNOWN;
+ }
+ NOTREACHED();
+ return CONTEXT_LOST_UNKNOWN;
+}
+
+void RecordContextLost(CommandBufferContextType type,
+ CommandBufferContextLostReason reason) {
+ switch (type) {
+ case BROWSER_COMPOSITOR_ONSCREEN_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.BrowserCompositor", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.BrowserMainThread", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case RENDER_COMPOSITOR_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.RenderCompositor", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case RENDER_WORKER_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.RenderWorker", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case RENDERER_MAINTHREAD_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.RenderMainThread", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case GPU_VIDEO_ACCELERATOR_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.VideoAccelerator", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case OFFSCREEN_VIDEO_CAPTURE_CONTEXT:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.VideoCapture", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case OFFSCREEN_CONTEXT_FOR_WEBGL:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.WebGL", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ case CONTEXT_TYPE_UNKNOWN:
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.Unknown", reason,
+ CONTEXT_LOST_REASON_MAX_ENUM);
+ break;
+ }
+}
+
+} // anonymous namespace
+
+std::string CommandBufferContextTypeToString(CommandBufferContextType type) {
+ switch (type) {
+ case OFFSCREEN_CONTEXT_FOR_TESTING:
+ return "Context-For-Testing";
+ case BROWSER_COMPOSITOR_ONSCREEN_CONTEXT:
+ return "Compositor";
+ case BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT:
+ return "Offscreen-MainThread";
+ case RENDER_COMPOSITOR_CONTEXT:
+ return "RenderCompositor";
+ case RENDER_WORKER_CONTEXT:
+ return "RenderWorker";
+ case RENDERER_MAINTHREAD_CONTEXT:
+ return "Offscreen-MainThread";
+ case GPU_VIDEO_ACCELERATOR_CONTEXT:
+ return "GPU-VideoAccelerator-Offscreen";
+ case OFFSCREEN_VIDEO_CAPTURE_CONTEXT:
+ return "Offscreen-CaptureThread";
+ case OFFSCREEN_CONTEXT_FOR_WEBGL:
+ return "Offscreen-For-WebGL";
+ default:
+ NOTREACHED();
+ return "unknown";
+ }
+}
+
+void UmaRecordContextInitFailed(CommandBufferContextType type) {
+ RecordContextLost(type, CONTEXT_INIT_FAILED);
+}
+
+void UmaRecordContextLost(CommandBufferContextType type,
+ gpu::error::Error error,
+ gpu::error::ContextLostReason reason) {
+ CommandBufferContextLostReason converted_reason =
+ GetContextLostReason(error, reason);
+ RecordContextLost(type, converted_reason);
+}
+
+} // namespace content
diff --git a/content/common/gpu/client/command_buffer_metrics.h b/content/common/gpu/client/command_buffer_metrics.h
new file mode 100644
index 0000000..e198d85
--- /dev/null
+++ b/content/common/gpu/client/command_buffer_metrics.h
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_GPU_CLIENT_COMMAND_BUFFER_METRICS_H_
+#define CONTENT_COMMON_GPU_CLIENT_COMMAND_BUFFER_METRICS_H_
+
+#include <string>
+
+#include "gpu/command_buffer/common/constants.h"
+
+namespace content {
+
+enum CommandBufferContextType {
+ BROWSER_COMPOSITOR_ONSCREEN_CONTEXT,
+ BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT,
+ RENDER_COMPOSITOR_CONTEXT,
+ RENDER_WORKER_CONTEXT,
+ RENDERER_MAINTHREAD_CONTEXT,
+ GPU_VIDEO_ACCELERATOR_CONTEXT,
+ OFFSCREEN_VIDEO_CAPTURE_CONTEXT,
+ OFFSCREEN_CONTEXT_FOR_WEBGL,
+ CONTEXT_TYPE_UNKNOWN,
+ OFFSCREEN_CONTEXT_FOR_TESTING = CONTEXT_TYPE_UNKNOWN,
+};
+
+std::string CommandBufferContextTypeToString(CommandBufferContextType type);
+
+void UmaRecordContextInitFailed(CommandBufferContextType type);
+
+void UmaRecordContextLost(CommandBufferContextType type,
+ gpu::error::Error error,
+ gpu::error::ContextLostReason reason);
+
+} // namespace content
+
+#endif // CONTENT_COMMON_GPU_CLIENT_COMMAND_BUFFER_METRICS_H_
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.cc b/content/common/gpu/client/command_buffer_proxy_impl.cc
index 257e084..8ca94b3 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.cc
+++ b/content/common/gpu/client/command_buffer_proxy_impl.cc
@@ -71,23 +71,24 @@ void CommandBufferProxyImpl::OnChannelError() {
scoped_ptr<base::AutoLock> lock;
if (lock_)
lock.reset(new base::AutoLock(*lock_));
- OnDestroyed(gpu::error::kUnknown);
+ OnDestroyed(gpu::error::kGpuChannelLost, gpu::error::kLostContext);
}
-void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason) {
+void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason,
+ gpu::error::Error error) {
CheckLock();
// Prevent any further messages from being sent.
channel_ = NULL;
// When the client sees that the context is lost, they should delete this
// CommandBufferProxyImpl and create a new one.
- last_state_.error = gpu::error::kLostContext;
+ last_state_.error = error;
last_state_.context_lost_reason = reason;
- if (!channel_error_callback_.is_null()) {
- channel_error_callback_.Run();
+ if (!context_lost_callback_.is_null()) {
+ context_lost_callback_.Run();
// Avoid calling the error callback more than once.
- channel_error_callback_.Reset();
+ context_lost_callback_.Reset();
}
}
@@ -134,10 +135,10 @@ void CommandBufferProxyImpl::OnSignalSyncPointAck(uint32 id) {
callback.Run();
}
-void CommandBufferProxyImpl::SetChannelErrorCallback(
+void CommandBufferProxyImpl::SetContextLostCallback(
const base::Closure& callback) {
CheckLock();
- channel_error_callback_ = callback;
+ context_lost_callback_ = callback;
}
bool CommandBufferProxyImpl::Initialize() {
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.h b/content/common/gpu/client/command_buffer_proxy_impl.h
index 2da5bbc..6e73c82 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.h
+++ b/content/common/gpu/client/command_buffer_proxy_impl.h
@@ -120,7 +120,7 @@ class CommandBufferProxyImpl
int GetRouteID() const;
bool ProduceFrontBuffer(const gpu::Mailbox& mailbox);
- void SetChannelErrorCallback(const base::Closure& callback);
+ void SetContextLostCallback(const base::Closure& callback);
typedef base::Callback<void(const gpu::MemoryAllocation&)>
MemoryAllocationChangedCallback;
@@ -175,7 +175,8 @@ class CommandBufferProxyImpl
// Message handlers:
void OnUpdateState(const gpu::CommandBuffer::State& state);
- void OnDestroyed(gpu::error::ContextLostReason reason);
+ void OnDestroyed(gpu::error::ContextLostReason reason,
+ gpu::error::Error error);
void OnConsoleMessage(const GPUCommandBufferConsoleMessage& message);
void OnSetMemoryAllocation(const gpu::MemoryAllocation& allocation);
void OnSignalSyncPointAck(uint32 id);
@@ -208,7 +209,7 @@ class CommandBufferProxyImpl
int32 last_put_offset_;
int32 last_barrier_put_offset_;
- base::Closure channel_error_callback_;
+ base::Closure context_lost_callback_;
MemoryAllocationChangedCallback memory_allocation_changed_callback_;
diff --git a/content/common/gpu/client/context_provider_command_buffer.cc b/content/common/gpu/client/context_provider_command_buffer.cc
index 439e14f..8bfdc09 100644
--- a/content/common/gpu/client/context_provider_command_buffer.cc
+++ b/content/common/gpu/client/context_provider_command_buffer.cc
@@ -39,18 +39,19 @@ class ContextProviderCommandBuffer::LostContextCallbackProxy
scoped_refptr<ContextProviderCommandBuffer>
ContextProviderCommandBuffer::Create(
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
- const std::string& debug_name) {
+ CommandBufferContextType type) {
if (!context3d)
return NULL;
- return new ContextProviderCommandBuffer(context3d.Pass(), debug_name);
+ return new ContextProviderCommandBuffer(context3d.Pass(), type);
}
ContextProviderCommandBuffer::ContextProviderCommandBuffer(
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
- const std::string& debug_name)
+ CommandBufferContextType type)
: context3d_(context3d.Pass()),
- debug_name_(debug_name),
+ context_type_(type),
+ debug_name_(CommandBufferContextTypeToString(type)),
destroyed_(false) {
DCHECK(main_thread_checker_.CalledOnValidThread());
DCHECK(context3d_);
@@ -93,6 +94,7 @@ bool ContextProviderCommandBuffer::BindToCurrentThread() {
if (lost_context_callback_proxy_)
return true;
+ context3d_->SetContextType(context_type_);
if (!context3d_->InitializeOnCurrentThread())
return false;
diff --git a/content/common/gpu/client/context_provider_command_buffer.h b/content/common/gpu/client/context_provider_command_buffer.h
index 101c0a0..ee10bf1 100644
--- a/content/common/gpu/client/context_provider_command_buffer.h
+++ b/content/common/gpu/client/context_provider_command_buffer.h
@@ -12,6 +12,7 @@
#include "cc/blink/context_provider_web_context.h"
#include "cc/output/context_provider.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/client/command_buffer_metrics.h"
#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
namespace webkit {
@@ -29,7 +30,7 @@ class CONTENT_EXPORT ContextProviderCommandBuffer
public:
static scoped_refptr<ContextProviderCommandBuffer> Create(
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
- const std::string& debug_name);
+ CommandBufferContextType type);
CommandBufferProxyImpl* GetCommandBufferProxy();
@@ -58,7 +59,7 @@ class CONTENT_EXPORT ContextProviderCommandBuffer
protected:
ContextProviderCommandBuffer(
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d,
- const std::string& debug_name);
+ CommandBufferContextType type);
~ContextProviderCommandBuffer() override;
void OnLostContext();
@@ -74,6 +75,7 @@ class CONTENT_EXPORT ContextProviderCommandBuffer
scoped_ptr<webkit::gpu::GrContextForWebGraphicsContext3D> gr_context_;
cc::ContextProvider::Capabilities capabilities_;
+ CommandBufferContextType context_type_;
std::string debug_name_;
LostContextCallback lost_context_callback_;
diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
index 0cf028f..b06b06e 100644
--- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
+++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc
@@ -97,10 +97,13 @@ WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
host_(host),
surface_id_(surface_id),
active_url_(active_url),
+ context_type_(CONTEXT_TYPE_UNKNOWN),
gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
: gfx::PreferIntegratedGpu),
mem_limits_(limits),
weak_ptr_factory_(this) {
+ if (attributes_.webGL)
+ context_type_ = OFFSCREEN_CONTEXT_FOR_WEBGL;
if (share_context) {
DCHECK(!attributes_.shareResources);
share_group_ = share_context->share_group_;
@@ -145,8 +148,8 @@ bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
if (gl_ && attributes_.webGL)
gl_->EnableFeatureCHROMIUM("webgl_enable_glsl_webgl_validation");
- command_buffer_->SetChannelErrorCallback(
- base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost,
+ command_buffer_->SetContextLostCallback(
+ base::Bind(&WebGraphicsContext3DCommandBufferImpl::OnContextLost,
weak_ptr_factory_.GetWeakPtr()));
command_buffer_->SetOnConsoleMessageCallback(
@@ -198,6 +201,7 @@ bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
if (!command_buffer_) {
DLOG(ERROR) << "GpuChannelHost failed to create command buffer.";
+ UmaRecordContextInitFailed(context_type_);
return false;
}
@@ -207,6 +211,8 @@ bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
// Initialize the command buffer.
bool result = command_buffer_->Initialize();
LOG_IF(ERROR, !result) << "CommandBufferProxy::Initialize failed.";
+ if (!result)
+ UmaRecordContextInitFailed(context_type_);
return result;
}
@@ -351,7 +357,7 @@ bool WebGraphicsContext3DCommandBufferImpl::IsCommandBufferContextLost() {
if (host_.get() && host_->IsLost())
return true;
gpu::CommandBuffer::State state = command_buffer_->GetLastState();
- return state.error == gpu::error::kLostContext;
+ return gpu::error::IsError(state.error);
}
// static
@@ -387,7 +393,10 @@ WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
return GL_GUILTY_CONTEXT_RESET_ARB;
case gpu::error::kInnocent:
return GL_INNOCENT_CONTEXT_RESET_ARB;
+ case gpu::error::kOutOfMemory:
+ case gpu::error::kMakeCurrentFailed:
case gpu::error::kUnknown:
+ case gpu::error::kGpuChannelLost:
return GL_UNKNOWN_CONTEXT_RESET_ARB;
}
@@ -397,9 +406,9 @@ WGC3Denum convertReason(gpu::error::ContextLostReason reason) {
} // anonymous namespace
-void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
- context_lost_reason_ = convertReason(
- command_buffer_->GetLastState().context_lost_reason);
+void WebGraphicsContext3DCommandBufferImpl::OnContextLost() {
+ context_lost_reason_ =
+ convertReason(command_buffer_->GetLastState().context_lost_reason);
if (context_lost_callback_) {
context_lost_callback_->onContextLost();
}
@@ -411,6 +420,9 @@ void WebGraphicsContext3DCommandBufferImpl::OnGpuChannelLost() {
base::AutoLock lock(g_default_share_groups_lock.Get());
g_default_share_groups.Get().erase(host_.get());
}
+
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState();
+ UmaRecordContextLost(context_type_, state.error, state.context_lost_reason);
}
} // namespace content
diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
index ea14d7f..115e167 100644
--- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
+++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h
@@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/client/command_buffer_metrics.h"
#include "content/common/gpu/client/command_buffer_proxy_impl.h"
#include "gpu/blink/webgraphicscontext3d_impl.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
@@ -150,6 +151,9 @@ class WebGraphicsContext3DCommandBufferImpl
virtual blink::WGC3Denum getGraphicsResetStatusARB();
+ void SetContextType(CommandBufferContextType type) {
+ context_type_ = type;
+ }
private:
// These are the same error codes as used by EGL.
enum Error {
@@ -184,7 +188,7 @@ class WebGraphicsContext3DCommandBufferImpl
// unnecessary complexity at the moment.
bool CreateContext(bool onscreen);
- virtual void OnGpuChannelLost();
+ virtual void OnContextLost();
bool lose_context_when_out_of_memory_;
blink::WebGraphicsContext3D::Attributes attributes_;
@@ -195,6 +199,7 @@ class WebGraphicsContext3DCommandBufferImpl
scoped_refptr<GpuChannelHost> host_;
int32 surface_id_;
GURL active_url_;
+ CommandBufferContextType context_type_;
gfx::GpuPreference gpu_preference_;
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 29ad1da..fd344da 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -429,9 +429,11 @@ void GpuCommandBufferStub::Destroy() {
scheduler_.reset();
bool have_context = false;
- if (decoder_ && command_buffer_ &&
- command_buffer_->GetLastState().error != gpu::error::kLostContext)
- have_context = decoder_->MakeCurrent();
+ if (decoder_) {
+ // Try to make the context current regardless of whether it was lost, so we
+ // don't leak resources.
+ have_context = decoder_->GetGLContext()->MakeCurrent(surface_.get());
+ }
FOR_EACH_OBSERVER(DestructionObserver,
destruction_observers_,
OnWillDestroyStub());
@@ -679,7 +681,7 @@ void GpuCommandBufferStub::OnParseError() {
DCHECK(command_buffer_.get());
gpu::CommandBuffer::State state = command_buffer_->GetLastState();
IPC::Message* msg = new GpuCommandBufferMsg_Destroyed(
- route_id_, state.context_lost_reason);
+ route_id_, state.context_lost_reason, state.error);
msg->set_unblock(true);
Send(msg);
@@ -1097,7 +1099,7 @@ void GpuCommandBufferStub::MarkContextLost() {
command_buffer_->SetContextLostReason(gpu::error::kUnknown);
if (decoder_)
- decoder_->LoseContext(GL_UNKNOWN_CONTEXT_RESET_ARB);
+ decoder_->MarkContextLost(gpu::error::kUnknown);
command_buffer_->SetParseError(gpu::error::kLostContext);
}
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index d984ac3..6bdec50 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -54,6 +54,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(gfx::SurfaceType,
gfx::SURFACE_TYPE_LAST)
IPC_ENUM_TRAITS_MAX_VALUE(gpu::MemoryAllocation::PriorityCutoff,
gpu::MemoryAllocation::CUTOFF_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(gpu::error::Error, gpu::error::kErrorLast)
IPC_ENUM_TRAITS_MAX_VALUE(gpu::error::ContextLostReason,
gpu::error::kContextLostReasonLast)
IPC_ENUM_TRAITS_MAX_VALUE(media::VideoEncodeAccelerator::Error,
@@ -552,8 +553,9 @@ IPC_SYNC_MESSAGE_ROUTED5_1(GpuCommandBufferMsg_CreateVideoEncoder,
// Tells the proxy that there was an error and the command buffer had to be
// destroyed for some reason.
-IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_Destroyed,
- gpu::error::ContextLostReason /* reason */)
+IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_Destroyed,
+ gpu::error::ContextLostReason, /* reason */
+ gpu::error::Error /* error */)
// Tells the browser that SwapBuffers returned and passes latency info
IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_SwapBuffersCompleted,
diff --git a/content/content_common.gypi b/content/content_common.gypi
index b481104..d4ef54f 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -267,6 +267,8 @@
'common/gin_java_bridge_messages.h',
'common/gpu/client/command_buffer_proxy_impl.cc',
'common/gpu/client/command_buffer_proxy_impl.h',
+ 'common/gpu/client/command_buffer_metrics.cc',
+ 'common/gpu/client/command_buffer_metrics.h',
'common/gpu/client/context_provider_command_buffer.cc',
'common/gpu/client/context_provider_command_buffer.h',
'common/gpu/client/gl_helper.cc',
diff --git a/content/renderer/pepper/pepper_video_encoder_host.cc b/content/renderer/pepper/pepper_video_encoder_host.cc
index 39292bb..7c0b9fd 100644
--- a/content/renderer/pepper/pepper_video_encoder_host.cc
+++ b/content/renderer/pepper/pepper_video_encoder_host.cc
@@ -513,7 +513,7 @@ bool PepperVideoEncoderHost::EnsureGpuChannel() {
return false;
}
- command_buffer_->SetChannelErrorCallback(media::BindToCurrentLoop(
+ command_buffer_->SetContextLostCallback(media::BindToCurrentLoop(
base::Bind(&PepperVideoEncoderHost::NotifyPepperError,
weak_ptr_factory_.GetWeakPtr(), PP_ERROR_RESOURCE_FAILED)));
if (!command_buffer_->Initialize()) {
diff --git a/content/renderer/pepper/ppb_graphics_3d_impl.cc b/content/renderer/pepper/ppb_graphics_3d_impl.cc
index b703cef..2de5e10 100644
--- a/content/renderer/pepper/ppb_graphics_3d_impl.cc
+++ b/content/renderer/pepper/ppb_graphics_3d_impl.cc
@@ -312,7 +312,7 @@ bool PPB_Graphics3D_Impl::InitRaw(
return false;
sync_point_ = command_buffer_->InsertSyncPoint();
- command_buffer_->SetChannelErrorCallback(base::Bind(
+ command_buffer_->SetContextLostCallback(base::Bind(
&PPB_Graphics3D_Impl::OnContextLost, weak_ptr_factory_.GetWeakPtr()));
command_buffer_->SetOnConsoleMessageCallback(base::Bind(
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 61fd840..acda8a7 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1294,7 +1294,7 @@ RenderThreadImpl::GetGpuFactories() {
GURL("chrome://gpu/RenderThreadImpl::GetGpuVDAContext3D"),
WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits(),
NULL)),
- "GPU-VideoAccelerator-Offscreen");
+ GPU_VIDEO_ACCELERATOR_CONTEXT);
}
}
if (gpu_va_context_provider_.get()) {
@@ -1336,7 +1336,7 @@ RenderThreadImpl::SharedMainThreadContextProvider() {
#endif
if (!shared_main_thread_contexts_.get()) {
shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create(
- CreateOffscreenContext3d(), "Offscreen-MainThread");
+ CreateOffscreenContext3d(), RENDERER_MAINTHREAD_CONTEXT);
}
if (shared_main_thread_contexts_.get() &&
!shared_main_thread_contexts_->BindToCurrentThread())
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 14fccc2..94d484f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1013,14 +1013,14 @@ scoped_ptr<cc::OutputSurface> RenderWidget::CreateOutputSurface(bool fallback) {
scoped_refptr<ContextProviderCommandBuffer> worker_context_provider;
if (!use_software) {
context_provider = ContextProviderCommandBuffer::Create(
- CreateGraphicsContext3D(), "RenderCompositor");
+ CreateGraphicsContext3D(), RENDER_COMPOSITOR_CONTEXT);
if (!context_provider.get()) {
// Cause the compositor to wait and try again.
return scoped_ptr<cc::OutputSurface>();
}
worker_context_provider = ContextProviderCommandBuffer::Create(
- CreateGraphicsContext3D(), "RenderWorker");
+ CreateGraphicsContext3D(), RENDER_WORKER_CONTEXT);
if (!worker_context_provider.get()) {
// Cause the compositor to wait and try again.
return scoped_ptr<cc::OutputSurface>();
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index f0be2e5..8cb4ec4 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -165,6 +165,8 @@ test("gpu_unittests") {
"command_buffer/service/context_group_unittest.cc",
"command_buffer/service/feature_info_unittest.cc",
"command_buffer/service/framebuffer_manager_unittest.cc",
+ "command_buffer/service/gl_context_mock.cc",
+ "command_buffer/service/gl_context_mock.h",
"command_buffer/service/gl_surface_mock.cc",
"command_buffer/service/gl_surface_mock.h",
"command_buffer/service/gles2_cmd_decoder_unittest.cc",
@@ -181,6 +183,7 @@ test("gpu_unittests") {
"command_buffer/service/gles2_cmd_decoder_unittest_base.cc",
"command_buffer/service/gles2_cmd_decoder_unittest_base.h",
"command_buffer/service/gles2_cmd_decoder_unittest_buffers.cc",
+ "command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc",
"command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc",
"command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc",
"command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc",
diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h
index 054708f..d06836c 100644
--- a/gpu/command_buffer/common/constants.h
+++ b/gpu/command_buffer/common/constants.h
@@ -23,7 +23,8 @@ namespace error {
kInvalidArguments,
kLostContext,
kGenericError,
- kDeferCommandUntilLater
+ kDeferCommandUntilLater,
+ kErrorLast = kDeferCommandUntilLater,
};
// Return true if the given error code is an actual error.
@@ -41,7 +42,17 @@ namespace error {
// It is unknown whether this context provoked the loss of context.
kUnknown,
- kContextLostReasonLast = kUnknown
+
+ // GL_OUT_OF_MEMORY caused this context to be lost.
+ kOutOfMemory,
+
+ // A failure to make the context current caused it to be lost.
+ kMakeCurrentFailed,
+
+ // The GPU channel was lost. This error is set client-side.
+ kGpuChannelLost,
+
+ kContextLostReasonLast = kGpuChannelLost
};
}
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index 17871b9..d2667f0 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -360,10 +360,10 @@ uint32 ContextGroup::GetMemRepresented() const {
return total;
}
-void ContextGroup::LoseContexts(GLenum reset_status) {
+void ContextGroup::LoseContexts(error::ContextLostReason reason) {
for (size_t ii = 0; ii < decoders_.size(); ++ii) {
if (decoders_[ii].get()) {
- decoders_[ii]->LoseContext(reset_status);
+ decoders_[ii]->MarkContextLost(reason);
}
}
}
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 248fa19..f2e832f 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -166,7 +166,7 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
uint32 GetMemRepresented() const;
// Loses all the context associated with this group.
- void LoseContexts(GLenum reset_status);
+ void LoseContexts(error::ContextLostReason reason);
// EXT_draw_buffer related states for backbuffer.
GLenum draw_buffer() const {
diff --git a/gpu/command_buffer/service/gl_context_mock.cc b/gpu/command_buffer/service/gl_context_mock.cc
new file mode 100644
index 0000000..ce5d10f
--- /dev/null
+++ b/gpu/command_buffer/service/gl_context_mock.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/gl_context_mock.h"
+
+namespace gpu {
+
+GLContextMock::GLContextMock() {
+}
+GLContextMock::~GLContextMock() {
+}
+
+} // namespace gpu
diff --git a/gpu/command_buffer/service/gl_context_mock.h b/gpu/command_buffer/service/gl_context_mock.h
new file mode 100644
index 0000000..77d669c
--- /dev/null
+++ b/gpu/command_buffer/service/gl_context_mock.h
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_GL_CONTEXT_MOCK_H_
+#define GPU_COMMAND_BUFFER_SERVICE_GL_CONTEXT_MOCK_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/gl/gl_context_stub_with_extensions.h"
+
+namespace gpu {
+
+class GLContextMock : public gfx::GLContextStubWithExtensions {
+ public:
+ GLContextMock();
+
+ MOCK_METHOD1(MakeCurrent, bool(gfx::GLSurface* surface));
+
+ protected:
+ virtual ~GLContextMock();
+};
+
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_GL_CONTEXT_MOCK_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 983fb77..d119e7f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -760,6 +760,9 @@ class GLES2DecoderImpl : public GLES2Decoder,
void OnFboChanged() const;
void OnUseFramebuffer() const;
+ error::ContextLostReason GetContextLostReasonFromResetStatus(
+ GLenum reset_status) const;
+
// TODO(gman): Cache these pointers?
BufferManager* buffer_manager() {
return group_->buffer_manager();
@@ -1657,10 +1660,10 @@ class GLES2DecoderImpl : public GLES2Decoder,
GLenum* result_type,
GLsizei* result_size);
- void MaybeExitOnContextLost();
- bool WasContextLost() override;
- bool WasContextLostByRobustnessExtension() override;
- void LoseContext(uint32 reset_status) override;
+ bool WasContextLost() const override;
+ bool WasContextLostByRobustnessExtension() const override;
+ void MarkContextLost(error::ContextLostReason reason) override;
+ bool CheckResetStatus();
#if defined(OS_MACOSX)
void ReleaseIOSurfaceForTexture(GLuint texture_id);
@@ -1863,7 +1866,8 @@ class GLES2DecoderImpl : public GLES2Decoder,
int commands_to_process_;
bool has_robustness_extension_;
- GLenum reset_status_;
+ error::ContextLostReason context_lost_reason_;
+ bool context_was_lost_;
bool reset_by_robustness_extension_;
bool supports_post_sub_buffer_;
@@ -2405,7 +2409,8 @@ GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group)
feature_info_(group_->feature_info()),
frame_number_(0),
has_robustness_extension_(false),
- reset_status_(GL_NO_ERROR),
+ context_lost_reason_(error::kUnknown),
+ context_was_lost_(false),
reset_by_robustness_extension_(false),
supports_post_sub_buffer_(false),
force_webgl_glsl_validation_(false),
@@ -3328,11 +3333,22 @@ bool GLES2DecoderImpl::MakeCurrent() {
if (!context_.get())
return false;
- if (!context_->MakeCurrent(surface_.get()) || WasContextLost()) {
- LOG(ERROR) << " GLES2DecoderImpl: Context lost during MakeCurrent.";
+ if (WasContextLost()) {
+ LOG(ERROR) << " GLES2DecoderImpl: Trying to make lost context current.";
+ return false;
+ }
- MaybeExitOnContextLost();
+ if (!context_->MakeCurrent(surface_.get())) {
+ LOG(ERROR) << " GLES2DecoderImpl: Context lost during MakeCurrent.";
+ MarkContextLost(error::kMakeCurrentFailed);
+ group_->LoseContexts(error::kUnknown);
+ return false;
+ }
+ if (CheckResetStatus()) {
+ LOG(ERROR)
+ << " GLES2DecoderImpl: Context reset detected after MakeCurrent.";
+ group_->LoseContexts(error::kUnknown);
return false;
}
@@ -10306,7 +10322,8 @@ void GLES2DecoderImpl::DoSwapBuffers() {
GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR) << "GLES2DecoderImpl::ResizeOffscreenFrameBuffer failed "
<< "because offscreen saved FBO was incomplete.";
- LoseContext(GL_UNKNOWN_CONTEXT_RESET_ARB);
+ MarkContextLost(error::kUnknown);
+ group_->LoseContexts(error::kUnknown);
return;
}
@@ -10363,7 +10380,10 @@ void GLES2DecoderImpl::DoSwapBuffers() {
} else {
if (!surface_->SwapBuffers()) {
LOG(ERROR) << "Context lost because SwapBuffers failed.";
- LoseContext(GL_UNKNOWN_CONTEXT_RESET_ARB);
+ if (!CheckResetStatus()) {
+ MarkContextLost(error::kUnknown);
+ group_->LoseContexts(error::kUnknown);
+ }
}
}
@@ -10623,7 +10643,12 @@ error::Error GLES2DecoderImpl::HandleGetTransformFeedbackVaryingsCHROMIUM(
}
error::ContextLostReason GLES2DecoderImpl::GetContextLostReason() {
- switch (reset_status_) {
+ return context_lost_reason_;
+}
+
+error::ContextLostReason GLES2DecoderImpl::GetContextLostReasonFromResetStatus(
+ GLenum reset_status) const {
+ switch (reset_status) {
case GL_NO_ERROR:
// TODO(kbr): improve the precision of the error code in this case.
// Consider delegating to context for error code if MakeCurrent fails.
@@ -10640,7 +10665,24 @@ error::ContextLostReason GLES2DecoderImpl::GetContextLostReason() {
return error::kUnknown;
}
-void GLES2DecoderImpl::MaybeExitOnContextLost() {
+bool GLES2DecoderImpl::WasContextLost() const {
+ return context_was_lost_;
+}
+
+bool GLES2DecoderImpl::WasContextLostByRobustnessExtension() const {
+ return WasContextLost() && reset_by_robustness_extension_;
+}
+
+void GLES2DecoderImpl::MarkContextLost(error::ContextLostReason reason) {
+ // Only lose the context once.
+ if (WasContextLost())
+ return;
+
+ // Don't make GL calls in here, the context might not be current.
+ context_lost_reason_ = reason;
+ current_decoder_error_ = error::kLostContext;
+ context_was_lost_ = true;
+
// Some D3D drivers cannot recover from device lost in the GPU process
// sandbox. Allow a new GPU process to launch.
if (workarounds().exit_on_context_lost) {
@@ -10653,57 +10695,43 @@ void GLES2DecoderImpl::MaybeExitOnContextLost() {
}
}
-bool GLES2DecoderImpl::WasContextLost() {
- if (reset_status_ != GL_NO_ERROR) {
- MaybeExitOnContextLost();
- return true;
- }
- if (IsRobustnessSupported()) {
- GLenum status = glGetGraphicsResetStatusARB();
- if (status != GL_NO_ERROR) {
- // The graphics card was reset. Signal a lost context to the application.
- reset_status_ = status;
- reset_by_robustness_extension_ = true;
- LOG(ERROR) << (surface_->IsOffscreen() ? "Offscreen" : "Onscreen")
- << " context lost via ARB/EXT_robustness. Reset status = "
- << GLES2Util::GetStringEnum(status);
- MaybeExitOnContextLost();
- return true;
- }
- }
- return false;
-}
-
-bool GLES2DecoderImpl::WasContextLostByRobustnessExtension() {
- return WasContextLost() && reset_by_robustness_extension_;
-}
-
-void GLES2DecoderImpl::LoseContext(uint32 reset_status) {
- // Only loses the context once.
- if (reset_status_ != GL_NO_ERROR) {
- return;
- }
+bool GLES2DecoderImpl::CheckResetStatus() {
+ DCHECK(!WasContextLost());
+ DCHECK(context_->IsCurrent(NULL));
- if (workarounds().use_virtualized_gl_contexts) {
- // If the context is virtual, the real context being guilty does not ensure
- // that the virtual context is guilty.
- if (reset_status == GL_GUILTY_CONTEXT_RESET_ARB) {
- reset_status = GL_UNKNOWN_CONTEXT_RESET_ARB;
- }
- } else if (reset_status == GL_UNKNOWN_CONTEXT_RESET_ARB &&
- IsRobustnessSupported()) {
+ if (IsRobustnessSupported()) {
// If the reason for the call was a GL error, we can try to determine the
// reset status more accurately.
GLenum driver_status = glGetGraphicsResetStatusARB();
- if (driver_status == GL_GUILTY_CONTEXT_RESET_ARB ||
- driver_status == GL_INNOCENT_CONTEXT_RESET_ARB) {
- reset_status = driver_status;
+ if (driver_status == GL_NO_ERROR)
+ return false;
+
+ LOG(ERROR) << (surface_->IsOffscreen() ? "Offscreen" : "Onscreen")
+ << " context lost via ARB/EXT_robustness. Reset status = "
+ << GLES2Util::GetStringEnum(driver_status);
+
+ // Don't pretend we know which client was responsible.
+ if (workarounds().use_virtualized_gl_contexts)
+ driver_status = GL_UNKNOWN_CONTEXT_RESET_ARB;
+
+ switch (driver_status) {
+ case GL_GUILTY_CONTEXT_RESET_ARB:
+ MarkContextLost(error::kGuilty);
+ break;
+ case GL_INNOCENT_CONTEXT_RESET_ARB:
+ MarkContextLost(error::kInnocent);
+ break;
+ case GL_UNKNOWN_CONTEXT_RESET_ARB:
+ MarkContextLost(error::kUnknown);
+ break;
+ default:
+ NOTREACHED();
+ return false;
}
+ reset_by_robustness_extension_ = true;
+ return true;
}
-
- // Marks this context as lost.
- reset_status_ = reset_status;
- current_decoder_error_ = error::kLostContext;
+ return false;
}
error::Error GLES2DecoderImpl::HandleInsertSyncPointCHROMIUM(
@@ -12079,9 +12107,9 @@ void GLES2DecoderImpl::DoDrawBuffersEXT(
}
void GLES2DecoderImpl::DoLoseContextCHROMIUM(GLenum current, GLenum other) {
- group_->LoseContexts(other);
- reset_status_ = current;
- current_decoder_error_ = error::kLostContext;
+ MarkContextLost(GetContextLostReasonFromResetStatus(current));
+ group_->LoseContexts(GetContextLostReasonFromResetStatus(other));
+ reset_by_robustness_extension_ = true;
}
void GLES2DecoderImpl::DoMatrixLoadfCHROMIUM(GLenum matrix_mode,
@@ -12599,8 +12627,9 @@ error::Error GLES2DecoderImpl::HandleUnmapBuffer(
// the second unmap could still return GL_FALSE. For now, we simply lose
// the contexts in the share group.
LOG(ERROR) << "glUnmapBuffer unexpectedly returned GL_FALSE";
- group_->LoseContexts(GL_INNOCENT_CONTEXT_RESET_ARB);
- reset_status_ = GL_GUILTY_CONTEXT_RESET_ARB;
+ // Need to lose current context before broadcasting!
+ MarkContextLost(error::kGuilty);
+ group_->LoseContexts(error::kInnocent);
return error::kLostContext;
}
return error::kNoError;
@@ -12612,13 +12641,27 @@ void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
DoDidUseTexImageIfNeeded(texture, texture->target());
}
+// Note that GL_LOST_CONTEXT is specific to GLES.
+// For desktop GL we have to query the reset status proactively.
void GLES2DecoderImpl::OnContextLostError() {
- group_->LoseContexts(GL_UNKNOWN_CONTEXT_RESET_ARB);
+ if (!WasContextLost()) {
+ // Need to lose current context before broadcasting!
+ CheckResetStatus();
+ group_->LoseContexts(error::kUnknown);
+ reset_by_robustness_extension_ = true;
+ }
}
void GLES2DecoderImpl::OnOutOfMemoryError() {
- if (lose_context_when_out_of_memory_) {
- group_->LoseContexts(GL_UNKNOWN_CONTEXT_RESET_ARB);
+ if (lose_context_when_out_of_memory_ && !WasContextLost()) {
+ error::ContextLostReason other = error::kOutOfMemory;
+ if (CheckResetStatus()) {
+ other = error::kUnknown;
+ } else {
+ // Need to lose current context before broadcasting!
+ MarkContextLost(error::kOutOfMemory);
+ }
+ group_->LoseContexts(other);
}
}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 0cbe0fe..ac01c95 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -241,13 +241,13 @@ class GPU_EXPORT GLES2Decoder : public base::SupportsWeakPtr<GLES2Decoder>,
// Returns true if the context was lost either by GL_ARB_robustness, forced
// context loss or command buffer parse error.
- virtual bool WasContextLost() = 0;
+ virtual bool WasContextLost() const = 0;
// Returns true if the context was lost specifically by GL_ARB_robustness.
- virtual bool WasContextLostByRobustnessExtension() = 0;
+ virtual bool WasContextLostByRobustnessExtension() const = 0;
// Lose this context.
- virtual void LoseContext(uint32 reset_status) = 0;
+ virtual void MarkContextLost(error::ContextLostReason reason) = 0;
virtual Logger* GetLogger() = 0;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index c58405b..65df53c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -124,9 +124,9 @@ class MockGLES2Decoder : public GLES2Decoder {
MOCK_METHOD0(GetTotalTextureUploadTime, base::TimeDelta());
MOCK_METHOD0(GetTotalProcessingCommandsTime, base::TimeDelta());
MOCK_METHOD1(AddProcessingCommandsTime, void(base::TimeDelta));
- MOCK_METHOD0(WasContextLost, bool());
- MOCK_METHOD0(WasContextLostByRobustnessExtension, bool());
- MOCK_METHOD1(LoseContext, void(uint32 reset_status));
+ MOCK_CONST_METHOD0(WasContextLost, bool());
+ MOCK_CONST_METHOD0(WasContextLostByRobustnessExtension, bool());
+ MOCK_METHOD1(MarkContextLost, void(gpu::error::ContextLostReason reason));
DISALLOW_COPY_AND_ASSIGN(MockGLES2Decoder);
};
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index cae6c00..d3a20b3 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1125,17 +1125,30 @@ TEST_P(GLES2DecoderManualInitTest, ImmutableCopyTexImage2D) {
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
-TEST_P(GLES2DecoderTest, LoseContextCHROMIUMValidArgs) {
- EXPECT_CALL(*mock_decoder_, LoseContext(GL_GUILTY_CONTEXT_RESET_ARB))
+TEST_P(GLES2DecoderTest, LoseContextCHROMIUMGuilty) {
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kInnocent))
.Times(1);
cmds::LoseContextCHROMIUM cmd;
- cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_GUILTY_CONTEXT_RESET_ARB);
+ cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
+}
+
+TEST_P(GLES2DecoderTest, LoseContextCHROMIUMUnkown) {
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
+ .Times(1);
+ cmds::LoseContextCHROMIUM cmd;
+ cmd.Init(GL_UNKNOWN_CONTEXT_RESET_ARB, GL_UNKNOWN_CONTEXT_RESET_ARB);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs0_0) {
- EXPECT_CALL(*mock_decoder_, LoseContext(_))
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(_))
.Times(0);
cmds::LoseContextCHROMIUM cmd;
cmd.Init(GL_NONE, GL_GUILTY_CONTEXT_RESET_ARB);
@@ -1144,7 +1157,7 @@ TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs0_0) {
}
TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs1_0) {
- EXPECT_CALL(*mock_decoder_, LoseContext(_))
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(_))
.Times(0);
cmds::LoseContextCHROMIUM cmd;
cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_NONE);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 8648134..d757926 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -200,11 +200,11 @@ void GLES2DecoderTestBase::InitDecoderWithCommandLine(
// Context needs to be created before initializing ContextGroup, which will
// in turn initialize FeatureInfo, which needs a context to determine
// extension support.
- context_ = new gfx::GLContextStubWithExtensions;
+ context_ = new StrictMock<GLContextMock>();
context_->AddExtensionsString(normalized_init.extensions.c_str());
context_->SetGLVersionString(normalized_init.gl_version.c_str());
- context_->MakeCurrent(surface_.get());
+ context_->GLContextStubWithExtensions::MakeCurrent(surface_.get());
gfx::GLSurface::InitializeDynamicMockBindingsForTests(context_.get());
TestHelper::SetupContextGroupInitExpectations(
@@ -401,6 +401,11 @@ void GLES2DecoderTestBase::InitDecoderWithCommandLine(
surface_->GetSize(),
DisallowedFeatures(),
attribs);
+ EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
+ if (context_->WasAllocatedUsingRobustnessExtension()) {
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
+ .WillOnce(Return(GL_NO_ERROR));
+ }
decoder_->MakeCurrent();
decoder_->set_engine(engine_.get());
decoder_->BeginDecoding();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 0ca537a..7719cdf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -11,6 +11,7 @@
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/framebuffer_manager.h"
+#include "gpu/command_buffer/service/gl_context_mock.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
#include "gpu/command_buffer/service/program_manager.h"
@@ -22,7 +23,6 @@
#include "gpu/command_buffer/service/valuebuffer_manager.h"
#include "gpu/command_buffer/service/vertex_array_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gl/gl_context_stub_with_extensions.h"
#include "ui/gl/gl_surface_stub.h"
#include "ui/gl/gl_mock.h"
@@ -211,6 +211,10 @@ class GLES2DecoderTestBase : public ::testing::TestWithParam<bool> {
return *group_.get();
}
+ void LoseContexts(error::ContextLostReason reason) const {
+ group_->LoseContexts(reason);
+ }
+
::testing::StrictMock< ::gfx::MockGLInterface>* GetGLMock() const {
return gl_.get();
}
@@ -540,7 +544,7 @@ class GLES2DecoderTestBase : public ::testing::TestWithParam<bool> {
// Use StrictMock to make 100% sure we know how GL will be called.
scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_;
scoped_refptr<gfx::GLSurfaceStub> surface_;
- scoped_refptr<gfx::GLContextStubWithExtensions> context_;
+ scoped_refptr<GLContextMock> context_;
scoped_ptr<MockGLES2Decoder> mock_decoder_;
scoped_ptr<GLES2Decoder> decoder_;
MemoryTracker* memory_tracker_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
new file mode 100644
index 0000000..3fe88b9
--- /dev/null
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
@@ -0,0 +1,273 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/service/cmd_buffer_engine.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gl_surface_mock.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/command_buffer/service/mocks.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_mock.h"
+
+using ::gfx::MockGLInterface;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MatcherCast;
+using ::testing::Mock;
+using ::testing::Pointee;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArrayArgument;
+using ::testing::SetArgumentPointee;
+using ::testing::SetArgPointee;
+using ::testing::StrEq;
+using ::testing::StrictMock;
+
+namespace gpu {
+namespace gles2 {
+
+using namespace cmds;
+
+class GLES2DecoderDrawOOMTest : public GLES2DecoderManualInitTest {
+ protected:
+ void Init(bool has_robustness) {
+ InitState init;
+ init.lose_context_when_out_of_memory = true;
+ if (has_robustness)
+ init.extensions = "GL_ARB_robustness";
+ InitDecoder(init);
+ SetupDefaultProgram();
+ }
+
+ void Draw(GLenum reset_status,
+ error::ContextLostReason expected_other_reason) {
+ const GLsizei kFakeLargeCount = 0x1234;
+ SetupTexture();
+ if (context_->WasAllocatedUsingRobustnessExtension()) {
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
+ .WillOnce(Return(reset_status));
+ }
+ AddExpectationsForSimulatedAttrib0WithError(kFakeLargeCount, 0,
+ GL_OUT_OF_MEMORY);
+ EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0).RetiresOnSaturation();
+ // Other contexts in the group should be lost also.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(expected_other_reason))
+ .Times(1)
+ .RetiresOnSaturation();
+ DrawArrays cmd;
+ cmd.Init(GL_TRIANGLES, 0, kFakeLargeCount);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ }
+};
+
+// Test that we lose context.
+TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonOOM) {
+ Init(false); // without robustness
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kOutOfMemory;
+ Draw(GL_NO_ERROR, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kOutOfMemory, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsNoError) {
+ Init(true); // with robustness
+ // If the reset status is NO_ERROR, we should be signaling kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kOutOfMemory;
+ Draw(GL_NO_ERROR, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kOutOfMemory, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsGuilty) {
+ Init(true);
+ // If there was a reset, it should override kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kUnknown;
+ Draw(GL_GUILTY_CONTEXT_RESET_ARB, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kGuilty, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsUnknown) {
+ Init(true);
+ // If there was a reset, it should override kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kUnknown;
+ Draw(GL_UNKNOWN_CONTEXT_RESET_ARB, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kUnknown, decoder_->GetContextLostReason());
+}
+
+INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderDrawOOMTest, ::testing::Bool());
+
+class GLES2DecoderLostContextTest : public GLES2DecoderManualInitTest {
+ protected:
+ void Init(bool has_robustness) {
+ InitState init;
+ init.gl_version = "opengl es 2.0";
+ if (has_robustness)
+ init.extensions = "GL_KHR_robustness";
+ InitDecoder(init);
+ }
+
+ void InitWithVirtualContextsAndRobustness() {
+ base::CommandLine command_line(0, NULL);
+ command_line.AppendSwitchASCII(
+ switches::kGpuDriverBugWorkarounds,
+ base::IntToString(USE_VIRTUALIZED_GL_CONTEXTS));
+ InitState init;
+ init.gl_version = "opengl es 2.0";
+ init.extensions = "GL_KHR_robustness";
+ InitDecoderWithCommandLine(init, &command_line);
+ }
+
+ void DoGetErrorWithContextLost(GLenum reset_status) {
+ DCHECK(context_->HasExtension("GL_KHR_robustness"));
+ EXPECT_CALL(*gl_, GetError())
+ .WillOnce(Return(GL_CONTEXT_LOST_KHR))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
+ .WillOnce(Return(reset_status));
+ cmds::GetError cmd;
+ cmd.Init(shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ EXPECT_EQ(static_cast<GLuint>(GL_NO_ERROR), *GetSharedMemoryAs<GLenum*>());
+ }
+
+ void ClearCurrentDecoderError() {
+ DCHECK(decoder_->WasContextLost());
+ EXPECT_CALL(*gl_, GetError())
+ .WillOnce(Return(GL_CONTEXT_LOST_KHR))
+ .RetiresOnSaturation();
+ cmds::GetError cmd;
+ cmd.Init(shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ }
+};
+
+TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrent) {
+ Init(false); // without robustness
+ EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
+ // Expect the group to be lost.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
+ decoder_->MakeCurrent();
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kMakeCurrentFailed, decoder_->GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrentWithRobustness) {
+ Init(true); // with robustness
+ // If we can't make the context current, we cannot query the robustness
+ // extension.
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()).Times(0);
+ EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
+ // Expect the group to be lost.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
+ decoder_->MakeCurrent();
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_FALSE(decoder_->WasContextLostByRobustnessExtension());
+ EXPECT_EQ(error::kMakeCurrentFailed, decoder_->GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(GLES2DecoderLostContextTest, LostFromResetAfterMakeCurrent) {
+ Init(true); // with robustness
+ InSequence seq;
+ EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
+ .WillOnce(Return(GL_GUILTY_CONTEXT_RESET_KHR));
+ // Expect the group to be lost.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
+ decoder_->MakeCurrent();
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
+ EXPECT_EQ(error::kGuilty, decoder_->GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(GLES2DecoderLostContextTest, LoseGuiltyFromGLError) {
+ Init(true);
+ // Always expect other contexts to be signaled as 'kUnknown' since we can't
+ // query their status without making them current.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
+ .Times(1);
+ DoGetErrorWithContextLost(GL_GUILTY_CONTEXT_RESET_KHR);
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
+ EXPECT_EQ(error::kGuilty, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderLostContextTest, LoseInnocentFromGLError) {
+ Init(true);
+ // Always expect other contexts to be signaled as 'kUnknown' since we can't
+ // query their status without making them current.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
+ .Times(1);
+ DoGetErrorWithContextLost(GL_INNOCENT_CONTEXT_RESET_KHR);
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
+ EXPECT_EQ(error::kInnocent, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderLostContextTest, LoseVirtualContextWithRobustness) {
+ InitWithVirtualContextsAndRobustness();
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
+ .Times(1);
+ // Signal guilty....
+ DoGetErrorWithContextLost(GL_GUILTY_CONTEXT_RESET_KHR);
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
+ // ...but make sure we don't pretend, since for virtual contexts we don't
+ // know if this was really the guilty client.
+ EXPECT_EQ(error::kUnknown, decoder_->GetContextLostReason());
+}
+
+TEST_P(GLES2DecoderLostContextTest, LoseGroupFromRobustness) {
+ // If one context in a group is lost through robustness,
+ // the other ones should also get lost and query the reset status.
+ Init(true);
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
+ .Times(1);
+ // There should be no GL calls, since we might not have a current context.
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()).Times(0);
+ LoseContexts(error::kUnknown);
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kUnknown, decoder_->GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+INSTANTIATE_TEST_CASE_P(Service,
+ GLES2DecoderLostContextTest,
+ ::testing::Bool());
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
index fc5e2de..87e95b7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
@@ -739,35 +739,6 @@ TEST_P(GLES2DecoderWithShaderTest, DrawArraysSimulatedAttrib0OOMFails) {
EXPECT_FALSE(GetDecoder()->WasContextLost());
}
-// Test that we lose context.
-TEST_P(GLES2DecoderManualInitTest, LoseContextWhenOOM) {
- InitState init;
- init.has_alpha = true;
- init.has_depth = true;
- init.request_alpha = true;
- init.request_depth = true;
- init.bind_generates_resource = true;
- init.lose_context_when_out_of_memory = true;
- InitDecoder(init);
- SetupDefaultProgram();
-
- const GLsizei kFakeLargeCount = 0x1234;
- SetupTexture();
- AddExpectationsForSimulatedAttrib0WithError(
- kFakeLargeCount, 0, GL_OUT_OF_MEMORY);
- EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0).RetiresOnSaturation();
- // Other contexts in the group should be lost also.
- EXPECT_CALL(*mock_decoder_, LoseContext(GL_UNKNOWN_CONTEXT_RESET_ARB))
- .Times(1)
- .RetiresOnSaturation();
- DrawArrays cmd;
- cmd.Init(GL_TRIANGLES, 0, kFakeLargeCount);
- // This context should be lost.
- EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
- EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
- EXPECT_TRUE(decoder_->WasContextLost());
-}
-
TEST_P(GLES2DecoderWithShaderTest, DrawArraysBadTextureUsesBlack) {
DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
// This is an NPOT texture. As the default filtering requires mips
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 8988349..23779ea 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -404,8 +404,8 @@ void GLManager::Destroy() {
gles2_helper_.reset();
command_buffer_.reset();
if (decoder_.get()) {
- decoder_->MakeCurrent();
- decoder_->Destroy(true);
+ bool have_context = decoder_->GetGLContext()->MakeCurrent(surface_.get());
+ decoder_->Destroy(have_context);
decoder_.reset();
}
}
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index aa75746..9b50458 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -205,6 +205,8 @@
'command_buffer/service/context_group_unittest.cc',
'command_buffer/service/feature_info_unittest.cc',
'command_buffer/service/framebuffer_manager_unittest.cc',
+ 'command_buffer/service/gl_context_mock.cc',
+ 'command_buffer/service/gl_context_mock.h',
'command_buffer/service/gl_surface_mock.cc',
'command_buffer/service/gl_surface_mock.h',
'command_buffer/service/gles2_cmd_decoder_unittest.cc',
@@ -221,6 +223,7 @@
'command_buffer/service/gles2_cmd_decoder_unittest_base.cc',
'command_buffer/service/gles2_cmd_decoder_unittest_base.h',
'command_buffer/service/gles2_cmd_decoder_unittest_buffers.cc',
+ 'command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc',
'command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc',
'command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc',
'command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ab12322..638b36a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -11466,6 +11466,13 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary>
</histogram>
+<histogram name="GPU.ContextLost" enum="ContextLostReason">
+ <owner>sievers@chromium.org</owner>
+ <summary>
+ The reason a GPU command buffer context of a given type was lost.
+ </summary>
+</histogram>
+
<histogram name="GPU.CreateBrowserCompositor" units="microseconds">
<owner>vangelis@chromium.org</owner>
<summary>
@@ -48156,6 +48163,22 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="22" label="App banner setting (Android only)"/>
</enum>
+<enum name="ContextLostReason" type="int">
+ <summary>The reason for losing a GPU context.</summary>
+ <int value="0" label="CONTEXT_INIT_FAILED"/>
+ <int value="1" label="CONTEXT_LOST_GPU_CHANNEL_ERROR"/>
+ <int value="2" label="CONTEXT_PARSE_ERROR_INVALID_SIZE"/>
+ <int value="3" label="CONTEXT_PARSE_ERROR_OUT_OF_BOUNDS"/>
+ <int value="4" label="CONTEXT_PARSE_ERROR_UNKNOWN_COMMAND"/>
+ <int value="5" label="CONTEXT_PARSE_ERROR_INVALID_ARGS"/>
+ <int value="6" label="CONTEXT_PARSE_ERROR_GENERIC_ERROR"/>
+ <int value="7" label="CONTEXT_LOST_GUILTY"/>
+ <int value="8" label="CONTEXT_LOST_INNOCENT"/>
+ <int value="9" label="CONTEXT_LOST_UNKNOWN"/>
+ <int value="10" label="CONTEXT_LOST_OUT_OF_MEMORY"/>
+ <int value="11" label="CONTEXT_LOST_MAKECURRENT_FAILED"/>
+</enum>
+
<enum name="CookieDeletionCause" type="int">
<summary>Reason why a cookie was removed from the cookie store</summary>
<int value="0" label="explicit">
@@ -64799,6 +64822,26 @@ To add a new entry, add it with any value and run test to compute valid value.
<affected-histogram name="PLT.LoadType"/>
</histogram_suffixes>
+<histogram_suffixes name="ContextType" separator=".">
+ <suffix name="BrowserCompositor"
+ label="A BrowserCompositor GPU command buffer context"/>
+ <suffix name="BrowserMainThread"
+ label="A BrowserMainThread GPU command buffer context"/>
+ <suffix name="RenderCompositor"
+ label="A RenderCompositor GPU command buffer context"/>
+ <suffix name="RenderMainThread"
+ label="A RenderMainThread GPU command buffer context"/>
+ <suffix name="RenderWorker"
+ label="A RenderWorker GPU command buffer context"/>
+ <suffix name="Unknown" label="A GPU command buffer context of unknown type"/>
+ <suffix name="VideoAccelerator"
+ label="A VideoAccelerator GPU command buffer context"/>
+ <suffix name="VideoCapture"
+ label="A VideoCapture GPU command buffer context"/>
+ <suffix name="WebGL" label="A WebGL GPU command buffer context"/>
+ <affected-histogram name="GPU.ContextLost"/>
+</histogram_suffixes>
+
<histogram_suffixes name="CreditCardScanSuccess">
<suffix name="Completed" label="Credit card scan completed."/>
<suffix name="Cancelled" label="Credit card scan was cancelled."/>
diff --git a/ui/gl/gl_context_stub_with_extensions.cc b/ui/gl/gl_context_stub_with_extensions.cc
index 1065df7..855eaa2 100644
--- a/ui/gl/gl_context_stub_with_extensions.cc
+++ b/ui/gl/gl_context_stub_with_extensions.cc
@@ -27,4 +27,10 @@ std::string GLContextStubWithExtensions::GetGLVersion() {
return version_str_;
}
+bool GLContextStubWithExtensions::WasAllocatedUsingRobustnessExtension() {
+ return HasExtension("GL_ARB_robustness") ||
+ HasExtension("GL_KHR_robustness") ||
+ HasExtension("GL_EXT_robustness");
+}
+
} // namespace gfx
diff --git a/ui/gl/gl_context_stub_with_extensions.h b/ui/gl/gl_context_stub_with_extensions.h
index 190e03e..d9da9d5 100644
--- a/ui/gl/gl_context_stub_with_extensions.h
+++ b/ui/gl/gl_context_stub_with_extensions.h
@@ -19,6 +19,7 @@ class GL_EXPORT GLContextStubWithExtensions : public gfx::GLContextStub {
void AddExtensionsString(const char* extensions);
void SetGLVersionString(const char* version_str);
+ bool WasAllocatedUsingRobustnessExtension() override;
protected:
std::string GetGLVersion() override;