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": {
|
||||
"buffer map async": [
|
||||
{ "name": "buffer id", "type": "ObjectId" },
|
||||
{ "name": "request serial", "type": "uint32_t" },
|
||||
{ "name": "request serial", "type": "uint64_t" },
|
||||
{ "name": "mode", "type": "map mode" },
|
||||
{ "name": "offset", "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}
|
||||
{ "name": "size", "type": "uint64_t"}
|
||||
],
|
||||
"buffer update mapped data": [
|
||||
{ "name": "buffer id", "type": "ObjectId" },
|
||||
{ "name": "write flush 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 length", "type": "uint64_t" },
|
||||
{ "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": [
|
||||
{ "name": "device id", "type": "ObjectId" },
|
||||
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
|
||||
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
|
||||
{ "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}
|
||||
{ "name": "read handle create info length", "type": "uint64_t" },
|
||||
{ "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": [
|
||||
{ "name": "device id", "type": "ObjectId" },
|
||||
|
@ -87,10 +89,10 @@
|
|||
"return commands": {
|
||||
"buffer map async callback": [
|
||||
{ "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": "read initial data 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 length", "type": "uint64_t" },
|
||||
{ "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": [
|
||||
{ "name": "device", "type": "ObjectHandle", "handle_type": "device" },
|
||||
|
|
|
@ -68,8 +68,10 @@ namespace dawn_wire {
|
|||
|
||||
MemoryTransferService::WriteHandle::~WriteHandle() = default;
|
||||
|
||||
void MemoryTransferService::WriteHandle::SetTarget(void* data, size_t dataLength) {
|
||||
void MemoryTransferService::WriteHandle::SetTarget(void* data) {
|
||||
mTargetData = data;
|
||||
}
|
||||
void MemoryTransferService::WriteHandle::SetDataLength(size_t dataLength) {
|
||||
mDataLength = dataLength;
|
||||
}
|
||||
} // namespace server
|
||||
|
|
|
@ -33,64 +33,93 @@ namespace dawn_wire { namespace client {
|
|||
return device->CreateErrorBuffer();
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = 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
|
||||
// mapping of the whole buffer.
|
||||
if (descriptor->mappedAtCreation) {
|
||||
// Create the handle.
|
||||
DeviceCreateBufferCmd cmd;
|
||||
cmd.deviceId = device->id;
|
||||
cmd.descriptor = descriptor;
|
||||
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(
|
||||
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
|
||||
if (writeHandle == nullptr) {
|
||||
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
|
||||
device->InjectError(WGPUErrorType_OutOfMemory,
|
||||
"Failed to create buffer mapping");
|
||||
return device->CreateErrorBuffer();
|
||||
}
|
||||
|
||||
// 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();
|
||||
cmd.writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
|
||||
}
|
||||
ASSERT(writeDataLength == descriptor->size);
|
||||
|
||||
// Get the serialization size of the write handle.
|
||||
writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
|
||||
}
|
||||
|
||||
// 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);
|
||||
Buffer* buffer = bufferObjectAndSerial->object.get();
|
||||
buffer->mDevice = device;
|
||||
buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
|
||||
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) {
|
||||
char* writeHandleBuffer;
|
||||
WIRE_TRY(
|
||||
serializeBuffer->NextN(writeHandleCreateInfoLength, &writeHandleBuffer));
|
||||
// Serialize the WriteHandle into the space after the command.
|
||||
writeHandle->SerializeCreate(writeHandleBuffer);
|
||||
// If the buffer is mapped at creation, a write handle is created and will be
|
||||
// destructed on unmap if the buffer doesn't have MapWrite usage
|
||||
// The buffer is mapped right now.
|
||||
buffer->mMapState = MapState::MappedAtCreation;
|
||||
|
||||
// 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->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 ToAPI(buffer);
|
||||
|
@ -120,7 +149,7 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
mRequests.clear();
|
||||
|
||||
FreeMappedData(true);
|
||||
FreeMappedData();
|
||||
}
|
||||
|
||||
void Buffer::CancelCallbacksForDisconnect() {
|
||||
|
@ -146,97 +175,41 @@ namespace dawn_wire { namespace client {
|
|||
size = mSize - offset;
|
||||
}
|
||||
|
||||
bool isReadMode = mode & WGPUMapMode_Read;
|
||||
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
|
||||
// Create the request structure that will hold information while this mapping is
|
||||
// in flight.
|
||||
uint32_t serial = mRequestSerial++;
|
||||
uint64_t serial = mRequestSerial++;
|
||||
ASSERT(mRequests.find(serial) == mRequests.end());
|
||||
|
||||
Buffer::MapRequestData request = {};
|
||||
request.callback = callback;
|
||||
request.userdata = userdata;
|
||||
request.size = size;
|
||||
request.offset = offset;
|
||||
|
||||
// Step 2a: Create the read / write handles for this request.
|
||||
if (isReadMode) {
|
||||
request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
|
||||
if (request.readHandle == nullptr) {
|
||||
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;
|
||||
}
|
||||
request.size = size;
|
||||
if (mode & WGPUMapMode_Read) {
|
||||
request.type = MapRequestType::Read;
|
||||
} else if (mode & WGPUMapMode_Write) {
|
||||
request.type = MapRequestType::Write;
|
||||
}
|
||||
|
||||
// Step 3. Serialize the command to send to the server.
|
||||
// Serialize the command to send to the server.
|
||||
BufferMapAsyncCmd cmd;
|
||||
cmd.bufferId = this->id;
|
||||
cmd.requestSerial = serial;
|
||||
cmd.mode = mode;
|
||||
cmd.offset = offset;
|
||||
cmd.size = size;
|
||||
cmd.handleCreateInfo = nullptr;
|
||||
|
||||
// Step 3a. Fill the handle create info in the command.
|
||||
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;
|
||||
});
|
||||
}
|
||||
client->SerializeCommand(cmd);
|
||||
|
||||
// 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.
|
||||
mRequests[serial] = std::move(request);
|
||||
}
|
||||
|
||||
bool Buffer::OnMapAsyncCallback(uint32_t requestSerial,
|
||||
bool Buffer::OnMapAsyncCallback(uint64_t requestSerial,
|
||||
uint32_t status,
|
||||
uint64_t readInitialDataInfoLength,
|
||||
const uint8_t* readInitialDataInfo) {
|
||||
uint64_t readDataUpdateInfoLength,
|
||||
const uint8_t* readDataUpdateInfo) {
|
||||
auto requestIt = mRequests.find(requestSerial);
|
||||
if (requestIt == mRequests.end()) {
|
||||
return false;
|
||||
|
@ -254,60 +227,50 @@ namespace dawn_wire { namespace client {
|
|||
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.
|
||||
if (status == WGPUBufferMapAsyncStatus_Success) {
|
||||
status = request.clientStatus;
|
||||
}
|
||||
|
||||
size_t mappedDataLength = 0;
|
||||
const void* mappedData = nullptr;
|
||||
if (status == WGPUBufferMapAsyncStatus_Success) {
|
||||
if (mReadHandle || mWriteHandle) {
|
||||
// Buffer is already mapped.
|
||||
switch (request.type) {
|
||||
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();
|
||||
}
|
||||
|
||||
if (isRead) {
|
||||
if (readInitialDataInfoLength > std::numeric_limits<size_t>::max()) {
|
||||
// This is the size of data deserialized from the command stream, which must be
|
||||
// CPU-addressable.
|
||||
// Validate to prevent bad map request; buffer destroyed during map request
|
||||
if (mReadHandle == nullptr) {
|
||||
return FailRequest();
|
||||
}
|
||||
|
||||
// The server serializes metadata to initialize the contents of the ReadHandle.
|
||||
// Deserialize the message and return a pointer and size of the mapped data for
|
||||
// reading.
|
||||
if (!request.readHandle->DeserializeInitialData(
|
||||
readInitialDataInfo, static_cast<size_t>(readInitialDataInfoLength),
|
||||
&mappedData, &mappedDataLength)) {
|
||||
// Deserialization shouldn't fail. This is a fatal error.
|
||||
// Update user map data with server returned data
|
||||
if (!mReadHandle->DeserializeDataUpdate(
|
||||
readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
|
||||
request.offset, request.size)) {
|
||||
return FailRequest();
|
||||
}
|
||||
ASSERT(mappedData != nullptr);
|
||||
|
||||
} else {
|
||||
// Open the WriteHandle. This returns a pointer and size of mapped memory.
|
||||
// On failure, |mappedData| may be null.
|
||||
std::tie(mappedData, mappedDataLength) = request.writeHandle->Open();
|
||||
|
||||
if (mappedData == nullptr) {
|
||||
mMapState = MapState::MappedForRead;
|
||||
mMappedData = const_cast<void*>(mReadHandle->GetData());
|
||||
break;
|
||||
}
|
||||
case MapRequestType::Write: {
|
||||
if (mWriteHandle == nullptr) {
|
||||
return FailRequest();
|
||||
}
|
||||
mMapState = MapState::MappedForWrite;
|
||||
mMappedData = mWriteHandle->GetData();
|
||||
break;
|
||||
}
|
||||
|
||||
// The MapAsync request was successful. The buffer now owns the Read/Write handles
|
||||
// until Unmap().
|
||||
mReadHandle = std::move(request.readHandle);
|
||||
mWriteHandle = std::move(request.writeHandle);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
mMapOffset = request.offset;
|
||||
mMapSize = request.size;
|
||||
mMappedData = const_cast<void*>(mappedData);
|
||||
}
|
||||
|
||||
if (request.callback) {
|
||||
request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
|
||||
}
|
||||
|
@ -319,7 +282,7 @@ namespace dawn_wire { namespace client {
|
|||
if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
|
||||
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) {
|
||||
|
@ -327,7 +290,7 @@ namespace dawn_wire { namespace client {
|
|||
!CheckGetMappedRangeOffsetSize(offset, size)) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
|
||||
return static_cast<uint8_t*>(mMappedData) + offset;
|
||||
}
|
||||
|
||||
void Buffer::Unmap() {
|
||||
|
@ -339,32 +302,57 @@ namespace dawn_wire { namespace client {
|
|||
// - Server -> Client: Result of MapRequest1
|
||||
// - Unmap locally on the client
|
||||
// - 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
|
||||
// in-flight callbacks which may read the updated data.
|
||||
ASSERT(mReadHandle == nullptr);
|
||||
|
||||
// Get the serialization size of metadata to flush writes.
|
||||
size_t writeFlushInfoLength = mWriteHandle->SerializeFlushSize();
|
||||
// Get the serialization size of data update writes.
|
||||
size_t writeDataUpdateInfoLength =
|
||||
mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
|
||||
|
||||
BufferUpdateMappedDataCmd cmd;
|
||||
cmd.bufferId = id;
|
||||
cmd.writeFlushInfoLength = writeFlushInfoLength;
|
||||
cmd.writeFlushInfo = nullptr;
|
||||
cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
|
||||
cmd.writeDataUpdateInfo = nullptr;
|
||||
cmd.offset = mMapOffset;
|
||||
cmd.size = mMapSize;
|
||||
|
||||
client->SerializeCommand(
|
||||
cmd, writeFlushInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
||||
cmd, writeDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
|
||||
char* writeHandleBuffer;
|
||||
WIRE_TRY(serializeBuffer->NextN(writeFlushInfoLength, &writeHandleBuffer));
|
||||
WIRE_TRY(serializeBuffer->NextN(writeDataUpdateInfoLength, &writeHandleBuffer));
|
||||
|
||||
// Serialize flush metadata into the space after the command.
|
||||
// This closes the handle for writing.
|
||||
mWriteHandle->SerializeFlush(writeHandleBuffer);
|
||||
mWriteHandle->SerializeDataUpdate(writeHandleBuffer, cmd.offset, cmd.size);
|
||||
|
||||
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.
|
||||
for (auto& it : mRequests) {
|
||||
|
@ -376,11 +364,13 @@ namespace dawn_wire { namespace client {
|
|||
BufferUnmapCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
client->SerializeCommand(cmd);
|
||||
// TODO(dawn:608): add tracking mapped status and change to return bool for returning unmap
|
||||
// success/failure
|
||||
}
|
||||
|
||||
void Buffer::Destroy() {
|
||||
// Remove the current mapping.
|
||||
FreeMappedData(true);
|
||||
// Remove the current mapping and destroy Read/WriteHandles.
|
||||
FreeMappedData();
|
||||
|
||||
// Tag all mapping requests still in flight as destroyed before callback.
|
||||
for (auto& it : mRequests) {
|
||||
|
@ -395,11 +385,11 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
|
||||
bool Buffer::IsMappedForReading() const {
|
||||
return mReadHandle != nullptr;
|
||||
return mMapState == MapState::MappedForRead;
|
||||
}
|
||||
|
||||
bool Buffer::IsMappedForWriting() const {
|
||||
return mWriteHandle != nullptr;
|
||||
return mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation;
|
||||
}
|
||||
|
||||
bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
|
||||
|
@ -415,20 +405,20 @@ namespace dawn_wire { namespace client {
|
|||
return offsetInMappedRange <= mMapSize - size;
|
||||
}
|
||||
|
||||
void Buffer::FreeMappedData(bool destruction) {
|
||||
void Buffer::FreeMappedData() {
|
||||
#if defined(DAWN_ENABLE_ASSERTS)
|
||||
// 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
|
||||
// interaction of mapping and GC.
|
||||
if (mMappedData && destruction) {
|
||||
memset(mMappedData, 0xCA, mMapSize);
|
||||
if (mMappedData) {
|
||||
memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
|
||||
}
|
||||
#endif // defined(DAWN_ENABLE_ASSERTS)
|
||||
|
||||
mMapOffset = 0;
|
||||
mMapSize = 0;
|
||||
mWriteHandle = nullptr;
|
||||
mReadHandle = nullptr;
|
||||
mWriteHandle = nullptr;
|
||||
mMappedData = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,10 +35,10 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
~Buffer();
|
||||
|
||||
bool OnMapAsyncCallback(uint32_t requestSerial,
|
||||
bool OnMapAsyncCallback(uint64_t requestSerial,
|
||||
uint32_t status,
|
||||
uint64_t readInitialDataInfoLength,
|
||||
const uint8_t* readInitialDataInfo);
|
||||
uint64_t readDataUpdateInfoLength,
|
||||
const uint8_t* readDataUpdateInfo);
|
||||
void MapAsync(WGPUMapModeFlags mode,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
|
@ -57,10 +57,19 @@ namespace dawn_wire { namespace client {
|
|||
bool IsMappedForWriting() const;
|
||||
bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
|
||||
|
||||
void FreeMappedData(bool destruction);
|
||||
void FreeMappedData();
|
||||
|
||||
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
|
||||
// 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.
|
||||
|
@ -75,12 +84,10 @@ namespace dawn_wire { namespace client {
|
|||
// from the server take precedence over the client-side status.
|
||||
WGPUBufferMapAsyncStatus clientStatus = WGPUBufferMapAsyncStatus_Success;
|
||||
|
||||
// TODO(enga): Use a tagged pointer to save space.
|
||||
std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
|
||||
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
|
||||
MapRequestType type = MapRequestType::None;
|
||||
};
|
||||
std::map<uint32_t, MapRequestData> mRequests;
|
||||
uint32_t mRequestSerial = 0;
|
||||
std::map<uint64_t, MapRequestData> mRequests;
|
||||
uint64_t mRequestSerial = 0;
|
||||
uint64_t mSize = 0;
|
||||
|
||||
// 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.
|
||||
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
|
||||
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
|
||||
MapState mMapState = MapState::Unmapped;
|
||||
bool mDestructWriteHandleOnUnmap = false;
|
||||
|
||||
void* mMappedData = nullptr;
|
||||
size_t mMapOffset = 0;
|
||||
size_t mMapSize = 0;
|
||||
|
|
|
@ -73,16 +73,16 @@ namespace dawn_wire { namespace client {
|
|||
}
|
||||
|
||||
bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
|
||||
uint32_t requestSerial,
|
||||
uint64_t requestSerial,
|
||||
uint32_t status,
|
||||
uint64_t readInitialDataInfoLength,
|
||||
const uint8_t* readInitialDataInfo) {
|
||||
uint64_t readDataUpdateInfoLength,
|
||||
const uint8_t* readDataUpdateInfo) {
|
||||
// The buffer might have been deleted or recreated so this isn't an error.
|
||||
if (buffer == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
|
||||
readInitialDataInfo);
|
||||
return buffer->OnMapAsyncCallback(requestSerial, status, readDataUpdateInfoLength,
|
||||
readDataUpdateInfo);
|
||||
}
|
||||
|
||||
bool Client::DoQueueWorkDoneCallback(Queue* queue,
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace dawn_wire { namespace client {
|
|||
class InlineMemoryTransferService : public MemoryTransferService {
|
||||
class ReadHandleImpl : public ReadHandle {
|
||||
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;
|
||||
|
@ -36,36 +37,36 @@ namespace dawn_wire { namespace client {
|
|||
void SerializeCreate(void*) override {
|
||||
}
|
||||
|
||||
bool DeserializeInitialData(const void* deserializePointer,
|
||||
const void* GetData() override {
|
||||
return mStagingData.get();
|
||||
}
|
||||
|
||||
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
const void** data,
|
||||
size_t* dataLength) override {
|
||||
if (deserializeSize != mSize || deserializePointer == nullptr) {
|
||||
size_t offset,
|
||||
size_t size) override {
|
||||
if (deserializeSize != size || deserializePointer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
|
||||
if (!mStagingData) {
|
||||
if (offset > mSize || size > mSize - offset) {
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t mSize;
|
||||
std::unique_ptr<uint8_t[]> mStagingData;
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
class WriteHandleImpl : public WriteHandle {
|
||||
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;
|
||||
|
@ -77,28 +78,27 @@ namespace dawn_wire { namespace client {
|
|||
void SerializeCreate(void*) override {
|
||||
}
|
||||
|
||||
std::pair<void*, size_t> Open() override {
|
||||
mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
|
||||
if (!mStagingData) {
|
||||
return std::make_pair(nullptr, 0);
|
||||
}
|
||||
memset(mStagingData.get(), 0, mSize);
|
||||
return std::make_pair(mStagingData.get(), mSize);
|
||||
void* GetData() override {
|
||||
return mStagingData.get();
|
||||
}
|
||||
|
||||
size_t SerializeFlushSize() override {
|
||||
return mSize;
|
||||
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
|
||||
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(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:
|
||||
size_t mSize;
|
||||
std::unique_ptr<uint8_t[]> mStagingData;
|
||||
size_t mSize;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -107,11 +107,20 @@ namespace dawn_wire { namespace client {
|
|||
~InlineMemoryTransferService() override = default;
|
||||
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
bool MockMemoryTransferService::MockReadHandle::DeserializeInitialData(
|
||||
const void* MockMemoryTransferService::MockReadHandle::GetData() {
|
||||
return mService->OnReadHandleGetData(this);
|
||||
}
|
||||
|
||||
bool MockMemoryTransferService::MockReadHandle::DeserializeDataUpdate(
|
||||
const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
const void** data,
|
||||
size_t* dataLength) {
|
||||
size_t offset,
|
||||
size_t size) {
|
||||
ASSERT(deserializeSize % sizeof(uint32_t) == 0);
|
||||
return mService->OnReadHandleDeserializeInitialData(
|
||||
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, data,
|
||||
dataLength);
|
||||
return mService->OnReadHandleDeserializeDataUpdate(
|
||||
this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
|
||||
size);
|
||||
}
|
||||
|
||||
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
||||
|
@ -62,16 +66,19 @@ namespace dawn_wire { namespace client {
|
|||
mService->OnWriteHandleSerializeCreate(this, serializePointer);
|
||||
}
|
||||
|
||||
std::pair<void*, size_t> MockMemoryTransferService::MockWriteHandle::Open() {
|
||||
return mService->OnWriteHandleOpen(this);
|
||||
void* MockMemoryTransferService::MockWriteHandle::GetData() {
|
||||
return mService->OnWriteHandleGetData(this);
|
||||
}
|
||||
|
||||
size_t MockMemoryTransferService::MockWriteHandle::SerializeFlushSize() {
|
||||
return mService->OnWriteHandleSerializeFlushSize(this);
|
||||
size_t MockMemoryTransferService::MockWriteHandle::SizeOfSerializeDataUpdate(size_t offset,
|
||||
size_t size) {
|
||||
return mService->OnWriteHandleSizeOfSerializeDataUpdate(this, offset, size);
|
||||
}
|
||||
|
||||
void MockMemoryTransferService::MockWriteHandle::SerializeFlush(void* serializePointer) {
|
||||
mService->OnWriteHandleSerializeFlush(this, serializePointer);
|
||||
void MockMemoryTransferService::MockWriteHandle::SerializeDataUpdate(void* serializePointer,
|
||||
size_t offset,
|
||||
size_t size) {
|
||||
mService->OnWriteHandleSerializeDataUpdate(this, serializePointer, offset, size);
|
||||
}
|
||||
|
||||
MockMemoryTransferService::MockMemoryTransferService() = default;
|
||||
|
|
|
@ -31,10 +31,11 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
size_t SerializeCreateSize() override;
|
||||
void SerializeCreate(void* serializePointer) override;
|
||||
bool DeserializeInitialData(const void* deserializePointer,
|
||||
const void* GetData() override;
|
||||
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
const void** data,
|
||||
size_t* dataLength) override;
|
||||
size_t offset,
|
||||
size_t size) override;
|
||||
|
||||
private:
|
||||
MockMemoryTransferService* mService;
|
||||
|
@ -47,9 +48,9 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
size_t SerializeCreateSize() override;
|
||||
void SerializeCreate(void* serializePointer) override;
|
||||
std::pair<void*, size_t> Open() override;
|
||||
size_t SerializeFlushSize() override;
|
||||
void SerializeFlush(void* serializePointer) override;
|
||||
void* GetData() override;
|
||||
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
|
||||
void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override;
|
||||
|
||||
private:
|
||||
MockMemoryTransferService* mService;
|
||||
|
@ -69,24 +70,27 @@ namespace dawn_wire { namespace client {
|
|||
|
||||
MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
|
||||
MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
|
||||
MOCK_METHOD((const void*), OnReadHandleGetData, (const ReadHandle*));
|
||||
MOCK_METHOD(bool,
|
||||
OnReadHandleDeserializeInitialData,
|
||||
OnReadHandleDeserializeDataUpdate,
|
||||
(const ReadHandle*,
|
||||
const uint32_t* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
const void** data,
|
||||
size_t* dataLength));
|
||||
size_t offset,
|
||||
size_t size));
|
||||
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
|
||||
|
||||
MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
|
||||
MOCK_METHOD(void,
|
||||
OnWriteHandleSerializeCreate,
|
||||
(const void* WriteHandle, void* serializePointer));
|
||||
MOCK_METHOD((std::pair<void*, size_t>), OnWriteHandleOpen, (const void* WriteHandle));
|
||||
MOCK_METHOD(size_t, OnWriteHandleSerializeFlushSize, (const void* WriteHandle));
|
||||
MOCK_METHOD(void,
|
||||
OnWriteHandleSerializeFlush,
|
||||
(const void* WriteHandle, void* serializePointer));
|
||||
MOCK_METHOD((void*), OnWriteHandleGetData, (const void* WriteHandle));
|
||||
MOCK_METHOD(size_t,
|
||||
OnWriteHandleSizeOfSerializeDataUpdate,
|
||||
(const void* WriteHandle, size_t offset, size_t size));
|
||||
MOCK_METHOD(size_t,
|
||||
OnWriteHandleSerializeDataUpdate,
|
||||
(const void* WriteHandle, void* serializePointer, size_t offset, size_t size));
|
||||
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::WriteHandle> writeHandle;
|
||||
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
|
||||
|
|
|
@ -111,13 +111,10 @@ namespace dawn_wire { namespace server {
|
|||
|
||||
ObjectHandle buffer;
|
||||
WGPUBuffer bufferObj;
|
||||
uint32_t requestSerial;
|
||||
uint64_t requestSerial;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
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 {
|
||||
|
|
|
@ -25,9 +25,13 @@ namespace dawn_wire { namespace server {
|
|||
auto* buffer = BufferObjects().Get(cmd.selfId);
|
||||
DAWN_ASSERT(buffer != nullptr);
|
||||
|
||||
// The buffer was unmapped. Clear the Read/WriteHandle.
|
||||
buffer->readHandle = nullptr;
|
||||
if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) {
|
||||
// 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->mapWriteState = BufferMapWriteState::Unmapped;
|
||||
|
||||
return true;
|
||||
|
@ -47,12 +51,10 @@ namespace dawn_wire { namespace server {
|
|||
}
|
||||
|
||||
bool Server::DoBufferMapAsync(ObjectId bufferId,
|
||||
uint32_t requestSerial,
|
||||
uint64_t requestSerial,
|
||||
WGPUMapModeFlags mode,
|
||||
uint64_t offset64,
|
||||
uint64_t size64,
|
||||
uint64_t handleCreateInfoLength,
|
||||
const uint8_t* handleCreateInfo) {
|
||||
uint64_t size64) {
|
||||
// These requests are just forwarded to the buffer, with userdata containing what the
|
||||
// client will require in the return command.
|
||||
|
||||
|
@ -66,13 +68,6 @@ namespace dawn_wire { namespace server {
|
|||
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>();
|
||||
userdata->buffer = ObjectHandle{bufferId, buffer->generation};
|
||||
userdata->bufferObj = buffer->handle;
|
||||
|
@ -80,8 +75,7 @@ namespace dawn_wire { namespace server {
|
|||
userdata->mode = mode;
|
||||
|
||||
if (offset64 > std::numeric_limits<size_t>::max() ||
|
||||
size64 > std::numeric_limits<size_t>::max() ||
|
||||
handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
|
||||
size64 > std::numeric_limits<size_t>::max()) {
|
||||
OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get());
|
||||
return true;
|
||||
}
|
||||
|
@ -92,32 +86,6 @@ namespace dawn_wire { namespace server {
|
|||
userdata->offset = offset;
|
||||
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(
|
||||
buffer->handle, mode, offset, size,
|
||||
ForwardToServer<decltype(
|
||||
|
@ -130,8 +98,10 @@ namespace dawn_wire { namespace server {
|
|||
bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
|
||||
const WGPUBufferDescriptor* descriptor,
|
||||
ObjectHandle bufferResult,
|
||||
uint64_t handleCreateInfoLength,
|
||||
const uint8_t* handleCreateInfo) {
|
||||
uint64_t readHandleCreateInfoLength,
|
||||
const uint8_t* readHandleCreateInfo,
|
||||
uint64_t writeHandleCreateInfoLength,
|
||||
const uint8_t* writeHandleCreateInfo) {
|
||||
auto* device = DeviceObjects().Get(deviceId);
|
||||
if (device == nullptr) {
|
||||
return false;
|
||||
|
@ -145,56 +115,84 @@ namespace dawn_wire { namespace server {
|
|||
resultData->generation = bufferResult.generation;
|
||||
resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
|
||||
resultData->deviceInfo = device->info.get();
|
||||
resultData->usage = descriptor->usage;
|
||||
resultData->mappedAtCreation = descriptor->mappedAtCreation;
|
||||
if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the buffer isn't mapped at creation, we are done.
|
||||
if (!descriptor->mappedAtCreation) {
|
||||
return handleCreateInfoLength == 0;
|
||||
}
|
||||
// isReadMode and isWriteMode could be true at the same time if usage contains
|
||||
// WGPUMapMode_Read and buffer is mappedAtCreation
|
||||
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,
|
||||
// which must be CPU-addressable.
|
||||
if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
|
||||
// This is the size of data deserialized from the command stream to create the read/write
|
||||
// handle, which must be CPU-addressable.
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
// A zero mapping is used to indicate an allocation error of an error buffer. This is a
|
||||
// valid case and isn't fatal. Remember the buffer is an error so as to skip subsequent
|
||||
// mapping operations.
|
||||
// A zero mapping is used to indicate an allocation error of an error buffer.
|
||||
// This is a valid case and isn't fatal. Remember the buffer is an error so as
|
||||
// to skip subsequent mapping operations.
|
||||
resultData->mapWriteState = BufferMapWriteState::MapError;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Set the target of the WriteHandle to the mapped GPU memory.
|
||||
ASSERT(writeHandle != nullptr);
|
||||
writeHandle->SetTarget(mapping, descriptor->size);
|
||||
ASSERT(mapping != nullptr);
|
||||
writeHandle->SetTarget(mapping);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
|
||||
uint64_t writeFlushInfoLength,
|
||||
const uint8_t* writeFlushInfo) {
|
||||
uint64_t writeDataUpdateInfoLength,
|
||||
const uint8_t* writeDataUpdateInfo,
|
||||
uint64_t offset,
|
||||
uint64_t size) {
|
||||
// The null object isn't valid as `self`
|
||||
if (bufferId == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -220,8 +218,9 @@ namespace dawn_wire { namespace server {
|
|||
|
||||
// Deserialize the flush info and flush updated data from the handle into the target
|
||||
// of the handle. The target is set via WriteHandle::SetTarget.
|
||||
return buffer->writeHandle->DeserializeFlush(writeFlushInfo,
|
||||
static_cast<size_t>(writeFlushInfoLength));
|
||||
return buffer->writeHandle->DeserializeDataUpdate(
|
||||
writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
|
||||
static_cast<size_t>(offset), static_cast<size_t>(size));
|
||||
}
|
||||
|
||||
void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) {
|
||||
|
@ -238,40 +237,42 @@ namespace dawn_wire { namespace server {
|
|||
cmd.buffer = data->buffer;
|
||||
cmd.requestSerial = data->requestSerial;
|
||||
cmd.status = status;
|
||||
cmd.readInitialDataInfoLength = 0;
|
||||
cmd.readInitialDataInfo = nullptr;
|
||||
cmd.readDataUpdateInfoLength = 0;
|
||||
cmd.readDataUpdateInfo = 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 (isRead) {
|
||||
char* readHandleBuffer;
|
||||
WIRE_TRY(
|
||||
serializeBuffer->NextN(cmd.readInitialDataInfoLength, &readHandleBuffer));
|
||||
|
||||
// Serialize the initialization message into the space after the command.
|
||||
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);
|
||||
// Get the serialization size of the message to initialize ReadHandle data.
|
||||
readData =
|
||||
mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
|
||||
cmd.readDataUpdateInfoLength =
|
||||
bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
|
||||
} else {
|
||||
ASSERT(data->mode & WGPUMapMode_Write);
|
||||
// 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;
|
||||
// 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(
|
||||
mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size),
|
||||
data->size);
|
||||
static_cast<uint8_t*>(
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,17 +28,18 @@ namespace dawn_wire { namespace server {
|
|||
}
|
||||
~ReadHandleImpl() override = default;
|
||||
|
||||
size_t SerializeInitialDataSize(const void* data, size_t dataLength) override {
|
||||
return dataLength;
|
||||
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
|
||||
return size;
|
||||
}
|
||||
|
||||
void SerializeInitialData(const void* data,
|
||||
size_t dataLength,
|
||||
void SerializeDataUpdate(const void* data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* serializePointer) override {
|
||||
if (dataLength > 0) {
|
||||
if (size > 0) {
|
||||
ASSERT(data != nullptr);
|
||||
ASSERT(serializePointer != nullptr);
|
||||
memcpy(serializePointer, data, dataLength);
|
||||
memcpy(serializePointer, data, size);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -49,12 +50,18 @@ namespace dawn_wire { namespace server {
|
|||
}
|
||||
~WriteHandleImpl() override = default;
|
||||
|
||||
bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override {
|
||||
if (deserializeSize != mDataLength || mTargetData == nullptr ||
|
||||
bool DeserializeDataUpdate(const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
size_t offset,
|
||||
size_t size) override {
|
||||
if (deserializeSize != size || mTargetData == nullptr ||
|
||||
deserializePointer == nullptr) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,15 +26,16 @@ namespace dawn_wire { namespace server {
|
|||
mService->OnReadHandleDestroy(this);
|
||||
}
|
||||
|
||||
size_t MockMemoryTransferService::MockReadHandle::SerializeInitialDataSize(const void* data,
|
||||
size_t dataLength) {
|
||||
return mService->OnReadHandleSerializeInitialDataSize(this, data, dataLength);
|
||||
size_t MockMemoryTransferService::MockReadHandle::SizeOfSerializeDataUpdate(size_t offset,
|
||||
size_t size) {
|
||||
return mService->OnReadHandleSizeOfSerializeDataUpdate(this, offset, size);
|
||||
}
|
||||
|
||||
void MockMemoryTransferService::MockReadHandle::SerializeInitialData(const void* data,
|
||||
size_t dataLength,
|
||||
void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* serializePointer) {
|
||||
mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer);
|
||||
mService->OnReadHandleSerializeDataUpdate(this, data, offset, size, serializePointer);
|
||||
}
|
||||
|
||||
MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
|
||||
|
@ -45,18 +46,21 @@ namespace dawn_wire { namespace server {
|
|||
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 {
|
||||
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;
|
||||
|
||||
|
|
|
@ -29,9 +29,10 @@ namespace dawn_wire { namespace server {
|
|||
MockReadHandle(MockMemoryTransferService* service);
|
||||
~MockReadHandle() override;
|
||||
|
||||
size_t SerializeInitialDataSize(const void* data, size_t dataLength) override;
|
||||
void SerializeInitialData(const void* data,
|
||||
size_t dataLength,
|
||||
size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
|
||||
void SerializeDataUpdate(const void* data,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* serializePointer) override;
|
||||
|
||||
private:
|
||||
|
@ -43,7 +44,10 @@ namespace dawn_wire { namespace server {
|
|||
MockWriteHandle(MockMemoryTransferService* service);
|
||||
~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;
|
||||
|
||||
|
@ -78,21 +82,24 @@ namespace dawn_wire { namespace server {
|
|||
WriteHandle** writeHandle));
|
||||
|
||||
MOCK_METHOD(size_t,
|
||||
OnReadHandleSerializeInitialDataSize,
|
||||
(const ReadHandle* readHandle, const void* data, size_t dataLength));
|
||||
OnReadHandleSizeOfSerializeDataUpdate,
|
||||
(const ReadHandle* readHandle, size_t offset, size_t size));
|
||||
MOCK_METHOD(void,
|
||||
OnReadHandleSerializeInitialData,
|
||||
OnReadHandleSerializeDataUpdate,
|
||||
(const ReadHandle* readHandle,
|
||||
const void* data,
|
||||
size_t dataLength,
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* serializePointer));
|
||||
MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
|
||||
|
||||
MOCK_METHOD(bool,
|
||||
OnWriteHandleDeserializeFlush,
|
||||
OnWriteHandleDeserializeDataUpdate,
|
||||
(const WriteHandle* writeHandle,
|
||||
const uint32_t* deserializePointer,
|
||||
size_t deserializeSize));
|
||||
size_t deserializeSize,
|
||||
size_t offset,
|
||||
size_t size));
|
||||
MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
|
||||
};
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ namespace dawn_wire {
|
|||
// deserialize the data update and apply
|
||||
// it to the range (offset, offset + size) of allocation
|
||||
// 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.
|
||||
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
|
|
|
@ -132,8 +132,9 @@ namespace dawn_wire {
|
|||
|
||||
// Set the target for writes from the client. DeserializeFlush should copy data
|
||||
// into the target.
|
||||
// TODO(dawn:773): only set backing buffer pointer data
|
||||
void SetTarget(void* data, size_t dataLength);
|
||||
void SetTarget(void* data);
|
||||
// Set Staging data length for OOB check
|
||||
void SetDataLength(size_t dataLength);
|
||||
|
||||
// TODO(dawn:773): remove after update on chromium side.
|
||||
virtual bool DeserializeFlush(const void* deserializePointer,
|
||||
|
@ -143,6 +144,7 @@ namespace dawn_wire {
|
|||
|
||||
// This function takes in the serialized result of
|
||||
// client::MemoryTransferService::WriteHandle::SerializeDataUpdate.
|
||||
// Needs to check potential offset/size OOB and overflow
|
||||
virtual bool DeserializeDataUpdate(const void* deserializePointer,
|
||||
size_t deserializeSize,
|
||||
size_t offset,
|
||||
|
@ -152,7 +154,6 @@ namespace dawn_wire {
|
|||
|
||||
protected:
|
||||
void* mTargetData = nullptr;
|
||||
// TODO(dawn:773): only set backing buffer pointer data
|
||||
size_t mDataLength = 0;
|
||||
|
||||
private:
|
||||
|
|
|
@ -113,6 +113,21 @@ TEST_P(BufferMappingTests, MapRead_Twice) {
|
|||
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_P(BufferMappingTests, MapRead_Large) {
|
||||
constexpr uint32_t kDataSize = 1000 * 1000;
|
||||
|
@ -257,6 +272,70 @@ TEST_P(BufferMappingTests, MapWrite_Twice) {
|
|||
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_P(BufferMappingTests, MapWrite_Large) {
|
||||
constexpr uint32_t kDataSize = 1000 * 1000;
|
||||
|
@ -330,20 +409,26 @@ TEST_P(BufferMappingTests, OffsetNotUpdatedOnError) {
|
|||
queue.WriteBuffer(buffer, 0, data, sizeof(data));
|
||||
|
||||
// Map the buffer but do not wait on the result yet.
|
||||
bool done = false;
|
||||
bool done1 = false;
|
||||
bool done2 = false;
|
||||
buffer.MapAsync(
|
||||
wgpu::MapMode::Read, 8, 4,
|
||||
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
||||
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
||||
*static_cast<bool*>(userdata) = true;
|
||||
},
|
||||
&done);
|
||||
&done1);
|
||||
|
||||
// Call MapAsync another time, it is an error because the buffer is already being mapped so
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,17 +47,7 @@ class WireBufferMappingTests : public WireTest {
|
|||
WireTest::SetUp();
|
||||
|
||||
mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
|
||||
|
||||
WGPUBufferDescriptor descriptor = {};
|
||||
descriptor.size = kBufferSize;
|
||||
|
||||
apiBuffer = api.GetNewBuffer();
|
||||
buffer = wgpuDeviceCreateBuffer(device, &descriptor);
|
||||
|
||||
EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
|
||||
.WillOnce(Return(apiBuffer))
|
||||
.RetiresOnSaturation();
|
||||
FlushClient();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
|
@ -77,6 +67,19 @@ class WireBufferMappingTests : public WireTest {
|
|||
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:
|
||||
static constexpr uint64_t kBufferSize = sizeof(uint32_t);
|
||||
// A successfully created buffer
|
||||
|
@ -85,9 +88,21 @@ class WireBufferMappingTests : public WireTest {
|
|||
};
|
||||
|
||||
// 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
|
||||
TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
|
||||
TEST_F(WireBufferMappingReadTests, MappingForReadSuccessBuffer) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// reading
|
||||
TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) {
|
||||
TEST_F(WireBufferMappingReadTests, ErrorWhileMappingForRead) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// request is finished
|
||||
TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
|
||||
TEST_F(WireBufferMappingReadTests, DestroyBeforeReadRequestEnd) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
// Return success
|
||||
|
@ -158,7 +173,7 @@ TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
|
|||
|
||||
// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
|
||||
// have worked, but Unmap was called
|
||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
|
||||
TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForRead) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// validation errors.
|
||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForReadButServerSideError) {
|
||||
TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForReadButServerSideError) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// have worked, but Destroy was called
|
||||
TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForRead) {
|
||||
TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForRead) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// validation errors.
|
||||
TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
||||
TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
|
||||
|
@ -258,8 +273,9 @@ TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
|
|||
FlushServer();
|
||||
}
|
||||
|
||||
// Check that an error map read callback gets nullptr while a buffer is already mapped
|
||||
TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
|
||||
// Check that an error map read while a buffer is already mapped won't changed the result of get
|
||||
// mapped range
|
||||
TEST_F(WireBufferMappingReadTests, MappingForReadingErrorWhileAlreadyMappedUnchangeMapData) {
|
||||
// Successful map
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
|
@ -289,11 +305,12 @@ TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullp
|
|||
|
||||
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_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
|
||||
TEST_F(WireBufferMappingReadTests, UnmapInsideMapReadCallback) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// callback
|
||||
TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
|
||||
TEST_F(WireBufferMappingReadTests, DestroyInsideMapReadCallback) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
uint32_t bufferContent = 31337;
|
||||
|
@ -342,9 +359,21 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
|
|||
}
|
||||
|
||||
// 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
|
||||
TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
|
||||
TEST_F(WireBufferMappingWriteTests, MappingForWriteSuccessBuffer) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// writing
|
||||
TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) {
|
||||
TEST_F(WireBufferMappingWriteTests, ErrorWhileMappingForWrite) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// destroyed before the request is finished
|
||||
TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
|
||||
TEST_F(WireBufferMappingWriteTests, DestroyBeforeWriteRequestEnd) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
// Return success
|
||||
|
@ -423,9 +452,9 @@ TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
|
|||
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
|
||||
TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
|
||||
TEST_F(WireBufferMappingWriteTests, UnmapCalledTooEarlyForWrite) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
uint32_t bufferContent = 31337;
|
||||
|
@ -447,8 +476,8 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
|
|||
FlushServer();
|
||||
}
|
||||
|
||||
// Check that an error map read callback gets nullptr while a buffer is already mapped
|
||||
TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
|
||||
// Check that an error map write while a buffer is already mapped
|
||||
TEST_F(WireBufferMappingWriteTests, MappingForWritingErrorWhileAlreadyMapped) {
|
||||
// Successful map
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
|
@ -478,11 +507,12 @@ TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullp
|
|||
|
||||
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_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
|
||||
TEST_F(WireBufferMappingWriteTests, UnmapInsideMapWriteCallback) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
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
|
||||
// callback
|
||||
TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
|
||||
TEST_F(WireBufferMappingWriteTests, DestroyInsideMapWriteCallback) {
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
|
||||
|
||||
uint32_t bufferContent = 31337;
|
||||
|
@ -578,6 +608,7 @@ TEST_F(WireBufferMappingTests, MappedAtCreationReleaseBeforeUnmap) {
|
|||
TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) {
|
||||
WGPUBufferDescriptor descriptor = {};
|
||||
descriptor.size = 4;
|
||||
descriptor.usage = WGPUMapMode_Write;
|
||||
descriptor.mappedAtCreation = true;
|
||||
|
||||
WGPUBuffer apiBuffer = api.GetNewBuffer();
|
||||
|
@ -639,7 +670,8 @@ TEST_F(WireBufferMappingTests, MappedAtCreationThenMapFailure) {
|
|||
|
||||
FlushServer();
|
||||
|
||||
EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
|
||||
EXPECT_NE(nullptr,
|
||||
static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
|
||||
|
||||
wgpuBufferUnmap(buffer);
|
||||
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
|
||||
// DeviceLost.
|
||||
TEST_F(WireBufferMappingTests, MapThenDisconnect) {
|
||||
SetupBuffer(WGPUMapMode_Write);
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this);
|
||||
|
||||
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
|
||||
// DeviceLost.
|
||||
TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
|
||||
SetupBuffer(WGPUMapMode_Read);
|
||||
|
||||
GetWireClient()->Disconnect();
|
||||
|
||||
EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
|
||||
|
|
|
@ -56,58 +56,3 @@ TEST_F(WireDestroyObjectTests, DestroyDeviceDestroysChildren) {
|
|||
wgpuCommandEncoderFinish(encoder, nullptr);
|
||||
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