blob: d75d8444306b2d0b87a14b3a5ecf65e239ba90da [file] [log] [blame]
// 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 expected;
expected.insert_fence_sync.Init(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_, WaitSyncToken(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