| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Tests for GLES2Implementation. |
| |
| #include "gpu/command_buffer/client/gles2_implementation.h" |
| |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| #include <GLES2/gl2extchromium.h> |
| #include <GLES3/gl3.h> |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/compiler_specific.h" |
| #include "gpu/command_buffer/client/client_test_helper.h" |
| #include "gpu/command_buffer/client/gles2_cmd_helper.h" |
| #include "gpu/command_buffer/client/program_info_manager.h" |
| #include "gpu/command_buffer/client/query_tracker.h" |
| #include "gpu/command_buffer/client/ring_buffer.h" |
| #include "gpu/command_buffer/client/shared_memory_limits.h" |
| #include "gpu/command_buffer/client/transfer_buffer.h" |
| #include "gpu/command_buffer/common/command_buffer.h" |
| #include "gpu/command_buffer/common/sync_token.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #if !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| #define GLES2_SUPPORT_CLIENT_SIDE_ARRAYS |
| #endif |
| |
| using testing::_; |
| using testing::AtLeast; |
| using testing::AnyNumber; |
| using testing::DoAll; |
| using testing::InSequence; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::Pointee; |
| using testing::SaveArg; |
| using testing::Sequence; |
| using testing::StrictMock; |
| using testing::Truly; |
| using testing::Return; |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| ACTION_P2(SetMemory, dst, obj) { |
| memcpy(dst, &obj, sizeof(obj)); |
| } |
| |
| ACTION_P3(SetMemoryFromArray, dst, array, size) { |
| memcpy(dst, 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)) { |
| memcpy(result_, &result, sizeof(T)); |
| } |
| |
| private: |
| uint32_t size_; |
| char result_[sizeof(T)]; |
| }; |
| |
| // 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) |
| |
| class MockTransferBuffer : public TransferBufferInterface { |
| public: |
| struct ExpectedMemoryInfo { |
| uint32_t offset; |
| int32_t id; |
| uint8_t* ptr; |
| }; |
| |
| MockTransferBuffer( |
| CommandBuffer* command_buffer, |
| unsigned int size, |
| unsigned int result_size, |
| unsigned int alignment, |
| bool initialize_fail) |
| : command_buffer_(command_buffer), |
| size_(size), |
| result_size_(result_size), |
| alignment_(alignment), |
| actual_buffer_index_(0), |
| expected_buffer_index_(0), |
| last_alloc_(NULL), |
| expected_offset_(result_size), |
| actual_offset_(result_size), |
| initialize_fail_(initialize_fail) { |
| // We have to allocate the buffers here because |
| // we need to know their address before GLES2Implementation::Initialize |
| // is called. |
| for (int ii = 0; ii < kNumBuffers; ++ii) { |
| buffers_[ii] = command_buffer_->CreateTransferBuffer( |
| size_ + ii * alignment_, |
| &buffer_ids_[ii]); |
| EXPECT_NE(-1, buffer_ids_[ii]); |
| } |
| } |
| |
| ~MockTransferBuffer() override {} |
| |
| bool Initialize(unsigned int starting_buffer_size, |
| unsigned int result_size, |
| unsigned int /* min_buffer_size */, |
| unsigned int /* max_buffer_size */, |
| unsigned int alignment, |
| unsigned int size_to_flush) override; |
| int GetShmId() override; |
| void* GetResultBuffer() override; |
| int GetResultOffset() override; |
| void Free() override; |
| bool HaveBuffer() const override; |
| void* AllocUpTo(unsigned int size, unsigned int* size_allocated) override; |
| void* Alloc(unsigned int size) override; |
| RingBuffer::Offset GetOffset(void* pointer) const override; |
| void DiscardBlock(void* p) override; |
| void FreePendingToken(void* p, unsigned int /* token */) override; |
| unsigned int GetSize() const override; |
| unsigned int GetFreeSize() const override; |
| |
| size_t MaxTransferBufferSize() { |
| return size_ - result_size_; |
| } |
| |
| unsigned int RoundToAlignment(unsigned int size) { |
| return (size + alignment_ - 1) & ~(alignment_ - 1); |
| } |
| |
| bool InSync() { |
| return expected_buffer_index_ == actual_buffer_index_ && |
| expected_offset_ == actual_offset_; |
| } |
| |
| ExpectedMemoryInfo GetExpectedMemory(size_t size) { |
| ExpectedMemoryInfo mem; |
| mem.offset = AllocateExpectedTransferBuffer(size); |
| mem.id = GetExpectedTransferBufferId(); |
| mem.ptr = static_cast<uint8_t*>( |
| GetExpectedTransferAddressFromOffset(mem.offset, size)); |
| return mem; |
| } |
| |
| ExpectedMemoryInfo GetExpectedResultMemory(size_t size) { |
| ExpectedMemoryInfo mem; |
| mem.offset = GetExpectedResultBufferOffset(); |
| mem.id = GetExpectedResultBufferId(); |
| mem.ptr = static_cast<uint8_t*>( |
| GetExpectedTransferAddressFromOffset(mem.offset, size)); |
| return mem; |
| } |
| |
| private: |
| static const int kNumBuffers = 2; |
| |
| uint8_t* actual_buffer() const { |
| return static_cast<uint8_t*>(buffers_[actual_buffer_index_]->memory()); |
| } |
| |
| uint8_t* expected_buffer() const { |
| return static_cast<uint8_t*>(buffers_[expected_buffer_index_]->memory()); |
| } |
| |
| uint32_t AllocateExpectedTransferBuffer(size_t size) { |
| EXPECT_LE(size, MaxTransferBufferSize()); |
| |
| // Toggle which buffer we get each time to simulate the buffer being |
| // reallocated. |
| expected_buffer_index_ = (expected_buffer_index_ + 1) % kNumBuffers; |
| |
| if (expected_offset_ + size > size_) { |
| expected_offset_ = result_size_; |
| } |
| uint32_t offset = expected_offset_; |
| expected_offset_ += RoundToAlignment(size); |
| |
| // Make sure each buffer has a different offset. |
| return offset + expected_buffer_index_ * alignment_; |
| } |
| |
| void* GetExpectedTransferAddressFromOffset(uint32_t offset, size_t size) { |
| EXPECT_GE(offset, expected_buffer_index_ * alignment_); |
| EXPECT_LE(offset + size, size_ + expected_buffer_index_ * alignment_); |
| return expected_buffer() + offset; |
| } |
| |
| int GetExpectedResultBufferId() { |
| return buffer_ids_[expected_buffer_index_]; |
| } |
| |
| uint32_t GetExpectedResultBufferOffset() { |
| return expected_buffer_index_ * alignment_; |
| } |
| |
| int GetExpectedTransferBufferId() { |
| return buffer_ids_[expected_buffer_index_]; |
| } |
| |
| CommandBuffer* command_buffer_; |
| size_t size_; |
| size_t result_size_; |
| uint32_t alignment_; |
| int buffer_ids_[kNumBuffers]; |
| scoped_refptr<Buffer> buffers_[kNumBuffers]; |
| int actual_buffer_index_; |
| int expected_buffer_index_; |
| void* last_alloc_; |
| uint32_t expected_offset_; |
| uint32_t actual_offset_; |
| bool initialize_fail_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockTransferBuffer); |
| }; |
| |
| bool MockTransferBuffer::Initialize( |
| unsigned int starting_buffer_size, |
| unsigned int result_size, |
| unsigned int /* min_buffer_size */, |
| unsigned int /* max_buffer_size */, |
| unsigned int alignment, |
| unsigned int /* size_to_flush */) { |
| // Just check they match. |
| return size_ == starting_buffer_size && |
| result_size_ == result_size && |
| alignment_ == alignment && !initialize_fail_; |
| }; |
| |
| int MockTransferBuffer::GetShmId() { |
| return buffer_ids_[actual_buffer_index_]; |
| } |
| |
| void* MockTransferBuffer::GetResultBuffer() { |
| return actual_buffer() + actual_buffer_index_ * alignment_; |
| } |
| |
| int MockTransferBuffer::GetResultOffset() { |
| return actual_buffer_index_ * alignment_; |
| } |
| |
| void MockTransferBuffer::Free() { |
| NOTREACHED(); |
| } |
| |
| bool MockTransferBuffer::HaveBuffer() const { |
| return true; |
| } |
| |
| void* MockTransferBuffer::AllocUpTo( |
| unsigned int size, unsigned int* size_allocated) { |
| EXPECT_TRUE(size_allocated != NULL); |
| EXPECT_TRUE(last_alloc_ == NULL); |
| |
| // Toggle which buffer we get each time to simulate the buffer being |
| // reallocated. |
| actual_buffer_index_ = (actual_buffer_index_ + 1) % kNumBuffers; |
| |
| size = std::min(static_cast<size_t>(size), MaxTransferBufferSize()); |
| if (actual_offset_ + size > size_) { |
| actual_offset_ = result_size_; |
| } |
| uint32_t offset = actual_offset_; |
| actual_offset_ += RoundToAlignment(size); |
| *size_allocated = size; |
| |
| // Make sure each buffer has a different offset. |
| last_alloc_ = actual_buffer() + offset + actual_buffer_index_ * alignment_; |
| return last_alloc_; |
| } |
| |
| void* MockTransferBuffer::Alloc(unsigned int size) { |
| EXPECT_LE(size, MaxTransferBufferSize()); |
| unsigned int temp = 0; |
| void* p = AllocUpTo(size, &temp); |
| EXPECT_EQ(temp, size); |
| return p; |
| } |
| |
| RingBuffer::Offset MockTransferBuffer::GetOffset(void* pointer) const { |
| // Make sure each buffer has a different offset. |
| return static_cast<uint8_t*>(pointer) - actual_buffer(); |
| } |
| |
| void MockTransferBuffer::DiscardBlock(void* p) { |
| EXPECT_EQ(last_alloc_, p); |
| last_alloc_ = NULL; |
| } |
| |
| void MockTransferBuffer::FreePendingToken(void* p, unsigned int /* token */) { |
| EXPECT_EQ(last_alloc_, p); |
| last_alloc_ = NULL; |
| } |
| |
| unsigned int MockTransferBuffer::GetSize() const { |
| return 0; |
| } |
| |
| unsigned int MockTransferBuffer::GetFreeSize() const { |
| return 0; |
| } |
| |
| // API wrapper for Buffers. |
| class GenBuffersAPI { |
| public: |
| static void Gen(GLES2Implementation* gl_impl, GLsizei n, GLuint* ids) { |
| gl_impl->GenBuffers(n, ids); |
| } |
| |
| static void Delete(GLES2Implementation* gl_impl, |
| GLsizei n, |
| const GLuint* ids) { |
| gl_impl->DeleteBuffers(n, ids); |
| } |
| }; |
| |
| // API wrapper for Framebuffers. |
| class GenFramebuffersAPI { |
| public: |
| static void Gen(GLES2Implementation* gl_impl, GLsizei n, GLuint* ids) { |
| gl_impl->GenFramebuffers(n, ids); |
| } |
| |
| static void Delete(GLES2Implementation* gl_impl, |
| GLsizei n, |
| const GLuint* ids) { |
| gl_impl->DeleteFramebuffers(n, ids); |
| } |
| }; |
| |
| // API wrapper for Renderbuffers. |
| class GenRenderbuffersAPI { |
| public: |
| static void Gen(GLES2Implementation* gl_impl, GLsizei n, GLuint* ids) { |
| gl_impl->GenRenderbuffers(n, ids); |
| } |
| |
| static void Delete(GLES2Implementation* gl_impl, |
| GLsizei n, |
| const GLuint* ids) { |
| gl_impl->DeleteRenderbuffers(n, ids); |
| } |
| }; |
| |
| // API wrapper for Textures. |
| class GenTexturesAPI { |
| public: |
| static void Gen(GLES2Implementation* gl_impl, GLsizei n, GLuint* ids) { |
| gl_impl->GenTextures(n, ids); |
| } |
| |
| static void Delete(GLES2Implementation* gl_impl, |
| GLsizei n, |
| const GLuint* ids) { |
| gl_impl->DeleteTextures(n, ids); |
| } |
| }; |
| |
| class GLES2ImplementationTest : public testing::Test { |
| protected: |
| static const int kNumTestContexts = 2; |
| static const uint8_t kInitialValue = 0xBD; |
| static const int32_t kNumCommandEntries = 500; |
| static const int32_t kCommandBufferSizeBytes = |
| kNumCommandEntries * sizeof(CommandBufferEntry); |
| static const size_t kTransferBufferSize = 512; |
| |
| 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 kMaxTransformFeedbackSeparateAttribs = 4; |
| static const GLuint kMaxUniformBufferBindings = 36; |
| static const GLuint kStartId = 1024; |
| static const GLuint kBuffersStartId = 1; |
| static const GLuint kFramebuffersStartId = 1; |
| static const GLuint kProgramsAndShadersStartId = 1; |
| static const GLuint kRenderbuffersStartId = 1; |
| static const GLuint kSamplersStartId = 1; |
| static const GLuint kTexturesStartId = 1; |
| static const GLuint kTransformFeedbacksStartId = 1; |
| static const GLuint kQueriesStartId = 1; |
| static const GLuint kVertexArraysStartId = 1; |
| |
| typedef MockTransferBuffer::ExpectedMemoryInfo ExpectedMemoryInfo; |
| |
| class TestContext { |
| public: |
| TestContext() : commands_(NULL), token_(0) {} |
| |
| bool Initialize(ShareGroup* share_group, |
| bool bind_generates_resource_client, |
| bool bind_generates_resource_service, |
| bool lose_context_when_out_of_memory, |
| bool transfer_buffer_initialize_fail, |
| bool sync_query, |
| bool occlusion_query_boolean, |
| bool timer_queries, |
| int major_version, |
| int minor_version) { |
| command_buffer_.reset(new StrictMock<MockClientCommandBuffer>()); |
| |
| transfer_buffer_.reset( |
| new MockTransferBuffer(command_buffer_.get(), |
| kTransferBufferSize, |
| GLES2Implementation::kStartingOffset, |
| GLES2Implementation::kAlignment, |
| transfer_buffer_initialize_fail)); |
| |
| helper_.reset(new GLES2CmdHelper(command_buffer())); |
| helper_->Initialize(kCommandBufferSizeBytes); |
| |
| gpu_control_.reset(new StrictMock<MockClientGpuControl>()); |
| Capabilities capabilities; |
| capabilities.VisitPrecisions( |
| [](GLenum shader, GLenum type, |
| Capabilities::ShaderPrecision* precision) { |
| precision->min_range = 3; |
| precision->max_range = 5; |
| precision->precision = 7; |
| }); |
| capabilities.max_combined_texture_image_units = |
| kMaxCombinedTextureImageUnits; |
| capabilities.max_cube_map_texture_size = kMaxCubeMapTextureSize; |
| capabilities.max_fragment_uniform_vectors = kMaxFragmentUniformVectors; |
| capabilities.max_renderbuffer_size = kMaxRenderbufferSize; |
| capabilities.max_texture_image_units = kMaxTextureImageUnits; |
| capabilities.max_texture_size = kMaxTextureSize; |
| capabilities.max_varying_vectors = kMaxVaryingVectors; |
| capabilities.max_vertex_attribs = kMaxVertexAttribs; |
| capabilities.max_vertex_texture_image_units = kMaxVertexTextureImageUnits; |
| capabilities.max_vertex_uniform_vectors = kMaxVertexUniformVectors; |
| capabilities.num_compressed_texture_formats = |
| kNumCompressedTextureFormats; |
| capabilities.num_shader_binary_formats = kNumShaderBinaryFormats; |
| capabilities.max_transform_feedback_separate_attribs = |
| kMaxTransformFeedbackSeparateAttribs; |
| capabilities.max_uniform_buffer_bindings = kMaxUniformBufferBindings; |
| capabilities.bind_generates_resource_chromium = |
| bind_generates_resource_service ? 1 : 0; |
| capabilities.sync_query = sync_query; |
| capabilities.occlusion_query_boolean = occlusion_query_boolean; |
| capabilities.timer_queries = timer_queries; |
| capabilities.major_version = major_version; |
| capabilities.minor_version = minor_version; |
| EXPECT_CALL(*gpu_control_, GetCapabilities()) |
| .WillOnce(Return(capabilities)); |
| |
| { |
| InSequence sequence; |
| |
| const bool support_client_side_arrays = true; |
| gl_.reset(new GLES2Implementation(helper_.get(), |
| share_group, |
| transfer_buffer_.get(), |
| bind_generates_resource_client, |
| lose_context_when_out_of_memory, |
| support_client_side_arrays, |
| gpu_control_.get())); |
| } |
| |
| // The client should be set to something non-null. |
| EXPECT_CALL(*gpu_control_, SetGpuControlClient(gl_.get())).Times(1); |
| |
| if (!gl_->Initialize(kTransferBufferSize, kTransferBufferSize, |
| kTransferBufferSize, SharedMemoryLimits::kNoLimit)) |
| return false; |
| |
| helper_->CommandBufferHelper::Finish(); |
| Mock::VerifyAndClearExpectations(gl_.get()); |
| |
| scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer(); |
| commands_ = static_cast<CommandBufferEntry*>(ring_buffer->memory()) + |
| command_buffer()->GetPutOffset(); |
| ClearCommands(); |
| EXPECT_TRUE(transfer_buffer_->InSync()); |
| |
| Mock::VerifyAndClearExpectations(command_buffer()); |
| return true; |
| } |
| |
| void TearDown() { |
| Mock::VerifyAndClear(gl_.get()); |
| EXPECT_CALL(*command_buffer(), OnFlush()).Times(AnyNumber()); |
| // For command buffer. |
| EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_)) |
| .Times(AtLeast(1)); |
| // The client should be unset. |
| EXPECT_CALL(*gpu_control_, SetGpuControlClient(nullptr)).Times(1); |
| gl_.reset(); |
| } |
| |
| MockClientCommandBuffer* command_buffer() const { |
| return command_buffer_.get(); |
| } |
| |
| int GetNextToken() { return ++token_; } |
| |
| void ClearCommands() { |
| scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer(); |
| memset(ring_buffer->memory(), kInitialValue, ring_buffer->size()); |
| } |
| |
| std::unique_ptr<MockClientCommandBuffer> command_buffer_; |
| std::unique_ptr<MockClientGpuControl> gpu_control_; |
| std::unique_ptr<GLES2CmdHelper> helper_; |
| std::unique_ptr<MockTransferBuffer> transfer_buffer_; |
| std::unique_ptr<GLES2Implementation> gl_; |
| CommandBufferEntry* commands_; |
| int token_; |
| }; |
| |
| GLES2ImplementationTest() : commands_(NULL) {} |
| |
| void SetUp() override; |
| void TearDown() override; |
| |
| bool NoCommandsWritten() { |
| scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer(); |
| const uint8_t* cmds = |
| reinterpret_cast<const uint8_t*>(ring_buffer->memory()); |
| const uint8_t* end = cmds + ring_buffer->size(); |
| for (; cmds < end; ++cmds) { |
| if (*cmds != kInitialValue) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| QueryTracker::Query* GetQuery(GLuint id) { |
| return gl_->query_tracker_->GetQuery(id); |
| } |
| |
| QueryTracker* GetQueryTracker() { |
| return gl_->query_tracker_.get(); |
| } |
| |
| struct ContextInitOptions { |
| ContextInitOptions() |
| : bind_generates_resource_client(true), |
| bind_generates_resource_service(true), |
| lose_context_when_out_of_memory(false), |
| transfer_buffer_initialize_fail(false), |
| sync_query(true), |
| occlusion_query_boolean(true), |
| timer_queries(true), |
| major_version(2), |
| minor_version(0) {} |
| |
| bool bind_generates_resource_client; |
| bool bind_generates_resource_service; |
| bool lose_context_when_out_of_memory; |
| bool transfer_buffer_initialize_fail; |
| bool sync_query; |
| bool occlusion_query_boolean; |
| bool timer_queries; |
| int major_version; |
| int minor_version; |
| }; |
| |
| bool Initialize(const ContextInitOptions& init_options) { |
| bool success = true; |
| share_group_ = new ShareGroup(init_options.bind_generates_resource_client, |
| 0 /* tracing_id */); |
| |
| for (int i = 0; i < kNumTestContexts; i++) { |
| if (!test_contexts_[i].Initialize( |
| share_group_.get(), |
| init_options.bind_generates_resource_client, |
| init_options.bind_generates_resource_service, |
| init_options.lose_context_when_out_of_memory, |
| init_options.transfer_buffer_initialize_fail, |
| init_options.sync_query, |
| init_options.occlusion_query_boolean, |
| init_options.timer_queries, |
| init_options.major_version, |
| init_options.minor_version)) |
| success = false; |
| } |
| |
| // Default to test context 0. |
| gpu_control_ = test_contexts_[0].gpu_control_.get(); |
| helper_ = test_contexts_[0].helper_.get(); |
| transfer_buffer_ = test_contexts_[0].transfer_buffer_.get(); |
| gl_ = test_contexts_[0].gl_.get(); |
| commands_ = test_contexts_[0].commands_; |
| return success; |
| } |
| |
| MockClientCommandBuffer* command_buffer() const { |
| return test_contexts_[0].command_buffer_.get(); |
| } |
| |
| int GetNextToken() { return test_contexts_[0].GetNextToken(); } |
| |
| const void* GetPut() { |
| return helper_->GetSpace(0); |
| } |
| |
| void ClearCommands() { |
| scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer(); |
| memset(ring_buffer->memory(), kInitialValue, ring_buffer->size()); |
| } |
| |
| size_t MaxTransferBufferSize() { |
| return transfer_buffer_->MaxTransferBufferSize(); |
| } |
| |
| void SetMappedMemoryLimit(size_t limit) { |
| gl_->mapped_memory_->set_max_allocated_bytes(limit); |
| } |
| |
| ExpectedMemoryInfo GetExpectedMemory(size_t size) { |
| return transfer_buffer_->GetExpectedMemory(size); |
| } |
| |
| ExpectedMemoryInfo GetExpectedResultMemory(size_t size) { |
| return transfer_buffer_->GetExpectedResultMemory(size); |
| } |
| |
| ExpectedMemoryInfo GetExpectedMappedMemory(size_t size) { |
| ExpectedMemoryInfo mem; |
| |
| // Temporarily allocate memory and expect that memory block to be reused. |
| mem.ptr = static_cast<uint8_t*>( |
| gl_->mapped_memory_->Alloc(size, &mem.id, &mem.offset)); |
| gl_->mapped_memory_->Free(mem.ptr); |
| |
| return mem; |
| } |
| |
| // Sets the ProgramInfoManager. The manager will be owned |
| // by the ShareGroup. |
| void SetProgramInfoManager(ProgramInfoManager* manager) { |
| gl_->share_group()->SetProgramInfoManagerForTesting(manager); |
| } |
| |
| int CheckError() { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| return gl_->GetError(); |
| } |
| |
| const std::string& GetLastError() { |
| return gl_->GetLastError(); |
| } |
| |
| bool GetBucketContents(uint32_t bucket_id, std::vector<int8_t>* data) { |
| return gl_->GetBucketContents(bucket_id, data); |
| } |
| |
| bool AllowExtraTransferBufferSize() { |
| return gl_->max_extra_transfer_buffer_size_ > 0; |
| } |
| |
| TestContext test_contexts_[kNumTestContexts]; |
| |
| scoped_refptr<ShareGroup> share_group_; |
| MockClientGpuControl* gpu_control_; |
| GLES2CmdHelper* helper_; |
| MockTransferBuffer* transfer_buffer_; |
| GLES2Implementation* gl_; |
| CommandBufferEntry* commands_; |
| }; |
| |
| void GLES2ImplementationTest::SetUp() { |
| ContextInitOptions init_options; |
| ASSERT_TRUE(Initialize(init_options)); |
| } |
| |
| void GLES2ImplementationTest::TearDown() { |
| for (int i = 0; i < kNumTestContexts; i++) |
| test_contexts_[i].TearDown(); |
| } |
| |
| class GLES2ImplementationManualInitTest : public GLES2ImplementationTest { |
| protected: |
| void SetUp() override {} |
| }; |
| |
| class GLES2ImplementationStrictSharedTest : public GLES2ImplementationTest { |
| protected: |
| void SetUp() override; |
| |
| template <class ResApi> |
| void FlushGenerationTest() { |
| GLuint id1, id2, id3; |
| |
| // Generate valid id. |
| ResApi::Gen(gl_, 1, &id1); |
| EXPECT_NE(id1, 0u); |
| |
| // Delete id1 and generate id2. id1 should not be reused. |
| ResApi::Delete(gl_, 1, &id1); |
| ResApi::Gen(gl_, 1, &id2); |
| EXPECT_NE(id2, 0u); |
| EXPECT_NE(id2, id1); |
| |
| // Expect id1 reuse after Flush. |
| gl_->Flush(); |
| ResApi::Gen(gl_, 1, &id3); |
| EXPECT_EQ(id3, id1); |
| } |
| |
| // Ids should not be reused unless the |Deleting| context does a Flush() |
| // AND triggers a lazy release after that. |
| template <class ResApi> |
| void CrossContextGenerationTest() { |
| GLES2Implementation* gl1 = test_contexts_[0].gl_.get(); |
| GLES2Implementation* gl2 = test_contexts_[1].gl_.get(); |
| GLuint id1, id2, id3; |
| |
| // Delete, no flush on context 1. No reuse. |
| ResApi::Gen(gl1, 1, &id1); |
| ResApi::Delete(gl1, 1, &id1); |
| ResApi::Gen(gl1, 1, &id2); |
| EXPECT_NE(id1, id2); |
| |
| // Flush context 2. Still no reuse. |
| gl2->Flush(); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_NE(id1, id3); |
| EXPECT_NE(id2, id3); |
| |
| // Flush on context 1, but no lazy release. Still no reuse. |
| gl1->Flush(); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_NE(id1, id3); |
| |
| // Lazy release triggered by another Delete. Should reuse id1. |
| ResApi::Delete(gl1, 1, &id2); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_EQ(id1, id3); |
| } |
| |
| // Same as CrossContextGenerationTest(), but triggers an Auto Flush on |
| // the Delete(). Tests an edge case regression. |
| template <class ResApi> |
| void CrossContextGenerationAutoFlushTest() { |
| GLES2Implementation* gl1 = test_contexts_[0].gl_.get(); |
| GLES2Implementation* gl2 = test_contexts_[1].gl_.get(); |
| GLuint id1, id2, id3; |
| |
| // Delete, no flush on context 1. No reuse. |
| // By half filling the buffer, an internal flush is forced on the Delete(). |
| ResApi::Gen(gl1, 1, &id1); |
| gl1->helper()->Noop(kNumCommandEntries / 2); |
| ResApi::Delete(gl1, 1, &id1); |
| ResApi::Gen(gl1, 1, &id2); |
| EXPECT_NE(id1, id2); |
| |
| // Flush context 2. Still no reuse. |
| gl2->Flush(); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_NE(id1, id3); |
| EXPECT_NE(id2, id3); |
| |
| // Flush on context 1, but no lazy release. Still no reuse. |
| gl1->Flush(); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_NE(id1, id3); |
| |
| // Lazy release triggered by another Delete. Should reuse id1. |
| ResApi::Delete(gl1, 1, &id2); |
| ResApi::Gen(gl2, 1, &id3); |
| EXPECT_EQ(id1, id3); |
| } |
| }; |
| |
| void GLES2ImplementationStrictSharedTest::SetUp() { |
| ContextInitOptions init_options; |
| init_options.bind_generates_resource_client = false; |
| init_options.bind_generates_resource_service = false; |
| ASSERT_TRUE(Initialize(init_options)); |
| } |
| |
| class GLES3ImplementationTest : public GLES2ImplementationTest { |
| protected: |
| void SetUp() override; |
| }; |
| |
| void GLES3ImplementationTest::SetUp() { |
| ContextInitOptions init_options; |
| init_options.major_version = 3; |
| init_options.minor_version = 0; |
| ASSERT_TRUE(Initialize(init_options)); |
| } |
| |
| // GCC requires these declarations, but MSVC requires they not be present |
| #ifndef _MSC_VER |
| const uint8_t GLES2ImplementationTest::kInitialValue; |
| const int32_t GLES2ImplementationTest::kNumCommandEntries; |
| const int32_t GLES2ImplementationTest::kCommandBufferSizeBytes; |
| const size_t GLES2ImplementationTest::kTransferBufferSize; |
| const GLint GLES2ImplementationTest::kMaxCombinedTextureImageUnits; |
| const GLint GLES2ImplementationTest::kMaxCubeMapTextureSize; |
| const GLint GLES2ImplementationTest::kMaxFragmentUniformVectors; |
| const GLint GLES2ImplementationTest::kMaxRenderbufferSize; |
| const GLint GLES2ImplementationTest::kMaxTextureImageUnits; |
| const GLint GLES2ImplementationTest::kMaxTextureSize; |
| const GLint GLES2ImplementationTest::kMaxVaryingVectors; |
| const GLint GLES2ImplementationTest::kMaxVertexAttribs; |
| const GLint GLES2ImplementationTest::kMaxVertexTextureImageUnits; |
| const GLint GLES2ImplementationTest::kMaxVertexUniformVectors; |
| const GLint GLES2ImplementationTest::kNumCompressedTextureFormats; |
| const GLint GLES2ImplementationTest::kNumShaderBinaryFormats; |
| const GLuint GLES2ImplementationTest::kStartId; |
| const GLuint GLES2ImplementationTest::kBuffersStartId; |
| const GLuint GLES2ImplementationTest::kFramebuffersStartId; |
| const GLuint GLES2ImplementationTest::kProgramsAndShadersStartId; |
| const GLuint GLES2ImplementationTest::kRenderbuffersStartId; |
| const GLuint GLES2ImplementationTest::kSamplersStartId; |
| const GLuint GLES2ImplementationTest::kTexturesStartId; |
| const GLuint GLES2ImplementationTest::kTransformFeedbacksStartId; |
| const GLuint GLES2ImplementationTest::kQueriesStartId; |
| const GLuint GLES2ImplementationTest::kVertexArraysStartId; |
| #endif |
| |
| TEST_F(GLES2ImplementationTest, Basic) { |
| EXPECT_TRUE(gl_->share_group() != NULL); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetBucketContents) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const uint32_t kTestSize = MaxTransferBufferSize() + 32; |
| |
| std::unique_ptr<uint8_t[]> buf(new uint8_t[kTestSize]); |
| uint8_t* expected_data = buf.get(); |
| for (uint32_t ii = 0; ii < kTestSize; ++ii) { |
| expected_data[ii] = ii * 3; |
| } |
| |
| struct Cmds { |
| cmd::GetBucketStart get_bucket_start; |
| cmd::SetToken set_token1; |
| cmd::GetBucketData get_bucket_data; |
| cmd::SetToken set_token2; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = GetExpectedResultMemory(sizeof(uint32_t)); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory( |
| kTestSize - MaxTransferBufferSize()); |
| |
| Cmds expected; |
| expected.get_bucket_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.get_bucket_data.Init( |
| kBucketId, MaxTransferBufferSize(), |
| kTestSize - MaxTransferBufferSize(), mem2.id, mem2.offset); |
| expected.set_bucket_size2.Init(kBucketId, 0); |
| expected.set_token2.Init(GetNextToken()); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(DoAll( |
| SetMemory(result1.ptr, kTestSize), |
| SetMemoryFromArray( |
| mem1.ptr, expected_data, MaxTransferBufferSize()))) |
| .WillOnce(SetMemoryFromArray( |
| mem2.ptr, expected_data + MaxTransferBufferSize(), |
| kTestSize - MaxTransferBufferSize())) |
| .RetiresOnSaturation(); |
| |
| std::vector<int8_t> data; |
| GetBucketContents(kBucketId, &data); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| ASSERT_EQ(kTestSize, data.size()); |
| EXPECT_EQ(0, memcmp(expected_data, &data[0], data.size())); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetShaderPrecisionFormat) { |
| struct Cmds { |
| cmds::GetShaderPrecisionFormat cmd; |
| }; |
| typedef cmds::GetShaderPrecisionFormat::Result Result; |
| const unsigned kDummyType1 = 3; |
| const unsigned kDummyType2 = 4; |
| |
| // The first call for dummy type 1 should trigger a command buffer request. |
| GLint range1[2] = {0, 0}; |
| GLint precision1 = 0; |
| Cmds expected1; |
| ExpectedMemoryInfo client_result1 = GetExpectedResultMemory(4); |
| expected1.cmd.Init(GL_FRAGMENT_SHADER, kDummyType1, client_result1.id, |
| client_result1.offset); |
| Result server_result1 = {true, 14, 14, 10}; |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(client_result1.ptr, server_result1)) |
| .RetiresOnSaturation(); |
| gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType1, range1, |
| &precision1); |
| const void* commands2 = GetPut(); |
| EXPECT_NE(commands_, commands2); |
| EXPECT_EQ(0, memcmp(&expected1, commands_, sizeof(expected1))); |
| EXPECT_EQ(range1[0], 14); |
| EXPECT_EQ(range1[1], 14); |
| EXPECT_EQ(precision1, 10); |
| |
| // The second call for dummy type 1 should use the cached value and avoid |
| // triggering a command buffer request, so we do not expect a call to |
| // OnFlush() here. We do expect the results to be correct though. |
| GLint range2[2] = {0, 0}; |
| GLint precision2 = 0; |
| gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType1, range2, |
| &precision2); |
| const void* commands3 = GetPut(); |
| EXPECT_EQ(commands2, commands3); |
| EXPECT_EQ(range2[0], 14); |
| EXPECT_EQ(range2[1], 14); |
| EXPECT_EQ(precision2, 10); |
| |
| // If we then make a request for dummy type 2, we should get another command |
| // buffer request since it hasn't been cached yet. |
| GLint range3[2] = {0, 0}; |
| GLint precision3 = 0; |
| Cmds expected3; |
| ExpectedMemoryInfo result3 = GetExpectedResultMemory(4); |
| expected3.cmd.Init(GL_FRAGMENT_SHADER, kDummyType2, result3.id, |
| result3.offset); |
| Result result3_source = {true, 62, 62, 16}; |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result3.ptr, result3_source)) |
| .RetiresOnSaturation(); |
| gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, kDummyType2, range3, |
| &precision3); |
| const void* commands4 = GetPut(); |
| EXPECT_NE(commands3, commands4); |
| EXPECT_EQ(0, memcmp(&expected3, commands3, sizeof(expected3))); |
| EXPECT_EQ(range3[0], 62); |
| EXPECT_EQ(range3[1], 62); |
| EXPECT_EQ(precision3, 16); |
| |
| // Any call for predefined types should use the cached value from the |
| // Capabilities and avoid triggering a command buffer request, so we do not |
| // expect a call to OnFlush() here. We do expect the results to be correct |
| // though. |
| GLint range4[2] = {0, 0}; |
| GLint precision4 = 0; |
| gl_->GetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, range4, |
| &precision4); |
| const void* commands5 = GetPut(); |
| EXPECT_EQ(commands4, commands5); |
| EXPECT_EQ(range4[0], 3); |
| EXPECT_EQ(range4[1], 5); |
| EXPECT_EQ(precision4, 7); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetShaderSource) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const GLuint kShaderId = 456; |
| const Str7 kString = {"foobar"}; |
| const char kBad = 0x12; |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetShaderSource get_shader_source; |
| cmd::GetBucketStart get_bucket_start; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = GetExpectedResultMemory(sizeof(uint32_t)); |
| |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_shader_source.Init(kShaderId, kBucketId); |
| expected.get_bucket_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.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(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .RetiresOnSaturation(); |
| |
| GLsizei length = 0; |
| gl_->GetShaderSource(kShaderId, sizeof(buf), &length, buf); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| 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 { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawArrays draw; |
| cmds::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; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| 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, DrawArraysInstancedANGLEClientSideBuffers) { |
| 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 { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::VertexAttribDivisorANGLE divisor; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawArraysInstancedANGLE draw; |
| cmds::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 GLuint kDivisor = 1; |
| const GLsizei kSize1 = |
| arraysize(verts) * kNumComponents1 * sizeof(verts[0][0]); |
| const GLsizei kSize2 = |
| 1 * kNumComponents2 * sizeof(verts[0][0]); |
| const GLsizei kEmuOffset1 = 0; |
| const GLsizei kEmuOffset2 = kSize1; |
| const GLsizei kTotalSize = kSize1 + kSize2; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kSize2); |
| |
| Cmds expected; |
| expected.enable1.Init(kAttribIndex1); |
| expected.enable2.Init(kAttribIndex2); |
| expected.divisor.Init(kAttribIndex2, kDivisor); |
| 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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| expected.set_token2.Init(GetNextToken()); |
| expected.set_pointer2.Init( |
| kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, 0, kEmuOffset2); |
| expected.draw.Init(GL_POINTS, kFirst, kCount, 1); |
| 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_->VertexAttribDivisorANGLE(kAttribIndex2, kDivisor); |
| gl_->DrawArraysInstancedANGLE(GL_POINTS, kFirst, kCount, 1); |
| 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_t indices[] = { |
| 1, 2, |
| }; |
| struct Cmds { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::BindBuffer bind_to_index_emu; |
| cmds::BufferData set_index_size; |
| cmds::BufferSubData copy_data0; |
| cmd::SetToken set_token0; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawElements draw; |
| cmds::BindBuffer restore; |
| cmds::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; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kIndexSize); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem3 = GetExpectedMemory(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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| 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, mem3.id, mem3.offset); |
| 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, DrawElementsClientSideBuffersIndexUint) { |
| 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 uint32_t indices[] = { |
| 1, 2, |
| }; |
| struct Cmds { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::BindBuffer bind_to_index_emu; |
| cmds::BufferData set_index_size; |
| cmds::BufferSubData copy_data0; |
| cmd::SetToken set_token0; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawElements draw; |
| cmds::BindBuffer restore; |
| cmds::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; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kIndexSize); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem3 = GetExpectedMemory(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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| 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, mem3.id, mem3.offset); |
| 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_INT, 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_INT, indices); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, DrawElementsClientSideBuffersInvalidIndexUint) { |
| 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 uint32_t indices[] = {1, 0x90000000}; |
| |
| 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; |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| 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_INT, indices); |
| |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), gl_->GetError()); |
| } |
| |
| 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 { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::BindBuffer bind_to_index; |
| cmds::GetMaxValueInBufferCHROMIUM get_max; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawElements draw; |
| cmds::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; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedResultMemory(sizeof(uint32_t)); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem3 = GetExpectedMemory(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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| 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, mem3.id, mem3.offset); |
| 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(mem1.ptr,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, DrawElementsInstancedANGLEClientSideBuffers) { |
| 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_t indices[] = { |
| 1, 2, |
| }; |
| struct Cmds { |
| cmds::EnableVertexAttribArray enable1; |
| cmds::EnableVertexAttribArray enable2; |
| cmds::VertexAttribDivisorANGLE divisor; |
| cmds::BindBuffer bind_to_index_emu; |
| cmds::BufferData set_index_size; |
| cmds::BufferSubData copy_data0; |
| cmd::SetToken set_token0; |
| cmds::BindBuffer bind_to_emu; |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::VertexAttribPointer set_pointer1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| cmds::VertexAttribPointer set_pointer2; |
| cmds::DrawElementsInstancedANGLE draw; |
| cmds::BindBuffer restore; |
| cmds::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 = |
| 1 * kNumComponents2 * sizeof(verts[0][0]); |
| const GLuint kDivisor = 1; |
| const GLsizei kEmuOffset1 = 0; |
| const GLsizei kEmuOffset2 = kSize1; |
| const GLsizei kTotalSize = kSize1 + kSize2; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kIndexSize); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kSize1); |
| ExpectedMemoryInfo mem3 = GetExpectedMemory(kSize2); |
| |
| Cmds expected; |
| expected.enable1.Init(kAttribIndex1); |
| expected.enable2.Init(kAttribIndex2); |
| expected.divisor.Init(kAttribIndex2, kDivisor); |
| 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, mem1.id, mem1.offset); |
| 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, mem2.id, mem2.offset); |
| 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, mem3.id, mem3.offset); |
| 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, 1); |
| 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_->VertexAttribDivisorANGLE(kAttribIndex2, kDivisor); |
| gl_->DrawElementsInstancedANGLE( |
| GL_POINTS, kCount, GL_UNSIGNED_SHORT, indices, 1); |
| 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; |
| |
| // It's all cached on the client side so no get commands are issued. |
| struct Cmds { |
| cmds::BindBuffer bind; |
| cmds::VertexAttribPointer set_pointer; |
| }; |
| |
| Cmds expected; |
| expected.bind.Init(GL_ARRAY_BUFFER, kBufferId); |
| expected.set_pointer.Init(kAttribIndex2, kNumComponents2, GL_FLOAT, GL_FALSE, |
| kStride2, kOffset2); |
| |
| // 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); |
| 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 { |
| cmds::EnableVertexAttribArray enable; |
| cmds::BindBuffer bind; |
| cmds::VertexAttribPointer set_pointer; |
| cmds::GetVertexAttribfv get2; // for getting the value from attrib1 |
| }; |
| |
| ExpectedMemoryInfo mem2 = GetExpectedResultMemory(16); |
| |
| 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.get2.Init(kAttribIndex1, |
| GL_CURRENT_VERTEX_ATTRIB, |
| mem2.id, mem2.offset); |
| |
| FourFloats current_attrib(1.2f, 3.4f, 5.6f, 7.8f); |
| |
| // One call to flush to wait for last call to GetVertexAttribiv |
| // as others are all cached. |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory( |
| mem2.ptr, 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 { |
| cmds::GetError get; |
| }; |
| Cmds expected; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedResultMemory( |
| sizeof(cmds::GetError::Result)); |
| |
| expected.get.Init(mem1.id, mem1.offset); |
| |
| // One call to flush to wait for GetError |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(mem1.ptr, 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 { |
| cmds::ReadPixels read1; |
| cmd::SetToken set_token1; |
| cmds::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; |
| |
| ExpectedMemoryInfo mem1 = |
| GetExpectedMemory(kWidth * kHeight / 2 * kBytesPerPixel); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::ReadPixels::Result)); |
| ExpectedMemoryInfo mem2 = |
| GetExpectedMemory(kWidth * kHeight / 2 * kBytesPerPixel); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::ReadPixels::Result)); |
| |
| Cmds expected; |
| expected.read1.Init( |
| 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| mem1.id, mem1.offset, result1.id, result1.offset, |
| false); |
| expected.set_token1.Init(GetNextToken()); |
| expected.read2.Init( |
| 0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, |
| mem2.id, mem2.offset, result2.id, result2.offset, false); |
| expected.set_token2.Init(GetNextToken()); |
| std::unique_ptr<int8_t[]> buffer( |
| new int8_t[kWidth * kHeight * kBytesPerPixel]); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, static_cast<uint32_t>(1))) |
| .WillOnce(SetMemory(result2.ptr, static_cast<uint32_t>(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 { |
| 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; |
| |
| ExpectedMemoryInfo mem1 = |
| GetExpectedMemory(kWidth * kHeight * kBytesPerPixel); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::ReadPixels::Result)); |
| |
| Cmds expected; |
| expected.read.Init( |
| 0, 0, kWidth, kHeight, kFormat, kType, |
| mem1.id, mem1.offset, result1.id, result1.offset, false); |
| expected.set_token.Init(GetNextToken()); |
| std::unique_ptr<int8_t[]> buffer( |
| new int8_t[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 { |
| cmds::BufferSubData buf; |
| cmd::SetToken set_token; |
| }; |
| const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER; |
| const GLintptr kOffset = 15; |
| const GLsizeiptr kSize = 16; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kSize); |
| |
| Cmds expected; |
| expected.buf.Init( |
| kTarget, kOffset, kSize, mem1.id, mem1.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 { |
| cmds::BufferSubData buf; |
| cmd::SetToken set_token; |
| }; |
| const GLenum kTarget = GL_ELEMENT_ARRAY_BUFFER; |
| const GLintptr kOffset = 15; |
| const GLsizeiptr kSize = 16; |
| |
| uint32_t offset = 0; |
| Cmds expected; |
| expected.buf.Init( |
| kTarget, kOffset, kSize, |
| command_buffer()->GetNextFreeTransferBufferId(), 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; |
| |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result3 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result4 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| // Calls to flush to wait for GetError |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result3.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result4.ptr, 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 { |
| 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_t offset = 0; |
| Cmds expected; |
| expected.tex.Init( |
| GL_TEXTURE_2D, kLevel, kXOffset, kYOffset, kWidth, kHeight, kFormat, |
| kType, |
| command_buffer()->GetNextFreeTransferBufferId(), 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; |
| |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result3 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result4 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result5 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result6 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result7 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| // Calls to flush to wait for GetError |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result3.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result4.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result5.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result6.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result7.ptr, 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, GetProgramInfoCHROMIUMGoodArgs) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const GLuint kProgramId = 123; |
| const char kBad = 0x12; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| ExpectedMemoryInfo mem1 = |
| GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmd::GetBucketStart::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| memset(buf, kBad, sizeof(buf)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetProgramInfoCHROMIUM get_program_info; |
| cmd::GetBucketStart get_bucket_start; |
| 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_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.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_t kBucketId = GLES2Implementation::kResultBucketId; |
| const GLuint kProgramId = 123; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmd::GetBucketStart::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result3 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result4 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result3.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result4.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| // try bufsize not big enough. |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetProgramInfoCHROMIUM get_program_info; |
| cmd::GetBucketStart get_bucket_start; |
| 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_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.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_F(GLES2ImplementationTest, GetUniformBlocksCHROMIUMGoodArgs) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const GLuint kProgramId = 123; |
| const char kBad = 0x12; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| ExpectedMemoryInfo mem1 = |
| GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmd::GetBucketStart::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| memset(buf, kBad, sizeof(buf)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetUniformBlocksCHROMIUM get_uniform_blocks; |
| cmd::GetBucketStart get_bucket_start; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_uniform_blocks.Init(kProgramId, kBucketId); |
| expected.get_bucket_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_bucket_size2.Init(kBucketId, 0); |
| gl_->GetUniformBlocksCHROMIUM(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, GetUniformBlocksCHROMIUMBadArgs) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const GLuint kProgramId = 123; |
| GLsizei size = 0; |
| const Str7 kString = {"foobar"}; |
| char buf[20]; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmd::GetBucketStart::Result)); |
| ExpectedMemoryInfo result2 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result3 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| ExpectedMemoryInfo result4 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .WillOnce(SetMemory(result2.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result3.ptr, GLuint(GL_NO_ERROR))) |
| .WillOnce(SetMemory(result4.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| // try bufsize not big enough. |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetUniformBlocksCHROMIUM get_uniform_blocks; |
| cmd::GetBucketStart get_bucket_start; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_uniform_blocks.Init(kProgramId, kBucketId); |
| expected.get_bucket_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_bucket_size2.Init(kBucketId, 0); |
| gl_->GetUniformBlocksCHROMIUM(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_->GetUniformBlocksCHROMIUM(kProgramId, -1, &size, &buf); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| ClearCommands(); |
| // try no size ptr. |
| gl_->GetUniformBlocksCHROMIUM(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_TEXTURE_BINDING_EXTERNAL_OES, 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); |
| } |
| |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetIntegerDisjointValue) { |
| ExpectedMemoryInfo mem = GetExpectedMappedMemory(sizeof(DisjointValueSync)); |
| gl_->SetDisjointValueSyncCHROMIUM(); |
| ASSERT_EQ(mem.id, GetQueryTracker()->DisjointCountSyncShmID()); |
| ASSERT_EQ(mem.offset, GetQueryTracker()->DisjointCountSyncShmOffset()); |
| DisjointValueSync* disjoint_sync = |
| reinterpret_cast<DisjointValueSync*>(mem.ptr); |
| |
| ClearCommands(); |
| GLint disjoint_value = -1; |
| gl_->GetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0, disjoint_value); |
| |
| // After setting disjoint, it should be true. |
| disjoint_value = -1; |
| disjoint_sync->SetDisjointCount(1); |
| gl_->GetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(1, disjoint_value); |
| |
| // After checking disjoint, it should be false again. |
| disjoint_value = -1; |
| gl_->GetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_value); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0, disjoint_value); |
| |
| // Check for errors. |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, 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); |
| gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, 8); |
| |
| 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, }, |
| {GL_TEXTURE_BINDING_EXTERNAL_OES, 8, }, }; |
| 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); |
| } |
| |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| } |
| |
| static bool CheckRect(int width, |
| int height, |
| GLenum format, |
| GLenum type, |
| int alignment, |
| const uint8_t* r1, |
| const uint8_t* r2) { |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| if (!GLES2Util::ComputeImageDataSizes( |
| width, height, 1, format, type, alignment, &size, &unpadded_row_size, |
| &padded_row_size)) { |
| return false; |
| } |
| |
| int r2_stride = static_cast<int>(padded_row_size); |
| |
| 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_P7(CheckRectAction, width, height, format, type, alignment, r1, r2) { |
| EXPECT_TRUE(CheckRect( |
| width, height, format, type, alignment, r1, r2)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TexImage2D) { |
| struct Cmds { |
| cmds::TexImage2D tex_image_2d; |
| cmd::SetToken set_token; |
| }; |
| struct Cmds2 { |
| cmds::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_t 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, |
| }; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(sizeof(pixels)); |
| |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kFormat, kType, |
| mem1.id, mem1.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, |
| pixels, mem1.ptr)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TexImage2DViaMappedMem) { |
| if (!AllowExtraTransferBufferSize()) { |
| LOG(WARNING) << "Low memory device do not support MappedMem. Skipping test"; |
| return; |
| } |
| |
| struct Cmds { |
| cmds::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 GLint kBorder = 0; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| const GLsizei kMaxHeight = (MaxTransferBufferSize() / padded_row_size) * 2; |
| const GLsizei kHeight = kMaxHeight * 2; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMappedMemory(size); |
| |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kFormat, kType, |
| mem1.id, mem1.offset); |
| expected.set_token.Init(GetNextToken()); |
| 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, kFormat, kType, kPixelStoreUnpackAlignment, |
| pixels.get(), mem1.ptr)); |
| } |
| |
| // Test TexImage2D with 2 writes |
| TEST_F(GLES2ImplementationTest, TexImage2DViaTexSubImage2D) { |
| // Set limit to 1 to effectively disable mapped memory. |
| SetMappedMemoryLimit(1); |
| |
| struct Cmds { |
| cmds::TexImage2D tex_image_2d; |
| cmds::TexSubImage2D tex_sub_image_2d1; |
| cmd::SetToken set_token1; |
| cmds::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_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| const GLsizei kHeight = (MaxTransferBufferSize() / padded_row_size) * 2; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, NULL, NULL)); |
| uint32_t half_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight / 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &half_size, NULL, NULL)); |
| |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(half_size); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(half_size); |
| |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kFormat, kType, |
| 0, 0); |
| expected.tex_sub_image_2d1.Init( |
| kTarget, kLevel, 0, 0, kWidth, kHeight / 2, kFormat, kType, |
| mem1.id, mem1.offset, true); |
| expected.set_token1.Init(GetNextToken()); |
| expected.tex_sub_image_2d2.Init( |
| kTarget, kLevel, 0, kHeight / 2, kWidth, kHeight / 2, kFormat, kType, |
| mem2.id, mem2.offset, 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(), |
| // GetExpectedTransferAddressFromOffsetAs<uint8_t>(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, |
| pixels.get() + kHeight / 2 * padded_row_size, mem2.ptr)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, SubImage2DUnpack) { |
| static const GLint unpack_alignments[] = { 1, 2, 4, 8 }; |
| |
| static const GLenum kFormat = GL_RGB; |
| static const GLenum kType = GL_UNSIGNED_BYTE; |
| static const GLint kLevel = 0; |
| static const GLint kBorder = 0; |
| // We're testing using the unpack params to pull a subimage out of a larger |
| // source of pixels. Here we specify the subimage by its border rows / |
| // columns. |
| static const GLint kSrcWidth = 33; |
| static const GLint kSrcSubImageX0 = 11; |
| static const GLint kSrcSubImageX1 = 20; |
| static const GLint kSrcSubImageY0 = 18; |
| static const GLint kSrcSubImageY1 = 23; |
| static const GLint kSrcSubImageWidth = kSrcSubImageX1 - kSrcSubImageX0; |
| static const GLint kSrcSubImageHeight = kSrcSubImageY1 - kSrcSubImageY0; |
| |
| // these are only used in the texsubimage tests |
| static const GLint kTexWidth = 1023; |
| static const GLint kTexHeight = 511; |
| static const GLint kTexSubXOffset = 419; |
| static const GLint kTexSubYOffset = 103; |
| |
| struct { |
| cmds::PixelStorei pixel_store_i; |
| cmds::TexImage2D tex_image_2d; |
| } texImageExpected; |
| |
| struct { |
| cmds::PixelStorei pixel_store_i; |
| cmds::TexImage2D tex_image_2d; |
| cmds::TexSubImage2D tex_sub_image_2d; |
| } texSubImageExpected; |
| |
| uint32_t pixel_size; |
| PixelStoreParams pixel_params; |
| // Makes sure the pixels size is large enough for all tests. |
| pixel_params.alignment = 8; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcWidth, kSrcSubImageY1, 1, kFormat, kType, |
| pixel_params, &pixel_size, nullptr, nullptr, nullptr, nullptr)); |
| std::unique_ptr<uint8_t[]> src_pixels; |
| src_pixels.reset(new uint8_t[pixel_size]); |
| for (size_t i = 0; i < pixel_size; ++i) { |
| src_pixels[i] = static_cast<uint8_t>(i % 255); |
| } |
| |
| for (int sub = 0; sub < 2; ++sub) { |
| for (size_t a = 0; a < arraysize(unpack_alignments); ++a) { |
| const void* commands = GetPut(); |
| |
| GLint alignment = unpack_alignments[a]; |
| gl_->PixelStorei(GL_UNPACK_ALIGNMENT, alignment); |
| gl_->PixelStorei(GL_UNPACK_ROW_LENGTH, kSrcWidth); |
| gl_->PixelStorei(GL_UNPACK_SKIP_PIXELS, kSrcSubImageX0); |
| gl_->PixelStorei(GL_UNPACK_SKIP_ROWS, kSrcSubImageY0); |
| |
| uint32_t client_size; |
| uint32_t client_unpadded_row_size; |
| uint32_t client_padded_row_size; |
| uint32_t client_skip_size; |
| { |
| PixelStoreParams params; |
| params.alignment = alignment; |
| params.row_length = kSrcWidth; |
| params.skip_pixels = kSrcSubImageX0; |
| params.skip_rows = kSrcSubImageY0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcSubImageWidth, kSrcSubImageHeight, 1, kFormat, kType, params, |
| &client_size, &client_unpadded_row_size, &client_padded_row_size, |
| &client_skip_size, nullptr)); |
| ASSERT_TRUE(client_size + client_skip_size <= pixel_size); |
| } |
| |
| uint32_t service_size; |
| uint32_t service_unpadded_row_size; |
| uint32_t service_padded_row_size; |
| uint32_t service_skip_size; |
| { |
| PixelStoreParams params; |
| // For pixels we send to service side, we already applied all unpack |
| // parameters except for UNPACK_ALIGNMENT. |
| params.alignment = alignment; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcSubImageWidth, kSrcSubImageHeight, 1, kFormat, kType, params, |
| &service_size, &service_unpadded_row_size, &service_padded_row_size, |
| &service_skip_size, nullptr)); |
| ASSERT_TRUE(service_size <= MaxTransferBufferSize()); |
| ASSERT_TRUE(service_skip_size == 0); |
| ASSERT_TRUE(client_unpadded_row_size == service_unpadded_row_size); |
| } |
| |
| ExpectedMemoryInfo mem = GetExpectedMemory(service_size); |
| if (sub) { |
| gl_->TexImage2D( |
| GL_TEXTURE_2D, kLevel, kFormat, kTexWidth, kTexHeight, kBorder, |
| kFormat, kType, nullptr); |
| gl_->TexSubImage2D( |
| GL_TEXTURE_2D, kLevel, kTexSubXOffset, kTexSubYOffset, |
| kSrcSubImageWidth, kSrcSubImageHeight, kFormat, kType, |
| src_pixels.get()); |
| texSubImageExpected.pixel_store_i.Init(GL_UNPACK_ALIGNMENT, alignment); |
| texSubImageExpected.tex_image_2d.Init( |
| GL_TEXTURE_2D, kLevel, kFormat, kTexWidth, kTexHeight, |
| kFormat, kType, 0, 0); |
| texSubImageExpected.tex_sub_image_2d.Init( |
| GL_TEXTURE_2D, kLevel, kTexSubXOffset, kTexSubYOffset, |
| kSrcSubImageWidth, kSrcSubImageHeight, kFormat, kType, mem.id, |
| mem.offset, GL_FALSE); |
| EXPECT_EQ(0, memcmp(&texSubImageExpected, commands, |
| sizeof(texSubImageExpected))); |
| } else { |
| gl_->TexImage2D( |
| GL_TEXTURE_2D, kLevel, kFormat, |
| kSrcSubImageWidth, kSrcSubImageHeight, kBorder, kFormat, kType, |
| src_pixels.get()); |
| texImageExpected.pixel_store_i.Init(GL_UNPACK_ALIGNMENT, alignment); |
| texImageExpected.tex_image_2d.Init( |
| GL_TEXTURE_2D, kLevel, kFormat, kSrcSubImageWidth, |
| kSrcSubImageHeight, kFormat, kType, mem.id, mem.offset); |
| EXPECT_EQ(0, memcmp(&texImageExpected, commands, |
| sizeof(texImageExpected))); |
| } |
| for (int y = 0; y < kSrcSubImageHeight; ++y) { |
| const uint8_t* src_row = |
| src_pixels.get() + client_skip_size + y * client_padded_row_size; |
| const uint8_t* dst_row = mem.ptr + y * service_padded_row_size; |
| EXPECT_EQ(0, memcmp(src_row, dst_row, service_unpadded_row_size)); |
| } |
| ClearCommands(); |
| } |
| } |
| } |
| |
| TEST_F(GLES3ImplementationTest, SubImage3DUnpack) { |
| static const GLint unpack_alignments[] = { 1, 2, 4, 8 }; |
| |
| static const GLenum kFormat = GL_RGB; |
| static const GLenum kType = GL_UNSIGNED_BYTE; |
| static const GLint kLevel = 0; |
| static const GLint kBorder = 0; |
| // We're testing using the unpack params to pull a subimage out of a larger |
| // source of pixels. Here we specify the subimage by its border rows / |
| // columns. |
| static const GLint kSrcWidth = 23; |
| static const GLint kSrcHeight = 7; |
| static const GLint kSrcSubImageX0 = 11; |
| static const GLint kSrcSubImageX1 = 16; |
| static const GLint kSrcSubImageY0 = 20; |
| static const GLint kSrcSubImageY1 = 23; |
| static const GLint kSrcSubImageZ0 = 2; |
| static const GLint kSrcSubImageZ1 = 5; |
| static const GLint kSrcSubImageWidth = kSrcSubImageX1 - kSrcSubImageX0; |
| static const GLint kSrcSubImageHeight = kSrcSubImageY1 - kSrcSubImageY0; |
| static const GLint kSrcSubImageDepth = kSrcSubImageZ1 - kSrcSubImageZ0; |
| |
| // these are only used in the texsubimage tests |
| static const GLint kTexWidth = 255; |
| static const GLint kTexHeight = 127; |
| static const GLint kTexDepth = 11; |
| static const GLint kTexSubXOffset = 119; |
| static const GLint kTexSubYOffset = 63; |
| static const GLint kTexSubZOffset = 1; |
| |
| struct { |
| cmds::PixelStorei pixel_store_i[3]; |
| cmds::TexImage3D tex_image_3d; |
| } texImageExpected; |
| |
| struct { |
| cmds::PixelStorei pixel_store_i[3]; |
| cmds::TexImage3D tex_image_3d; |
| cmds::TexSubImage3D tex_sub_image_3d; |
| } texSubImageExpected; |
| |
| uint32_t pixel_size; |
| PixelStoreParams pixel_params; |
| // Makes sure the pixels size is large enough for all tests. |
| pixel_params.alignment = 8; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcWidth, kSrcSubImageY1, kSrcSubImageZ1, kFormat, kType, |
| pixel_params, &pixel_size, nullptr, nullptr, nullptr, nullptr)); |
| std::unique_ptr<uint8_t[]> src_pixels; |
| src_pixels.reset(new uint8_t[pixel_size]); |
| for (size_t i = 0; i < pixel_size; ++i) { |
| src_pixels[i] = static_cast<uint8_t>(i % 255); |
| } |
| |
| for (int sub = 0; sub < 2; ++sub) { |
| for (size_t a = 0; a < arraysize(unpack_alignments); ++a) { |
| const void* commands = GetPut(); |
| |
| GLint alignment = unpack_alignments[a]; |
| gl_->PixelStorei(GL_UNPACK_ALIGNMENT, alignment); |
| gl_->PixelStorei(GL_UNPACK_ROW_LENGTH, kSrcWidth); |
| gl_->PixelStorei(GL_UNPACK_IMAGE_HEIGHT, kSrcHeight); |
| gl_->PixelStorei(GL_UNPACK_SKIP_PIXELS, kSrcSubImageX0); |
| gl_->PixelStorei(GL_UNPACK_SKIP_ROWS, kSrcSubImageY0); |
| gl_->PixelStorei(GL_UNPACK_SKIP_IMAGES, kSrcSubImageZ0); |
| |
| uint32_t client_size; |
| uint32_t client_unpadded_row_size; |
| uint32_t client_padded_row_size; |
| uint32_t client_skip_size; |
| { |
| PixelStoreParams params; |
| params.alignment = alignment; |
| params.row_length = kSrcWidth; |
| params.image_height = kSrcHeight; |
| params.skip_pixels = kSrcSubImageX0; |
| params.skip_rows = kSrcSubImageY0; |
| params.skip_images = kSrcSubImageZ0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kFormat, kType, params, |
| &client_size, &client_unpadded_row_size, &client_padded_row_size, |
| &client_skip_size, nullptr)); |
| ASSERT_TRUE(client_size + client_skip_size <= pixel_size); |
| } |
| |
| uint32_t service_size; |
| uint32_t service_unpadded_row_size; |
| uint32_t service_padded_row_size; |
| uint32_t service_skip_size; |
| { |
| PixelStoreParams params; |
| // For pixels we send to service side, we already applied all unpack |
| // parameters except for UNPACK_ALIGNMENT. |
| params.alignment = alignment; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizesES3( |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kFormat, kType, params, |
| &service_size, &service_unpadded_row_size, &service_padded_row_size, |
| &service_skip_size, nullptr)); |
| ASSERT_TRUE(service_size <= MaxTransferBufferSize()); |
| ASSERT_TRUE(service_skip_size == 0); |
| ASSERT_TRUE(client_unpadded_row_size == service_unpadded_row_size); |
| } |
| |
| ExpectedMemoryInfo mem = GetExpectedMemory(service_size); |
| if (sub) { |
| gl_->TexImage3D( |
| GL_TEXTURE_3D, kLevel, kFormat, kTexWidth, kTexHeight, kTexDepth, |
| kBorder, kFormat, kType, nullptr); |
| gl_->TexSubImage3D( |
| GL_TEXTURE_3D, kLevel, |
| kTexSubXOffset, kTexSubYOffset, kTexSubZOffset, |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kFormat, kType, src_pixels.get()); |
| texSubImageExpected.pixel_store_i[0].Init( |
| GL_UNPACK_ALIGNMENT, alignment); |
| texSubImageExpected.pixel_store_i[1].Init( |
| GL_UNPACK_ROW_LENGTH, kSrcWidth); |
| texSubImageExpected.pixel_store_i[2].Init( |
| GL_UNPACK_IMAGE_HEIGHT, kSrcHeight); |
| texSubImageExpected.tex_image_3d.Init( |
| GL_TEXTURE_3D, kLevel, kFormat, kTexWidth, kTexHeight, kTexDepth, |
| kFormat, kType, 0, 0); |
| texSubImageExpected.tex_sub_image_3d.Init( |
| GL_TEXTURE_3D, kLevel, |
| kTexSubXOffset, kTexSubYOffset, kTexSubZOffset, |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kFormat, kType, mem.id, mem.offset, GL_FALSE); |
| EXPECT_EQ(0, memcmp(&texSubImageExpected, commands, |
| sizeof(texSubImageExpected))); |
| } else { |
| gl_->TexImage3D( |
| GL_TEXTURE_3D, kLevel, kFormat, |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kBorder, kFormat, kType, src_pixels.get()); |
| texImageExpected.pixel_store_i[0].Init(GL_UNPACK_ALIGNMENT, alignment); |
| texImageExpected.pixel_store_i[1].Init( |
| GL_UNPACK_ROW_LENGTH, kSrcWidth); |
| texImageExpected.pixel_store_i[2].Init( |
| GL_UNPACK_IMAGE_HEIGHT, kSrcHeight); |
| texImageExpected.tex_image_3d.Init( |
| GL_TEXTURE_3D, kLevel, kFormat, |
| kSrcSubImageWidth, kSrcSubImageHeight, kSrcSubImageDepth, |
| kFormat, kType, mem.id, mem.offset); |
| EXPECT_EQ(0, memcmp(&texImageExpected, commands, |
| sizeof(texImageExpected))); |
| } |
| for (int z = 0; z < kSrcSubImageDepth; ++z) { |
| for (int y = 0; y < kSrcSubImageHeight; ++y) { |
| const uint8_t* src_row = src_pixels.get() + client_skip_size + |
| (kSrcHeight * z + y) * client_padded_row_size; |
| const uint8_t* dst_row = mem.ptr + |
| (kSrcSubImageHeight * z + y) * service_padded_row_size; |
| EXPECT_EQ(0, memcmp(src_row, dst_row, service_unpadded_row_size)); |
| } |
| } |
| ClearCommands(); |
| } |
| } |
| } |
| |
| // Test texture related calls with invalid arguments. |
| TEST_F(GLES2ImplementationTest, TextureInvalidArguments) { |
| struct Cmds { |
| cmds::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 GLint kInvalidBorder = 1; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| static uint8_t 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, |
| }; |
| |
| // Verify that something works. |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(sizeof(pixels)); |
| |
| Cmds expected; |
| expected.tex_image_2d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kFormat, kType, |
| mem1.id, mem1.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, |
| pixels, mem1.ptr)); |
| |
| ClearCommands(); |
| |
| // Use invalid border. |
| gl_->TexImage2D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kInvalidBorder, kFormat, kType, |
| pixels); |
| |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| |
| ClearCommands(); |
| |
| // Checking for CompressedTexImage2D argument validation is a bit tricky due |
| // to (runtime-detected) compression formats. Try to infer the error with an |
| // aux check. |
| const GLenum kCompressedFormat = GL_ETC1_RGB8_OES; |
| gl_->CompressedTexImage2D( |
| kTarget, kLevel, kCompressedFormat, kWidth, kHeight, kBorder, |
| arraysize(pixels), pixels); |
| |
| // In the above, kCompressedFormat and arraysize(pixels) are possibly wrong |
| // values. First ensure that these do not cause failures at the client. If |
| // this check ever fails, it probably means that client checks more than at |
| // the time of writing of this test. In this case, more code needs to be |
| // written for this test. |
| EXPECT_FALSE(NoCommandsWritten()); |
| |
| ClearCommands(); |
| |
| // Changing border to invalid border should make the call fail at the client |
| // checks. |
| gl_->CompressedTexImage2D( |
| kTarget, kLevel, kCompressedFormat, kWidth, kHeight, kInvalidBorder, |
| arraysize(pixels), pixels); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TexImage3DSingleCommand) { |
| struct Cmds { |
| cmds::TexImage3D tex_image_3d; |
| }; |
| const GLenum kTarget = GL_TEXTURE_3D; |
| const GLint kLevel = 0; |
| const GLint kBorder = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kWidth = 3; |
| const GLsizei kDepth = 2; |
| |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, kDepth, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| // Makes sure we can just send over the data in one command. |
| const GLsizei kHeight = MaxTransferBufferSize() / padded_row_size / kDepth; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, kDepth, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, NULL, NULL)); |
| |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| ExpectedMemoryInfo mem = GetExpectedMemory(size); |
| |
| Cmds expected; |
| expected.tex_image_3d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kDepth, |
| kFormat, kType, mem.id, mem.offset); |
| |
| gl_->TexImage3D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kDepth, kBorder, |
| kFormat, kType, pixels.get()); |
| |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(CheckRect(kWidth, kHeight * kDepth, kFormat, kType, |
| kPixelStoreUnpackAlignment, |
| reinterpret_cast<uint8_t*>(pixels.get()), mem.ptr)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TexImage3DViaMappedMem) { |
| if (!AllowExtraTransferBufferSize()) { |
| LOG(WARNING) << "Low memory device do not support MappedMem. Skipping test"; |
| return; |
| } |
| |
| struct Cmds { |
| cmds::TexImage3D tex_image_3d; |
| }; |
| const GLenum kTarget = GL_TEXTURE_3D; |
| const GLint kLevel = 0; |
| const GLint kBorder = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kWidth = 3; |
| const GLsizei kDepth = 2; |
| |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, kDepth, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| // Makes sure we can just send over the data in one command. |
| const GLsizei kMaxHeight = MaxTransferBufferSize() / padded_row_size / kDepth; |
| const GLsizei kHeight = kMaxHeight * 2; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, kDepth, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, NULL, NULL)); |
| |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| ExpectedMemoryInfo mem = GetExpectedMappedMemory(size); |
| |
| Cmds expected; |
| expected.tex_image_3d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kDepth, |
| kFormat, kType, mem.id, mem.offset); |
| |
| gl_->TexImage3D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, kDepth, kBorder, |
| kFormat, kType, pixels.get()); |
| |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(CheckRect(kWidth, kHeight * kDepth, kFormat, kType, |
| kPixelStoreUnpackAlignment, |
| reinterpret_cast<uint8_t*>(pixels.get()), mem.ptr)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TexImage3DViaTexSubImage3D) { |
| // Set limit to 1 to effectively disable mapped memory. |
| SetMappedMemoryLimit(1); |
| |
| struct Cmds { |
| cmds::TexImage3D tex_image_3d; |
| cmds::TexSubImage3D tex_sub_image_3d1; |
| cmd::SetToken set_token; |
| cmds::TexSubImage3D tex_sub_image_3d2; |
| }; |
| const GLenum kTarget = GL_TEXTURE_3D; |
| const GLint kLevel = 0; |
| const GLint kBorder = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kWidth = 3; |
| |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| // Makes sure the data is more than one command can hold. |
| const GLsizei kHeight = MaxTransferBufferSize() / padded_row_size + 3; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, NULL, NULL)); |
| uint32_t first_size = padded_row_size * (kHeight - 3); |
| uint32_t second_size = |
| padded_row_size * 3 - (padded_row_size - unpadded_row_size); |
| EXPECT_EQ(size, first_size + second_size); |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(first_size); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(second_size); |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| Cmds expected; |
| expected.tex_image_3d.Init( |
| kTarget, kLevel, kFormat, kWidth, kHeight, 1, kFormat, kType, 0, 0); |
| expected.tex_sub_image_3d1.Init( |
| kTarget, kLevel, 0, 0, 0, kWidth, kHeight - 3, 1, kFormat, kType, |
| mem1.id, mem1.offset, GL_TRUE); |
| expected.tex_sub_image_3d2.Init( |
| kTarget, kLevel, 0, kHeight - 3, 0, kWidth, 3, 1, kFormat, kType, |
| mem2.id, mem2.offset, GL_TRUE); |
| expected.set_token.Init(GetNextToken()); |
| |
| gl_->TexImage3D( |
| kTarget, kLevel, kFormat, kWidth, kHeight, 1, kBorder, |
| kFormat, kType, pixels.get()); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| // Test TexSubImage3D with 4 writes |
| TEST_F(GLES2ImplementationTest, TexSubImage3D4Writes) { |
| struct Cmds { |
| cmds::TexSubImage3D tex_sub_image_3d1_1; |
| cmd::SetToken set_token1; |
| cmds::TexSubImage3D tex_sub_image_3d1_2; |
| cmd::SetToken set_token2; |
| cmds::TexSubImage3D tex_sub_image_3d2_1; |
| cmd::SetToken set_token3; |
| cmds::TexSubImage3D tex_sub_image_3d2_2; |
| }; |
| const GLenum kTarget = GL_TEXTURE_3D; |
| const GLint kLevel = 0; |
| const GLint kXOffset = 0; |
| const GLint kYOffset = 0; |
| const GLint kZOffset = 0; |
| const GLenum kFormat = GL_RGB; |
| const GLenum kType = GL_UNSIGNED_BYTE; |
| const GLint kPixelStoreUnpackAlignment = 4; |
| const GLsizei kWidth = 3; |
| const GLsizei kDepth = 2; |
| |
| uint32_t size = 0; |
| uint32_t unpadded_row_size = 0; |
| uint32_t padded_row_size = 0; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, 2, 1, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, &unpadded_row_size, &padded_row_size)); |
| const GLsizei kHeight = MaxTransferBufferSize() / padded_row_size + 2; |
| ASSERT_TRUE(GLES2Util::ComputeImageDataSizes( |
| kWidth, kHeight, kDepth, kFormat, kType, kPixelStoreUnpackAlignment, |
| &size, NULL, NULL)); |
| uint32_t first_size = (kHeight - 2) * padded_row_size; |
| uint32_t second_size = 2 * padded_row_size; |
| uint32_t third_size = first_size; |
| uint32_t fourth_size = second_size - (padded_row_size - unpadded_row_size); |
| EXPECT_EQ(size, first_size + second_size + third_size + fourth_size); |
| |
| std::unique_ptr<uint8_t[]> pixels(new uint8_t[size]); |
| for (uint32_t ii = 0; ii < size; ++ii) { |
| pixels[ii] = static_cast<uint8_t>(ii); |
| } |
| |
| ExpectedMemoryInfo mem1_1 = GetExpectedMemory(first_size); |
| ExpectedMemoryInfo mem1_2 = GetExpectedMemory(second_size); |
| ExpectedMemoryInfo mem2_1 = GetExpectedMemory(third_size); |
| ExpectedMemoryInfo mem2_2 = GetExpectedMemory(fourth_size); |
| |
| Cmds expected; |
| expected.tex_sub_image_3d1_1.Init( |
| kTarget, kLevel, kXOffset, kYOffset, kZOffset, |
| kWidth, kHeight - 2, 1, kFormat, kType, |
| mem1_1.id, mem1_1.offset, GL_FALSE); |
| expected.tex_sub_image_3d1_2.Init( |
| kTarget, kLevel, kXOffset, kYOffset + kHeight - 2, kZOffset, |
| kWidth, 2, 1, kFormat, kType, mem1_2.id, mem1_2.offset, GL_FALSE); |
| expected.tex_sub_image_3d2_1.Init( |
| kTarget, kLevel, kXOffset, kYOffset, kZOffset + 1, |
| kWidth, kHeight - 2, 1, kFormat, kType, |
| mem2_1.id, mem2_1.offset, GL_FALSE); |
| expected.tex_sub_image_3d2_2.Init( |
| kTarget, kLevel, kXOffset, kYOffset + kHeight - 2, kZOffset + 1, |
| kWidth, 2, 1, kFormat, kType, mem2_2.id, mem2_2.offset, GL_FALSE); |
| expected.set_token1.Init(GetNextToken()); |
| expected.set_token2.Init(GetNextToken()); |
| expected.set_token3.Init(GetNextToken()); |
| |
| gl_->TexSubImage3D( |
| kTarget, kLevel, kXOffset, kYOffset, kZOffset, kWidth, kHeight, kDepth, |
| kFormat, kType, pixels.get()); |
| |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| uint32_t offset_to_last = first_size + second_size + third_size; |
| EXPECT_TRUE(CheckRect( |
| kWidth, 2, kFormat, kType, kPixelStoreUnpackAlignment, |
| reinterpret_cast<uint8_t*>(pixels.get()) + offset_to_last, mem2_2.ptr)); |
| } |
| |
| // glGen* Ids must not be reused until glDelete* commands have been |
| // flushed by glFlush. |
| TEST_F(GLES2ImplementationStrictSharedTest, FlushGenerationTestBuffers) { |
| FlushGenerationTest<GenBuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, FlushGenerationTestFramebuffers) { |
| FlushGenerationTest<GenFramebuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, FlushGenerationTestRenderbuffers) { |
| FlushGenerationTest<GenRenderbuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, FlushGenerationTestTextures) { |
| FlushGenerationTest<GenTexturesAPI>(); |
| } |
| |
| // glGen* Ids must not be reused cross-context until glDelete* commands are |
| // flushed by glFlush, and the Ids are lazily freed after. |
| TEST_F(GLES2ImplementationStrictSharedTest, CrossContextGenerationTestBuffers) { |
| CrossContextGenerationTest<GenBuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationTestFramebuffers) { |
| CrossContextGenerationTest<GenFramebuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationTestRenderbuffers) { |
| CrossContextGenerationTest<GenRenderbuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationTestTextures) { |
| CrossContextGenerationTest<GenTexturesAPI>(); |
| } |
| |
| // Test Delete which causes auto flush. Tests a regression case that occurred |
| // in testing. |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationAutoFlushTestBuffers) { |
| CrossContextGenerationAutoFlushTest<GenBuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationAutoFlushTestFramebuffers) { |
| CrossContextGenerationAutoFlushTest<GenFramebuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationAutoFlushTestRenderbuffers) { |
| CrossContextGenerationAutoFlushTest<GenRenderbuffersAPI>(); |
| } |
| TEST_F(GLES2ImplementationStrictSharedTest, |
| CrossContextGenerationAutoFlushTestTextures) { |
| CrossContextGenerationAutoFlushTest<GenTexturesAPI>(); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetString) { |
| const uint32_t kBucketId = GLES2Implementation::kResultBucketId; |
| const Str7 kString = {"foobar"}; |
| // GL_CHROMIUM_map_sub is hard coded into GLES2Implementation. |
| const char* expected_str = |
| "foobar " |
| "GL_EXT_unpack_subimage " |
| "GL_CHROMIUM_map_sub"; |
| const char kBad = 0x12; |
| struct Cmds { |
| cmd::SetBucketSize set_bucket_size1; |
| cmds::GetString get_string; |
| cmd::GetBucketStart get_bucket_start; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize set_bucket_size2; |
| }; |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(MaxTransferBufferSize()); |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmd::GetBucketStart::Result)); |
| Cmds expected; |
| expected.set_bucket_size1.Init(kBucketId, 0); |
| expected.get_string.Init(GL_EXTENSIONS, kBucketId); |
| expected.get_bucket_start.Init( |
| kBucketId, result1.id, result1.offset, |
| MaxTransferBufferSize(), mem1.id, mem1.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(DoAll(SetMemory(result1.ptr, uint32_t(sizeof(kString))), |
| SetMemory(mem1.ptr, kString))) |
| .RetiresOnSaturation(); |
| |
| const GLubyte* result = gl_->GetString(GL_EXTENSIONS); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_STREQ(expected_str, reinterpret_cast<const char*>(result)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, CreateProgram) { |
| struct Cmds { |
| cmds::CreateProgram cmd; |
| }; |
| |
| Cmds expected; |
| expected.cmd.Init(kProgramsAndShadersStartId); |
| GLuint id = gl_->CreateProgram(); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(kProgramsAndShadersStartId, id); |
| } |
| |
| TEST_F(GLES2ImplementationTest, BufferDataLargerThanTransferBuffer) { |
| struct Cmds { |
| cmds::BufferData set_size; |
| cmds::BufferSubData copy_data1; |
| cmd::SetToken set_token1; |
| cmds::BufferSubData copy_data2; |
| cmd::SetToken set_token2; |
| }; |
| const unsigned kUsableSize = |
| kTransferBufferSize - GLES2Implementation::kStartingOffset; |
| uint8_t buf[kUsableSize * 2] = { |
| 0, |
| }; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kUsableSize); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kUsableSize); |
| |
| Cmds expected; |
| expected.set_size.Init( |
| GL_ARRAY_BUFFER, arraysize(buf), 0, 0, GL_DYNAMIC_DRAW); |
| expected.copy_data1.Init( |
| GL_ARRAY_BUFFER, 0, kUsableSize, mem1.id, mem1.offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.copy_data2.Init( |
| GL_ARRAY_BUFFER, kUsableSize, kUsableSize, mem2.id, mem2.offset); |
| expected.set_token2.Init(GetNextToken()); |
| gl_->BufferData(GL_ARRAY_BUFFER, arraysize(buf), buf, GL_DYNAMIC_DRAW); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, CapabilitiesAreCached) { |
| static const GLenum kStates[] = { |
| GL_DITHER, |
| GL_BLEND, |
| GL_CULL_FACE, |
| GL_DEPTH_TEST, |
| GL_POLYGON_OFFSET_FILL, |
| GL_SAMPLE_ALPHA_TO_COVERAGE, |
| GL_SAMPLE_COVERAGE, |
| GL_SCISSOR_TEST, |
| GL_STENCIL_TEST, |
| }; |
| struct Cmds { |
| cmds::Enable enable_cmd; |
| }; |
| Cmds expected; |
| |
| for (size_t ii = 0; ii < arraysize(kStates); ++ii) { |
| GLenum state = kStates[ii]; |
| expected.enable_cmd.Init(state); |
| GLboolean result = gl_->IsEnabled(state); |
| EXPECT_EQ(static_cast<GLboolean>(ii == 0), result); |
| EXPECT_TRUE(NoCommandsWritten()); |
| const void* commands = GetPut(); |
| if (!result) { |
| gl_->Enable(state); |
| EXPECT_EQ(0, memcmp(&expected, commands, sizeof(expected))); |
| } |
| ClearCommands(); |
| result = gl_->IsEnabled(state); |
| EXPECT_TRUE(result); |
| EXPECT_TRUE(NoCommandsWritten()); |
| } |
| } |
| |
| TEST_F(GLES2ImplementationTest, BindVertexArrayOES) { |
| GLuint id = 0; |
| gl_->GenVertexArraysOES(1, &id); |
| ClearCommands(); |
| |
| struct Cmds { |
| cmds::BindVertexArrayOES cmd; |
| }; |
| Cmds expected; |
| expected.cmd.Init(id); |
| |
| const void* commands = GetPut(); |
| gl_->BindVertexArrayOES(id); |
| EXPECT_EQ(0, memcmp(&expected, commands, sizeof(expected))); |
| ClearCommands(); |
| gl_->BindVertexArrayOES(id); |
| EXPECT_TRUE(NoCommandsWritten()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, BeginEndQueryEXT) { |
| // Test GetQueryivEXT returns 0 if no current query. |
| GLint param = -1; |
| gl_->GetQueryivEXT(GL_ANY_SAMPLES_PASSED_EXT, GL_CURRENT_QUERY_EXT, ¶m); |
| EXPECT_EQ(0, param); |
| |
| GLuint expected_ids[2] = { 1, 2 }; // These must match what's actually genned. |
| struct GenCmds { |
| cmds::GenQueriesEXTImmediate gen; |
| GLuint data[2]; |
| }; |
| GenCmds expected_gen_cmds; |
| expected_gen_cmds.gen.Init(arraysize(expected_ids), &expected_ids[0]); |
| GLuint ids[arraysize(expected_ids)] = { 0, }; |
| gl_->GenQueriesEXT(arraysize(expected_ids), &ids[0]); |
| EXPECT_EQ(0, memcmp( |
| &expected_gen_cmds, commands_, sizeof(expected_gen_cmds))); |
| GLuint id1 = ids[0]; |
| GLuint id2 = ids[1]; |
| ClearCommands(); |
| |
| // Test BeginQueryEXT fails if id = 0. |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, 0); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test BeginQueryEXT inserts command. |
| struct BeginCmds { |
| cmds::BeginQueryEXT begin_query; |
| }; |
| BeginCmds expected_begin_cmds; |
| const void* commands = GetPut(); |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id1); |
| QueryTracker::Query* query = GetQuery(id1); |
| ASSERT_TRUE(query != NULL); |
| expected_begin_cmds.begin_query.Init( |
| GL_ANY_SAMPLES_PASSED_EXT, id1, query->shm_id(), query->shm_offset()); |
| EXPECT_EQ(0, memcmp( |
| &expected_begin_cmds, commands, sizeof(expected_begin_cmds))); |
| ClearCommands(); |
| |
| // Test GetQueryivEXT returns id. |
| param = -1; |
| gl_->GetQueryivEXT(GL_ANY_SAMPLES_PASSED_EXT, GL_CURRENT_QUERY_EXT, ¶m); |
| EXPECT_EQ(id1, static_cast<GLuint>(param)); |
| gl_->GetQueryivEXT( |
| GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, GL_CURRENT_QUERY_EXT, ¶m); |
| EXPECT_EQ(0, param); |
| |
| // Test BeginQueryEXT fails if between Begin/End. |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id2); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test EndQueryEXT fails if target not same as current query. |
| ClearCommands(); |
| gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test EndQueryEXT sends command |
| struct EndCmds { |
| cmds::EndQueryEXT end_query; |
| }; |
| EndCmds expected_end_cmds; |
| expected_end_cmds.end_query.Init( |
| GL_ANY_SAMPLES_PASSED_EXT, query->submit_count()); |
| commands = GetPut(); |
| gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| EXPECT_EQ(0, memcmp( |
| &expected_end_cmds, commands, sizeof(expected_end_cmds))); |
| |
| // Test EndQueryEXT fails if no current query. |
| ClearCommands(); |
| gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test 2nd Begin/End increments count. |
| base::subtle::Atomic32 old_submit_count = query->submit_count(); |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id1); |
| EXPECT_NE(old_submit_count, query->submit_count()); |
| expected_end_cmds.end_query.Init( |
| GL_ANY_SAMPLES_PASSED_EXT, query->submit_count()); |
| commands = GetPut(); |
| gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| EXPECT_EQ(0, memcmp( |
| &expected_end_cmds, commands, sizeof(expected_end_cmds))); |
| |
| // Test BeginQueryEXT fails if target changed. |
| ClearCommands(); |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, id1); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test GetQueryObjectuivEXT fails if unused id |
| GLuint available = 0xBDu; |
| ClearCommands(); |
| gl_->GetQueryObjectuivEXT(id2, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0xBDu, available); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test GetQueryObjectuivEXT fails if bad id |
| ClearCommands(); |
| gl_->GetQueryObjectuivEXT(4567, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0xBDu, available); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test GetQueryObjectuivEXT CheckResultsAvailable |
| ClearCommands(); |
| gl_->GetQueryObjectuivEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_EQ(0u, available); |
| |
| // Test GetQueryObjectui64vEXT fails if unused id |
| GLuint64 available2 = 0xBDu; |
| ClearCommands(); |
| gl_->GetQueryObjectui64vEXT(id2, GL_QUERY_RESULT_AVAILABLE_EXT, &available2); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0xBDu, available2); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test GetQueryObjectui64vEXT fails if bad id |
| ClearCommands(); |
| gl_->GetQueryObjectui64vEXT(4567, GL_QUERY_RESULT_AVAILABLE_EXT, &available2); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0xBDu, available2); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test GetQueryObjectui64vEXT CheckResultsAvailable |
| ClearCommands(); |
| gl_->GetQueryObjectui64vEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available2); |
| EXPECT_EQ(0u, available2); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, BadQueryTargets) { |
| ContextInitOptions init_options; |
| init_options.sync_query = false; |
| init_options.occlusion_query_boolean = false; |
| init_options.timer_queries = false; |
| ASSERT_TRUE(Initialize(init_options)); |
| |
| GLuint id = 0; |
| gl_->GenQueriesEXT(1, &id); |
| ClearCommands(); |
| |
| gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED, id); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE, id); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->BeginQueryEXT(GL_TIME_ELAPSED_EXT, id); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->BeginQueryEXT(0x123, id); |
| EXPECT_EQ(GL_INVALID_ENUM, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->QueryCounterEXT(id, GL_TIMESTAMP_EXT); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| |
| gl_->QueryCounterEXT(id, 0x123); |
| EXPECT_EQ(GL_INVALID_ENUM, CheckError()); |
| EXPECT_EQ(nullptr, GetQuery(id)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, SetDisjointSync) { |
| struct SetDisjointSyncCmd { |
| cmds::SetDisjointValueSyncCHROMIUM disjoint_sync; |
| }; |
| SetDisjointSyncCmd expected_disjoint_sync_cmd; |
| const void* commands = GetPut(); |
| gl_->SetDisjointValueSyncCHROMIUM(); |
| expected_disjoint_sync_cmd.disjoint_sync.Init( |
| GetQueryTracker()->DisjointCountSyncShmID(), |
| GetQueryTracker()->DisjointCountSyncShmOffset()); |
| |
| EXPECT_EQ(0, memcmp(&expected_disjoint_sync_cmd, commands, |
| sizeof(expected_disjoint_sync_cmd))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, QueryCounterEXT) { |
| GLuint expected_ids[2] = { 1, 2 }; // These must match what's actually genned. |
| struct GenCmds { |
| cmds::GenQueriesEXTImmediate gen; |
| GLuint data[2]; |
| }; |
| GenCmds expected_gen_cmds; |
| expected_gen_cmds.gen.Init(arraysize(expected_ids), &expected_ids[0]); |
| GLuint ids[arraysize(expected_ids)] = { 0, }; |
| gl_->GenQueriesEXT(arraysize(expected_ids), &ids[0]); |
| EXPECT_EQ(0, memcmp( |
| &expected_gen_cmds, commands_, sizeof(expected_gen_cmds))); |
| GLuint id1 = ids[0]; |
| GLuint id2 = ids[1]; |
| ClearCommands(); |
| |
| // Make sure disjoint value is synchronized already. |
| gl_->SetDisjointValueSyncCHROMIUM(); |
| ClearCommands(); |
| |
| // Test QueryCounterEXT fails if id = 0. |
| gl_->QueryCounterEXT(0, GL_TIMESTAMP_EXT); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| // Test QueryCounterEXT fails if target is unknown. |
| ClearCommands(); |
| gl_->QueryCounterEXT(id1, GL_TIME_ELAPSED_EXT); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_INVALID_ENUM, CheckError()); |
| |
| // Test QueryCounterEXT inserts command. |
| struct QueryCounterCmds { |
| cmds::QueryCounterEXT query_counter; |
| }; |
| QueryCounterCmds expected_query_counter_cmds; |
| const void* commands = GetPut(); |
| gl_->QueryCounterEXT(id1, GL_TIMESTAMP_EXT); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| QueryTracker::Query* query = GetQuery(id1); |
| ASSERT_TRUE(query != NULL); |
| expected_query_counter_cmds.query_counter.Init( |
| id1, GL_TIMESTAMP_EXT, query->shm_id(), query->shm_offset(), |
| query->submit_count()); |
| EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, |
| sizeof(expected_query_counter_cmds))); |
| ClearCommands(); |
| |
| // Test 2nd QueryCounterEXT succeeds. |
| commands = GetPut(); |
| gl_->QueryCounterEXT(id2, GL_TIMESTAMP_EXT); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| QueryTracker::Query* query2 = GetQuery(id2); |
| ASSERT_TRUE(query2 != NULL); |
| expected_query_counter_cmds.query_counter.Init( |
| id2, GL_TIMESTAMP_EXT, query2->shm_id(), query2->shm_offset(), |
| query2->submit_count()); |
| EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, |
| sizeof(expected_query_counter_cmds))); |
| ClearCommands(); |
| |
| // Test QueryCounterEXT increments count. |
| base::subtle::Atomic32 old_submit_count = query->submit_count(); |
| commands = GetPut(); |
| gl_->QueryCounterEXT(id1, GL_TIMESTAMP_EXT); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| EXPECT_NE(old_submit_count, query->submit_count()); |
| expected_query_counter_cmds.query_counter.Init( |
| id1, GL_TIMESTAMP_EXT, query->shm_id(), query->shm_offset(), |
| query->submit_count()); |
| EXPECT_EQ(0, memcmp(&expected_query_counter_cmds, commands, |
| sizeof(expected_query_counter_cmds))); |
| ClearCommands(); |
| |
| // Test GetQueryObjectuivEXT CheckResultsAvailable |
| GLuint available = 0xBDu; |
| ClearCommands(); |
| gl_->GetQueryObjectuivEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_EQ(0u, available); |
| |
| // Test GetQueryObjectui64vEXT CheckResultsAvailable |
| GLuint64 available2 = 0xBDu; |
| ClearCommands(); |
| gl_->GetQueryObjectui64vEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available2); |
| EXPECT_EQ(0u, available2); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ErrorQuery) { |
| GLuint id = 0; |
| gl_->GenQueriesEXT(1, &id); |
| ClearCommands(); |
| |
| // Test BeginQueryEXT does NOT insert commands. |
| gl_->BeginQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM, id); |
| EXPECT_TRUE(NoCommandsWritten()); |
| QueryTracker::Query* query = GetQuery(id); |
| ASSERT_TRUE(query != NULL); |
| |
| // Test EndQueryEXT sends both begin and end command |
| struct EndCmds { |
| cmds::BeginQueryEXT begin_query; |
| cmds::EndQueryEXT end_query; |
| }; |
| EndCmds expected_end_cmds; |
| expected_end_cmds.begin_query.Init( |
| GL_GET_ERROR_QUERY_CHROMIUM, id, query->shm_id(), query->shm_offset()); |
| expected_end_cmds.end_query.Init( |
| GL_GET_ERROR_QUERY_CHROMIUM, query->submit_count()); |
| const void* commands = GetPut(); |
| gl_->EndQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM); |
| EXPECT_EQ(0, memcmp( |
| &expected_end_cmds, commands, sizeof(expected_end_cmds))); |
| ClearCommands(); |
| |
| // Check result is not yet available. |
| GLuint available = 0xBDu; |
| gl_->GetQueryObjectuivEXT(id, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(0u, available); |
| |
| // Test no commands are sent if there is a client side error. |
| |
| // Generate a client side error |
| gl_->ActiveTexture(GL_TEXTURE0 - 1); |
| |
| gl_->BeginQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM, id); |
| gl_->EndQueryEXT(GL_GET_ERROR_QUERY_CHROMIUM); |
| EXPECT_TRUE(NoCommandsWritten()); |
| |
| // Check result is available. |
| gl_->GetQueryObjectuivEXT(id, GL_QUERY_RESULT_AVAILABLE_EXT, &available); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_NE(0u, available); |
| |
| // Check result. |
| GLuint result = 0xBDu; |
| gl_->GetQueryObjectuivEXT(id, GL_QUERY_RESULT_EXT, &result); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLuint>(GL_INVALID_ENUM), result); |
| } |
| |
| #if !defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) |
| TEST_F(GLES2ImplementationTest, VertexArrays) { |
| const GLuint kAttribIndex1 = 1; |
| const GLint kNumComponents1 = 3; |
| const GLsizei kClientStride = 12; |
| |
| GLuint id = 0; |
| gl_->GenVertexArraysOES(1, &id); |
| ClearCommands(); |
| |
| gl_->BindVertexArrayOES(id); |
| |
| // Test that VertexAttribPointer cannot be called with a bound buffer of 0 |
| // unless the offset is NULL |
| gl_->BindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| gl_->VertexAttribPointer( |
| kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kClientStride, |
| reinterpret_cast<const void*>(4)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| gl_->VertexAttribPointer( |
| kAttribIndex1, kNumComponents1, GL_FLOAT, GL_FALSE, kClientStride, NULL); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| } |
| #endif |
| |
| TEST_F(GLES2ImplementationTest, Disable) { |
| struct Cmds { |
| cmds::Disable cmd; |
| }; |
| Cmds expected; |
| expected.cmd.Init(GL_DITHER); // Note: DITHER defaults to enabled. |
| |
| gl_->Disable(GL_DITHER); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| // Check it's cached and not called again. |
| ClearCommands(); |
| gl_->Disable(GL_DITHER); |
| EXPECT_TRUE(NoCommandsWritten()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, Enable) { |
| struct Cmds { |
| cmds::Enable cmd; |
| }; |
| Cmds expected; |
| expected.cmd.Init(GL_BLEND); // Note: BLEND defaults to disabled. |
| |
| gl_->Enable(GL_BLEND); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| // Check it's cached and not called again. |
| ClearCommands(); |
| gl_->Enable(GL_BLEND); |
| EXPECT_TRUE(NoCommandsWritten()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ConsumeTextureCHROMIUM) { |
| struct Cmds { |
| cmds::ConsumeTextureCHROMIUMImmediate cmd; |
| GLbyte data[64]; |
| }; |
| |
| Mailbox mailbox = Mailbox::Generate(); |
| Cmds expected; |
| expected.cmd.Init(GL_TEXTURE_2D, mailbox.name); |
| gl_->ConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, CreateAndConsumeTextureCHROMIUM) { |
| struct Cmds { |
| cmds::CreateAndConsumeTextureCHROMIUMImmediate cmd; |
| GLbyte data[64]; |
| }; |
| |
| Mailbox mailbox = Mailbox::Generate(); |
| Cmds expected; |
| expected.cmd.Init(GL_TEXTURE_2D, kTexturesStartId, mailbox.name); |
| GLuint id = gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(kTexturesStartId, id); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ProduceTextureCHROMIUM) { |
| struct Cmds { |
| cmds::ProduceTextureCHROMIUMImmediate cmd; |
| GLbyte data[64]; |
| }; |
| |
| Mailbox mailbox = Mailbox::Generate(); |
| Cmds expected; |
| expected.cmd.Init(GL_TEXTURE_2D, mailbox.name); |
| gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ProduceTextureDirectCHROMIUM) { |
| struct Cmds { |
| cmds::ProduceTextureDirectCHROMIUMImmediate cmd; |
| GLbyte data[64]; |
| }; |
| |
| Mailbox mailbox = Mailbox::Generate(); |
| Cmds expected; |
| expected.cmd.Init(kTexturesStartId, GL_TEXTURE_2D, mailbox.name); |
| gl_->ProduceTextureDirectCHROMIUM( |
| kTexturesStartId, GL_TEXTURE_2D, mailbox.name); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, LimitSizeAndOffsetTo32Bit) { |
| GLsizeiptr size; |
| GLintptr offset; |
| if (sizeof(size) <= 4 || sizeof(offset) <= 4) |
| return; |
| // The below two casts should be no-op, as we return early if |
| // it's 32-bit system. |
| int64_t value64 = 0x100000000; |
| size = static_cast<GLsizeiptr>(value64); |
| offset = static_cast<GLintptr>(value64); |
| |
| const char kSizeOverflowMessage[] = "size more than 32-bit"; |
| const char kOffsetOverflowMessage[] = "offset more than 32-bit"; |
| |
| const GLfloat buf[] = { 1.0, 1.0, 1.0, 1.0 }; |
| const GLubyte indices[] = { 0 }; |
| |
| const GLuint kClientArrayBufferId = 0x789; |
| const GLuint kClientElementArrayBufferId = 0x790; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kClientArrayBufferId); |
| gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, kClientElementArrayBufferId); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // Call BufferData() should succeed with legal paramaters. |
| gl_->BufferData(GL_ARRAY_BUFFER, sizeof(buf), buf, GL_DYNAMIC_DRAW); |
| gl_->BufferData( |
| GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // BufferData: size |
| gl_->BufferData(GL_ARRAY_BUFFER, size, buf, GL_DYNAMIC_DRAW); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); |
| |
| // Call BufferSubData() should succeed with legal paramaters. |
| gl_->BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(buf[0]), buf); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // BufferSubData: offset |
| gl_->BufferSubData(GL_ARRAY_BUFFER, offset, 1, buf); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); |
| |
| // BufferSubData: size |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| gl_->BufferSubData(GL_ARRAY_BUFFER, 0, size, buf); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); |
| |
| // Call MapBufferSubDataCHROMIUM() should succeed with legal paramaters. |
| void* mem = |
| gl_->MapBufferSubDataCHROMIUM(GL_ARRAY_BUFFER, 0, 1, GL_WRITE_ONLY); |
| EXPECT_TRUE(NULL != mem); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| gl_->UnmapBufferSubDataCHROMIUM(mem); |
| |
| // MapBufferSubDataCHROMIUM: offset |
| EXPECT_TRUE(NULL == gl_->MapBufferSubDataCHROMIUM( |
| GL_ARRAY_BUFFER, offset, 1, GL_WRITE_ONLY)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); |
| |
| // MapBufferSubDataCHROMIUM: size |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| EXPECT_TRUE(NULL == gl_->MapBufferSubDataCHROMIUM( |
| GL_ARRAY_BUFFER, 0, size, GL_WRITE_ONLY)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kSizeOverflowMessage, GetLastError().c_str()); |
| |
| // Call DrawElements() should succeed with legal paramaters. |
| gl_->DrawElements(GL_POINTS, 1, GL_UNSIGNED_BYTE, NULL); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // DrawElements: offset |
| gl_->DrawElements( |
| GL_POINTS, 1, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); |
| |
| // Call DrawElementsInstancedANGLE() should succeed with legal paramaters. |
| gl_->DrawElementsInstancedANGLE(GL_POINTS, 1, GL_UNSIGNED_BYTE, NULL, 1); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // DrawElementsInstancedANGLE: offset |
| gl_->DrawElementsInstancedANGLE( |
| GL_POINTS, 1, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset), 1); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); |
| |
| // Call VertexAttribPointer() should succeed with legal paramaters. |
| const GLuint kAttribIndex = 1; |
| const GLsizei kStride = 4; |
| gl_->VertexAttribPointer( |
| kAttribIndex, 1, GL_FLOAT, GL_FALSE, kStride, NULL); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // VertexAttribPointer: offset |
| gl_->VertexAttribPointer( |
| kAttribIndex, 1, GL_FLOAT, GL_FALSE, kStride, |
| reinterpret_cast<void*>(offset)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| EXPECT_STREQ(kOffsetOverflowMessage, GetLastError().c_str()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, TraceBeginCHROMIUM) { |
| const uint32_t kCategoryBucketId = GLES2Implementation::kResultBucketId; |
| const uint32_t kNameBucketId = GLES2Implementation::kResultBucketId + 1; |
| const std::string category_name = "test category"; |
| const std::string trace_name = "test trace"; |
| const size_t kPaddedString1Size = |
| transfer_buffer_->RoundToAlignment(category_name.size() + 1); |
| const size_t kPaddedString2Size = |
| transfer_buffer_->RoundToAlignment(trace_name.size() + 1); |
| |
| gl_->TraceBeginCHROMIUM(category_name.c_str(), trace_name.c_str()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| struct Cmds { |
| cmd::SetBucketSize category_size1; |
| cmd::SetBucketData category_data; |
| cmd::SetToken set_token1; |
| cmd::SetBucketSize name_size1; |
| cmd::SetBucketData name_data; |
| cmd::SetToken set_token2; |
| cmds::TraceBeginCHROMIUM trace_call_begin; |
| cmd::SetBucketSize category_size2; |
| cmd::SetBucketSize name_size2; |
| }; |
| |
| ExpectedMemoryInfo mem1 = GetExpectedMemory(kPaddedString1Size); |
| ExpectedMemoryInfo mem2 = GetExpectedMemory(kPaddedString2Size); |
| |
| ASSERT_STREQ(category_name.c_str(), reinterpret_cast<char*>(mem1.ptr)); |
| ASSERT_STREQ(trace_name.c_str(), reinterpret_cast<char*>(mem2.ptr)); |
| |
| Cmds expected; |
| expected.category_size1.Init(kCategoryBucketId, category_name.size() + 1); |
| expected.category_data.Init( |
| kCategoryBucketId, 0, category_name.size() + 1, mem1.id, mem1.offset); |
| expected.set_token1.Init(GetNextToken()); |
| expected.name_size1.Init(kNameBucketId, trace_name.size() + 1); |
| expected.name_data.Init( |
| kNameBucketId, 0, trace_name.size() + 1, mem2.id, mem2.offset); |
| expected.set_token2.Init(GetNextToken()); |
| expected.trace_call_begin.Init(kCategoryBucketId, kNameBucketId); |
| expected.category_size2.Init(kCategoryBucketId, 0); |
| expected.name_size2.Init(kNameBucketId, 0); |
| |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, AllowNestedTracesCHROMIUM) { |
| const std::string category1_name = "test category 1"; |
| const std::string trace1_name = "test trace 1"; |
| const std::string category2_name = "test category 2"; |
| const std::string trace2_name = "test trace 2"; |
| |
| gl_->TraceBeginCHROMIUM(category1_name.c_str(), trace1_name.c_str()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| gl_->TraceBeginCHROMIUM(category2_name.c_str(), trace2_name.c_str()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| gl_->TraceEndCHROMIUM(); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| gl_->TraceEndCHROMIUM(); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| // No more corresponding begin tracer marker should error. |
| gl_->TraceEndCHROMIUM(); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, InsertFenceSyncCHROMIUM) { |
| const GLuint64 kFenceSync = 123u; |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) |
| .WillOnce(Return(kFenceSync)); |
| |
| struct Cmds { |
| cmds::InsertFenceSyncCHROMIUM insert_fence_sync; |
| }; |
| Cmds expected; |
| expected.insert_fence_sync.Init(kFenceSync); |
| |
| const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM(); |
| EXPECT_EQ(kFenceSync, fence_sync); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GenSyncTokenCHROMIUM) { |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; |
| const CommandBufferId kCommandBufferId = |
| CommandBufferId::FromUnsafeValue(234u); |
| const GLuint64 kFenceSync = 123u; |
| GLbyte sync_token_data[GL_SYNC_TOKEN_SIZE_CHROMIUM]; |
| |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) |
| .WillRepeatedly(Return(kNamespaceId)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillRepeatedly(Return(kCommandBufferId)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()) |
| .WillRepeatedly(Return(0)); |
| |
| gl_->GenSyncTokenCHROMIUM(kFenceSync, nullptr); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(false)); |
| gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushReceived(kFenceSync)) |
| .WillOnce(Return(false)); |
| gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushReceived(kFenceSync)) |
| .WillOnce(Return(true)); |
| ClearCommands(); |
| gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| SyncToken sync_token; |
| memcpy(&sync_token, sync_token_data, sizeof(SyncToken)); |
| EXPECT_TRUE(sync_token.verified_flush()); |
| EXPECT_EQ(kNamespaceId, sync_token.namespace_id()); |
| EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id()); |
| EXPECT_EQ(kFenceSync, sync_token.release_count()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GenUnverifiedSyncTokenCHROMIUM) { |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; |
| const CommandBufferId kCommandBufferId = |
| CommandBufferId::FromUnsafeValue(234u); |
| const GLuint64 kFenceSync = 123u; |
| GLbyte sync_token_data[GL_SYNC_TOKEN_SIZE_CHROMIUM]; |
| |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) |
| .WillRepeatedly(Return(kNamespaceId)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillRepeatedly(Return(kCommandBufferId)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()) |
| .WillRepeatedly(Return(0)); |
| |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync, nullptr); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(false)); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync)) |
| .WillOnce(Return(false)); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync)) |
| .WillOnce(Return(true)); |
| ClearCommands(); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync, sync_token_data); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| SyncToken sync_token; |
| memcpy(&sync_token, sync_token_data, sizeof(SyncToken)); |
| EXPECT_FALSE(sync_token.verified_flush()); |
| EXPECT_EQ(kNamespaceId, sync_token.namespace_id()); |
| EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id()); |
| EXPECT_EQ(kFenceSync, sync_token.release_count()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, VerifySyncTokensCHROMIUM) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; |
| const CommandBufferId kCommandBufferId = |
| CommandBufferId::FromUnsafeValue(234u); |
| const GLuint64 kFenceSync = 123u; |
| gpu::SyncToken sync_token; |
| GLbyte* sync_token_datas[] = { sync_token.GetData() }; |
| |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) |
| .WillRepeatedly(Return(kNamespaceId)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillRepeatedly(Return(kCommandBufferId)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()) |
| .WillRepeatedly(Return(0)); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync)) |
| .WillOnce(Return(true)); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync, sync_token.GetData()); |
| ASSERT_TRUE(sync_token.HasData()); |
| ASSERT_FALSE(sync_token.verified_flush()); |
| |
| ClearCommands(); |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(_)) |
| .WillOnce(Return(false)); |
| gl_->VerifySyncTokensCHROMIUM(sync_token_datas, 1); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| EXPECT_FALSE(sync_token.verified_flush()); |
| |
| ClearCommands(); |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(_)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()); |
| gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas)); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| EXPECT_EQ(kNamespaceId, sync_token.namespace_id()); |
| EXPECT_EQ(kCommandBufferId, sync_token.command_buffer_id()); |
| EXPECT_EQ(kFenceSync, sync_token.release_count()); |
| EXPECT_TRUE(sync_token.verified_flush()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, VerifySyncTokensCHROMIUM_Sequence) { |
| // To verify sync tokens, the sync tokens must all be verified after |
| // CanWaitUnverifiedSyncTokens() are called. This test ensures the right |
| // sequence. |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; |
| const CommandBufferId kCommandBufferId = |
| CommandBufferId::FromUnsafeValue(234u); |
| const GLuint64 kFenceSync1 = 123u; |
| const GLuint64 kFenceSync2 = 234u; |
| gpu::SyncToken sync_token1; |
| gpu::SyncToken sync_token2; |
| GLbyte* sync_token_datas[] = { sync_token1.GetData(), sync_token2.GetData() }; |
| |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) |
| .WillRepeatedly(Return(kNamespaceId)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillRepeatedly(Return(kCommandBufferId)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()) |
| .WillRepeatedly(Return(0)); |
| |
| // Generate sync token 1. |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync1)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync1)) |
| .WillOnce(Return(true)); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync1, sync_token1.GetData()); |
| ASSERT_TRUE(sync_token1.HasData()); |
| ASSERT_FALSE(sync_token1.verified_flush()); |
| |
| // Generate sync token 2. |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync2)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushed(kFenceSync2)) |
| .WillOnce(Return(true)); |
| gl_->GenUnverifiedSyncTokenCHROMIUM(kFenceSync2, sync_token2.GetData()); |
| ASSERT_TRUE(sync_token2.HasData()); |
| ASSERT_FALSE(sync_token2.verified_flush()); |
| |
| // Ensure proper sequence of checking and validating. |
| Sequence sequence; |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(Pointee(sync_token1))) |
| .InSequence(sequence) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(Pointee(sync_token2))) |
| .InSequence(sequence) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()) |
| .InSequence(sequence); |
| gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas)); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); |
| |
| EXPECT_TRUE(sync_token1.verified_flush()); |
| EXPECT_TRUE(sync_token2.verified_flush()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, WaitSyncTokenCHROMIUM) { |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; |
| const CommandBufferId kCommandBufferId = |
| CommandBufferId::FromUnsafeValue(234u); |
| const GLuint64 kFenceSync = 456u; |
| GLbyte sync_token[GL_SYNC_TOKEN_SIZE_CHROMIUM]; |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushReceived(kFenceSync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillOnce(Return(kCommandBufferId)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()).WillOnce(Return(0)); |
| gl_->GenSyncTokenCHROMIUM(kFenceSync, sync_token); |
| |
| struct Cmds { |
| cmds::WaitSyncTokenCHROMIUM wait_sync_token; |
| }; |
| Cmds expected; |
| expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId.GetUnsafeValue(), |
| kFenceSync); |
| |
| gl_->WaitSyncTokenCHROMIUM(sync_token); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, WaitSyncTokenCHROMIUMErrors) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::GetError::Result)); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillRepeatedly(SetMemory(result.ptr, GLuint(GL_NO_ERROR))) |
| .RetiresOnSaturation(); |
| |
| // Empty sync tokens should be produce no error and be a nop. |
| ClearCommands(); |
| gl_->WaitSyncTokenCHROMIUM(nullptr); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| |
| // Invalid sync tokens should produce no error and be a nop. |
| ClearCommands(); |
| gpu::SyncToken invalid_sync_token; |
| gl_->WaitSyncTokenCHROMIUM(invalid_sync_token.GetConstData()); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetError()); |
| |
| // Unverified sync token should produce INVALID_OPERATION. |
| ClearCommands(); |
| gpu::SyncToken unverified_sync_token(CommandBufferNamespace::GPU_IO, 0, |
| gpu::CommandBufferId(), 0); |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(_)) |
| .WillOnce(Return(false)); |
| gl_->WaitSyncTokenCHROMIUM(unverified_sync_token.GetConstData()); |
| EXPECT_TRUE(NoCommandsWritten()); |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, IsEnabled) { |
| // If we use a valid enum, its state is cached on client side, so no command |
| // is actually generated, and this test will fail. |
| // TODO(zmo): it seems we never need the command. Maybe remove it. |
| GLenum kCap = 1; |
| struct Cmds { |
| cmds::IsEnabled cmd; |
| }; |
| |
| Cmds expected; |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::IsEnabled::Result)); |
| expected.cmd.Init(kCap, result1.id, result1.offset); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, uint32_t(GL_TRUE))) |
| .RetiresOnSaturation(); |
| |
| GLboolean result = gl_->IsEnabled(kCap); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_TRUE(result); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ClientWaitSync) { |
| const GLuint client_sync_id = 36; |
| struct Cmds { |
| cmds::ClientWaitSync cmd; |
| }; |
| |
| Cmds expected; |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(cmds::ClientWaitSync::Result)); |
| const GLuint64 kTimeout = 0xABCDEF0123456789; |
| expected.cmd.Init(client_sync_id, GL_SYNC_FLUSH_COMMANDS_BIT, |
| kTimeout, result1.id, result1.offset); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, uint32_t(GL_CONDITION_SATISFIED))) |
| .RetiresOnSaturation(); |
| |
| GLenum result = gl_->ClientWaitSync( |
| reinterpret_cast<GLsync>(client_sync_id), GL_SYNC_FLUSH_COMMANDS_BIT, |
| kTimeout); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), result); |
| } |
| |
| TEST_F(GLES2ImplementationTest, WaitSync) { |
| const GLuint kClientSyncId = 36; |
| struct Cmds { |
| cmds::WaitSync cmd; |
| }; |
| Cmds expected; |
| const GLuint64 kTimeout = GL_TIMEOUT_IGNORED; |
| expected.cmd.Init(kClientSyncId, 0, kTimeout); |
| |
| gl_->WaitSync(reinterpret_cast<GLsync>(kClientSyncId), 0, kTimeout); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationTest, MapBufferRangeUnmapBufferWrite) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(1))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_WRITE_BIT); |
| EXPECT_TRUE(mem != nullptr); |
| |
| EXPECT_TRUE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, MapBufferRangeWriteWithInvalidateBit) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(1))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| GLsizeiptr kSize = 64; |
| void* mem = gl_->MapBufferRange( |
| GL_ARRAY_BUFFER, 10, kSize, |
| GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT); |
| EXPECT_TRUE(mem != nullptr); |
| std::vector<int8_t> zero(kSize); |
| memset(&zero[0], 0, kSize); |
| EXPECT_EQ(0, memcmp(mem, &zero[0], kSize)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, MapBufferRangeWriteWithGLError) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| // Return a result of 0 to indicate an GL error. |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(0))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_WRITE_BIT); |
| EXPECT_TRUE(mem == nullptr); |
| } |
| |
| TEST_F(GLES2ImplementationTest, MapBufferRangeUnmapBufferRead) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(1))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_READ_BIT); |
| EXPECT_TRUE(mem != nullptr); |
| |
| EXPECT_TRUE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| } |
| |
| TEST_F(GLES2ImplementationTest, MapBufferRangeReadWithGLError) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| // Return a result of 0 to indicate an GL error. |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(0))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_READ_BIT); |
| EXPECT_TRUE(mem == nullptr); |
| } |
| |
| TEST_F(GLES2ImplementationTest, UnmapBufferFails) { |
| // No bound buffer. |
| EXPECT_FALSE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| // Buffer is unmapped. |
| EXPECT_FALSE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, BufferDataUnmapsDataStore) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(1))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_WRITE_BIT); |
| EXPECT_TRUE(mem != nullptr); |
| |
| std::vector<uint8_t> data(16); |
| // BufferData unmaps the data store. |
| gl_->BufferData(GL_ARRAY_BUFFER, 16, &data[0], GL_STREAM_DRAW); |
| |
| EXPECT_FALSE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, DeleteBuffersUnmapsDataStore) { |
| ExpectedMemoryInfo result = |
| GetExpectedResultMemory(sizeof(cmds::MapBufferRange::Result)); |
| |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result.ptr, uint32_t(1))) |
| .RetiresOnSaturation(); |
| |
| const GLuint kBufferId = 123; |
| gl_->BindBuffer(GL_ARRAY_BUFFER, kBufferId); |
| |
| void* mem = gl_->MapBufferRange(GL_ARRAY_BUFFER, 10, 64, GL_MAP_WRITE_BIT); |
| EXPECT_TRUE(mem != nullptr); |
| |
| std::vector<uint8_t> data(16); |
| // DeleteBuffers unmaps the data store. |
| gl_->DeleteBuffers(1, &kBufferId); |
| |
| EXPECT_FALSE(gl_->UnmapBuffer(GL_ARRAY_BUFFER)); |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); |
| } |
| |
| TEST_F(GLES2ImplementationTest, GetInternalformativ) { |
| const GLint kNumSampleCounts = 8; |
| struct Cmds { |
| cmds::GetInternalformativ cmd; |
| }; |
| typedef cmds::GetInternalformativ::Result::Type ResultType; |
| ResultType result = 0; |
| Cmds expected; |
| ExpectedMemoryInfo result1 = |
| GetExpectedResultMemory(sizeof(uint32_t) + sizeof(ResultType)); |
| expected.cmd.Init(123, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, |
| result1.id, result1.offset); |
| EXPECT_CALL(*command_buffer(), OnFlush()) |
| .WillOnce(SetMemory(result1.ptr, |
| SizedResultHelper<ResultType>(kNumSampleCounts))) |
| .RetiresOnSaturation(); |
| gl_->GetInternalformativ(123, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1, &result); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| EXPECT_EQ(static_cast<ResultType>(kNumSampleCounts), result); |
| } |
| |
| static void CountCallback(int* count) { |
| (*count)++; |
| } |
| |
| TEST_F(GLES2ImplementationTest, SignalSyncToken) { |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()).WillOnce(Return(1)); |
| const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM(); |
| gl_->ShallowFlushCHROMIUM(); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(fence_sync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushReceived(fence_sync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(GPU_IO)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()).WillOnce(Return(0)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillOnce(Return(CommandBufferId::FromUnsafeValue(1))); |
| gpu::SyncToken sync_token; |
| gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); |
| |
| int signaled_count = 0; |
| |
| // Request a signal sync token, which gives a callback to the GpuControl to |
| // run when the sync token is reached. |
| base::Closure signal_closure; |
| EXPECT_CALL(*gpu_control_, SignalSyncToken(_, _)) |
| .WillOnce(SaveArg<1>(&signal_closure)); |
| gl_->SignalSyncToken(sync_token, base::Bind(&CountCallback, &signaled_count)); |
| EXPECT_EQ(0, signaled_count); |
| |
| // When GpuControl runs the callback, the original callback we gave to |
| // GLES2Implementation is run. |
| signal_closure.Run(); |
| EXPECT_EQ(1, signaled_count); |
| } |
| |
| TEST_F(GLES2ImplementationTest, SignalSyncTokenAfterContextLoss) { |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()).WillOnce(Return(1)); |
| const uint64_t fence_sync = gl_->InsertFenceSyncCHROMIUM(); |
| gl_->ShallowFlushCHROMIUM(); |
| |
| EXPECT_CALL(*gpu_control_, IsFenceSyncRelease(fence_sync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, IsFenceSyncFlushReceived(fence_sync)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(GPU_IO)); |
| EXPECT_CALL(*gpu_control_, GetExtraCommandBufferData()).WillOnce(Return(0)); |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) |
| .WillOnce(Return(CommandBufferId::FromUnsafeValue(1))); |
| gpu::SyncToken sync_token; |
| gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); |
| |
| int signaled_count = 0; |
| |
| // Request a signal sync token, which gives a callback to the GpuControl to |
| // run when the sync token is reached. |
| base::Closure signal_closure; |
| EXPECT_CALL(*gpu_control_, SignalSyncToken(_, _)) |
| .WillOnce(SaveArg<1>(&signal_closure)); |
| gl_->SignalSyncToken(sync_token, base::Bind(&CountCallback, &signaled_count)); |
| EXPECT_EQ(0, signaled_count); |
| |
| // Inform the GLES2Implementation that the context is lost. |
| GpuControlClient* gl_as_client = gl_; |
| gl_as_client->OnGpuControlLostContext(); |
| |
| // When GpuControl runs the callback, the original callback we gave to |
| // GLES2Implementation is *not* run, since the context is lost and we |
| // have already run the lost context callback. |
| signal_closure.Run(); |
| EXPECT_EQ(0, signaled_count); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ReportLoss) { |
| GpuControlClient* gl_as_client = gl_; |
| int lost_count = 0; |
| gl_->SetLostContextCallback(base::Bind(&CountCallback, &lost_count)); |
| EXPECT_EQ(0, lost_count); |
| |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR()); |
| gl_as_client->OnGpuControlLostContext(); |
| EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR()); |
| // The lost context callback should be run when GLES2Implementation is |
| // notified of the loss. |
| EXPECT_EQ(1, lost_count); |
| } |
| |
| TEST_F(GLES2ImplementationTest, ReportLossReentrant) { |
| GpuControlClient* gl_as_client = gl_; |
| int lost_count = 0; |
| gl_->SetLostContextCallback(base::Bind(&CountCallback, &lost_count)); |
| EXPECT_EQ(0, lost_count); |
| |
| EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR()); |
| gl_as_client->OnGpuControlLostContextMaybeReentrant(); |
| EXPECT_NE(static_cast<GLenum>(GL_NO_ERROR), gl_->GetGraphicsResetStatusKHR()); |
| // The lost context callback should not be run yet to avoid calling back into |
| // clients re-entrantly, and having them re-enter GLES2Implementation. |
| EXPECT_EQ(0, lost_count); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, LoseContextOnOOM) { |
| ContextInitOptions init_options; |
| init_options.lose_context_when_out_of_memory = true; |
| ASSERT_TRUE(Initialize(init_options)); |
| |
| struct Cmds { |
| cmds::LoseContextCHROMIUM cmd; |
| }; |
| |
| GLsizei max = std::numeric_limits<GLsizei>::max(); |
| EXPECT_CALL(*gpu_control_, CreateGpuMemoryBufferImage(max, max, _, _)) |
| .WillOnce(Return(-1)); |
| gl_->CreateGpuMemoryBufferImageCHROMIUM(max, max, GL_RGBA, |
| GL_READ_WRITE_CHROMIUM); |
| // The context should be lost. |
| Cmds expected; |
| expected.cmd.Init(GL_GUILTY_CONTEXT_RESET_ARB, GL_UNKNOWN_CONTEXT_RESET_ARB); |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, NoLoseContextOnOOM) { |
| ContextInitOptions init_options; |
| ASSERT_TRUE(Initialize(init_options)); |
| |
| struct Cmds { |
| cmds::LoseContextCHROMIUM cmd; |
| }; |
| |
| GLsizei max = std::numeric_limits<GLsizei>::max(); |
| EXPECT_CALL(*gpu_control_, CreateGpuMemoryBufferImage(max, max, _, _)) |
| .WillOnce(Return(-1)); |
| gl_->CreateGpuMemoryBufferImageCHROMIUM(max, max, GL_RGBA, |
| GL_READ_WRITE_CHROMIUM); |
| // The context should not be lost. |
| EXPECT_TRUE(NoCommandsWritten()); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, FailInitOnBGRMismatch1) { |
| ContextInitOptions init_options; |
| init_options.bind_generates_resource_client = false; |
| init_options.bind_generates_resource_service = true; |
| EXPECT_FALSE(Initialize(init_options)); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, FailInitOnBGRMismatch2) { |
| ContextInitOptions init_options; |
| init_options.bind_generates_resource_client = true; |
| init_options.bind_generates_resource_service = false; |
| EXPECT_FALSE(Initialize(init_options)); |
| } |
| |
| TEST_F(GLES2ImplementationManualInitTest, FailInitOnTransferBufferFail) { |
| ContextInitOptions init_options; |
| init_options.transfer_buffer_initialize_fail = true; |
| EXPECT_FALSE(Initialize(init_options)); |
| } |
| |
| #include "base/macros.h" |
| #include "gpu/command_buffer/client/gles2_implementation_unittest_autogen.h" |
| |
| } // namespace gles2 |
| } // namespace gpu |