diff options
author | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-14 00:38:43 +0000 |
---|---|---|
committer | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-14 00:38:43 +0000 |
commit | 38d139d7410c8e42e731c5c58fab23d8d07b2721 (patch) | |
tree | fc3b1dba6528b6685354480990c4e11be03200e2 /gpu/command_buffer/service | |
parent | 2c812ba0ee1bf617c72a8bcd1a26b3a8418b0b5f (diff) | |
download | chromium_src-38d139d7410c8e42e731c5c58fab23d8d07b2721.zip chromium_src-38d139d7410c8e42e731c5c58fab23d8d07b2721.tar.gz chromium_src-38d139d7410c8e42e731c5c58fab23d8d07b2721.tar.bz2 |
Detect and expose loss of OpenGL context using GL_ARB_robustness.
(This CL was originally reviewed under
http://codereview.chromium.org/7331020/ . The only difference is the
removal of an #include from command_buffer.h that was accidentally
left in and which caused a significant increase in the number of files
containing static initializers, presumably because of the dependent
#include of <iostream>.)
This initial patch changes the Linux port to use
GLX_ARB_create_context_robustness when available, and tests
periodically whether the context has been lost after each draw call
and when making the context current. The detection of context loss
also works with EGL and ANGLE, although it always reports an unknown
reset status.
WebKit changes will follow which test the reset status and determine
what to do in response; for example, the policy might be to never
restore a WebGL context which was lost (due to a GPU reset) and which
was determined to be the guilty context.
Tested manually with WebGL stress tests and verified on Linux and
Windows that in at least some situations it is possible to detect
guilty contexts and shut down the associated WebGL application. Some
precision of this detection was recently lost and will need to be
fixed in following CLs. Also updated and ran GPU unit tests.
BUG=88106
TEST=none (tested manually; try servers)
R=gman,apatrick,piman
Review URL: http://codereview.chromium.org/7362005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92453 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service')
9 files changed, 127 insertions, 59 deletions
diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc index b042a23..064341d 100644 --- a/gpu/command_buffer/service/command_buffer_service.cc +++ b/gpu/command_buffer/service/command_buffer_service.cc @@ -98,6 +98,7 @@ CommandBufferService::State CommandBufferService::GetState() { state.put_offset = put_offset_; state.token = token_; state.error = error_; + state.context_lost_reason = context_lost_reason_; state.generation = ++generation_; return state; @@ -254,6 +255,11 @@ void CommandBufferService::SetParseError(error::Error error) { } } +void CommandBufferService::SetContextLostReason( + error::ContextLostReason reason) { + context_lost_reason_ = reason; +} + void CommandBufferService::SetPutOffsetChangeCallback( Callback1<bool>::Type* callback) { put_offset_change_callback_.reset(callback); diff --git a/gpu/command_buffer/service/command_buffer_service.h b/gpu/command_buffer/service/command_buffer_service.h index cffef5f..9c52531 100644 --- a/gpu/command_buffer/service/command_buffer_service.h +++ b/gpu/command_buffer/service/command_buffer_service.h @@ -40,6 +40,7 @@ class CommandBufferService : public CommandBuffer { virtual Buffer GetTransferBuffer(int32 handle); virtual void SetToken(int32 token); virtual void SetParseError(error::Error error); + virtual void SetContextLostReason(error::ContextLostReason); // Sets a callback that is called whenever the put offset is changed. When // called with sync==true, the callback must not return until some progress @@ -64,6 +65,7 @@ class CommandBufferService : public CommandBuffer { int32 token_; uint32 generation_; error::Error error_; + error::ContextLostReason context_lost_reason_; }; } // namespace gpu diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 045dfc3..e989af6 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -496,6 +496,8 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, bool BoundFramebufferHasDepthAttachment(); bool BoundFramebufferHasStencilAttachment(); + virtual error::ContextLostReason GetContextLostReason(); + private: friend class ScopedGLErrorSuppressor; friend class ScopedResolvedFrameBufferBinder; @@ -888,9 +890,6 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // Wrapper for glDetachShader void DoDetachShader(GLuint client_program_id, GLint client_shader_id); - // Wrapper for glDrawArrays. - void DoDrawArrays(GLenum mode, GLint first, GLsizei count); - // Wrapper for glDisable void DoDisable(GLenum cap); @@ -1134,6 +1133,9 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, error::Error* error, GLuint* service_id, void** result, GLenum* result_type); + // Returns true if the context was just lost due to e.g. GL_ARB_robustness. + bool WasContextLost(); + // Generate a member function prototype for each command in an automated and // typesafe way. #define GLES2_CMD_OP(name) \ @@ -1301,6 +1303,9 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, int frame_number_; + bool has_arb_robustness_; + GLenum reset_status_; + DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl); }; @@ -1637,7 +1642,9 @@ GLES2DecoderImpl::GLES2DecoderImpl(SurfaceManager* surface_manager, validators_(group_->feature_info()->validators()), feature_info_(group_->feature_info()), tex_image_2d_failed_(false), - frame_number_(0) { + frame_number_(0), + has_arb_robustness_(false), + reset_status_(GL_NO_ERROR) { attrib_0_value_.v[0] = 0.0f; attrib_0_value_.v[1] = 0.0f; attrib_0_value_.v[2] = 0.0f; @@ -1885,6 +1892,8 @@ bool GLES2DecoderImpl::Initialize( glEnable(GL_POINT_SPRITE); } + has_arb_robustness_ = context->HasExtension("GL_ARB_robustness"); + if (!InitializeShaderTranslator()) { return false; } @@ -2064,7 +2073,13 @@ void GLES2DecoderImpl::DeleteTexturesHelper( // } // anonymous namespace bool GLES2DecoderImpl::MakeCurrent() { - return context_.get() ? context_->MakeCurrent(surface_.get()) : false; + bool result = context_.get() ? context_->MakeCurrent(surface_.get()) : false; + if (result && WasContextLost()) { + LOG(ERROR) << " GLES2DecoderImpl: Context lost during MakeCurrent."; + result = false; + } + + return result; } void GLES2DecoderImpl::RestoreCurrentRenderbufferBindings() { @@ -3361,43 +3376,6 @@ void GLES2DecoderImpl::DoClear(GLbitfield mask) { } } -void GLES2DecoderImpl::DoDrawArrays( - GLenum mode, GLint first, GLsizei count) { - if (!CheckFramebufferComplete("glDrawArrays")) { - return; - } - // We have to check this here because the prototype for glDrawArrays - // is GLint not GLsizei. - if (first < 0) { - SetGLError(GL_INVALID_VALUE, "glDrawArrays: first < 0"); - return; - } - - if (count == 0) { - return; - } - - GLuint max_vertex_accessed = first + count - 1; - if (IsDrawValid(max_vertex_accessed)) { - bool simulated_attrib_0 = SimulateAttrib0(max_vertex_accessed); - bool simulated_fixed_attribs = false; - if (SimulateFixedAttribs(max_vertex_accessed, &simulated_fixed_attribs)) { - bool textures_set = SetBlackTextureForNonRenderableTextures(); - ApplyDirtyState(); - glDrawArrays(mode, first, count); - if (textures_set) { - RestoreStateForNonRenderableTextures(); - } - if (simulated_fixed_attribs) { - RestoreStateForSimulatedFixedAttribs(); - } - } - if (simulated_attrib_0) { - RestoreStateForSimulatedAttrib0(); - } - } -} - void GLES2DecoderImpl::DoFramebufferRenderbuffer( GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint client_renderbuffer_id) { @@ -4371,6 +4349,59 @@ void GLES2DecoderImpl::RestoreStateForSimulatedFixedAttribs() { bound_array_buffer_ ? bound_array_buffer_->service_id() : 0); } +error::Error GLES2DecoderImpl::HandleDrawArrays( + uint32 immediate_data_size, const gles2::DrawArrays& c) { + GLenum mode = static_cast<GLenum>(c.mode); + GLint first = static_cast<GLint>(c.first); + GLsizei count = static_cast<GLsizei>(c.count); + if (!validators_->draw_mode.IsValid(mode)) { + SetGLError(GL_INVALID_ENUM, "glDrawArrays: mode GL_INVALID_ENUM"); + return error::kNoError; + } + if (count < 0) { + SetGLError(GL_INVALID_VALUE, "glDrawArrays: count < 0"); + return error::kNoError; + } + if (!CheckFramebufferComplete("glDrawArrays")) { + return error::kNoError; + } + // We have to check this here because the prototype for glDrawArrays + // is GLint not GLsizei. + if (first < 0) { + SetGLError(GL_INVALID_VALUE, "glDrawArrays: first < 0"); + return error::kNoError; + } + + if (count == 0) { + return error::kNoError; + } + + GLuint max_vertex_accessed = first + count - 1; + if (IsDrawValid(max_vertex_accessed)) { + bool simulated_attrib_0 = SimulateAttrib0(max_vertex_accessed); + bool simulated_fixed_attribs = false; + if (SimulateFixedAttribs(max_vertex_accessed, &simulated_fixed_attribs)) { + bool textures_set = SetBlackTextureForNonRenderableTextures(); + ApplyDirtyState(); + glDrawArrays(mode, first, count); + if (textures_set) { + RestoreStateForNonRenderableTextures(); + } + if (simulated_fixed_attribs) { + RestoreStateForSimulatedFixedAttribs(); + } + } + if (simulated_attrib_0) { + RestoreStateForSimulatedAttrib0(); + } + if (WasContextLost()) { + LOG(ERROR) << " GLES2DecoderImpl: Context lost during DrawArrays."; + return error::kLostContext; + } + } + return error::kNoError; +} + error::Error GLES2DecoderImpl::HandleDrawElements( uint32 immediate_data_size, const gles2::DrawElements& c) { if (!bound_element_array_buffer_ || @@ -4435,6 +4466,10 @@ error::Error GLES2DecoderImpl::HandleDrawElements( if (simulated_attrib_0) { RestoreStateForSimulatedAttrib0(); } + if (WasContextLost()) { + LOG(ERROR) << " GLES2DecoderImpl: Context lost during DrawElements."; + return error::kLostContext; + } } return error::kNoError; } @@ -6730,6 +6765,39 @@ error::Error GLES2DecoderImpl::HandleGetProgramInfoCHROMIUM( return error::kNoError; } +error::ContextLostReason GLES2DecoderImpl::GetContextLostReason() { + 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. + return error::kUnknown; + case GL_GUILTY_CONTEXT_RESET_ARB: + return error::kGuilty; + case GL_INNOCENT_CONTEXT_RESET_ARB: + return error::kInnocent; + case GL_UNKNOWN_CONTEXT_RESET_ARB: + return error::kUnknown; + } + + NOTREACHED(); + return error::kUnknown; +} + +bool GLES2DecoderImpl::WasContextLost() { + if (context_->WasAllocatedUsingARBRobustness() && has_arb_robustness_) { + GLenum status = glGetGraphicsResetStatusARB(); + if (status != GL_NO_ERROR) { + // The graphics card was reset. Signal a lost context to the application. + reset_status_ = status; + LOG(ERROR) << (surface_->IsOffscreen() ? "Offscreen" : "Onscreen") + << " context lost via ARB_robustness. Reset status = 0x" + << std::hex << status << std::dec; + return true; + } + } + return false; +} + // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator. diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 0551e5a..abd2b85 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h @@ -120,6 +120,9 @@ class GLES2Decoder : public CommonDecoder { virtual bool GetServiceTextureId(uint32 client_texture_id, uint32* service_texture_id); + // Provides detail about a lost context if one occurred. + virtual error::ContextLostReason GetContextLostReason() = 0; + protected: GLES2Decoder(); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 89cf621..44cba0b 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -630,23 +630,6 @@ error::Error GLES2DecoderImpl::HandleDisableVertexAttribArray( return error::kNoError; } -error::Error GLES2DecoderImpl::HandleDrawArrays( - uint32 immediate_data_size, const gles2::DrawArrays& c) { - GLenum mode = static_cast<GLenum>(c.mode); - GLint first = static_cast<GLint>(c.first); - GLsizei count = static_cast<GLsizei>(c.count); - if (!validators_->draw_mode.IsValid(mode)) { - SetGLError(GL_INVALID_ENUM, "glDrawArrays: mode GL_INVALID_ENUM"); - return error::kNoError; - } - if (count < 0) { - SetGLError(GL_INVALID_VALUE, "glDrawArrays: count < 0"); - return error::kNoError; - } - DoDrawArrays(mode, first, count); - return error::kNoError; -} - error::Error GLES2DecoderImpl::HandleEnable( uint32 immediate_data_size, const gles2::Enable& c) { GLenum cap = static_cast<GLenum>(c.cap); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 0a44ecf..2f1275c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h @@ -54,6 +54,7 @@ class MockGLES2Decoder : public GLES2Decoder { const void* cmd_data)); MOCK_METHOD2(GetServiceTextureId, bool(uint32 client_texture_id, uint32* service_texture_id)); + MOCK_METHOD0(GetContextLostReason, error::ContextLostReason()); MOCK_CONST_METHOD1(GetCommandName, const char*(unsigned int command_id)); DISALLOW_COPY_AND_ASSIGN(MockGLES2Decoder); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h index fe90c47..209f259 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h @@ -704,6 +704,7 @@ TEST_F(GLES2DecoderTest1, DisableVertexAttribArrayValidArgs) { EXPECT_EQ(GL_NO_ERROR, GetGLError()); } // TODO(gman): DrawArrays + // TODO(gman): DrawElements diff --git a/gpu/command_buffer/service/gpu_scheduler.cc b/gpu/command_buffer/service/gpu_scheduler.cc index 0a7d1dd..9365118 100644 --- a/gpu/command_buffer/service/gpu_scheduler.cc +++ b/gpu/command_buffer/service/gpu_scheduler.cc @@ -170,6 +170,7 @@ void GpuScheduler::ProcessCommands() { if (decoder_.get()) { if (!decoder_->MakeCurrent()) { LOG(ERROR) << "Context lost because MakeCurrent failed."; + command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); command_buffer_->SetParseError(error::kLostContext); return; } @@ -213,6 +214,7 @@ void GpuScheduler::ProcessCommands() { is_break = true; break; } else if (error::IsError(error)) { + command_buffer_->SetContextLostReason(decoder_->GetContextLostReason()); command_buffer_->SetParseError(error); return; } diff --git a/gpu/command_buffer/service/gpu_scheduler_unittest.cc b/gpu/command_buffer/service/gpu_scheduler_unittest.cc index 8cdb361..3d21f90 100644 --- a/gpu/command_buffer/service/gpu_scheduler_unittest.cc +++ b/gpu/command_buffer/service/gpu_scheduler_unittest.cc @@ -217,6 +217,8 @@ TEST_F(GpuSchedulerTest, SetsErrorCodeOnCommandBuffer) { error::kUnknownCommand)); EXPECT_CALL(*command_buffer_, SetGetOffset(1)); + EXPECT_CALL(*decoder_, GetContextLostReason()) + .WillOnce(Return(error::kUnknown)); EXPECT_CALL(*command_buffer_, SetParseError(error::kUnknownCommand)); |