blob: 1e7458c5b08ec4aed00da3bdd262bfeec668f421 [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 "helper/platform_device_helper.h"
#include "msd_intel_device.h"
#include "test_command_buffer.h"
#include "gtest/gtest.h"
class ContextRelease {
public:
ContextRelease(std::shared_ptr<ClientContext> context) : context_(context) {}
~ContextRelease() { context_->Shutdown(); }
private:
std::shared_ptr<ClientContext> context_;
};
class TestExec {
public:
void GlobalGttReuseGpuAddress() { Exec(true, true, false); }
void PerProcessGttReuseGpuAddress() { Exec(false, true, false); }
void PerProcessGttCheckMappingRelease() { Exec(false, false, true); }
// Submits a few command buffers through the full connection-context flow.
// Uses per process gtt unless |kUseGlobalGtt| is specified.
// If |kReuseGpuAddr|, ensures two command buffers are submitted with the same gpu address.
// If |kCheckMappingRelease|, checks a buffer mapping is released after the next command buffer
void Exec(const bool kUseGlobalGtt, const bool kReuseGpuAddr, const bool kCheckMappingRelease)
{
ASSERT_FALSE(kUseGlobalGtt && kCheckMappingRelease);
magma::PlatformPciDevice* platform_device = TestPlatformPciDevice::GetInstance();
ASSERT_NE(platform_device, nullptr);
std::unique_ptr<MsdIntelDevice> device(
MsdIntelDevice::Create(platform_device->GetDeviceHandle(), true));
ASSERT_NE(device, nullptr);
auto connection =
std::shared_ptr<MsdIntelConnection>(MsdIntelConnection::Create(device.get(), 1));
ASSERT_NE(connection, nullptr);
auto address_space = kUseGlobalGtt ? device->gtt() : connection->per_process_gtt();
auto context = std::make_shared<ClientContext>(connection, address_space);
ASSERT_NE(context, nullptr);
ContextRelease context_release(context);
// Semaphore for signalling command buffer completion
auto semaphore =
std::shared_ptr<magma::PlatformSemaphore>(magma::PlatformSemaphore::Create());
// Create batch buffer
auto batch_buffer =
std::shared_ptr<MsdIntelBuffer>(MsdIntelBuffer::Create(PAGE_SIZE, "batch"));
auto batch_mapping = AddressSpace::GetSharedGpuMapping(
address_space, batch_buffer, 0, batch_buffer->platform_buffer()->size());
ASSERT_NE(batch_mapping, nullptr);
// Send a no-op batch to get the context initialized.
{
void* batch_cpu_addr;
ASSERT_TRUE(batch_mapping->buffer()->platform_buffer()->MapCpu(&batch_cpu_addr));
auto batch_ptr = reinterpret_cast<uint32_t*>(batch_cpu_addr);
*batch_ptr++ = 0;
*batch_ptr++ = 0;
*batch_ptr++ = 0;
*batch_ptr++ = 0;
*batch_ptr++ = (0xA << 23); // batch end
}
std::unique_ptr<CommandBuffer> command_buffer;
// Create a command buffer
{
auto buffer = MsdIntelBuffer::Create(PAGE_SIZE, "cmd buf");
void* vaddr;
ASSERT_TRUE(buffer->platform_buffer()->MapCpu(&vaddr));
auto cmd_buf = static_cast<magma_system_command_buffer*>(vaddr);
cmd_buf->batch_buffer_resource_index = 0;
cmd_buf->batch_start_offset = 0;
cmd_buf->num_resources = 1;
cmd_buf->wait_semaphore_count = 0;
cmd_buf->signal_semaphore_count = 1;
auto semaphores = reinterpret_cast<uint64_t*>(cmd_buf + 1);
semaphores[0] = semaphore->id();
// Batch buffer
auto resources = reinterpret_cast<magma_system_exec_resource*>(semaphores + 1);
resources[0].buffer_id = batch_buffer->platform_buffer()->id();
resources[0].num_relocations = 0;
resources[0].offset = 0;
resources[0].length = batch_buffer->platform_buffer()->size();
command_buffer = TestCommandBuffer::Create(std::move(buffer), context, {batch_buffer},
{}, {semaphore});
ASSERT_NE(nullptr, command_buffer);
}
EXPECT_TRUE(command_buffer->PrepareForExecution());
EXPECT_TRUE(context->SubmitCommandBuffer(std::move(command_buffer)));
EXPECT_TRUE(semaphore->Wait(1000));
// Create two destination buffers, but only one mapping because we want to reuse the same
// gpu address.
std::vector<std::shared_ptr<MsdIntelBuffer>> dst_buffer(2);
dst_buffer[0] = std::shared_ptr<MsdIntelBuffer>(MsdIntelBuffer::Create(PAGE_SIZE, "dst0"));
dst_buffer[1] = std::shared_ptr<MsdIntelBuffer>(MsdIntelBuffer::Create(PAGE_SIZE, "dst1"));
// Initialize the target
std::vector<void*> dst_cpu_addr(2);
ASSERT_TRUE(dst_buffer[0]->platform_buffer()->MapCpu(&dst_cpu_addr[0]));
ASSERT_TRUE(dst_buffer[1]->platform_buffer()->MapCpu(&dst_cpu_addr[1]));
// Map the first buffer
std::vector<std::shared_ptr<GpuMapping>> dst_mapping(2);
dst_mapping[0] = AddressSpace::GetSharedGpuMapping(
address_space, dst_buffer[0], 0, dst_buffer[0]->platform_buffer()->size());
ASSERT_NE(dst_mapping[0], nullptr);
// Initialize the batch buffer
constexpr uint32_t kExpectedVal = 12345678;
{
void* batch_cpu_addr;
ASSERT_TRUE(batch_mapping->buffer()->platform_buffer()->MapCpu(&batch_cpu_addr));
static constexpr uint32_t kDwordCount = 4;
auto batch_ptr = reinterpret_cast<uint32_t*>(batch_cpu_addr);
*batch_ptr++ =
(0x20 << 23) | (kDwordCount - 2) | (kUseGlobalGtt ? 1 << 22 : 0); // store dword
*batch_ptr++ = magma::lower_32_bits(dst_mapping[0]->gpu_addr());
*batch_ptr++ = magma::upper_32_bits(dst_mapping[0]->gpu_addr());
*batch_ptr++ = kExpectedVal;
*batch_ptr++ = (0xA << 23); // batch end
}
constexpr uint32_t kInitVal = 0xdeadbeef;
reinterpret_cast<uint32_t*>(dst_cpu_addr[0])[0] = kInitVal;
reinterpret_cast<uint32_t*>(dst_cpu_addr[1])[0] = kInitVal;
// Create a command buffer writing to buffer 0
{
auto buffer = MsdIntelBuffer::Create(PAGE_SIZE, "cmd buf");
void* vaddr;
ASSERT_TRUE(buffer->platform_buffer()->MapCpu(&vaddr));
auto cmd_buf = static_cast<magma_system_command_buffer*>(vaddr);
cmd_buf->batch_buffer_resource_index = 0;
cmd_buf->batch_start_offset = 0;
cmd_buf->num_resources = 2;
cmd_buf->wait_semaphore_count = 0;
cmd_buf->signal_semaphore_count = 1;
auto semaphores = reinterpret_cast<uint64_t*>(cmd_buf + 1);
semaphores[0] = semaphore->id();
// Batch buffer
auto resources = reinterpret_cast<magma_system_exec_resource*>(semaphores + 1);
resources[0].buffer_id = batch_buffer->platform_buffer()->id();
resources[0].num_relocations = 0;
resources[0].offset = 0;
resources[0].length = batch_buffer->platform_buffer()->size();
// Destination buffer
resources[1].buffer_id = dst_buffer[0]->platform_buffer()->id();
resources[1].num_relocations = 0;
resources[1].offset = 0;
resources[1].length = dst_buffer[0]->platform_buffer()->size();
command_buffer = TestCommandBuffer::Create(
std::move(buffer), context, {batch_buffer, dst_buffer[0]}, {}, {semaphore});
ASSERT_NE(nullptr, command_buffer);
}
EXPECT_EQ(2u, dst_mapping[0].use_count());
EXPECT_TRUE(command_buffer->PrepareForExecution());
EXPECT_EQ(3u, dst_mapping[0].use_count());
EXPECT_TRUE(context->SubmitCommandBuffer(std::move(command_buffer)));
EXPECT_TRUE(semaphore->Wait(1000));
EXPECT_EQ(2u, dst_mapping[0].use_count());
EXPECT_EQ(kExpectedVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[0])[0]);
EXPECT_EQ(kInitVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[1])[0]);
// Release the first buffer, map the second
{
gpu_addr_t gpu_addr = dst_mapping[0]->gpu_addr();
dst_mapping[0].reset();
if (kCheckMappingRelease) {
connection->ReleaseBuffer(dst_buffer[0]->platform_buffer());
const std::vector<std::shared_ptr<GpuMapping>>& mappings_to_release =
connection->mappings_to_release();
ASSERT_EQ(1u, mappings_to_release.size());
EXPECT_EQ(1u, mappings_to_release[0].use_count());
dst_mapping[0] = mappings_to_release[0];
EXPECT_EQ(2u, dst_mapping[0].use_count());
} else {
std::vector<std::shared_ptr<GpuMapping>> mappings;
address_space->ReleaseBuffer(dst_buffer[0]->platform_buffer(), &mappings);
mappings.clear();
}
dst_mapping[1] = AddressSpace::GetSharedGpuMapping(
address_space, dst_buffer[1], 0, dst_buffer[1]->platform_buffer()->size());
ASSERT_NE(dst_mapping[1], nullptr);
if (kReuseGpuAddr) {
ASSERT_EQ(gpu_addr, dst_mapping[1]->gpu_addr());
}
}
reinterpret_cast<uint32_t*>(dst_cpu_addr[0])[0] = kInitVal;
reinterpret_cast<uint32_t*>(dst_cpu_addr[1])[0] = kInitVal;
// Create a command buffer writing to buffer 1
{
auto buffer = MsdIntelBuffer::Create(PAGE_SIZE, "cmd buf");
void* vaddr;
ASSERT_TRUE(buffer->platform_buffer()->MapCpu(&vaddr));
auto cmd_buf = static_cast<magma_system_command_buffer*>(vaddr);
cmd_buf->batch_buffer_resource_index = 0;
cmd_buf->batch_start_offset = 0;
cmd_buf->num_resources = 2;
cmd_buf->wait_semaphore_count = 0;
cmd_buf->signal_semaphore_count = 1;
auto semaphores = reinterpret_cast<uint64_t*>(cmd_buf + 1);
semaphores[0] = semaphore->id();
// Batch buffer
auto resources = reinterpret_cast<magma_system_exec_resource*>(semaphores + 1);
resources[0].buffer_id = batch_buffer->platform_buffer()->id();
resources[0].num_relocations = 0;
resources[0].offset = 0;
resources[0].length = batch_buffer->platform_buffer()->size();
// Destination buffer
resources[1].buffer_id = dst_buffer[1]->platform_buffer()->id();
resources[1].num_relocations = 0;
resources[1].offset = 0;
resources[1].length = dst_buffer[1]->platform_buffer()->size();
command_buffer = TestCommandBuffer::Create(
std::move(buffer), context, {batch_buffer, dst_buffer[1]}, {}, {semaphore});
ASSERT_NE(nullptr, command_buffer);
}
EXPECT_TRUE(command_buffer->PrepareForExecution());
EXPECT_TRUE(context->SubmitCommandBuffer(std::move(command_buffer)));
EXPECT_TRUE(semaphore->Wait(1000));
if (kReuseGpuAddr) {
EXPECT_EQ(kInitVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[0])[0]);
EXPECT_EQ(kExpectedVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[1])[0]);
} else {
EXPECT_EQ(kExpectedVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[0])[0]);
EXPECT_EQ(kInitVal, reinterpret_cast<uint32_t*>(dst_cpu_addr[1])[0]);
}
if (kCheckMappingRelease) {
EXPECT_EQ(1u, dst_mapping[0].use_count());
}
}
};
TEST(Exec, GlobalGttReuseGpuAddress) { TestExec().GlobalGttReuseGpuAddress(); }
TEST(Exec, PerProcessGttReuseGpuAddress) { TestExec().PerProcessGttReuseGpuAddress(); }
TEST(Exec, PerProcessGttCheckMappingRelease) { TestExec().PerProcessGttCheckMappingRelease(); }