blob: f28a327260fb2c8f81e648ebf310cf009351912b [file] [log] [blame]
// Copyright 2021 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 "render_command_streamer.h"
#include "device_id.h"
#include "instructions.h"
#include "registers.h"
std::unique_ptr<RenderInitBatch> RenderEngineCommandStreamer::CreateRenderInitBatch(
uint32_t device_id) {
std::unique_ptr<RenderInitBatch> batch;
if (DeviceId::is_gen9(device_id)) {
return std::unique_ptr<RenderInitBatch>(new RenderInitBatchGen9());
}
return DRETP(nullptr, "unhandled device id");
}
RenderEngineCommandStreamer::RenderEngineCommandStreamer(EngineCommandStreamer::Owner* owner,
std::unique_ptr<GpuMapping> hw_status_page)
: EngineCommandStreamer(owner, RENDER_COMMAND_STREAMER, kRenderEngineMmioBase,
std::move(hw_status_page), Scheduler::CreateFifoScheduler()) {
set_forcewake_domain(ForceWakeDomain::RENDER);
}
bool RenderEngineCommandStreamer::RenderInit(std::shared_ptr<MsdIntelContext> context,
std::unique_ptr<RenderInitBatch> init_batch,
std::shared_ptr<AddressSpace> address_space) {
DASSERT(context);
DASSERT(init_batch);
DASSERT(address_space);
auto buffer = std::unique_ptr<MsdIntelBuffer>(
MsdIntelBuffer::Create(init_batch->size(), "render-init-batch"));
if (!buffer)
return DRETF(false, "failed to allocate render init buffer");
auto mapping = init_batch->Init(std::move(buffer), address_space);
if (!mapping)
return DRETF(false, "batch init failed");
std::unique_ptr<SimpleMappedBatch> mapped_batch(
new SimpleMappedBatch(context, std::move(mapping)));
return ExecBatch(std::move(mapped_batch));
}
std::unique_ptr<IndirectContextBatch> RenderEngineCommandStreamer::CreateIndirectContextBatch(
std::shared_ptr<AddressSpace> address_space) {
auto buffer = std::shared_ptr<MsdIntelBuffer>(
MsdIntelBuffer::Create(magma::page_size(), "indirect-context-batch"));
if (!buffer)
return DRETP(nullptr, "failed to create buffer");
gpu_addr_t gpu_addr =
hardware_status_page()->gpu_addr() + GlobalHardwareStatusPage::kScratchOffset;
constexpr uint32_t kFlags =
MiPipeControl::kAddressSpaceGen9ClearEuBit | MiPipeControl::kCommandStreamerStallEnableBit;
uint32_t length = 0;
{
void* ptr;
if (!buffer->platform_buffer()->MapCpu(&ptr))
return DRETP(nullptr, "failed to map");
class Writer : public magma::InstructionWriter {
public:
Writer(uint32_t* ptr) : ptr_(ptr) {}
void Write32(uint32_t dword) override {
*ptr_++ = dword;
length_ += sizeof(uint32_t);
}
uint32_t length() { return length_; }
private:
uint32_t* ptr_;
uint32_t length_ = 0;
};
Writer writer(reinterpret_cast<uint32_t*>(ptr));
MiPipeControl::write(&writer, Sequencer::kInvalidSequenceNumber, gpu_addr, kFlags);
length = magma::round_up(writer.length(), DeviceId::cache_line_size());
// Memory should already be zero, but to be sure we pad with no-ops
static_assert(MiNoop::kDwordCount == 1);
DASSERT((length - writer.length()) % sizeof(uint32_t) == 0);
uint32_t noop_count = (length - writer.length()) / sizeof(uint32_t);
for (uint32_t i = 0; i < noop_count; i++) {
MiNoop::write(&writer);
}
DASSERT(writer.length() % DeviceId::cache_line_size() == 0);
buffer->platform_buffer()->UnmapCpu();
}
auto mapping = AddressSpace::MapBufferGpu(address_space, buffer);
if (!mapping)
return DRETP(nullptr, "batch init failed");
return std::make_unique<IndirectContextBatch>(std::move(mapping), length);
}
bool RenderEngineCommandStreamer::WriteBatchToRingBuffer(MappedBatch* mapped_batch,
uint32_t* sequence_number_out) {
auto context = mapped_batch->GetContext().lock();
DASSERT(context);
{
gpu_addr_t gpu_addr;
// Some "mapped batches" have no batch
if (mapped_batch->GetGpuAddress(&gpu_addr)) {
if (!StartBatchBuffer(context.get(), gpu_addr, context->exec_address_space()->type()))
return DRETF(false, "failed to emit batch");
}
}
uint32_t sequence_number;
if (!PipeControl(context.get(), mapped_batch->GetPipeControlFlags(), &sequence_number))
return DRETF(false, "PipeControl failed");
auto ringbuffer = context->get_ringbuffer(id());
// TODO(https://fxbug.dev/42078195): don't allocate a sequence number if no space for the user interrupt
if (!ringbuffer->HasSpace(MiUserInterrupt::kDwordCount * sizeof(uint32_t)))
return DRETF(false, "ringbuffer has insufficient space");
MiUserInterrupt::write(ringbuffer);
*sequence_number_out = sequence_number;
return true;
}
bool RenderEngineCommandStreamer::PipeControl(MsdIntelContext* context, uint32_t flags,
uint32_t* sequence_number_out) {
auto ringbuffer = context->get_ringbuffer(id());
uint32_t dword_count = MiPipeControl::kDwordCount + MiNoop::kDwordCount;
if (!ringbuffer->HasSpace(dword_count * sizeof(uint32_t)))
return DRETF(false, "ringbuffer has insufficient space");
gpu_addr_t gpu_addr =
hardware_status_page()->gpu_addr() + GlobalHardwareStatusPage::kSequenceNumberOffset;
uint32_t sequence_number = sequencer()->next_sequence_number();
DLOG("writing sequence number update to 0x%x", sequence_number);
MiPipeControl::write(ringbuffer, sequence_number, gpu_addr, flags);
MiNoop::write(ringbuffer);
*sequence_number_out = sequence_number;
return true;
}