blob: 3ced4f152a85ff20684aceb92debaa033038f5f2 [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 "msd_intel_context.h"
#include "address_space.h"
#include "command_buffer.h"
#include "msd_intel_connection.h"
#include "platform_thread.h"
#include "platform_trace.h"
void MsdIntelContext::SetEngineState(EngineCommandStreamerId id,
std::unique_ptr<MsdIntelBuffer> context_buffer,
std::unique_ptr<Ringbuffer> ringbuffer)
{
DASSERT(context_buffer);
DASSERT(ringbuffer);
auto iter = state_map_.find(id);
DASSERT(iter == state_map_.end());
state_map_[id] = PerEngineState{std::move(context_buffer), nullptr, std::move(ringbuffer)};
}
bool MsdIntelContext::Map(std::shared_ptr<AddressSpace> address_space, EngineCommandStreamerId id)
{
auto iter = state_map_.find(id);
if (iter == state_map_.end())
return DRETF(false, "couldn't find engine command streamer");
DLOG("Mapping context for engine %d", id);
PerEngineState& state = iter->second;
if (state.context_mapping) {
if (state.context_mapping->address_space().lock() == address_space)
return true;
return DRETF(false, "already mapped to a different address space");
}
state.context_mapping = AddressSpace::MapBufferGpu(address_space, state.context_buffer);
if (!state.context_mapping)
return DRETF(false, "context map failed");
if (!state.ringbuffer->Map(address_space)) {
state.context_mapping.reset();
return DRETF(false, "ringbuffer map failed");
}
return true;
}
bool MsdIntelContext::Unmap(EngineCommandStreamerId id)
{
auto iter = state_map_.find(id);
if (iter == state_map_.end())
return DRETF(false, "couldn't find engine command streamer");
DLOG("Unmapping context for engine %d", id);
PerEngineState& state = iter->second;
if (!state.context_mapping)
return DRETF(false, "context not mapped");
state.context_mapping.reset();
if (!state.ringbuffer->Unmap())
return DRETF(false, "ringbuffer unmap failed");
return true;
}
bool MsdIntelContext::GetGpuAddress(EngineCommandStreamerId id, gpu_addr_t* addr_out)
{
auto iter = state_map_.find(id);
if (iter == state_map_.end())
return DRETF(false, "couldn't find engine command streamer");
PerEngineState& state = iter->second;
if (!state.context_mapping)
return DRETF(false, "context not mapped");
*addr_out = state.context_mapping->gpu_addr();
return true;
}
bool MsdIntelContext::GetRingbufferGpuAddress(EngineCommandStreamerId id, gpu_addr_t* addr_out)
{
auto iter = state_map_.find(id);
if (iter == state_map_.end())
return DRETF(false, "couldn't find engine command streamer");
PerEngineState& state = iter->second;
if (!state.context_mapping)
return DRETF(false, "context not mapped");
if (!state.ringbuffer->GetGpuAddress(addr_out))
return DRETF(false, "failed to get gpu address");
return true;
}
ClientContext::~ClientContext() { DASSERT(!wait_thread_.joinable()); }
void ClientContext::Shutdown()
{
if (semaphore_port_)
semaphore_port_->Close();
if (wait_thread_.joinable()) {
DLOG("joining wait thread");
wait_thread_.join();
DLOG("joined wait thread");
}
semaphore_port_.reset();
}
magma::Status ClientContext::SubmitCommandBuffer(std::unique_ptr<CommandBuffer> command_buffer)
{
TRACE_DURATION("magma", "ReceiveCommandBuffer");
uint64_t ATTRIBUTE_UNUSED buffer_id = command_buffer->GetBatchBufferId();
TRACE_FLOW_STEP("magma", "command_buffer", buffer_id);
{
std::shared_ptr<MsdIntelContext> context = command_buffer->GetContext().lock();
DASSERT(context.get() == static_cast<MsdIntelContext*>(this));
std::shared_ptr<MsdIntelConnection> connection = context->connection().lock();
if (!connection)
return DRET(MAGMA_STATUS_CONNECTION_LOST);
// If there are any mappings pending release, submit them now.
connection->SubmitPendingReleaseMappings(context);
}
if (killed())
return DRET(MAGMA_STATUS_CONTEXT_KILLED);
if (!semaphore_port_) {
semaphore_port_ = magma::SemaphorePort::Create();
DASSERT(!wait_thread_.joinable());
wait_thread_ = std::thread([this] {
magma::PlatformThreadHelper::SetCurrentThreadName("ConnectionWaitThread");
DLOG("context wait thread started");
while (semaphore_port_->WaitOne()) {
}
DLOG("context wait thread exited");
});
}
std::unique_lock<std::mutex> lock(pending_command_buffer_mutex_);
pending_command_buffer_queue_.push(std::move(command_buffer));
if (pending_command_buffer_queue_.size() == 1)
return SubmitPendingCommandBuffer(true);
return MAGMA_STATUS_OK;
}
magma::Status ClientContext::SubmitPendingCommandBuffer(bool have_lock)
{
auto callback = [this](magma::SemaphorePort::WaitSet* wait_set) {
this->SubmitPendingCommandBuffer(false);
};
auto lock = have_lock
? std::unique_lock<std::mutex>(pending_command_buffer_mutex_, std::adopt_lock)
: std::unique_lock<std::mutex>(pending_command_buffer_mutex_);
while (pending_command_buffer_queue_.size()) {
DLOG("pending_command_buffer_queue_ size %zu", pending_command_buffer_queue_.size());
std::unique_ptr<CommandBuffer>& command_buffer = pending_command_buffer_queue_.front();
// Takes ownership
auto semaphores = command_buffer->wait_semaphores();
if (semaphores.size() == 0) {
auto connection = connection_.lock();
if (!connection)
return DRET_MSG(MAGMA_STATUS_CONNECTION_LOST,
"couldn't lock reference to connection");
if (killed())
return DRET(MAGMA_STATUS_CONTEXT_KILLED);
{
TRACE_DURATION("magma", "SubmitCommandBuffer");
uint64_t ATTRIBUTE_UNUSED buffer_id = command_buffer->GetBatchBufferId();
TRACE_FLOW_STEP("magma", "command_buffer", buffer_id);
}
connection->SubmitBatch(std::move(command_buffer));
pending_command_buffer_queue_.pop();
} else {
DLOG("adding waitset with %zu semaphores", semaphores.size());
// Invoke the callback when semaphores are satisfied;
// the next ProcessPendingFlip will see an empty semaphore array for the front request.
bool result = semaphore_port_->AddWaitSet(
std::make_unique<magma::SemaphorePort::WaitSet>(callback, std::move(semaphores)));
if (result) {
break;
} else {
magma::log(magma::LOG_WARNING,
"SubmitPendingCommandBuffer: failed to add to waitset");
}
}
}
return MAGMA_STATUS_OK;
}
void ClientContext::Kill()
{
if (killed_)
return;
killed_ = true;
auto connection = connection_.lock();
if (connection)
connection->SendContextKilled();
}
//////////////////////////////////////////////////////////////////////////////
void msd_context_destroy(msd_context_t* ctx)
{
auto abi_context = MsdIntelAbiContext::cast(ctx);
// get a copy of the shared ptr
auto client_context = abi_context->ptr();
client_context->Shutdown();
// delete the abi container
delete abi_context;
// can safely unmap contexts only from the device thread; for that we go through the connection
auto connection = client_context->connection().lock();
DASSERT(connection);
connection->DestroyContext(std::move(client_context));
}
magma_status_t msd_context_execute_command_buffer(msd_context_t* ctx, msd_buffer_t* cmd_buf,
msd_buffer_t** exec_resources,
msd_semaphore_t** wait_semaphores,
msd_semaphore_t** signal_semaphores)
{
auto context = MsdIntelAbiContext::cast(ctx)->ptr();
auto command_buffer =
CommandBuffer::Create(cmd_buf, exec_resources, context, wait_semaphores, signal_semaphores);
TRACE_DURATION_BEGIN("magma", "PrepareForExecution", "id", command_buffer->GetBatchBufferId());
if (!command_buffer->PrepareForExecution())
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR,
"Failed to prepare command buffer for execution");
TRACE_DURATION_END("magma", "PrepareForExecution");
magma::Status status = context->SubmitCommandBuffer(std::move(command_buffer));
return status.get();
}
magma_status_t msd_context_execute_immediate_commands(msd_context_t* ctx, uint64_t commands_size,
void* commands, uint64_t semaphore_count,
msd_semaphore_t** msd_semaphores)
{
return MAGMA_STATUS_CONTEXT_KILLED;
}