blob: 89171e267de9432251bb6274a7e22438e1689614 [file] [edit]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/magma_service/test_util/platform_msd_device_helper.h>
#include <gtest/gtest.h>
#include "src/graphics/drivers/msd-vsi-vip/src/command_buffer.h"
#include "src/graphics/drivers/msd-vsi-vip/src/msd_vsi_device.h"
class TestCommandBuffer : public ::testing::Test {
public:
static constexpr uint32_t kAddressSpaceIndex = 1;
struct BufferDesc {
uint32_t buffer_size;
uint32_t map_page_count;
uint32_t data_size;
uint32_t batch_offset;
uint32_t gpu_addr;
};
void SetUp() override {
constexpr bool kEnableSuspend = true;
device_ = MsdVsiDevice::Create(GetTestDeviceHandle(), kEnableSuspend);
ASSERT_NE(device_, nullptr);
ASSERT_TRUE(device_->IsIdle());
address_space_owner_ = std::make_unique<AddressSpaceOwner>(device_->GetBusMapper());
ASSERT_NO_FATAL_FAILURE(CreateClient(kAddressSpaceIndex, &client_));
}
void Release() {
client_.reset();
device_.reset();
address_space_owner_.reset();
}
protected:
// This holds the buffer that can be passed as the context state buffer for a batch.
class FakeContextStateBuffer {
public:
// Returns a fake context state buffer in |out_buffer|, which has an EVENT command written
// to the underlying platform buffer. After submitting the batch, the caller can verify the
// buffer was executed by calling |WaitForCompletion|, which will wait for the EVENT
// to be executed.
static void CreateWithEvent(std::unique_ptr<MsdVsiDevice>& device,
std::shared_ptr<MsdVsiContext> context, uint32_t gpu_addr,
std::unique_ptr<FakeContextStateBuffer>* out_buffer) {
constexpr uint32_t kBufferSize = 4096;
constexpr uint32_t kMapPageCount = 1;
constexpr uint32_t kDataSize = 8; // EVENT command.
std::shared_ptr<MsdVsiBuffer> buf;
std::unique_ptr<magma::PlatformSemaphore> semaphore;
ASSERT_NO_FATAL_FAILURE(
CreateAndMapBuffer(context, kBufferSize, kMapPageCount, gpu_addr, &buf));
ASSERT_NO_FATAL_FAILURE(WriteEventCommand(device, context, buf, 0 /* offset */, &semaphore));
*out_buffer =
std::make_unique<FakeContextStateBuffer>(std::move(buf), kDataSize, std::move(semaphore));
}
FakeContextStateBuffer(std::shared_ptr<MsdVsiBuffer> buf, uint32_t data_size,
std::unique_ptr<magma::PlatformSemaphore> semaphore)
: buf_(buf), data_size_(data_size), semaphore_(std::move(semaphore)) {}
CommandBuffer::ExecResource ExecResource() {
return CommandBuffer::ExecResource{.buffer = buf_, .offset = 0, .length = data_size_};
}
void WaitForCompletion() {
constexpr uint64_t kTimeoutMs = 1000;
ASSERT_EQ(MAGMA_STATUS_OK, semaphore_->Wait(kTimeoutMs).get());
}
private:
std::shared_ptr<MsdVsiBuffer> buf_;
uint32_t data_size_;
std::unique_ptr<magma::PlatformSemaphore> semaphore_;
};
class AddressSpaceOwner : public AddressSpace::Owner {
public:
AddressSpaceOwner(magma::PlatformBusMapper* bus_mapper) : bus_mapper_(bus_mapper) {}
virtual ~AddressSpaceOwner() = default;
void AddressSpaceReleased(AddressSpace* address_space) override {}
magma::PlatformBusMapper* GetBusMapper() override { return bus_mapper_; }
private:
magma::PlatformBusMapper* bus_mapper_;
};
struct Client {
std::shared_ptr<MsdVsiConnection> connection;
std::shared_ptr<MsdVsiContext> context;
std::shared_ptr<AddressSpace> address_space;
Client(std::shared_ptr<MsdVsiConnection> connection, std::shared_ptr<MsdVsiContext> context,
std::shared_ptr<AddressSpace> address_space)
: connection(connection), context(context), address_space(address_space) {}
};
void CreateClient(uint32_t address_space_index, std::unique_ptr<Client>* out_client) {
std::shared_ptr<AddressSpace> address_space =
AddressSpace::Create(address_space_owner_.get(), address_space_index);
ASSERT_NE(address_space, nullptr);
device_->page_table_arrays()->AssignAddressSpace(address_space_index, address_space.get());
auto connection =
std::make_shared<MsdVsiConnection>(device_.get(), address_space, 1 /* client_id */);
ASSERT_NE(connection, nullptr);
auto context = MsdVsiContext::Create(connection, address_space, device_->GetRingbuffer());
ASSERT_NE(context, nullptr);
*out_client = std::make_unique<Client>(connection, context, address_space);
}
// Creates a buffer of |buffer_size| and stores it in |out_buffer|.
static void CreateMsdBuffer(uint32_t buffer_size, std::shared_ptr<MsdVsiBuffer>* out_buffer);
// Creates a buffer of |buffer_size| bytes, and maps the buffer to |gpu_addr|.
// |map_page_count| may be less bytes than buffer size.
static void CreateAndMapBuffer(std::shared_ptr<MsdVsiContext> context, uint32_t buffer_size,
uint32_t map_page_count, uint32_t gpu_addr,
std::shared_ptr<MsdVsiBuffer>* out_buffer);
// Creates a new command buffer.
// |data_size| is the actual length of the user provided data and may be smaller than
// the size of |buffer|.
// |signal| is an optional semaphore. If present, it will be signalled after the batch
// is submitted via |SubmitBatch| and execution completes.
void CreateAndPrepareBatch(std::shared_ptr<MsdVsiContext> context,
std::shared_ptr<MsdVsiBuffer> buffer, uint32_t data_size,
uint32_t batch_offset,
std::shared_ptr<magma::PlatformSemaphore> signal,
std::optional<CommandBuffer::ExecResource> context_state_buffer,
std::unique_ptr<CommandBuffer>* out_batch);
// Creates a buffer from |buffer_desc|, writes a test instruction to it and
// submits it as a command buffer.
// |signal| is an optional semaphore that will be passed as a signal semaphore for the batch.
// If |fault_addr| is populated, the submitted buffer will contain faulting instructions.
// If |validate_batch| is true, this will check that the batch is correctly mapped and has valid
// sized buffers. If |out_buffer| is non-null, it will be populated with the created buffer.
void CreateAndSubmitBuffer(
std::shared_ptr<MsdVsiContext> context, const BufferDesc& buffer_desc,
std::shared_ptr<magma::PlatformSemaphore> signal, std::optional<uint32_t> fault_addr,
std::optional<CommandBuffer::ExecResource> context_state_buffer = std::nullopt,
bool validate_batch = true, std::shared_ptr<MsdVsiBuffer>* out_buffer = nullptr);
// Creates a buffer from |buffer_desc|, writes a test instruction to it and
// submits it as a command buffer. This will wait for execution to complete.
// If |out_buffer| is non-null, it will be populated with the created buffer.
void CreateAndSubmitBufferWaitCompletion(
std::shared_ptr<MsdVsiContext> context, const BufferDesc& buffer_desc,
std::optional<CommandBuffer::ExecResource> context_state_buffer = std::nullopt,
bool validate_batch = true, std::shared_ptr<MsdVsiBuffer>* out_buffer = nullptr);
// Writes a single WAIT command in |buf| at |offset|.
void WriteWaitCommand(std::shared_ptr<MsdVsiBuffer> buf, uint32_t offset);
// Writes a single LINK command in |buf| at |offset|.
void WriteLinkCommand(std::shared_ptr<MsdVsiBuffer> buffer, uint32_t offset, uint16_t prefetch,
uint32_t gpu_addr);
// Writes an EVENT command in |buf| at |offset|. |out_semaphore| will be populated with the
// semaphore that will be signalled once the interrupt associated with the event occurs.
static void WriteEventCommand(std::unique_ptr<MsdVsiDevice>& device,
std::shared_ptr<MsdVsiContext> context,
std::shared_ptr<MsdVsiBuffer> buffer, uint32_t offset,
std::unique_ptr<magma::PlatformSemaphore>* out_semaphore);
void DropDefaultClient() { client_ = nullptr; }
std::shared_ptr<MsdVsiConnection> default_connection() { return client_->connection; }
std::shared_ptr<MsdVsiContext> default_context() { return client_->context; }
std::shared_ptr<AddressSpace> default_address_space() { return client_->address_space; }
std::unique_ptr<AddressSpaceOwner> address_space_owner_;
std::unique_ptr<MsdVsiDevice> device_;
std::unique_ptr<Client> client_;
};