blob: 4bc15508e50cafc51fc860faf80f30d3629651a4 [file] [log] [blame]
// Copyright 2020 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.
#ifndef COMMAND_BUFFER_H
#define COMMAND_BUFFER_H
#include <lib/magma_service/util/accessor.h>
#include <lib/magma_service/util/command_buffer.h>
#include "gpu_mapping.h"
#include "instructions.h"
#include "msd_vsi_context.h"
class CommandBuffer : public magma::CommandBuffer<MsdVsiContext, GpuMapping> {
public:
// The client is required to provide a buffer with at least 8 additional bytes available
// and mapped, which the driver will write a LINK instruction in.
static constexpr uint32_t kAdditionalBytes = kInstructionDwords * sizeof(uint32_t);
// Only up to 2 resources are supported, the batch buffer and optional context state buffer.
static constexpr uint32_t kMaxAllowedResources = 2;
static std::unique_ptr<CommandBuffer> Create(
std::shared_ptr<MsdVsiContext> context, msd::msd_client_id_t client_id,
std::unique_ptr<msd::magma_command_buffer> cmd_buf, std::vector<ExecResource> resources,
std::vector<std::shared_ptr<magma::PlatformSemaphore>> signal_semaphores) {
if (cmd_buf->resource_count > kMaxAllowedResources) {
MAGMA_LOG(ERROR, "Invalid resource count, only 1 additional context state is supported");
return nullptr;
}
std::optional<uint32_t> context_state_buffer_resource_index;
if (cmd_buf->resource_count == 2) {
context_state_buffer_resource_index = cmd_buf->batch_buffer_resource_index == 0 ? 1 : 0;
}
auto command_buffer = std::make_unique<CommandBuffer>(context, client_id, std::move(cmd_buf),
context_state_buffer_resource_index);
if (!command_buffer->InitializeResources(std::move(resources), {} /* wait_semaphores */,
std::move(signal_semaphores))) {
MAGMA_LOG(ERROR, "Failed to initialize resources");
return nullptr;
}
return command_buffer;
}
CommandBuffer(std::weak_ptr<MsdVsiContext> context, uint64_t connection_id,
std::unique_ptr<msd::magma_command_buffer> command_buffer,
std::optional<uint32_t> csb_resource_index = std::nullopt)
: magma::CommandBuffer<MsdVsiContext, GpuMapping>(context, connection_id,
std::move(command_buffer)),
csb_index_(csb_resource_index) {}
// Returns a pointer to the batch buffer.
magma::PlatformBuffer* GetBatchBuffer() {
if (batch_buffer_index() < exec_resources_.size()) {
return exec_resources_[batch_buffer_index()].buffer->platform_buffer();
}
DASSERT(false);
return nullptr;
}
// Returns the offset into the batch buffer that points to the end of the user data.
uint32_t GetBatchBufferWriteOffset() {
uint64_t length = magma::round_up(GetLength(), sizeof(uint64_t));
return magma::to_uint32(batch_start_offset() + length);
}
// Returns a pointer to the resource for the context state buffer.
// May be null if no context state buffer is present.
const ExecResource* GetContextStateBufferResource() const {
if (!csb_index_.has_value()) {
return nullptr;
}
auto index = csb_index_.value();
DASSERT(index < exec_resources_.size());
return &exec_resources_[index];
}
// Returns a read only view of the context state buffer's GPU mapping.
// May be null if no context state buffer is present.
const GpuMappingView* GetContextStateBufferMapping() const {
DASSERT(prepared_to_execute_);
if (!csb_index_.has_value()) {
return nullptr;
}
auto index = csb_index_.value();
DASSERT(index < exec_resource_mappings_.size());
return exec_resource_mappings_[index].get();
}
// Returns whether the batch buffer and context state buffer (if present) are valid.
// This should only be called after |PrepareForExecution|.
bool IsValidBatch() {
DASSERT(prepared_to_execute_);
if ((batch_start_offset() & (sizeof(uint64_t) - 1)) != 0) {
MAGMA_LOG(ERROR, "batch start offset is not 8 byte aligned");
return false;
}
auto mapping = GetBatchMapping();
// |GetLength| returns the actual size of the user's data.
if (mapping->length() < batch_start_offset() + GetLength() + kAdditionalBytes) {
MAGMA_LOG(ERROR, "insufficient space for LINK command, mapped %lu used %lu need %u\n",
mapping->length(), GetLength(), kAdditionalBytes);
return false;
}
auto csb = GetContextStateBufferResource();
if (csb) {
// Check that the mapped length can fit the user data and also an additional LINK command.
auto csb_mapping = GetContextStateBufferMapping();
DASSERT(csb_mapping);
if (csb_mapping->length() < csb->length + kAdditionalBytes) {
MAGMA_LOG(ERROR, "CSB: insufficient space for LINK command, mapped %lu used %lu need %u\n",
csb_mapping->length(), csb->length, kAdditionalBytes);
return false;
}
}
return true;
}
private:
std::optional<uint32_t> csb_index_;
};
#endif // COMMAND_BUFFER_H