1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
6
7 #include "gpu/command_buffer/common/gles2_cmd_format.h"
8 #include "gpu/command_buffer/service/gl_surface_mock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/gl/gl_mock.h"
11
12 using ::testing::_;
13 using ::testing::InSequence;
14 using ::testing::Pointee;
15 using ::testing::Return;
16 using ::testing::SetArgPointee;
17
18 namespace gpu {
19 namespace gles2 {
20
21 class GLES2DecoderDrawOOMTest : public GLES2DecoderManualInitTest {
22 protected:
Init(bool has_robustness)23 void Init(bool has_robustness) {
24 InitState init;
25 init.lose_context_when_out_of_memory = true;
26 if (has_robustness)
27 init.extensions = "GL_ARB_robustness";
28 InitDecoder(init);
29 SetupDefaultProgram();
30 }
31
Draw(GLenum reset_status,error::ContextLostReason expected_other_reason)32 void Draw(GLenum reset_status,
33 error::ContextLostReason expected_other_reason) {
34 const GLsizei kFakeLargeCount = 0x1234;
35 SetupTexture();
36 if (context_->HasRobustness()) {
37 EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
38 .WillOnce(Return(reset_status));
39 }
40 AddExpectationsForSimulatedAttrib0WithError(kFakeLargeCount, 0,
41 GL_OUT_OF_MEMORY);
42 EXPECT_CALL(*gl_, DrawArrays(_, _, _)).Times(0).RetiresOnSaturation();
43 // Other contexts in the group should be lost also.
44 EXPECT_CALL(*mock_decoder_, MarkContextLost(expected_other_reason))
45 .Times(1)
46 .RetiresOnSaturation();
47 cmds::DrawArrays cmd;
48 cmd.Init(GL_TRIANGLES, 0, kFakeLargeCount);
49 EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
50 }
51 };
52
53 // Test that we lose context.
TEST_P(GLES2DecoderDrawOOMTest,ContextLostReasonOOM)54 TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonOOM) {
55 Init(false); // without robustness
56 const error::ContextLostReason expected_reason_for_other_contexts =
57 error::kOutOfMemory;
58 Draw(GL_NO_ERROR, expected_reason_for_other_contexts);
59 EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
60 EXPECT_TRUE(decoder_->WasContextLost());
61 EXPECT_EQ(error::kOutOfMemory, GetContextLostReason());
62 }
63
TEST_P(GLES2DecoderDrawOOMTest,ContextLostReasonWhenStatusIsNoError)64 TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsNoError) {
65 Init(true); // with robustness
66 // If the reset status is NO_ERROR, we should be signaling kOutOfMemory.
67 const error::ContextLostReason expected_reason_for_other_contexts =
68 error::kOutOfMemory;
69 Draw(GL_NO_ERROR, expected_reason_for_other_contexts);
70 EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
71 EXPECT_TRUE(decoder_->WasContextLost());
72 EXPECT_EQ(error::kOutOfMemory, GetContextLostReason());
73 }
74
TEST_P(GLES2DecoderDrawOOMTest,ContextLostReasonWhenStatusIsGuilty)75 TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsGuilty) {
76 Init(true);
77 // If there was a reset, it should override kOutOfMemory.
78 const error::ContextLostReason expected_reason_for_other_contexts =
79 error::kUnknown;
80 Draw(GL_GUILTY_CONTEXT_RESET_ARB, expected_reason_for_other_contexts);
81 EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
82 EXPECT_TRUE(decoder_->WasContextLost());
83 EXPECT_EQ(error::kGuilty, GetContextLostReason());
84 }
85
TEST_P(GLES2DecoderDrawOOMTest,ContextLostReasonWhenStatusIsUnknown)86 TEST_P(GLES2DecoderDrawOOMTest, ContextLostReasonWhenStatusIsUnknown) {
87 Init(true);
88 // If there was a reset, it should override kOutOfMemory.
89 const error::ContextLostReason expected_reason_for_other_contexts =
90 error::kUnknown;
91 Draw(GL_UNKNOWN_CONTEXT_RESET_ARB, expected_reason_for_other_contexts);
92 EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
93 EXPECT_TRUE(decoder_->WasContextLost());
94 EXPECT_EQ(error::kUnknown, GetContextLostReason());
95 }
96
97 INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderDrawOOMTest, ::testing::Bool());
98
99 class GLES2DecoderLostContextTest : public GLES2DecoderManualInitTest {
100 protected:
Init(bool has_robustness)101 void Init(bool has_robustness) {
102 InitState init;
103 init.gl_version = "OpenGL ES 2.0";
104 if (has_robustness)
105 init.extensions = "GL_KHR_robustness";
106 InitDecoder(init);
107 }
108
DoGetErrorWithContextLost(GLenum reset_status)109 void DoGetErrorWithContextLost(GLenum reset_status) {
110 DCHECK(context_->HasExtension("GL_KHR_robustness"));
111 EXPECT_CALL(*gl_, GetError())
112 .WillOnce(Return(GL_CONTEXT_LOST_KHR))
113 .RetiresOnSaturation();
114 EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
115 .WillOnce(Return(reset_status));
116 cmds::GetError cmd;
117 cmd.Init(shared_memory_id_, shared_memory_offset_);
118 EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
119 EXPECT_EQ(static_cast<GLuint>(GL_NO_ERROR), *GetSharedMemoryAs<GLenum*>());
120 }
121
ClearCurrentDecoderError()122 void ClearCurrentDecoderError() {
123 DCHECK(decoder_->WasContextLost());
124 EXPECT_CALL(*gl_, GetError())
125 .WillOnce(Return(GL_CONTEXT_LOST_KHR))
126 .RetiresOnSaturation();
127 cmds::GetError cmd;
128 cmd.Init(shared_memory_id_, shared_memory_offset_);
129 EXPECT_EQ(error::kLostContext, ExecuteCmd(cmd));
130 }
131 };
132
TEST_P(GLES2DecoderLostContextTest,LostFromMakeCurrent)133 TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrent) {
134 Init(false); // without robustness
135 EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
136 // Expect the group to be lost.
137 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
138 decoder_->MakeCurrent();
139 EXPECT_TRUE(decoder_->WasContextLost());
140 EXPECT_EQ(error::kMakeCurrentFailed, GetContextLostReason());
141
142 // We didn't process commands, so we need to clear the decoder error,
143 // so that we can shut down cleanly.
144 ClearCurrentDecoderError();
145 }
146
TEST_P(GLES2DecoderLostContextTest,LostFromMakeCurrentWithRobustness)147 TEST_P(GLES2DecoderLostContextTest, LostFromMakeCurrentWithRobustness) {
148 Init(true); // with robustness
149 // If we can't make the context current, we cannot query the robustness
150 // extension.
151 EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()).Times(0);
152 EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
153 // Expect the group to be lost.
154 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
155 decoder_->MakeCurrent();
156 EXPECT_TRUE(decoder_->WasContextLost());
157 EXPECT_FALSE(decoder_->WasContextLostByRobustnessExtension());
158 EXPECT_EQ(error::kMakeCurrentFailed, GetContextLostReason());
159
160 // We didn't process commands, so we need to clear the decoder error,
161 // so that we can shut down cleanly.
162 ClearCurrentDecoderError();
163 }
164
TEST_P(GLES2DecoderLostContextTest,TextureDestroyAfterLostFromMakeCurrent)165 TEST_P(GLES2DecoderLostContextTest, TextureDestroyAfterLostFromMakeCurrent) {
166 Init(true);
167 // Create a texture and framebuffer, and attach the texture to the
168 // framebuffer.
169 const GLuint kClientTextureId = 4100;
170 const GLuint kServiceTextureId = 4101;
171 EXPECT_CALL(*gl_, GenTextures(_, _))
172 .WillOnce(SetArgPointee<1>(kServiceTextureId))
173 .RetiresOnSaturation();
174 GenHelper<cmds::GenTexturesImmediate>(kClientTextureId);
175 DoBindTexture(GL_TEXTURE_2D, kClientTextureId, kServiceTextureId);
176 DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 5, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE,
177 shared_memory_id_, kSharedMemoryOffset);
178 DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_,
179 kServiceFramebufferId);
180 DoFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
181 kClientTextureId, kServiceTextureId, 0, GL_NO_ERROR);
182
183 // The texture should never be deleted at the GL level.
184 EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(kServiceTextureId)))
185 .Times(0)
186 .RetiresOnSaturation();
187
188 DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0);
189 EXPECT_CALL(*gl_, BindTexture(_, 0)).Times(testing::AnyNumber());
190 GenHelper<cmds::DeleteTexturesImmediate>(kClientTextureId);
191
192 // Force context lost for MakeCurrent().
193 EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
194 // Expect the group to be lost.
195 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
196
197 decoder_->MakeCurrent();
198 EXPECT_TRUE(decoder_->WasContextLost());
199 EXPECT_EQ(error::kMakeCurrentFailed, GetContextLostReason());
200 ClearCurrentDecoderError();
201 }
202
TEST_P(GLES2DecoderLostContextTest,QueryDestroyAfterLostFromMakeCurrent)203 TEST_P(GLES2DecoderLostContextTest, QueryDestroyAfterLostFromMakeCurrent) {
204 InitState init;
205 init.extensions = "GL_EXT_occlusion_query_boolean GL_ARB_sync";
206 init.gl_version = "2.0";
207 init.has_alpha = true;
208 init.request_alpha = true;
209 init.bind_generates_resource = true;
210 InitDecoder(init);
211
212 const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef);
213 GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId);
214
215 cmds::BeginQueryEXT begin_cmd;
216 begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId,
217 shared_memory_id_, kSharedMemoryOffset);
218 EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
219 EXPECT_EQ(GL_NO_ERROR, GetGLError());
220
221 QueryManager* query_manager = decoder_->GetQueryManager();
222 ASSERT_TRUE(query_manager != nullptr);
223 QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
224 ASSERT_TRUE(query != nullptr);
225 EXPECT_FALSE(query->IsPending());
226
227 EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation();
228 EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
229 .WillOnce(Return(kGlSync))
230 .RetiresOnSaturation();
231 #if DCHECK_IS_ON()
232 EXPECT_CALL(*gl_, IsSync(kGlSync))
233 .WillOnce(Return(GL_TRUE))
234 .RetiresOnSaturation();
235 #endif
236
237 cmds::EndQueryEXT end_cmd;
238 end_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, 1);
239 EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
240 EXPECT_EQ(GL_NO_ERROR, GetGLError());
241
242 #if DCHECK_IS_ON()
243 EXPECT_CALL(*gl_, IsSync(kGlSync)).Times(0).RetiresOnSaturation();
244 #endif
245 EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(0).RetiresOnSaturation();
246
247 // Force context lost for MakeCurrent().
248 EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
249 // Expect the group to be lost.
250 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
251
252 decoder_->MakeCurrent();
253 EXPECT_TRUE(decoder_->WasContextLost());
254 EXPECT_EQ(error::kMakeCurrentFailed, GetContextLostReason());
255 ClearCurrentDecoderError();
256 ResetDecoder();
257 }
258
TEST_P(GLES2DecoderLostContextTest,LostFromResetAfterMakeCurrent)259 TEST_P(GLES2DecoderLostContextTest, LostFromResetAfterMakeCurrent) {
260 Init(true); // with robustness
261 InSequence seq;
262 EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
263 EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
264 .WillOnce(Return(GL_GUILTY_CONTEXT_RESET_KHR));
265 // Expect the group to be lost.
266 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
267 decoder_->MakeCurrent();
268 EXPECT_TRUE(decoder_->WasContextLost());
269 EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
270 EXPECT_EQ(error::kGuilty, GetContextLostReason());
271
272 // We didn't process commands, so we need to clear the decoder error,
273 // so that we can shut down cleanly.
274 ClearCurrentDecoderError();
275 }
276
TEST_P(GLES2DecoderLostContextTest,LoseGuiltyFromGLError)277 TEST_P(GLES2DecoderLostContextTest, LoseGuiltyFromGLError) {
278 Init(true);
279 // Always expect other contexts to be signaled as 'kUnknown' since we can't
280 // query their status without making them current.
281 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
282 .Times(1);
283 DoGetErrorWithContextLost(GL_GUILTY_CONTEXT_RESET_KHR);
284 EXPECT_TRUE(decoder_->WasContextLost());
285 EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
286 EXPECT_EQ(error::kGuilty, GetContextLostReason());
287 }
288
TEST_P(GLES2DecoderLostContextTest,LoseInnocentFromGLError)289 TEST_P(GLES2DecoderLostContextTest, LoseInnocentFromGLError) {
290 Init(true);
291 // Always expect other contexts to be signaled as 'kUnknown' since we can't
292 // query their status without making them current.
293 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
294 .Times(1);
295 DoGetErrorWithContextLost(GL_INNOCENT_CONTEXT_RESET_KHR);
296 EXPECT_TRUE(decoder_->WasContextLost());
297 EXPECT_TRUE(decoder_->WasContextLostByRobustnessExtension());
298 EXPECT_EQ(error::kInnocent, GetContextLostReason());
299 }
300
TEST_P(GLES2DecoderLostContextTest,LoseGroupFromRobustness)301 TEST_P(GLES2DecoderLostContextTest, LoseGroupFromRobustness) {
302 // If one context in a group is lost through robustness,
303 // the other ones should also get lost and query the reset status.
304 Init(true);
305 EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown))
306 .Times(1);
307 // There should be no GL calls, since we might not have a current context.
308 EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()).Times(0);
309 LoseContexts(error::kUnknown);
310 EXPECT_TRUE(decoder_->WasContextLost());
311 EXPECT_EQ(error::kUnknown, GetContextLostReason());
312
313 // We didn't process commands, so we need to clear the decoder error,
314 // so that we can shut down cleanly.
315 ClearCurrentDecoderError();
316 }
317
318 INSTANTIATE_TEST_SUITE_P(Service,
319 GLES2DecoderLostContextTest,
320 ::testing::Bool());
321
322 } // namespace gles2
323 } // namespace gpu
324