| // Copyright (c) 2011 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 <GLES2/gl2ext.h> |
| #include "gpu/command_buffer/common/command_buffer.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| #if !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS |
| #endif |
| |
| 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 bool Initialize(base::SharedMemory* buffer, int32 size) { |
| GPU_NOTREACHED(); |
| return false; |
| } |
| |
| virtual Buffer GetRingBuffer() { |
| return ring_buffer_buffer_; |
| } |
| |
| virtual State GetState() { |
| return state_; |
| } |
| |
| virtual State GetLastState() { |
| return state_; |
| } |
| |
| virtual void Flush(int32 put_offset) { |
| state_.put_offset = put_offset; |
| } |
| |
| virtual State FlushSync(int32 put_offset, int32 last_known_get) { |
| 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, int32 id_request) { |
| transfer_buffer_.reset(new int8[size]); |
| transfer_buffer_buffer_.ptr = transfer_buffer_.get(); |
| transfer_buffer_buffer_.size = size; |
| return kTransferBufferId; |
| } |
| |
| virtual void DestroyTransferBuffer(int32 /* id */) { |
| GPU_NOTREACHED(); |
| } |
| |
| virtual Buffer GetTransferBuffer(int32 id) { |
| GPU_DCHECK_EQ(id, kTransferBufferId); |
| return transfer_buffer_buffer_; |
| } |
| |
| virtual int32 RegisterTransferBuffer(base::SharedMemory* shared_memory, |
| size_t size, |
| int32 id_request) { |
| GPU_NOTREACHED(); |
| return -1; |
| } |
| |
| virtual void SetToken(int32 token) { |
| GPU_NOTREACHED(); |
| state_.token = token; |
| } |
| |
| virtual void SetParseError(error::Error error) { |
| GPU_NOTREACHED(); |
| state_.error = error; |
| } |
| |
| virtual void SetContextLostReason(error::ContextLostReason reason) { |
| GPU_NOTREACHED(); |
| state_.context_lost_reason = reason; |
| } |
| |
| virtual void OnFlush(void* transfer_buffer) = 0; |
| |
| private: |
| scoped_array<int8> transfer_buffer_; |
| Buffer transfer_buffer_buffer_; |
| scoped_array<CommandBufferEntry> 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)); |
| MOCK_METHOD1(DestroyTransferBuffer, void(int32 id)); |
| }; |
| |
| // GCC requires these declarations, but MSVC requires they not be present |
| #ifndef _MSC_VER |
| const int32 GLES2MockCommandBufferHelper::kTransferBufferId; |
| #endif |
| |
| namespace gles2 { |
| |
| using testing::_; |
| using testing::DoAll; |
| using testing::InSequence; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::Sequence; |
| using testing::Truly; |
| using testing::Return; |
| |
| ACTION_P(SetMemory, obj) { |
| memcpy(arg0, &obj, sizeof(obj)); |
| } |
| |
| ACTION_P2(SetMemoryAtOffset, offset, obj) { |
| memcpy(static_cast<char*>(arg0) + offset, &obj, sizeof(obj)); |
| } |
| |
| ACTION_P3(SetMemoryAtOffsetFromArray, offset, array, size) { |
| memcpy(static_cast<char*>(arg0) + offset, array, size); |
| } |
| |
| // Used to help set the transfer buffer result to SizedResult of a single value. |
| template <typename T> |
| 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 = 400; |
| 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 kMaxCombinedTextureImageUnits = 8; |
| static const GLint kMaxCubeMapTextureSize = 64; |
| static const GLint kMaxFragmentUniformVectors = 16; |
| static const GLint kMaxRenderbufferSize = 64; |
| static const GLint kMaxTextureImageUnits = 8; |
| static const GLint kMaxTextureSize = 128; |
| static const GLint kMaxVaryingVectors = 8; |
| static const GLint kMaxVertexAttribs = 8; |
| static const GLint kMaxVertexTextureImageUnits = 0; |
| static const GLint kMaxVertexUniformVectors = 128; |
| static const GLint kNumCompressedTextureFormats = 0; |
| static const GLint kNumShaderBinaryFormats = 0; |
| static const GLuint kStartId = 1024; |
| |
| GLES2ImplementationTest() |
| : commands_(NULL), |
| token_(0), |
| offset_(0) { |
| } |
| |
| virtual void SetUp() { |
| Initialize(false, true); |
| } |
| |
| virtual void TearDown() { |
| } |
| |
| void Initialize(bool shared_resources, bool bind_generates_resource) { |
| offset_ = GLES2Implementation::kStartingOffset; |
| |
| command_buffer_.reset(new MockGLES2CommandBuffer()); |
| command_buffer_->Initialize(kCommandBufferSizeBytes); |
| |
| EXPECT_EQ(kTransferBufferId, |
| command_buffer_->CreateTransferBuffer(kTransferBufferSize, -1)); |
| transfer_buffer_ = command_buffer_->GetTransferBuffer(kTransferBufferId); |
| ClearTransferBuffer(); |
| |
| helper_.reset(new GLES2CmdHelper(command_buffer_.get())); |
| helper_->Initialize(kCommandBufferSizeBytes); |
| |
| GLES2Implementation::GLState state; |
| state.max_combined_texture_image_units = kMaxCombinedTextureImageUnits; |
| state.max_cube_map_texture_size = kMaxCubeMapTextureSize; |
| state.max_fragment_uniform_vectors = kMaxFragmentUniformVectors; |
| state.max_renderbuffer_size = kMaxRenderbufferSize; |
| state.max_texture_image_units = kMaxTextureImageUnits; |
| state.max_texture_size = kMaxTextureSize; |
| state.max_varying_vectors = kMaxVaryingVectors; |
| state.max_vertex_attribs = kMaxVertexAttribs; |
| state.max_vertex_texture_image_units = kMaxVertexTextureImageUnits; |
| state.max_vertex_uniform_vectors = kMaxVertexUniformVectors; |
| state.num_compressed_texture_formats = kNumCompressedTextureFormats; |
| state.num_shader_binary_formats = kNumShaderBinaryFormats; |
| |
| // This just happens to work for now because GLState has 1 GLint per |
| // state. If GLState gets more complicated this code will need to get |
| // more complicated. |
| AllocateTransferBuffer(sizeof(state)); // in |
| uint32 offset = AllocateTransferBuffer(sizeof(state)); // out |
| |
| { |
| InSequence sequence; |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemoryAtOffset(offset, state)) |
| .RetiresOnSaturation(); |
| GetNextToken(); // eat the token that starting up will use. |
| |
| // Must match StrictSharedIdHandler::kNumIdsToGet. |
| GLuint num_ids = 2048; |
| scoped_array<GLuint> all_ids(new GLuint[num_ids]); |
| if (shared_resources) { |
| if (!bind_generates_resource) { |
| GLuint start = kStartId; |
| GLuint max_num_per = MaxTransferBufferSize() / sizeof(GLuint); |
| GLuint* ids = all_ids.get(); |
| for (GLuint ii = 0; ii < num_ids; ++ii) { |
| ids[ii] = start + ii; |
| } |
| while (num_ids) { |
| GLuint num = std::min(num_ids, max_num_per); |
| size_t size = num * sizeof(ids[0]); |
| uint32 offset = AllocateTransferBuffer(size); |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemoryAtOffsetFromArray(offset, ids, size)) |
| .RetiresOnSaturation(); |
| GetNextToken(); |
| start += num; |
| ids += num; |
| num_ids -= num; |
| } |
| } |
| } |
| |
| gl_.reset(new GLES2Implementation( |
| helper_.get(), |
| kTransferBufferSize, |
| transfer_buffer_.ptr, |
| kTransferBufferId, |
| shared_resources, |
| bind_generates_resource)); |
| } |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| helper_->CommandBufferHelper::Finish(); |
| Buffer ring_buffer = command_buffer_->GetRingBuffer(); |
| commands_ = static_cast<CommandBufferEntry*>(ring_buffer.ptr) + |
| command_buffer_->GetState().put_offset; |
| ClearCommands(); |
| } |
| |
| const void* GetPut() { |
| return helper_->GetSpace(0); |
| } |
| |
| size_t MaxTransferBufferSize() { |
| return kTransferBufferSize - GLES2Implementation::kStartingOffset; |
| } |
| |
| void ClearCommands() { |
| Buffer ring_buffer = command_buffer_->GetRingBuffer(); |
| memset(ring_buffer.ptr, kInitialValue, ring_buffer.size); |
| } |
| |
| bool NoCommandsWritten() { |
| return static_cast<const uint8*>(static_cast<const void*>(commands_))[0] == |
| kInitialValue; |
| } |
| |
| void ClearTransferBuffer() { |
| memset(transfer_buffer_.ptr, kInitialValue, kTransferBufferSize); |
| } |
| |
| static unsigned int RoundToAlignment(unsigned int size) { |
| return (size + GLES2Implementation::kAlignment - 1) & |
| ~(GLES2Implementation::kAlignment - 1); |
| } |
| |
| int GetNextToken() { |
| return ++token_; |
| } |
| |
| uint32 AllocateTransferBuffer(size_t size) { |
| if (offset_ + size > kTransferBufferSize) { |
| offset_ = GLES2Implementation::kStartingOffset; |
| } |
| uint32 offset = offset_; |
| offset_ += RoundToAlignment(size); |
| return offset; |
| } |
| |
| void* GetTransferAddressFromOffset(uint32 offset, size_t size) { |
| EXPECT_LE(offset + size, transfer_buffer_.size); |
| return static_cast<int8*>(transfer_buffer_.ptr) + offset; |
| } |
| |
| template <typename T> |
| T* GetTransferAddressFromOffsetAs(uint32 offset, size_t size) { |
| return static_cast<T*>(GetTransferAddressFromOffset(offset, size)); |
| } |
| |
| Buffer transfer_buffer_; |
| CommandBufferEntry* commands_; |
| scoped_ptr<MockGLES2CommandBuffer> command_buffer_; |
| scoped_ptr<GLES2CmdHelper> helper_; |
| Sequence sequence_; |
| scoped_ptr<GLES2Implementation> gl_; |
| int token_; |
| uint32 offset_; |
| }; |
| |
| class GLES2ImplementationStrictSharedTest : public GLES2ImplementationTest { |
| protected: |
| virtual void SetUp() { |
| Initialize(true, false); |
| } |
| }; |
| |
| // 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); |
| const size_t kPaddedString3Size = RoundToAlignment(kString3Size); |
| 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; |
| }; |
| Cmds expected; |
| expected.set_bucket_size.Init(kBucketId, kSourceSize); |
| expected.set_bucket_data1.Init( |
| kBucketId, 0, kString1Size, kTransferBufferId, |
| AllocateTransferBuffer(kPaddedString1Size)); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_bucket_data2.Init( |
| kBucketId, kString1Size, kString2Size, kTransferBufferId, |
| AllocateTransferBuffer(kPaddedString2Size)); |
| expected.set_token2.Init(GetNextToken()); |
| expected.set_bucket_data3.Init( |
| kBucketId, kString1Size + kString2Size, |
| kString3Size, kTransferBufferId, |
| AllocateTransferBuffer(kPaddedString3Size)); |
| expected.set_token3.Init(GetNextToken()); |
| 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; |
| }; |
| uint32 offset = AllocateTransferBuffer(sizeof(kString)); |
| 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(GetNextToken()); |
| 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<size_t>(length)); |
| EXPECT_STREQ(kString.str, buf); |
| EXPECT_EQ(buf[sizeof(kString)], kBad); |
| } |
| |
| #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| |
| 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; |
| 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, |
| AllocateTransferBuffer(kSize1)); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, |
| GL_FLOAT, GL_FALSE, 0, kEmuOffset1); |
| expected.copy_data2.Init( |
| GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, |
| AllocateTransferBuffer(kSize2)); |
| expected.set_token2.Init(GetNextToken()); |
| 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; |
| 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, |
| AllocateTransferBuffer(kIndexSize)); |
| expected.set_token0.Init(GetNextToken()); |
| 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, |
| AllocateTransferBuffer(kSize1)); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, |
| GL_FLOAT, GL_FALSE, 0, kEmuOffset1); |
| expected.copy_data2.Init( |
| GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, |
| AllocateTransferBuffer(kSize2)); |
| expected.set_token2.Init(GetNextToken()); |
| 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; |
| 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, |
| AllocateTransferBuffer(kSize1)); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_pointer1.Init(kAttribIndex1, kNumComponents1, |
| GL_FLOAT, GL_FALSE, 0, kEmuOffset1); |
| expected.copy_data2.Init( |
| GL_ARRAY_BUFFER, kEmuOffset2, kSize2, kTransferBufferId, |
| AllocateTransferBuffer(kSize2)); |
| expected.set_token2.Init(GetNextToken()); |
| 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<const void*>(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<uint32>(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<const void*>(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<const void*>(&verts) == ptr1); |
| // because the service is not running ptr2 is not read. |
| EXPECT_TRUE(ptr2 == reinterpret_cast<void*>(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<GLuint>(kBufferId))) |
| .WillOnce(SetMemory(SizedResultHelper<FourFloats>(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<const void*>(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<GLuint>(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))); |
| } |
| |
| 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<GLenum>(GL_INVALID_OPERATION), err); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| #endif // defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| |
| 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; |
| |
| Cmds expected; |
| expected.read1.Init( |
| 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, |
| AllocateTransferBuffer(kWidth * kHeight / 2 * kBytesPerPixel), |
| kTransferBufferId, 0); |
| expected.set_token1.Init(GetNextToken()); |
| expected.read2.Init( |
| 0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, |
| AllocateTransferBuffer(kWidth * kHeight / 2 * kBytesPerPixel), |
| kTransferBufferId, 0); |
| expected.set_token2.Init(GetNextToken()); |
| scoped_array<int8> buffer(new int8[kWidth * kHeight * kBytesPerPixel]); |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(static_cast<uint32>(1))) |
| .WillOnce(SetMemory(static_cast<uint32>(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; |
| |
| Cmds expected; |
| expected.read.Init( |
| 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, |
| AllocateTransferBuffer(kWidth * kHeight * kBytesPerPixel), |
| kTransferBufferId, 0); |
| expected.set_token.Init(GetNextToken()); |
| scoped_array<int8> 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, FreeUnusedSharedMemory) { |
| struct Cmds { |
| BufferSubData buf; |
| cmd::SetToken set_token; |
| }; |
| const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER; |
| const GLintptr kOffset = 15; |
| const GLsizeiptr kSize = 16; |
| |
| uint32 offset = 0; |
| Cmds expected; |
| expected.buf.Init( |
| kTarget, kOffset, kSize, kTransferBufferId, offset); |
| expected.set_token.Init(GetNextToken()); |
| |
| void* mem = gl_->MapBufferSubDataCHROMIUM( |
| kTarget, kOffset, kSize, GL_WRITE_ONLY); |
| ASSERT_TRUE(mem != NULL); |
| gl_->UnmapBufferSubDataCHROMIUM(mem); |
| EXPECT_CALL(*command_buffer_, DestroyTransferBuffer(_))\ |
| .Times(1) |
| .RetiresOnSaturation(); |
| gl_->FreeUnusedSharedMemory(); |
| } |
| |
| 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; |
| |
| uint32 offset = 0; |
| Cmds expected; |
| expected.buf.Init( |
| kTarget, kOffset, kSize, kTransferBufferId, offset); |
| expected.set_token.Init(GetNextToken()); |
| |
| 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<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| mem = gl_->MapBufferSubDataCHROMIUM(kTarget, kOffset, -1, GL_WRITE_ONLY); |
| ASSERT_TRUE(mem == NULL); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| mem = gl_->MapBufferSubDataCHROMIUM(kTarget, kOffset, kSize, GL_READ_ONLY); |
| ASSERT_TRUE(mem == NULL); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), gl_->GetError()); |
| const char* kPtr = "something"; |
| gl_->UnmapBufferSubDataCHROMIUM(kPtr); |
| EXPECT_EQ(static_cast<GLenum>(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; |
| |
| uint32 offset = 0; |
| Cmds expected; |
| expected.tex.Init( |
| GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, kHeight, kFormat, |
| kType, kTransferBufferId, offset, GL_FALSE); |
| expected.set_token.Init(GetNextToken()); |
| |
| 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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(GL_INVALID_ENUM), gl_->GetError()); |
| const char* kPtr = "something"; |
| gl_->UnmapTexSubImage2DCHROMIUM(kPtr); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetMultipleIntegervCHROMIUMValidArgs) { |
| const GLenum pnames[] = { |
| GL_DEPTH_WRITEMASK, |
| GL_COLOR_WRITEMASK, |
| GL_STENCIL_WRITEMASK, |
| }; |
| const GLint num_results = 6; |
| GLint results[num_results + 1]; |
| struct Cmds { |
| GetMultipleIntegervCHROMIUM get_multiple; |
| cmd::SetToken set_token; |
| }; |
| const GLsizei kNumPnames = arraysize(pnames); |
| const GLsizeiptr kResultsSize = num_results * sizeof(results[0]); |
| const uint32 kPnamesOffset = |
| AllocateTransferBuffer(kNumPnames * sizeof(pnames[0])); |
| const uint32 kResultsOffset = AllocateTransferBuffer(kResultsSize); |
| Cmds expected; |
| expected.get_multiple.Init( |
| kTransferBufferId, kPnamesOffset, kNumPnames, |
| kTransferBufferId, kResultsOffset, kResultsSize); |
| expected.set_token.Init(GetNextToken()); |
| |
| const GLint kSentinel = 0x12345678; |
| memset(results, 0, sizeof(results)); |
| results[num_results] = kSentinel; |
| const GLint returned_results[] = { |
| 1, 0, 1, 0, 1, -1, |
| }; |
| // One call to flush to wait for results |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemoryAtOffsetFromArray( |
| kResultsOffset, returned_results, sizeof(returned_results))) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| gl_->GetMultipleIntegervCHROMIUM( |
| &pnames[0], kNumPnames, &results[0], kResultsSize); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(0, memcmp(&returned_results, results, sizeof(returned_results))); |
| EXPECT_EQ(kSentinel, results[num_results]); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetMultipleIntegervCHROMIUMBadArgs) { |
| GLenum pnames[] = { |
| GL_DEPTH_WRITEMASK, |
| GL_COLOR_WRITEMASK, |
| GL_STENCIL_WRITEMASK, |
| }; |
| const GLint num_results = 6; |
| GLint results[num_results + 1]; |
| const GLsizei kNumPnames = arraysize(pnames); |
| const GLsizeiptr kResultsSize = num_results * sizeof(results[0]); |
| |
| // 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(); |
| |
| const GLint kSentinel = 0x12345678; |
| memset(results, 0, sizeof(results)); |
| results[num_results] = kSentinel; |
| // try bad size. |
| gl_->GetMultipleIntegervCHROMIUM( |
| &pnames[0], kNumPnames, &results[0], kResultsSize + 1); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| EXPECT_EQ(0, results[0]); |
| EXPECT_EQ(kSentinel, results[num_results]); |
| // try bad size. |
| ClearCommands(); |
| gl_->GetMultipleIntegervCHROMIUM( |
| &pnames[0], kNumPnames, &results[0], kResultsSize - 1); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| EXPECT_EQ(0, results[0]); |
| EXPECT_EQ(kSentinel, results[num_results]); |
| // try uncleared results. |
| ClearCommands(); |
| results[2] = 1; |
| gl_->GetMultipleIntegervCHROMIUM( |
| &pnames[0], kNumPnames, &results[0], kResultsSize); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| EXPECT_EQ(0, results[0]); |
| EXPECT_EQ(kSentinel, results[num_results]); |
| // try bad enum results. |
| ClearCommands(); |
| results[2] = 0; |
| pnames[1] = GL_TRUE; |
| gl_->GetMultipleIntegervCHROMIUM( |
| &pnames[0], kNumPnames, &results[0], kResultsSize); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), gl_->GetError()); |
| EXPECT_EQ(0, results[0]); |
| EXPECT_EQ(kSentinel, results[num_results]); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetProgramInfoCHROMIUMGoodArgs) { |
| const uint32 kBucketId = 1; // This id is hardcoded into GLES2Implemenation |
| const GLuint kProgramId = 123; |
| const char kBad = 0x12; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| memset(buf, kBad, sizeof(buf)); |
| uint32 offset = AllocateTransferBuffer(sizeof(kString)); |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(uint32(sizeof(kString)))) |
| .WillOnce(SetMemoryAtOffset(offset, kString)) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| GetProgramInfoCHROMIUM get_program_info; |
| cmd::GetBucketSize get_bucket_size; |
| cmd::GetBucketData get_bucket_data; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_program_info.Init(kProgramId, kBucketId); |
| expected.get_bucket_size.Init(kBucketId, kTransferBufferId, 0); |
| expected.get_bucket_data.Init( |
| kBucketId, 0, sizeof(kString), kTransferBufferId, offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_bucket_size2.Init(kBucketId, 0); |
| gl_->GetProgramInfoCHROMIUM(kProgramId, sizeof(buf), &size, &buf); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| EXPECT_EQ(sizeof(kString), static_cast<size_t>(size)); |
| EXPECT_STREQ(kString.str, buf); |
| EXPECT_EQ(buf[sizeof(kString)], kBad); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetProgramInfoCHROMIUMBadArgs) { |
| const uint32 kBucketId = 1; // This id is hardcoded into GLES2Implemenation |
| const GLuint kProgramId = 123; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| uint32 offset = AllocateTransferBuffer(sizeof(kString)); |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(uint32(sizeof(kString)))) |
| .WillOnce(SetMemoryAtOffset(offset, kString)) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| // try bufsize not big enough. |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| GetProgramInfoCHROMIUM get_program_info; |
| cmd::GetBucketSize get_bucket_size; |
| cmd::GetBucketData get_bucket_data; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_program_info.Init(kProgramId, kBucketId); |
| expected.get_bucket_size.Init(kBucketId, kTransferBufferId, 0); |
| expected.get_bucket_data.Init( |
| kBucketId, 0, sizeof(kString), kTransferBufferId, offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_bucket_size2.Init(kBucketId, 0); |
| gl_->GetProgramInfoCHROMIUM(kProgramId, 6, &size, &buf); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), gl_->GetError()); |
| ClearCommands(); |
| |
| // try bad bufsize |
| gl_->GetProgramInfoCHROMIUM(kProgramId, -1, &size, &buf); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| ClearCommands(); |
| // try no size ptr. |
| gl_->GetProgramInfoCHROMIUM(kProgramId, sizeof(buf), NULL, &buf); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| } |
| |
| // Test that things are cached |
| TEST_F(GLES2ImplementationTest, GetIntegerCacheRead) { |
| struct PNameValue { |
| GLenum pname; |
| GLint expected; |
| }; |
| const PNameValue pairs[] = { |
| { GL_ACTIVE_TEXTURE, GL_TEXTURE0, }, |
| { GL_TEXTURE_BINDING_2D, 0, }, |
| { GL_TEXTURE_BINDING_CUBE_MAP, 0, }, |
| { GL_FRAMEBUFFER_BINDING, 0, }, |
| { GL_RENDERBUFFER_BINDING, 0, }, |
| { GL_ARRAY_BUFFER_BINDING, 0, }, |
| { GL_ELEMENT_ARRAY_BUFFER_BINDING, 0, }, |
| { GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, kMaxCombinedTextureImageUnits, }, |
| { GL_MAX_CUBE_MAP_TEXTURE_SIZE, kMaxCubeMapTextureSize, }, |
| { GL_MAX_FRAGMENT_UNIFORM_VECTORS, kMaxFragmentUniformVectors, }, |
| { GL_MAX_RENDERBUFFER_SIZE, kMaxRenderbufferSize, }, |
| { GL_MAX_TEXTURE_IMAGE_UNITS, kMaxTextureImageUnits, }, |
| { GL_MAX_TEXTURE_SIZE, kMaxTextureSize, }, |
| { GL_MAX_VARYING_VECTORS, kMaxVaryingVectors, }, |
| { GL_MAX_VERTEX_ATTRIBS, kMaxVertexAttribs, }, |
| { GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, kMaxVertexTextureImageUnits, }, |
| { GL_MAX_VERTEX_UNIFORM_VECTORS, kMaxVertexUniformVectors, }, |
| { GL_NUM_COMPRESSED_TEXTURE_FORMATS, kNumCompressedTextureFormats, }, |
| { GL_NUM_SHADER_BINARY_FORMATS, kNumShaderBinaryFormats, }, |
| }; |
| size_t num_pairs = sizeof(pairs) / sizeof(pairs[0]); |
| for (size_t ii = 0; ii < num_pairs; ++ii) { |
| const PNameValue& pv = pairs[ii]; |
| GLint v = -1; |
| gl_->GetIntegerv(pv.pname, &v); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(pv.expected, v); |
| } |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetIntegerCacheWrite) { |
| struct PNameValue { |
| GLenum pname; |
| GLint expected; |
| }; |
| gl_->ActiveTexture(GL_TEXTURE4); |
| gl_->BindBuffer(GL_ARRAY_BUFFER, 2); |
| gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 3); |
| gl_->BindFramebuffer(GL_FRAMEBUFFER, 4); |
| gl_->BindRenderbuffer(GL_RENDERBUFFER, 5); |
| gl_->BindTexture(GL_TEXTURE_2D, 6); |
| gl_->BindTexture(GL_TEXTURE_CUBE_MAP, 7); |
| |
| const PNameValue pairs[] = { |
| { GL_ACTIVE_TEXTURE, GL_TEXTURE4, }, |
| { GL_ARRAY_BUFFER_BINDING, 2, }, |
| { GL_ELEMENT_ARRAY_BUFFER_BINDING, 3, }, |
| { GL_FRAMEBUFFER_BINDING, 4, }, |
| { GL_RENDERBUFFER_BINDING, 5, }, |
| { GL_TEXTURE_BINDING_2D, 6, }, |
| { GL_TEXTURE_BINDING_CUBE_MAP, 7, }, |
| }; |
| size_t num_pairs = sizeof(pairs) / sizeof(pairs[0]); |
| for (size_t ii = 0; ii < num_pairs; ++ii) { |
| const PNameValue& pv = pairs[ii]; |
| GLint v = -1; |
| gl_->GetIntegerv(pv.pname, &v); |
| EXPECT_EQ(pv.expected, v); |
| } |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| } |
| |
| static bool ComputeImageDataSizes( |
| int width, int height, int format, int type, int unpack_alignment, |
| uint32* size, uint32* unpadded_row_size, uint32* padded_row_size) { |
| uint32 temp_size; |
| if (!GLES2Util::ComputeImageDataSize( |
| width, 1, format, type, unpack_alignment, &temp_size)) { |
| return false; |
| } |
| *unpadded_row_size = temp_size; |
| if (!GLES2Util::ComputeImageDataSize( |
| width, 2, format, type, unpack_alignment, &temp_size)) { |
| return false; |
| } |
| *padded_row_size = temp_size - *unpadded_row_size; |
| return GLES2Util::ComputeImageDataSize( |
| width, height, format, type, unpack_alignment, size); |
| } |
| |
| static bool CheckRect( |
| int width, int height, GLenum format, GLenum type, int alignment, |
| bool flip_y, const uint8* r1, const uint8* r2) { |
| uint32 size = 0; |
| uint32 unpadded_row_size = 0; |
| uint32 padded_row_size = 0; |
| if (!ComputeImageDataSizes( |
| width, height, format, type, alignment, &size, &unpadded_row_size, |
| &padded_row_size)) { |
| return false; |
| } |
| |
| int r2_stride = flip_y ? |
| -static_cast<int>(padded_row_size) : |
| static_cast<int>(padded_row_size); |
| r2 = flip_y ? (r2 + (height - 1) * padded_row_size) : r2; |
| |
| for (int y = 0; y < height; ++y) { |
| if (memcmp(r1, r2, unpadded_row_size) != 0) { |
| return false; |
| } |
| r1 += padded_row_size; |
| r2 += r2_stride; |
| } |
| return true; |
| } |
| |
| ACTION_P8(CheckRectAction, width, height, format, type, alignment, flip_y, |
| r1, r2) { |
| EXPECT_TRUE(CheckRect( |
| width, height, format, type, alignment, flip_y, r1, r2)); |
| } |
| |
| // Test TexImage2D with and without flip_y |
| TEST_F(GLES2ImplementationTest, TexImage2D) { |
| struct Cmds { |
| TexImage2D tex_image_2d; |
| cmd::SetToken set_token; |
| }; |
| struct Cmds2 { |
| TexImage2D tex_image_2d; |
| cmd::SetToken set_token; |
| }; |
| const GLenum kTarget = GL_TEXTURE_2D; |
| const GLint kLevel = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLsizei kWidth = 3; |
| const GLsizei kHeight = 4; |
| const GLint kBorder = 0; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| static uint8 pixels[] = { |
| 11, 12, 13, 13, 14, 15, 15, 16, 17, 101, 102, 103, |
| 21, 22, 23, 23, 24, 25, 25, 26, 27, 201, 202, 203, |
| 31, 32, 33, 33, 34, 35, 35, 36, 37, 123, 124, 125, |
| 41, 42, 43, 43, 44, 45, 45, 46, 47, |
| }; |
| uint32 offset = AllocateTransferBuffer(sizeof(pixels)); |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| kTransferBufferId, offset); |
| expected.set_token.Init(GetNextToken()); |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(CheckRect( |
| kWidth, kHeight, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| pixels, GetTransferAddressFromOffsetAs<uint8>(offset, sizeof(pixels)))); |
| |
| ClearCommands(); |
| uint32 offset2 = AllocateTransferBuffer(sizeof(pixels)); |
| Cmds2 expected2; |
| expected2.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| kTransferBufferId, offset2); |
| expected2.set_token.Init(GetNextToken()); |
| const void* commands2 = GetPut(); |
| gl_->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, GL_TRUE); |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels); |
| EXPECT_EQ(0, memcmp(&expected2, commands2, sizeof(expected2))); |
| EXPECT_TRUE(CheckRect( |
| kWidth, kHeight, kFormat, kType, kPixelStoreUnpackAlignment, true, |
| pixels, GetTransferAddressFromOffsetAs<uint8>(offset2, sizeof(pixels)))); |
| } |
| |
| // Test TexImage2D with 2 writes |
| TEST_F(GLES2ImplementationTest, TexImage2D2Writes) { |
| struct Cmds { |
| TexImage2D tex_image_2d; |
| TexSubImage2D tex_sub_image_2d1; |
| cmd::SetToken set_token1; |
| TexSubImage2D tex_sub_image_2d2; |
| cmd::SetToken set_token2; |
| }; |
| const GLenum kTarget = GL_TEXTURE_2D; |
| const GLint kLevel = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLint kBorder = 0; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kWidth = 3; |
| |
| uint32 size = 0; |
| uint32 unpadded_row_size = 0; |
| uint32 padded_row_size = 0; |
| ASSERT_TRUE(ComputeImageDataSizes( |
| kWidth, 2, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| const GLsizei kHeight = (MaxTransferBufferSize() / padded_row_size) * 2; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSize( |
| kWidth, kHeight, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size)); |
| uint32 half_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSize( |
| kWidth, kHeight / 2, kFormat, kType, kPixelStoreUnpackAlignment, |
| &half_size)); |
| |
| scoped_array<uint8> pixels(new uint8[size]); |
| for (uint32 ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8>(ii); |
| } |
| uint32 offset1 = AllocateTransferBuffer(half_size); |
| uint32 offset2 = AllocateTransferBuffer(half_size); |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| 0, 0); |
| expected.tex_sub_image_2d1.Init( |
| kTarget, kLevel, 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, offset1, true); |
| expected.set_token1.Init(GetNextToken()); |
| expected.tex_sub_image_2d2.Init( |
| kTarget, kLevel, 0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, offset2, true); |
| expected.set_token2.Init(GetNextToken()); |
| |
| // TODO(gman): Make it possible to run this test |
| // EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| // .WillOnce(CheckRectAction( |
| // kWidth, kHeight / 2, kFormat, kType, kPixelStoreUnpackAlignment, |
| // false, pixels.get(), |
| // GetTransferAddressFromOffsetAs<uint8>(offset1, half_size))) |
| // .RetiresOnSaturation(); |
| |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels.get()); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(CheckRect( |
| kWidth, kHeight / 2, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| pixels.get() + kHeight / 2 * padded_row_size, |
| GetTransferAddressFromOffsetAs<uint8>(offset2, half_size))); |
| |
| ClearCommands(); |
| const void* commands2 = GetPut(); |
| uint32 offset3 = AllocateTransferBuffer(half_size); |
| uint32 offset4 = AllocateTransferBuffer(half_size); |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| 0, 0); |
| expected.tex_sub_image_2d1.Init( |
| kTarget, kLevel, 0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, offset3, true); |
| expected.set_token1.Init(GetNextToken()); |
| expected.tex_sub_image_2d2.Init( |
| kTarget, kLevel, 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| kTransferBufferId, offset4, true); |
| expected.set_token2.Init(GetNextToken()); |
| |
| // TODO(gman): Make it possible to run this test |
| // EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| // .WillOnce(CheckRectAction( |
| // kWidth, kHeight / 2, kFormat, kType, kPixelStoreUnpackAlignment, |
| // true, pixels.get(), |
| // GetTransferAddressFromOffsetAs<uint8>(offset3, half_size))) |
| // .RetiresOnSaturation(); |
| |
| gl_->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, GL_TRUE); |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels.get()); |
| EXPECT_EQ(0, memcmp(&expected, commands2, sizeof(expected))); |
| EXPECT_TRUE(CheckRect( |
| kWidth, kHeight / 2, kFormat, kType, kPixelStoreUnpackAlignment, true, |
| pixels.get() + kHeight / 2 * padded_row_size, |
| GetTransferAddressFromOffsetAs<uint8>(offset4, half_size))); |
| } |
| |
| // Test TexImage2D with sub rows |
| TEST_F(GLES2ImplementationTest, TexImage2DSubRows) { |
| struct Cmds { |
| TexImage2D tex_image_2d; |
| TexSubImage2D tex_sub_image_2d1; |
| cmd::SetToken set_token1; |
| TexSubImage2D tex_sub_image_2d2; |
| cmd::SetToken set_token2; |
| TexSubImage2D tex_sub_image_2d3; |
| cmd::SetToken set_token3; |
| TexSubImage2D tex_sub_image_2d4; |
| cmd::SetToken set_token4; |
| }; |
| const GLenum kTarget = GL_TEXTURE_2D; |
| const GLint kLevel = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLint kBorder = 0; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kHeight = 2; |
| const GLsizei kWidth = (MaxTransferBufferSize() / 3) * 2; |
| |
| uint32 size = 0; |
| uint32 unpadded_row_size = 0; |
| uint32 padded_row_size = 0; |
| ASSERT_TRUE(ComputeImageDataSizes( |
| kWidth, kHeight, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| uint32 part_size = kWidth * 3 / 2; |
| |
| scoped_array<uint8> pixels(new uint8[size]); |
| for (uint32 ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8>(ii); |
| } |
| uint32 offset1 = AllocateTransferBuffer(part_size); |
| uint32 offset2 = AllocateTransferBuffer(part_size); |
| uint32 offset3 = AllocateTransferBuffer(part_size); |
| uint32 offset4 = AllocateTransferBuffer(part_size); |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| 0, 0); |
| expected.tex_sub_image_2d1.Init( |
| kTarget, kLevel, 0, 0, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset1, true); |
| expected.set_token1.Init(GetNextToken()); |
| expected.tex_sub_image_2d2.Init( |
| kTarget, kLevel, kWidth / 2, 0, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset2, true); |
| expected.set_token2.Init(GetNextToken()); |
| expected.tex_sub_image_2d3.Init( |
| kTarget, kLevel, 0, 1, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset3, true); |
| expected.set_token3.Init(GetNextToken()); |
| expected.tex_sub_image_2d4.Init( |
| kTarget, kLevel, kWidth / 2, 1, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset4, true); |
| expected.set_token4.Init(GetNextToken()); |
| |
| // TODO(gman): Make it possible to run this test |
| // EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get(), |
| // GetTransferAddressFromOffsetAs<uint8>(offset1, part_size))) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get() + part_size, |
| // GetTransferAddressFromOffsetAs<uint8>(offset2, part_size))) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get() + padded_row_size, |
| // GetTransferAddressFromOffsetAs<uint8>(offset3, part_size))) |
| // .RetiresOnSaturation(); |
| |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels.get()); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(CheckRect( |
| kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| pixels.get() + padded_row_size + part_size, |
| GetTransferAddressFromOffsetAs<uint8>(offset4, part_size))); |
| |
| ClearCommands(); |
| const void* commands2 = GetPut(); |
| offset1 = AllocateTransferBuffer(part_size); |
| offset2 = AllocateTransferBuffer(part_size); |
| offset3 = AllocateTransferBuffer(part_size); |
| offset4 = AllocateTransferBuffer(part_size); |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| 0, 0); |
| expected.tex_sub_image_2d1.Init( |
| kTarget, kLevel, 0, 1, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset1, true); |
| expected.set_token1.Init(GetNextToken()); |
| expected.tex_sub_image_2d2.Init( |
| kTarget, kLevel, kWidth / 2, 1, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset2, true); |
| expected.set_token2.Init(GetNextToken()); |
| expected.tex_sub_image_2d3.Init( |
| kTarget, kLevel, 0, 0, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset3, true); |
| expected.set_token3.Init(GetNextToken()); |
| expected.tex_sub_image_2d4.Init( |
| kTarget, kLevel, kWidth / 2, 0, kWidth / 2, 1, kFormat, kType, |
| kTransferBufferId, offset4, true); |
| expected.set_token4.Init(GetNextToken()); |
| |
| // TODO(gman): Make it possible to run this test |
| // EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get(), |
| // GetTransferAddressFromOffsetAs<uint8>(offset1, part_size))) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get() + part_size, |
| // GetTransferAddressFromOffsetAs<uint8>(offset2, part_size))) |
| // .WillOnce(CheckRectAction( |
| // kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| // pixels.get() + padded_row_size, |
| // GetTransferAddressFromOffsetAs<uint8>(offset3, part_size))) |
| // .RetiresOnSaturation(); |
| |
| gl_->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, GL_TRUE); |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kBorder, kFormat, kType, |
| pixels.get()); |
| EXPECT_EQ(0, memcmp(&expected, commands2, sizeof(expected))); |
| EXPECT_TRUE(CheckRect( |
| kWidth / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, false, |
| pixels.get() + padded_row_size + part_size, |
| GetTransferAddressFromOffsetAs<uint8>(offset4, part_size))); |
| } |
| |
| // Test that GenBuffer does not call GenSharedIds. |
| // This is because with client side arrays on we know the StrictSharedIdHandler |
| // for buffers has already gotten a set of ids |
| TEST_F(GLES2ImplementationStrictSharedTest, GenBuffer) { |
| // Starts at + 2 because client side arrays take first 2 ids. |
| GLuint ids[3] = { kStartId + 2, kStartId + 3, kStartId + 4 }; |
| struct Cmds { |
| GenBuffersImmediate gen; |
| GLuint data[3]; |
| }; |
| Cmds expected; |
| expected.gen.Init(arraysize(ids), &ids[0]); |
| gl_->GenBuffers(arraysize(ids), &ids[0]); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_NE(0u, ids[0]); |
| EXPECT_NE(0u, ids[1]); |
| EXPECT_NE(0u, ids[2]); |
| } |
| |
| // Binds can not be cached with bind_generates_resource = false because |
| // our id might not be valid. |
| TEST_F(GLES2ImplementationStrictSharedTest, BindsNotCached) { |
| struct PNameValue { |
| GLenum pname; |
| GLint expected; |
| }; |
| const PNameValue pairs[] = { |
| { GL_TEXTURE_BINDING_2D, 1, }, |
| { GL_TEXTURE_BINDING_CUBE_MAP, 2, }, |
| { GL_FRAMEBUFFER_BINDING, 3, }, |
| { GL_RENDERBUFFER_BINDING, 4, }, |
| { GL_ARRAY_BUFFER_BINDING, 5, }, |
| { GL_ELEMENT_ARRAY_BUFFER_BINDING, 6, }, |
| }; |
| size_t num_pairs = sizeof(pairs) / sizeof(pairs[0]); |
| for (size_t ii = 0; ii < num_pairs; ++ii) { |
| const PNameValue& pv = pairs[ii]; |
| GLint v = -1; |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemory(SizedResultHelper<GLuint>(pv.expected))) |
| .RetiresOnSaturation(); |
| gl_->GetIntegerv(pv.pname, &v); |
| EXPECT_EQ(pv.expected, v); |
| } |
| } |
| |
| TEST_F(GLES2ImplementationStrictSharedTest, CanNotDeleteIdsWeDidNotCreate) { |
| GLuint id = 0x12345678; |
| |
| 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))) |
| .RetiresOnSaturation(); |
| |
| gl_->DeleteBuffers(1, &id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| gl_->DeleteFramebuffers(1, &id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| gl_->DeleteRenderbuffers(1, &id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| gl_->DeleteTextures(1, &id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| gl_->DeleteProgram(id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| gl_->DeleteShader(id); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, CreateStreamTextureCHROMIUM) { |
| const GLuint kTextureId = 123; |
| const GLuint kResult = 456; |
| const uint32 kResultOffset = 0; |
| |
| struct Cmds { |
| CreateStreamTextureCHROMIUM create_stream; |
| }; |
| |
| Cmds expected; |
| expected.create_stream.Init(kTextureId, kTransferBufferId, kResultOffset); |
| |
| EXPECT_CALL(*command_buffer_, OnFlush(_)) |
| .WillOnce(SetMemoryAtOffset(kResultOffset, kResult)) |
| .WillOnce(SetMemory(GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| GLuint handle = gl_->CreateStreamTextureCHROMIUM(kTextureId); |
| EXPECT_EQ(handle, kResult); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, DestroyStreamTextureCHROMIUM) { |
| const GLuint kTextureHandle = 456; |
| |
| struct Cmds { |
| DestroyStreamTextureCHROMIUM destroy_stream; |
| }; |
| |
| Cmds expected; |
| expected.destroy_stream.Init(kTextureHandle); |
| |
| gl_->DestroyStreamTextureCHROMIUM(kTextureHandle); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |
| |
| |