// Copyright (c) 2009 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_base.h" #include "base/string_util.h" #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/service/gl_mock.h" #include "gpu/command_buffer/service/cmd_buffer_engine.h" #include "gpu/command_buffer/service/context_group.h" #include "gpu/command_buffer/service/program_manager.h" #include "testing/gtest/include/gtest/gtest.h" using ::gles2::MockGLInterface; using ::testing::_; using ::testing::DoAll; using ::testing::InSequence; using ::testing::MatcherCast; using ::testing::Pointee; using ::testing::Return; using ::testing::SetArrayArgument; using ::testing::SetArgumentPointee; using ::testing::StrEq; using ::testing::StrictMock; namespace gpu { namespace gles2 { void GLES2DecoderTestBase::SetUp() { gl_.reset(new StrictMock()); ::gles2::GLInterface::SetGLInterface(gl_.get()); InSequence sequence; EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_VERTEX_ATTRIBS, _)) .WillOnce(SetArgumentPointee<1>(kNumVertexAttribs)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, _)) .WillOnce(SetArgumentPointee<1>(kNumTextureUnits)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxTextureSize)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, _)) .WillOnce(SetArgumentPointee<1>(kMaxCubeMapTextureSize)) .RetiresOnSaturation(); static GLuint black_ids[] = { kServiceBlackTexture2dId, kServiceBlackTextureCubemapId, }; EXPECT_CALL(*gl_, GenTextures(2, _)) .WillOnce(SetArrayArgument<1>(black_ids, black_ids + 2)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceBlackTexture2dId)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, 0)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_CUBE_MAP, kServiceBlackTextureCubemapId)) .Times(1) .RetiresOnSaturation(); static GLenum faces[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; for (size_t ii = 0; ii < arraysize(faces); ++ii) { EXPECT_CALL(*gl_, TexImage2D(faces[ii], 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, _)) .Times(1) .RetiresOnSaturation(); } EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_CUBE_MAP, 0)) .Times(1) .RetiresOnSaturation(); engine_.reset(new StrictMock()); Buffer buffer = engine_->GetSharedMemoryBuffer(kSharedMemoryId); shared_memory_offset_ = kSharedMemoryOffset; shared_memory_address_ = reinterpret_cast(buffer.ptr) + shared_memory_offset_; shared_memory_id_ = kSharedMemoryId; decoder_.reset(GLES2Decoder::Create(&group_)); decoder_->Initialize(); decoder_->set_engine(engine_.get()); EXPECT_CALL(*gl_, GenBuffersARB(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceBufferId)) .RetiresOnSaturation(); GenHelper(client_buffer_id_); EXPECT_CALL(*gl_, GenFramebuffersEXT(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceFramebufferId)) .RetiresOnSaturation(); GenHelper(client_framebuffer_id_); EXPECT_CALL(*gl_, GenRenderbuffersEXT(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceRenderbufferId)) .RetiresOnSaturation(); GenHelper(client_renderbuffer_id_); EXPECT_CALL(*gl_, GenTextures(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceTextureId)) .RetiresOnSaturation(); GenHelper(client_texture_id_); EXPECT_CALL(*gl_, GenBuffersARB(_, _)) .WillOnce(SetArgumentPointee<1>(kServiceElementBufferId)) .RetiresOnSaturation(); GenHelper(client_element_buffer_id_); { EXPECT_CALL(*gl_, CreateProgram()) .Times(1) .WillOnce(Return(kServiceProgramId)) .RetiresOnSaturation(); CreateProgram cmd; cmd.Init(client_program_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } { EXPECT_CALL(*gl_, CreateShader(_)) .Times(1) .WillOnce(Return(kServiceShaderId)) .RetiresOnSaturation(); CreateShader cmd; cmd.Init(GL_VERTEX_SHADER, client_shader_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } EXPECT_EQ(GL_NO_ERROR, GetGLError()); } void GLES2DecoderTestBase::TearDown() { decoder_->Destroy(); decoder_.reset(); engine_.reset(); ::gles2::GLInterface::SetGLInterface(NULL); gl_.reset(); } GLint GLES2DecoderTestBase::GetGLError() { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); GetError cmd; cmd.Init(shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); return static_cast(*GetSharedMemoryAs()); } void GLES2DecoderTestBase::DoBindTexture( GLenum target, GLuint client_id, GLuint service_id) { EXPECT_CALL(*gl_, BindTexture(target, service_id)) .Times(1) .RetiresOnSaturation(); BindTexture cmd; cmd.Init(target, client_id); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderTestBase::DoTexImage2D( GLenum target, GLint level, GLenum internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, uint32 shared_memory_id, uint32 shared_memory_offset) { EXPECT_CALL(*gl_, TexImage2D(target, level, internal_format, width, height, border, format, type, _)) .Times(1) .RetiresOnSaturation(); TexImage2D cmd; cmd.Init(target, level, internal_format, width, height, border, format, type, shared_memory_id, shared_memory_offset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::SetUp() { GLES2DecoderTestBase::SetUp(); { static AttribInfo attribs[] = { { kAttrib1Name, kAttrib1Size, kAttrib1Type, kAttrib1Location, }, { kAttrib2Name, kAttrib2Size, kAttrib2Type, kAttrib2Location, }, { kAttrib3Name, kAttrib3Size, kAttrib3Type, kAttrib3Location, }, }; static UniformInfo uniforms[] = { { kUniform1Name, kUniform1Size, kUniform1Type, kUniform1Location, }, { kUniform2Name, kUniform2Size, kUniform2Type, kUniform2Location, }, { kUniform3Name, kUniform3Size, kUniform3Type, kUniform3Location, }, }; SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms), client_program_id_, kServiceProgramId); } { EXPECT_CALL(*gl_, UseProgram(kServiceProgramId)) .Times(1) .RetiresOnSaturation(); UseProgram cmd; cmd.Init(client_program_id_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } } void GLES2DecoderWithShaderTestBase::TearDown() { GLES2DecoderTestBase::TearDown(); } void GLES2DecoderWithShaderTestBase::SetupShader( GLES2DecoderWithShaderTestBase::AttribInfo* attribs, size_t num_attribs, GLES2DecoderWithShaderTestBase::UniformInfo* uniforms, size_t num_uniforms, GLuint client_id, GLuint service_id) { LinkProgram cmd; cmd.Init(client_id); { InSequence s; EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, LinkProgram(service_id)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTES, _)) .WillOnce(SetArgumentPointee<2>(num_attribs)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetProgramiv(service_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, _)) .WillOnce(SetArgumentPointee<2>(kMaxAttribLength)) .RetiresOnSaturation(); for (size_t ii = 0; ii < num_attribs; ++ii) { const AttribInfo& info = attribs[ii]; EXPECT_CALL(*gl_, GetActiveAttrib(service_id, ii, kMaxAttribLength, _, _, _, _)) .WillOnce(DoAll( SetArgumentPointee<3>(strlen(info.name)), SetArgumentPointee<4>(info.size), SetArgumentPointee<5>(info.type), SetArrayArgument<6>(info.name, info.name + strlen(info.name) + 1))) .RetiresOnSaturation(); if (!ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) { EXPECT_CALL(*gl_, GetAttribLocation(service_id, StrEq(info.name))) .WillOnce(Return(info.location)) .RetiresOnSaturation(); } } EXPECT_CALL(*gl_, GetProgramiv(service_id, GL_ACTIVE_UNIFORMS, _)) .WillOnce(SetArgumentPointee<2>(num_uniforms)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetProgramiv(service_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, _)) .WillOnce(SetArgumentPointee<2>(kMaxUniformLength)) .RetiresOnSaturation(); for (size_t ii = 0; ii < num_uniforms; ++ii) { const UniformInfo& info = uniforms[ii]; EXPECT_CALL(*gl_, GetActiveUniform(service_id, ii, kMaxUniformLength, _, _, _, _)) .WillOnce(DoAll( SetArgumentPointee<3>(strlen(info.name)), SetArgumentPointee<4>(info.size), SetArgumentPointee<5>(info.type), SetArrayArgument<6>(info.name, info.name + strlen(info.name) + 1))) .RetiresOnSaturation(); if (!ProgramManager::IsInvalidPrefix(info.name, strlen(info.name))) { EXPECT_CALL(*gl_, GetUniformLocation(service_id, StrEq(info.name))) .WillOnce(Return(info.location)) .RetiresOnSaturation(); if (info.size > 1) { for (GLsizei jj = 1; jj < info.size; ++jj) { std::string element_name( std::string(info.name) + "[" + IntToString(jj) + "]"); EXPECT_CALL(*gl_, GetUniformLocation(service_id, StrEq(element_name))) .WillOnce(Return(info.location + jj)) .RetiresOnSaturation(); } } } } } EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoEnableVertexAttribArray(GLint index) { EXPECT_CALL(*gl_, EnableVertexAttribArray(index)) .Times(1) .RetiresOnSaturation(); EnableVertexAttribArray cmd; cmd.Init(index); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoBindBuffer( GLenum target, GLuint client_id, GLuint service_id) { EXPECT_CALL(*gl_, BindBuffer(target, service_id)) .Times(1) .RetiresOnSaturation(); BindBuffer cmd; cmd.Init(target, client_id); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoBufferData(GLenum target, GLsizei size) { EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, BufferData(target, size, _, GL_STREAM_DRAW)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*gl_, GetError()) .WillOnce(Return(GL_NO_ERROR)) .RetiresOnSaturation(); BufferData cmd; cmd.Init(target, size, 0, 0, GL_STREAM_DRAW); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoBufferSubData( GLenum target, GLint offset, GLsizei size, const void* data) { EXPECT_CALL(*gl_, BufferSubData(target, offset, size, shared_memory_address_)) .Times(1) .RetiresOnSaturation(); memcpy(shared_memory_address_, data, size); BufferSubData cmd; cmd.Init(target, offset, size, shared_memory_id_, shared_memory_offset_); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoDeleteBuffer( GLuint client_id, GLuint service_id) { EXPECT_CALL(*gl_, DeleteBuffersARB(1, Pointee(service_id))) .Times(1) .RetiresOnSaturation(); DeleteBuffers cmd; cmd.Init(1, shared_memory_id_, shared_memory_offset_); memcpy(shared_memory_address_, &client_id, sizeof(client_id)); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoDeleteProgram( GLuint client_id, GLuint service_id) { EXPECT_CALL(*gl_, DeleteProgram(service_id)) .Times(1) .RetiresOnSaturation(); DeleteProgram cmd; cmd.Init(client_id); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::DoVertexAttribPointer( GLuint index, GLint size, GLenum type, GLsizei stride, GLuint offset) { EXPECT_CALL(*gl_, VertexAttribPointer(index, size, type, GL_FALSE, stride, BufferOffset(offset))) .Times(1) .RetiresOnSaturation(); VertexAttribPointer cmd; cmd.Init(index, size, GL_FLOAT, GL_FALSE, stride, offset); EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); } void GLES2DecoderWithShaderTestBase::SetupVertexBuffer() { DoEnableVertexAttribArray(1); DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId); GLfloat f = 0; DoBufferData(GL_ARRAY_BUFFER, kNumVertices * 2 * sizeof(f)); } void GLES2DecoderWithShaderTestBase::SetupIndexBuffer() { DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER, client_element_buffer_id_, kServiceElementBufferId); static const GLshort indices[] = {100, 1, 2, 3, 4, 5, 6, 7, 100, 9}; COMPILE_ASSERT(arraysize(indices) == kNumIndices, Indices_is_not_10); DoBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices)); DoBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indices), indices); } void GLES2DecoderWithShaderTestBase::SetupTexture() { DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId); DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); }; void GLES2DecoderWithShaderTestBase::DeleteVertexBuffer() { DoDeleteBuffer(client_buffer_id_, kServiceBufferId); } void GLES2DecoderWithShaderTestBase::DeleteIndexBuffer() { DoDeleteBuffer(client_element_buffer_id_, kServiceElementBufferId); } const char* GLES2DecoderWithShaderTestBase::kAttrib1Name = "attrib1"; const char* GLES2DecoderWithShaderTestBase::kAttrib2Name = "attrib2"; const char* GLES2DecoderWithShaderTestBase::kAttrib3Name = "attrib3"; const char* GLES2DecoderWithShaderTestBase::kUniform1Name = "uniform1"; const char* GLES2DecoderWithShaderTestBase::kUniform2Name = "uniform2"; const char* GLES2DecoderWithShaderTestBase::kUniform3Name = "uniform3"; } // namespace gles2 } // namespace gpu