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:
shrekshao 2021-07-08 22:48:57 +00:00 committed by Dawn LUCI CQ
parent 15838b98af
commit 6e680fc56f
20 changed files with 936 additions and 772 deletions

View File

@ -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" },

View File

@ -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

View File

@ -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.
writeHandle.reset(
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
if (writeHandle == nullptr) {
device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
return device->CreateErrorBuffer();
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();
}
// 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();
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,
"Failed to create buffer mapping");
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;
if (descriptor->mappedAtCreation) {
// 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;
buffer->mMapOffset = 0;
buffer->mMapSize = buffer->mSize;
ASSERT(writeHandle != nullptr);
buffer->mMappedData = writeHandle->GetData();
}
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;
cmd, cmd.readHandleCreateInfoLength + cmd.writeHandleCreateInfoLength,
[&](SerializeBuffer* serializeBuffer) {
if (readHandle != nullptr) {
char* readHandleBuffer;
WIRE_TRY(
serializeBuffer->NextN(writeHandleCreateInfoLength, &writeHandleBuffer));
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);
// 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;
}
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.
return FailRequest();
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();
}
// Validate to prevent bad map request; buffer destroyed during map request
if (mReadHandle == nullptr) {
return FailRequest();
}
// Update user map data with server returned data
if (!mReadHandle->DeserializeDataUpdate(
readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
request.offset, request.size)) {
return FailRequest();
}
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;
}
default:
UNREACHABLE();
}
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.
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.
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) {
return FailRequest();
}
}
// 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);
mMapOffset = request.offset;
mMapSize = request.size;
}
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;
}

View File

@ -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;

View File

@ -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,

View File

@ -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,
size_t deserializeSize,
const void** data,
size_t* dataLength) override {
if (deserializeSize != mSize || deserializePointer == nullptr) {
const void* GetData() override {
return mStagingData.get();
}
bool DeserializeDataUpdate(const void* deserializePointer,
size_t deserializeSize,
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;
}
};

View File

@ -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;

View File

@ -31,10 +31,11 @@ namespace dawn_wire { namespace client {
size_t SerializeCreateSize() override;
void SerializeCreate(void* serializePointer) override;
bool DeserializeInitialData(const void* deserializePointer,
size_t deserializeSize,
const void** data,
size_t* dataLength) override;
const void* GetData() override;
bool DeserializeDataUpdate(const void* deserializePointer,
size_t deserializeSize,
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));
};

View File

@ -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

View File

@ -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 {

View File

@ -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;
buffer->writeHandle = 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 (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.
resultData->mapWriteState = BufferMapWriteState::MapError;
return true;
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.
resultData->mapWriteState = BufferMapWriteState::MapError;
return true;
}
ASSERT(mapping != nullptr);
writeHandle->SetTarget(mapping);
resultData->mapWriteState = BufferMapWriteState::Mapped;
}
}
// 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;
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);
}
// Set the target of the WriteHandle to the mapped GPU memory.
ASSERT(writeHandle != nullptr);
writeHandle->SetTarget(mapping, descriptor->size);
resultData->mapWriteState = BufferMapWriteState::Mapped;
resultData->writeHandle.reset(writeHandle);
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,39 +237,41 @@ 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);
if (isSuccess) {
if (isRead) {
// 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.
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(
static_cast<uint8_t*>(
mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) -
data->offset);
}
}
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);
} else {
// 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.
bufferData->writeHandle->SetTarget(
mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size),
data->size);
}
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;
});

View File

@ -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* serializePointer) override {
if (dataLength > 0) {
void SerializeDataUpdate(const void* data,
size_t offset,
size_t size,
void* serializePointer) override {
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;
}
};

View File

@ -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* serializePointer) {
mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer);
void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
size_t offset,
size_t size,
void* 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;

View File

@ -29,10 +29,11 @@ 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,
void* serializePointer) override;
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:
MockMemoryTransferService* mService;
@ -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));
};

View File

@ -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,

View File

@ -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:

View File

@ -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();
}

View File

@ -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);

View File

@ -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