#ifndef GPU_COMMAND_BUFFER_CLIENT_CMD_BUFFER_HELPER_H_
#define GPU_COMMAND_BUFFER_CLIENT_CMD_BUFFER_HELPER_H_
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/functional/function_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_provider.h"
#include "build/build_config.h"
#include "gpu/command_buffer/client/gpu_command_buffer_client_export.h"
#include "gpu/command_buffer/common/cmd_buffer_common.h"
#include "gpu/command_buffer/common/command_buffer.h"
#include "gpu/command_buffer/common/context_result.h"
#if BUILDFLAG(IS_ARKWEB)
#include "third_party/ohos_ndk/includes/ohos_adapter/ohos_adapter_helper.h"
#endif
namespace gpu {
class Buffer;
#if !BUILDFLAG(IS_ANDROID)
#define CMD_HELPER_PERIODIC_FLUSH_CHECK
const int kCommandsPerFlushCheck = 100;
const int kPeriodicFlushDelayInMicroseconds = 500;
#endif
const int kAutoFlushSmall = 16;
const int kAutoFlushBig = 2;
class GPU_COMMAND_BUFFER_CLIENT_EXPORT CommandBufferHelper {
public:
explicit CommandBufferHelper(CommandBuffer* command_buffer);
CommandBufferHelper(const CommandBufferHelper&) = delete;
CommandBufferHelper& operator=(const CommandBufferHelper&) = delete;
virtual ~CommandBufferHelper();
gpu::ContextResult Initialize(uint32_t ring_buffer_size);
void SetAutomaticFlushes(bool enabled);
bool IsContextLost();
void Flush();
void FlushLazy();
void OrderingBarrier();
bool Finish();
void WaitForAvailableEntries(int32_t count);
int32_t InsertToken();
bool HasTokenPassed(int32_t token);
bool HasCachedTokenPassed(int32_t token);
void RefreshCachedToken();
void WaitForToken(int32_t token);
void* GetSpace(int32_t entries) {
#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
++commands_issued_;
if (flush_automatically_ &&
#if BUILDFLAG(IS_ARKWEB)
(commands_issued_ % kCommandsPerFlushCheck == 0) && EnablePeriodFlush()) {
#else
(commands_issued_ % kCommandsPerFlushCheck == 0)) {
#endif
PeriodicFlushCheck();
}
#endif
if (entries > immediate_entry_count_) {
WaitForAvailableEntries(entries);
if (entries > immediate_entry_count_)
return nullptr;
}
DCHECK_LE(entries, immediate_entry_count_);
CommandBufferEntry* space = UNSAFE_TODO(&entries_[put_]);
put_ += entries;
immediate_entry_count_ -= entries;
DCHECK_LE(put_, total_entry_count_);
return space;
}
template <typename T>
T* GetCmdSpace() {
static_assert(T::kArgFlags == cmd::kFixed,
"T::kArgFlags should equal cmd::kFixed");
int32_t space_needed = ComputeNumEntries(sizeof(T));
T* data = static_cast<T*>(GetSpace(space_needed));
return data;
}
template <typename T>
T* GetImmediateCmdSpace(size_t data_space) {
static_assert(T::kArgFlags == cmd::kAtLeastN,
"T::kArgFlags should equal cmd::kAtLeastN");
int32_t space_needed = ComputeNumEntries(sizeof(T) + data_space);
T* data = static_cast<T*>(GetSpace(space_needed));
return data;
}
template <typename T>
T* GetImmediateCmdSpaceTotalSize(size_t total_space) {
static_assert(T::kArgFlags == cmd::kAtLeastN,
"T::kArgFlags should equal cmd::kAtLeastN");
int32_t space_needed = ComputeNumEntries(total_space);
T* data = static_cast<T*>(GetSpace(space_needed));
return data;
}
void Noop(uint32_t skip_count) {
cmd::Noop* cmd = GetImmediateCmdSpace<cmd::Noop>(
(skip_count - 1) * sizeof(CommandBufferEntry));
if (cmd) {
cmd->Init(skip_count);
}
}
void SetToken(uint32_t token) {
cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
if (cmd) {
cmd->Init(token);
}
}
void SetBucketSize(uint32_t bucket_id, uint32_t size) {
cmd::SetBucketSize* cmd = GetCmdSpace<cmd::SetBucketSize>();
if (cmd) {
cmd->Init(bucket_id, size);
}
}
void SetBucketData(uint32_t bucket_id,
uint32_t offset,
uint32_t size,
uint32_t shared_memory_id,
uint32_t shared_memory_offset) {
cmd::SetBucketData* cmd = GetCmdSpace<cmd::SetBucketData>();
if (cmd) {
cmd->Init(bucket_id, offset, size, shared_memory_id,
shared_memory_offset);
}
}
void SetBucketDataImmediate(uint32_t bucket_id,
uint32_t offset,
const void* data,
uint32_t size) {
cmd::SetBucketDataImmediate* cmd =
GetImmediateCmdSpace<cmd::SetBucketDataImmediate>(size);
if (cmd) {
cmd->Init(bucket_id, offset, size);
UNSAFE_TODO(memcpy(ImmediateDataAddress(cmd), data, size));
}
}
void GetBucketStart(uint32_t bucket_id,
uint32_t result_memory_id,
uint32_t result_memory_offset,
uint32_t data_memory_size,
uint32_t data_memory_id,
uint32_t data_memory_offset) {
cmd::GetBucketStart* cmd = GetCmdSpace<cmd::GetBucketStart>();
if (cmd) {
cmd->Init(bucket_id, result_memory_id, result_memory_offset,
data_memory_size, data_memory_id, data_memory_offset);
}
}
void GetBucketData(uint32_t bucket_id,
uint32_t offset,
uint32_t size,
uint32_t shared_memory_id,
uint32_t shared_memory_offset) {
cmd::GetBucketData* cmd = GetCmdSpace<cmd::GetBucketData>();
if (cmd) {
cmd->Init(bucket_id, offset, size, shared_memory_id,
shared_memory_offset);
}
}
uint64_t InsertFenceSync(base::FunctionRef<uint64_t()> sync_token_generator) {
cmd::InsertFenceSync* cmd = GetCmdSpace<cmd::InsertFenceSync>();
if (cmd) {
uint64_t release_count = sync_token_generator();
cmd->Init(release_count);
return release_count;
}
return 0;
}
CommandBuffer* command_buffer() const { return command_buffer_; }
scoped_refptr<Buffer> get_ring_buffer() const { return ring_buffer_; }
int32_t get_ring_buffer_id() const { return ring_buffer_id_; }
uint32_t flush_generation() const { return flush_generation_; }
void FreeRingBuffer();
bool HaveRingBuffer() const {
bool have_ring_buffer = !!ring_buffer_;
DCHECK(usable() || !have_ring_buffer);
return have_ring_buffer;
}
bool usable() const { return usable_; }
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd);
int32_t GetPutOffsetForTest() const { return put_; }
#if BUILDFLAG(IS_ARKWEB)
bool EnablePeriodFlush() const {
return enable_period_flush_;
}
#endif
private:
void CalcImmediateEntries(int waiting_count);
bool AllocateRingBuffer();
void SetGetBuffer(int32_t id, scoped_refptr<Buffer> buffer);
bool WaitForGetOffsetInRange(int32_t start, int32_t end);
#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
void PeriodicFlushCheck();
#endif
int32_t GetTotalFreeEntriesNoWaiting() const;
void UpdateCachedState(const CommandBuffer::State& state);
const raw_ptr<CommandBuffer> command_buffer_;
int32_t ring_buffer_id_ = -1;
uint32_t ring_buffer_size_ = 0;
scoped_refptr<gpu::Buffer> ring_buffer_;
raw_ptr<CommandBufferEntry, AllowPtrArithmetic> entries_ = nullptr;
int32_t total_entry_count_ = 0;
int32_t immediate_entry_count_ = 0;
int32_t token_ = 0;
int32_t put_ = 0;
int32_t cached_last_token_read_ = 0;
int32_t cached_get_offset_ = 0;
uint32_t set_get_buffer_count_ = 0;
bool service_on_old_buffer_ = false;
#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
int commands_issued_ = 0;
#if BUILDFLAG(IS_ARKWEB)
bool enable_period_flush_ = false;
#endif
#endif
bool usable_ = true;
bool context_lost_ = false;
bool flush_automatically_ = true;
int32_t last_flush_put_ = 0;
int32_t last_ordering_barrier_put_ = 0;
base::TimeTicks last_flush_time_;
uint32_t flush_generation_ = 0;
friend class CommandBufferHelperTest;
};
}
#endif