// 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. // Tests for the Command Buffer Helper. #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/common/command_buffer.h" #include "gpu/GLES2/gles2_command_buffer.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" namespace gpu { class GLES2MockCommandBufferHelper : public CommandBuffer { public: static const int32 kTransferBufferId = 0x123; GLES2MockCommandBufferHelper() { } virtual ~GLES2MockCommandBufferHelper() { } // CommandBuffer implementation: virtual bool Initialize(int32 size) { ring_buffer_.reset(new CommandBufferEntry[size]); ring_buffer_buffer_.ptr = ring_buffer_.get(); ring_buffer_buffer_.size = size; state_.num_entries = size / sizeof(ring_buffer_[0]); state_.token = 10000; // All token checks in the tests should pass. return true; } virtual Buffer GetRingBuffer() { return ring_buffer_buffer_; } virtual State GetState() { return state_; } virtual State Flush(int32 put_offset) { state_.put_offset = put_offset; state_.get_offset = put_offset; OnFlush(transfer_buffer_buffer_.ptr); return state_; } virtual void SetGetOffset(int32 get_offset) { state_.get_offset = get_offset; } virtual int32 CreateTransferBuffer(size_t size) { transfer_buffer_.reset(new int8[size]); transfer_buffer_buffer_.ptr = transfer_buffer_.get(); transfer_buffer_buffer_.size = size; return kTransferBufferId; } virtual void DestroyTransferBuffer(int32) { // NOLINT GPU_NOTREACHED(); } virtual Buffer GetTransferBuffer(int32 id) { GPU_DCHECK_EQ(id, kTransferBufferId); return transfer_buffer_buffer_; } virtual void SetToken(int32 token) { GPU_NOTREACHED(); state_.token = token; } virtual void SetParseError(error::Error error) { GPU_NOTREACHED(); state_.error = error; } virtual void OnFlush(void* transfer_buffer) = 0; private: scoped_array transfer_buffer_; Buffer transfer_buffer_buffer_; scoped_array ring_buffer_; Buffer ring_buffer_buffer_; State state_; }; class MockGLES2CommandBuffer : public GLES2MockCommandBufferHelper { public: virtual ~MockGLES2CommandBuffer() { } // This is so we can use all the gmock functions when Flush is called. MOCK_METHOD1(OnFlush, void(void* result)); }; // GCC requires these declarations, but MSVC requires they not be present #ifndef _MSC_VER const int32 GLES2MockCommandBufferHelper::kTransferBufferId; #endif namespace gles2 { using testing::Return; using testing::Mock; using testing::Truly; using testing::Sequence; using testing::DoAll; using testing::Invoke; using testing::_; ACTION_P(SetMemory, obj) { memcpy(arg0, &obj, sizeof(obj)); } ACTION_P2(SetMemoryAtOffset, offset, obj) { memcpy(static_cast(arg0) + offset, &obj, sizeof(obj)); } // Used to help set the transfer buffer result to SizedResult of a single value. template class SizedResultHelper { public: explicit SizedResultHelper(T result) : size_(sizeof(result)), result_(result) { } private: uint32 size_; T result_; }; // Struct to make it easy to pass a vec4 worth of floats. struct FourFloats { FourFloats(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } float x; float y; float z; float w; }; #pragma pack(push, 1) // Struct that holds 7 characters. struct Str7 { char str[7]; }; #pragma pack(pop) // Test fixture for CommandBufferHelper test. class GLES2ImplementationTest : public testing::Test { protected: static const int32 kNumCommandEntries = 100; static const int32 kCommandBufferSizeBytes = kNumCommandEntries * sizeof(CommandBufferEntry); static const size_t kTransferBufferSize = 256; static const int32 kTransferBufferId = GLES2MockCommandBufferHelper::kTransferBufferId; static const uint8 kInitialValue = 0xBD; static const GLint kMaxVertexAttribs = 8; virtual void SetUp() { command_buffer_.reset(new MockGLES2CommandBuffer()); command_buffer_->Initialize(kCommandBufferSizeBytes); EXPECT_EQ(kTransferBufferId, command_buffer_->CreateTransferBuffer(kTransferBufferSize)); transfer_buffer_ = command_buffer_->GetTransferBuffer(kTransferBufferId); ClearTransferBuffer(); helper_.reset(new GLES2CmdHelper(command_buffer_.get())); helper_->Initialize(kCommandBufferSizeBytes); #if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS) EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(SizedResultHelper(kMaxVertexAttribs))) .RetiresOnSaturation(); #endif gl_.reset(new GLES2Implementation( helper_.get(), kTransferBufferSize, transfer_buffer_.ptr, kTransferBufferId, false)); EXPECT_CALL(*command_buffer_, OnFlush(_)).Times(1).RetiresOnSaturation(); helper_->CommandBufferHelper::Flush(); Buffer ring_buffer = command_buffer_->GetRingBuffer(); commands_ = static_cast(ring_buffer.ptr) + command_buffer_->GetState().put_offset; } virtual void TearDown() { } void ClearTransferBuffer() { memset(transfer_buffer_.ptr, kInitialValue, kTransferBufferSize); } static unsigned int RoundToAlignment(unsigned int size) { return (size + GLES2Implementation::kAlignment - 1) & ~(GLES2Implementation::kAlignment - 1); } Buffer transfer_buffer_; CommandBufferEntry* commands_; scoped_ptr command_buffer_; scoped_ptr helper_; Sequence sequence_; scoped_ptr gl_; }; // GCC requires these declarations, but MSVC requires they not be present #ifndef _MSC_VER const int32 GLES2ImplementationTest::kTransferBufferId; #endif TEST_F(GLES2ImplementationTest, ShaderSource) { const uint32 kBucketId = 1; // This id is hardcoded into GLES2Implemenation const GLuint kShaderId = 456; const char* kString1 = "foobar"; const char* kString2 = "barfoo"; const size_t kString1Size = strlen(kString1); const size_t kString2Size = strlen(kString2); const size_t kString3Size = 1; // Want the NULL; const size_t kSourceSize = kString1Size + kString2Size + kString3Size; const size_t kPaddedString1Size = RoundToAlignment(kString1Size); const size_t kPaddedString2Size = RoundToAlignment(kString2Size); struct Cmds { cmd::SetBucketSize set_bucket_size; cmd::SetBucketData set_bucket_data1; cmd::SetToken set_token1; cmd::SetBucketData set_bucket_data2; cmd::SetToken set_token2; cmd::SetBucketData set_bucket_data3; cmd::SetToken set_token3; ShaderSourceBucket shader_source_bucket; cmd::SetBucketSize clear_bucket_size; }; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.set_bucket_size.Init(kBucketId, kSourceSize); expected.set_bucket_data1.Init( kBucketId, 0, kString1Size, kTransferBufferId, offset); expected.set_token1.Init(token++); expected.set_bucket_data2.Init( kBucketId, kString1Size, kString2Size, kTransferBufferId, offset + kPaddedString1Size); expected.set_token2.Init(token++); expected.set_bucket_data3.Init( kBucketId, kString1Size + kString2Size, kString3Size, kTransferBufferId, offset + kPaddedString1Size + kPaddedString2Size); expected.set_token3.Init(token++); expected.shader_source_bucket.Init(kShaderId, kBucketId); expected.clear_bucket_size.Init(kBucketId, 0); const char* strings[] = { kString1, kString2, }; gl_->ShaderSource(kShaderId, 2, strings, NULL); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, GetShaderSource) { const uint32 kBucketId = 1; // This id is hardcoded into GLES2Implemenation const GLuint kShaderId = 456; const Str7 kString = {"foobar"}; const char kBad = 0x12; struct Cmds { cmd::SetBucketSize set_bucket_size1; GetShaderSource get_shader_source; cmd::GetBucketSize get_bucket_size; cmd::GetBucketData get_bucket_data; cmd::SetToken set_token1; cmd::SetBucketSize set_bucket_size2; }; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.set_bucket_size1.Init(kBucketId, 0); expected.get_shader_source.Init(kShaderId, kBucketId); expected.get_bucket_size.Init(kBucketId, kTransferBufferId, 0); expected.get_bucket_data.Init( kBucketId, 0, sizeof(kString), kTransferBufferId, offset); expected.set_token1.Init(token++); expected.set_bucket_size2.Init(kBucketId, 0); char buf[sizeof(kString) + 1]; memset(buf, kBad, sizeof(buf)); EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(uint32(sizeof(kString)))) .WillOnce(SetMemoryAtOffset(offset, kString)) .RetiresOnSaturation(); GLsizei length = 0; gl_->GetShaderSource(kShaderId, sizeof(buf), &length, buf); EXPECT_EQ(sizeof(kString) - 1, static_cast(length)); EXPECT_STREQ(kString.str, buf); EXPECT_EQ(buf[sizeof(kString)], kBad); } #if defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS) TEST_F(GLES2ImplementationTest, DrawArraysClientSideBuffers) { static const float verts[][4] = { { 12.0f, 23.0f, 34.0f, 45.0f, }, { 56.0f, 67.0f, 78.0f, 89.0f, }, { 13.0f, 24.0f, 35.0f, 46.0f, }, }; struct Cmds { EnableVertexAttribArray enable1; EnableVertexAttribArray enable2; BindBuffer bind_to_emu; BufferData set_size; BufferSubData copy_data1; cmd::SetToken set_token1; VertexAttribPointer set_pointer1; BufferSubData copy_data2; cmd::SetToken set_token2; VertexAttribPointer set_pointer2; DrawArrays draw; BindBuffer restore; }; const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId; const GLuint kAttribIndex1 = 1; const GLuint kAttribIndex2 = 3; const GLint kNumComponents1 = 3; const GLint kNumComponents2 = 2; const GLsizei kClientStride = sizeof(verts[0]); const GLint kFirst = 1; const GLsizei kCount = 2; const GLsizei kSize1 = arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]); const GLsizei kSize2 = arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]); const GLsizei kEmuOffset1 = 0; const GLsizei kEmuOffset2 = kSize1; const GLsizei kTotalSize = kSize1 + kSize2; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.enable1.Init(kAttribIndex1); expected.enable2.Init(kAttribIndex2); expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId); expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW); expected.copy_data1.Init( GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset); expected.set_token1.Init(token++); expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, 0, kEmuOffset1); expected.copy_data2.Init( GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset + kSize1); expected.set_token2.Init(token++); expected.set_pointer2.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, 0, kEmuOffset2); expected.draw.Init(GL_POINTS, kFirst, kCount); expected.restore.Init(GL_ARRAY_BUFFER, 0); gl_->EnableVertexAttribArray(kAttribIndex1); gl_->EnableVertexAttribArray(kAttribIndex2); gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->DrawArrays(GL_POINTS, kFirst, kCount); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, DrawElementsClientSideBuffers) { static const float verts[][4] = { { 12.0f, 23.0f, 34.0f, 45.0f, }, { 56.0f, 67.0f, 78.0f, 89.0f, }, { 13.0f, 24.0f, 35.0f, 46.0f, }, }; static const uint16 indices[] = { 1, 2, }; struct Cmds { EnableVertexAttribArray enable1; EnableVertexAttribArray enable2; BindBuffer bind_to_index_emu; BufferData set_index_size; BufferSubData copy_data0; cmd::SetToken set_token0; BindBuffer bind_to_emu; BufferData set_size; BufferSubData copy_data1; cmd::SetToken set_token1; VertexAttribPointer set_pointer1; BufferSubData copy_data2; cmd::SetToken set_token2; VertexAttribPointer set_pointer2; DrawElements draw; BindBuffer restore; BindBuffer restore_element; }; const GLsizei kIndexSize = sizeof(indices); const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId; const GLuint kEmuIndexBufferId = GLES2Implementation::kClientSideElementArrayId; const GLuint kAttribIndex1 = 1; const GLuint kAttribIndex2 = 3; const GLint kNumComponents1 = 3; const GLint kNumComponents2 = 2; const GLsizei kClientStride = sizeof(verts[0]); const GLsizei kCount = 2; const GLsizei kSize1 = arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]); const GLsizei kSize2 = arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]); const GLsizei kEmuOffset1 = 0; const GLsizei kEmuOffset2 = kSize1; const GLsizei kTotalSize = kSize1 + kSize2; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.enable1.Init(kAttribIndex1); expected.enable2.Init(kAttribIndex2); expected.bind_to_index_emu.Init(GL_ELEMENT_ARRAY_BUFFER, kEmuIndexBufferId); expected.set_index_size.Init( GL_ELEMENT_ARRAY_BUFFER, kIndexSize, 0, 0, GL_DYNAMIC_DRAW); expected.copy_data0.Init( GL_ELEMENT_ARRAY_BUFFER, 0, kIndexSize, kTransferBufferId, offset); offset += kIndexSize; expected.set_token0.Init(token++); expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId); expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW); expected.copy_data1.Init( GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset); offset += kSize1; expected.set_token1.Init(token++); expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, 0, kEmuOffset1); expected.copy_data2.Init( GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset); expected.set_token2.Init(token++); expected.set_pointer2.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, 0, kEmuOffset2); expected.draw.Init(GL_POINTS, kCount, GL_UNSIGNED_SHORT, 0); expected.restore.Init(GL_ARRAY_BUFFER, 0); expected.restore_element.Init(GL_ELEMENT_ARRAY_BUFFER, 0); gl_->EnableVertexAttribArray(kAttribIndex1); gl_->EnableVertexAttribArray(kAttribIndex2); gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->DrawElements(GL_POINTS, kCount, GL_UNSIGNED_SHORT, indices); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, DrawElementsClientSideBuffersServiceSideIndices) { static const float verts[][4] = { { 12.0f, 23.0f, 34.0f, 45.0f, }, { 56.0f, 67.0f, 78.0f, 89.0f, }, { 13.0f, 24.0f, 35.0f, 46.0f, }, }; struct Cmds { EnableVertexAttribArray enable1; EnableVertexAttribArray enable2; BindBuffer bind_to_index; GetMaxValueInBufferCHROMIUM get_max; BindBuffer bind_to_emu; BufferData set_size; BufferSubData copy_data1; cmd::SetToken set_token1; VertexAttribPointer set_pointer1; BufferSubData copy_data2; cmd::SetToken set_token2; VertexAttribPointer set_pointer2; DrawElements draw; BindBuffer restore; }; const GLuint kEmuBufferId = GLES2Implementation::kClientSideArrayId; const GLuint kClientIndexBufferId = 0x789; const GLuint kIndexOffset = 0x40; const GLuint kMaxIndex = 2; const GLuint kAttribIndex1 = 1; const GLuint kAttribIndex2 = 3; const GLint kNumComponents1 = 3; const GLint kNumComponents2 = 2; const GLsizei kClientStride = sizeof(verts[0]); const GLsizei kCount = 2; const GLsizei kSize1 = arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]); const GLsizei kSize2 = arraysize(verts) * kNumComponents2 * sizeof(verts[0][0]); const GLsizei kEmuOffset1 = 0; const GLsizei kEmuOffset2 = kSize1; const GLsizei kTotalSize = kSize1 + kSize2; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.enable1.Init(kAttribIndex1); expected.enable2.Init(kAttribIndex2); expected.bind_to_index.Init(GL_ELEMENT_ARRAY_BUFFER, kClientIndexBufferId); expected.get_max.Init(kClientIndexBufferId, kCount, GL_UNSIGNED_SHORT, kIndexOffset, kTransferBufferId, 0); expected.bind_to_emu.Init(GL_ARRAY_BUFFER, kEmuBufferId); expected.set_size.Init(GL_ARRAY_BUFFER, kTotalSize, 0, 0, GL_DYNAMIC_DRAW); expected.copy_data1.Init( GL_ARRAY_BUFFER, kEmuOffset1, kSize1, kTransferBufferId, offset); offset += kSize1; expected.set_token1.Init(token++); expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, 0, kEmuOffset1); expected.copy_data2.Init( GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, offset); expected.set_token2.Init(token++); expected.set_pointer2.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, 0, kEmuOffset2); expected.draw.Init(GL_POINTS, kCount, GL_UNSIGNED_SHORT, kIndexOffset); expected.restore.Init(GL_ARRAY_BUFFER, 0); EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(kMaxIndex)) .RetiresOnSaturation(); gl_->EnableVertexAttribArray(kAttribIndex1); gl_->EnableVertexAttribArray(kAttribIndex2); gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, kClientIndexBufferId); gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kClientStride, verts); gl_->DrawElements(GL_POINTS, kCount, GL_UNSIGNED_SHORT, reinterpret_cast(kIndexOffset)); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, GetVertexBufferPointerv) { static const float verts[1] = { 0.0f, }; const GLuint kAttribIndex1 = 1; const GLuint kAttribIndex2 = 3; const GLint kNumComponents1 = 3; const GLint kNumComponents2 = 2; const GLsizei kStride1 = 12; const GLsizei kStride2 = 0; const GLuint kBufferId = 0x123; const GLint kOffset2 = 0x456; // Only one set and one get because the client side buffer's info is stored // on the client side. struct Cmds { BindBuffer bind; VertexAttribPointer set_pointer; GetVertexAttribPointerv get_pointer; }; Cmds expected; expected.bind.Init(GL_ARRAY_BUFFER, kBufferId); expected.set_pointer.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kStride2, kOffset2); expected.get_pointer.Init(kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_POINTER, kTransferBufferId, 0); // One call to flush to way for GetVertexAttribPointerv EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(SizedResultHelper(kOffset2))) .RetiresOnSaturation(); // Set one client side buffer. gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kStride1, verts); // Set one VBO gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kStride2, reinterpret_cast(kOffset2)); // now get them both. void* ptr1 = NULL; void* ptr2 = NULL; gl_->GetVertexAttribPointerv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr1); gl_->GetVertexAttribPointerv( kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr2); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); EXPECT_TRUE(static_cast(&verts) == ptr1); // because the service is not running ptr2 is not read. EXPECT_TRUE(ptr2 == reinterpret_cast(kOffset2)); } TEST_F(GLES2ImplementationTest, GetVertexAttrib) { static const float verts[1] = { 0.0f, }; const GLuint kAttribIndex1 = 1; const GLuint kAttribIndex2 = 3; const GLint kNumComponents1 = 3; const GLint kNumComponents2 = 2; const GLsizei kStride1 = 12; const GLsizei kStride2 = 0; const GLuint kBufferId = 0x123; const GLint kOffset2 = 0x456; // Only one set and one get because the client side buffer's info is stored // on the client side. struct Cmds { EnableVertexAttribArray enable; BindBuffer bind; VertexAttribPointer set_pointer; GetVertexAttribiv get1; // for getting the buffer from attrib2 GetVertexAttribfv get2; // for getting the value from attrib1 }; Cmds expected; expected.enable.Init(kAttribIndex1); expected.bind.Init(GL_ARRAY_BUFFER, kBufferId); expected.set_pointer.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kStride2, kOffset2); expected.get1.Init(kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, kTransferBufferId, 0); expected.get2.Init(kAttribIndex1, GL_CURRENT_VERTEX_ATTRIB, kTransferBufferId, 0); FourFloats current_attrib(1.2f, 3.4f, 5.6f, 7.8f); // One call to flush to way for GetVertexAttribiv EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(SizedResultHelper(kBufferId))) .WillOnce(SetMemory(SizedResultHelper(current_attrib))) .RetiresOnSaturation(); gl_->EnableVertexAttribArray(kAttribIndex1); // Set one client side buffer. gl_->VertexAttribPointer(kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kStride1, verts); // Set one VBO gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); gl_->VertexAttribPointer(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, kStride2, reinterpret_cast(kOffset2)); // first get the service side once to see that we make a command GLint buffer_id = 0; GLint enabled = 0; GLint size = 0; GLint stride = 0; GLint type = 0; GLint normalized = 1; float current[4] = { 0.0f, }; gl_->GetVertexAttribiv( kAttribIndex2, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &buffer_id); EXPECT_EQ(kBufferId, static_cast(buffer_id)); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &buffer_id); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &stride); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type); gl_->GetVertexAttribiv( kAttribIndex1, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &normalized); gl_->GetVertexAttribfv( kAttribIndex1, GL_CURRENT_VERTEX_ATTRIB, ¤t[0]); EXPECT_EQ(0, buffer_id); EXPECT_EQ(GL_TRUE, enabled); EXPECT_EQ(kNumComponents1, size); EXPECT_EQ(kStride1, stride); EXPECT_EQ(GL_FLOAT, type); EXPECT_EQ(GL_FALSE, normalized); EXPECT_EQ(0, memcmp(¤t_attrib, ¤t, sizeof(current_attrib))); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_BUFFERS) TEST_F(GLES2ImplementationTest, ReservedIds) { // Only the get error command should be issued. struct Cmds { GetError get; }; Cmds expected; expected.get.Init(kTransferBufferId, 0); // One call to flush to wait for GetError EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .RetiresOnSaturation(); gl_->BindBuffer( GL_ARRAY_BUFFER, GLES2Implementation::kClientSideArrayId); gl_->BindBuffer( GL_ARRAY_BUFFER, GLES2Implementation::kClientSideElementArrayId); GLenum err = gl_->GetError(); EXPECT_EQ(static_cast(GL_INVALID_OPERATION), err); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, ReadPixels2Reads) { struct Cmds { ReadPixels read1; cmd::SetToken set_token1; ReadPixels read2; cmd::SetToken set_token2; }; const GLint kBytesPerPixel = 4; const GLint kWidth = (kTransferBufferSize - GLES2Implementation::kStartingOffset) / kBytesPerPixel; const GLint kHeight = 2; const GLenum kFormat = GL_RGBA; const GLenum kType = GL_UNSIGNED_BYTE; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.read1.Init(0, 0, kWidth, kHeight / 2, kFormat, kType, kTransferBufferId, offset, kTransferBufferId, 0); expected.set_token1.Init(token++); expected.read2.Init(0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, kTransferBufferId, offset, kTransferBufferId, 0); expected.set_token2.Init(token++); scoped_array buffer(new int8[kWidth * kHeight * kBytesPerPixel]); EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(static_cast(1))) .WillOnce(SetMemory(static_cast(1))) .RetiresOnSaturation(); gl_->ReadPixels(0, 0, kWidth, kHeight, kFormat, kType, buffer.get()); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, ReadPixelsBadFormatType) { struct Cmds { ReadPixels read; cmd::SetToken set_token; }; const GLint kBytesPerPixel = 4; const GLint kWidth = 2; const GLint kHeight = 2; const GLenum kFormat = 0; const GLenum kType = 0; int32 token = 1; uint32 offset = GLES2Implementation::kStartingOffset; Cmds expected; expected.read.Init(0, 0, kWidth, kHeight / 2, kFormat, kType, kTransferBufferId, offset, kTransferBufferId, 0); expected.set_token.Init(token++); scoped_array buffer(new int8[kWidth * kHeight * kBytesPerPixel]); EXPECT_CALL(*command_buffer_, OnFlush(_)) .Times(1) .RetiresOnSaturation(); gl_->ReadPixels(0, 0, kWidth, kHeight, kFormat, kType, buffer.get()); } TEST_F(GLES2ImplementationTest, MapUnmapBufferSubDataCHROMIUM) { struct Cmds { BufferSubData buf; cmd::SetToken set_token; }; const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER; const GLintptr kOffset = 15; const GLsizeiptr kSize = 16; int32 token = 1; uint32 offset = 0; Cmds expected; expected.buf.Init( kTarget, kOffset, kSize, kTransferBufferId, offset); expected.set_token.Init(token++); void* mem = gl_->MapBufferSubDataCHROMIUM( kTarget, kOffset, kSize, GL_WRITE_ONLY); ASSERT_TRUE(mem != NULL); gl_->UnmapBufferSubDataCHROMIUM(mem); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, MapUnmapBufferSubDataCHROMIUMBadArgs) { const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER; const GLintptr kOffset = 15; const GLsizeiptr kSize = 16; // Calls to flush to wait for GetError EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .RetiresOnSaturation(); void* mem; mem = gl_->MapBufferSubDataCHROMIUM(kTarget, -1, kSize, GL_WRITE_ONLY); ASSERT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapBufferSubDataCHROMIUM(kTarget, kOffset, -1, GL_WRITE_ONLY); ASSERT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapBufferSubDataCHROMIUM(kTarget, kOffset, kSize, GL_READ_ONLY); ASSERT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_ENUM), gl_->GetError()); const char* kPtr = "something"; gl_->UnmapBufferSubDataCHROMIUM(kPtr); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); } TEST_F(GLES2ImplementationTest, MapUnmapTexSubImage2DCHROMIUM) { struct Cmds { TexSubImage2D tex; cmd::SetToken set_token; }; const GLint kLevel = 1; const GLint kXOffset = 2; const GLint kYOffset = 3; const GLint kWidth = 4; const GLint kHeight = 5; const GLenum kFormat = GL_RGBA; const GLenum kType = GL_UNSIGNED_BYTE; int32 token = 1; uint32 offset = 0; Cmds expected; expected.tex.Init( GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, kHeight, kFormat, kType, kTransferBufferId, offset); expected.set_token.Init(token++); void* mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, kHeight, kFormat, kType, GL_WRITE_ONLY); ASSERT_TRUE(mem != NULL); gl_->UnmapTexSubImage2DCHROMIUM(mem); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } TEST_F(GLES2ImplementationTest, MapUnmapTexSubImage2DCHROMIUMBadArgs) { const GLint kLevel = 1; const GLint kXOffset = 2; const GLint kYOffset = 3; const GLint kWidth = 4; const GLint kHeight = 5; const GLenum kFormat = GL_RGBA; const GLenum kType = GL_UNSIGNED_BYTE; // Calls to flush to wait for GetError EXPECT_CALL(*command_buffer_, OnFlush(_)) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) .RetiresOnSaturation(); void* mem; mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, -1, kXOffset, kYOffset, kWidth, kHeight, kFormat, kType, GL_WRITE_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, -1, kYOffset, kWidth, kHeight, kFormat, kType, GL_WRITE_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, kXOffset, -1, kWidth, kHeight, kFormat, kType, GL_WRITE_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, -1, kHeight, kFormat, kType, GL_WRITE_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, -1, kFormat, kType, GL_WRITE_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); mem = gl_->MapTexSubImage2DCHROMIUM( GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, kHeight, kFormat, kType, GL_READ_ONLY); EXPECT_TRUE(mem == NULL); EXPECT_EQ(static_cast(GL_INVALID_ENUM), gl_->GetError()); const char* kPtr = "something"; gl_->UnmapTexSubImage2DCHROMIUM(kPtr); EXPECT_EQ(static_cast(GL_INVALID_VALUE), gl_->GetError()); } } // namespace gles2 } // namespace gpu