| // Copyright 2018 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 RasterImplementation. | |
| #include "gpu/command_buffer/client/raster_implementation.h" | |
| #include <GLES2/gl2.h> | |
| #include <GLES2/gl2extchromium.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <memory> | |
| #include "base/compiler_specific.h" | |
| #include "cc/paint/raw_memory_transfer_cache_entry.h" | |
| #include "cc/paint/transfer_cache_serialize_helper.h" | |
| #include "gpu/command_buffer/client/client_test_helper.h" | |
| #include "gpu/command_buffer/client/mock_transfer_buffer.h" | |
| #include "gpu/command_buffer/client/query_tracker.h" | |
| #include "gpu/command_buffer/client/raster_cmd_helper.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" | |
| using gpu::gles2::QueryTracker; | |
| using testing::_; | |
| using testing::AtLeast; | |
| using testing::AnyNumber; | |
| using testing::DoAll; | |
| using testing::InSequence; | |
| using testing::Invoke; | |
| using testing::Mock; | |
| using testing::Sequence; | |
| using testing::StrictMock; | |
| using testing::Return; | |
| using testing::ReturnRef; | |
| namespace gpu { | |
| namespace raster { | |
| 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)]; | |
| }; | |
| class RasterImplementationTest : public testing::Test { | |
| protected: | |
| 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 kMaxTextureImageUnits = 8; | |
| static const GLint kMaxTextureSize = 128; | |
| static const GLint kNumCompressedTextureFormats = 0; | |
| static const GLuint kStartId = 1024; | |
| static const GLuint kBuffersStartId = 1; | |
| static const GLuint kTexturesStartId = 1; | |
| static const GLuint kQueriesStartId = 1; | |
| typedef MockTransferBuffer::ExpectedMemoryInfo ExpectedMemoryInfo; | |
| class TestContext { | |
| public: | |
| TestContext() : commands_(nullptr), token_(0) {} | |
| bool Initialize(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) { | |
| SharedMemoryLimits limits = SharedMemoryLimitsForTesting(); | |
| command_buffer_.reset(new StrictMock<MockClientCommandBuffer>()); | |
| transfer_buffer_.reset(new MockTransferBuffer( | |
| command_buffer_.get(), kTransferBufferSize, | |
| RasterImplementation::kStartingOffset, | |
| RasterImplementation::kAlignment, transfer_buffer_initialize_fail)); | |
| helper_.reset(new RasterCmdHelper(command_buffer())); | |
| helper_->Initialize(limits.command_buffer_size); | |
| gpu_control_.reset(new StrictMock<MockClientGpuControl>()); | |
| capabilities_.max_combined_texture_image_units = | |
| kMaxCombinedTextureImageUnits; | |
| capabilities_.max_texture_image_units = kMaxTextureImageUnits; | |
| capabilities_.max_texture_size = kMaxTextureSize; | |
| capabilities_.num_compressed_texture_formats = | |
| kNumCompressedTextureFormats; | |
| capabilities_.bind_generates_resource_chromium = | |
| bind_generates_resource_service ? 1 : 0; | |
| capabilities_.sync_query = sync_query; | |
| EXPECT_CALL(*gpu_control_, GetCapabilities()) | |
| .WillOnce(ReturnRef(capabilities_)); | |
| { | |
| InSequence sequence; | |
| gl_.reset(new RasterImplementation( | |
| helper_.get(), transfer_buffer_.get(), | |
| bind_generates_resource_client, lose_context_when_out_of_memory, | |
| gpu_control_.get(), nullptr /* image_decode_accelerator */)); | |
| } | |
| // The client should be set to something non-null. | |
| EXPECT_CALL(*gpu_control_, SetGpuControlClient(gl_.get())).Times(1); | |
| if (gl_->Initialize(limits) != gpu::ContextResult::kSuccess) | |
| 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()->GetServicePutOffset(); | |
| 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<RasterCmdHelper> helper_; | |
| std::unique_ptr<MockTransferBuffer> transfer_buffer_; | |
| std::unique_ptr<RasterImplementation> gl_; | |
| CommandBufferEntry* commands_; | |
| int token_; | |
| Capabilities capabilities_; | |
| }; | |
| RasterImplementationTest() : commands_(nullptr) {} | |
| void SetUp() override; | |
| void TearDown() override; | |
| bool NoCommandsWritten() { | |
| scoped_refptr<Buffer> ring_buffer = helper_->get_ring_buffer(); | |
| const uint8_t* cmds = static_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(); } | |
| void* MapRasterCHROMIUM(GLsizeiptr size) { | |
| return gl_->MapRasterCHROMIUM(size); | |
| } | |
| void UnmapRasterCHROMIUM(GLsizeiptr written_size) { | |
| gl_->UnmapRasterCHROMIUM(written_size, written_size); | |
| } | |
| 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) {} | |
| 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 Initialize(const ContextInitOptions& init_options) { | |
| bool success = true; | |
| if (!test_context_.Initialize(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)) { | |
| success = false; | |
| } | |
| // Default to test context 0. | |
| gpu_control_ = test_context_.gpu_control_.get(); | |
| helper_ = test_context_.helper_.get(); | |
| transfer_buffer_ = test_context_.transfer_buffer_.get(); | |
| gl_ = test_context_.gl_.get(); | |
| commands_ = test_context_.commands_; | |
| return success; | |
| } | |
| MockClientCommandBuffer* command_buffer() const { | |
| return test_context_.command_buffer_.get(); | |
| } | |
| int GetNextToken() { return test_context_.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; | |
| } | |
| 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); | |
| } | |
| static SharedMemoryLimits SharedMemoryLimitsForTesting() { | |
| SharedMemoryLimits limits; | |
| limits.command_buffer_size = kCommandBufferSizeBytes; | |
| limits.start_transfer_buffer_size = kTransferBufferSize; | |
| limits.min_transfer_buffer_size = kTransferBufferSize; | |
| limits.max_transfer_buffer_size = kTransferBufferSize; | |
| limits.mapped_memory_reclaim_limit = SharedMemoryLimits::kNoLimit; | |
| return limits; | |
| } | |
| TestContext test_context_; | |
| MockClientGpuControl* gpu_control_; | |
| RasterCmdHelper* helper_; | |
| MockTransferBuffer* transfer_buffer_; | |
| RasterImplementation* gl_; | |
| CommandBufferEntry* commands_; | |
| }; | |
| void RasterImplementationTest::SetUp() { | |
| ContextInitOptions init_options; | |
| ASSERT_TRUE(Initialize(init_options)); | |
| } | |
| void RasterImplementationTest::TearDown() { | |
| test_context_.TearDown(); | |
| } | |
| class RasterImplementationManualInitTest : public RasterImplementationTest { | |
| protected: | |
| void SetUp() override {} | |
| }; | |
| // GCC requires these declarations, but MSVC requires they not be present | |
| #ifndef _MSC_VER | |
| const uint8_t RasterImplementationTest::kInitialValue; | |
| const int32_t RasterImplementationTest::kNumCommandEntries; | |
| const int32_t RasterImplementationTest::kCommandBufferSizeBytes; | |
| const size_t RasterImplementationTest::kTransferBufferSize; | |
| const GLint RasterImplementationTest::kMaxCombinedTextureImageUnits; | |
| const GLint RasterImplementationTest::kMaxTextureImageUnits; | |
| const GLint RasterImplementationTest::kMaxTextureSize; | |
| const GLint RasterImplementationTest::kNumCompressedTextureFormats; | |
| const GLuint RasterImplementationTest::kStartId; | |
| const GLuint RasterImplementationTest::kBuffersStartId; | |
| const GLuint RasterImplementationTest::kTexturesStartId; | |
| const GLuint RasterImplementationTest::kQueriesStartId; | |
| #endif | |
| TEST_F(RasterImplementationTest, GetBucketContents) { | |
| const uint32_t kBucketId = RasterImplementation::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(RasterImplementationTest, BeginEndQueryEXT) { | |
| // GL_COMMANDS_COMPLETED_CHROMIUM, | |
| // GL_CURRENT_QUERY_EXT | |
| 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_COMMANDS_COMPLETED_CHROMIUM, 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_COMMANDS_COMPLETED_CHROMIUM, id1); | |
| QueryTracker::Query* query = GetQuery(id1); | |
| ASSERT_TRUE(query != nullptr); | |
| expected_begin_cmds.begin_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM, id1, | |
| query->shm_id(), query->shm_offset()); | |
| EXPECT_EQ( | |
| 0, memcmp(&expected_begin_cmds, commands, sizeof(expected_begin_cmds))); | |
| ClearCommands(); | |
| // Test BeginQueryEXT fails if between Begin/End. | |
| gl_->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, id2); | |
| EXPECT_TRUE(NoCommandsWritten()); | |
| EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); | |
| // Test EndQueryEXT sends command | |
| struct EndCmds { | |
| cmds::EndQueryEXT end_query; | |
| }; | |
| commands = GetPut(); | |
| gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM); | |
| EndCmds expected_end_cmds; | |
| expected_end_cmds.end_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM, | |
| query->submit_count()); | |
| EXPECT_EQ(0, memcmp(&expected_end_cmds, commands, sizeof(expected_end_cmds))); | |
| // Test EndQueryEXT fails if no current query. | |
| ClearCommands(); | |
| gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM); | |
| 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_COMMANDS_COMPLETED_CHROMIUM, id1); | |
| EXPECT_EQ(old_submit_count, query->submit_count()); | |
| commands = GetPut(); | |
| gl_->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM); | |
| EXPECT_NE(old_submit_count, query->submit_count()); | |
| expected_end_cmds.end_query.Init(GL_COMMANDS_COMPLETED_CHROMIUM, | |
| query->submit_count()); | |
| EXPECT_EQ(0, memcmp(&expected_end_cmds, commands, sizeof(expected_end_cmds))); | |
| // 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); | |
| available = 1u; | |
| gl_->GetQueryObjectuivEXT( | |
| id1, GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT, &available); | |
| EXPECT_EQ(0u, available); | |
| } | |
| TEST_F(RasterImplementationManualInitTest, BadQueryTargets) { | |
| ContextInitOptions init_options; | |
| init_options.sync_query = 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(0x123, id); | |
| EXPECT_EQ(GL_INVALID_ENUM, CheckError()); | |
| EXPECT_EQ(nullptr, GetQuery(id)); | |
| } | |
| TEST_F(RasterImplementationTest, GenSyncTokenCHROMIUM) { | |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; | |
| const CommandBufferId kCommandBufferId = | |
| CommandBufferId::FromUnsafeValue(234u); | |
| const GLuint64 kFenceSync = 123u; | |
| SyncToken sync_token; | |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) | |
| .WillRepeatedly(Return(kNamespaceId)); | |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) | |
| .WillRepeatedly(Return(kCommandBufferId)); | |
| gl_->GenSyncTokenCHROMIUM(nullptr); | |
| EXPECT_TRUE(NoCommandsWritten()); | |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); | |
| const void* commands = GetPut(); | |
| cmds::InsertFenceSyncCHROMIUM insert_fence_sync; | |
| insert_fence_sync.Init(kFenceSync); | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()); | |
| gl_->GenSyncTokenCHROMIUM(sync_token.GetData()); | |
| EXPECT_EQ(0, memcmp(&insert_fence_sync, commands, sizeof(insert_fence_sync))); | |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); | |
| 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(RasterImplementationTest, GenUnverifiedSyncTokenCHROMIUM) { | |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; | |
| const CommandBufferId kCommandBufferId = | |
| CommandBufferId::FromUnsafeValue(234u); | |
| const GLuint64 kFenceSync = 123u; | |
| SyncToken sync_token; | |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) | |
| .WillRepeatedly(Return(kNamespaceId)); | |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) | |
| .WillRepeatedly(Return(kCommandBufferId)); | |
| gl_->GenUnverifiedSyncTokenCHROMIUM(nullptr); | |
| EXPECT_TRUE(NoCommandsWritten()); | |
| EXPECT_EQ(GL_INVALID_VALUE, CheckError()); | |
| const void* commands = GetPut(); | |
| cmds::InsertFenceSyncCHROMIUM insert_fence_sync; | |
| insert_fence_sync.Init(kFenceSync); | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); | |
| EXPECT_EQ(0, memcmp(&insert_fence_sync, commands, sizeof(insert_fence_sync))); | |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); | |
| 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(RasterImplementationTest, 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_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); | |
| EXPECT_TRUE(sync_token.HasData()); | |
| EXPECT_FALSE(sync_token.verified_flush()); | |
| ClearCommands(); | |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token)) | |
| .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(sync_token)) | |
| .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(RasterImplementationTest, 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)); | |
| // Generate sync token 1. | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync1)); | |
| gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token1.GetData()); | |
| EXPECT_TRUE(sync_token1.HasData()); | |
| EXPECT_FALSE(sync_token1.verified_flush()); | |
| // Generate sync token 2. | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync2)); | |
| gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token2.GetData()); | |
| EXPECT_TRUE(sync_token2.HasData()); | |
| EXPECT_FALSE(sync_token2.verified_flush()); | |
| // Ensure proper sequence of checking and validating. | |
| Sequence sequence; | |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token1)) | |
| .InSequence(sequence) | |
| .WillOnce(Return(true)); | |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token2)) | |
| .InSequence(sequence) | |
| .WillOnce(Return(true)); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()).InSequence(sequence); | |
| gl_->VerifySyncTokensCHROMIUM(sync_token_datas, arraysize(sync_token_datas)); | |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); | |
| EXPECT_TRUE(sync_token1.verified_flush()); | |
| EXPECT_TRUE(sync_token2.verified_flush()); | |
| } | |
| TEST_F(RasterImplementationTest, VerifySyncTokensCHROMIUM_EmptySyncToken) { | |
| // 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(); | |
| gpu::SyncToken sync_token1, sync_token2; | |
| GLbyte* sync_token_datas[] = {sync_token1.GetData(), sync_token2.GetData()}; | |
| // Ensure proper sequence of checking and validating. | |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(_)).Times(0); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()).Times(0); | |
| 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(RasterImplementationTest, WaitSyncTokenCHROMIUM) { | |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; | |
| const CommandBufferId kCommandBufferId = | |
| CommandBufferId::FromUnsafeValue(234u); | |
| const GLuint64 kFenceSync = 456u; | |
| gpu::SyncToken sync_token; | |
| GLbyte* sync_token_data = sync_token.GetData(); | |
| struct Cmds { | |
| cmds::InsertFenceSyncCHROMIUM insert_fence_sync; | |
| cmds::WaitSyncTokenCHROMIUM wait_sync_token; | |
| }; | |
| Cmds expected; | |
| expected.insert_fence_sync.Init(kFenceSync); | |
| expected.wait_sync_token.Init(kNamespaceId, kCommandBufferId.GetUnsafeValue(), | |
| kFenceSync); | |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId)); | |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) | |
| .WillOnce(Return(kCommandBufferId)); | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()); | |
| gl_->GenSyncTokenCHROMIUM(sync_token_data); | |
| EXPECT_CALL(*gpu_control_, WaitSyncTokenHint(sync_token)); | |
| gl_->WaitSyncTokenCHROMIUM(sync_token_data); | |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); | |
| } | |
| TEST_F(RasterImplementationTest, 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, | |
| gpu::CommandBufferId(), 0); | |
| EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(unverified_sync_token)) | |
| .WillOnce(Return(false)); | |
| gl_->WaitSyncTokenCHROMIUM(unverified_sync_token.GetConstData()); | |
| EXPECT_TRUE(NoCommandsWritten()); | |
| EXPECT_EQ(static_cast<GLenum>(GL_INVALID_VALUE), gl_->GetError()); | |
| } | |
| static void CountCallback(int* count) { | |
| (*count)++; | |
| } | |
| TEST_F(RasterImplementationTest, SignalSyncToken) { | |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; | |
| const CommandBufferId kCommandBufferId = CommandBufferId::FromUnsafeValue(1); | |
| const uint64_t kFenceSync = 123u; | |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()) | |
| .WillRepeatedly(Return(kNamespaceId)); | |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) | |
| .WillRepeatedly(Return(kCommandBufferId)); | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()); | |
| gpu::SyncToken sync_token; | |
| gl_->GenSyncTokenCHROMIUM(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::OnceClosure signal_closure; | |
| EXPECT_CALL(*gpu_control_, DoSignalSyncToken(_, _)) | |
| .WillOnce(Invoke([&signal_closure](const SyncToken& sync_token, | |
| base::OnceClosure* callback) { | |
| signal_closure = std::move(*callback); | |
| })); | |
| gl_->SignalSyncToken(sync_token, | |
| base::BindOnce(&CountCallback, &signaled_count)); | |
| EXPECT_EQ(0, signaled_count); | |
| // When GpuControl runs the callback, the original callback we gave to | |
| // RasterImplementation is run. | |
| std::move(signal_closure).Run(); | |
| EXPECT_EQ(1, signaled_count); | |
| } | |
| TEST_F(RasterImplementationTest, SignalSyncTokenAfterContextLoss) { | |
| const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO; | |
| const CommandBufferId kCommandBufferId = CommandBufferId::FromUnsafeValue(1); | |
| const uint64_t kFenceSync = 123u; | |
| EXPECT_CALL(*gpu_control_, GetNamespaceID()).WillOnce(Return(kNamespaceId)); | |
| EXPECT_CALL(*gpu_control_, GetCommandBufferID()) | |
| .WillOnce(Return(kCommandBufferId)); | |
| EXPECT_CALL(*gpu_control_, GenerateFenceSyncRelease()) | |
| .WillOnce(Return(kFenceSync)); | |
| EXPECT_CALL(*gpu_control_, EnsureWorkVisible()); | |
| gpu::SyncToken sync_token; | |
| gl_->GenSyncTokenCHROMIUM(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::OnceClosure signal_closure; | |
| EXPECT_CALL(*gpu_control_, DoSignalSyncToken(_, _)) | |
| .WillOnce(Invoke([&signal_closure](const SyncToken& sync_token, | |
| base::OnceClosure* callback) { | |
| signal_closure = std::move(*callback); | |
| })); | |
| gl_->SignalSyncToken(sync_token, | |
| base::BindOnce(&CountCallback, &signaled_count)); | |
| EXPECT_EQ(0, signaled_count); | |
| // Inform the RasterImplementation 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 | |
| // RasterImplementation is *not* run, since the context is lost and we | |
| // have already run the lost context callback. | |
| std::move(signal_closure).Run(); | |
| EXPECT_EQ(0, signaled_count); | |
| } | |
| TEST_F(RasterImplementationTest, ReportLoss) { | |
| GpuControlClient* gl_as_client = gl_; | |
| int lost_count = 0; | |
| gl_->SetLostContextCallback(base::BindOnce(&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 RasterImplementation is | |
| // notified of the loss. | |
| EXPECT_EQ(1, lost_count); | |
| } | |
| TEST_F(RasterImplementationTest, ReportLossReentrant) { | |
| GpuControlClient* gl_as_client = gl_; | |
| int lost_count = 0; | |
| gl_->SetLostContextCallback(base::BindOnce(&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 RasterImplementation. | |
| EXPECT_EQ(0, lost_count); | |
| } | |
| TEST_F(RasterImplementationManualInitTest, FailInitOnTransferBufferFail) { | |
| ContextInitOptions init_options; | |
| init_options.transfer_buffer_initialize_fail = true; | |
| EXPECT_FALSE(Initialize(init_options)); | |
| } | |
| TEST_F(RasterImplementationTest, TransferCacheSerialization) { | |
| gl_->set_max_inlined_entry_size_for_testing(768u); | |
| size_t buffer_size = transfer_buffer_->MaxTransferBufferSize(); | |
| ScopedTransferBufferPtr buffer(buffer_size, helper_, transfer_buffer_); | |
| ASSERT_EQ(buffer.size(), buffer_size); | |
| char* buffer_start = reinterpret_cast<char*>(buffer.address()); | |
| memset(buffer_start, 0, buffer_size); | |
| gl_->SetRasterMappedBufferForTesting(std::move(buffer)); | |
| auto transfer_cache = gl_->CreateTransferCacheHelperForTesting(); | |
| std::vector<uint8_t> data(buffer_size - 16u); | |
| char* memory = buffer_start + 8u; | |
| cc::ClientRawMemoryTransferCacheEntry inlined_entry(data); | |
| EXPECT_EQ(transfer_cache->CreateEntry(inlined_entry, memory), data.size()); | |
| EXPECT_EQ(memcmp(data.data(), memory, data.size()), 0); | |
| data.resize(buffer_size + 16u); | |
| memory = buffer_start + 8u; | |
| cc::ClientRawMemoryTransferCacheEntry non_inlined_entry(data); | |
| EXPECT_EQ(transfer_cache->CreateEntry(non_inlined_entry, memory), 0u); | |
| } | |
| TEST_F(RasterImplementationTest, SetActiveURLCHROMIUM) { | |
| const uint32_t kURLBucketId = RasterImplementation::kResultBucketId; | |
| const std::string url = "chrome://test"; | |
| const size_t kPaddedStringSize = | |
| transfer_buffer_->RoundToAlignment(url.size()); | |
| gl_->SetActiveURLCHROMIUM(url.c_str()); | |
| EXPECT_EQ(GL_NO_ERROR, CheckError()); | |
| struct Cmds { | |
| cmd::SetBucketSize url_size; | |
| cmd::SetBucketData url_data; | |
| cmd::SetToken set_token; | |
| cmds::SetActiveURLCHROMIUM set_url_call; | |
| cmd::SetBucketSize url_size_end; | |
| }; | |
| ExpectedMemoryInfo mem = GetExpectedMemory(kPaddedStringSize); | |
| EXPECT_EQ(0, | |
| memcmp(url.c_str(), reinterpret_cast<char*>(mem.ptr), url.size())); | |
| Cmds expected; | |
| expected.url_size.Init(kURLBucketId, url.size()); | |
| expected.url_data.Init(kURLBucketId, 0, url.size(), mem.id, mem.offset); | |
| expected.set_token.Init(GetNextToken()); | |
| expected.set_url_call.Init(kURLBucketId); | |
| expected.url_size_end.Init(kURLBucketId, 0); | |
| EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); | |
| // Same URL shouldn't make any commands. | |
| EXPECT_FALSE(NoCommandsWritten()); | |
| ClearCommands(); | |
| gl_->SetActiveURLCHROMIUM(url.c_str()); | |
| EXPECT_TRUE(NoCommandsWritten()); | |
| } | |
| #include "base/macros.h" | |
| #include "gpu/command_buffer/client/raster_implementation_unittest_autogen.h" | |
| } // namespace raster | |
| } // namespace gpu |