| // 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 <stddef.h> |
| #include <stdint.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/context_state.h" |
| #include "gpu/command_buffer/service/gl_stream_texture_image_stub.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/command_buffer/service/validating_abstract_texture_impl.h" |
| #include "gpu/config/gpu_switches.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gl/gl_image_stub.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" |
| #include "ui/gl/scoped_make_current.h" |
| |
| #if !defined(GL_DEPTH24_STENCIL8) |
| #define GL_DEPTH24_STENCIL8 0x88F0 |
| #endif |
| |
| using ::gl::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() { |
| InitState init; |
| init.bind_generates_resource = true; |
| InitDecoder(init); |
| SetupDefaultProgram(); |
| } |
| |
| 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()); |
| } |
| } |
| |
| |
| TEST_P(GLES3DecoderTest, Basic) { |
| // Make sure the setup is correct for ES3. |
| EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context()); |
| EXPECT_FALSE(feature_info()->IsWebGLContext()); |
| EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid( |
| GL_TEXTURE_3D)); |
| } |
| |
| TEST_P(WebGL2DecoderTest, Basic) { |
| // Make sure the setup is correct for WebGL2. |
| EXPECT_TRUE(feature_info()->IsWebGL2OrES3Context()); |
| EXPECT_TRUE(feature_info()->IsWebGLContext()); |
| EXPECT_TRUE(feature_info()->validators()->texture_bind_target.IsValid( |
| GL_TEXTURE_3D)); |
| } |
| |
| TEST_P(GLES2DecoderTest, InvalidCommand) { |
| cmd::Noop cmd; |
| cmd.header.Init(gles2::kNumCommands, 1); |
| EXPECT_EQ(error::kUnknownCommand, ExecuteImmediateCmd(cmd, 0)); |
| } |
| |
| 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<Result*>(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<GetMaxValueInBufferCHROMIUM::Result*>(shared_memory_address_); |
| *result = 0; |
| |
| GetMaxValueInBufferCHROMIUM cmd; |
| cmd.Init(client_element_buffer_id_, kValidIndexRangeCount, GL_UNSIGNED_SHORT, |
| kValidIndexRangeStart * 2, shared_memory_id_, 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, shared_memory_id_, |
| 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, shared_memory_id_, 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, shared_memory_id_, |
| 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, shared_memory_id_, |
| 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, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| cmd.Init(client_buffer_id_, kValidIndexRangeCount + 1, GL_UNSIGNED_SHORT, |
| kValidIndexRangeStart * 2, shared_memory_id_, 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, shared_memory_id_, |
| 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_)); |
| EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| 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(GLES2DecoderTest, TestImageBindingForDecoderManagement) { |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_EXTERNAL_OES; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| scoped_refptr<gl::GLImage> image(new gl::GLImageStub); |
| abstract_texture->BindImage(image.get(), GetParam()); |
| ValidatingAbstractTextureImpl* validating_texture = |
| static_cast<ValidatingAbstractTextureImpl*>(abstract_texture.get()); |
| TextureRef* texture_ref = validating_texture->GetTextureRefForTesting(); |
| Texture::ImageState state; |
| EXPECT_EQ(texture_ref->texture()->GetLevelImage(target, 0, &state), |
| image.get()); |
| EXPECT_EQ(state, GetParam() ? Texture::ImageState::BOUND |
| : Texture::ImageState::UNBOUND); |
| |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| abstract_texture.reset(); |
| } |
| |
| TEST_P(GLES2DecoderTest, CreateAbstractTexture) { |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_EXTERNAL_OES; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| EXPECT_EQ(abstract_texture->GetTextureBase()->target(), target); |
| EXPECT_EQ(abstract_texture->service_id(), service_id); |
| Texture* texture = Texture::CheckedCast(abstract_texture->GetTextureBase()); |
| EXPECT_EQ(texture->SafeToRenderFrom(), false); |
| |
| // Set some parameters, and verify that we set them. |
| // These three are for ScopedTextureBinder. |
| // TODO(liberato): Is there a way to make this less brittle? |
| EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, BindTexture(target, _)).Times(1).RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, BindTexture(target, abstract_texture->service_id())) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| // This one we actually care about. |
| EXPECT_CALL(*gl_, TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); |
| abstract_texture->SetParameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| EXPECT_EQ(texture->min_filter(), static_cast<GLenum>(GL_LINEAR)); |
| |
| // Attach an image and see if it works. |
| scoped_refptr<gl::GLImage> image(new gl::GLImageStub); |
| abstract_texture->BindImage(image.get(), true); |
| EXPECT_EQ(abstract_texture->GetImage(), image.get()); |
| // Binding an image should make the texture renderable. |
| EXPECT_EQ(texture->SafeToRenderFrom(), true); |
| EXPECT_EQ(texture->GetLevelImage(target, 0), image.get()); |
| |
| // Unbinding should make it not renderable. |
| abstract_texture->BindImage(nullptr, false); |
| EXPECT_EQ(texture->SafeToRenderFrom(), false); |
| EXPECT_EQ(abstract_texture->GetImage(), nullptr); |
| |
| // Attach a stream image, and verify that the image changes and the service_id |
| // matches the one we provide. |
| scoped_refptr<gpu::gles2::GLStreamTextureImage> stream_image( |
| new gpu::gles2::GLStreamTextureImageStub); |
| const GLuint surface_texture_service_id = service_id + 1; |
| abstract_texture->BindStreamTextureImage(stream_image.get(), |
| surface_texture_service_id); |
| EXPECT_EQ(texture->SafeToRenderFrom(), true); |
| EXPECT_EQ(texture->GetLevelStreamTextureImage(target, 0), stream_image.get()); |
| EXPECT_EQ(abstract_texture->service_id(), surface_texture_service_id); |
| |
| // Deleting |abstract_texture| should delete the platform texture as well, |
| // since we haven't make a copy of the TextureRef. Also make sure that the |
| // cleanup CB is called. |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| bool cleanup_flag = false; |
| abstract_texture->SetCleanupCallback(base::BindOnce( |
| [](bool* flag, AbstractTexture*) { *flag = true; }, &cleanup_flag)); |
| abstract_texture.reset(); |
| EXPECT_TRUE(cleanup_flag); |
| } |
| |
| TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedWithDecoder) { |
| // Deleting the decoder should delete the AbstractTexture's TextureRef. |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_EXTERNAL_OES; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| bool cleanup_flag = false; |
| abstract_texture->SetCleanupCallback(base::BindOnce( |
| [](bool* flag, AbstractTexture*) { *flag = true; }, &cleanup_flag)); |
| |
| // There is only one TextureRef, so it should delete the platform texture. It |
| // should also call the cleanup cb. |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| ResetDecoder(); |
| // The texture should no longer have a TextureRef. |
| EXPECT_EQ(abstract_texture->GetTextureBase(), nullptr); |
| EXPECT_TRUE(cleanup_flag); |
| } |
| |
| TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedWhenMadeCurrent) { |
| // When an AbstractTexture is destroyed, the ref will be dropped by the next |
| // call to MakeCurrent if the context isn't already current. |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_EXTERNAL_OES; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| |
| // Make the context not current, so that it's not destroyed immediately. |
| context_->ReleaseCurrent(surface_.get()); |
| abstract_texture.reset(); |
| // Make the context current again, |context_| overrides it with a mock. |
| context_->GLContextStub::MakeCurrent(surface_.get()); |
| |
| // Having textures to delete should signal idle work. |
| EXPECT_EQ(GetDecoder()->HasMoreIdleWork(), true); |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| |
| // Allow the context to be made current. |
| EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true)); |
| GetDecoder()->MakeCurrent(); |
| } |
| |
| TEST_P(GLES2DecoderTest, AbstractTextureIsDestroyedIfAlreadyCurrent) { |
| // When an AbstractTexture is destroyed, the ref will be dropped immediately |
| // if the context is current. |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_EXTERNAL_OES; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| abstract_texture.reset(); |
| EXPECT_EQ(GetDecoder()->HasMoreIdleWork(), false); |
| } |
| |
| TEST_P(GLES2DecoderTest, TestAbstractTextureSetClearedWorks) { |
| const GLuint service_id = 123; |
| EXPECT_CALL(*gl_, GenTextures(1, _)) |
| .Times(1) |
| .WillOnce(SetArgPointee<1>(service_id)) |
| .RetiresOnSaturation(); |
| const GLenum target = GL_TEXTURE_2D; |
| std::unique_ptr<AbstractTexture> abstract_texture = |
| GetDecoder()->CreateAbstractTexture(target, GL_RGBA, 256, /* width */ |
| 256, /* height */ |
| 1, /* depth */ |
| 0, /* border */ |
| GL_RGBA, GL_UNSIGNED_BYTE); |
| Texture* texture = Texture::CheckedCast(abstract_texture->GetTextureBase()); |
| |
| // Texture should start off unrenderable. |
| EXPECT_EQ(texture->SafeToRenderFrom(), false); |
| |
| // Setting it to be cleared should make it renderable. |
| abstract_texture->SetCleared(); |
| EXPECT_EQ(texture->SafeToRenderFrom(), true); |
| |
| EXPECT_CALL(*gl_, DeleteTextures(1, _)).Times(1).RetiresOnSaturation(); |
| abstract_texture.reset(); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetInternalformativValidArgsSamples) { |
| const GLint kNumSampleCounts = 8; |
| typedef GetInternalformativ::Result Result; |
| Result* result = static_cast<Result*>(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; |
| GetInternalformativ cmd; |
| cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, |
| shared_memory_id_, shared_memory_offset_); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(kNumSampleCounts, result->GetNumResults()); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetInternalformativValidArgsNumSampleCounts) { |
| const GLint kNumSampleCounts = 8; |
| typedef GetInternalformativ::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| EXPECT_CALL(*gl_, GetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, |
| GL_NUM_SAMPLE_COUNTS, 1, _)) |
| .WillOnce(SetArgPointee<4>(kNumSampleCounts)) |
| .RetiresOnSaturation(); |
| result->size = 0; |
| GetInternalformativ cmd; |
| cmd.Init(GL_RENDERBUFFER, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, |
| shared_memory_id_, shared_memory_offset_); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(1, result->GetNumResults()); |
| EXPECT_EQ(kNumSampleCounts, result->GetData()[0]); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, ClientWaitSyncValid) { |
| typedef ClientWaitSync::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| 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<GLsync>(kServiceSyncId), |
| GL_SYNC_FLUSH_COMMANDS_BIT, 0)) |
| .WillOnce(Return(GL_CONDITION_SATISFIED)) |
| .RetiresOnSaturation(); |
| *result = GL_WAIT_FAILED; |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, ClientWaitSyncNonZeroTimeoutValid) { |
| typedef ClientWaitSync::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| 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<GLsync>(kServiceSyncId), |
| GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout)) |
| .WillOnce(Return(GL_CONDITION_SATISFIED)) |
| .RetiresOnSaturation(); |
| *result = GL_WAIT_FAILED; |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, ClientWaitSyncInvalidSyncFails) { |
| typedef ClientWaitSync::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| ClientWaitSync cmd; |
| 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<GLenum>(GL_WAIT_FAILED), *result); |
| EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, ClientWaitSyncResultNotInitFails) { |
| typedef ClientWaitSync::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| ClientWaitSync cmd; |
| 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(GLES3DecoderTest, ClientWaitSyncBadSharedMemoryFails) { |
| typedef ClientWaitSync::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| ClientWaitSync cmd; |
| *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(GLES3DecoderTest, WaitSyncValidArgs) { |
| const GLuint64 kTimeout = GL_TIMEOUT_IGNORED; |
| EXPECT_CALL(*gl_, WaitSync(reinterpret_cast<GLsync>(kServiceSyncId), |
| 0, kTimeout)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| WaitSync cmd; |
| cmd.Init(client_sync_id_, 0, kTimeout); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| 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(GLES2DecoderTest, GenQueriesEXTImmediateValidArgs) { |
| GenQueriesEXTImmediate* cmd = |
| GetImmediateAs<GenQueriesEXTImmediate>(); |
| GLuint temp = kNewClientId; |
| cmd->Init(1, &temp); |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(*cmd, sizeof(temp))); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| QueryManager* query_manager = decoder_->GetQueryManager(); |
| ASSERT_TRUE(query_manager != nullptr); |
| EXPECT_TRUE(query_manager->IsValidQuery(kNewClientId)); |
| } |
| |
| TEST_P(GLES2DecoderTest, GenQueriesEXTImmediateDuplicateOrNullIds) { |
| GenQueriesEXTImmediate* cmd = |
| GetImmediateAs<GenQueriesEXTImmediate>(); |
| GLuint temp[3] = {kNewClientId, kNewClientId + 1, kNewClientId}; |
| cmd->Init(3, temp); |
| EXPECT_EQ(error::kInvalidArguments, ExecuteImmediateCmd(*cmd, sizeof(temp))); |
| QueryManager* query_manager = decoder_->GetQueryManager(); |
| ASSERT_TRUE(query_manager != nullptr); |
| EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId)); |
| EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId + 1)); |
| GLuint null_id[2] = {kNewClientId, 0}; |
| cmd->Init(2, null_id); |
| EXPECT_EQ(error::kInvalidArguments, |
| ExecuteImmediateCmd(*cmd, sizeof(temp))); |
| EXPECT_FALSE(query_manager->IsValidQuery(kNewClientId)); |
| } |
| |
| TEST_P(GLES2DecoderTest, GenQueriesEXTImmediateInvalidArgs) { |
| GenQueriesEXTImmediate* cmd = |
| GetImmediateAs<GenQueriesEXTImmediate>(); |
| cmd->Init(1, &client_query_id_); |
| EXPECT_EQ(error::kInvalidArguments, |
| ExecuteImmediateCmd(*cmd, sizeof(&client_query_id_))); |
| } |
| |
| |
| 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, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| GenHelper<GenQueriesEXTImmediate>(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 != nullptr); |
| QueryManager::Query* query = query_manager->GetQuery(kNewClientId); |
| EXPECT_TRUE(query == nullptr); |
| |
| // BeginQueryEXT should fail if id is not generated from GenQueriesEXT. |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kInvalidClientId, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, shared_memory_id_, |
| 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 != nullptr); |
| 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()); |
| |
| // Begin should fail if using a different target |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, kNewClientId, |
| shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| // Begin should fail if using a different sync |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, kNewClientId, |
| shared_memory_id_, kSharedMemoryOffset + sizeof(QuerySync)); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| // QueryCounter should fail if using a different target |
| QueryCounterEXT query_counter_cmd; |
| query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_, |
| kSharedMemoryOffset, 1); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| // QueryCounter should fail if using a different sync |
| query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_, |
| kSharedMemoryOffset + sizeof(QuerySync), 1); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| 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<GLsync>(0xdeadbeef); |
| |
| static void ExecuteGenerateQueryCmd(GLES2DecoderTestBase* test, |
| ::gl::MockGLInterface* gl, |
| GLenum target, |
| GLuint client_id, |
| GLuint service_id) { |
| test->GenHelper<GenQueriesEXTImmediate>(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, |
| ::gl::MockGLInterface* gl, |
| ::gl::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, |
| ::gl::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, |
| ::gl::MockGLInterface* gl, |
| ::gl::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 void ProcessQuery(GLES2DecoderTestBase* test, |
| ::gl::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(); |
| ASSERT_TRUE(nullptr != query_manager); |
| query_manager->ProcessPendingQueries(false); |
| } |
| |
| static void CheckBeginEndQueryBadMemoryFails(GLES2DecoderTestBase* test, |
| GLuint client_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); |
| |
| test->GenHelper<GenQueriesEXTImmediate>(client_id); |
| |
| // Test bad shared memory fails |
| error::Error error = error::kNoError; |
| if (query_type.is_counter) { |
| QueryCounterEXT query_counter_cmd; |
| query_counter_cmd.Init(client_id, query_type.type, shm_id, shm_offset, 1); |
| error = test->ExecuteCmd(query_counter_cmd); |
| } else { |
| BeginQueryEXT begin_cmd; |
| begin_cmd.Init(query_type.type, client_id, shm_id, shm_offset); |
| error = test->ExecuteCmd(begin_cmd); |
| } |
| |
| EXPECT_TRUE(error != error::kNoError); |
| |
| test->ResetDecoder(); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryIdFails) { |
| for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { |
| CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i], |
| kInvalidSharedMemoryId, |
| kSharedMemoryOffset); |
| } |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, BeginEndQueryEXTBadMemoryOffsetFails) { |
| for (size_t i = 0; i < arraysize(kQueryTypes); ++i) { |
| // Out-of-bounds. |
| CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i], |
| shared_memory_id_, |
| kInvalidSharedMemoryOffset); |
| // Overflow. |
| CheckBeginEndQueryBadMemoryFails(this, kNewClientId, kQueryTypes[i], |
| shared_memory_id_, 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<::gl::MockGLInterface>* gl = GetGLMock(); |
| ::gl::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, shared_memory_id_, |
| kSharedMemoryOffset, 1)); |
| } else { |
| EXPECT_EQ( |
| error::kNoError, |
| ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type, |
| kNewClientId, kNewServiceId, shared_memory_id_, |
| kSharedMemoryOffset)); |
| EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, |
| query_type.type, 1)); |
| } |
| |
| 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, shared_memory_id_, |
| kSharedMemoryOffset, 2)); |
| } else { |
| EXPECT_EQ( |
| error::kNoError, |
| ExecuteBeginQueryCmd(this, gl, &gpu_timing_queries, query_type.type, |
| kNewClientId, kNewServiceId, shared_memory_id_, |
| kSharedMemoryOffset)); |
| EXPECT_EQ(error::kNoError, ExecuteEndQueryCmd(this, gl, |
| query_type.type, 2)); |
| } |
| |
| 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<GenQueriesEXTImmediate>(kNewClientId); |
| |
| // Test valid parameters work. |
| begin_cmd.Init(GL_COMMANDS_ISSUED_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()); |
| |
| // 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<GenQueriesEXTImmediate>(kNewClientId); |
| |
| // Test valid parameters work. |
| begin_cmd.Init(GL_GET_ERROR_QUERY_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()); |
| |
| // Test end succeeds |
| QuerySync* sync = static_cast<QuerySync*>(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<GLenum>(GL_INVALID_VALUE), |
| static_cast<GLenum>(sync->result)); |
| } |
| |
| TEST_P(GLES2DecoderTest, SetDisjointValueSync) { |
| SetDisjointValueSyncCHROMIUM cmd; |
| |
| cmd.Init(static_cast<uint32_t>(-1), 0u); |
| EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd)); |
| |
| cmd.Init(kInvalidSharedMemoryId, 0u); |
| EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); |
| |
| cmd.Init(shared_memory_id_, kSharedBufferSize); |
| EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); |
| |
| cmd.Init(shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| |
| cmd.Init(shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd)); |
| } |
| |
| 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<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()); |
| 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(); |
| query_manager->ProcessPendingQueries(false); |
| |
| 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(); |
| query_manager->ProcessPendingQueries(false); |
| |
| 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<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_INVALID_OPERATION, GetGLError()); |
| |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED, kNewClientId, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| begin_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, kNewClientId, |
| shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| begin_cmd.Init(GL_TIME_ELAPSED, kNewClientId, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| begin_cmd.Init(0xdeadbeef, kNewClientId, shared_memory_id_, |
| 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<GenQueriesEXTImmediate>(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, shared_memory_id_, |
| 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 != nullptr); |
| QueryManager::Query* query = query_manager->GetQuery(kNewClientId); |
| ASSERT_TRUE(query != nullptr); |
| 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<GenQueriesEXTImmediate>(kNewClientId); |
| |
| QueryCounterEXT query_counter_cmd; |
| query_counter_cmd.Init(kNewClientId, GL_TIMESTAMP, shared_memory_id_, |
| kSharedMemoryOffset, 1); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd)); |
| EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); |
| |
| query_counter_cmd.Init(kNewClientId, 0xdeadbeef, shared_memory_id_, |
| 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<IsEnabled::Result*>(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; |
| } |
| ~SizeOnlyMemoryTracker() override = default; |
| |
| void TrackMemoryAllocatedChange(uint64_t delta) override { |
| pool_info_.size += delta; |
| } |
| |
| uint64_t GetSize() const override { |
| return pool_info_.size - pool_info_.initial_size; |
| } |
| |
| uint64_t ClientTracingId() const override { return 0; } |
| int ClientId() const override { return 0; } |
| uint64_t ContextGroupTracingId() const override { return 0; } |
| |
| private: |
| struct PoolInfo { |
| PoolInfo() : initial_size(0), size(0) {} |
| uint64_t initial_size; |
| uint64_t size; |
| }; |
| PoolInfo pool_info_; |
| }; |
| |
| } // anonymous namespace. |
| |
| TEST_P(GLES2DecoderManualInitTest, MemoryTrackerInitialSize) { |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| InitState init; |
| init.bind_generates_resource = true; |
| InitDecoder(init); |
| // Expect that initial size - size is 0. |
| EXPECT_EQ(0u, memory_tracker_ptr->GetSize()); |
| EXPECT_EQ(0u, memory_tracker_ptr->GetSize()); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexImage2D) { |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| InitState init; |
| init.bind_generates_resource = true; |
| InitDecoder(init); |
| DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); |
| DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(128u, memory_tracker_ptr->GetSize()); |
| DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(64u, memory_tracker_ptr->GetSize()); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, MemoryTrackerTexStorage2DEXT) { |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| InitState init; |
| init.extensions = "GL_EXT_texture_storage"; |
| init.bind_generates_resource = true; |
| InitDecoder(init); |
| DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); |
| EXPECT_CALL(*gl_, TexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 4)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| TexStorage2DEXT cmd; |
| cmd.Init(GL_TEXTURE_2D, 1, GL_RGBA8, 8, 4); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(128u, memory_tracker_ptr->GetSize()); |
| } |
| |
| 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; |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| 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(*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_ptr->GetSize()); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, MemoryTrackerRenderbufferStorage) { |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| 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(*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_ptr->GetSize()); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, MemoryTrackerBufferData) { |
| auto memory_tracker = std::make_unique<SizeOnlyMemoryTracker>(); |
| auto* memory_tracker_ptr = memory_tracker.get(); |
| set_memory_tracker(std::move(memory_tracker)); |
| InitState init; |
| init.bind_generates_resource = true; |
| InitDecoder(init); |
| EXPECT_EQ(0u, memory_tracker_ptr->GetSize()); |
| 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(*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_ptr->GetSize()); |
| } |
| |
| TEST_P(GLES2DecoderManualInitTest, ImmutableCopyTexImage2D) { |
| const GLenum kTarget = GL_TEXTURE_2D; |
| const GLint kLevel = 0; |
| const GLint kLevels = 2; |
| 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; |
| init.gl_version = "OpenGL ES 2.0"; // To avoid TexStorage emulation. |
| InitDecoder(init); |
| DoBindTexture(kTarget, 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, kLevels, 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, kLevels, 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); |
| 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); |
| 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); |
| 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); |
| 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_P(GLES3DecoderTest, BeginInvalidTargetQueryFails) { |
| BeginQueryEXT begin_cmd; |
| begin_cmd.Init(0xdeadbeef, kNewClientId, shared_memory_id_, |
| kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); |
| EXPECT_EQ(GL_INVALID_ENUM, GetGLError()); |
| } |
| |
| |
| TEST_P(GLES3DecoderTest, BindTransformFeedbackValidArgs) { |
| EXPECT_CALL(*gl_, BindTransformFeedback(GL_TRANSFORM_FEEDBACK, |
| kServiceTransformFeedbackId)); |
| SpecializedSetup<BindTransformFeedback, 0>(true); |
| BindTransformFeedback cmd; |
| cmd.Init(GL_TRANSFORM_FEEDBACK, client_transformfeedback_id_); |
| EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, DeleteTransformFeedbacksImmediateInvalidArgs) { |
| DeleteTransformFeedbacksImmediate& cmd = |
| *GetImmediateAs<DeleteTransformFeedbacksImmediate>(); |
| SpecializedSetup<DeleteTransformFeedbacksImmediate, 0>(false); |
| GLuint temp = kInvalidClientId; |
| cmd.Init(1, &temp); |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp))); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetIntegeri_vValidArgs) { |
| EXPECT_CALL(*gl_, GetIntegeri_v(_, _, _)).Times(0); |
| typedef GetIntegeri_v::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| result->size = 0; |
| GetIntegeri_v cmd; |
| cmd.Init(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 2, shared_memory_id_, |
| shared_memory_offset_); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned( |
| GL_TRANSFORM_FEEDBACK_BUFFER_BINDING), |
| result->GetNumResults()); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetInteger64i_vValidArgs) { |
| EXPECT_CALL(*gl_, GetInteger64i_v(_, _, _)).Times(0); |
| typedef GetInteger64i_v::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| result->size = 0; |
| GetInteger64i_v cmd; |
| cmd.Init(GL_UNIFORM_BUFFER_SIZE, 2, shared_memory_id_, |
| shared_memory_offset_); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned( |
| GL_UNIFORM_BUFFER_SIZE), |
| result->GetNumResults()); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetSamplerBinding) { |
| const GLuint kClientID = 12; |
| const GLuint kServiceID = 1012; |
| const GLuint kUnit = 0; |
| DoCreateSampler(kClientID, kServiceID); |
| DoBindSampler(kUnit, kClientID, kServiceID); |
| |
| EXPECT_CALL(*gl_, GetError()) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .RetiresOnSaturation(); |
| |
| typedef cmds::GetIntegerv::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| cmds::GetIntegerv cmd; |
| cmd.Init(GL_SAMPLER_BINDING, shared_memory_id_, shared_memory_offset_); |
| result->size = 0; |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(1, result->GetNumResults()); |
| EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0])); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| TEST_P(GLES3DecoderTest, GetTransformFeedbackBinding) { |
| const GLuint kClientID = 12; |
| const GLuint kServiceID = 1012; |
| const GLenum kTarget = GL_TRANSFORM_FEEDBACK; |
| DoCreateTransformFeedback(kClientID, kServiceID); |
| EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| DoBindTransformFeedback(kTarget, kClientID, kServiceID); |
| |
| EXPECT_CALL(*gl_, GetError()) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .RetiresOnSaturation(); |
| |
| typedef cmds::GetIntegerv::Result Result; |
| Result* result = static_cast<Result*>(shared_memory_address_); |
| cmds::GetIntegerv cmd; |
| cmd.Init( |
| GL_TRANSFORM_FEEDBACK_BINDING, shared_memory_id_, shared_memory_offset_); |
| result->size = 0; |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(1, result->GetNumResults()); |
| EXPECT_EQ(kClientID, static_cast<GLuint>(result->GetData()[0])); |
| |
| EXPECT_CALL(*gl_, BindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| DoBindTransformFeedback(kTarget, 0, kServiceDefaultTransformFeedbackId); |
| DoDeleteTransformFeedback(kClientID, kServiceID); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| // 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()); |
| // gpu::CommandHeader::size is a 21-bit field, so casting it to int is safe. |
| // Without the explicit cast, Visual Studio ends up promoting the left hand |
| // side to unsigned, and emits a sign mismatch warning. |
| EXPECT_EQ(entries_per_cmd_ + static_cast<int>(cmds_[1].header.size), |
| num_processed); |
| } |
| |
| class GLES2DecoderDescheduleUntilFinishedTest : public GLES2DecoderTest { |
| public: |
| GLES2DecoderDescheduleUntilFinishedTest() = default; |
| |
| void SetUp() override { |
| InitState init; |
| init.gl_version = "4.4"; |
| init.extensions += " GL_ARB_compatibility GL_ARB_sync"; |
| InitDecoder(init); |
| |
| EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) |
| .Times(2) |
| .WillOnce(Return(sync_service_id_)) |
| .WillOnce(Return(sync_service_id2_)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, IsSync(sync_service_id_)).WillRepeatedly(Return(GL_TRUE)); |
| EXPECT_CALL(*gl_, Flush()).Times(2).RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, DeleteSync(sync_service_id_)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, DeleteSync(sync_service_id2_)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| } |
| |
| void OnDescheduleUntilFinished() override { |
| deschedule_until_finished_callback_count_++; |
| } |
| void OnRescheduleAfterFinished() override { |
| reschedule_after_finished_callback_count_++; |
| } |
| |
| protected: |
| int deschedule_until_finished_callback_count_ = 0; |
| int reschedule_after_finished_callback_count_ = 0; |
| GLsync sync_service_id_ = reinterpret_cast<GLsync>(0x15); |
| GLsync sync_service_id2_ = reinterpret_cast<GLsync>(0x15); |
| }; |
| |
| TEST_P(GLES2DecoderDescheduleUntilFinishedTest, AlreadySignalled) { |
| EXPECT_CALL(*gl_, ClientWaitSync(sync_service_id_, 0, 0)) |
| .Times(1) |
| .WillOnce(Return(GL_ALREADY_SIGNALED)) |
| .RetiresOnSaturation(); |
| |
| DescheduleUntilFinishedCHROMIUM cmd; |
| cmd.Init(); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(0, deschedule_until_finished_callback_count_); |
| EXPECT_EQ(0, reschedule_after_finished_callback_count_); |
| |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(0, deschedule_until_finished_callback_count_); |
| EXPECT_EQ(0, reschedule_after_finished_callback_count_); |
| } |
| |
| TEST_P(GLES2DecoderDescheduleUntilFinishedTest, NotYetSignalled) { |
| EXPECT_CALL(*gl_, ClientWaitSync(sync_service_id_, 0, 0)) |
| .Times(1) |
| .WillOnce(Return(GL_TIMEOUT_EXPIRED)) |
| .RetiresOnSaturation(); |
| |
| DescheduleUntilFinishedCHROMIUM cmd; |
| cmd.Init(); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| EXPECT_EQ(0, deschedule_until_finished_callback_count_); |
| EXPECT_EQ(0, reschedule_after_finished_callback_count_); |
| |
| EXPECT_EQ(error::kDeferLaterCommands, ExecuteCmd(cmd)); |
| EXPECT_EQ(1, deschedule_until_finished_callback_count_); |
| EXPECT_EQ(0, reschedule_after_finished_callback_count_); |
| } |
| |
| void GLES3DecoderTest::SetUp() { |
| InitState init; |
| init.gl_version = "OpenGL ES 3.0"; |
| init.bind_generates_resource = true; |
| init.context_type = CONTEXT_TYPE_OPENGLES3; |
| InitDecoder(init); |
| } |
| |
| void WebGL2DecoderTest::SetUp() { |
| InitState init; |
| init.gl_version = "OpenGL ES 3.0"; |
| init.bind_generates_resource = true; |
| init.context_type = CONTEXT_TYPE_WEBGL2; |
| InitDecoder(init); |
| } |
| |
| void GLES3DecoderWithShaderTest::SetUp() { |
| InitState init; |
| init.gl_version = "OpenGL ES 3.0"; |
| init.bind_generates_resource = true; |
| init.context_type = CONTEXT_TYPE_OPENGLES3; |
| InitDecoder(init); |
| SetupDefaultProgram(); |
| } |
| |
| void GLES3DecoderRGBBackbufferTest::SetUp() { |
| InitState init; |
| init.gl_version = "OpenGL ES 3.0"; |
| init.bind_generates_resource = true; |
| init.context_type = CONTEXT_TYPE_OPENGLES3; |
| InitDecoder(init); |
| 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, |
| GLES2DecoderDescheduleUntilFinishedTest, |
| ::testing::Bool()); |
| |
| INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest, ::testing::Bool()); |
| |
| INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderWithShaderTest, ::testing::Bool()); |
| |
| INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderManualInitTest, ::testing::Bool()); |
| |
| INSTANTIATE_TEST_CASE_P(Service, |
| GLES3DecoderRGBBackbufferTest, |
| ::testing::Bool()); |
| |
| } // namespace gles2 |
| } // namespace gpu |