Add MemoryTransfer interfaces to the wire

This patch adds MemoryTransfer client/server interfaces and
uses it to implement data transfers for buffer mapping.

This patch also provides a default "inline" implementation of
the MemoryTransfer which is used if the embedder does not
provide one on initialization.

Because implementations of MemoryTransfer perform their own
serialization, a skip_serialize option is added to WireCmd records.

Bug: dawn:156
Change-Id: I2fa035517628a3ad465b0bc18a6ffc477e2bd67f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8642
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng
2019-07-19 16:01:48 +00:00
committed by Commit Bot service account
parent 49aae0f3bd
commit 6a5418a760
25 changed files with 874 additions and 211 deletions

View File

@@ -23,7 +23,9 @@ namespace dawn_wire { namespace server {
auto* buffer = BufferObjects().Get(cmd.selfId);
DAWN_ASSERT(buffer != nullptr);
buffer->mappedData = nullptr;
// The buffer was unmapped. Clear the Read/WriteHandle.
buffer->readHandle = nullptr;
buffer->writeHandle = nullptr;
buffer->mapWriteState = BufferMapWriteState::Unmapped;
return true;
@@ -31,7 +33,9 @@ namespace dawn_wire { namespace server {
bool Server::DoBufferMapAsync(ObjectId bufferId,
uint32_t requestSerial,
bool isWrite) {
bool isWrite,
uint64_t handleCreateInfoLength,
const uint8_t* handleCreateInfo) {
// These requests are just forwarded to the buffer, with userdata containing what the
// client will require in the return command.
@@ -45,16 +49,44 @@ namespace dawn_wire { namespace server {
return false;
}
MapUserdata* userdata = new MapUserdata;
if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
// This is the size of data deserialized from the command stream, which must be
// CPU-addressable.
return false;
}
std::unique_ptr<MapUserdata> userdata = std::make_unique<MapUserdata>();
userdata->server = this;
userdata->buffer = ObjectHandle{bufferId, buffer->serial};
userdata->requestSerial = requestSerial;
userdata->isWrite = isWrite;
// The handle will point to the mapped memory or staging memory for the mapping.
// Store it on the map request.
if (isWrite) {
mProcs.bufferMapWriteAsync(buffer->handle, ForwardBufferMapWriteAsync, userdata);
// Deserialize metadata produced from the client to create a companion server handle.
MemoryTransferService::WriteHandle* writeHandle = nullptr;
if (!mMemoryTransferService->DeserializeWriteHandle(
handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
return false;
}
ASSERT(writeHandle != nullptr);
userdata->writeHandle =
std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
mProcs.bufferMapWriteAsync(buffer->handle, ForwardBufferMapWriteAsync,
userdata.release());
} else {
mProcs.bufferMapReadAsync(buffer->handle, ForwardBufferMapReadAsync, userdata);
// Deserialize metadata produced from the client to create a companion server handle.
MemoryTransferService::ReadHandle* readHandle = nullptr;
if (!mMemoryTransferService->DeserializeReadHandle(
handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &readHandle)) {
return false;
}
ASSERT(readHandle != nullptr);
userdata->readHandle = std::unique_ptr<MemoryTransferService::ReadHandle>(readHandle);
mProcs.bufferMapReadAsync(buffer->handle, ForwardBufferMapReadAsync,
userdata.release());
}
return true;
@@ -62,7 +94,15 @@ namespace dawn_wire { namespace server {
bool Server::DoDeviceCreateBufferMapped(DawnDevice device,
const DawnBufferDescriptor* descriptor,
ObjectHandle bufferResult) {
ObjectHandle bufferResult,
uint64_t handleCreateInfoLength,
const uint8_t* handleCreateInfo) {
if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
// This is the size of data deserialized from the command stream, which must be
// CPU-addressable.
return false;
}
auto* resultData = BufferObjects().Allocate(bufferResult.id);
if (resultData == nullptr) {
return false;
@@ -73,15 +113,29 @@ namespace dawn_wire { namespace server {
ASSERT(result.buffer != nullptr);
if (result.data == nullptr && result.dataLength != 0) {
// Non-zero dataLength but null data is used to indicate an allocation error.
// Don't return false because this is not fatal. result.buffer is an ErrorBuffer
// and subsequent operations will be errors.
// This should only happen when fuzzing with the Null backend.
resultData->mapWriteState = BufferMapWriteState::MapError;
} else {
// Deserialize metadata produced from the client to create a companion server handle.
MemoryTransferService::WriteHandle* writeHandle = nullptr;
if (!mMemoryTransferService->DeserializeWriteHandle(
handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
return false;
}
ASSERT(writeHandle != nullptr);
// Set the target of the WriteHandle to the mapped GPU memory.
writeHandle->SetTarget(result.data, result.dataLength);
// The buffer is mapped and has a valid mappedData pointer.
// The buffer may still be an error with fake staging data.
resultData->mapWriteState = BufferMapWriteState::Mapped;
resultData->writeHandle =
std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
}
resultData->handle = result.buffer;
resultData->mappedData = result.data;
resultData->mappedDataSize = result.dataLength;
return true;
}
@@ -89,8 +143,11 @@ namespace dawn_wire { namespace server {
bool Server::DoDeviceCreateBufferMappedAsync(DawnDevice device,
const DawnBufferDescriptor* descriptor,
uint32_t requestSerial,
ObjectHandle bufferResult) {
if (!DoDeviceCreateBufferMapped(device, descriptor, bufferResult)) {
ObjectHandle bufferResult,
uint64_t handleCreateInfoLength,
const uint8_t* handleCreateInfo) {
if (!DoDeviceCreateBufferMapped(device, descriptor, bufferResult, handleCreateInfoLength,
handleCreateInfo)) {
return false;
}
@@ -103,7 +160,6 @@ namespace dawn_wire { namespace server {
cmd.status = bufferData->mapWriteState == BufferMapWriteState::Mapped
? DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS
: DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR;
cmd.dataLength = bufferData->mappedDataSize;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
@@ -130,14 +186,20 @@ namespace dawn_wire { namespace server {
return true;
}
bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint32_t count, const uint8_t* data) {
bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
uint64_t writeFlushInfoLength,
const uint8_t* writeFlushInfo) {
// The null object isn't valid as `self`
if (bufferId == 0) {
return false;
}
if (writeFlushInfoLength > std::numeric_limits<size_t>::max()) {
return false;
}
auto* buffer = BufferObjects().Get(bufferId);
if (buffer == nullptr || buffer->mappedDataSize != count) {
if (buffer == nullptr) {
return false;
}
switch (buffer->mapWriteState) {
@@ -150,12 +212,15 @@ namespace dawn_wire { namespace server {
case BufferMapWriteState::Mapped:
break;
}
ASSERT(data != nullptr);
ASSERT(buffer->mappedData != nullptr);
memcpy(buffer->mappedData, data, count);
return true;
if (!buffer->writeHandle) {
// This check is performed after the check for the MapError state. It is permissible
// to Unmap and attempt to update mapped data of an error buffer.
return false;
}
// Deserialize the flush info and flush updated data from the handle into the target
// of the handle. The target is set via WriteHandle::SetTarget.
return buffer->writeHandle->DeserializeFlush(writeFlushInfo,
static_cast<size_t>(writeFlushInfoLength));
}
void Server::ForwardBufferMapReadAsync(DawnBufferMapAsyncStatus status,
@@ -186,20 +251,34 @@ namespace dawn_wire { namespace server {
return;
}
size_t initialDataInfoLength = 0;
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
// Get the serialization size of the message to initialize ReadHandle data.
initialDataInfoLength = data->readHandle->SerializeInitialData(ptr, dataLength);
} else {
dataLength = 0;
}
ReturnBufferMapReadAsyncCallbackCmd cmd;
cmd.buffer = data->buffer;
cmd.requestSerial = data->requestSerial;
cmd.status = status;
cmd.dataLength = 0;
cmd.data = static_cast<const uint8_t*>(ptr);
cmd.initialDataInfoLength = initialDataInfoLength;
cmd.initialDataInfo = nullptr;
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
cmd.dataLength = dataLength;
}
size_t requiredSize = cmd.GetRequiredSize();
size_t commandSize = cmd.GetRequiredSize();
size_t requiredSize = commandSize + initialDataInfoLength;
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
// Serialize the initialization message into the space after the command.
data->readHandle->SerializeInitialData(ptr, dataLength, allocatedBuffer + commandSize);
// The in-flight map request returned successfully.
// Move the ReadHandle so it is owned by the buffer.
bufferData->readHandle = std::move(data->readHandle);
}
}
void Server::OnBufferMapWriteAsyncCallback(DawnBufferMapAsyncStatus status,
@@ -218,16 +297,18 @@ namespace dawn_wire { namespace server {
cmd.buffer = data->buffer;
cmd.requestSerial = data->requestSerial;
cmd.status = status;
cmd.dataLength = dataLength;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
// The in-flight map request returned successfully.
// Move the WriteHandle so it is owned by the buffer.
bufferData->writeHandle = std::move(data->writeHandle);
bufferData->mapWriteState = BufferMapWriteState::Mapped;
bufferData->mappedData = ptr;
bufferData->mappedDataSize = dataLength;
// Set the target of the WriteHandle to the mapped buffer data.
bufferData->writeHandle->SetTarget(ptr, dataLength);
}
}