blob: 182fb273533ff78df624f5f2337b73b2ccf679a2 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/client/dawn_client_serializer.h"
#include "base/compiler_specific.h"
#include "base/numerics/checked_math.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/client/dawn_client_memory_transfer_service.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
#include "gpu/command_buffer/client/webgpu_cmd_helper.h"
#include "gpu/command_buffer/client/webgpu_implementation.h"
namespace gpu {
namespace webgpu {
DawnClientSerializer::DawnClientSerializer(
WebGPUImplementation* client,
WebGPUCmdHelper* helper,
DawnClientMemoryTransferService* memory_transfer_service_,
std::unique_ptr<TransferBuffer> transfer_buffer)
: client_(client),
helper_(helper),
memory_transfer_service_(memory_transfer_service_),
transfer_buffer_(std::move(transfer_buffer)),
buffer_initial_size_(transfer_buffer_->GetSize()),
buffer_(helper_, transfer_buffer_.get()) {
DCHECK_GT(buffer_initial_size_, 0u);
}
DawnClientSerializer::~DawnClientSerializer() = default;
size_t DawnClientSerializer::GetMaximumAllocationSize() const {
return transfer_buffer_->GetMaxSize();
}
#if DCHECK_IS_ON()
void DawnClientSerializer::OnSerializeError() {
NOTREACHED() << "DawnClientSerializer error";
}
#endif
void* DawnClientSerializer::GetCmdSpace(size_t size) {
// Note: Dawn will never call this function with |size| >
// GetMaximumAllocationSize().
DCHECK_LE(size, GetMaximumAllocationSize());
// The buffer size must be initialized before any commands are serialized.
DCHECK_NE(buffer_initial_size_, 0u);
DCHECK_LE(put_offset_, buffer_.size());
const bool overflows_remaining_space =
size > static_cast<size_t>(buffer_.size() - put_offset_);
if (buffer_.valid() && !overflows_remaining_space) [[likely]] {
// If the buffer is valid and has sufficient space, return the
// pointer and increment the offset.
uint8_t* ptr = static_cast<uint8_t*>(buffer_.address());
UNSAFE_TODO(ptr += put_offset_);
put_offset_ += static_cast<uint32_t>(size);
return ptr;
}
if (!transfer_buffer_) {
// The serializer hit a fatal error and was disconnected.
return nullptr;
}
// Otherwise, flush and reset the command stream.
Flush();
uint32_t allocation_size =
std::max(buffer_initial_size_, static_cast<uint32_t>(size));
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
"DawnClientSerializer::GetCmdSpace", "bytes", allocation_size);
buffer_.Reset(allocation_size);
if (!buffer_.valid() || buffer_.size() < size) {
DLOG(ERROR) << "Dawn wire transfer buffer allocation failed";
Disconnect();
client_->OnGpuControlLostContextMaybeReentrant();
return nullptr;
}
put_offset_ = size;
return buffer_.address();
}
void DawnClientSerializer::Commit() {
if (buffer_.valid()) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
"DawnClientSerializer::Flush", "bytes", put_offset_);
bool is_tracing = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
&is_tracing);
uint64_t trace_id;
if (is_tracing) {
trace_id = base::RandUint64();
TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
"DawnCommands", trace_id,
TRACE_EVENT_FLAG_FLOW_OUT);
} else {
trace_id = 0;
}
buffer_.Shrink(put_offset_);
helper_->DawnCommands(trace_id >> 32, trace_id & 0xFFFF'FFFF,
buffer_.shm_id(), buffer_.offset(), put_offset_);
put_offset_ = 0;
buffer_.Release();
memory_transfer_service_->FreeHandles(helper_);
}
}
void DawnClientSerializer::SetAwaitingFlush(bool awaiting_flush) {
// Set awaiting_flush_. Even if there are no commands in buffer_, this may be
// necessary since the buffer_ commands could have been committed and reset,
// but not yet flushed.
awaiting_flush_ = awaiting_flush;
}
void DawnClientSerializer::Disconnect() {
buffer_.Discard();
if (transfer_buffer_) {
auto transfer_buffer = std::move(transfer_buffer_);
// Wait for commands to finish before we free shared memory that
// the GPU process is using.
// TODO(crbug.com/40779774): This Finish may not be necessary if the
// shared memory is not immediately freed. Investigate this and
// consider optimization.
helper_->Finish();
transfer_buffer = nullptr;
}
}
bool DawnClientSerializer::Flush() {
Commit();
return true;
}
} // namespace webgpu
} // namespace gpu