// Copyright (c) 2012 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_unittest.h" #include #include #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/context_state.h" #include "gpu/command_buffer/service/gl_surface_mock.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/image_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/mocks.h" #include "gpu/command_buffer/service/program_manager.h" #include "gpu/command_buffer/service/test_helper.h" #include "gpu/config/gpu_switches.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_mock.h" #include "ui/gl/gl_surface_stub.h" #include "ui/gl/gpu_timing_fake.h" #if !defined(GL_DEPTH24_STENCIL8) #define GL_DEPTH24_STENCIL8 0x88F0 #endif using ::gfx::MockGLInterface; using ::testing::_; using ::testing::AtLeast; 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::StrEq; using ::testing::StrictMock; namespace gpu { namespace gles2 { using namespace cmds; void GLES2DecoderRGBBackbufferTest::SetUp() { // Test codepath with workaround clear_alpha_in_readpixels because // ReadPixelsEmulator emulates the incorrect driver behavior. base::CommandLine command_line(0, NULL); command_line.AppendSwitchASCII( switches::kGpuDriverBugWorkarounds, base::IntToString(gpu::CLEAR_ALPHA_IN_READPIXELS)); InitState init; init.bind_generates_resource = true; InitDecoderWithCommandLine(init, &command_line); SetupDefaultProgram(); } // Override default setup so nothing gets setup. void GLES2DecoderManualInitTest::SetUp() { } void GLES2DecoderManualInitTest::EnableDisableTest(GLenum cap, bool enable, bool expect_set) { if (expect_set) { SetupExpectationsForEnableDisable(cap, enable); } if (enable) { Enable cmd; cmd.Init(cap); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } else { Disable cmd; cmd.Init(cap); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); } } void GLES3DecoderTest::SetUp() { base::CommandLine command_line(0, NULL); command_line.AppendSwitch(switches::kEnableUnsafeES3APIs); InitState init; init.gl_version = "OpenGL ES 3.0"; init.bind_generates_resource = true; init.context_type = CONTEXT_TYPE_OPENGLES3; InitDecoderWithCommandLine(init, &command_line); } TEST_P(GLES3DecoderTest, Basic) { // Make sure the setup is correct for ES3. EXPECT_TRUE(decoder_->unsafe_es3_apis_enabled()); EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid( GL_TEXTURE_3D)); } TEST_P(GLES2DecoderTest, GetIntegervCached) { struct TestInfo { GLenum pname; GLint expected; }; TestInfo tests[] = { { GL_MAX_TEXTURE_SIZE, TestHelper::kMaxTextureSize, }, { GL_MAX_CUBE_MAP_TEXTURE_SIZE, TestHelper::kMaxCubeMapTextureSize, }, { GL_MAX_RENDERBUFFER_SIZE, TestHelper::kMaxRenderbufferSize, }, }; typedef GetIntegerv::Result Result; for (size_t ii = 0; ii < sizeof(tests) / sizeof(tests[0]); ++ii) { const TestInfo& test = tests[ii]; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetIntegerv(test.pname, _)).Times(0); result->size = 0; GetIntegerv cmd2; cmd2.Init(test.pname, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(test.pname), result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(test.expected, result->GetData()[0]); } } TEST_P(GLES2DecoderWithShaderTest, GetMaxValueInBufferCHROMIUM) { SetupIndexBuffer(); GetMaxValueInBufferCHROMIUM::Result* result = static_cast(shared_memory_address_); *result = 0; GetMaxValueInBufferCHROMIUM cmd; cmd.Init(client_element_buffer_id_, kValidIndexRangeCount, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(7u, *result); EXPECT_EQ(GL_NO_ERROR, GetGLError()); cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(100u, *result); EXPECT_EQ(GL_NO_ERROR, GetGLError()); cmd.Init(kInvalidClientId, kValidIndexRangeCount, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); cmd.Init(client_element_buffer_id_, kOutOfRangeIndexRangeEnd, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kOutOfRangeIndexRangeEnd * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); cmd.Init(client_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kInvalidSharedMemoryId, kSharedMemoryOffset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); cmd.Init(client_element_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, kValidIndexRangeStart * 2, kSharedMemoryId, kInvalidSharedMemoryOffset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, IsBuffer) { EXPECT_FALSE(DoIsBuffer(client_buffer_id_)); DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); EXPECT_TRUE(DoIsBuffer(client_buffer_id_)); DoDeleteBuffer(client_buffer_id_, kServiceBufferId); EXPECT_FALSE(DoIsBuffer(client_buffer_id_)); } TEST_P(GLES2DecoderTest, IsFramebuffer) { EXPECT_FALSE(DoIsFramebuffer(client_framebuffer_id_)); DoBindFramebuffer( GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId); EXPECT_TRUE(DoIsFramebuffer(client_framebuffer_id_)); DoDeleteFramebuffer(client_framebuffer_id_, kServiceFramebufferId, true, GL_FRAMEBUFFER, 0, true, GL_FRAMEBUFFER, 0); EXPECT_FALSE(DoIsFramebuffer(client_framebuffer_id_)); } TEST_P(GLES2DecoderTest, IsProgram) { // IsProgram is true as soon as the program is created. EXPECT_TRUE(DoIsProgram(client_program_id_)); EXPECT_CALL(*gl_, DeleteProgram(kServiceProgramId)) .Times(1) .RetiresOnSaturation(); DoDeleteProgram(client_program_id_, kServiceProgramId); EXPECT_FALSE(DoIsProgram(client_program_id_)); } TEST_P(GLES2DecoderTest, IsRenderbuffer) { EXPECT_FALSE(DoIsRenderbuffer(client_renderbuffer_id_)); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_TRUE(DoIsRenderbuffer(client_renderbuffer_id_)); DoDeleteRenderbuffer(client_renderbuffer_id_, kServiceRenderbufferId); EXPECT_FALSE(DoIsRenderbuffer(client_renderbuffer_id_)); } TEST_P(GLES2DecoderTest, IsShader) { // IsShader is true as soon as the program is created. EXPECT_TRUE(DoIsShader(client_shader_id_)); DoDeleteShader(client_shader_id_, kServiceShaderId); EXPECT_FALSE(DoIsShader(client_shader_id_)); } TEST_P(GLES2DecoderTest, IsTexture) { EXPECT_FALSE(DoIsTexture(client_texture_id_)); DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); EXPECT_TRUE(DoIsTexture(client_texture_id_)); DoDeleteTexture(client_texture_id_, kServiceTextureId); EXPECT_FALSE(DoIsTexture(client_texture_id_)); } TEST_P(GLES3DecoderTest, GetInternalformativValidArgsSamples) { const GLint kNumSampleCounts = 8; typedef cmds::GetInternalformativ::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1, _)) .WillOnce(SetArgPointee<4>(kNumSampleCounts)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, kNumSampleCounts, result->GetData())) .Times(1) .RetiresOnSaturation(); result->size = 0; cmds::GetInternalformativ cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, shared_memory_id_, shared_memory_offset_); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(kNumSampleCounts, result->GetNumResults()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); } TEST_P(GLES3DecoderTest, GetInternalformativValidArgsNumSampleCounts) { const GLint kNumSampleCounts = 8; typedef cmds::GetInternalformativ::Result Result; Result* result = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1, _)) .WillOnce(SetArgPointee<4>(kNumSampleCounts)) .RetiresOnSaturation(); result->size = 0; cmds::GetInternalformativ cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, shared_memory_id_, shared_memory_offset_); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(1, result->GetNumResults()); EXPECT_EQ(kNumSampleCounts, result->GetData()[0]); EXPECT_EQ(GL_NO_ERROR, GetGLError()); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); } TEST_P(GLES3DecoderTest, ClientWaitSyncValid) { typedef cmds::ClientWaitSync::Result Result; Result* result = static_cast(shared_memory_address_); cmds::ClientWaitSync cmd; cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, ClientWaitSync(reinterpret_cast(kServiceSyncId), GL_SYNC_FLUSH_COMMANDS_BIT, 0)) .WillOnce(Return(GL_CONDITION_SATISFIED)) .RetiresOnSaturation(); *result = GL_WAIT_FAILED; decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(static_cast(GL_CONDITION_SATISFIED), *result); EXPECT_EQ(GL_NO_ERROR, GetGLError()); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, ClientWaitSyncNonZeroTimeoutValid) { typedef cmds::ClientWaitSync::Result Result; Result* result = static_cast(shared_memory_address_); cmds::ClientWaitSync cmd; const GLuint64 kTimeout = 0xABCDEF0123456789; cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout, shared_memory_id_, shared_memory_offset_); EXPECT_CALL(*gl_, ClientWaitSync(reinterpret_cast(kServiceSyncId), GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout)) .WillOnce(Return(GL_CONDITION_SATISFIED)) .RetiresOnSaturation(); *result = GL_WAIT_FAILED; decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(static_cast(GL_CONDITION_SATISFIED), *result); EXPECT_EQ(GL_NO_ERROR, GetGLError()); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, ClientWaitSyncInvalidSyncFails) { typedef cmds::ClientWaitSync::Result Result; Result* result = static_cast(shared_memory_address_); cmds::ClientWaitSync cmd; decoder_->set_unsafe_es3_apis_enabled(true); cmd.Init(kInvalidClientId, GL_SYNC_FLUSH_COMMANDS_BIT, 0, shared_memory_id_, shared_memory_offset_); *result = GL_WAIT_FAILED; EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(static_cast(GL_WAIT_FAILED), *result); EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); } TEST_P(GLES2DecoderTest, ClientWaitSyncResultNotInitFails) { typedef cmds::ClientWaitSync::Result Result; Result* result = static_cast(shared_memory_address_); cmds::ClientWaitSync cmd; decoder_->set_unsafe_es3_apis_enabled(true); cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0, shared_memory_id_, shared_memory_offset_); *result = 1; // Any value other than GL_WAIT_FAILED EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, ClientWaitSyncBadSharedMemoryFails) { typedef cmds::ClientWaitSync::Result Result; Result* result = static_cast(shared_memory_address_); cmds::ClientWaitSync cmd; decoder_->set_unsafe_es3_apis_enabled(true); *result = GL_WAIT_FAILED; cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0, kInvalidSharedMemoryId, shared_memory_offset_); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); *result = GL_WAIT_FAILED; cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, 0, shared_memory_id_, kInvalidSharedMemoryOffset); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, WaitSyncValidArgs) { const GLuint64 kTimeout = GL_TIMEOUT_IGNORED; EXPECT_CALL(*gl_, WaitSync(reinterpret_cast(kServiceSyncId), 0, kTimeout)) .Times(1) .RetiresOnSaturation(); cmds::WaitSync cmd; cmd.Init(client_sync_id_, 0, kTimeout); decoder_->set_unsafe_es3_apis_enabled(true); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); decoder_->set_unsafe_es3_apis_enabled(false); EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderManualInitTest, BindGeneratesResourceFalse) { InitState init; InitDecoder(init); BindTexture cmd1; cmd1.Init(GL_TEXTURE_2D, kInvalidClientId); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); BindBuffer cmd2; cmd2.Init(GL_ARRAY_BUFFER, kInvalidClientId); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); BindFramebuffer cmd3; cmd3.Init(GL_FRAMEBUFFER, kInvalidClientId); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd3)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); BindRenderbuffer cmd4; cmd4.Init(GL_RENDERBUFFER, kInvalidClientId); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd4)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, EnableFeatureCHROMIUMBadBucket) { const uint32_t kBadBucketId = 123; EnableFeatureCHROMIUM cmd; cmd.Init(kBadBucketId, shared_memory_id_, shared_memory_offset_); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, RequestExtensionCHROMIUMBadBucket) { const uint32_t kBadBucketId = 123; RequestExtensionCHROMIUM cmd; cmd.Init(kBadBucketId); EXPECT_NE(error::kNoError, ExecuteCmd(cmd)); } TEST_P(GLES2DecoderTest, BeginQueryEXTDisabled) { // Test something fails if off. } TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXT) { InitState init; init.extensions = "GL_EXT_occlusion_query_boolean"; init.gl_version = "opengl es 2.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); // Test end fails if no begin. EndQueryEXT end_cmd; end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); BeginQueryEXT begin_cmd; // Test id = 0 fails. begin_cmd.Init( GL_ANY_SAMPLES_PASSED_EXT, 0, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); GenHelper(kNewClientId); // Test valid parameters work. EXPECT_CALL(*gl_, GenQueries(1, _)) .WillOnce(SetArgPointee<1>(kNewServiceId)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BeginQuery(GL_ANY_SAMPLES_PASSED_EXT, kNewServiceId)) .Times(1) .RetiresOnSaturation(); // Query object should not be created untill BeginQueriesEXT. QueryManager* query_manager = decoder_->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); QueryManager::Query* query = query_manager->GetQuery(kNewClientId); EXPECT_TRUE(query == NULL); // BeginQueryEXT should fail if id is not generated from GenQueriesEXT. begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kInvalidClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); // After BeginQueriesEXT id name should have query object associated with it. query = query_manager->GetQuery(kNewClientId); ASSERT_TRUE(query != NULL); EXPECT_FALSE(query->IsPending()); // Test trying begin again fails EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); // Test end fails with different target end_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); // Test end succeeds EXPECT_CALL(*gl_, EndQuery(GL_ANY_SAMPLES_PASSED_EXT)) .Times(1) .RetiresOnSaturation(); end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_TRUE(query->IsPending()); EXPECT_CALL(*gl_, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); } struct QueryType { GLenum type; bool is_counter; }; const QueryType kQueryTypes[] = { {GL_COMMANDS_ISSUED_CHROMIUM, false}, {GL_LATENCY_QUERY_CHROMIUM, false}, {GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, false}, {GL_GET_ERROR_QUERY_CHROMIUM, false}, {GL_COMMANDS_COMPLETED_CHROMIUM, false}, {GL_ANY_SAMPLES_PASSED_EXT, false}, {GL_TIME_ELAPSED, false}, {GL_TIMESTAMP, true}, }; const GLsync kGlSync = reinterpret_cast(0xdeadbeef); static void ExecuteGenerateQueryCmd(GLES2DecoderTestBase* test, ::gfx::MockGLInterface* gl, GLenum target, GLuint client_id, GLuint service_id) { test->GenHelper(client_id); if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL(*gl, GenQueries(1, _)) .WillOnce(SetArgPointee<1>(service_id)) .RetiresOnSaturation(); } } static error::Error ExecuteBeginQueryCmd(GLES2DecoderTestBase* test, ::gfx::MockGLInterface* gl, ::gfx::GPUTimingFake* timing_queries, GLenum target, GLuint client_id, GLuint service_id, int32_t shm_id, uint32_t shm_offset) { if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL(*gl, BeginQuery(target, service_id)) .Times(1) .RetiresOnSaturation(); } else if (GL_TIME_ELAPSED == target) { timing_queries->ExpectGPUTimerQuery(*gl, true); } BeginQueryEXT begin_cmd; begin_cmd.Init(target, client_id, shm_id, shm_offset); return test->ExecuteCmd(begin_cmd); } static error::Error ExecuteEndQueryCmd(GLES2DecoderTestBase* test, ::gfx::MockGLInterface* gl, GLenum target, uint32_t submit_count) { if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL(*gl, EndQuery(target)) .Times(1) .RetiresOnSaturation(); } else if (GL_GET_ERROR_QUERY_CHROMIUM == target) { EXPECT_CALL(*gl, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); } else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) { 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)) .WillRepeatedly(Return(GL_TRUE)); #endif } EndQueryEXT end_cmd; end_cmd.Init(target, submit_count); return test->ExecuteCmd(end_cmd); } static error::Error ExecuteQueryCounterCmd(GLES2DecoderTestBase* test, ::gfx::MockGLInterface* gl, ::gfx::GPUTimingFake* timing_queries, GLenum target, GLuint client_id, GLuint service_id, int32_t shm_id, uint32_t shm_offset, uint32_t submit_count) { if (GL_TIMESTAMP == target) { timing_queries->ExpectGPUTimeStampQuery(*gl, false); } QueryCounterEXT query_counter_cmd; query_counter_cmd.Init(client_id, target, shm_id, shm_offset, submit_count); return test->ExecuteCmd(query_counter_cmd); } static bool ProcessQuery(GLES2DecoderTestBase* test, ::gfx::MockGLInterface* gl, GLenum target, GLuint service_id) { if (GL_ANY_SAMPLES_PASSED_EXT == target) { EXPECT_CALL( *gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) .WillOnce(SetArgPointee<2>(1)) .RetiresOnSaturation(); EXPECT_CALL(*gl, GetQueryObjectuiv(service_id, GL_QUERY_RESULT_EXT, _)) .WillOnce(SetArgPointee<2>(1)) .RetiresOnSaturation(); } else if (GL_COMMANDS_COMPLETED_CHROMIUM == target) { EXPECT_CALL(*gl, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_ALREADY_SIGNALED)) .RetiresOnSaturation(); EXPECT_CALL(*gl, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation(); } QueryManager* query_manager = test->GetDecoder()->GetQueryManager(); EXPECT_TRUE(nullptr != query_manager); if (!query_manager) return false; return query_manager->ProcessPendingQueries(false); } static void CheckBeginEndQueryBadMemoryFails(GLES2DecoderTestBase* test, GLuint client_id, GLuint service_id, const QueryType& query_type, int32_t shm_id, uint32_t shm_offset) { // We need to reset the decoder on each iteration, because we lose the // context every time. GLES2DecoderTestBase::InitState init; init.extensions = "GL_EXT_occlusion_query_boolean" " GL_ARB_sync" " GL_ARB_timer_query"; init.gl_version = "opengl es 3.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; test->InitDecoder(init); ::testing::StrictMock< ::gfx::MockGLInterface>* gl = test->GetGLMock(); ::gfx::GPUTimingFake gpu_timing_queries; ExecuteGenerateQueryCmd(test, gl, query_type.type, client_id, service_id); // Test bad shared memory fails error::Error error1 = error::kNoError; error::Error error2 = error::kNoError; if (query_type.is_counter) { error1 = ExecuteQueryCounterCmd(test, gl, &gpu_timing_queries, query_type.type, client_id, service_id, shm_id, shm_offset, 1); } else { error1 = ExecuteBeginQueryCmd(test, gl, &gpu_timing_queries, query_type.type, client_id, service_id, shm_id, shm_offset); error2 = ExecuteEndQueryCmd(test, gl, query_type.type, 1); } bool process_success = ProcessQuery(test, gl, query_type.type, service_id); EXPECT_TRUE(error1 != error::kNoError || error2 != error::kNoError || !process_success); if (GL_ANY_SAMPLES_PASSED_EXT == query_type.type) EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); test->ResetDecoder(); } TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryIdFails) { for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kNewServiceId, kQueryTypes[i], kInvalidSharedMemoryId, kSharedMemoryOffset); } } TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryOffsetFails) { for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { // Out-of-bounds. CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kNewServiceId, kQueryTypes[i], kSharedMemoryId, kInvalidSharedMemoryOffset); // Overflow. CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kNewServiceId, kQueryTypes[i], kSharedMemoryId, 0xfffffffcu); } } TEST_P(GLES2DecoderManualInitTest, QueryReuseTest) { for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { const QueryType& query_type = kQueryTypes[i]; GLES2DecoderTestBase::InitState init; init.extensions = "GL_EXT_occlusion_query_boolean" " GL_ARB_sync" " GL_ARB_timer_query"; init.gl_version = "opengl es 3.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); ::testing::StrictMock< ::gfx::MockGLInterface>* gl = GetGLMock(); ::gfx::GPUTimingFake gpu_timing_queries; ExecuteGenerateQueryCmd(this, gl, query_type.type, kNewClientId, kNewServiceId); // Query once. if (query_type.is_counter) { EXPECT_EQ( error::kNoError, ExecuteQueryCounterCmd(this, gl, &gpu_timing_queries, query_type.type, kNewClientId, kNewServiceId, kSharedMemoryId, kSharedMemoryOffset, 1)); } else { EXPECT_EQ(error::kNoError, ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type, kNewClientId, kNewServiceId, kSharedMemoryId, kSharedMemoryOffset)); EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, query_type.type, 1)); } EXPECT_TRUE(ProcessQuery(this, gl, query_type.type, kNewServiceId)); // Reuse query. if (query_type.is_counter) { EXPECT_EQ( error::kNoError, ExecuteQueryCounterCmd(this, gl, &gpu_timing_queries, query_type.type, kNewClientId, kNewServiceId, kSharedMemoryId, kSharedMemoryOffset, 2)); } else { EXPECT_EQ(error::kNoError, ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type, kNewClientId, kNewServiceId, kSharedMemoryId, kSharedMemoryOffset)); EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, query_type.type, 2)); } EXPECT_TRUE(ProcessQuery(this, gl, query_type.type, kNewServiceId)); if (GL_ANY_SAMPLES_PASSED_EXT == query_type.type) EXPECT_CALL(*gl, DeleteQueries(1, _)).Times(1).RetiresOnSaturation(); ResetDecoder(); } } TEST_P(GLES2DecoderTest, BeginEndQueryEXTCommandsIssuedCHROMIUM) { BeginQueryEXT begin_cmd; GenHelper(kNewClientId); // Test valid parameters work. begin_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); QueryManager* query_manager = decoder_->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); QueryManager::Query* query = query_manager->GetQuery(kNewClientId); ASSERT_TRUE(query != NULL); EXPECT_FALSE(query->IsPending()); // Test end succeeds EndQueryEXT end_cmd; end_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_FALSE(query->IsPending()); } TEST_P(GLES2DecoderTest, BeginEndQueryEXTGetErrorQueryCHROMIUM) { BeginQueryEXT begin_cmd; GenHelper(kNewClientId); // Test valid parameters work. begin_cmd.Init(GL_GET_ERROR_QUERY_CHROMIUM, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); QueryManager* query_manager = decoder_->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); QueryManager::Query* query = query_manager->GetQuery(kNewClientId); ASSERT_TRUE(query != NULL); EXPECT_FALSE(query->IsPending()); // Test end succeeds QuerySync* sync = static_cast(shared_memory_address_); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_INVALID_VALUE)) .RetiresOnSaturation(); EndQueryEXT end_cmd; end_cmd.Init(GL_GET_ERROR_QUERY_CHROMIUM, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_FALSE(query->IsPending()); EXPECT_EQ(static_cast(GL_INVALID_VALUE), static_cast(sync->result)); } TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTCommandsCompletedCHROMIUM) { InitState init; init.extensions = "GL_EXT_occlusion_query_boolean GL_ARB_sync"; init.gl_version = "opengl es 2.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); GenHelper(kNewClientId); BeginQueryEXT begin_cmd; begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); QueryManager* query_manager = decoder_->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); QueryManager::Query* query = query_manager->GetQuery(kNewClientId); ASSERT_TRUE(query != NULL); 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()); EXPECT_TRUE(query->IsPending()); #if DCHECK_IS_ON() EXPECT_CALL(*gl_, IsSync(kGlSync)) .WillOnce(Return(GL_TRUE)) .RetiresOnSaturation(); #endif EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_TIMEOUT_EXPIRED)) .RetiresOnSaturation(); bool process_success = query_manager->ProcessPendingQueries(false); EXPECT_TRUE(process_success); EXPECT_TRUE(query->IsPending()); #if DCHECK_IS_ON() EXPECT_CALL(*gl_, IsSync(kGlSync)) .WillOnce(Return(GL_TRUE)) .RetiresOnSaturation(); #endif EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _)) .WillOnce(Return(GL_ALREADY_SIGNALED)) .RetiresOnSaturation(); process_success = query_manager->ProcessPendingQueries(false); EXPECT_TRUE(process_success); EXPECT_FALSE(query->IsPending()); #if DCHECK_IS_ON() EXPECT_CALL(*gl_, IsSync(kGlSync)) .WillOnce(Return(GL_TRUE)) .RetiresOnSaturation(); #endif EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation(); ResetDecoder(); } TEST_P(GLES2DecoderManualInitTest, BeginInvalidTargetQueryFails) { InitState init; init.extensions = ""; init.gl_version = "opengl es 2.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); GenHelper(kNewClientId); BeginQueryEXT begin_cmd; begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); begin_cmd.Init(GL_ANY_SAMPLES_PASSED, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); begin_cmd.Init(GL_TIME_ELAPSED, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); begin_cmd.Init(0xdeadbeef, kNewClientId, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, QueryCounterEXTTimeStamp) { InitState init; init.extensions = "GL_ARB_timer_query"; init.gl_version = "opengl es 3.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); GenHelper(kNewClientId); EXPECT_CALL(*gl_, GenQueries(1, _)) .WillOnce(SetArgPointee<1>(kNewServiceId)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, _)) .WillOnce(SetArgPointee<2>(64)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, QueryCounter(kNewServiceId, GL_TIMESTAMP)) .Times(1) .RetiresOnSaturation(); QueryCounterEXT query_counter_cmd; query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, kSharedMemoryId, kSharedMemoryOffset, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); QueryManager* query_manager = decoder_->GetQueryManager(); ASSERT_TRUE(query_manager != NULL); QueryManager::Query* query = query_manager->GetQuery(kNewClientId); ASSERT_TRUE(query != NULL); EXPECT_TRUE(query->IsPending()); } TEST_P(GLES2DecoderManualInitTest, InvalidTargetQueryCounterFails) { InitState init; init.extensions = ""; init.gl_version = "opengl es 2.0"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); GenHelper(kNewClientId); QueryCounterEXT query_counter_cmd; query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, kSharedMemoryId, kSharedMemoryOffset, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); query_counter_cmd.Init(kNewClientId, 0xdeadbeef, kSharedMemoryId, kSharedMemoryOffset, 1); EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } TEST_P(GLES2DecoderTest, IsEnabledReturnsCachedValue) { // NOTE: There are no expectations because no GL functions should be // called for DEPTH_TEST or STENCIL_TEST static const GLenum kStates[] = { GL_DEPTH_TEST, GL_STENCIL_TEST, }; for (size_t ii = 0; ii < arraysize(kStates); ++ii) { Enable enable_cmd; GLenum state = kStates[ii]; enable_cmd.Init(state); EXPECT_EQ(error::kNoError, ExecuteCmd(enable_cmd)); IsEnabled::Result* result = static_cast(shared_memory_address_); IsEnabled is_enabled_cmd; is_enabled_cmd.Init(state, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(is_enabled_cmd)); EXPECT_NE(0u, *result); Disable disable_cmd; disable_cmd.Init(state); EXPECT_EQ(error::kNoError, ExecuteCmd(disable_cmd)); EXPECT_EQ(error::kNoError, ExecuteCmd(is_enabled_cmd)); EXPECT_EQ(0u, *result); } } namespace { class SizeOnlyMemoryTracker : public MemoryTracker { public: SizeOnlyMemoryTracker() { // Account for the 7 default textures. 1 for TEXTURE_2D and 6 faces for // TEXTURE_CUBE_MAP. Each is 1x1, with 4 bytes per channel. pool_info_.initial_size = 28; pool_info_.size = 0; } // Ensure a certain amount of GPU memory is free. Returns true on success. MOCK_METHOD1(EnsureGPUMemoryAvailable, bool(size_t size_needed)); virtual void TrackMemoryAllocatedChange(size_t old_size, size_t new_size) { pool_info_.size += new_size - old_size; } size_t GetPoolSize() { return pool_info_.size - pool_info_.initial_size; } uint64_t ClientTracingId() const override { return 0; } int ClientId() const override { return 0; } uint64_t ShareGroupTracingGUID() const override { return 0; } private: virtual ~SizeOnlyMemoryTracker() {} struct PoolInfo { PoolInfo() : initial_size(0), size(0) {} size_t initial_size; size_t size; }; PoolInfo pool_info_; }; } // anonymous namespace. TEST_P(GLES2DecoderManualInitTest, MemoryTrackerInitialSize) { scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.bind_generates_resource = true; InitDecoder(init); // Expect that initial size - size is 0. EXPECT_EQ(0u, memory_tracker->GetPoolSize()); EXPECT_EQ(0u, memory_tracker->GetPoolSize()); } TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexImage2D) { scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.bind_generates_resource = true; InitDecoder(init); DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(true)) .RetiresOnSaturation(); DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(64)) .WillOnce(Return(true)) .RetiresOnSaturation(); DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(64u, memory_tracker->GetPoolSize()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); // Check we get out of memory and no call to glTexImage2D if Ensure fails. EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(64)) .WillOnce(Return(false)) .RetiresOnSaturation(); TexImage2D cmd; cmd.Init(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); EXPECT_EQ(64u, memory_tracker->GetPoolSize()); } TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexStorage2DEXT) { scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.bind_generates_resource = true; InitDecoder(init); DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); // Check we get out of memory and no call to glTexStorage2DEXT // if Ensure fails. EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(false)) .RetiresOnSaturation(); TexStorage2DEXT cmd; cmd.Init(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 4); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(0u, memory_tracker->GetPoolSize()); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); } TEST_P(GLES2DecoderManualInitTest, MemoryTrackerCopyTexImage2D) { GLenum target = GL_TEXTURE_2D; GLint level = 0; GLenum internal_format = GL_RGBA; GLsizei width = 4; GLsizei height = 8; GLint border = 0; scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(true)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, CopyTexImage2D( target, level, internal_format, 0, 0, width, height, border)) .Times(1) .RetiresOnSaturation(); CopyTexImage2D cmd; cmd.Init(target, level, internal_format, 0, 0, width, height); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); EXPECT_EQ(GL_NO_ERROR, GetGLError()); // Check we get out of memory and no call to glCopyTexImage2D if Ensure fails. EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(false)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); } TEST_P(GLES2DecoderManualInitTest, MemoryTrackerRenderbufferStorage) { scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.bind_generates_resource = true; InitDecoder(init); DoBindRenderbuffer( GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId); EnsureRenderbufferBound(false); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(true)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 8, 4)) .Times(1) .RetiresOnSaturation(); RenderbufferStorage cmd; cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 8, 4); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); // Check we get out of memory and no call to glRenderbufferStorage if Ensure // fails. EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(false)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); } TEST_P(GLES2DecoderManualInitTest, MemoryTrackerBufferData) { scoped_refptr memory_tracker = new SizeOnlyMemoryTracker(); set_memory_tracker(memory_tracker.get()); InitState init; init.bind_generates_resource = true; InitDecoder(init); EXPECT_EQ(0u, memory_tracker->GetPoolSize()); DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(true)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BufferData(GL_ARRAY_BUFFER, 128, _, GL_STREAM_DRAW)) .Times(1) .RetiresOnSaturation(); BufferData cmd; cmd.Init(GL_ARRAY_BUFFER, 128, 0, 0, GL_STREAM_DRAW); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); // Check we get out of memory and no call to glBufferData if Ensure // fails. EXPECT_CALL(*memory_tracker.get(), EnsureGPUMemoryAvailable(128)) .WillOnce(Return(false)) .RetiresOnSaturation(); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError()); EXPECT_EQ(128u, memory_tracker->GetPoolSize()); } TEST_P(GLES2DecoderManualInitTest, ImmutableCopyTexImage2D) { const GLenum kTarget = GL_TEXTURE_2D; const GLint kLevel = 0; const GLenum kInternalFormat = GL_RGBA; const GLenum kSizedInternalFormat = GL_RGBA8; const GLsizei kWidth = 4; const GLsizei kHeight = 8; const GLint kBorder = 0; InitState init; init.extensions = "GL_EXT_texture_storage"; init.has_alpha = true; init.request_alpha = true; init.bind_generates_resource = true; InitDecoder(init); DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); // CopyTexImage2D will call arbitrary amount of GetErrors. EXPECT_CALL(*gl_, GetError()) .Times(AtLeast(1)); EXPECT_CALL(*gl_, CopyTexImage2D( kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight, kBorder)) .Times(1); EXPECT_CALL(*gl_, TexStorage2DEXT( kTarget, kLevel, kSizedInternalFormat, kWidth, kHeight)) .Times(1); CopyTexImage2D copy_cmd; copy_cmd.Init(kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight); EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); TexStorage2DEXT storage_cmd; storage_cmd.Init(kTarget, kLevel, kSizedInternalFormat, kWidth, kHeight); EXPECT_EQ(error::kNoError, ExecuteCmd(storage_cmd)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); // This should not invoke CopyTexImage2D. copy_cmd.Init(kTarget, kLevel, kInternalFormat, 0, 0, kWidth, kHeight); EXPECT_EQ(error::kNoError, ExecuteCmd(copy_cmd)); EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); } TEST_P(GLES2DecoderTest, LoseContextCHROMIUMGuilty) { EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kInnocent)) .Times(1); cmds::LoseContextCHROMIUM cmd; 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_, MarkContextLost(_)) .Times(0); cmds::LoseContextCHROMIUM cmd; cmd.Init(GL_NONE, GL_GUILTY_CONTEXT_RESET_ARB); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } TEST_P(GLES2DecoderTest, LoseContextCHROMIUMInvalidArgs1_0) { EXPECT_CALL(*mock_decoder_, MarkContextLost(_)) .Times(0); cmds::LoseContextCHROMIUM cmd; cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_NONE); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); } class GLES2DecoderDoCommandsTest : public GLES2DecoderTest { public: GLES2DecoderDoCommandsTest() { for (int i = 0; i < 3; i++) { cmds_[i].Init(GL_BLEND); } entries_per_cmd_ = ComputeNumEntries(cmds_[0].ComputeSize()); } void SetExpectationsForNCommands(int num_commands) { for (int i = 0; i < num_commands; i++) SetupExpectationsForEnableDisable(GL_BLEND, true); } protected: Enable cmds_[3]; int entries_per_cmd_; }; // Test that processing with 0 entries does nothing. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOneOfZero) { int num_processed = -1; SetExpectationsForNCommands(0); EXPECT_EQ( error::kNoError, decoder_->DoCommands(1, &cmds_, entries_per_cmd_ * 0, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(0, num_processed); } // Test processing at granularity of single commands. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOneOfOne) { int num_processed = -1; SetExpectationsForNCommands(1); EXPECT_EQ( error::kNoError, decoder_->DoCommands(1, &cmds_, entries_per_cmd_ * 1, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_, num_processed); } // Test processing at granularity of multiple commands. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsThreeOfThree) { int num_processed = -1; SetExpectationsForNCommands(3); EXPECT_EQ( error::kNoError, decoder_->DoCommands(3, &cmds_, entries_per_cmd_ * 3, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_ * 3, num_processed); } // Test processing a request smaller than available entries. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsTwoOfThree) { int num_processed = -1; SetExpectationsForNCommands(2); EXPECT_EQ( error::kNoError, decoder_->DoCommands(2, &cmds_, entries_per_cmd_ * 3, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_ * 2, num_processed); } // Test that processing stops on a command with size 0. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsZeroCmdSize) { cmds_[1].header.size = 0; int num_processed = -1; SetExpectationsForNCommands(1); EXPECT_EQ( error::kInvalidSize, decoder_->DoCommands(2, &cmds_, entries_per_cmd_ * 2, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_, num_processed); } // Test that processing stops on a command with size greater than available. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsOutOfBounds) { int num_processed = -1; SetExpectationsForNCommands(1); EXPECT_EQ(error::kOutOfBounds, decoder_->DoCommands( 2, &cmds_, entries_per_cmd_ * 2 - 1, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_, num_processed); } // Test that commands with bad argument size are skipped without processing. TEST_P(GLES2DecoderDoCommandsTest, DoCommandsBadArgSize) { cmds_[1].header.size += 1; int num_processed = -1; SetExpectationsForNCommands(1); EXPECT_EQ(error::kInvalidArguments, decoder_->DoCommands( 2, &cmds_, entries_per_cmd_ * 2 + 1, &num_processed)); EXPECT_EQ(GL_NO_ERROR, GetGLError()); EXPECT_EQ(entries_per_cmd_ + cmds_[1].header.size, num_processed); } void GLES3DecoderWithESSL3ShaderTest::SetUp() { base::CommandLine command_line(0, nullptr); command_line.AppendSwitch(switches::kEnableUnsafeES3APIs); InitState init; init.gl_version = "OpenGL ES 3.0"; init.bind_generates_resource = true; init.context_type = CONTEXT_TYPE_OPENGLES3; InitDecoderWithCommandLine(init, &command_line); SetupDefaultProgram(); } INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderWithShaderTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderManualInitTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderRGBBackbufferTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderDoCommandsTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderWithESSL3ShaderTest, ::testing::Bool()); } // namespace gles2 } // namespace gpu