blob: 0e5496a36afe72ec113bd81e1c0dacc4c953d8ef [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_MLIR_EDGE_IREE_HAL_HOST_INPROC_COMMAND_BUFFER_H_
#define THIRD_PARTY_MLIR_EDGE_IREE_HAL_HOST_INPROC_COMMAND_BUFFER_H_
#include "third_party/mlir_edge/iree/base/arena.h"
#include "third_party/mlir_edge/iree/base/intrusive_list.h"
#include "third_party/mlir_edge/iree/base/status.h"
#include "third_party/mlir_edge/iree/hal/command_buffer.h"
namespace iree {
namespace hal {
// In-process command buffer with support for recording and playback.
// Commands are recorded into heap-allocated arenas with pointers to used
// resources (Buffer*, etc). To replay a command buffer against a real
// implementation use Process to call each command method as it was originally
// recorded.
//
// Thread-compatible (as with CommandBuffer itself).
class InProcCommandBuffer final : public CommandBuffer {
public:
InProcCommandBuffer(Allocator* allocator, CommandBufferModeBitfield mode,
CommandCategoryBitfield command_categories);
~InProcCommandBuffer() override;
bool is_recording() const override { return is_recording_; }
Status Begin() override;
Status End() override;
Status ExecutionBarrier(
ExecutionStageBitfield source_stage_mask,
ExecutionStageBitfield target_stage_mask,
absl::Span<const MemoryBarrier> memory_barriers,
absl::Span<const BufferBarrier> buffer_barriers) override;
Status SignalEvent(Event* event,
ExecutionStageBitfield source_stage_mask) override;
Status ResetEvent(Event* event,
ExecutionStageBitfield source_stage_mask) override;
Status WaitEvents(absl::Span<Event*> events,
ExecutionStageBitfield source_stage_mask,
ExecutionStageBitfield target_stage_mask,
absl::Span<const MemoryBarrier> memory_barriers,
absl::Span<const BufferBarrier> buffer_barriers) override;
Status FillBuffer(Buffer* target_buffer, device_size_t target_offset,
device_size_t length, const void* pattern,
size_t pattern_length) override;
Status DiscardBuffer(Buffer* buffer) override;
Status UpdateBuffer(const void* source_buffer, device_size_t source_offset,
Buffer* target_buffer, device_size_t target_offset,
device_size_t length) override;
Status CopyBuffer(Buffer* source_buffer, device_size_t source_offset,
Buffer* target_buffer, device_size_t target_offset,
device_size_t length) override;
Status Dispatch(const DispatchRequest& dispatch_request) override;
// Processes all commands in the buffer using the given |command_processor|.
// The commands are issued in the order they were recorded.
Status Process(CommandBuffer* command_processor) const;
private:
// Type of Cmd, used by CmdHeader to identify the command payload.
enum class CmdType {
kExecutionBarrier,
kSignalEvent,
kResetEvent,
kWaitEvents,
kFillBuffer,
kDiscardBuffer,
kUpdateBuffer,
kCopyBuffer,
kDispatch,
};
// Prefix for commands encoded into the CmdList.
// This is used to identify the type of a command as well as connect commands
// in the list sequence. Command data immediately follows the header in
// memory.
struct CmdHeader {
// Optional next command in the list.
CmdHeader* next;
// Type of the command.
CmdType type;
};
// A lightweight linked list of commands and an arena that stores them.
// CmdLists are designed to be reused so that the arena allocations are
// amortized across multiple uses.
//
// Note that this and the CmdHeader/Cmd types include raw pointers and as
// such are *not* portable across processes. It'd be possible, though, to
// extend this for cross-process use if a shared-memory Buffer was also
// implemented. For YAGNI we avoid that here.
struct CmdList : public IntrusiveLinkBase<void> {
static constexpr size_t kArenaBlockSize = 64 * 1024;
Arena arena{kArenaBlockSize};
CmdHeader* head = nullptr;
CmdHeader* tail = nullptr;
};
// Defines an execution barrier.
struct ExecutionBarrierCmd {
static constexpr CmdType kType = CmdType::kExecutionBarrier;
ExecutionStageBitfield source_stage_mask;
ExecutionStageBitfield target_stage_mask;
absl::Span<const MemoryBarrier> memory_barriers;
absl::Span<const BufferBarrier> buffer_barriers;
};
// Signals an event.
struct SignalEventCmd {
static constexpr CmdType kType = CmdType::kSignalEvent;
Event* event;
ExecutionStageBitfield source_stage_mask;
};
// Resets an event.
struct ResetEventCmd {
static constexpr CmdType kType = CmdType::kResetEvent;
Event* event;
ExecutionStageBitfield source_stage_mask;
};
// Waits for one or more events.
struct WaitEventsCmd {
static constexpr CmdType kType = CmdType::kWaitEvents;
absl::Span<Event*> events;
ExecutionStageBitfield source_stage_mask;
ExecutionStageBitfield target_stage_mask;
absl::Span<const MemoryBarrier> memory_barriers;
absl::Span<const BufferBarrier> buffer_barriers;
};
// Fills the target buffer with the given repeating value.
struct FillBufferCmd {
static constexpr CmdType kType = CmdType::kFillBuffer;
Buffer* target_buffer;
device_size_t target_offset;
device_size_t length;
uint8_t pattern[4];
size_t pattern_length;
};
// Hints to the device queue that the given buffer will not be used again.
struct DiscardBufferCmd {
static constexpr CmdType kType = CmdType::kDiscardBuffer;
Buffer* buffer;
};
// Writes a range of the given target buffer from the embedded memory.
// The source buffer contents immediately follow the command in the arena.
struct UpdateBufferCmd {
static constexpr CmdType kType = CmdType::kUpdateBuffer;
const void* source_buffer;
Buffer* target_buffer;
device_size_t target_offset;
device_size_t length;
};
// Copies a range of one buffer to another.
struct CopyBufferCmd {
static constexpr CmdType kType = CmdType::kCopyBuffer;
Buffer* source_buffer;
device_size_t source_offset;
Buffer* target_buffer;
device_size_t target_offset;
device_size_t length;
};
// Dispatches an execution request.
struct DispatchCmd {
static constexpr CmdType kType = CmdType::kDispatch;
DispatchRequest request;
};
// Resets the command list.
void Reset();
// Allocates a command and appends it to the current command list.
// The caller must populate the fields in the returned pointer.
template <typename T>
StatusOr<T*> AppendCmd() {
return reinterpret_cast<T*>(AppendCmdHeader(T::kType, sizeof(T)) + 1);
}
// Appends a command with the given |type| and payload |cmd_size| prefixed
// with a CmdHeader. Returns a pointer to the CmdHeader that is followed
// immediately by |cmd_size| zero bytes.
CmdHeader* AppendCmdHeader(CmdType type, size_t cmd_size);
// Appends a byte buffer to the command buffer and returns a pointer to the
// copied data within the command buffer arena.
void* AppendCmdData(const void* source_buffer, device_size_t source_offset,
device_size_t source_length);
// Appends a span of POD structs to the current CmdList and returns a span
// pointing into the CmdList arena.
template <typename T>
absl::Span<T> AppendStructSpan(absl::Span<T> value) {
static_assert(std::is_standard_layout<T>::value,
"Struct must be a POD type");
void* data_ptr = AppendCmdData(value.data(), 0, value.size() * sizeof(T));
return absl::MakeSpan(static_cast<T*>(data_ptr), value.size());
}
// Processes a single command.
Status ProcessCmd(CmdHeader* cmd_header,
CommandBuffer* command_processor) const;
bool is_recording_ = false;
// NOTE: not synchronized. Expected to be used from a single thread.
CmdList current_cmd_list_;
};
} // namespace hal
} // namespace iree
#endif // THIRD_PARTY_MLIR_EDGE_IREE_HAL_HOST_INPROC_COMMAND_BUFFER_H_