MapAsync update: keep read/write handle through lifetime of mappable buffers
Change dawn read/write handle for buffer mapping to be created at buffer creation time instead of at mapAsync time. Update related buffer mapping tests and wire tests. Bug: dawn:773 Change-Id: I7dd423c94e1bc15cfe561ea33ec9e348ddf2bfe0 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51164 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Shrek Shao <shrekshao@google.com>
This commit is contained in:
parent
15838b98af
commit
6e680fc56f
|
@ -20,24 +20,26 @@
|
||||||
"commands": {
|
"commands": {
|
||||||
"buffer map async": [
|
"buffer map async": [
|
||||||
{ "name": "buffer id", "type": "ObjectId" },
|
{ "name": "buffer id", "type": "ObjectId" },
|
||||||
{ "name": "request serial", "type": "uint32_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "mode", "type": "map mode" },
|
{ "name": "mode", "type": "map mode" },
|
||||||
{ "name": "offset", "type": "uint64_t"},
|
{ "name": "offset", "type": "uint64_t"},
|
||||||
{ "name": "size", "type": "uint64_t"},
|
{ "name": "size", "type": "uint64_t"}
|
||||||
{ "name": "handle create info length", "type": "uint64_t" },
|
|
||||||
{ "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
|
|
||||||
],
|
],
|
||||||
"buffer update mapped data": [
|
"buffer update mapped data": [
|
||||||
{ "name": "buffer id", "type": "ObjectId" },
|
{ "name": "buffer id", "type": "ObjectId" },
|
||||||
{ "name": "write flush info length", "type": "uint64_t" },
|
{ "name": "write data update info length", "type": "uint64_t" },
|
||||||
{ "name": "write flush info", "type": "uint8_t", "annotation": "const*", "length": "write flush info length", "skip_serialize": true}
|
{ "name": "write data update info", "type": "uint8_t", "annotation": "const*", "length": "write data update info length", "skip_serialize": true},
|
||||||
|
{ "name": "offset", "type": "uint64_t"},
|
||||||
|
{ "name": "size", "type": "uint64_t"}
|
||||||
],
|
],
|
||||||
"device create buffer": [
|
"device create buffer": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId" },
|
||||||
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
|
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
|
||||||
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
|
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
|
||||||
{ "name": "handle create info length", "type": "uint64_t" },
|
{ "name": "read handle create info length", "type": "uint64_t" },
|
||||||
{ "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
|
{ "name": "read handle create info", "type": "uint8_t", "annotation": "const*", "length": "read handle create info length", "skip_serialize": true},
|
||||||
|
{ "name": "write handle create info length", "type": "uint64_t" },
|
||||||
|
{ "name": "write handle create info", "type": "uint8_t", "annotation": "const*", "length": "write handle create info length", "skip_serialize": true}
|
||||||
],
|
],
|
||||||
"device create compute pipeline async": [
|
"device create compute pipeline async": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId" },
|
||||||
|
@ -87,10 +89,10 @@
|
||||||
"return commands": {
|
"return commands": {
|
||||||
"buffer map async callback": [
|
"buffer map async callback": [
|
||||||
{ "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" },
|
{ "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" },
|
||||||
{ "name": "request serial", "type": "uint32_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "status", "type": "uint32_t" },
|
{ "name": "status", "type": "uint32_t" },
|
||||||
{ "name": "read initial data info length", "type": "uint64_t" },
|
{ "name": "read data update info length", "type": "uint64_t" },
|
||||||
{ "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true }
|
{ "name": "read data update info", "type": "uint8_t", "annotation": "const*", "length": "read data update info length", "skip_serialize": true }
|
||||||
],
|
],
|
||||||
"device create compute pipeline async callback": [
|
"device create compute pipeline async callback": [
|
||||||
{ "name": "device", "type": "ObjectHandle", "handle_type": "device" },
|
{ "name": "device", "type": "ObjectHandle", "handle_type": "device" },
|
||||||
|
|
|
@ -68,8 +68,10 @@ namespace dawn_wire {
|
||||||
|
|
||||||
MemoryTransferService::WriteHandle::~WriteHandle() = default;
|
MemoryTransferService::WriteHandle::~WriteHandle() = default;
|
||||||
|
|
||||||
void MemoryTransferService::WriteHandle::SetTarget(void* data, size_t dataLength) {
|
void MemoryTransferService::WriteHandle::SetTarget(void* data) {
|
||||||
mTargetData = data;
|
mTargetData = data;
|
||||||
|
}
|
||||||
|
void MemoryTransferService::WriteHandle::SetDataLength(size_t dataLength) {
|
||||||
mDataLength = dataLength;
|
mDataLength = dataLength;
|
||||||
}
|
}
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -33,64 +33,93 @@ namespace dawn_wire { namespace client {
|
||||||
return device->CreateErrorBuffer();
|
return device->CreateErrorBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
||||||
void* writeData = nullptr;
|
|
||||||
size_t writeHandleCreateInfoLength = 0;
|
|
||||||
|
|
||||||
// If the buffer is mapped at creation, create a write handle that will represent the
|
DeviceCreateBufferCmd cmd;
|
||||||
// mapping of the whole buffer.
|
cmd.deviceId = device->id;
|
||||||
if (descriptor->mappedAtCreation) {
|
cmd.descriptor = descriptor;
|
||||||
// Create the handle.
|
cmd.readHandleCreateInfoLength = 0;
|
||||||
|
cmd.readHandleCreateInfo = nullptr;
|
||||||
|
cmd.writeHandleCreateInfoLength = 0;
|
||||||
|
cmd.writeHandleCreateInfo = nullptr;
|
||||||
|
|
||||||
|
if (mappable) {
|
||||||
|
if ((descriptor->usage & WGPUBufferUsage_MapRead) != 0) {
|
||||||
|
// Create the read handle on buffer creation.
|
||||||
|
readHandle.reset(
|
||||||
|
wireClient->GetMemoryTransferService()->CreateReadHandle(descriptor->size));
|
||||||
|
if (readHandle == nullptr) {
|
||||||
|
device->InjectError(WGPUErrorType_OutOfMemory,
|
||||||
|
"Failed to create buffer mapping");
|
||||||
|
return device->CreateErrorBuffer();
|
||||||
|
}
|
||||||
|
cmd.readHandleCreateInfoLength = readHandle->SerializeCreateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((descriptor->usage & WGPUBufferUsage_MapWrite) != 0 ||
|
||||||
|
descriptor->mappedAtCreation) {
|
||||||
|
// Create the write handle on buffer creation.
|
||||||
writeHandle.reset(
|
writeHandle.reset(
|
||||||
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
|
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
|
||||||
if (writeHandle == nullptr) {
|
if (writeHandle == nullptr) {
|
||||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
device->InjectError(WGPUErrorType_OutOfMemory,
|
||||||
|
"Failed to create buffer mapping");
|
||||||
return device->CreateErrorBuffer();
|
return device->CreateErrorBuffer();
|
||||||
}
|
}
|
||||||
|
cmd.writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
|
||||||
// Open the handle, it may fail by returning a nullptr in writeData.
|
|
||||||
size_t writeDataLength = 0;
|
|
||||||
std::tie(writeData, writeDataLength) = writeHandle->Open();
|
|
||||||
if (writeData == nullptr) {
|
|
||||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
|
||||||
return device->CreateErrorBuffer();
|
|
||||||
}
|
}
|
||||||
ASSERT(writeDataLength == descriptor->size);
|
|
||||||
|
|
||||||
// Get the serialization size of the write handle.
|
|
||||||
writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the buffer and send the creation command.
|
// Create the buffer and send the creation command.
|
||||||
|
// This must happen after any potential device->CreateErrorBuffer()
|
||||||
|
// as server expects allocating ids to be monotonically increasing
|
||||||
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
|
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
|
||||||
Buffer* buffer = bufferObjectAndSerial->object.get();
|
Buffer* buffer = bufferObjectAndSerial->object.get();
|
||||||
buffer->mDevice = device;
|
buffer->mDevice = device;
|
||||||
buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
|
buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
|
||||||
buffer->mSize = descriptor->size;
|
buffer->mSize = descriptor->size;
|
||||||
|
buffer->mDestructWriteHandleOnUnmap = false;
|
||||||
|
|
||||||
DeviceCreateBufferCmd cmd;
|
|
||||||
cmd.deviceId = device->id;
|
|
||||||
cmd.descriptor = descriptor;
|
|
||||||
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
|
|
||||||
cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
|
|
||||||
cmd.handleCreateInfo = nullptr;
|
|
||||||
|
|
||||||
wireClient->SerializeCommand(
|
|
||||||
cmd, writeHandleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
|
||||||
if (descriptor->mappedAtCreation) {
|
if (descriptor->mappedAtCreation) {
|
||||||
char* writeHandleBuffer;
|
// If the buffer is mapped at creation, a write handle is created and will be
|
||||||
WIRE_TRY(
|
// destructed on unmap if the buffer doesn't have MapWrite usage
|
||||||
serializeBuffer->NextN(writeHandleCreateInfoLength, &writeHandleBuffer));
|
// The buffer is mapped right now.
|
||||||
// Serialize the WriteHandle into the space after the command.
|
buffer->mMapState = MapState::MappedAtCreation;
|
||||||
writeHandle->SerializeCreate(writeHandleBuffer);
|
|
||||||
|
// This flag is for write handle created by mappedAtCreation
|
||||||
|
// instead of MapWrite usage. We don't have such a case for read handle
|
||||||
|
buffer->mDestructWriteHandleOnUnmap =
|
||||||
|
(descriptor->usage & WGPUBufferUsage_MapWrite) == 0;
|
||||||
|
|
||||||
// Set the buffer state for the mapping at creation. The buffer now owns the
|
|
||||||
// write handle..
|
|
||||||
buffer->mWriteHandle = std::move(writeHandle);
|
|
||||||
buffer->mMappedData = writeData;
|
|
||||||
buffer->mMapOffset = 0;
|
buffer->mMapOffset = 0;
|
||||||
buffer->mMapSize = buffer->mSize;
|
buffer->mMapSize = buffer->mSize;
|
||||||
|
ASSERT(writeHandle != nullptr);
|
||||||
|
buffer->mMappedData = writeHandle->GetData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
|
||||||
|
|
||||||
|
wireClient->SerializeCommand(
|
||||||
|
cmd, cmd.readHandleCreateInfoLength + cmd.writeHandleCreateInfoLength,
|
||||||
|
[&](SerializeBuffer* serializeBuffer) {
|
||||||
|
if (readHandle != nullptr) {
|
||||||
|
char* readHandleBuffer;
|
||||||
|
WIRE_TRY(
|
||||||
|
serializeBuffer->NextN(cmd.readHandleCreateInfoLength, &readHandleBuffer));
|
||||||
|
// Serialize the ReadHandle into the space after the command.
|
||||||
|
readHandle->SerializeCreate(readHandleBuffer);
|
||||||
|
buffer->mReadHandle = std::move(readHandle);
|
||||||
|
}
|
||||||
|
if (writeHandle != nullptr) {
|
||||||
|
char* writeHandleBuffer;
|
||||||
|
WIRE_TRY(serializeBuffer->NextN(cmd.writeHandleCreateInfoLength,
|
||||||
|
&writeHandleBuffer));
|
||||||
|
// Serialize the WriteHandle into the space after the command.
|
||||||
|
writeHandle->SerializeCreate(writeHandleBuffer);
|
||||||
|
buffer->mWriteHandle = std::move(writeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
return WireResult::Success;
|
return WireResult::Success;
|
||||||
});
|
});
|
||||||
return ToAPI(buffer);
|
return ToAPI(buffer);
|
||||||
|
@ -120,7 +149,7 @@ namespace dawn_wire { namespace client {
|
||||||
}
|
}
|
||||||
mRequests.clear();
|
mRequests.clear();
|
||||||
|
|
||||||
FreeMappedData(true);
|
FreeMappedData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::CancelCallbacksForDisconnect() {
|
void Buffer::CancelCallbacksForDisconnect() {
|
||||||
|
@ -146,97 +175,41 @@ namespace dawn_wire { namespace client {
|
||||||
size = mSize - offset;
|
size = mSize - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isReadMode = mode & WGPUMapMode_Read;
|
// Create the request structure that will hold information while this mapping is
|
||||||
bool isWriteMode = mode & WGPUMapMode_Write;
|
|
||||||
|
|
||||||
// Step 1. Do early validation of READ ^ WRITE because the server rejects mode = 0.
|
|
||||||
if (!(isReadMode ^ isWriteMode)) {
|
|
||||||
if (!mDeviceIsAlive.expired()) {
|
|
||||||
mDevice->InjectError(WGPUErrorType_Validation,
|
|
||||||
"MapAsync mode must be exactly one of Read or Write");
|
|
||||||
}
|
|
||||||
if (callback != nullptr) {
|
|
||||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2. Create the request structure that will hold information while this mapping is
|
|
||||||
// in flight.
|
// in flight.
|
||||||
uint32_t serial = mRequestSerial++;
|
uint64_t serial = mRequestSerial++;
|
||||||
ASSERT(mRequests.find(serial) == mRequests.end());
|
ASSERT(mRequests.find(serial) == mRequests.end());
|
||||||
|
|
||||||
Buffer::MapRequestData request = {};
|
Buffer::MapRequestData request = {};
|
||||||
request.callback = callback;
|
request.callback = callback;
|
||||||
request.userdata = userdata;
|
request.userdata = userdata;
|
||||||
request.size = size;
|
|
||||||
request.offset = offset;
|
request.offset = offset;
|
||||||
|
request.size = size;
|
||||||
// Step 2a: Create the read / write handles for this request.
|
if (mode & WGPUMapMode_Read) {
|
||||||
if (isReadMode) {
|
request.type = MapRequestType::Read;
|
||||||
request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
|
} else if (mode & WGPUMapMode_Write) {
|
||||||
if (request.readHandle == nullptr) {
|
request.type = MapRequestType::Write;
|
||||||
if (!mDeviceIsAlive.expired()) {
|
|
||||||
mDevice->InjectError(WGPUErrorType_OutOfMemory,
|
|
||||||
"Failed to create buffer mapping");
|
|
||||||
}
|
|
||||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ASSERT(isWriteMode);
|
|
||||||
request.writeHandle.reset(client->GetMemoryTransferService()->CreateWriteHandle(size));
|
|
||||||
if (request.writeHandle == nullptr) {
|
|
||||||
if (!mDeviceIsAlive.expired()) {
|
|
||||||
mDevice->InjectError(WGPUErrorType_OutOfMemory,
|
|
||||||
"Failed to create buffer mapping");
|
|
||||||
}
|
|
||||||
callback(WGPUBufferMapAsyncStatus_Error, userdata);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3. Serialize the command to send to the server.
|
// Serialize the command to send to the server.
|
||||||
BufferMapAsyncCmd cmd;
|
BufferMapAsyncCmd cmd;
|
||||||
cmd.bufferId = this->id;
|
cmd.bufferId = this->id;
|
||||||
cmd.requestSerial = serial;
|
cmd.requestSerial = serial;
|
||||||
cmd.mode = mode;
|
cmd.mode = mode;
|
||||||
cmd.offset = offset;
|
cmd.offset = offset;
|
||||||
cmd.size = size;
|
cmd.size = size;
|
||||||
cmd.handleCreateInfo = nullptr;
|
|
||||||
|
|
||||||
// Step 3a. Fill the handle create info in the command.
|
client->SerializeCommand(cmd);
|
||||||
if (isReadMode) {
|
|
||||||
cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
|
|
||||||
client->SerializeCommand(
|
|
||||||
cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
|
||||||
char* readHandleBuffer;
|
|
||||||
WIRE_TRY(serializeBuffer->NextN(cmd.handleCreateInfoLength, &readHandleBuffer));
|
|
||||||
request.readHandle->SerializeCreate(readHandleBuffer);
|
|
||||||
return WireResult::Success;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ASSERT(isWriteMode);
|
|
||||||
cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
|
|
||||||
client->SerializeCommand(
|
|
||||||
cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
|
||||||
char* writeHandleBuffer;
|
|
||||||
WIRE_TRY(
|
|
||||||
serializeBuffer->NextN(cmd.handleCreateInfoLength, &writeHandleBuffer));
|
|
||||||
request.writeHandle->SerializeCreate(writeHandleBuffer);
|
|
||||||
return WireResult::Success;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4. Register this request so that we can retrieve it from its serial when the server
|
// Register this request so that we can retrieve it from its serial when the server
|
||||||
// sends the callback.
|
// sends the callback.
|
||||||
mRequests[serial] = std::move(request);
|
mRequests[serial] = std::move(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Buffer::OnMapAsyncCallback(uint32_t requestSerial,
|
bool Buffer::OnMapAsyncCallback(uint64_t requestSerial,
|
||||||
uint32_t status,
|
uint32_t status,
|
||||||
uint64_t readInitialDataInfoLength,
|
uint64_t readDataUpdateInfoLength,
|
||||||
const uint8_t* readInitialDataInfo) {
|
const uint8_t* readDataUpdateInfo) {
|
||||||
auto requestIt = mRequests.find(requestSerial);
|
auto requestIt = mRequests.find(requestSerial);
|
||||||
if (requestIt == mRequests.end()) {
|
if (requestIt == mRequests.end()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -254,60 +227,50 @@ namespace dawn_wire { namespace client {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isRead = request.readHandle != nullptr;
|
|
||||||
bool isWrite = request.writeHandle != nullptr;
|
|
||||||
ASSERT(isRead != isWrite);
|
|
||||||
|
|
||||||
// Take into account the client-side status of the request if the server says it is a success.
|
// Take into account the client-side status of the request if the server says it is a success.
|
||||||
if (status == WGPUBufferMapAsyncStatus_Success) {
|
if (status == WGPUBufferMapAsyncStatus_Success) {
|
||||||
status = request.clientStatus;
|
status = request.clientStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t mappedDataLength = 0;
|
|
||||||
const void* mappedData = nullptr;
|
|
||||||
if (status == WGPUBufferMapAsyncStatus_Success) {
|
if (status == WGPUBufferMapAsyncStatus_Success) {
|
||||||
if (mReadHandle || mWriteHandle) {
|
switch (request.type) {
|
||||||
// Buffer is already mapped.
|
case MapRequestType::Read: {
|
||||||
|
if (readDataUpdateInfoLength > std::numeric_limits<size_t>::max()) {
|
||||||
|
// This is the size of data deserialized from the command stream, which must
|
||||||
|
// be CPU-addressable.
|
||||||
return FailRequest();
|
return FailRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRead) {
|
// Validate to prevent bad map request; buffer destroyed during map request
|
||||||
if (readInitialDataInfoLength > std::numeric_limits<size_t>::max()) {
|
if (mReadHandle == nullptr) {
|
||||||
// This is the size of data deserialized from the command stream, which must be
|
|
||||||
// CPU-addressable.
|
|
||||||
return FailRequest();
|
return FailRequest();
|
||||||
}
|
}
|
||||||
|
// Update user map data with server returned data
|
||||||
// The server serializes metadata to initialize the contents of the ReadHandle.
|
if (!mReadHandle->DeserializeDataUpdate(
|
||||||
// Deserialize the message and return a pointer and size of the mapped data for
|
readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
|
||||||
// reading.
|
request.offset, request.size)) {
|
||||||
if (!request.readHandle->DeserializeInitialData(
|
|
||||||
readInitialDataInfo, static_cast<size_t>(readInitialDataInfoLength),
|
|
||||||
&mappedData, &mappedDataLength)) {
|
|
||||||
// Deserialization shouldn't fail. This is a fatal error.
|
|
||||||
return FailRequest();
|
return FailRequest();
|
||||||
}
|
}
|
||||||
ASSERT(mappedData != nullptr);
|
mMapState = MapState::MappedForRead;
|
||||||
|
mMappedData = const_cast<void*>(mReadHandle->GetData());
|
||||||
} else {
|
break;
|
||||||
// Open the WriteHandle. This returns a pointer and size of mapped memory.
|
}
|
||||||
// On failure, |mappedData| may be null.
|
case MapRequestType::Write: {
|
||||||
std::tie(mappedData, mappedDataLength) = request.writeHandle->Open();
|
if (mWriteHandle == nullptr) {
|
||||||
|
|
||||||
if (mappedData == nullptr) {
|
|
||||||
return FailRequest();
|
return FailRequest();
|
||||||
}
|
}
|
||||||
|
mMapState = MapState::MappedForWrite;
|
||||||
|
mMappedData = mWriteHandle->GetData();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// The MapAsync request was successful. The buffer now owns the Read/Write handles
|
UNREACHABLE();
|
||||||
// until Unmap().
|
|
||||||
mReadHandle = std::move(request.readHandle);
|
|
||||||
mWriteHandle = std::move(request.writeHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mMapOffset = request.offset;
|
mMapOffset = request.offset;
|
||||||
mMapSize = request.size;
|
mMapSize = request.size;
|
||||||
mMappedData = const_cast<void*>(mappedData);
|
}
|
||||||
|
|
||||||
if (request.callback) {
|
if (request.callback) {
|
||||||
request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
|
request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
|
||||||
}
|
}
|
||||||
|
@ -319,7 +282,7 @@ namespace dawn_wire { namespace client {
|
||||||
if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
|
if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
|
return static_cast<uint8_t*>(mMappedData) + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* Buffer::GetConstMappedRange(size_t offset, size_t size) {
|
const void* Buffer::GetConstMappedRange(size_t offset, size_t size) {
|
||||||
|
@ -327,7 +290,7 @@ namespace dawn_wire { namespace client {
|
||||||
!CheckGetMappedRangeOffsetSize(offset, size)) {
|
!CheckGetMappedRangeOffsetSize(offset, size)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
|
return static_cast<uint8_t*>(mMappedData) + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::Unmap() {
|
void Buffer::Unmap() {
|
||||||
|
@ -339,32 +302,57 @@ namespace dawn_wire { namespace client {
|
||||||
// - Server -> Client: Result of MapRequest1
|
// - Server -> Client: Result of MapRequest1
|
||||||
// - Unmap locally on the client
|
// - Unmap locally on the client
|
||||||
// - Server -> Client: Result of MapRequest2
|
// - Server -> Client: Result of MapRequest2
|
||||||
if (mWriteHandle) {
|
|
||||||
|
// TODO(dawn:608): mDevice->InjectError(WGPUErrorType_Validation) for map oom failure
|
||||||
|
// and separate from buffer destroyed before unmap case
|
||||||
|
|
||||||
|
// mWriteHandle can still be nullptr if buffer has been destroyed before unmap
|
||||||
|
if ((mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation) &&
|
||||||
|
mWriteHandle != nullptr) {
|
||||||
// Writes need to be flushed before Unmap is sent. Unmap calls all associated
|
// Writes need to be flushed before Unmap is sent. Unmap calls all associated
|
||||||
// in-flight callbacks which may read the updated data.
|
// in-flight callbacks which may read the updated data.
|
||||||
ASSERT(mReadHandle == nullptr);
|
|
||||||
|
|
||||||
// Get the serialization size of metadata to flush writes.
|
// Get the serialization size of data update writes.
|
||||||
size_t writeFlushInfoLength = mWriteHandle->SerializeFlushSize();
|
size_t writeDataUpdateInfoLength =
|
||||||
|
mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
|
||||||
|
|
||||||
BufferUpdateMappedDataCmd cmd;
|
BufferUpdateMappedDataCmd cmd;
|
||||||
cmd.bufferId = id;
|
cmd.bufferId = id;
|
||||||
cmd.writeFlushInfoLength = writeFlushInfoLength;
|
cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
|
||||||
cmd.writeFlushInfo = nullptr;
|
cmd.writeDataUpdateInfo = nullptr;
|
||||||
|
cmd.offset = mMapOffset;
|
||||||
|
cmd.size = mMapSize;
|
||||||
|
|
||||||
client->SerializeCommand(
|
client->SerializeCommand(
|
||||||
cmd, writeFlushInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
cmd, writeDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
||||||
char* writeHandleBuffer;
|
char* writeHandleBuffer;
|
||||||
WIRE_TRY(serializeBuffer->NextN(writeFlushInfoLength, &writeHandleBuffer));
|
WIRE_TRY(serializeBuffer->NextN(writeDataUpdateInfoLength, &writeHandleBuffer));
|
||||||
|
|
||||||
// Serialize flush metadata into the space after the command.
|
// Serialize flush metadata into the space after the command.
|
||||||
// This closes the handle for writing.
|
// This closes the handle for writing.
|
||||||
mWriteHandle->SerializeFlush(writeHandleBuffer);
|
mWriteHandle->SerializeDataUpdate(writeHandleBuffer, cmd.offset, cmd.size);
|
||||||
|
|
||||||
return WireResult::Success;
|
return WireResult::Success;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If mDestructWriteHandleOnUnmap is true, that means the write handle is merely
|
||||||
|
// for mappedAtCreation usage. It is destroyed on unmap after flush to server
|
||||||
|
// instead of at buffer destruction.
|
||||||
|
if (mMapState == MapState::MappedAtCreation && mDestructWriteHandleOnUnmap) {
|
||||||
|
mWriteHandle = nullptr;
|
||||||
|
if (mReadHandle) {
|
||||||
|
// If it's both mappedAtCreation and MapRead we need to reset
|
||||||
|
// mMappedData to readHandle's GetData(). This could be changed to
|
||||||
|
// merging read/write handle in future
|
||||||
|
mMappedData = const_cast<void*>(mReadHandle->GetData());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeMappedData(false);
|
// Free map access tokens
|
||||||
|
mMapState = MapState::Unmapped;
|
||||||
|
mMapOffset = 0;
|
||||||
|
mMapSize = 0;
|
||||||
|
|
||||||
// Tag all mapping requests still in flight as unmapped before callback.
|
// Tag all mapping requests still in flight as unmapped before callback.
|
||||||
for (auto& it : mRequests) {
|
for (auto& it : mRequests) {
|
||||||
|
@ -376,11 +364,13 @@ namespace dawn_wire { namespace client {
|
||||||
BufferUnmapCmd cmd;
|
BufferUnmapCmd cmd;
|
||||||
cmd.self = ToAPI(this);
|
cmd.self = ToAPI(this);
|
||||||
client->SerializeCommand(cmd);
|
client->SerializeCommand(cmd);
|
||||||
|
// TODO(dawn:608): add tracking mapped status and change to return bool for returning unmap
|
||||||
|
// success/failure
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::Destroy() {
|
void Buffer::Destroy() {
|
||||||
// Remove the current mapping.
|
// Remove the current mapping and destroy Read/WriteHandles.
|
||||||
FreeMappedData(true);
|
FreeMappedData();
|
||||||
|
|
||||||
// Tag all mapping requests still in flight as destroyed before callback.
|
// Tag all mapping requests still in flight as destroyed before callback.
|
||||||
for (auto& it : mRequests) {
|
for (auto& it : mRequests) {
|
||||||
|
@ -395,11 +385,11 @@ namespace dawn_wire { namespace client {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Buffer::IsMappedForReading() const {
|
bool Buffer::IsMappedForReading() const {
|
||||||
return mReadHandle != nullptr;
|
return mMapState == MapState::MappedForRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Buffer::IsMappedForWriting() const {
|
bool Buffer::IsMappedForWriting() const {
|
||||||
return mWriteHandle != nullptr;
|
return mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
|
bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
|
||||||
|
@ -415,20 +405,20 @@ namespace dawn_wire { namespace client {
|
||||||
return offsetInMappedRange <= mMapSize - size;
|
return offsetInMappedRange <= mMapSize - size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::FreeMappedData(bool destruction) {
|
void Buffer::FreeMappedData() {
|
||||||
#if defined(DAWN_ENABLE_ASSERTS)
|
#if defined(DAWN_ENABLE_ASSERTS)
|
||||||
// When in "debug" mode, 0xCA-out the mapped data when we free it so that in we can detect
|
// When in "debug" mode, 0xCA-out the mapped data when we free it so that in we can detect
|
||||||
// use-after-free of the mapped data. This is particularly useful for WebGPU test about the
|
// use-after-free of the mapped data. This is particularly useful for WebGPU test about the
|
||||||
// interaction of mapping and GC.
|
// interaction of mapping and GC.
|
||||||
if (mMappedData && destruction) {
|
if (mMappedData) {
|
||||||
memset(mMappedData, 0xCA, mMapSize);
|
memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
|
||||||
}
|
}
|
||||||
#endif // defined(DAWN_ENABLE_ASSERTS)
|
#endif // defined(DAWN_ENABLE_ASSERTS)
|
||||||
|
|
||||||
mMapOffset = 0;
|
mMapOffset = 0;
|
||||||
mMapSize = 0;
|
mMapSize = 0;
|
||||||
mWriteHandle = nullptr;
|
|
||||||
mReadHandle = nullptr;
|
mReadHandle = nullptr;
|
||||||
|
mWriteHandle = nullptr;
|
||||||
mMappedData = nullptr;
|
mMappedData = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@ namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
~Buffer();
|
~Buffer();
|
||||||
|
|
||||||
bool OnMapAsyncCallback(uint32_t requestSerial,
|
bool OnMapAsyncCallback(uint64_t requestSerial,
|
||||||
uint32_t status,
|
uint32_t status,
|
||||||
uint64_t readInitialDataInfoLength,
|
uint64_t readDataUpdateInfoLength,
|
||||||
const uint8_t* readInitialDataInfo);
|
const uint8_t* readDataUpdateInfo);
|
||||||
void MapAsync(WGPUMapModeFlags mode,
|
void MapAsync(WGPUMapModeFlags mode,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
size_t size,
|
size_t size,
|
||||||
|
@ -57,10 +57,19 @@ namespace dawn_wire { namespace client {
|
||||||
bool IsMappedForWriting() const;
|
bool IsMappedForWriting() const;
|
||||||
bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
|
bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
|
||||||
|
|
||||||
void FreeMappedData(bool destruction);
|
void FreeMappedData();
|
||||||
|
|
||||||
Device* mDevice;
|
Device* mDevice;
|
||||||
|
|
||||||
|
enum class MapRequestType { None, Read, Write };
|
||||||
|
|
||||||
|
enum class MapState {
|
||||||
|
Unmapped,
|
||||||
|
MappedForRead,
|
||||||
|
MappedForWrite,
|
||||||
|
MappedAtCreation,
|
||||||
|
};
|
||||||
|
|
||||||
// We want to defer all the validation to the server, which means we could have multiple
|
// We want to defer all the validation to the server, which means we could have multiple
|
||||||
// map request in flight at a single time and need to track them separately.
|
// map request in flight at a single time and need to track them separately.
|
||||||
// On well-behaved applications, only one request should exist at a single time.
|
// On well-behaved applications, only one request should exist at a single time.
|
||||||
|
@ -75,12 +84,10 @@ namespace dawn_wire { namespace client {
|
||||||
// from the server take precedence over the client-side status.
|
// from the server take precedence over the client-side status.
|
||||||
WGPUBufferMapAsyncStatus clientStatus = WGPUBufferMapAsyncStatus_Success;
|
WGPUBufferMapAsyncStatus clientStatus = WGPUBufferMapAsyncStatus_Success;
|
||||||
|
|
||||||
// TODO(enga): Use a tagged pointer to save space.
|
MapRequestType type = MapRequestType::None;
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
|
||||||
};
|
};
|
||||||
std::map<uint32_t, MapRequestData> mRequests;
|
std::map<uint64_t, MapRequestData> mRequests;
|
||||||
uint32_t mRequestSerial = 0;
|
uint64_t mRequestSerial = 0;
|
||||||
uint64_t mSize = 0;
|
uint64_t mSize = 0;
|
||||||
|
|
||||||
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
|
// Only one mapped pointer can be active at a time because Unmap clears all the in-flight
|
||||||
|
@ -88,6 +95,9 @@ namespace dawn_wire { namespace client {
|
||||||
// TODO(enga): Use a tagged pointer to save space.
|
// TODO(enga): Use a tagged pointer to save space.
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
|
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
|
||||||
|
MapState mMapState = MapState::Unmapped;
|
||||||
|
bool mDestructWriteHandleOnUnmap = false;
|
||||||
|
|
||||||
void* mMappedData = nullptr;
|
void* mMappedData = nullptr;
|
||||||
size_t mMapOffset = 0;
|
size_t mMapOffset = 0;
|
||||||
size_t mMapSize = 0;
|
size_t mMapSize = 0;
|
||||||
|
|
|
@ -73,16 +73,16 @@ namespace dawn_wire { namespace client {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
|
bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
|
||||||
uint32_t requestSerial,
|
uint64_t requestSerial,
|
||||||
uint32_t status,
|
uint32_t status,
|
||||||
uint64_t readInitialDataInfoLength,
|
uint64_t readDataUpdateInfoLength,
|
||||||
const uint8_t* readInitialDataInfo) {
|
const uint8_t* readDataUpdateInfo) {
|
||||||
// The buffer might have been deleted or recreated so this isn't an error.
|
// The buffer might have been deleted or recreated so this isn't an error.
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
|
return buffer->OnMapAsyncCallback(requestSerial, status, readDataUpdateInfoLength,
|
||||||
readInitialDataInfo);
|
readDataUpdateInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoQueueWorkDoneCallback(Queue* queue,
|
bool Client::DoQueueWorkDoneCallback(Queue* queue,
|
||||||
|
|
|
@ -24,7 +24,8 @@ namespace dawn_wire { namespace client {
|
||||||
class InlineMemoryTransferService : public MemoryTransferService {
|
class InlineMemoryTransferService : public MemoryTransferService {
|
||||||
class ReadHandleImpl : public ReadHandle {
|
class ReadHandleImpl : public ReadHandle {
|
||||||
public:
|
public:
|
||||||
explicit ReadHandleImpl(size_t size) : mSize(size) {
|
explicit ReadHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
|
||||||
|
: mStagingData(std::move(stagingData)), mSize(size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~ReadHandleImpl() override = default;
|
~ReadHandleImpl() override = default;
|
||||||
|
@ -36,36 +37,36 @@ namespace dawn_wire { namespace client {
|
||||||
void SerializeCreate(void*) override {
|
void SerializeCreate(void*) override {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeserializeInitialData(const void* deserializePointer,
|
const void* GetData() override {
|
||||||
|
return mStagingData.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
const void** data,
|
size_t offset,
|
||||||
size_t* dataLength) override {
|
size_t size) override {
|
||||||
if (deserializeSize != mSize || deserializePointer == nullptr) {
|
if (deserializeSize != size || deserializePointer == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
|
if (offset > mSize || size > mSize - offset) {
|
||||||
if (!mStagingData) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(mStagingData.get(), deserializePointer, mSize);
|
|
||||||
|
|
||||||
ASSERT(data != nullptr);
|
|
||||||
ASSERT(dataLength != nullptr);
|
|
||||||
*data = mStagingData.get();
|
|
||||||
*dataLength = mSize;
|
|
||||||
|
|
||||||
|
void* start = static_cast<uint8_t*>(mStagingData.get()) + offset;
|
||||||
|
memcpy(start, deserializePointer, size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t mSize;
|
|
||||||
std::unique_ptr<uint8_t[]> mStagingData;
|
std::unique_ptr<uint8_t[]> mStagingData;
|
||||||
|
size_t mSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WriteHandleImpl : public WriteHandle {
|
class WriteHandleImpl : public WriteHandle {
|
||||||
public:
|
public:
|
||||||
explicit WriteHandleImpl(size_t size) : mSize(size) {
|
explicit WriteHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
|
||||||
|
: mStagingData(std::move(stagingData)), mSize(size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~WriteHandleImpl() override = default;
|
~WriteHandleImpl() override = default;
|
||||||
|
@ -77,28 +78,27 @@ namespace dawn_wire { namespace client {
|
||||||
void SerializeCreate(void*) override {
|
void SerializeCreate(void*) override {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<void*, size_t> Open() override {
|
void* GetData() override {
|
||||||
mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
|
return mStagingData.get();
|
||||||
if (!mStagingData) {
|
|
||||||
return std::make_pair(nullptr, 0);
|
|
||||||
}
|
|
||||||
memset(mStagingData.get(), 0, mSize);
|
|
||||||
return std::make_pair(mStagingData.get(), mSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SerializeFlushSize() override {
|
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
|
||||||
return mSize;
|
ASSERT(offset <= mSize);
|
||||||
|
ASSERT(size <= mSize - offset);
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerializeFlush(void* serializePointer) override {
|
void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override {
|
||||||
ASSERT(mStagingData != nullptr);
|
ASSERT(mStagingData != nullptr);
|
||||||
ASSERT(serializePointer != nullptr);
|
ASSERT(serializePointer != nullptr);
|
||||||
memcpy(serializePointer, mStagingData.get(), mSize);
|
ASSERT(offset <= mSize);
|
||||||
|
ASSERT(size <= mSize - offset);
|
||||||
|
memcpy(serializePointer, static_cast<uint8_t*>(mStagingData.get()) + offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t mSize;
|
|
||||||
std::unique_ptr<uint8_t[]> mStagingData;
|
std::unique_ptr<uint8_t[]> mStagingData;
|
||||||
|
size_t mSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -107,11 +107,20 @@ namespace dawn_wire { namespace client {
|
||||||
~InlineMemoryTransferService() override = default;
|
~InlineMemoryTransferService() override = default;
|
||||||
|
|
||||||
ReadHandle* CreateReadHandle(size_t size) override {
|
ReadHandle* CreateReadHandle(size_t size) override {
|
||||||
return new ReadHandleImpl(size);
|
auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
|
||||||
|
if (stagingData) {
|
||||||
|
return new ReadHandleImpl(std::move(stagingData), size);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteHandle* CreateWriteHandle(size_t size) override {
|
WriteHandle* CreateWriteHandle(size_t size) override {
|
||||||
return new WriteHandleImpl(size);
|
auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
|
||||||
|
if (stagingData) {
|
||||||
|
memset(stagingData.get(), 0, size);
|
||||||
|
return new WriteHandleImpl(std::move(stagingData), size);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,15 +35,19 @@ namespace dawn_wire { namespace client {
|
||||||
mService->OnReadHandleSerializeCreate(this, serializePointer);
|
mService->OnReadHandleSerializeCreate(this, serializePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MockMemoryTransferService::MockReadHandle::DeserializeInitialData(
|
const void* MockMemoryTransferService::MockReadHandle::GetData() {
|
||||||
|
return mService->OnReadHandleGetData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MockMemoryTransferService::MockReadHandle::DeserializeDataUpdate(
|
||||||
const void* deserializePointer,
|
const void* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
const void** data,
|
size_t offset,
|
||||||
size_t* dataLength) {
|
size_t size) {
|
||||||
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
|
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
|
||||||
return mService->OnReadHandleDeserializeInitialData(
|
return mService->OnReadHandleDeserializeDataUpdate(
|
||||||
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, data,
|
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
|
||||||
dataLength);
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
||||||
|
@ -62,16 +66,19 @@ namespace dawn_wire { namespace client {
|
||||||
mService->OnWriteHandleSerializeCreate(this, serializePointer);
|
mService->OnWriteHandleSerializeCreate(this, serializePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<void*, size_t> MockMemoryTransferService::MockWriteHandle::Open() {
|
void* MockMemoryTransferService::MockWriteHandle::GetData() {
|
||||||
return mService->OnWriteHandleOpen(this);
|
return mService->OnWriteHandleGetData(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MockMemoryTransferService::MockWriteHandle::SerializeFlushSize() {
|
size_t MockMemoryTransferService::MockWriteHandle::SizeOfSerializeDataUpdate(size_t offset,
|
||||||
return mService->OnWriteHandleSerializeFlushSize(this);
|
size_t size) {
|
||||||
|
return mService->OnWriteHandleSizeOfSerializeDataUpdate(this, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MockMemoryTransferService::MockWriteHandle::SerializeFlush(void* serializePointer) {
|
void MockMemoryTransferService::MockWriteHandle::SerializeDataUpdate(void* serializePointer,
|
||||||
mService->OnWriteHandleSerializeFlush(this, serializePointer);
|
size_t offset,
|
||||||
|
size_t size) {
|
||||||
|
mService->OnWriteHandleSerializeDataUpdate(this, serializePointer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
MockMemoryTransferService::MockMemoryTransferService() = default;
|
MockMemoryTransferService::MockMemoryTransferService() = default;
|
||||||
|
|
|
@ -31,10 +31,11 @@ namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
size_t SerializeCreateSize() override;
|
size_t SerializeCreateSize() override;
|
||||||
void SerializeCreate(void* serializePointer) override;
|
void SerializeCreate(void* serializePointer) override;
|
||||||
bool DeserializeInitialData(const void* deserializePointer,
|
const void* GetData() override;
|
||||||
|
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
const void** data,
|
size_t offset,
|
||||||
size_t* dataLength) override;
|
size_t size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockMemoryTransferService* mService;
|
MockMemoryTransferService* mService;
|
||||||
|
@ -47,9 +48,9 @@ namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
size_t SerializeCreateSize() override;
|
size_t SerializeCreateSize() override;
|
||||||
void SerializeCreate(void* serializePointer) override;
|
void SerializeCreate(void* serializePointer) override;
|
||||||
std::pair<void*, size_t> Open() override;
|
void* GetData() override;
|
||||||
size_t SerializeFlushSize() override;
|
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
|
||||||
void SerializeFlush(void* serializePointer) override;
|
void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MockMemoryTransferService* mService;
|
MockMemoryTransferService* mService;
|
||||||
|
@ -69,24 +70,27 @@ namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
|
MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
|
||||||
MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
|
MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
|
||||||
|
MOCK_METHOD((const void*), OnReadHandleGetData, (const ReadHandle*));
|
||||||
MOCK_METHOD(bool,
|
MOCK_METHOD(bool,
|
||||||
OnReadHandleDeserializeInitialData,
|
OnReadHandleDeserializeDataUpdate,
|
||||||
(const ReadHandle*,
|
(const ReadHandle*,
|
||||||
const uint32_t* deserializePointer,
|
const uint32_t* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
const void** data,
|
size_t offset,
|
||||||
size_t* dataLength));
|
size_t size));
|
||||||
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
|
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
|
||||||
|
|
||||||
MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
|
MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
|
||||||
MOCK_METHOD(void,
|
MOCK_METHOD(void,
|
||||||
OnWriteHandleSerializeCreate,
|
OnWriteHandleSerializeCreate,
|
||||||
(const void* WriteHandle, void* serializePointer));
|
(const void* WriteHandle, void* serializePointer));
|
||||||
MOCK_METHOD((std::pair<void*, size_t>), OnWriteHandleOpen, (const void* WriteHandle));
|
MOCK_METHOD((void*), OnWriteHandleGetData, (const void* WriteHandle));
|
||||||
MOCK_METHOD(size_t, OnWriteHandleSerializeFlushSize, (const void* WriteHandle));
|
MOCK_METHOD(size_t,
|
||||||
MOCK_METHOD(void,
|
OnWriteHandleSizeOfSerializeDataUpdate,
|
||||||
OnWriteHandleSerializeFlush,
|
(const void* WriteHandle, size_t offset, size_t size));
|
||||||
(const void* WriteHandle, void* serializePointer));
|
MOCK_METHOD(size_t,
|
||||||
|
OnWriteHandleSerializeDataUpdate,
|
||||||
|
(const void* WriteHandle, void* serializePointer, size_t offset, size_t size));
|
||||||
MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle));
|
MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,9 @@ namespace dawn_wire { namespace server {
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle;
|
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle;
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle;
|
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle;
|
||||||
BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped;
|
BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped;
|
||||||
|
WGPUBufferUsageFlags usage = WGPUBufferUsage_None;
|
||||||
|
// Indicate if writeHandle needs to be destroyed on unmap
|
||||||
|
bool mappedAtCreation = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pack the ObjectType and ObjectId as a single value for storage in
|
// Pack the ObjectType and ObjectId as a single value for storage in
|
||||||
|
|
|
@ -111,13 +111,10 @@ namespace dawn_wire { namespace server {
|
||||||
|
|
||||||
ObjectHandle buffer;
|
ObjectHandle buffer;
|
||||||
WGPUBuffer bufferObj;
|
WGPUBuffer bufferObj;
|
||||||
uint32_t requestSerial;
|
uint64_t requestSerial;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
WGPUMapModeFlags mode;
|
WGPUMapModeFlags mode;
|
||||||
// TODO(enga): Use a tagged pointer to save space.
|
|
||||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
|
||||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ErrorScopeUserdata : CallbackUserdata {
|
struct ErrorScopeUserdata : CallbackUserdata {
|
||||||
|
|
|
@ -25,9 +25,13 @@ namespace dawn_wire { namespace server {
|
||||||
auto* buffer = BufferObjects().Get(cmd.selfId);
|
auto* buffer = BufferObjects().Get(cmd.selfId);
|
||||||
DAWN_ASSERT(buffer != nullptr);
|
DAWN_ASSERT(buffer != nullptr);
|
||||||
|
|
||||||
// The buffer was unmapped. Clear the Read/WriteHandle.
|
if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) {
|
||||||
buffer->readHandle = nullptr;
|
// This indicates the writeHandle is for mappedAtCreation only. Destroy on unmap
|
||||||
|
// writeHandle could have possibly been deleted if buffer is already destroyed so we
|
||||||
|
// don't assert it's non-null
|
||||||
buffer->writeHandle = nullptr;
|
buffer->writeHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
buffer->mapWriteState = BufferMapWriteState::Unmapped;
|
buffer->mapWriteState = BufferMapWriteState::Unmapped;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -47,12 +51,10 @@ namespace dawn_wire { namespace server {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::DoBufferMapAsync(ObjectId bufferId,
|
bool Server::DoBufferMapAsync(ObjectId bufferId,
|
||||||
uint32_t requestSerial,
|
uint64_t requestSerial,
|
||||||
WGPUMapModeFlags mode,
|
WGPUMapModeFlags mode,
|
||||||
uint64_t offset64,
|
uint64_t offset64,
|
||||||
uint64_t size64,
|
uint64_t size64) {
|
||||||
uint64_t handleCreateInfoLength,
|
|
||||||
const uint8_t* handleCreateInfo) {
|
|
||||||
// These requests are just forwarded to the buffer, with userdata containing what the
|
// These requests are just forwarded to the buffer, with userdata containing what the
|
||||||
// client will require in the return command.
|
// client will require in the return command.
|
||||||
|
|
||||||
|
@ -66,13 +68,6 @@ namespace dawn_wire { namespace server {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The server only knows how to deal with write XOR read. Validate that.
|
|
||||||
bool isReadMode = mode & WGPUMapMode_Read;
|
|
||||||
bool isWriteMode = mode & WGPUMapMode_Write;
|
|
||||||
if (!(isReadMode ^ isWriteMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
|
std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
|
||||||
userdata->buffer = ObjectHandle{bufferId, buffer->generation};
|
userdata->buffer = ObjectHandle{bufferId, buffer->generation};
|
||||||
userdata->bufferObj = buffer->handle;
|
userdata->bufferObj = buffer->handle;
|
||||||
|
@ -80,8 +75,7 @@ namespace dawn_wire { namespace server {
|
||||||
userdata->mode = mode;
|
userdata->mode = mode;
|
||||||
|
|
||||||
if (offset64 > std::numeric_limits<size_t>::max() ||
|
if (offset64 > std::numeric_limits<size_t>::max() ||
|
||||||
size64 > std::numeric_limits<size_t>::max() ||
|
size64 > std::numeric_limits<size_t>::max()) {
|
||||||
handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
|
|
||||||
OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get());
|
OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -92,32 +86,6 @@ namespace dawn_wire { namespace server {
|
||||||
userdata->offset = offset;
|
userdata->offset = offset;
|
||||||
userdata->size = size;
|
userdata->size = size;
|
||||||
|
|
||||||
// The handle will point to the mapped memory or staging memory for the mapping.
|
|
||||||
// Store it on the map request.
|
|
||||||
if (isWriteMode) {
|
|
||||||
// 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);
|
|
||||||
} else {
|
|
||||||
ASSERT(isReadMode);
|
|
||||||
// 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.bufferMapAsync(
|
mProcs.bufferMapAsync(
|
||||||
buffer->handle, mode, offset, size,
|
buffer->handle, mode, offset, size,
|
||||||
ForwardToServer<decltype(
|
ForwardToServer<decltype(
|
||||||
|
@ -130,8 +98,10 @@ namespace dawn_wire { namespace server {
|
||||||
bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
|
bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
|
||||||
const WGPUBufferDescriptor* descriptor,
|
const WGPUBufferDescriptor* descriptor,
|
||||||
ObjectHandle bufferResult,
|
ObjectHandle bufferResult,
|
||||||
uint64_t handleCreateInfoLength,
|
uint64_t readHandleCreateInfoLength,
|
||||||
const uint8_t* handleCreateInfo) {
|
const uint8_t* readHandleCreateInfo,
|
||||||
|
uint64_t writeHandleCreateInfoLength,
|
||||||
|
const uint8_t* writeHandleCreateInfo) {
|
||||||
auto* device = DeviceObjects().Get(deviceId);
|
auto* device = DeviceObjects().Get(deviceId);
|
||||||
if (device == nullptr) {
|
if (device == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -145,56 +115,84 @@ namespace dawn_wire { namespace server {
|
||||||
resultData->generation = bufferResult.generation;
|
resultData->generation = bufferResult.generation;
|
||||||
resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
|
resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
|
||||||
resultData->deviceInfo = device->info.get();
|
resultData->deviceInfo = device->info.get();
|
||||||
|
resultData->usage = descriptor->usage;
|
||||||
|
resultData->mappedAtCreation = descriptor->mappedAtCreation;
|
||||||
if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) {
|
if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the buffer isn't mapped at creation, we are done.
|
// isReadMode and isWriteMode could be true at the same time if usage contains
|
||||||
if (!descriptor->mappedAtCreation) {
|
// WGPUMapMode_Read and buffer is mappedAtCreation
|
||||||
return handleCreateInfoLength == 0;
|
bool isReadMode = descriptor->usage & WGPUMapMode_Read;
|
||||||
}
|
bool isWriteMode = descriptor->usage & WGPUMapMode_Write || descriptor->mappedAtCreation;
|
||||||
|
|
||||||
// This is the size of data deserialized from the command stream to create the write handle,
|
// This is the size of data deserialized from the command stream to create the read/write
|
||||||
// which must be CPU-addressable.
|
// handle, which must be CPU-addressable.
|
||||||
if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
|
if (readHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
|
||||||
|
writeHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
|
||||||
|
readHandleCreateInfoLength >
|
||||||
|
std::numeric_limits<size_t>::max() - writeHandleCreateInfoLength) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* mapping = mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
|
if (isWriteMode) {
|
||||||
|
MemoryTransferService::WriteHandle* writeHandle = nullptr;
|
||||||
|
// Deserialize metadata produced from the client to create a companion server handle.
|
||||||
|
if (!mMemoryTransferService->DeserializeWriteHandle(
|
||||||
|
writeHandleCreateInfo, static_cast<size_t>(writeHandleCreateInfoLength),
|
||||||
|
&writeHandle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASSERT(writeHandle != nullptr);
|
||||||
|
resultData->writeHandle.reset(writeHandle);
|
||||||
|
writeHandle->SetDataLength(descriptor->size);
|
||||||
|
|
||||||
|
if (descriptor->mappedAtCreation) {
|
||||||
|
void* mapping =
|
||||||
|
mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
|
||||||
if (mapping == nullptr) {
|
if (mapping == nullptr) {
|
||||||
// A zero mapping is used to indicate an allocation error of an error buffer. This is a
|
// A zero mapping is used to indicate an allocation error of an error buffer.
|
||||||
// valid case and isn't fatal. Remember the buffer is an error so as to skip subsequent
|
// This is a valid case and isn't fatal. Remember the buffer is an error so as
|
||||||
// mapping operations.
|
// to skip subsequent mapping operations.
|
||||||
resultData->mapWriteState = BufferMapWriteState::MapError;
|
resultData->mapWriteState = BufferMapWriteState::MapError;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
ASSERT(mapping != nullptr);
|
||||||
// Deserialize metadata produced from the client to create a companion server handle.
|
writeHandle->SetTarget(mapping);
|
||||||
MemoryTransferService::WriteHandle* writeHandle = nullptr;
|
|
||||||
if (!mMemoryTransferService->DeserializeWriteHandle(
|
|
||||||
handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the target of the WriteHandle to the mapped GPU memory.
|
|
||||||
ASSERT(writeHandle != nullptr);
|
|
||||||
writeHandle->SetTarget(mapping, descriptor->size);
|
|
||||||
|
|
||||||
resultData->mapWriteState = BufferMapWriteState::Mapped;
|
resultData->mapWriteState = BufferMapWriteState::Mapped;
|
||||||
resultData->writeHandle.reset(writeHandle);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReadMode) {
|
||||||
|
MemoryTransferService::ReadHandle* readHandle = nullptr;
|
||||||
|
// Deserialize metadata produced from the client to create a companion server handle.
|
||||||
|
if (!mMemoryTransferService->DeserializeReadHandle(
|
||||||
|
readHandleCreateInfo, static_cast<size_t>(readHandleCreateInfoLength),
|
||||||
|
&readHandle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ASSERT(readHandle != nullptr);
|
||||||
|
|
||||||
|
resultData->readHandle.reset(readHandle);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
|
bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
|
||||||
uint64_t writeFlushInfoLength,
|
uint64_t writeDataUpdateInfoLength,
|
||||||
const uint8_t* writeFlushInfo) {
|
const uint8_t* writeDataUpdateInfo,
|
||||||
|
uint64_t offset,
|
||||||
|
uint64_t size) {
|
||||||
// The null object isn't valid as `self`
|
// The null object isn't valid as `self`
|
||||||
if (bufferId == 0) {
|
if (bufferId == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeFlushInfoLength > std::numeric_limits<size_t>::max()) {
|
if (writeDataUpdateInfoLength > std::numeric_limits<size_t>::max() ||
|
||||||
|
offset > std::numeric_limits<size_t>::max() ||
|
||||||
|
size > std::numeric_limits<size_t>::max()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,8 +218,9 @@ namespace dawn_wire { namespace server {
|
||||||
|
|
||||||
// Deserialize the flush info and flush updated data from the handle into the target
|
// Deserialize the flush info and flush updated data from the handle into the target
|
||||||
// of the handle. The target is set via WriteHandle::SetTarget.
|
// of the handle. The target is set via WriteHandle::SetTarget.
|
||||||
return buffer->writeHandle->DeserializeFlush(writeFlushInfo,
|
return buffer->writeHandle->DeserializeDataUpdate(
|
||||||
static_cast<size_t>(writeFlushInfoLength));
|
writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
|
||||||
|
static_cast<size_t>(offset), static_cast<size_t>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) {
|
void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) {
|
||||||
|
@ -238,40 +237,42 @@ namespace dawn_wire { namespace server {
|
||||||
cmd.buffer = data->buffer;
|
cmd.buffer = data->buffer;
|
||||||
cmd.requestSerial = data->requestSerial;
|
cmd.requestSerial = data->requestSerial;
|
||||||
cmd.status = status;
|
cmd.status = status;
|
||||||
cmd.readInitialDataInfoLength = 0;
|
cmd.readDataUpdateInfoLength = 0;
|
||||||
cmd.readInitialDataInfo = nullptr;
|
cmd.readDataUpdateInfo = nullptr;
|
||||||
|
|
||||||
const void* readData = nullptr;
|
const void* readData = nullptr;
|
||||||
if (isSuccess && isRead) {
|
|
||||||
// Get the serialization size of the message to initialize ReadHandle data.
|
|
||||||
readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
|
|
||||||
cmd.readInitialDataInfoLength =
|
|
||||||
data->readHandle->SerializeInitialDataSize(readData, data->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
SerializeCommand(cmd, cmd.readInitialDataInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
if (isRead) {
|
if (isRead) {
|
||||||
char* readHandleBuffer;
|
// Get the serialization size of the message to initialize ReadHandle data.
|
||||||
WIRE_TRY(
|
readData =
|
||||||
serializeBuffer->NextN(cmd.readInitialDataInfoLength, &readHandleBuffer));
|
mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
|
||||||
|
cmd.readDataUpdateInfoLength =
|
||||||
// Serialize the initialization message into the space after the command.
|
bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
|
||||||
data->readHandle->SerializeInitialData(readData, data->size, readHandleBuffer);
|
|
||||||
// The in-flight map request returned successfully.
|
|
||||||
// Move the ReadHandle so it is owned by the buffer.
|
|
||||||
bufferData->readHandle = std::move(data->readHandle);
|
|
||||||
} else {
|
} else {
|
||||||
|
ASSERT(data->mode & WGPUMapMode_Write);
|
||||||
// The in-flight map request returned successfully.
|
// 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->mapWriteState = BufferMapWriteState::Mapped;
|
||||||
// Set the target of the WriteHandle to the mapped buffer data.
|
// Set the target of the WriteHandle to the mapped buffer data.
|
||||||
|
// writeHandle Target always refers to the buffer base address.
|
||||||
|
// but we call getMappedRange exactly with the range of data that is potentially
|
||||||
|
// modified (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a
|
||||||
|
// subset of the buffer is actually mapped) in case the implementation does some
|
||||||
|
// range tracking.
|
||||||
bufferData->writeHandle->SetTarget(
|
bufferData->writeHandle->SetTarget(
|
||||||
mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size),
|
static_cast<uint8_t*>(
|
||||||
data->size);
|
mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) -
|
||||||
|
data->offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SerializeCommand(cmd, cmd.readDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
||||||
|
if (isSuccess && isRead) {
|
||||||
|
char* readHandleBuffer;
|
||||||
|
WIRE_TRY(serializeBuffer->NextN(cmd.readDataUpdateInfoLength, &readHandleBuffer));
|
||||||
|
// The in-flight map request returned successfully.
|
||||||
|
bufferData->readHandle->SerializeDataUpdate(readData, data->offset, data->size,
|
||||||
|
readHandleBuffer);
|
||||||
|
}
|
||||||
return WireResult::Success;
|
return WireResult::Success;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,17 +28,18 @@ namespace dawn_wire { namespace server {
|
||||||
}
|
}
|
||||||
~ReadHandleImpl() override = default;
|
~ReadHandleImpl() override = default;
|
||||||
|
|
||||||
size_t SerializeInitialDataSize(const void* data, size_t dataLength) override {
|
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
|
||||||
return dataLength;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerializeInitialData(const void* data,
|
void SerializeDataUpdate(const void* data,
|
||||||
size_t dataLength,
|
size_t offset,
|
||||||
|
size_t size,
|
||||||
void* serializePointer) override {
|
void* serializePointer) override {
|
||||||
if (dataLength > 0) {
|
if (size > 0) {
|
||||||
ASSERT(data != nullptr);
|
ASSERT(data != nullptr);
|
||||||
ASSERT(serializePointer != nullptr);
|
ASSERT(serializePointer != nullptr);
|
||||||
memcpy(serializePointer, data, dataLength);
|
memcpy(serializePointer, data, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -49,12 +50,18 @@ namespace dawn_wire { namespace server {
|
||||||
}
|
}
|
||||||
~WriteHandleImpl() override = default;
|
~WriteHandleImpl() override = default;
|
||||||
|
|
||||||
bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override {
|
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
if (deserializeSize != mDataLength || mTargetData == nullptr ||
|
size_t deserializeSize,
|
||||||
|
size_t offset,
|
||||||
|
size_t size) override {
|
||||||
|
if (deserializeSize != size || mTargetData == nullptr ||
|
||||||
deserializePointer == nullptr) {
|
deserializePointer == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(mTargetData, deserializePointer, mDataLength);
|
if ((offset >= mDataLength && offset > 0) || size > mDataLength - offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(static_cast<uint8_t*>(mTargetData) + offset, deserializePointer, size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,15 +26,16 @@ namespace dawn_wire { namespace server {
|
||||||
mService->OnReadHandleDestroy(this);
|
mService->OnReadHandleDestroy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MockMemoryTransferService::MockReadHandle::SerializeInitialDataSize(const void* data,
|
size_t MockMemoryTransferService::MockReadHandle::SizeOfSerializeDataUpdate(size_t offset,
|
||||||
size_t dataLength) {
|
size_t size) {
|
||||||
return mService->OnReadHandleSerializeInitialDataSize(this, data, dataLength);
|
return mService->OnReadHandleSizeOfSerializeDataUpdate(this, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MockMemoryTransferService::MockReadHandle::SerializeInitialData(const void* data,
|
void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
|
||||||
size_t dataLength,
|
size_t offset,
|
||||||
|
size_t size,
|
||||||
void* serializePointer) {
|
void* serializePointer) {
|
||||||
mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer);
|
mService->OnReadHandleSerializeDataUpdate(this, data, offset, size, serializePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
||||||
|
@ -45,18 +46,21 @@ namespace dawn_wire { namespace server {
|
||||||
mService->OnWriteHandleDestroy(this);
|
mService->OnWriteHandleDestroy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MockMemoryTransferService::MockWriteHandle::DeserializeFlush(
|
|
||||||
const void* deserializePointer,
|
|
||||||
size_t deserializeSize) {
|
|
||||||
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
|
|
||||||
return mService->OnWriteHandleDeserializeFlush(
|
|
||||||
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const {
|
const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const {
|
||||||
return reinterpret_cast<const uint32_t*>(mTargetData);
|
return reinterpret_cast<const uint32_t*>(mTargetData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MockMemoryTransferService::MockWriteHandle::DeserializeDataUpdate(
|
||||||
|
const void* deserializePointer,
|
||||||
|
size_t deserializeSize,
|
||||||
|
size_t offset,
|
||||||
|
size_t size) {
|
||||||
|
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
|
||||||
|
return mService->OnWriteHandleDeserializeDataUpdate(
|
||||||
|
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
|
||||||
|
size);
|
||||||
|
}
|
||||||
|
|
||||||
MockMemoryTransferService::MockMemoryTransferService() = default;
|
MockMemoryTransferService::MockMemoryTransferService() = default;
|
||||||
MockMemoryTransferService::~MockMemoryTransferService() = default;
|
MockMemoryTransferService::~MockMemoryTransferService() = default;
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ namespace dawn_wire { namespace server {
|
||||||
MockReadHandle(MockMemoryTransferService* service);
|
MockReadHandle(MockMemoryTransferService* service);
|
||||||
~MockReadHandle() override;
|
~MockReadHandle() override;
|
||||||
|
|
||||||
size_t SerializeInitialDataSize(const void* data, size_t dataLength) override;
|
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
|
||||||
void SerializeInitialData(const void* data,
|
void SerializeDataUpdate(const void* data,
|
||||||
size_t dataLength,
|
size_t offset,
|
||||||
|
size_t size,
|
||||||
void* serializePointer) override;
|
void* serializePointer) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -43,7 +44,10 @@ namespace dawn_wire { namespace server {
|
||||||
MockWriteHandle(MockMemoryTransferService* service);
|
MockWriteHandle(MockMemoryTransferService* service);
|
||||||
~MockWriteHandle() override;
|
~MockWriteHandle() override;
|
||||||
|
|
||||||
bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override;
|
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
|
size_t deserializeSize,
|
||||||
|
size_t offset,
|
||||||
|
size_t size) override;
|
||||||
|
|
||||||
const uint32_t* GetData() const;
|
const uint32_t* GetData() const;
|
||||||
|
|
||||||
|
@ -78,21 +82,24 @@ namespace dawn_wire { namespace server {
|
||||||
WriteHandle** writeHandle));
|
WriteHandle** writeHandle));
|
||||||
|
|
||||||
MOCK_METHOD(size_t,
|
MOCK_METHOD(size_t,
|
||||||
OnReadHandleSerializeInitialDataSize,
|
OnReadHandleSizeOfSerializeDataUpdate,
|
||||||
(const ReadHandle* readHandle, const void* data, size_t dataLength));
|
(const ReadHandle* readHandle, size_t offset, size_t size));
|
||||||
MOCK_METHOD(void,
|
MOCK_METHOD(void,
|
||||||
OnReadHandleSerializeInitialData,
|
OnReadHandleSerializeDataUpdate,
|
||||||
(const ReadHandle* readHandle,
|
(const ReadHandle* readHandle,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t dataLength,
|
size_t offset,
|
||||||
|
size_t size,
|
||||||
void* serializePointer));
|
void* serializePointer));
|
||||||
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
|
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
|
||||||
|
|
||||||
MOCK_METHOD(bool,
|
MOCK_METHOD(bool,
|
||||||
OnWriteHandleDeserializeFlush,
|
OnWriteHandleDeserializeDataUpdate,
|
||||||
(const WriteHandle* writeHandle,
|
(const WriteHandle* writeHandle,
|
||||||
const uint32_t* deserializePointer,
|
const uint32_t* deserializePointer,
|
||||||
size_t deserializeSize));
|
size_t deserializeSize,
|
||||||
|
size_t offset,
|
||||||
|
size_t size));
|
||||||
MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
|
MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,7 @@ namespace dawn_wire {
|
||||||
// deserialize the data update and apply
|
// deserialize the data update and apply
|
||||||
// it to the range (offset, offset + size) of allocation
|
// it to the range (offset, offset + size) of allocation
|
||||||
// There could be nothing to be deserialized (if using shared memory)
|
// There could be nothing to be deserialized (if using shared memory)
|
||||||
|
// Needs to check potential offset/size OOB and overflow
|
||||||
// TODO(dawn:773): change to pure virtual after update on chromium side.
|
// TODO(dawn:773): change to pure virtual after update on chromium side.
|
||||||
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
|
|
|
@ -132,8 +132,9 @@ namespace dawn_wire {
|
||||||
|
|
||||||
// Set the target for writes from the client. DeserializeFlush should copy data
|
// Set the target for writes from the client. DeserializeFlush should copy data
|
||||||
// into the target.
|
// into the target.
|
||||||
// TODO(dawn:773): only set backing buffer pointer data
|
void SetTarget(void* data);
|
||||||
void SetTarget(void* data, size_t dataLength);
|
// Set Staging data length for OOB check
|
||||||
|
void SetDataLength(size_t dataLength);
|
||||||
|
|
||||||
// TODO(dawn:773): remove after update on chromium side.
|
// TODO(dawn:773): remove after update on chromium side.
|
||||||
virtual bool DeserializeFlush(const void* deserializePointer,
|
virtual bool DeserializeFlush(const void* deserializePointer,
|
||||||
|
@ -143,6 +144,7 @@ namespace dawn_wire {
|
||||||
|
|
||||||
// This function takes in the serialized result of
|
// This function takes in the serialized result of
|
||||||
// client::MemoryTransferService::WriteHandle::SerializeDataUpdate.
|
// client::MemoryTransferService::WriteHandle::SerializeDataUpdate.
|
||||||
|
// Needs to check potential offset/size OOB and overflow
|
||||||
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
||||||
size_t deserializeSize,
|
size_t deserializeSize,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
|
@ -152,7 +154,6 @@ namespace dawn_wire {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* mTargetData = nullptr;
|
void* mTargetData = nullptr;
|
||||||
// TODO(dawn:773): only set backing buffer pointer data
|
|
||||||
size_t mDataLength = 0;
|
size_t mDataLength = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -113,6 +113,21 @@ TEST_P(BufferMappingTests, MapRead_Twice) {
|
||||||
buffer.Unmap();
|
buffer.Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map read and test multiple get mapped range data
|
||||||
|
TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
|
||||||
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
||||||
|
|
||||||
|
uint32_t myData[] = {0x00010203, 0x04050607, 0x08090a0b};
|
||||||
|
queue.WriteBuffer(buffer, 0, &myData, 12);
|
||||||
|
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 12);
|
||||||
|
ASSERT_EQ(myData[0], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)));
|
||||||
|
ASSERT_EQ(myData[1], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 1));
|
||||||
|
ASSERT_EQ(myData[2], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 2));
|
||||||
|
ASSERT_EQ(myData[2], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(8)));
|
||||||
|
buffer.Unmap();
|
||||||
|
}
|
||||||
|
|
||||||
// Test map-reading a large buffer.
|
// Test map-reading a large buffer.
|
||||||
TEST_P(BufferMappingTests, MapRead_Large) {
|
TEST_P(BufferMappingTests, MapRead_Large) {
|
||||||
constexpr uint32_t kDataSize = 1000 * 1000;
|
constexpr uint32_t kDataSize = 1000 * 1000;
|
||||||
|
@ -257,6 +272,70 @@ TEST_P(BufferMappingTests, MapWrite_Twice) {
|
||||||
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map write and unmap twice with different ranges and make sure the first write is preserved
|
||||||
|
TEST_P(BufferMappingTests, MapWrite_TwicePreserve) {
|
||||||
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
||||||
|
|
||||||
|
uint32_t data1 = 0x08090a0b;
|
||||||
|
size_t offset1 = 8;
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, sizeof(data1));
|
||||||
|
memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
uint32_t data2 = 0x00010203;
|
||||||
|
size_t offset2 = 0;
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, sizeof(data2));
|
||||||
|
memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
|
||||||
|
EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map write and unmap twice with overlapping ranges and make sure data is updated correctly
|
||||||
|
TEST_P(BufferMappingTests, MapWrite_TwiceRangeOverlap) {
|
||||||
|
wgpu::Buffer buffer = CreateMapWriteBuffer(16);
|
||||||
|
|
||||||
|
uint32_t data1[] = {0x01234567, 0x89abcdef};
|
||||||
|
size_t offset1 = 8;
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, 8);
|
||||||
|
memcpy(buffer.GetMappedRange(offset1), data1, 8);
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 0);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 4);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 8);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
|
||||||
|
|
||||||
|
uint32_t data2[] = {0x01234567, 0x89abcdef, 0x55555555};
|
||||||
|
size_t offset2 = 0;
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, 12);
|
||||||
|
memcpy(buffer.GetMappedRange(offset2), data2, 12);
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 0);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 4);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x55555555, buffer, 8);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map write and test multiple mapped range data get updated correctly
|
||||||
|
TEST_P(BufferMappingTests, MapWrite_MultipleMappedRange) {
|
||||||
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
||||||
|
|
||||||
|
uint32_t data1 = 0x08090a0b;
|
||||||
|
size_t offset1 = 8;
|
||||||
|
uint32_t data2 = 0x00010203;
|
||||||
|
size_t offset2 = 0;
|
||||||
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 12);
|
||||||
|
memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
|
||||||
|
memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
|
||||||
|
EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
|
||||||
|
}
|
||||||
|
|
||||||
// Test mapping a large buffer.
|
// Test mapping a large buffer.
|
||||||
TEST_P(BufferMappingTests, MapWrite_Large) {
|
TEST_P(BufferMappingTests, MapWrite_Large) {
|
||||||
constexpr uint32_t kDataSize = 1000 * 1000;
|
constexpr uint32_t kDataSize = 1000 * 1000;
|
||||||
|
@ -330,20 +409,26 @@ TEST_P(BufferMappingTests, OffsetNotUpdatedOnError) {
|
||||||
queue.WriteBuffer(buffer, 0, data, sizeof(data));
|
queue.WriteBuffer(buffer, 0, data, sizeof(data));
|
||||||
|
|
||||||
// Map the buffer but do not wait on the result yet.
|
// Map the buffer but do not wait on the result yet.
|
||||||
bool done = false;
|
bool done1 = false;
|
||||||
|
bool done2 = false;
|
||||||
buffer.MapAsync(
|
buffer.MapAsync(
|
||||||
wgpu::MapMode::Read, 8, 4,
|
wgpu::MapMode::Read, 8, 4,
|
||||||
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
||||||
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
||||||
*static_cast<bool*>(userdata) = true;
|
*static_cast<bool*>(userdata) = true;
|
||||||
},
|
},
|
||||||
&done);
|
&done1);
|
||||||
|
|
||||||
// Call MapAsync another time, it is an error because the buffer is already being mapped so
|
// Call MapAsync another time, it is an error because the buffer is already being mapped so
|
||||||
// mMapOffset is not updated.
|
// mMapOffset is not updated.
|
||||||
ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr));
|
ASSERT_DEVICE_ERROR(buffer.MapAsync(
|
||||||
|
wgpu::MapMode::Read, 0, 4,
|
||||||
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
||||||
|
*static_cast<bool*>(userdata) = true;
|
||||||
|
},
|
||||||
|
&done2));
|
||||||
|
|
||||||
while (!done) {
|
while (!done1 || !done2) {
|
||||||
WaitABit();
|
WaitABit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,17 +47,7 @@ class WireBufferMappingTests : public WireTest {
|
||||||
WireTest::SetUp();
|
WireTest::SetUp();
|
||||||
|
|
||||||
mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
|
mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
|
||||||
|
|
||||||
WGPUBufferDescriptor descriptor = {};
|
|
||||||
descriptor.size = kBufferSize;
|
|
||||||
|
|
||||||
apiBuffer = api.GetNewBuffer();
|
apiBuffer = api.GetNewBuffer();
|
||||||
buffer = wgpuDeviceCreateBuffer(device, &descriptor);
|
|
||||||
|
|
||||||
EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
|
|
||||||
.WillOnce(Return(apiBuffer))
|
|
||||||
.RetiresOnSaturation();
|
|
||||||
FlushClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
|
@ -77,6 +67,19 @@ class WireBufferMappingTests : public WireTest {
|
||||||
Mock::VerifyAndClearExpectations(&mockBufferMapCallback);
|
Mock::VerifyAndClearExpectations(&mockBufferMapCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupBuffer(WGPUBufferUsageFlags usage) {
|
||||||
|
WGPUBufferDescriptor descriptor = {};
|
||||||
|
descriptor.size = kBufferSize;
|
||||||
|
descriptor.usage = usage;
|
||||||
|
|
||||||
|
buffer = wgpuDeviceCreateBuffer(device, &descriptor);
|
||||||
|
|
||||||
|
EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
|
||||||
|
.WillOnce(Return(apiBuffer))
|
||||||
|
.RetiresOnSaturation();
|
||||||
|
FlushClient();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static constexpr uint64_t kBufferSize = sizeof(uint32_t);
|
static constexpr uint64_t kBufferSize = sizeof(uint32_t);
|
||||||
// A successfully created buffer
|
// A successfully created buffer
|
||||||
|
@ -85,9 +88,21 @@ class WireBufferMappingTests : public WireTest {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tests specific to mapping for reading
|
// Tests specific to mapping for reading
|
||||||
|
class WireBufferMappingReadTests : public WireBufferMappingTests {
|
||||||
|
public:
|
||||||
|
WireBufferMappingReadTests() {
|
||||||
|
}
|
||||||
|
~WireBufferMappingReadTests() override = default;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
WireBufferMappingTests::SetUp();
|
||||||
|
|
||||||
|
SetupBuffer(WGPUBufferUsage_MapRead);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check mapping for reading a succesfully created buffer
|
// Check mapping for reading a succesfully created buffer
|
||||||
TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
|
TEST_F(WireBufferMappingReadTests, MappingForReadSuccessBuffer) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -115,7 +130,7 @@ TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
|
||||||
|
|
||||||
// Check that things work correctly when a validation error happens when mapping the buffer for
|
// Check that things work correctly when a validation error happens when mapping the buffer for
|
||||||
// reading
|
// reading
|
||||||
TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) {
|
TEST_F(WireBufferMappingReadTests, ErrorWhileMappingForRead) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
||||||
|
@ -133,7 +148,7 @@ TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) {
|
||||||
|
|
||||||
// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the
|
// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the
|
||||||
// request is finished
|
// request is finished
|
||||||
TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
|
TEST_F(WireBufferMappingReadTests, DestroyBeforeReadRequestEnd) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
// Return success
|
// Return success
|
||||||
|
@ -158,7 +173,7 @@ TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
|
||||||
|
|
||||||
// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
|
// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
|
||||||
// have worked, but Unmap was called
|
// have worked, but Unmap was called
|
||||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
|
TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForRead) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -186,7 +201,7 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
|
||||||
|
|
||||||
// Check that even if Unmap() was called early client-side, we correctly surface server-side
|
// Check that even if Unmap() was called early client-side, we correctly surface server-side
|
||||||
// validation errors.
|
// validation errors.
|
||||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForReadButServerSideError) {
|
TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForReadButServerSideError) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
||||||
|
@ -209,7 +224,7 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForReadButServerSideError) {
|
||||||
|
|
||||||
// Check the map read callback is called with "DestroyedBeforeCallback" when the map request would
|
// Check the map read callback is called with "DestroyedBeforeCallback" when the map request would
|
||||||
// have worked, but Destroy was called
|
// have worked, but Destroy was called
|
||||||
TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForRead) {
|
TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForRead) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -237,7 +252,7 @@ TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForRead) {
|
||||||
|
|
||||||
// Check that even if Destroy() was called early client-side, we correctly surface server-side
|
// Check that even if Destroy() was called early client-side, we correctly surface server-side
|
||||||
// validation errors.
|
// validation errors.
|
||||||
TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
||||||
|
@ -258,8 +273,9 @@ TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
||||||
FlushServer();
|
FlushServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that an error map read callback gets nullptr while a buffer is already mapped
|
// Check that an error map read while a buffer is already mapped won't changed the result of get
|
||||||
TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
|
// mapped range
|
||||||
|
TEST_F(WireBufferMappingReadTests, MappingForReadingErrorWhileAlreadyMappedUnchangeMapData) {
|
||||||
// Successful map
|
// Successful map
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
|
@ -289,11 +305,12 @@ TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullp
|
||||||
|
|
||||||
FlushServer();
|
FlushServer();
|
||||||
|
|
||||||
EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
|
EXPECT_EQ(bufferContent,
|
||||||
|
*static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
|
// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
|
||||||
TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
|
TEST_F(WireBufferMappingReadTests, UnmapInsideMapReadCallback) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -318,7 +335,7 @@ TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
|
||||||
|
|
||||||
// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the
|
// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the
|
||||||
// callback
|
// callback
|
||||||
TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
|
TEST_F(WireBufferMappingReadTests, DestroyInsideMapReadCallback) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -342,9 +359,21 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests specific to mapping for writing
|
// Tests specific to mapping for writing
|
||||||
|
class WireBufferMappingWriteTests : public WireBufferMappingTests {
|
||||||
|
public:
|
||||||
|
WireBufferMappingWriteTests() {
|
||||||
|
}
|
||||||
|
~WireBufferMappingWriteTests() override = default;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
WireBufferMappingTests::SetUp();
|
||||||
|
|
||||||
|
SetupBuffer(WGPUBufferUsage_MapWrite);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check mapping for writing a succesfully created buffer
|
// Check mapping for writing a succesfully created buffer
|
||||||
TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
|
TEST_F(WireBufferMappingWriteTests, MappingForWriteSuccessBuffer) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t serverBufferContent = 31337;
|
uint32_t serverBufferContent = 31337;
|
||||||
|
@ -382,7 +411,7 @@ TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
|
||||||
|
|
||||||
// Check that things work correctly when a validation error happens when mapping the buffer for
|
// Check that things work correctly when a validation error happens when mapping the buffer for
|
||||||
// writing
|
// writing
|
||||||
TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) {
|
TEST_F(WireBufferMappingWriteTests, ErrorWhileMappingForWrite) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
|
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
|
||||||
|
@ -400,7 +429,7 @@ TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) {
|
||||||
|
|
||||||
// Check that the map write callback is called with "DestroyedBeforeCallback" when the buffer is
|
// Check that the map write callback is called with "DestroyedBeforeCallback" when the buffer is
|
||||||
// destroyed before the request is finished
|
// destroyed before the request is finished
|
||||||
TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
|
TEST_F(WireBufferMappingWriteTests, DestroyBeforeWriteRequestEnd) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
// Return success
|
// Return success
|
||||||
|
@ -423,9 +452,9 @@ TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
|
||||||
FlushServer();
|
FlushServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
|
// Check the map write callback is called with "UnmappedBeforeCallback" when the map request would
|
||||||
// have worked, but Unmap was called
|
// have worked, but Unmap was called
|
||||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
|
TEST_F(WireBufferMappingWriteTests, UnmapCalledTooEarlyForWrite) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -447,8 +476,8 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
|
||||||
FlushServer();
|
FlushServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that an error map read callback gets nullptr while a buffer is already mapped
|
// Check that an error map write while a buffer is already mapped
|
||||||
TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
|
TEST_F(WireBufferMappingWriteTests, MappingForWritingErrorWhileAlreadyMapped) {
|
||||||
// Successful map
|
// Successful map
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
|
@ -478,11 +507,12 @@ TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullp
|
||||||
|
|
||||||
FlushServer();
|
FlushServer();
|
||||||
|
|
||||||
EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, kBufferSize));
|
EXPECT_NE(nullptr,
|
||||||
|
static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
|
// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
|
||||||
TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
|
TEST_F(WireBufferMappingWriteTests, UnmapInsideMapWriteCallback) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -507,7 +537,7 @@ TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
|
||||||
|
|
||||||
// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the
|
// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the
|
||||||
// callback
|
// callback
|
||||||
TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
|
TEST_F(WireBufferMappingWriteTests, DestroyInsideMapWriteCallback) {
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||||
|
|
||||||
uint32_t bufferContent = 31337;
|
uint32_t bufferContent = 31337;
|
||||||
|
@ -578,6 +608,7 @@ TEST_F(WireBufferMappingTests, MappedAtCreationReleaseBeforeUnmap) {
|
||||||
TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) {
|
TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) {
|
||||||
WGPUBufferDescriptor descriptor = {};
|
WGPUBufferDescriptor descriptor = {};
|
||||||
descriptor.size = 4;
|
descriptor.size = 4;
|
||||||
|
descriptor.usage = WGPUMapMode_Write;
|
||||||
descriptor.mappedAtCreation = true;
|
descriptor.mappedAtCreation = true;
|
||||||
|
|
||||||
WGPUBuffer apiBuffer = api.GetNewBuffer();
|
WGPUBuffer apiBuffer = api.GetNewBuffer();
|
||||||
|
@ -639,7 +670,8 @@ TEST_F(WireBufferMappingTests, MappedAtCreationThenMapFailure) {
|
||||||
|
|
||||||
FlushServer();
|
FlushServer();
|
||||||
|
|
||||||
EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
|
EXPECT_NE(nullptr,
|
||||||
|
static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
|
||||||
|
|
||||||
wgpuBufferUnmap(buffer);
|
wgpuBufferUnmap(buffer);
|
||||||
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
|
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
|
||||||
|
@ -694,6 +726,7 @@ TEST_F(WireBufferMappingTests, MaxSizeMappableBufferOOMDirectly) {
|
||||||
// Test that registering a callback then wire disconnect calls the callback with
|
// Test that registering a callback then wire disconnect calls the callback with
|
||||||
// DeviceLost.
|
// DeviceLost.
|
||||||
TEST_F(WireBufferMappingTests, MapThenDisconnect) {
|
TEST_F(WireBufferMappingTests, MapThenDisconnect) {
|
||||||
|
SetupBuffer(WGPUMapMode_Write);
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this);
|
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this);
|
||||||
|
|
||||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
|
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
|
||||||
|
@ -711,6 +744,8 @@ TEST_F(WireBufferMappingTests, MapThenDisconnect) {
|
||||||
// Test that registering a callback after wire disconnect calls the callback with
|
// Test that registering a callback after wire disconnect calls the callback with
|
||||||
// DeviceLost.
|
// DeviceLost.
|
||||||
TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
|
TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
|
||||||
|
SetupBuffer(WGPUMapMode_Read);
|
||||||
|
|
||||||
GetWireClient()->Disconnect();
|
GetWireClient()->Disconnect();
|
||||||
|
|
||||||
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
|
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
|
||||||
|
|
|
@ -56,58 +56,3 @@ TEST_F(WireDestroyObjectTests, DestroyDeviceDestroysChildren) {
|
||||||
wgpuCommandEncoderFinish(encoder, nullptr);
|
wgpuCommandEncoderFinish(encoder, nullptr);
|
||||||
FlushClient(false);
|
FlushClient(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that calling a function that would generate an InjectError doesn't crash after
|
|
||||||
// the device is destroyed.
|
|
||||||
TEST_F(WireDestroyObjectTests, ImplicitInjectErrorAfterDestroyDevice) {
|
|
||||||
WGPUBufferDescriptor bufferDesc = {};
|
|
||||||
bufferDesc.size = 4;
|
|
||||||
WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
|
||||||
|
|
||||||
WGPUBuffer apiBuffer = api.GetNewBuffer();
|
|
||||||
EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
|
|
||||||
|
|
||||||
FlushClient();
|
|
||||||
|
|
||||||
{
|
|
||||||
// Control case: MapAsync errors on invalid WGPUMapMode.
|
|
||||||
MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
|
|
||||||
|
|
||||||
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, _));
|
|
||||||
EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this));
|
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
|
|
||||||
mockBufferMapCallback.MakeUserdata(this));
|
|
||||||
|
|
||||||
FlushClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Now, release the device. InjectError shouldn't happen.
|
|
||||||
wgpuDeviceRelease(device);
|
|
||||||
MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
|
|
||||||
|
|
||||||
EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1));
|
|
||||||
wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
|
|
||||||
mockBufferMapCallback.MakeUserdata(this + 1));
|
|
||||||
|
|
||||||
Sequence s1, s2;
|
|
||||||
// The device and child objects alre also released.
|
|
||||||
EXPECT_CALL(api, BufferRelease(apiBuffer)).InSequence(s1);
|
|
||||||
EXPECT_CALL(api, QueueRelease(apiQueue)).InSequence(s2);
|
|
||||||
EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
|
|
||||||
.Times(1)
|
|
||||||
.InSequence(s1, s2);
|
|
||||||
EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr))
|
|
||||||
.Times(1)
|
|
||||||
.InSequence(s1, s2);
|
|
||||||
EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr))
|
|
||||||
.Times(1)
|
|
||||||
.InSequence(s1, s2);
|
|
||||||
EXPECT_CALL(api, DeviceRelease(apiDevice)).InSequence(s1, s2);
|
|
||||||
|
|
||||||
FlushClient();
|
|
||||||
|
|
||||||
// Signal that we already released and cleared callbacks for |apiDevice|
|
|
||||||
DefaultApiDeviceWasReleased();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue