blob: 611d41e9c822ffeac377e3846813b9f5abc6f09e [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 "command_buffer.h"
#include <lib/magma/platform/platform_trace.h>
#include "address_space.h"
#include "instructions.h"
#include "magma_intel_gen_defs.h"
#include "msd_intel_connection.h"
#include "msd_intel_context.h"
#include "msd_intel_semaphore.h"
std::unique_ptr<CommandBuffer> CommandBuffer::Create(std::weak_ptr<MsdIntelContext> context,
msd::magma_command_buffer* cmd_buf,
magma_exec_resource* exec_resources,
msd::Buffer** msd_buffers,
msd::Semaphore** msd_wait_semaphores,
msd::Semaphore** msd_signal_semaphores) {
if (cmd_buf->resource_count == 0)
return DRETP(nullptr, "Command buffer requires at least 1 resource");
switch (cmd_buf->flags) {
case 0:
case kMagmaIntelGenCommandBufferForRender:
case kMagmaIntelGenCommandBufferForVideo:
break;
default:
return DRETP(nullptr, "Invalid flags: 0x%lx", cmd_buf->flags);
}
std::vector<ExecResource> resources;
resources.reserve(cmd_buf->resource_count);
for (uint32_t i = 0; i < cmd_buf->resource_count; i++) {
resources.emplace_back(ExecResource{static_cast<MsdIntelAbiBuffer*>(msd_buffers[i])->ptr(),
exec_resources[i].offset, exec_resources[i].length});
}
std::vector<std::shared_ptr<magma::PlatformSemaphore>> wait_semaphores;
wait_semaphores.reserve(cmd_buf->wait_semaphore_count);
for (uint32_t i = 0; i < cmd_buf->wait_semaphore_count; i++) {
wait_semaphores.emplace_back(static_cast<MsdIntelAbiSemaphore*>(msd_wait_semaphores[i])->ptr());
}
std::vector<std::shared_ptr<magma::PlatformSemaphore>> signal_semaphores;
signal_semaphores.reserve(cmd_buf->signal_semaphore_count);
for (uint32_t i = 0; i < cmd_buf->signal_semaphore_count; i++) {
signal_semaphores.emplace_back(
static_cast<MsdIntelAbiSemaphore*>(msd_signal_semaphores[i])->ptr());
}
auto command_buffer = std::unique_ptr<CommandBuffer>(
new CommandBuffer(context, std::make_unique<msd::magma_command_buffer>(*cmd_buf)));
if (!command_buffer->InitializeResources(std::move(resources), std::move(wait_semaphores),
std::move(signal_semaphores)))
return DRETP(nullptr, "failed to initialize command buffer resources");
return command_buffer;
}
CommandBuffer::CommandBuffer(std::weak_ptr<MsdIntelContext> context,
std::unique_ptr<msd::magma_command_buffer> cmd_buf)
: MappedBatch(COMMAND_BUFFER),
context_(context),
command_buffer_(std::move(cmd_buf)),
nonce_(TRACE_NONCE()) {
switch (command_buffer_->flags) {
case 0:
case kMagmaIntelGenCommandBufferForRender:
MappedBatch::set_command_streamer(RENDER_COMMAND_STREAMER);
break;
case kMagmaIntelGenCommandBufferForVideo:
MappedBatch::set_command_streamer(VIDEO_COMMAND_STREAMER);
break;
default:
DASSERT(false);
}
}
CommandBuffer::~CommandBuffer() {
if (!prepared_to_execute_)
return;
std::shared_ptr<MsdIntelConnection> connection = locked_context_->connection().lock();
uint64_t ATTRIBUTE_UNUSED connection_id = connection ? connection->client_id() : 0;
uint64_t ATTRIBUTE_UNUSED current_ticks = magma::PlatformTrace::GetCurrentTicks();
uint64_t ATTRIBUTE_UNUSED buffer_id = GetBatchBufferId();
TRACE_DURATION("magma", "Command Buffer End");
TRACE_VTHREAD_FLOW_STEP("magma", "command_buffer", "GPU", connection_id, buffer_id,
current_ticks);
TRACE_FLOW_END("magma", "command_buffer", buffer_id);
UnmapResourcesGpu();
for (auto& semaphore : signal_semaphores_) {
semaphore->Signal();
}
if (connection) {
std::vector<uint64_t> buffer_ids(num_resources());
for (uint32_t i = 0; i < num_resources(); i++) {
buffer_ids[i] = exec_resources_[i].buffer->platform_buffer()->id();
}
connection->SendNotification(buffer_ids);
}
TRACE_ASYNC_END("magma-exec", "CommandBuffer Exec", nonce_);
}
void CommandBuffer::SetSequenceNumber(uint32_t sequence_number) {
TRACE_ASYNC_BEGIN("magma-exec", "CommandBuffer Exec", nonce_, "id", GetBatchBufferId());
sequence_number_ = sequence_number;
}
bool CommandBuffer::InitializeResources(
std::vector<ExecResource> resources,
std::vector<std::shared_ptr<magma::PlatformSemaphore>> wait_semaphores,
std::vector<std::shared_ptr<magma::PlatformSemaphore>> signal_semaphores) {
TRACE_DURATION("magma", "InitializeResources");
if (num_resources() != resources.size())
return DRETF(false, "resources size mismatch");
if (wait_semaphores.size() != wait_semaphore_count())
return DRETF(false, "wait semaphore count mismatch");
if (signal_semaphores.size() != signal_semaphore_count())
return DRETF(false, "wait semaphore count mismatch");
exec_resources_ = std::move(resources);
wait_semaphores_ = std::move(wait_semaphores);
signal_semaphores_ = std::move(signal_semaphores);
return true;
}
std::weak_ptr<MsdIntelContext> CommandBuffer::GetContext() { return context_; }
uint32_t CommandBuffer::GetPipeControlFlags() {
uint32_t flags = MiPipeControl::kCommandStreamerStallEnableBit;
// Experimentally including this bit has been shown to resolve gpu faults where a batch
// completes; we clear gtt mappings for resources; then on the next batch,
// an invalid address is emitted corresponding to a cleared gpu mapping. This was
// first seen when a compute shader was introduced.
flags |= MiPipeControl::kGenericMediaStateClearBit;
// Similarly, including this bit was shown to resolve the emission of an invalid address.
flags |= MiPipeControl::kIndirectStatePointersDisableBit;
// This one is needed when l3 caching enabled via mocs (memory object control state).
flags |= MiPipeControl::kDcFlushEnableBit;
return flags;
}
bool CommandBuffer::GetGpuAddress(gpu_addr_t* gpu_addr_out) {
if (!prepared_to_execute_)
return DRETF(false, "not prepared to execute");
*gpu_addr_out = exec_resource_mappings_[batch_buffer_index_]->gpu_addr() + batch_start_offset_;
return true;
}
uint64_t CommandBuffer::GetBatchBufferId() {
if (num_resources() == 0)
return 0;
DASSERT(batch_buffer_resource_index() < exec_resources_.size());
return exec_resources_[batch_buffer_resource_index()].buffer->platform_buffer()->id();
}
void CommandBuffer::UnmapResourcesGpu() { exec_resource_mappings_.clear(); }
bool CommandBuffer::PrepareForExecution() {
locked_context_ = context_.lock();
if (!locked_context_)
return DRETF(false, "context has already been deleted, aborting");
exec_resource_mappings_.clear();
exec_resource_mappings_.reserve(exec_resources_.size());
TRACE_FLOW_STEP("magma", "command_buffer", GetBatchBufferId());
if (!MapResourcesGpu(locked_context_->exec_address_space(), exec_resource_mappings_))
return DRETF(false, "failed to map execution resources");
batch_buffer_index_ = batch_buffer_resource_index();
batch_start_offset_ = batch_start_offset();
prepared_to_execute_ = true;
return true;
}
bool CommandBuffer::MapResourcesGpu(std::shared_ptr<AddressSpace> address_space,
std::vector<std::shared_ptr<GpuMapping>>& mappings) {
TRACE_DURATION("magma", "MapResourcesGpu");
for (auto res : exec_resources_) {
std::shared_ptr<GpuMapping> mapping =
address_space->FindGpuMapping(res.buffer->platform_buffer(), res.offset, res.length);
if (!mapping)
return DRETF(false, "failed to find gpu mapping for buffer %lu",
res.buffer->platform_buffer()->id());
DLOG("MapResourcesGpu aspace %p buffer 0x%" PRIx64 " offset 0x%" PRIx64 " length 0x%" PRIx64
" gpu_addr 0x%" PRIx64,
address_space.get(), res.buffer->platform_buffer()->id(), res.offset, res.length,
mapping->gpu_addr());
mappings.push_back(mapping);
}
return true;
}