diff options
author | sievers <sievers@chromium.org> | 2015-04-27 17:45:31 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-28 00:45:49 +0000 |
commit | fbaa5dc2fcb2e482c1daec1b9bcf1d577d53ca30 (patch) | |
tree | b5024f14c70c616a96e9e4254315b7bce287ac27 /gpu | |
parent | 8fbbc669b356f7c25b8c6417738986f5c8525d37 (diff) | |
download | chromium_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}
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/BUILD.gn | 3 | ||||
-rw-r--r-- | gpu/command_buffer/common/constants.h | 15 | ||||
-rw-r--r-- | gpu/command_buffer/service/context_group.cc | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/context_group.h | 2 | ||||
-rw-r--r-- | gpu/command_buffer/service/gl_context_mock.cc | 14 | ||||
-rw-r--r-- | gpu/command_buffer/service/gl_context_mock.h | 25 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 175 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.h | 6 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_mock.h | 6 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc | 23 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc | 9 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h | 8 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc | 273 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc | 29 | ||||
-rw-r--r-- | gpu/command_buffer/tests/gl_manager.cc | 4 | ||||
-rw-r--r-- | gpu/gpu.gyp | 3 |
16 files changed, 482 insertions, 117 deletions
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', |