blob: 2e126b68560c56807f6235cfa4c904843fab4d45 [file] [log] [blame]
// Copyright 2016 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 <fcntl.h>
#include <poll.h>
#include <array>
#include <thread>
#if defined(__Fuchsia__)
#include <lib/fdio/directory.h>
#include <lib/zx/channel.h>
#include <filesystem>
#include "fuchsia/sysmem/cpp/fidl.h"
#include "magma_sysmem.h"
#include "platform_logger.h"
#include "platform_trace_provider.h"
#endif
#include "gtest/gtest.h"
#include "magma.h"
#include "magma_common_defs.h"
extern "C" {
#include "test_magma_abi.h"
}
namespace {
inline uint64_t page_size() { return sysconf(_SC_PAGESIZE); }
inline constexpr int64_t ms_to_ns(int64_t ms) { return ms * 1000000ull; }
} // namespace
class TestConnection {
public:
static constexpr const char* kDevicePathFuchsia = "/dev/class/gpu";
static constexpr const char* kDeviceNameLinux = "/dev/dri/renderD128";
static constexpr const char* kDeviceNameVirt = "/dev/magma0";
#if defined(VIRTMAGMA)
static std::string device_name() { return kDeviceNameVirt; }
#elif defined(__linux__)
static std::string device_name() { return kDeviceNameLinux; }
#elif defined(__Fuchsia__)
static std::string device_name() {
// Return the first entry in the device path
for (auto& p : std::filesystem::directory_iterator(kDevicePathFuchsia)) {
return p.path();
}
return "";
}
#else
#error Unimplemented
#endif
static bool is_virtmagma() { return device_name() == kDeviceNameVirt; }
TestConnection() {
std::string device = device_name();
EXPECT_FALSE(device.empty()) << " No GPU device";
if (device.empty())
return;
#if defined(__Fuchsia__)
zx::channel server_end, client_end;
zx::channel::create(0, &server_end, &client_end);
zx_status_t status = fdio_service_connect(device.c_str(), server_end.release());
EXPECT_EQ(status, ZX_OK);
if (status == ZX_OK) {
EXPECT_EQ(MAGMA_STATUS_OK, magma_device_import(client_end.release(), &device_));
}
#elif defined(__linux__)
int fd = open(device.c_str(), O_RDWR);
EXPECT_TRUE(fd >= 0);
if (fd >= 0) {
EXPECT_EQ(MAGMA_STATUS_OK, magma_device_import(fd, &device_));
}
#else
#error Unimplemented
#endif
if (device_) {
magma_create_connection2(device_, &connection_);
}
}
~TestConnection() {
if (connection_)
magma_release_connection(connection_);
if (device_)
magma_device_release(device_);
if (fd_ >= 0)
close(fd_);
}
int fd() { return fd_; }
magma_connection_t connection() { return connection_; }
void Connection() { ASSERT_NE(connection_, nullptr); }
void Context() {
ASSERT_NE(connection_, nullptr);
uint32_t context_id[2];
magma_create_context(connection_, &context_id[0]);
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_error(connection_));
magma_create_context(connection_, &context_id[1]);
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_error(connection_));
magma_release_context(connection_, context_id[0]);
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_error(connection_));
magma_release_context(connection_, context_id[1]);
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_error(connection_));
magma_release_context(connection_, context_id[1]);
EXPECT_NE(MAGMA_STATUS_OK, magma_get_error(connection_));
}
void NotificationChannelHandle() {
uint32_t handle = magma_get_notification_channel_handle(connection_);
EXPECT_NE(0u, handle);
uint32_t handle2 = magma_get_notification_channel_handle(connection_);
EXPECT_EQ(handle, handle2);
}
void WaitNotificationChannel() {
constexpr uint64_t kOneSecondInNs = 1000000000;
magma_status_t status = magma_wait_notification_channel(connection_, kOneSecondInNs);
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, status);
}
void ReadNotificationChannel() {
std::array<unsigned char, 1024> buffer;
uint64_t buffer_size = ~0;
magma_status_t status =
magma_read_notification_channel(connection_, buffer.data(), buffer.size(), &buffer_size);
EXPECT_EQ(MAGMA_STATUS_OK, status);
EXPECT_EQ(0u, buffer_size);
}
void Buffer() {
ASSERT_NE(connection_, nullptr);
uint64_t size = page_size();
uint64_t actual_size;
uint64_t id;
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_buffer(connection_, size, &actual_size, &id));
EXPECT_GE(size, actual_size);
EXPECT_NE(id, 0u);
magma_release_buffer(connection_, id);
}
void BufferMap() {
ASSERT_NE(connection_, nullptr);
uint64_t size = page_size();
uint64_t actual_size;
uint64_t id;
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_buffer(connection_, size, &actual_size, &id));
EXPECT_NE(id, 0u);
magma_map_buffer_gpu(connection_, id, 1024, 0, size / page_size(), MAGMA_GPU_MAP_FLAG_READ);
magma_unmap_buffer_gpu(connection_, id, 2048);
EXPECT_NE(MAGMA_STATUS_OK, magma_get_error(connection_));
EXPECT_EQ(MAGMA_STATUS_MEMORY_ERROR, magma_commit_buffer(connection_, id, 100, 100));
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_error(connection_));
magma_release_buffer(connection_, id);
}
void BufferExport(uint32_t* handle_out, uint64_t* id_out) {
ASSERT_NE(connection_, nullptr);
uint64_t size = page_size();
magma_buffer_t buffer;
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_buffer(connection_, size, &size, &buffer));
*id_out = magma_get_buffer_id(buffer);
EXPECT_EQ(MAGMA_STATUS_OK, magma_export(connection_, buffer, handle_out));
}
void BufferReleaseHandle() {
if (is_virtmagma())
GTEST_SKIP(); // TODO(fxb/13278)
uint64_t id;
uint32_t handle;
BufferExport(&handle, &id);
EXPECT_EQ(MAGMA_STATUS_OK, magma_release_buffer_handle(handle));
}
void BufferImport(uint32_t handle, uint64_t id) {
ASSERT_NE(connection_, nullptr);
magma_buffer_t buffer;
ASSERT_EQ(MAGMA_STATUS_OK, magma_import(connection_, handle, &buffer));
EXPECT_EQ(magma_get_buffer_id(buffer), id);
}
static void BufferImportExport(TestConnection* test1, TestConnection* test2) {
if (is_virtmagma())
GTEST_SKIP(); // TODO(fxb/13278)
uint32_t handle;
uint64_t id;
test1->BufferExport(&handle, &id);
test2->BufferImport(handle, id);
}
void Semaphore(uint32_t count) {
ASSERT_TRUE(connection_);
std::vector<magma_semaphore_t> semaphore(count);
for (uint32_t i = 0; i < count; i++) {
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_semaphore(connection_, &semaphore[i]));
EXPECT_NE(0u, magma_get_semaphore_id(semaphore[i]));
}
// Wait for all
bool wait_all = true;
magma_signal_semaphore(semaphore[0]);
constexpr uint32_t kTimeoutMs = 100;
auto start = std::chrono::steady_clock::now();
EXPECT_EQ(count == 1 ? MAGMA_STATUS_OK : MAGMA_STATUS_TIMED_OUT,
magma_wait_semaphores(semaphore.data(), semaphore.size(), kTimeoutMs, wait_all));
if (count > 1) {
// Subtract to allow for rounding errors in magma_wait_semaphores time calculations
EXPECT_LE(kTimeoutMs - count, std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count());
}
for (uint32_t i = 1; i < semaphore.size(); i++) {
magma_signal_semaphore(semaphore[i]);
}
EXPECT_EQ(MAGMA_STATUS_OK,
magma_wait_semaphores(semaphore.data(), semaphore.size(), 0, wait_all));
for (uint32_t i = 0; i < semaphore.size(); i++) {
magma_reset_semaphore(semaphore[i]);
}
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait_semaphores(semaphore.data(), semaphore.size(), 0, wait_all));
// Wait for one
wait_all = false;
start = std::chrono::steady_clock::now();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait_semaphores(semaphore.data(), semaphore.size(), kTimeoutMs, wait_all));
// Subtract to allow for rounding errors in magma_wait_semaphores time calculations
EXPECT_LE(kTimeoutMs - count, std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start)
.count());
magma_signal_semaphore(semaphore[semaphore.size() - 1]);
EXPECT_EQ(MAGMA_STATUS_OK,
magma_wait_semaphores(semaphore.data(), semaphore.size(), 0, wait_all));
magma_reset_semaphore(semaphore[semaphore.size() - 1]);
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait_semaphores(semaphore.data(), semaphore.size(), 0, wait_all));
for (uint32_t i = 0; i < semaphore.size(); i++) {
magma_release_semaphore(connection_, semaphore[i]);
}
}
void PollWithNotificationChannel(uint32_t semaphore_count) {
ASSERT_TRUE(connection_);
std::vector<magma_poll_item_t> items;
for (uint32_t i = 0; i < semaphore_count; i++) {
magma_semaphore_t semaphore;
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_semaphore(connection_, &semaphore));
items.push_back({.semaphore = semaphore,
.type = MAGMA_POLL_TYPE_SEMAPHORE,
.condition = MAGMA_POLL_CONDITION_SIGNALED});
}
items.push_back({
.handle = magma_get_notification_channel_handle(connection_),
.type = MAGMA_POLL_TYPE_HANDLE,
.condition = MAGMA_POLL_CONDITION_READABLE,
});
constexpr int64_t kTimeoutNs = ms_to_ns(100);
auto start = std::chrono::steady_clock::now();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_poll(items.data(), items.size(), kTimeoutNs));
EXPECT_LE(kTimeoutNs, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start)
.count());
magma_signal_semaphore(items[0].semaphore);
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(items.data(), items.size(), 0));
EXPECT_EQ(items[0].result, items[0].condition);
EXPECT_EQ(items[1].result, 0u);
magma_reset_semaphore(items[0].semaphore);
start = std::chrono::steady_clock::now();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_poll(items.data(), items.size(), kTimeoutNs));
EXPECT_LE(kTimeoutNs, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start)
.count());
for (uint32_t i = 0; i < semaphore_count; i++) {
magma_signal_semaphore(items[i].semaphore);
}
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(items.data(), items.size(), 0));
for (uint32_t i = 0; i < items.size(); i++) {
if (i < items.size() - 1) {
EXPECT_EQ(items[i].result, items[i].condition);
} else {
// Notification channel
EXPECT_EQ(items[i].result, 0u);
}
}
for (uint32_t i = 0; i < semaphore_count; i++) {
magma_release_semaphore(connection_, items[i].semaphore);
}
}
void PollWithTestChannel() {
#ifdef __Fuchsia__
ASSERT_TRUE(connection_);
zx::channel local, remote;
ASSERT_EQ(ZX_OK, zx::channel::create(0 /* flags */, &local, &remote));
magma_semaphore_t semaphore;
ASSERT_EQ(MAGMA_STATUS_OK, magma_create_semaphore(connection_, &semaphore));
std::vector<magma_poll_item_t> items;
items.push_back({.semaphore = semaphore,
.type = MAGMA_POLL_TYPE_SEMAPHORE,
.condition = MAGMA_POLL_CONDITION_SIGNALED});
items.push_back({
.handle = local.get(),
.type = MAGMA_POLL_TYPE_HANDLE,
.condition = MAGMA_POLL_CONDITION_READABLE,
});
constexpr int64_t kTimeoutNs = ms_to_ns(100);
auto start = std::chrono::steady_clock::now();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_poll(items.data(), items.size(), kTimeoutNs));
EXPECT_LE(kTimeoutNs, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start)
.count());
magma_signal_semaphore(semaphore);
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(items.data(), items.size(), 0));
EXPECT_EQ(items[0].result, items[0].condition);
EXPECT_EQ(items[1].result, 0u);
magma_reset_semaphore(semaphore);
start = std::chrono::steady_clock::now();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_poll(items.data(), items.size(), kTimeoutNs));
EXPECT_LE(kTimeoutNs, std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start)
.count());
uint32_t dummy;
EXPECT_EQ(ZX_OK, remote.write(0 /* flags */, &dummy, sizeof(dummy), nullptr /* handles */,
0 /* num_handles*/));
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(items.data(), items.size(), 0));
EXPECT_EQ(items[0].result, 0u);
EXPECT_EQ(items[1].result, items[1].condition);
magma_signal_semaphore(semaphore);
EXPECT_EQ(MAGMA_STATUS_OK, magma_poll(items.data(), items.size(), 0));
EXPECT_EQ(items[0].result, items[0].condition);
EXPECT_EQ(items[1].result, items[1].condition);
#else
GTEST_SKIP();
#endif
}
void PollChannelClosed() {
#ifdef __Fuchsia__
ASSERT_TRUE(connection_);
zx::channel local, remote;
ASSERT_EQ(ZX_OK, zx::channel::create(0 /* flags */, &local, &remote));
std::vector<magma_poll_item_t> items;
items.push_back({
.handle = local.get(),
.type = MAGMA_POLL_TYPE_HANDLE,
.condition = MAGMA_POLL_CONDITION_READABLE,
});
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_poll(items.data(), items.size(), 0));
remote.reset();
EXPECT_EQ(MAGMA_STATUS_CONNECTION_LOST, magma_poll(items.data(), items.size(), 0));
#else
GTEST_SKIP();
#endif
}
void SemaphoreExport(uint32_t* handle_out, uint64_t* id_out) {
ASSERT_NE(connection_, nullptr);
magma_semaphore_t semaphore;
ASSERT_EQ(magma_create_semaphore(connection_, &semaphore), MAGMA_STATUS_OK);
*id_out = magma_get_semaphore_id(semaphore);
EXPECT_EQ(magma_export_semaphore(connection_, semaphore, handle_out), MAGMA_STATUS_OK);
}
void SemaphoreImport(uint32_t handle, uint64_t id) {
ASSERT_NE(connection_, nullptr);
magma_semaphore_t semaphore;
ASSERT_EQ(magma_import_semaphore(connection_, handle, &semaphore), MAGMA_STATUS_OK);
EXPECT_EQ(magma_get_semaphore_id(semaphore), id);
}
static void SemaphoreImportExport(TestConnection* test1, TestConnection* test2) {
if (is_virtmagma())
GTEST_SKIP(); // TODO(fxb/13278)
uint32_t handle;
uint64_t id;
test1->SemaphoreExport(&handle, &id);
test2->SemaphoreImport(handle, id);
}
void ImmediateCommands() {
uint32_t context_id;
magma_create_context(connection_, &context_id);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_inline_command_buffer inline_command_buffer{};
magma_execute_immediate_commands2(connection_, context_id, 0, &inline_command_buffer);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_release_context(connection_, context_id);
EXPECT_EQ(magma_get_error(connection_), 0);
}
void ImageFormat() {
#if !defined(__Fuchsia__)
GTEST_SKIP();
#else
fuchsia::sysmem::SingleBufferSettings buffer_settings;
buffer_settings.has_image_format_constraints = true;
buffer_settings.image_format_constraints.pixel_format.type =
fuchsia::sysmem::PixelFormatType::NV12;
buffer_settings.image_format_constraints.min_bytes_per_row = 128;
buffer_settings.image_format_constraints.bytes_per_row_divisor = 256;
buffer_settings.image_format_constraints.min_coded_height = 64;
fidl::Encoder encoder(fidl::Encoder::NO_HEADER);
encoder.Alloc(
fidl::EncodingInlineSize<fuchsia::sysmem::SingleBufferSettings, fidl::Encoder>(&encoder));
buffer_settings.Encode(&encoder, 0);
std::vector<uint8_t> encoded_bytes = encoder.TakeBytes();
size_t real_size = encoded_bytes.size();
// Add an extra byte to ensure the size is correct.
encoded_bytes.push_back(0);
magma_buffer_format_description_t description;
ASSERT_EQ(MAGMA_STATUS_OK,
magma_get_buffer_format_description(encoded_bytes.data(), real_size, &description));
magma_image_plane_t planes[4];
EXPECT_EQ(MAGMA_STATUS_OK,
magma_get_buffer_format_plane_info_with_size(description, 128u, 64u, planes));
EXPECT_EQ(256u, planes[0].bytes_per_row);
EXPECT_EQ(0u, planes[0].byte_offset);
EXPECT_EQ(256u, planes[1].bytes_per_row);
EXPECT_EQ(256u * 64u, planes[1].byte_offset);
EXPECT_EQ(MAGMA_STATUS_OK,
magma_get_buffer_format_plane_info_with_size(description, 128u, 64u, planes));
EXPECT_EQ(256u, planes[0].bytes_per_row);
EXPECT_EQ(0u, planes[0].byte_offset);
EXPECT_EQ(256u, planes[1].bytes_per_row);
EXPECT_EQ(256u * 64u, planes[1].byte_offset);
magma_buffer_format_description_release(description);
EXPECT_EQ(MAGMA_STATUS_INVALID_ARGS, magma_get_buffer_format_description(
encoded_bytes.data(), real_size + 1, &description));
EXPECT_EQ(MAGMA_STATUS_INVALID_ARGS, magma_get_buffer_format_description(
encoded_bytes.data(), real_size - 1, &description));
#endif
}
void Sysmem(bool use_format_modifier) {
#if !defined(__Fuchsia__)
GTEST_SKIP();
#else
magma_sysmem_connection_t connection;
zx::channel local_endpoint, server_endpoint;
EXPECT_EQ(ZX_OK, zx::channel::create(0u, &local_endpoint, &server_endpoint));
EXPECT_EQ(ZX_OK,
fdio_service_connect("/svc/fuchsia.sysmem.Allocator", server_endpoint.release()));
EXPECT_EQ(MAGMA_STATUS_OK,
magma_sysmem_connection_import(local_endpoint.release(), &connection));
magma_buffer_collection_t collection;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_buffer_collection_import(connection, ZX_HANDLE_INVALID, &collection));
magma_buffer_format_constraints_t buffer_constraints{};
buffer_constraints.count = 1;
buffer_constraints.usage = 0;
buffer_constraints.secure_permitted = false;
buffer_constraints.secure_required = false;
buffer_constraints.cpu_domain_supported = true;
magma_sysmem_buffer_constraints_t constraints;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_buffer_constraints_create(connection, &buffer_constraints, &constraints));
// Create a set of basic 512x512 RGBA image constraints.
magma_image_format_constraints_t image_constraints{};
image_constraints.image_format = MAGMA_FORMAT_R8G8B8A8;
image_constraints.has_format_modifier = use_format_modifier;
image_constraints.format_modifier = use_format_modifier ? MAGMA_FORMAT_MODIFIER_LINEAR : 0;
image_constraints.width = 512;
image_constraints.height = 512;
image_constraints.layers = 1;
image_constraints.bytes_per_row_divisor = 1;
image_constraints.min_bytes_per_row = 0;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_buffer_constraints_set_format(connection, constraints, 0, &image_constraints));
EXPECT_EQ(MAGMA_STATUS_OK,
magma_buffer_collection_set_constraints(connection, collection, constraints));
// Buffer should be allocated now.
magma_buffer_format_description_t description;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_sysmem_get_description_from_collection(connection, collection, &description));
uint32_t buffer_count;
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_buffer_count(description, &buffer_count));
EXPECT_EQ(1u, buffer_count);
magma_bool_t is_secure;
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_buffer_is_secure(description, &is_secure));
EXPECT_FALSE(is_secure);
magma_bool_t has_format_modifier;
uint64_t format_modifier;
EXPECT_EQ(MAGMA_STATUS_OK, magma_get_buffer_format_modifier(description, &has_format_modifier,
&format_modifier));
if (has_format_modifier) {
EXPECT_EQ(MAGMA_FORMAT_MODIFIER_LINEAR, format_modifier);
}
magma_image_plane_t planes[4];
EXPECT_EQ(MAGMA_STATUS_OK,
magma_get_buffer_format_plane_info_with_size(description, 512u, 512u, planes));
EXPECT_EQ(512 * 4u, planes[0].bytes_per_row);
EXPECT_EQ(0u, planes[0].byte_offset);
EXPECT_EQ(MAGMA_STATUS_OK,
magma_get_buffer_format_plane_info_with_size(description, 512, 512, planes));
EXPECT_EQ(512 * 4u, planes[0].bytes_per_row);
EXPECT_EQ(0u, planes[0].byte_offset);
uint32_t handle;
uint32_t offset;
EXPECT_EQ(MAGMA_STATUS_OK, magma_sysmem_get_buffer_handle_from_collection(
connection, collection, 0, &handle, &offset));
EXPECT_EQ(MAGMA_STATUS_OK, magma_release_buffer_handle(handle));
magma_buffer_collection_release(connection, collection);
magma_buffer_constraints_release(connection, constraints);
magma_sysmem_connection_release(connection);
#endif
}
void TracingInit() {
#if !defined(__Fuchsia__)
GTEST_SKIP();
#else
zx::channel local_endpoint, server_endpoint;
EXPECT_EQ(ZX_OK, zx::channel::create(0u, &local_endpoint, &server_endpoint));
EXPECT_EQ(ZX_OK, fdio_service_connect("/svc/fuchsia.tracing.provider.Registry",
server_endpoint.release()));
EXPECT_EQ(MAGMA_STATUS_OK, magma_initialize_tracing(local_endpoint.release()));
if (magma::PlatformTraceProvider::Get())
EXPECT_TRUE(magma::PlatformTraceProvider::Get()->IsInitialized());
#endif
}
void LoggingInit() {
#if !defined(__Fuchsia__)
GTEST_SKIP();
#else
zx::channel local_endpoint, server_endpoint;
EXPECT_EQ(ZX_OK, zx::channel::create(0u, &local_endpoint, &server_endpoint));
EXPECT_EQ(ZX_OK,
fdio_service_connect("/svc/fuchsia.logger.LogSink", server_endpoint.release()));
EXPECT_EQ(MAGMA_STATUS_OK, magma_initialize_logging(local_endpoint.release()));
EXPECT_TRUE(magma::PlatformLogger::IsInitialized());
MAGMA_LOG(INFO, "LoggingInit test complete");
#endif
}
void GetDeviceIdImported() {
uint64_t device_id = 0;
EXPECT_EQ(MAGMA_STATUS_OK, magma_query2(device_, MAGMA_QUERY_DEVICE_ID, &device_id));
EXPECT_NE(0u, device_id);
}
void GetVendorIdImported() {
uint64_t vendor_id = 0;
EXPECT_EQ(MAGMA_STATUS_OK, magma_query2(device_, MAGMA_QUERY_VENDOR_ID, &vendor_id));
EXPECT_NE(0u, vendor_id);
}
void GetMinimumMappableAddressImported() {
uint64_t address;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_query2(device_, MAGMA_QUERY_MINIMUM_MAPPABLE_ADDRESS, &address));
}
void QueryReturnsBufferImported() {
uint32_t handle_out = 0;
// Drivers shouldn't allow this value to be queried through this entrypoint.
EXPECT_NE(MAGMA_STATUS_OK,
magma_query_returns_buffer2(device_, MAGMA_QUERY_DEVICE_ID, &handle_out));
EXPECT_EQ(0u, handle_out);
}
void QueryTestRestartSupported() {
uint64_t is_supported = 0;
EXPECT_EQ(MAGMA_STATUS_OK,
magma_query2(device_, MAGMA_QUERY_IS_TEST_RESTART_SUPPORTED, &is_supported));
// We don't care about the value of |is_supported|, just that the query returns ok.
}
private:
int fd_ = -1;
magma_device_t device_ = 0;
magma_connection_t connection_ = 0;
};
class TestConnectionWithContext : public TestConnection {
public:
TestConnectionWithContext() { magma_create_context(connection(), &context_id_); }
~TestConnectionWithContext() { magma_release_context(connection(), context_id_); }
uint32_t context_id() { return context_id_; }
void ExecuteCommandBufferWithResources(uint32_t resource_count) {
magma_system_command_buffer command_buffer = {.num_resources = resource_count};
magma_system_exec_resource resources[resource_count];
memset(resources, 0, sizeof(magma_system_exec_resource) * resource_count);
magma_execute_command_buffer_with_resources(connection(), context_id(), &command_buffer,
resources, nullptr);
// Command buffer is mostly zeros, so we expect an error here
EXPECT_NE(MAGMA_STATUS_OK, magma_get_error(connection()));
}
void ExecuteCommandBufferNoResources() {
magma_system_command_buffer command_buffer = {.num_resources = 0};
magma_execute_command_buffer_with_resources(connection(), context_id(), &command_buffer,
nullptr /* resources */, nullptr);
}
private:
uint32_t context_id_;
};
// NOTE: LoggingInit is first so other tests may use logging.
TEST(MagmaAbi, LoggingInit) {
TestConnection test;
test.LoggingInit();
}
TEST(MagmaAbi, DeviceId) {
TestConnection test;
test.GetDeviceIdImported();
}
TEST(MagmaAbi, VendorId) {
TestConnection test;
test.GetVendorIdImported();
}
TEST(MagmaAbi, MinimumMappableAddress) {
TestConnection test;
test.GetMinimumMappableAddressImported();
}
TEST(MagmaAbi, QueryReturnsBuffer) {
TestConnection test;
test.QueryReturnsBufferImported();
}
TEST(MagmaAbi, QueryTestRestartSupported) {
TestConnection test;
test.QueryTestRestartSupported();
}
TEST(MagmaAbi, TracingInit) {
TestConnection test;
test.TracingInit();
}
TEST(MagmaAbi, Buffer) {
TestConnection test;
test.Buffer();
}
TEST(MagmaAbi, Connection) {
TestConnection test;
test.Connection();
}
TEST(MagmaAbi, Context) {
TestConnection test;
test.Context();
}
TEST(MagmaAbi, NotificationChannelHandle) {
TestConnection test;
test.NotificationChannelHandle();
}
TEST(MagmaAbi, ReadNotificationChannel) {
TestConnection test;
test.ReadNotificationChannel();
}
TEST(MagmaAbi, BufferMap) {
TestConnection test;
test.BufferMap();
}
TEST(MagmaAbi, BufferReleaseHandle) {
TestConnection test;
test.BufferReleaseHandle();
}
TEST(MagmaAbi, BufferImportExport) {
TestConnection test1;
TestConnection test2;
TestConnection::BufferImportExport(&test1, &test2);
}
TEST(MagmaAbi, Semaphore) {
TestConnection test;
test.Semaphore(1);
test.Semaphore(2);
test.Semaphore(3);
}
TEST(MagmaAbi, SemaphoreImportExport) {
TestConnection test1;
TestConnection test2;
TestConnection::SemaphoreImportExport(&test1, &test2);
}
TEST(MagmaAbi, ImmediateCommands) { TestConnection().ImmediateCommands(); }
TEST(MagmaAbi, PollWithNotificationChannel) {
TestConnection().PollWithNotificationChannel(1);
TestConnection().PollWithNotificationChannel(2);
TestConnection().PollWithNotificationChannel(3);
}
TEST(MagmaAbi, PollWithTestChannel) { TestConnection().PollWithTestChannel(); }
TEST(MagmaAbi, PollChannelClosed) { TestConnection().PollChannelClosed(); }
TEST(MagmaAbi, ImageFormat) {
TestConnection test;
test.ImageFormat();
}
TEST(MagmaAbi, Sysmem) {
TestConnection test;
test.Sysmem(false);
}
TEST(MagmaAbi, SysmemLinearFormatModifier) {
TestConnection test;
test.Sysmem(true);
}
TEST(MagmaAbi, FromC) { EXPECT_TRUE(test_magma_abi_from_c(TestConnection::device_name().c_str())); }
TEST(MagmaAbi, ExecuteCommandBufferWithResources) {
TestConnectionWithContext().ExecuteCommandBufferWithResources(5);
}
TEST(MagmaAbi, ExecuteCommandBufferNoResources) {
TestConnectionWithContext().ExecuteCommandBufferNoResources();
}
TEST(MagmaAbiPerf, ExecuteCommandBufferWithResources) {
if (TestConnection::is_virtmagma())
GTEST_SKIP();
TestConnectionWithContext test;
auto start = std::chrono::steady_clock::now();
constexpr uint32_t kTestIterations = 10000;
for (uint32_t test_iter = kTestIterations; test_iter; --test_iter) {
test.ExecuteCommandBufferWithResources(10);
}
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start);
printf("ExecuteCommandBufferWithResources: avg duration %lld ns\n",
duration.count() / kTestIterations);
}