Implement raster context loss methods
Notable changes in this CL:
- mark all RasterDecoderImpl class final (maybe compiler can
devirtualize)
- copy over context loss related methods from GLES2DecoderImpl to
RasterDecoderImpl
- port over GLES2Decoder context loss unit tests to RasterDecoder
Bug: 789238
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I919163ce459603109b12ee864561debb136ff594
Reviewed-on: https://chromium-review.googlesource.com/1025961
Commit-Queue: Jonathan Backer <[email protected]>
Reviewed-by: Peng Huang <[email protected]>
Cr-Commit-Position: refs/heads/master@{#553538}
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 8d39107..f786ad8a 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -362,6 +362,7 @@
"command_buffer/service/raster_decoder_unittest_1.cc",
"command_buffer/service/raster_decoder_unittest_base.cc",
"command_buffer/service/raster_decoder_unittest_base.h",
+ "command_buffer/service/raster_decoder_unittest_context_lost.cc",
"command_buffer/service/renderbuffer_manager_unittest.cc",
"command_buffer/service/scheduler_unittest.cc",
"command_buffer/service/service_discardable_manager_unittest.cc",
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
index 6a615c42..0a0ab74 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
@@ -2,35 +2,18 @@
// 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/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 "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/service/gl_surface_mock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"
-using ::gl::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::SetArgPointee;
-using ::testing::SetArgPointee;
-using ::testing::StrEq;
-using ::testing::StrictMock;
namespace gpu {
namespace gles2 {
@@ -71,7 +54,7 @@
// Test that we lose context.
TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonOOM) {
- Init(false); // without robustness
+ Init(false); // without robustness
const error::ContextLostReason expected_reason_for_other_contexts =
error::kOutOfMemory;
Draw(GL_NO_ERROR, expected_reason_for_other_contexts);
@@ -81,7 +64,7 @@
}
TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsNoError) {
- Init(true); // with robustness
+ 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;
@@ -159,7 +142,7 @@
};
TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrent) {
- Init(false); // without robustness
+ 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);
@@ -173,7 +156,7 @@
}
TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrentWithRobustness) {
- Init(true); // with robustness
+ Init(true); // with robustness
// If we can't make the context current, we cannot query the robustness
// extension.
EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()).Times(0);
@@ -285,7 +268,7 @@
}
TEST_P(GLES2DecoderLostContextTest, LostFromResetAfterMakeCurrent) {
- Init(true); // with robustness
+ Init(true); // with robustness
InSequence seq;
EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 8abd633..88dcc1b 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -33,6 +33,7 @@
#include "gpu/command_buffer/common/raster_cmd_ids.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/buffer_manager.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/decoder_client.h"
@@ -237,7 +238,8 @@
} // namespace
-class RasterDecoderImpl : public RasterDecoder, public gles2::ErrorStateClient {
+class RasterDecoderImpl final : public RasterDecoder,
+ public gles2::ErrorStateClient {
public:
RasterDecoderImpl(DecoderClient* client,
CommandBufferServiceBase* command_buffer_service,
@@ -376,6 +378,11 @@
return feature_info_->workarounds();
}
+ bool IsRobustnessSupported() {
+ return has_robustness_extension_ &&
+ context_->WasAllocatedUsingRobustnessExtension();
+ }
+
const gl::GLVersionInfo& gl_version_info() {
return feature_info_->gl_version_info();
}
@@ -621,6 +628,10 @@
bool supports_oop_raster_ = false;
+ bool has_robustness_extension_ = false;
+ bool context_was_lost_ = false;
+ bool reset_by_robustness_extension_ = false;
+
// The current decoder error communicates the decoder error through command
// processing functions that do not return the error value. Should be set
// only if not returning an error.
@@ -662,6 +673,9 @@
bool gpu_debug_commands_;
+ // An optional behaviour to lose the context and group when OOM.
+ bool lose_context_when_out_of_memory_ = false;
+
// Log extra info.
bool service_logging_;
@@ -787,6 +801,10 @@
surface_ = surface;
context_ = context;
+ // Save the loseContextWhenOutOfMemory context creation attribute.
+ lose_context_when_out_of_memory_ =
+ attrib_helper.lose_context_when_out_of_memory;
+
auto result =
group_->Initialize(this, attrib_helper.context_type, disallowed_features);
if (result != ContextResult::kSuccess) {
@@ -838,6 +856,10 @@
api()->glActiveTextureFn(GL_TEXTURE0);
CHECK_GL_ERROR();
+ has_robustness_extension_ = features().arb_robustness ||
+ features().khr_robustness ||
+ features().ext_robustness;
+
// Set all the default state because some GL drivers get it wrong.
// TODO(backer): Not all of this state needs to be initialized. Reduce the set
// if perf becomes a problem.
@@ -969,6 +991,13 @@
}
DCHECK_EQ(api(), gl::g_current_gl_context);
+ if (CheckResetStatus()) {
+ LOG(ERROR)
+ << " RasterDecoderImpl: Context reset detected after MakeCurrent.";
+ group_->LoseContexts(error::kUnknown);
+ return false;
+ }
+
// Rebind textures if the service ids may have changed.
RestoreAllExternalTextureBindingsIfNeeded();
@@ -1170,20 +1199,65 @@
}
bool RasterDecoderImpl::WasContextLost() const {
- return false;
+ return context_was_lost_;
}
bool RasterDecoderImpl::WasContextLostByRobustnessExtension() const {
- NOTIMPLEMENTED();
- return false;
+ return WasContextLost() && reset_by_robustness_extension_;
}
void RasterDecoderImpl::MarkContextLost(error::ContextLostReason reason) {
- NOTIMPLEMENTED();
+ // Only lose the context once.
+ if (WasContextLost())
+ return;
+
+ // Don't make GL calls in here, the context might not be current.
+ command_buffer_service()->SetContextLostReason(reason);
+ current_decoder_error_ = error::kLostContext;
+ context_was_lost_ = true;
+
+ if (vertex_array_manager_.get()) {
+ vertex_array_manager_->MarkContextLost();
+ }
+ state_.MarkContextLost();
}
bool RasterDecoderImpl::CheckResetStatus() {
- NOTIMPLEMENTED();
+ DCHECK(!WasContextLost());
+ DCHECK(context_->IsCurrent(nullptr));
+
+ 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 = api()->glGetGraphicsResetStatusARBFn();
+ if (driver_status == GL_NO_ERROR)
+ return false;
+
+ LOG(ERROR)
+ << "RasterDecoder 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;
+ }
return false;
}
@@ -1500,11 +1574,25 @@
}
void RasterDecoderImpl::OnContextLostError() {
- NOTIMPLEMENTED();
+ if (!WasContextLost()) {
+ // Need to lose current context before broadcasting!
+ CheckResetStatus();
+ group_->LoseContexts(error::kUnknown);
+ reset_by_robustness_extension_ = true;
+ }
}
void RasterDecoderImpl::OnOutOfMemoryError() {
- NOTIMPLEMENTED();
+ 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);
+ }
}
error::Error RasterDecoderImpl::HandleWaitSyncTokenCHROMIUM(
@@ -2471,7 +2559,7 @@
namespace {
// Helper to read client data from transfer cache.
-class TransferCacheDeserializeHelperImpl
+class TransferCacheDeserializeHelperImpl final
: public cc::TransferCacheDeserializeHelper {
public:
explicit TransferCacheDeserializeHelperImpl(
@@ -2479,12 +2567,12 @@
: transfer_cache_(transfer_cache) {
DCHECK(transfer_cache_);
}
- ~TransferCacheDeserializeHelperImpl() final = default;
+ ~TransferCacheDeserializeHelperImpl() override = default;
private:
cc::ServiceTransferCacheEntry* GetEntryInternal(
cc::TransferCacheEntryType entry_type,
- uint32_t entry_id) final {
+ uint32_t entry_id) override {
return transfer_cache_->GetEntry(entry_type, entry_id);
}
ServiceTransferCache* const transfer_cache_;
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 18a9a55..725d5aac 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -56,14 +56,6 @@
RasterDecoderTest() = default;
};
-class RasterDecoderManualInitTest : public RasterDecoderTestBase {
- public:
- RasterDecoderManualInitTest() = default;
-
- // Override default setup so nothing gets setup.
- void SetUp() override {}
-};
-
INSTANTIATE_TEST_CASE_P(Service, RasterDecoderTest, ::testing::Bool());
INSTANTIATE_TEST_CASE_P(Service,
RasterDecoderManualInitTest,
@@ -251,7 +243,10 @@
}
TEST_P(RasterDecoderManualInitTest, TexStorage2DWithEXTTextureStorage) {
- InitDecoderWithWorkarounds({"GL_ARB_sync", "GL_EXT_texture_storage"});
+ InitState init;
+ init.extensions.push_back("GL_EXT_texture_storage");
+ InitDecoder(init);
+
DoTexStorage2D(client_texture_id_, 2 /* levels */, kWidth, kHeight);
gles2::TextureRef* texture_ref =
@@ -277,7 +272,8 @@
TEST_P(RasterDecoderManualInitTest, TexStorage2DOutOfMemory) {
scoped_refptr<MockMemoryTracker> memory_tracker = new MockMemoryTracker();
set_memory_tracker(memory_tracker.get());
- InitDecoderWithWorkarounds({"GL_ARB_sync"});
+
+ InitDecoder(InitState());
EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(_))
.WillOnce(Return(false))
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index 797713d..9ac04f6 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -48,10 +48,13 @@
namespace gpu {
namespace raster {
+RasterDecoderTestBase::InitState::InitState() = default;
+RasterDecoderTestBase::InitState::~InitState() = default;
+
RasterDecoderTestBase::RasterDecoderTestBase()
- : surface_(NULL),
- context_(NULL),
- memory_tracker_(NULL),
+ : surface_(nullptr),
+ context_(nullptr),
+ memory_tracker_(nullptr),
client_texture_id_(106),
shared_memory_id_(0),
shared_memory_offset_(0),
@@ -77,7 +80,7 @@
void RasterDecoderTestBase::OnRescheduleAfterFinished() {}
void RasterDecoderTestBase::SetUp() {
- InitDecoderWithWorkarounds({"GL_ARB_sync"});
+ InitDecoder(InitState());
}
void RasterDecoderTestBase::AddExpectationsForVertexAttribManager() {
@@ -164,15 +167,13 @@
}
}
-void RasterDecoderTestBase::InitDecoderWithWorkarounds(
- std::initializer_list<std::string> extensions) {
+void RasterDecoderTestBase::InitDecoder(const InitState& init) {
std::string all_extensions;
- for (const std::string& extension : extensions) {
+ for (const std::string& extension : init.extensions) {
all_extensions += extension + " ";
}
const std::string gl_version("2.1");
const bool bind_generates_resource(false);
- const bool lose_context_when_out_of_memory(false);
const ContextType context_type(CONTEXT_TYPE_OPENGLES2);
// For easier substring/extension matching
@@ -182,8 +183,7 @@
gl_.reset(new StrictMock<MockGLInterface>());
::gl::MockGLInterface::SetGLInterface(gl_.get());
- gpu::GpuDriverBugWorkarounds workarounds;
- scoped_refptr<FeatureInfo> feature_info = new FeatureInfo(workarounds);
+ scoped_refptr<FeatureInfo> feature_info = new FeatureInfo(init.workarounds);
group_ = scoped_refptr<ContextGroup>(new ContextGroup(
gpu_preferences_, false, &mailbox_manager_, memory_tracker_,
@@ -229,7 +229,8 @@
ClearSharedMemory();
ContextCreationAttribs attribs;
- attribs.lose_context_when_out_of_memory = lose_context_when_out_of_memory;
+ attribs.lose_context_when_out_of_memory =
+ init.lose_context_when_out_of_memory;
attribs.context_type = context_type;
if (group_->feature_info()->feature_flags().native_vertex_array_object) {
@@ -311,7 +312,7 @@
decoder_.reset();
group_->Destroy(mock_decoder_.get(), false);
command_buffer_service_.reset();
- ::gl::MockGLInterface::SetGLInterface(NULL);
+ ::gl::MockGLInterface::SetGLInterface(nullptr);
gl_.reset();
gl::init::ShutdownGL(false);
}
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index 33705da7..8588a65f 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -12,6 +12,7 @@
#include <initializer_list>
#include <memory>
#include <string>
+#include <vector>
#include "base/message_loop/message_loop.h"
#include "gpu/command_buffer/client/client_test_helper.h"
@@ -139,9 +140,16 @@
void AddExpectationsForBindVertexArrayOES();
void AddExpectationsForRestoreAttribState(GLuint attrib);
- void InitDecoderWithWorkarounds(
- std::initializer_list<std::string> extensions);
+ struct InitState {
+ InitState();
+ ~InitState();
+ std::vector<std::string> extensions = {"GL_ARB_sync"};
+ bool lose_context_when_out_of_memory = false;
+ gpu::GpuDriverBugWorkarounds workarounds;
+ };
+
+ void InitDecoder(const InitState& init);
void ResetDecoder();
const gles2::ContextGroup& group() const { return *group_.get(); }
@@ -264,6 +272,14 @@
gles2::MockCopyTextureResourceManager* copy_texture_manager_; // not owned
};
+class RasterDecoderManualInitTest : public RasterDecoderTestBase {
+ public:
+ RasterDecoderManualInitTest() = default;
+
+ // Override default setup so nothing gets setup.
+ void SetUp() override {}
+};
+
} // namespace raster
} // namespace gpu
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
new file mode 100644
index 0000000..6ed33006
--- /dev/null
+++ b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
@@ -0,0 +1,318 @@
+// Copyright 2018 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/common/raster_cmd_format.h"
+#include "gpu/command_buffer/service/query_manager.h"
+#include "gpu/command_buffer/service/raster_decoder_unittest_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_mock.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Pointee;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArrayArgument;
+
+namespace gpu {
+namespace raster {
+
+using namespace cmds;
+
+class RasterDecoderOOMTest : public RasterDecoderManualInitTest {
+ protected:
+ void Init(bool has_robustness) {
+ InitState init;
+ init.lose_context_when_out_of_memory = true;
+ init.workarounds.simulate_out_of_memory_on_large_textures = true;
+ if (has_robustness) {
+ init.extensions.push_back("GL_ARB_robustness");
+ }
+ InitDecoder(init);
+ }
+
+ void OOM(GLenum reset_status,
+ error::ContextLostReason expected_other_reason) {
+ if (context_->WasAllocatedUsingRobustnessExtension()) {
+ EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
+ .WillOnce(Return(reset_status));
+ }
+ // Other contexts in the group should be lost also.
+ EXPECT_CALL(*mock_decoder_, MarkContextLost(expected_other_reason))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ // Trigger OOM with simulate_out_of_memory_on_large_textures
+ cmds::TexStorage2D cmd;
+ cmd.Init(client_texture_id_, /*levels=*/1, /*width=*/50000,
+ /*height=*/50000);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ }
+};
+
+// Test that we lose context.
+TEST_P(RasterDecoderOOMTest, ContextLostReasonOOM) {
+ Init(/*has_robustness=*/false);
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kOutOfMemory;
+ OOM(GL_NO_ERROR, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kOutOfMemory, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderOOMTest, ContextLostReasonWhenStatusIsNoError) {
+ Init(/*has_robustness=*/true);
+ // If the reset status is NO_ERROR, we should be signaling kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kOutOfMemory;
+ OOM(GL_NO_ERROR, expected_reason_for_other_contexts);
+ EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
+ EXPECT_TRUE(decoder_->WasContextLost());
+ EXPECT_EQ(error::kOutOfMemory, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderOOMTest, ContextLostReasonWhenStatusIsGuilty) {
+ Init(/*has_robustness=*/true);
+ // If there was a reset, it should override kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kUnknown;
+ OOM(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, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderOOMTest, ContextLostReasonWhenStatusIsUnknown) {
+ Init(/*has_robustness=*/true);
+ // If there was a reset, it should override kOutOfMemory.
+ const error::ContextLostReason expected_reason_for_other_contexts =
+ error::kUnknown;
+ OOM(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, GetContextLostReason());
+}
+
+INSTANTIATE_TEST_CASE_P(Service, RasterDecoderOOMTest, ::testing::Bool());
+
+class RasterDecoderLostContextTest : public RasterDecoderManualInitTest {
+ protected:
+ void Init(bool has_robustness) {
+ InitState init;
+ if (has_robustness) {
+ init.extensions.push_back("GL_KHR_robustness");
+ }
+ InitDecoder(init);
+ }
+
+ void InitWithVirtualContextsAndRobustness() {
+ InitState init;
+ init.extensions.push_back("GL_KHR_robustness");
+ init.workarounds.use_virtualized_gl_contexts = true;
+ InitDecoder(init);
+ }
+
+ 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));
+ 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();
+ GetError cmd;
+ cmd.Init(shared_memory_id_, shared_memory_offset_);
+ EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
+ }
+};
+
+TEST_P(RasterDecoderLostContextTest, LostFromMakeCurrent) {
+ Init(/*has_robustness=*/false);
+ 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, GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(RasterDecoderLostContextTest, LostFromMakeCurrentWithRobustness) {
+ Init(/*has_robustness=*/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, GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(RasterDecoderLostContextTest, TextureDestroyAfterLostFromMakeCurrent) {
+ Init(/*has_robustness=*/true);
+
+ // Create the texture.
+ DoTexStorage2D(client_texture_id_, /*levels=*/1, /*width=*/10, /*height=*/10);
+
+ // The texture should never be deleted at the GL level.
+ EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(kServiceTextureId)))
+ .Times(0)
+ .RetiresOnSaturation();
+
+ // Force context lost for MakeCurrent().
+ 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, GetContextLostReason());
+ ClearCurrentDecoderError();
+}
+
+TEST_P(RasterDecoderLostContextTest, QueryDestroyAfterLostFromMakeCurrent) {
+ Init(/*has_robustness=*/false);
+
+ const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef);
+ GenHelper<GenQueriesEXTImmediate>(kNewClientId);
+
+ BeginQueryEXT begin_cmd;
+ begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId,
+ shared_memory_id_, kSharedMemoryOffset);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+ QueryManager* query_manager = decoder_->GetQueryManager();
+ ASSERT_TRUE(query_manager != nullptr);
+ QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
+ ASSERT_TRUE(query != nullptr);
+ EXPECT_FALSE(query->IsPending());
+
+ EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation();
+ EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
+ .WillOnce(Return(kGlSync))
+ .RetiresOnSaturation();
+#if DCHECK_IS_ON()
+ EXPECT_CALL(*gl_, IsSync(kGlSync))
+ .WillOnce(Return(GL_TRUE))
+ .RetiresOnSaturation();
+#endif
+
+ EndQueryEXT end_cmd;
+ end_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, 1);
+ EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
+ EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+#if DCHECK_IS_ON()
+ EXPECT_CALL(*gl_, IsSync(kGlSync)).Times(0).RetiresOnSaturation();
+#endif
+ EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(0).RetiresOnSaturation();
+
+ // Force context lost for MakeCurrent().
+ 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, GetContextLostReason());
+ ClearCurrentDecoderError();
+ ResetDecoder();
+}
+
+TEST_P(RasterDecoderLostContextTest, LostFromResetAfterMakeCurrent) {
+ Init(/*has_robustness=*/true);
+ 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, GetContextLostReason());
+
+ // We didn't process commands, so we need to clear the decoder error,
+ // so that we can shut down cleanly.
+ ClearCurrentDecoderError();
+}
+
+TEST_P(RasterDecoderLostContextTest, LoseGuiltyFromGLError) {
+ Init(/*has_robustness=*/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, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderLostContextTest, LoseInnocentFromGLError) {
+ Init(/*has_robustness=*/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, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderLostContextTest, 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, GetContextLostReason());
+}
+
+TEST_P(RasterDecoderLostContextTest, 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, 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,
+ RasterDecoderLostContextTest,
+ ::testing::Bool());
+
+} // namespace raster
+} // namespace gpu