
Split from: https://chromium-review.googlesource.com/c/chromium/src/+/6004959 Generated patch --------------- - Tool: ./tool/clang/spanify/rewrite-multiple-platform.sh - Platform: Linux. - Filter: This includes 2400/4222 patches. I included the std::array ones and excluded build errors. Google announcement: -------------------- https://groups.google.com/a/google.com/g/chrome-memory-safety/c/RMiO4gaVLQA/m/Yz-3NCObAgAJ Benchmarks: ---------- See design doc and https://chromium-review.googlesource.com/c/chromium/src/+/6004959/21 Description ----------- The consensus during the memory safety summit was to begin rewriting relevant C-style arrays to C++11 std::array. It can be done immediately, offers better developer ergonomics, and fix large chunks of the -Wunsafe-buffer-usage errors in Chrome. To clarify, this effort is complementary to the longer plan work with enabling -fsanitize=array-bounds, and we plan to leverage both, especially for protecting 3p code. [Attached] is a document detailing the rationale, benefits, and considerations for potential compile-time and performance impacts. [Attached]:https://docs.google.com/document/d/1z5aBDg26lHmNDjXRCysElWKx7E4PAJXqykI_k7ondJI/edit?tab=t.0#heading=h.cqgo7wvp0kzt NO_IFTTT=No need to update base/debug/stack_trace.h Bug: 378069401 Change-Id: Id0237efc85fcc2d4532fa2d826b2aed0635a70f2 R: dcheng@chromium.org AX-Relnotes: n/a. Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6038251 Owners-Override: Daniel Cheng <dcheng@chromium.org> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Commit-Queue: Daniel Cheng <dcheng@chromium.org> Auto-Submit: Arthur Sonzogni <arthursonzogni@chromium.org> Cr-Commit-Position: refs/heads/main@{#1403153}
893 lines
32 KiB
C++
893 lines
32 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifdef UNSAFE_BUFFERS_BUILD
|
|
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
|
|
#pragma allow_unsafe_buffers
|
|
#endif
|
|
|
|
// 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 <array>
|
|
#include <memory>
|
|
|
|
#include "base/compiler_specific.h"
|
|
#include "base/containers/heap_array.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/memory/raw_ptr.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 uint32_t kNumCommandEntries = 500;
|
|
static const uint32_t kCommandBufferSizeBytes =
|
|
kNumCommandEntries * sizeof(CommandBufferEntry);
|
|
static const uint32_t kTransferBufferSize = 512;
|
|
|
|
static const GLint kMaxTextureSize = 128;
|
|
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 lose_context_when_out_of_memory,
|
|
bool transfer_buffer_initialize_fail,
|
|
bool sync_query) {
|
|
SharedMemoryLimits limits = SharedMemoryLimitsForTesting();
|
|
command_buffer_ = std::make_unique<StrictMock<MockClientCommandBuffer>>();
|
|
|
|
transfer_buffer_ = base::WrapUnique(new MockTransferBuffer(
|
|
command_buffer_.get(), kTransferBufferSize,
|
|
RasterImplementation::kStartingOffset,
|
|
RasterImplementation::kAlignment, transfer_buffer_initialize_fail));
|
|
|
|
helper_ = std::make_unique<RasterCmdHelper>(command_buffer());
|
|
helper_->Initialize(limits.command_buffer_size);
|
|
|
|
gpu_control_ = std::make_unique<StrictMock<MockClientGpuControl>>();
|
|
capabilities_.max_texture_size = kMaxTextureSize;
|
|
capabilities_.sync_query = sync_query;
|
|
EXPECT_CALL(*gpu_control_, GetCapabilities())
|
|
.WillOnce(ReturnRef(capabilities_));
|
|
|
|
{
|
|
InSequence sequence;
|
|
|
|
gl_ = std::make_unique<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);
|
|
EXPECT_CALL(*gpu_control_, CancelAllQueries()).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_;
|
|
raw_ptr<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(); }
|
|
|
|
struct ContextInitOptions {
|
|
ContextInitOptions()
|
|
: bind_generates_resource_client(true),
|
|
lose_context_when_out_of_memory(false),
|
|
transfer_buffer_initialize_fail(false),
|
|
sync_query(true) {}
|
|
bool bind_generates_resource_client;
|
|
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.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());
|
|
}
|
|
|
|
uint32_t MaxTransferBufferSize() {
|
|
return transfer_buffer_->MaxTransferBufferSize();
|
|
}
|
|
|
|
void SetMappedMemoryLimit(size_t limit) {
|
|
gl_->mapped_memory_->set_max_allocated_bytes(limit);
|
|
}
|
|
|
|
ExpectedMemoryInfo GetExpectedMemory(uint32_t size) {
|
|
return transfer_buffer_->GetExpectedMemory(size);
|
|
}
|
|
|
|
ExpectedMemoryInfo GetExpectedResultMemory(uint32_t size) {
|
|
return transfer_buffer_->GetExpectedResultMemory(size);
|
|
}
|
|
|
|
ExpectedMemoryInfo GetExpectedMappedMemory(uint32_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_;
|
|
|
|
raw_ptr<MockClientGpuControl> gpu_control_;
|
|
raw_ptr<RasterCmdHelper> helper_;
|
|
raw_ptr<MockTransferBuffer> transfer_buffer_;
|
|
raw_ptr<RasterImplementation> gl_;
|
|
raw_ptr<CommandBufferEntry> commands_;
|
|
};
|
|
|
|
void RasterImplementationTest::SetUp() {
|
|
ContextInitOptions init_options;
|
|
ASSERT_TRUE(Initialize(init_options));
|
|
}
|
|
|
|
void RasterImplementationTest::TearDown() {
|
|
gl_ = nullptr;
|
|
test_context_.TearDown();
|
|
}
|
|
|
|
class RasterImplementationManualInitTest : public RasterImplementationTest {
|
|
protected:
|
|
void SetUp() override {}
|
|
};
|
|
|
|
const uint8_t RasterImplementationTest::kInitialValue;
|
|
const uint32_t RasterImplementationTest::kNumCommandEntries;
|
|
const uint32_t RasterImplementationTest::kCommandBufferSizeBytes;
|
|
const uint32_t RasterImplementationTest::kTransferBufferSize;
|
|
const GLint RasterImplementationTest::kMaxTextureSize;
|
|
const GLuint RasterImplementationTest::kStartId;
|
|
const GLuint RasterImplementationTest::kBuffersStartId;
|
|
const GLuint RasterImplementationTest::kTexturesStartId;
|
|
const GLuint RasterImplementationTest::kQueriesStartId;
|
|
|
|
TEST_F(RasterImplementationTest, GetBucketContents) {
|
|
const uint32_t kBucketId = RasterImplementation::kResultBucketId;
|
|
const uint32_t kTestSize = MaxTransferBufferSize() + 32;
|
|
|
|
auto buf = base::HeapArray<uint8_t>::Uninit(kTestSize);
|
|
for (uint32_t ii = 0; ii < kTestSize; ++ii) {
|
|
buf[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, buf.data(), MaxTransferBufferSize())))
|
|
.WillOnce(SetMemoryFromArray(mem2.ptr,
|
|
buf.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(buf.data(), &data[0], data.size()));
|
|
}
|
|
|
|
TEST_F(RasterImplementationTest, BeginEndQueryEXT) {
|
|
// GL_COMMANDS_COMPLETED_CHROMIUM,
|
|
// GL_CURRENT_QUERY_EXT
|
|
|
|
std::array<GLuint, 2> expected_ids = {
|
|
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(std::size(expected_ids), &expected_ids[0]);
|
|
std::array<GLuint, std::size(expected_ids)> ids = {};
|
|
gl_->GenQueriesEXT(std::size(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, 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();
|
|
cmd::InsertFenceSync 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, std::size(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, std::size(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, std::size(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 {
|
|
cmd::InsertFenceSync 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));
|
|
gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token_data);
|
|
|
|
EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token))
|
|
.WillOnce(Return(true));
|
|
gpu::SyncToken verified_sync_token = sync_token;
|
|
verified_sync_token.SetVerifyFlush();
|
|
EXPECT_CALL(*gpu_control_, WaitSyncToken(verified_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));
|
|
gpu::SyncToken sync_token;
|
|
gl_->GenUnverifiedSyncTokenCHROMIUM(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);
|
|
}));
|
|
EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token))
|
|
.WillOnce(Return(true));
|
|
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));
|
|
gpu::SyncToken sync_token;
|
|
gl_->GenUnverifiedSyncTokenCHROMIUM(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);
|
|
}));
|
|
EXPECT_CALL(*gpu_control_, CanWaitUnverifiedSyncToken(sync_token))
|
|
.WillOnce(Return(true));
|
|
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);
|
|
uint32_t buffer_size = transfer_buffer_->MaxTransferBufferSize();
|
|
ScopedTransferBufferPtr buffer(buffer_size, helper_, transfer_buffer_);
|
|
ASSERT_EQ(buffer.size(), buffer_size);
|
|
|
|
uint8_t* buffer_start = reinterpret_cast<uint8_t*>(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);
|
|
uint8_t* 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 uint32_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 "gpu/command_buffer/client/raster_implementation_unittest_autogen.h"
|
|
|
|
} // namespace raster
|
|
} // namespace gpu
|