Commit e41b1e94 authored by Austin Eng's avatar Austin Eng Committed by Commit Bot

Implement the dawn_wire MemoryTransferService using shared memory

Previously, mapping buffer data between the Dawn client and service
required additional copies. The client had to keep a shadow copy of the
buffer data, copy it into the command stream, and then the service would
copy it out. Implementing the dawn_wire MemoryTransferService with
shared memory completely removes the additional copies because the data
is visible to both the client and service.

Bug: dawn:156
Change-Id: I31e4c005a1b26ff5a1bf8139bb164d2dcabc48ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1713751
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarKai Ninomiya <kainino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#685454}
parent b7a97ae2
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/jumbo.gni")
import("//ui/gl/features.gni")
declare_args() {
# Enable GPU client logging without DCHECK being on.
......@@ -311,6 +312,12 @@ source_set("webgpu_sources") {
"webgpu_implementation_autogen.h",
"webgpu_implementation_impl_autogen.h",
]
if (use_dawn) {
sources += [
"dawn_client_memory_transfer_service.cc",
"dawn_client_memory_transfer_service.h",
]
}
}
# Library emulates GLES2 using command_buffers.
......
// Copyright 2019 The Chromium 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 "gpu/command_buffer/client/dawn_client_memory_transfer_service.h"
#include "gpu/command_buffer/client/mapped_memory.h"
#include "gpu/command_buffer/common/dawn_memory_transfer_handle.h"
namespace gpu {
namespace webgpu {
class DawnClientMemoryTransferService::ReadHandleImpl
: public dawn_wire::client::MemoryTransferService::ReadHandle {
public:
ReadHandleImpl(void* ptr,
MemoryTransferHandle handle,
DawnClientMemoryTransferService* service)
: ReadHandle(), ptr_(ptr), handle_(handle), service_(service) {}
~ReadHandleImpl() override {
// The shared memory can't be freed until the server consumes it. Add the
// the pointer to a list of blocks to process on the next Flush.
service_->MarkHandleFree(ptr_);
}
// Get the serialization size of SerializeCreate.
size_t SerializeCreateSize() override { return sizeof(MemoryTransferHandle); }
// Serialize the handle into |serialize_pointer| so it can be received by the
// service.
void SerializeCreate(void* serialize_pointer) override {
*reinterpret_cast<MemoryTransferHandle*>(serialize_pointer) = handle_;
}
// Load initial data and open the handle for reading.
// This function takes in the serialized result of
// ReadHandle::SerializeInitialData.
// It writes to |data| and |data_length| the pointer and size
// of the mapped data for reading.
// The allocation must live at least until the ReadHandle is destructed.
bool DeserializeInitialData(const void* deserialize_pointer,
size_t deserialize_size,
const void** data,
size_t* data_length) override {
// No data is deserialized because we're using shared memory.
DCHECK_EQ(deserialize_size, 0u);
DCHECK(data);
DCHECK(data_length);
// Write the pointer and size of the shared memory allocation.
// |data| and |data_length| are provided by the dawn_wire client.
*data = ptr_;
*data_length = handle_.size;
return true;
}
private:
void* ptr_; // Pointer to client-side shared memory.
MemoryTransferHandle handle_;
DawnClientMemoryTransferService* service_;
};
class DawnClientMemoryTransferService::WriteHandleImpl
: public dawn_wire::client::MemoryTransferService::WriteHandle {
public:
WriteHandleImpl(void* ptr,
MemoryTransferHandle handle,
DawnClientMemoryTransferService* service)
: WriteHandle(), ptr_(ptr), handle_(handle), service_(service) {}
~WriteHandleImpl() override {
// The shared memory can't be freed until the server consumes it. Add
// the pointer to a list of blocks to process on the next Flush.
service_->MarkHandleFree(ptr_);
}
// Get the serialization size of SerializeCreate.
size_t SerializeCreateSize() override { return sizeof(MemoryTransferHandle); }
// Serialize the handle into |serialize_pointer| so it can be received by the
// service.
void SerializeCreate(void* serialize_pointer) override {
*reinterpret_cast<MemoryTransferHandle*>(serialize_pointer) = handle_;
}
// Open the handle for writing.
// The data returned must live at least until the WriteHandle is destructed.
std::pair<void*, size_t> Open() override {
return std::make_pair(ptr_, handle_.size);
}
size_t SerializeFlushSize() override {
// No data is serialized because we're using shared memory.
return 0;
}
void SerializeFlush(void* serialize_pointer) override {
// No data is serialized because we're using shared memory.
}
private:
void* ptr_;
MemoryTransferHandle handle_;
DawnClientMemoryTransferService* service_;
};
DawnClientMemoryTransferService::DawnClientMemoryTransferService(
MappedMemoryManager* mapped_memory)
: dawn_wire::client::MemoryTransferService(),
mapped_memory_(mapped_memory) {}
DawnClientMemoryTransferService::~DawnClientMemoryTransferService() = default;
dawn_wire::client::MemoryTransferService::ReadHandle*
DawnClientMemoryTransferService::CreateReadHandle(size_t size) {
MemoryTransferHandle handle = {};
void* ptr = AllocateHandle(size, &handle);
if (ptr == nullptr) {
return nullptr;
}
return new ReadHandleImpl(ptr, handle, this);
}
dawn_wire::client::MemoryTransferService::WriteHandle*
DawnClientMemoryTransferService::CreateWriteHandle(size_t size) {
MemoryTransferHandle handle = {};
void* ptr = AllocateHandle(size, &handle);
if (ptr == nullptr) {
return nullptr;
}
// Zero-initialize the data.
memset(ptr, 0, handle.size);
return new WriteHandleImpl(ptr, handle, this);
}
void* DawnClientMemoryTransferService::AllocateHandle(
size_t size,
MemoryTransferHandle* handle) {
if (size > std::numeric_limits<uint32_t>::max()) {
return nullptr;
}
DCHECK(handle);
handle->size = static_cast<uint32_t>(size);
DCHECK(mapped_memory_);
return mapped_memory_->Alloc(handle->size, &handle->shm_id,
&handle->shm_offset);
}
void DawnClientMemoryTransferService::MarkHandleFree(void* ptr) {
free_blocks_.push_back(ptr);
}
void DawnClientMemoryTransferService::FreeHandlesPendingToken(int32_t token) {
std::vector<void*> to_free = std::move(free_blocks_);
for (void* ptr : to_free) {
mapped_memory_->FreePendingToken(ptr, token);
}
}
} // namespace webgpu
} // namespace gpu
// Copyright 2019 The Chromium 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 GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
#define GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
#include <dawn_wire/WireClient.h>
#include <vector>
namespace gpu {
class MappedMemoryManager;
namespace webgpu {
struct MemoryTransferHandle;
class DawnClientMemoryTransferService final
: public dawn_wire::client::MemoryTransferService {
public:
DawnClientMemoryTransferService(MappedMemoryManager* mapped_memory);
~DawnClientMemoryTransferService() override;
// Create a handle for reading shared memory data.
// This may fail and return nullptr.
ReadHandle* CreateReadHandle(size_t size) override;
// Create a handle for writing shared memory data.
// This may fail and return nullptr.
WriteHandle* CreateWriteHandle(size_t size) override;
// Free shared memory allocations after the token passes on the GPU process.
void FreeHandlesPendingToken(int32_t token);
private:
class ReadHandleImpl;
class WriteHandleImpl;
// Allocate a shared memory handle for the memory transfer.
void* AllocateHandle(size_t size, MemoryTransferHandle* handle);
// Mark a shared memory allocation as free. This should not be called more
// than once per block.
void MarkHandleFree(void* ptr);
MappedMemoryManager* mapped_memory_;
// Pointers to memory allocated by the MappedMemoryManager to free after
// the next Flush.
std::vector<void*> free_blocks_;
};
} // namespace webgpu
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_CLIENT_DAWN_CLIENT_MEMORY_TRANSFER_SERVICE_H_
......@@ -9,6 +9,7 @@
#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/gpu_control.h"
#include "gpu/command_buffer/client/shared_memory_limits.h"
......@@ -28,19 +29,15 @@ WebGPUImplementation::WebGPUImplementation(
GpuControl* gpu_control)
: ImplementationBase(helper, transfer_buffer, gpu_control),
helper_(helper),
#if BUILDFLAG(USE_DAWN)
wire_client_([this]() {
dawn_wire::WireClientDescriptor descriptor = {};
descriptor.serializer = this;
return new dawn_wire::WireClient(descriptor);
}()),
procs_(wire_client_->GetProcs()),
#endif
c2s_buffer_(helper, transfer_buffer) {
}
WebGPUImplementation::~WebGPUImplementation() {}
WebGPUImplementation::~WebGPUImplementation() {
// Wait for all commands to finish or we may free shared memory while
// commands are still in flight.
Flush();
helper_->Finish();
}
gpu::ContextResult WebGPUImplementation::Initialize(
const SharedMemoryLimits& limits) {
......@@ -53,6 +50,19 @@ gpu::ContextResult WebGPUImplementation::Initialize(
c2s_buffer_default_size_ = limits.start_transfer_buffer_size;
DCHECK_GT(c2s_buffer_default_size_, 0u);
#if BUILDFLAG(USE_DAWN)
memory_transfer_service_.reset(
new DawnClientMemoryTransferService(mapped_memory_.get()));
dawn_wire::WireClientDescriptor descriptor = {};
descriptor.serializer = this;
descriptor.memoryTransferService = memory_transfer_service_.get();
wire_client_.reset(new dawn_wire::WireClient(descriptor));
procs_ = wire_client_->GetProcs();
#endif
return gpu::ContextResult::kSuccess;
}
......@@ -278,6 +288,9 @@ bool WebGPUImplementation::Flush() {
c2s_put_offset_ = 0;
c2s_buffer_.Release();
}
#if BUILDFLAG(USE_DAWN)
memory_transfer_service_->FreeHandlesPendingToken(helper_->InsertToken());
#endif
return true;
}
......
......@@ -24,6 +24,8 @@
namespace gpu {
namespace webgpu {
class DawnClientMemoryTransferService;
class WEBGPU_EXPORT WebGPUImplementation final
: public dawn_wire::CommandSerializer,
public WebGPUInterface,
......@@ -122,6 +124,7 @@ class WEBGPU_EXPORT WebGPUImplementation final
WebGPUCmdHelper* helper_;
#if BUILDFLAG(USE_DAWN)
std::unique_ptr<DawnClientMemoryTransferService> memory_transfer_service_;
std::unique_ptr<dawn_wire::WireClient> wire_client_;
#endif
DawnProcTable procs_ = {};
......
......@@ -177,6 +177,7 @@ source_set("webgpu_sources") {
visibility = [ "//gpu/*" ]
sources = [
"dawn_memory_transfer_handle.h",
"webgpu_cmd_format.cc",
"webgpu_cmd_format.h",
"webgpu_cmd_format_autogen.h",
......
// Copyright 2019 The Chromium 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 GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
#define GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
namespace gpu {
namespace webgpu {
// This struct holds information describing a shared memory allocation used for
// bulk data transfers between the Dawn client and service. The shared memory is
// allocated by the client using MappedMemoryManager.
// On the GPU service, shared memory is received using
// CommonDecoder::GetSharedMemoryAs which checks the memory region is valid.
struct MemoryTransferHandle {
uint32_t size;
int32_t shm_id;
uint32_t shm_offset;
};
} // namespace webgpu
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_COMMON_DAWN_MEMORY_TRANSFER_HANDLE_H_
......@@ -268,6 +268,8 @@ target(link_target_type, "gles2_sources") {
if (use_dawn) {
sources += [
"dawn_service_memory_transfer_service.cc",
"dawn_service_memory_transfer_service.h",
"webgpu_decoder_impl.cc",
"webgpu_decoder_impl.h",
]
......
// Copyright 2019 The Chromium 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 "gpu/command_buffer/service/dawn_service_memory_transfer_service.h"
#include "gpu/command_buffer/common/dawn_memory_transfer_handle.h"
#include "gpu/command_buffer/service/common_decoder.h"
namespace gpu {
namespace webgpu {
namespace {
class ReadHandleImpl
: public dawn_wire::server::MemoryTransferService::ReadHandle {
public:
ReadHandleImpl(void* ptr, uint32_t size)
: ReadHandle(), ptr_(ptr), size_(size) {}
~ReadHandleImpl() override = default;
size_t SerializeInitialDataSize(const void* data,
size_t data_length) override {
// Nothing is serialized because we're using shared memory.
return 0;
}
void SerializeInitialData(const void* data,
size_t data_length,
void* serialize_pointer) override {
DCHECK_EQ(data_length, size_);
// Copy the initial data into the shared memory allocation.
// In the case of buffer mapping, this is the mapped GPU memory which we
// copy into client-visible shared memory.
memcpy(ptr_, data, data_length);
}
private:
void* ptr_;
uint32_t size_;
};
class WriteHandleImpl
: public dawn_wire::server::MemoryTransferService::WriteHandle {
public:
WriteHandleImpl(const void* ptr, uint32_t size)
: WriteHandle(), ptr_(ptr), size_(size) {}
~WriteHandleImpl() override = default;
bool DeserializeFlush(const void* deserialize_pointer,
size_t deserialize_size) override {
// Nothing is serialized because we're using shared memory.
DCHECK_EQ(deserialize_size, 0u);
DCHECK_EQ(mDataLength, size_);
DCHECK(mTargetData);
DCHECK(ptr_);
// Copy from shared memory into the target buffer.
memcpy(mTargetData, ptr_, size_);
return true;
}
private:
const void* ptr_; // Pointer to client-visible shared memory.
uint32_t size_;
};
} // namespace
DawnServiceMemoryTransferService::DawnServiceMemoryTransferService(
CommonDecoder* decoder)
: dawn_wire::server::MemoryTransferService(), decoder_(decoder) {}
DawnServiceMemoryTransferService::~DawnServiceMemoryTransferService() = default;
bool DawnServiceMemoryTransferService::DeserializeReadHandle(
const void* deserialize_pointer,
size_t deserialize_size,
ReadHandle** read_handle) {
DCHECK(deserialize_pointer);
DCHECK_EQ(deserialize_size, sizeof(MemoryTransferHandle));
const volatile MemoryTransferHandle* handle =
reinterpret_cast<const volatile MemoryTransferHandle*>(
deserialize_pointer);
uint32_t size = handle->size;
int32_t shm_id = handle->shm_id;
uint32_t shm_offset = handle->shm_offset;
void* ptr = decoder_->GetAddressAndCheckSize(shm_id, shm_offset, size);
if (ptr == nullptr) {
return false;
}
DCHECK(read_handle);
*read_handle = new ReadHandleImpl(ptr, size);
return true;
}
bool DawnServiceMemoryTransferService::DeserializeWriteHandle(
const void* deserialize_pointer,
size_t deserialize_size,
WriteHandle** write_handle) {
DCHECK(deserialize_pointer);
DCHECK_EQ(deserialize_size, sizeof(MemoryTransferHandle));
const volatile MemoryTransferHandle* handle =
reinterpret_cast<const volatile MemoryTransferHandle*>(
deserialize_pointer);
uint32_t size = handle->size;
int32_t shm_id = handle->shm_id;
uint32_t shm_offset = handle->shm_offset;
void* ptr = decoder_->GetAddressAndCheckSize(shm_id, shm_offset, size);
if (ptr == nullptr) {
return false;
}
DCHECK(write_handle);
*write_handle = new WriteHandleImpl(ptr, size);
return true;
}
} // namespace webgpu
} // namespace gpu
// Copyright 2019 The Chromium 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 GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
#define GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
#include <dawn_wire/WireServer.h>
namespace gpu {
class CommonDecoder;
namespace webgpu {
class DawnServiceMemoryTransferService final
: public dawn_wire::server::MemoryTransferService {
public:
DawnServiceMemoryTransferService(CommonDecoder* decoder);
~DawnServiceMemoryTransferService() override;
// Deserialize data to create Read/Write handles. These handles are for the
// client to Read/Write data. The serialized data is a MemoryTransferHandle
// which contains the id, offset, and size of a shared memory region. If the
// decoded region is invalid, these functions return false and result in a
// context lost.
// The Read and Write handles, respectively, are used to
// 1) Copy from GPU service memory into client-visible shared memory.
// 2) Copy from client-visible shared memory into GPU service memory.
bool DeserializeReadHandle(const void* deserialize_pointer,
size_t deserialize_size,
ReadHandle** read_handle) override;
bool DeserializeWriteHandle(const void* deserialize_pointer,
size_t deserialize_size,
WriteHandle** write_handle) override;
private:
CommonDecoder* decoder_;
};
} // namespace webgpu
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_SERVICE_DAWN_SERVICE_MEMORY_TRANSFER_SERVICE_H_
......@@ -18,6 +18,7 @@
#include "gpu/command_buffer/common/webgpu_cmd_format.h"
#include "gpu/command_buffer/common/webgpu_cmd_ids.h"
#include "gpu/command_buffer/service/command_buffer_service.h"
#include "gpu/command_buffer/service/dawn_service_memory_transfer_service.h"
#include "gpu/command_buffer/service/decoder_client.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_manager.h"
......@@ -335,6 +336,7 @@ class WebGPUDecoderImpl final : public WebGPUDecoder {
associated_shared_image_map_;
std::unique_ptr<WireServerCommandSerializer> wire_serializer_;
std::unique_ptr<DawnServiceMemoryTransferService> memory_transfer_service_;
std::unique_ptr<dawn_native::Instance> dawn_instance_;
DawnProcTable dawn_procs_;
DawnDevice dawn_device_ = nullptr;
......@@ -377,6 +379,7 @@ WebGPUDecoderImpl::WebGPUDecoderImpl(
shared_image_manager,
memory_tracker)),
wire_serializer_(new WireServerCommandSerializer(client)),
memory_transfer_service_(new DawnServiceMemoryTransferService(this)),
dawn_instance_(new dawn_native::Instance()),
dawn_procs_(dawn_native::GetProcs()) {}
......@@ -401,6 +404,7 @@ ContextResult WebGPUDecoderImpl::Initialize() {
descriptor.device = dawn_device_;
descriptor.procs = &dawn_procs_;
descriptor.serializer = wire_serializer_.get();
descriptor.memoryTransferService = memory_transfer_service_.get();
wire_server_ = std::make_unique<dawn_wire::WireServer>(descriptor);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment