blob: cdb459733f1ef6ccadea9aba1552e4490b90d2c3 [file] [log] [blame]
// Copyright 2018 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 "anv_magma.h"
#include "gen_gem.h"
#include "magma_sysmem.h"
#include "magma_util/inflight_list.h"
#include "magma_util/macros.h"
#include <chrono>
#include <map>
#include <vector>
class Buffer : public anv_magma_buffer {
public:
Buffer(magma_buffer_t buffer) { anv_magma_buffer::buffer = buffer; }
~Buffer() { DASSERT(!get()); }
magma_buffer_t get() { return anv_magma_buffer::buffer; }
void release(magma_connection_t connection)
{
magma_release_buffer(connection, get());
anv_magma_buffer::buffer = 0;
}
void AddMapping(uint64_t page_offset, uint64_t page_count, uint64_t addr)
{
mappings_[addr] = {page_offset, page_count};
}
struct Segment {
uint64_t page_offset = 0;
uint64_t page_count = 0;
};
bool HasMapping(uint64_t addr, Segment* segment_out) const
{
auto iter = mappings_.find(addr);
if (iter != mappings_.end()) {
if (segment_out) {
*segment_out = iter->second;
}
return true;
}
return false;
}
void RemoveMapping(uint64_t addr) { mappings_.erase(addr); }
private:
std::map<uint64_t, Segment> mappings_;
};
class Connection : public anv_connection {
public:
Connection(magma_connection_t magma_connection)
{
anv_connection::connection = magma_connection;
}
~Connection()
{
#if VK_USE_PLATFORM_FUCHSIA
if (sysmem_connection_) {
magma_sysmem_connection_release(sysmem_connection_);
}
#endif // VK_USE_PLATFORM_FUCHSIA
magma_release_connection(magma_connection());
}
magma_connection_t magma_connection() { return anv_connection::connection; }
magma::InflightList* inflight_list() { return &inflight_list_; }
#if VK_USE_PLATFORM_FUCHSIA
magma_status_t GetSysmemConnection(magma_sysmem_connection_t* sysmem_connection_out)
{
if (!sysmem_connection_) {
uint32_t client_handle;
VkResult result =
anv_magma_connect_to_service("/svc/fuchsia.sysmem.Allocator", &client_handle);
if (result != VK_SUCCESS)
return DRET(MAGMA_STATUS_INTERNAL_ERROR);
magma_status_t status = magma_sysmem_connection_import(client_handle, &sysmem_connection_);
if (status != MAGMA_STATUS_OK)
return DRET(status);
}
*sysmem_connection_out = sysmem_connection_;
return MAGMA_STATUS_OK;
}
#endif // VK_USE_PLATFORM_FUCHSIA
static Connection* cast(anv_connection* connection)
{
return static_cast<Connection*>(connection);
}
private:
#if VK_USE_PLATFORM_FUCHSIA
magma_sysmem_connection_t sysmem_connection_{};
#endif // #if VK_USE_PLATFORM_FUCHSIA
magma::InflightList inflight_list_;
};
anv_connection* AnvMagmaCreateConnection(magma_connection_t connection)
{
return new Connection(connection);
}
void AnvMagmaReleaseConnection(anv_connection* connection)
{
delete static_cast<Connection*>(connection);
}
#if VK_USE_PLATFORM_FUCHSIA
magma_status_t AnvMagmaGetSysmemConnection(struct anv_connection* connection,
magma_sysmem_connection_t* sysmem_connection_out)
{
return Connection::cast(connection)->GetSysmemConnection(sysmem_connection_out);
}
#endif // VK_USE_PLATFORM_FUCHSIA
magma_status_t AnvMagmaConnectionWait(anv_connection* connection, uint64_t buffer_id,
int64_t* timeout_ns)
{
magma::InflightList* inflight_list = Connection::cast(connection)->inflight_list();
auto start = std::chrono::high_resolution_clock::now();
while (inflight_list->is_inflight(buffer_id) &&
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now() - start)
.count() < *timeout_ns) {
magma_connection_t magma_connection = Connection::cast(connection)->magma_connection();
magma::Status status = inflight_list->WaitForCompletion(magma_connection, *timeout_ns);
if (status.ok()) {
inflight_list->ServiceCompletions(magma_connection);
} else {
return status.get();
}
}
return MAGMA_STATUS_OK;
}
int AnvMagmaConnectionIsBusy(anv_connection* connection, uint64_t buffer_id)
{
magma::InflightList* inflight_list = Connection::cast(connection)->inflight_list();
inflight_list->ServiceCompletions(Connection::cast(connection)->magma_connection());
return inflight_list->is_inflight(buffer_id) ? 1 : 0;
}
int AnvMagmaConnectionExec(anv_connection* connection, uint32_t context_id,
struct drm_i915_gem_execbuffer2* execbuf)
{
if (execbuf->buffer_count == 0)
return 0;
auto exec_objects = reinterpret_cast<drm_i915_gem_exec_object2*>(execbuf->buffers_ptr);
std::vector<magma_system_exec_resource> resources;
resources.reserve(execbuf->buffer_count);
for (uint32_t i = 0; i < execbuf->buffer_count; i++) {
auto buffer = reinterpret_cast<Buffer*>(exec_objects[i].handle);
uint64_t buffer_id = magma_get_buffer_id(buffer->get());
uint64_t offset = exec_objects[i].rsvd1;
uint64_t length = exec_objects[i].rsvd2;
resources.push_back({ .buffer_id = buffer_id,
.offset = offset,
.length = length,
});
if (!magma::is_page_aligned(offset))
return DRET_MSG(-1, "offset (0x%lx) not page aligned", offset);
uint64_t gpu_addr = gen_48b_address(exec_objects[i].offset);
uint64_t page_offset = offset / magma::page_size();
uint64_t page_count =
magma::round_up(length, magma::page_size()) / magma::page_size();
Buffer::Segment segment;
bool has_mapping = buffer->HasMapping(gpu_addr, &segment);
if (has_mapping) {
assert(page_offset == segment.page_offset);
if (page_count > segment.page_count) {
// Growing an existing mapping.
buffer->RemoveMapping(gpu_addr);
has_mapping = false;
} else {
assert(page_count == segment.page_count);
}
}
if (!has_mapping) {
DLOG("mapping to gpu addr 0x%lx: id %lu page_offset %lu page_count %lu", gpu_addr,
buffer_id, page_offset, page_count);
magma_map_buffer_gpu(Connection::cast(connection)->magma_connection(), buffer->get(),
page_offset, page_count, gpu_addr, 0);
buffer->AddMapping(page_offset, page_count, gpu_addr);
}
}
uint32_t syncobj_count = execbuf->num_cliprects;
std::vector<uint64_t> semaphore_ids;
semaphore_ids.reserve(syncobj_count);
uint64_t wait_semaphore_count = 0;
// Wait semaphores first, then signal
for (uint32_t i = 0; i < syncobj_count; i++) {
auto& syncobj = reinterpret_cast<drm_i915_gem_exec_fence*>(execbuf->cliprects_ptr)[i];
if (syncobj.flags & I915_EXEC_FENCE_WAIT) {
semaphore_ids.push_back(magma_get_semaphore_id(syncobj.handle));
wait_semaphore_count++;
}
}
for (uint32_t i = 0; i < syncobj_count; i++) {
auto& syncobj = reinterpret_cast<drm_i915_gem_exec_fence*>(execbuf->cliprects_ptr)[i];
if (syncobj.flags & I915_EXEC_FENCE_SIGNAL) {
semaphore_ids.push_back(magma_get_semaphore_id(syncobj.handle));
}
}
magma_system_command_buffer command_buffer = {
.batch_buffer_resource_index = execbuf->buffer_count - 1, // by drm convention
.batch_start_offset = execbuf->batch_start_offset,
.num_resources = execbuf->buffer_count,
.wait_semaphore_count = wait_semaphore_count,
.signal_semaphore_count = semaphore_ids.size() - wait_semaphore_count
};
magma_execute_command_buffer_with_resources(Connection::cast(connection)->magma_connection(),
context_id,
&command_buffer,
resources.data(),
semaphore_ids.data());
magma::InflightList* inflight_list = Connection::cast(connection)->inflight_list();
for (uint32_t i = 0; i < execbuf->buffer_count; i++) {
inflight_list->add(resources[i].buffer_id);
}
inflight_list->ServiceCompletions(Connection::cast(connection)->magma_connection());
return 0;
}
anv_magma_buffer* AnvMagmaCreateBuffer(anv_connection* connection, magma_buffer_t buffer)
{
return new Buffer(buffer);
}
void AnvMagmaReleaseBuffer(anv_connection* connection, anv_magma_buffer* anv_buffer)
{
auto buffer = static_cast<Buffer*>(anv_buffer);
// Hardware mappings are released when the buffer is released.
buffer->release(Connection::cast(connection)->magma_connection());
delete buffer;
}