blob: 68f001b53e7d5e71a22babf12b36c7b44fd9757b [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 "magma.h"
#include "magma_util/dlog.h"
#include "magma_util/macros.h"
#include "platform_buffer.h"
#include "gtest/gtest.h"
#include <thread>
extern "C" {
#include "test_magma_abi.h"
}
class TestConnection {
public:
TestConnection() : TestConnection(MAGMA_CAPABILITY_RENDERING) {}
TestConnection(uint32_t capability)
{
if (capability == MAGMA_CAPABILITY_RENDERING)
fd_ = open("/dev/class/gpu/000", O_RDONLY);
else if (capability == MAGMA_CAPABILITY_DISPLAY)
fd_ = open("/dev/class/display/000", O_RDONLY);
DASSERT(fd_ >= 0);
connection_ = magma_create_connection(fd(), capability);
}
~TestConnection()
{
if (connection_)
magma_release_connection(connection_);
close(fd_);
}
int fd() { return fd_; }
void GetDeviceId()
{
uint64_t device_id = 0;
EXPECT_EQ(MAGMA_STATUS_OK, magma_query(fd_, MAGMA_QUERY_DEVICE_ID, &device_id));
EXPECT_NE(0u, device_id);
}
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_get_error(connection_), 0);
magma_create_context(connection_, &context_id[1]);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_release_context(connection_, context_id[0]);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_release_context(connection_, context_id[1]);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_release_context(connection_, context_id[1]);
EXPECT_NE(magma_get_error(connection_), 0);
}
void NotificationChannel()
{
int fd1 = magma_get_notification_channel_fd(connection_);
EXPECT_LE(0, fd1);
int fd2 = magma_get_notification_channel_fd(connection_);
EXPECT_LE(0, fd1);
EXPECT_NE(fd1, fd2);
struct pollfd fds[2] = {{fd1, POLLIN, 0}, {fd2, POLLOUT, 0}};
int result = poll(fds, 2, 0);
// No events should exist.
EXPECT_EQ(0, result);
EXPECT_EQ(0, fds[0].revents);
EXPECT_EQ(0, fds[1].revents);
close(fd1);
close(fd2);
}
void Buffer()
{
ASSERT_NE(connection_, nullptr);
uint64_t size = PAGE_SIZE;
uint64_t actual_size;
uint64_t id;
EXPECT_EQ(magma_create_buffer(connection_, size, &actual_size, &id), 0);
EXPECT_GE(size, actual_size);
EXPECT_NE(id, 0u);
magma_release_buffer(connection_, id);
}
void WaitRendering()
{
ASSERT_NE(connection_, nullptr);
uint64_t size = PAGE_SIZE;
uint64_t id;
EXPECT_EQ(magma_create_buffer(connection_, size, &size, &id), 0);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_wait_rendering(connection_, id);
EXPECT_EQ(magma_get_error(connection_), 0);
magma_release_buffer(connection_, id);
EXPECT_EQ(magma_get_error(connection_), 0);
}
void BufferMap()
{
ASSERT_NE(connection_, nullptr);
uint64_t size = PAGE_SIZE;
uint64_t actual_size;
uint64_t id;
EXPECT_EQ(magma_create_buffer(connection_, size, &actual_size, &id), 0);
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);
magma_commit_buffer(connection_, id, 100, 100);
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;
EXPECT_EQ(magma_create_buffer(connection_, size, &size, &buffer), 0);
*id_out = magma_get_buffer_id(buffer);
EXPECT_EQ(magma_export(connection_, buffer, handle_out), 0);
}
void BufferImport(uint32_t handle, uint64_t id)
{
ASSERT_NE(connection_, nullptr);
magma_buffer_t buffer;
EXPECT_EQ(magma_import(connection_, handle, &buffer), 0);
EXPECT_EQ(magma_get_buffer_id(buffer), id);
}
static void BufferImportExport(TestConnection* test1, TestConnection* test2)
{
uint32_t handle;
uint64_t id;
test1->BufferExport(&handle, &id);
test2->BufferImport(handle, id);
}
void BufferExportFd(int* fd_out, uint64_t* id_out)
{
ASSERT_NE(connection_, nullptr);
uint64_t size = PAGE_SIZE;
magma_buffer_t buffer;
EXPECT_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_fd(connection_, buffer, fd_out));
}
void BufferImportFd(int fd, uint64_t id)
{
ASSERT_NE(connection_, nullptr);
magma_buffer_t buffer;
EXPECT_EQ(MAGMA_STATUS_OK, magma_import_fd(connection_, fd, &buffer));
EXPECT_EQ(magma_get_buffer_id(buffer), id);
}
static void BufferImportExportFd(TestConnection* test1, TestConnection* test2)
{
int fd;
uint64_t id;
test1->BufferExportFd(&fd, &id);
test2->BufferImportFd(fd, id);
// fd should still be open.
EXPECT_EQ(0, close(fd));
}
void Semaphore()
{
ASSERT_NE(connection_, nullptr);
magma_semaphore_t semaphore;
EXPECT_EQ(MAGMA_STATUS_OK, magma_create_semaphore(connection_, &semaphore));
EXPECT_NE(0u, magma_get_semaphore_id(semaphore));
std::thread thread([semaphore] {
EXPECT_EQ(MAGMA_STATUS_OK, magma_wait_semaphore_no_reset(semaphore, 0, 1000));
EXPECT_EQ(MAGMA_STATUS_OK, magma_wait_semaphore(semaphore, 0));
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_wait_semaphore(semaphore, 100));
});
magma_signal_semaphore(semaphore);
thread.join();
magma_signal_semaphore(semaphore);
magma_reset_semaphore(semaphore);
thread = std::thread([semaphore] {
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT, magma_wait_semaphore(semaphore, 100));
});
thread.join();
magma_release_semaphore(connection_, semaphore);
}
void SemaphoreExport(uint32_t* handle_out, uint64_t* id_out)
{
ASSERT_NE(connection_, nullptr);
magma_semaphore_t semaphore;
EXPECT_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;
EXPECT_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)
{
uint32_t handle;
uint64_t id;
test1->SemaphoreExport(&handle, &id);
test2->SemaphoreImport(handle, id);
}
private:
int fd_ = -1;
magma_connection_t* connection_;
};
class TestDisplayConnection : public TestConnection {
public:
TestDisplayConnection() : TestConnection(MAGMA_CAPABILITY_DISPLAY) {}
bool Display(uint32_t num_buffers, uint32_t num_frames)
{
magma_status_t status;
magma_display_size display_size;
status = magma_display_get_size(fd(), &display_size);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "magma_display_get_size returned %d", status);
std::vector<magma_buffer_t> buffers;
std::vector<magma_semaphore_t> wait_semaphores;
std::vector<magma_semaphore_t> signal_semaphores;
std::vector<magma_semaphore_t> buffer_presented_semaphores;
for (uint32_t i = 0; i < num_buffers; i++) {
magma_buffer_t buffer;
uint64_t actual_size;
status = magma_create_buffer(connection(), display_size.width * 4 * display_size.height,
&actual_size, &buffer);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "magma_alloc returned %d", status);
buffers.push_back(buffer);
magma_semaphore_t semaphore;
status = magma_create_semaphore(connection(), &semaphore);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "magma_create_semaphore returned %d", status);
wait_semaphores.push_back(semaphore);
status = magma_create_semaphore(connection(), &semaphore);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "magma_create_semaphore returned %d", status);
signal_semaphores.push_back(semaphore);
status = magma_create_semaphore(connection(), &semaphore);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "magma_create_semaphore returned %d", status);
buffer_presented_semaphores.push_back(semaphore);
}
for (uint32_t frame = 0; frame < num_frames; frame++) {
uint32_t index = frame % buffers.size();
status = magma_display_page_flip(connection(), buffers[index], 1,
&wait_semaphores[index], 1, &signal_semaphores[index],
buffer_presented_semaphores[index]);
magma_signal_semaphore(wait_semaphores[index]);
if (frame > 0) {
uint32_t last_index = (frame - 1) % buffers.size();
status = magma_wait_semaphore(buffer_presented_semaphores[last_index], 1000);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "wait on signal semaphore failed");
DLOG("buffer presented");
status = magma_wait_semaphore(signal_semaphores[last_index], 1000);
if (status != MAGMA_STATUS_OK)
return DRETF(false, "wait on signal semaphore failed");
}
}
return true;
}
};
TEST(MagmaAbi, DeviceId)
{
TestConnection test;
test.GetDeviceId();
}
TEST(MagmaAbi, Buffer)
{
TestConnection test;
test.Buffer();
}
TEST(MagmaAbi, Connection)
{
TestConnection test;
test.Connection();
}
TEST(MagmaAbi, Context)
{
TestConnection test;
test.Context();
}
TEST(MagmaAbi, NotificationChannel)
{
TestConnection test;
test.NotificationChannel();
}
TEST(MagmaAbi, WaitRendering)
{
TestConnection test;
test.WaitRendering();
}
TEST(MagmaAbi, BufferMap)
{
TestConnection test;
test.BufferMap();
}
TEST(MagmaAbi, BufferImportExport)
{
TestConnection test1;
TestConnection test2;
TestConnection::BufferImportExport(&test1, &test2);
}
TEST(MagmaAbi, BufferImportExportFd)
{
TestConnection test1;
TestConnection test2;
TestConnection::BufferImportExportFd(&test1, &test2);
}
TEST(MagmaAbi, Semaphore)
{
TestConnection test;
test.Semaphore();
}
TEST(MagmaAbi, SemaphoreImportExport)
{
TestConnection test1;
TestConnection test2;
TestConnection::SemaphoreImportExport(&test1, &test2);
}
TEST(MagmaAbi, FromC) { EXPECT_TRUE(test_magma_abi_from_c()); }
TEST(MagmaAbi, DisplayDoubleBuffered)
{
TestDisplayConnection test;
EXPECT_TRUE(test.Display(2, 60));
}
TEST(MagmaAbi, DisplayTripleBuffered)
{
TestDisplayConnection test;
EXPECT_TRUE(test.Display(3, 60));
}