Revert "[WGPUFuture] Update buffer mapping in wire to keep reference to Buffer."
This reverts commit b445b0be7f620f1a662b7d835adbf59675fb45ea.
Reason for revert: Broke CI because raced submit with https://dawn-review.googlesource.com/c/dawn/+/166220
Original change's description:
> [WGPUFuture] Update buffer mapping in wire to keep reference to Buffer.
>
> - Also updates buffer mapping on the wire to be more consistent with
> other async future functions.
> - Note that buffer release no longer causes map async callbacks to
> trigger since the event holds a reference to the buffer now.
>
> Bug: dawn:2061
> Change-Id: Ifd0d0588f61ca531f0e75d1dd4082a3dc3e257a4
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/168800
> Commit-Queue: Loko Kung <lokokung@google.com>
> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
> Kokoro: Kokoro <noreply+kokoro@google.com>
TBR=cwallez@chromium.org,kainino@chromium.org,noreply+kokoro@google.com,dawn-scoped@luci-project-accounts.iam.gserviceaccount.com,lokokung@google.com
Change-Id: Icae2acf26efd641b77c097936b5313529da5cc47
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: dawn:2061
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/170581
Reviewed-by: Loko Kung <lokokung@google.com>
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Kokoro: Loko Kung <lokokung@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 5e92ad7..f37d6ac 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -116,9 +116,10 @@
},
"return commands": {
"buffer map async callback": [
+ { "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer", "_comment": "TODO(dawn:2061) Remove this field once mapping is updated." },
{ "name": "event manager", "type": "ObjectHandle" },
{ "name": "future", "type": "future" },
- { "name": "status", "type": "buffer map async status" },
+ { "name": "status", "type": "uint32_t" },
{ "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 }
],
diff --git a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
index d72f166..c75ea3a 100644
--- a/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -303,6 +303,22 @@
WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
}
+// Check the map callback is called with "DestroyedBeforeCallback" when the map request would have
+// worked, but Release() was called on the last ref.
+TEST_P(WireBufferMappingTests, ReleaseCalledTooEarly) {
+ TestEarlyMapCancelled(
+ &wgpuBufferRelease, [&]() { EXPECT_CALL(api, BufferRelease(apiBuffer)); },
+ WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
+}
+
+// Check that if Release() was called early client-side on the last ref, we disregard server-side
+// validation errors.
+TEST_P(WireBufferMappingTests, ReleaseCalledTooEarlyServerSideError) {
+ TestEarlyMapErrorCancelled(
+ &wgpuBufferRelease, [&]() { EXPECT_CALL(api, BufferRelease(apiBuffer)); },
+ WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
+}
+
// Test that the callback isn't fired twice when Unmap() is called inside the callback.
TEST_P(WireBufferMappingTests, UnmapInsideMapCallback) {
TestCancelInCallback(&wgpuBufferUnmap, [&]() { EXPECT_CALL(api, BufferUnmap(apiBuffer)); });
@@ -815,6 +831,52 @@
}
}
+#if defined(DAWN_ENABLE_ASSERTS)
+// Test that request inside user callbacks after object release is called.
+TEST_P(WireBufferMappingTests, MapInsideCallbackAfterRelease) {
+ WGPUMapMode mapMode = GetMapMode();
+ SetupBuffer(mapMode);
+ BufferMapAsync(buffer, mapMode, 0, kBufferSize, nullptr);
+
+ uint32_t bufferContent = 31337;
+ EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, mapMode, 0, kBufferSize, _, _))
+ .WillOnce(InvokeWithoutArgs(
+ [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
+ ExpectMappedRangeCall(kBufferSize, &bufferContent);
+ EXPECT_CALL(api, BufferRelease(apiBuffer));
+
+ // By releasing the buffer the refcount reaches zero and pending map async
+ // should fail with destroyed before callback status.
+ if (IsSpontaneous()) {
+ // In spontaneous mode, the callback gets called as a part of the release.
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
+ .WillOnce([&]() {
+ ASSERT_DEATH_IF_SUPPORTED(
+ { BufferMapAsync(buffer, mapMode, 0, kBufferSize, nullptr); }, "");
+ });
+
+ wgpuBufferRelease(buffer);
+ });
+ FlushClient();
+ FlushCallbacks();
+ } else {
+ // Otherwise, the callback happens when we flush it.
+ wgpuBufferRelease(buffer);
+ FlushClient();
+ ExpectWireCallbacksWhen([&](auto& mockCb) {
+ EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
+ .WillOnce([&]() {
+ ASSERT_DEATH_IF_SUPPORTED(
+ { BufferMapAsync(buffer, mapMode, 0, kBufferSize, nullptr); }, "");
+ });
+
+ FlushCallbacks();
+ });
+ }
+}
+#endif // defined(DAWN_ENABLE_ASSERTS)
+
// Test that requests inside user callbacks before disconnect are called.
TEST_P(WireBufferMappingTests, MapInsideCallbackBeforeDisconnect) {
WGPUMapMode mapMode = GetMapMode();
@@ -844,8 +906,8 @@
});
}
-// Test that requests inside user callbacks before buffer destroy are called.
-TEST_P(WireBufferMappingTests, MapInsideCallbackBeforeDestroy) {
+// Test that requests inside user callbacks before object release are called.
+TEST_P(WireBufferMappingTests, MapInsideCallbackBeforeRelease) {
WGPUMapMode mapMode = GetMapMode();
SetupBuffer(mapMode);
BufferMapAsync(buffer, mapMode, 0, kBufferSize, nullptr);
@@ -863,7 +925,7 @@
if (IsSpontaneous()) {
// In spontaneous mode, when the success callback fires, the first MapAsync request
// generated by the callback is queued, then all subsequent requests' callbacks are
- // immediately called with MappingAlreadyPending. Finally, when we call Destroy, the queued
+ // immediately called with MappingAlreadyPending. Finally, when we call Release, the queued
// request's callback is then called with DestroyedBeforeCallback.
ExpectWireCallbacksWhen([&](auto& mockCb) {
EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_Success, _)).WillOnce([&]() {
@@ -879,13 +941,13 @@
ExpectWireCallbacksWhen([&](auto& mockCb) {
EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _)).Times(1);
- wgpuBufferDestroy(buffer);
+ wgpuBufferRelease(buffer);
});
FlushCallbacks();
} else {
// In non-spontaneous modes, the first callback doesn't trigger any other immediate
// callbacks, but internally, all but the first MapAsync call's callback is set to be ready
- // with MappingAlreadyPending. When we call Destroy, the first pending request is then
+ // with MappingAlreadyPending. When we call Release, the first pending request is then
// marked ready with DestroyedBeforeCallback. The callbacks all run when we flush them.
ExpectWireCallbacksWhen([&](auto& mockCb) {
EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_Success, _)).WillOnce([&]() {
@@ -896,7 +958,7 @@
FlushCallbacks();
});
- wgpuBufferDestroy(buffer);
+ wgpuBufferRelease(buffer);
ExpectWireCallbacksWhen([&](auto& mockCb) {
EXPECT_CALL(mockCb, Call(WGPUBufferMapAsyncStatus_MappingAlreadyPending, _))
.Times(kNumRequests - 1);
diff --git a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
index 79ea2b8..e038b01 100644
--- a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -526,7 +526,7 @@
// Failed deserialization is a fatal failure and the client synchronously receives a
// DEVICE_LOST callback.
- EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)).Times(1);
+ EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1);
FlushServer(false);
diff --git a/src/dawn/wire/client/Buffer.cpp b/src/dawn/wire/client/Buffer.cpp
index 238f19f..4dcf340 100644
--- a/src/dawn/wire/client/Buffer.cpp
+++ b/src/dawn/wire/client/Buffer.cpp
@@ -40,6 +40,7 @@
namespace dawn::wire::client {
namespace {
+
WGPUBuffer CreateErrorBufferOOMAtClient(Device* device, const WGPUBufferDescriptor* descriptor) {
if (descriptor->mappedAtCreation) {
return nullptr;
@@ -51,112 +52,23 @@
errorBufferDescriptor.nextInChain = &errorInfo.chain;
return GetProcs().deviceCreateErrorBuffer(ToAPI(device), &errorBufferDescriptor);
}
-} // anonymous namespace
-class Buffer::MapAsyncEvent : public TrackedEvent {
+class MapAsyncEvent : public TrackedEvent {
public:
static constexpr EventType kType = EventType::MapAsync;
- MapAsyncEvent(const WGPUBufferMapCallbackInfo& callbackInfo, Buffer* buffer)
+ explicit MapAsyncEvent(const WGPUBufferMapCallbackInfo& callbackInfo,
+ const Ref<MapStateData>& mapStateData)
: TrackedEvent(callbackInfo.mode),
mCallback(callbackInfo.callback),
mUserdata(callbackInfo.userdata),
- mBuffer(buffer) {
- DAWN_ASSERT(buffer != nullptr);
- GetProcs().bufferReference(ToAPI(mBuffer));
+ mMapStateData(mapStateData) {
+ DAWN_ASSERT(mMapStateData.Get() != nullptr);
}
- ~MapAsyncEvent() override { GetProcs().bufferRelease(ToAPI(mBuffer)); }
-
EventType GetType() override { return kType; }
- bool IsPendingRequest(FutureID futureID) {
- return mBuffer->mPendingMapRequest && mBuffer->mPendingMapRequest->futureID == futureID;
- }
-
- WireResult ReadyHook(FutureID futureID,
- WGPUBufferMapAsyncStatus status,
- uint64_t readDataUpdateInfoLength = 0,
- const uint8_t* readDataUpdateInfo = nullptr) {
- auto FailRequest = [this]() -> WireResult {
- mStatus = WGPUBufferMapAsyncStatus_Unknown;
- return WireResult::FatalError;
- };
-
- // Handling for different statuses.
- switch (status) {
- case WGPUBufferMapAsyncStatus_MappingAlreadyPending: {
- DAWN_ASSERT(!IsPendingRequest(futureID));
- mStatus = status;
- break;
- }
-
- // For client-side rejection errors, we clear the pending request now since they always
- // take precedence.
- case WGPUBufferMapAsyncStatus_DestroyedBeforeCallback:
- case WGPUBufferMapAsyncStatus_UnmappedBeforeCallback: {
- mStatus = status;
- mBuffer->mPendingMapRequest = std::nullopt;
- break;
- }
-
- case WGPUBufferMapAsyncStatus_Success: {
- if (!IsPendingRequest(futureID)) {
- // If a success occurs (which must come from the server), but it does not
- // correspond to the pending request, the pending request must have been
- // rejected early and hence the status must be set.
- DAWN_ASSERT(mStatus);
- break;
- }
- mStatus = status;
- auto& pending = mBuffer->mPendingMapRequest.value();
- if (!pending.type) {
- return FailRequest();
- }
- switch (*pending.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 (mBuffer->mReadHandle == nullptr) {
- return FailRequest();
- }
- // Update user map data with server returned data
- if (!mBuffer->mReadHandle->DeserializeDataUpdate(
- readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
- pending.offset, pending.size)) {
- return FailRequest();
- }
- mBuffer->mMappedData = const_cast<void*>(mBuffer->mReadHandle->GetData());
- break;
- }
- case MapRequestType::Write: {
- if (mBuffer->mWriteHandle == nullptr) {
- return FailRequest();
- }
- mBuffer->mMappedData = mBuffer->mWriteHandle->GetData();
- break;
- }
- }
- mBuffer->mMappedOffset = pending.offset;
- mBuffer->mMappedSize = pending.size;
- break;
- }
-
- // All other statuses are server-side status.
- default: {
- if (!IsPendingRequest(futureID)) {
- break;
- }
- mStatus = status;
- }
- }
- return WireResult::Success;
- }
+ void ReadyHook(WGPUBufferMapAsyncStatus status) { mStatus = status; }
private:
void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
@@ -166,38 +78,24 @@
if (mStatus) {
status = *mStatus;
}
-
- auto Callback = [this, &status]() {
- if (mCallback) {
- mCallback(status, mUserdata);
+ if (mMapStateData->pendingRequest && futureID == mMapStateData->pendingRequest->futureID) {
+ if (status == WGPUBufferMapAsyncStatus_Success) {
+ switch (mMapStateData->pendingRequest->type) {
+ case MapRequestType::Read:
+ mMapStateData->mapState = MapState::MappedForRead;
+ break;
+ case MapRequestType::Write:
+ mMapStateData->mapState = MapState::MappedForWrite;
+ break;
+ default:
+ DAWN_UNREACHABLE();
+ }
}
- };
-
- if (!IsPendingRequest(futureID)) {
- DAWN_ASSERT(status != WGPUBufferMapAsyncStatus_Success);
- return Callback();
+ mMapStateData->pendingRequest = std::nullopt;
}
-
- if (status == WGPUBufferMapAsyncStatus_Success) {
- if (mBuffer->mIsDeviceAlive.expired()) {
- // If the device lost its last ref before this callback was resolved, we want to
- // overwrite the status. This is necessary because otherwise dropping the last
- // device reference could race w.r.t what this callback would see.
- status = WGPUBufferMapAsyncStatus_DestroyedBeforeCallback;
- return Callback();
- }
- DAWN_ASSERT(mBuffer->mPendingMapRequest->type);
- switch (*mBuffer->mPendingMapRequest->type) {
- case MapRequestType::Read:
- mBuffer->mMappedState = MapState::MappedForRead;
- break;
- case MapRequestType::Write:
- mBuffer->mMappedState = MapState::MappedForWrite;
- break;
- }
+ if (mCallback) {
+ mCallback(status, mUserdata);
}
- mBuffer->mPendingMapRequest = std::nullopt;
- return Callback();
}
WGPUBufferMapCallback mCallback;
@@ -206,10 +104,12 @@
std::optional<WGPUBufferMapAsyncStatus> mStatus;
- // Strong reference to the buffer so that when we call the callback we can pass the buffer.
- Buffer* const mBuffer;
+ // Shared data with the Buffer that may be modified between event handling and user inputs.
+ Ref<MapStateData> mMapStateData;
};
+} // anonymous namespace
+
// static
WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor) {
Client* wireClient = device->GetClient();
@@ -262,15 +162,20 @@
// This must happen after any potential error buffer creation
// as server expects allocating ids to be monotonically increasing
Buffer* buffer = wireClient->Make<Buffer>(device->GetEventManagerHandle(), descriptor);
- buffer->mIsDeviceAlive = device->GetAliveWeakPtr();
+ 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->mMappedState = MapState::MappedAtCreation;
- buffer->mMappedOffset = 0;
- buffer->mMappedSize = buffer->mSize;
+ buffer->mMapStateData->mapState = 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;
DAWN_ASSERT(writeHandle != nullptr);
buffer->mMappedData = writeHandle->GetData();
}
@@ -306,23 +211,32 @@
const ObjectHandle& eventManagerHandle,
const WGPUBufferDescriptor* descriptor)
: ObjectWithEventsBase(params, eventManagerHandle),
+ mMapStateData(AcquireRef(new MapStateData{})),
mSize(descriptor->size),
- mUsage(static_cast<WGPUBufferUsage>(descriptor->usage)),
- // This flag is for the write handle created by mappedAtCreation
- // instead of MapWrite usage. We don't have such a case for read handle.
- mDestructWriteHandleOnUnmap(descriptor->mappedAtCreation &&
- ((descriptor->usage & WGPUBufferUsage_MapWrite) == 0)) {}
+ mUsage(static_cast<WGPUBufferUsage>(descriptor->usage)) {}
Buffer::~Buffer() {
FreeMappedData();
+ SetFutureStatusAndClearPending(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
}
-void Buffer::SetFutureStatus(WGPUBufferMapAsyncStatus status) {
- if (!mPendingMapRequest) {
+bool Buffer::SetFutureStatus(WGPUBufferMapAsyncStatus status) {
+ DAWN_ASSERT(mMapStateData->pendingRequest);
+ return GetEventManager().SetFutureReady<MapAsyncEvent>(mMapStateData->pendingRequest->futureID,
+ status) == WireResult::Success;
+}
+
+void Buffer::SetFutureStatusAndClearPending(WGPUBufferMapAsyncStatus status) {
+ if (!mMapStateData->pendingRequest) {
+ // Since this is unconditionally called on destruction, we might not have a pending map
+ // request all the time.
return;
}
- DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent>(mPendingMapRequest->futureID,
- status) == WireResult::Success);
+
+ FutureID futureID = mMapStateData->pendingRequest->futureID;
+ mMapStateData->pendingRequest = std::nullopt;
+ DAWN_UNUSED(GetEventManager().SetFutureReady<MapAsyncEvent>(futureID, status));
+ return;
}
void Buffer::MapAsync(WGPUMapModeFlags mode,
@@ -345,12 +259,12 @@
Client* client = GetClient();
auto [futureIDInternal, tracked] =
- GetEventManager().TrackEvent(std::make_unique<MapAsyncEvent>(callbackInfo, this));
+ GetEventManager().TrackEvent(std::make_unique<MapAsyncEvent>(callbackInfo, mMapStateData));
if (!tracked) {
return {futureIDInternal};
}
- if (mPendingMapRequest) {
+ if (mMapStateData->pendingRequest) {
DAWN_UNUSED(GetEventManager().SetFutureReady<MapAsyncEvent>(
futureIDInternal, WGPUBufferMapAsyncStatus_MappingAlreadyPending));
return {futureIDInternal};
@@ -362,14 +276,14 @@
}
// Set up the request structure that will hold information while this mapping is in flight.
- std::optional<MapRequestType> mapMode;
+ MapRequestType mapMode = MapRequestType::None;
if (mode & WGPUMapMode_Read) {
mapMode = MapRequestType::Read;
} else if (mode & WGPUMapMode_Write) {
mapMode = MapRequestType::Write;
}
- mPendingMapRequest = {futureIDInternal, offset, size, mapMode};
+ mMapStateData->pendingRequest = {futureIDInternal, offset, size, mapMode};
// Serialize the command to send to the server.
BufferMapAsyncCmd cmd;
@@ -384,14 +298,63 @@
return {futureIDInternal};
}
-bool Client::DoBufferMapAsyncCallback(ObjectHandle eventManager,
- WGPUFuture future,
- WGPUBufferMapAsyncStatus status,
- uint64_t readDataUpdateInfoLength,
- const uint8_t* readDataUpdateInfo) {
- return GetEventManager(eventManager)
- .SetFutureReady<Buffer::MapAsyncEvent>(future.id, status, readDataUpdateInfoLength,
- readDataUpdateInfo) == WireResult::Success;
+bool Buffer::OnMapAsyncCallback(WGPUFuture future,
+ uint32_t status,
+ uint64_t readDataUpdateInfoLength,
+ const uint8_t* readDataUpdateInfo) {
+ // Check that the response doesn't correspond to a request that has already been rejected by
+ // unmap or destroy.
+ if (!mMapStateData->pendingRequest) {
+ return true;
+ }
+ MapRequestData& pendingRequest = mMapStateData->pendingRequest.value();
+ if (pendingRequest.futureID != future.id) {
+ return true;
+ }
+
+ auto FailRequest = [this]() -> bool {
+ SetFutureStatus(WGPUBufferMapAsyncStatus_DeviceLost);
+ return false;
+ };
+
+ if (status == WGPUBufferMapAsyncStatus_Success) {
+ switch (pendingRequest.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),
+ pendingRequest.offset, pendingRequest.size)) {
+ return FailRequest();
+ }
+ mMappedData = const_cast<void*>(mReadHandle->GetData());
+ break;
+ }
+ case MapRequestType::Write: {
+ if (mWriteHandle == nullptr) {
+ return FailRequest();
+ }
+ mMappedData = mWriteHandle->GetData();
+ break;
+ }
+ default:
+ DAWN_UNREACHABLE();
+ }
+
+ mMapOffset = pendingRequest.offset;
+ mMapSize = pendingRequest.size;
+ }
+
+ return SetFutureStatus(static_cast<WGPUBufferMapAsyncStatus>(status));
}
void* Buffer::GetMappedRange(size_t offset, size_t size) {
@@ -420,20 +383,23 @@
// - Server -> Client: Result of MapRequest2
Client* client = GetClient();
- if (IsMappedForWriting()) {
+ // mWriteHandle can still be nullptr if buffer has been destroyed before unmap
+ if ((mMapStateData->mapState == MapState::MappedForWrite ||
+ mMapStateData->mapState == 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.
// Get the serialization size of data update writes.
size_t writeDataUpdateInfoLength =
- mWriteHandle->SizeOfSerializeDataUpdate(mMappedOffset, mMappedSize);
+ mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
BufferUpdateMappedDataCmd cmd;
cmd.bufferId = GetWireId();
cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
cmd.writeDataUpdateInfo = nullptr;
- cmd.offset = mMappedOffset;
- cmd.size = mMappedSize;
+ cmd.offset = mMapOffset;
+ cmd.size = mMapSize;
client->SerializeCommand(
cmd, CommandExtension{writeDataUpdateInfoLength, [&](char* writeHandleBuffer) {
@@ -446,11 +412,11 @@
// 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 (mDestructWriteHandleOnUnmap) {
+ if (mMapStateData->mapState == MapState::MappedAtCreation && mDestructWriteHandleOnUnmap) {
mWriteHandle = nullptr;
if (mReadHandle) {
// If it's both mappedAtCreation and MapRead we need to reset
- // mData to readHandle's GetData(). This could be changed to
+ // mMappedData to readHandle's GetData(). This could be changed to
// merging read/write handle in future
mMappedData = const_cast<void*>(mReadHandle->GetData());
}
@@ -458,15 +424,15 @@
}
// Free map access tokens
- mMappedState = MapState::Unmapped;
- mMappedOffset = 0;
- mMappedSize = 0;
+ mMapStateData->mapState = MapState::Unmapped;
+ mMapOffset = 0;
+ mMapSize = 0;
BufferUnmapCmd cmd;
cmd.self = ToAPI(this);
client->SerializeCommand(cmd);
- SetFutureStatus(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback);
+ SetFutureStatusAndClearPending(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback);
}
void Buffer::Destroy() {
@@ -474,12 +440,13 @@
// Remove the current mapping and destroy Read/WriteHandles.
FreeMappedData();
+ mMapStateData->mapState = MapState::Unmapped;
BufferDestroyCmd cmd;
cmd.self = ToAPI(this);
client->SerializeCommand(cmd);
- SetFutureStatus(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
+ SetFutureStatusAndClearPending(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
}
WGPUBufferUsage Buffer::GetUsage() const {
@@ -491,13 +458,13 @@
}
WGPUBufferMapState Buffer::GetMapState() const {
- switch (mMappedState) {
+ switch (mMapStateData->mapState) {
case MapState::MappedForRead:
case MapState::MappedForWrite:
case MapState::MappedAtCreation:
return WGPUBufferMapState_Mapped;
case MapState::Unmapped:
- if (mPendingMapRequest) {
+ if (mMapStateData->pendingRequest) {
return WGPUBufferMapState_Pending;
} else {
return WGPUBufferMapState_Unmapped;
@@ -507,26 +474,27 @@
}
bool Buffer::IsMappedForReading() const {
- return mMappedState == MapState::MappedForRead;
+ return mMapStateData->mapState == MapState::MappedForRead;
}
bool Buffer::IsMappedForWriting() const {
- return mMappedState == MapState::MappedForWrite || mMappedState == MapState::MappedAtCreation;
+ return mMapStateData->mapState == MapState::MappedForWrite ||
+ mMapStateData->mapState == MapState::MappedAtCreation;
}
bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
- if (offset % 8 != 0 || offset < mMappedOffset || offset > mSize) {
+ if (offset % 8 != 0 || offset < mMapOffset || offset > mSize) {
return false;
}
size_t rangeSize = size == WGPU_WHOLE_MAP_SIZE ? mSize - offset : size;
- if (rangeSize % 4 != 0 || rangeSize > mMappedSize) {
+ if (rangeSize % 4 != 0 || rangeSize > mMapSize) {
return false;
}
- size_t offsetInMappedRange = offset - mMappedOffset;
- return offsetInMappedRange <= mMappedSize - rangeSize;
+ size_t offsetInMappedRange = offset - mMapOffset;
+ return offsetInMappedRange <= mMapSize - rangeSize;
}
void Buffer::FreeMappedData() {
@@ -535,16 +503,15 @@
// use-after-free of the mapped data. This is particularly useful for WebGPU test about the
// interaction of mapping and GC.
if (mMappedData) {
- memset(static_cast<uint8_t*>(mMappedData) + mMappedOffset, 0xCA, mMappedSize);
+ memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
}
#endif // defined(DAWN_ENABLE_ASSERTS)
- mMappedOffset = 0;
- mMappedSize = 0;
+ mMapOffset = 0;
+ mMapSize = 0;
mReadHandle = nullptr;
mWriteHandle = nullptr;
mMappedData = nullptr;
- mMappedState = MapState::Unmapped;
}
} // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/Buffer.h b/src/dawn/wire/client/Buffer.h
index 7747790..1c52d57 100644
--- a/src/dawn/wire/client/Buffer.h
+++ b/src/dawn/wire/client/Buffer.h
@@ -43,6 +43,28 @@
class Device;
+enum class MapRequestType { None, Read, Write };
+
+enum class MapState {
+ Unmapped,
+ MappedForRead,
+ MappedForWrite,
+ MappedAtCreation,
+};
+
+struct MapRequestData {
+ FutureID futureID = kNullFutureID;
+ size_t offset = 0;
+ size_t size = 0;
+ MapRequestType type = MapRequestType::None;
+};
+
+struct MapStateData : public RefCounted {
+ // Up to only one request can exist at a single time. Other requests are rejected.
+ std::optional<MapRequestData> pendingRequest = std::nullopt;
+ MapState mapState = MapState::Unmapped;
+};
+
class Buffer final : public ObjectWithEventsBase {
public:
static WGPUBuffer Create(Device* device, const WGPUBufferDescriptor* descriptor);
@@ -52,6 +74,10 @@
const WGPUBufferDescriptor* descriptor);
~Buffer() override;
+ bool OnMapAsyncCallback(WGPUFuture future,
+ uint32_t status,
+ uint64_t readDataUpdateInfoLength,
+ const uint8_t* readDataUpdateInfo);
void MapAsync(WGPUMapModeFlags mode,
size_t offset,
size_t size,
@@ -74,11 +100,9 @@
WGPUBufferMapState GetMapState() const;
private:
- friend class Client;
- class MapAsyncEvent;
-
// Prepares the callbacks to be called and potentially calls them
- void SetFutureStatus(WGPUBufferMapAsyncStatus status);
+ bool SetFutureStatus(WGPUBufferMapAsyncStatus status);
+ void SetFutureStatusAndClearPending(WGPUBufferMapAsyncStatus status);
bool IsMappedForReading() const;
bool IsMappedForWriting() const;
@@ -86,39 +110,25 @@
void FreeMappedData();
- const uint64_t mSize = 0;
- const WGPUBufferUsage mUsage;
- const bool mDestructWriteHandleOnUnmap;
+ // The map state is a shared resource with the TrackedEvent so that it is updated only when the
+ // callback is actually called. This is important for WaitAny and ProcessEvents cases where the
+ // server may have responded, but due to an early Unmap or Destroy before the corresponding
+ // WaitAny or ProcessEvents call, we need to update the callback result.
+ Ref<MapStateData> mMapStateData;
- std::weak_ptr<bool> mIsDeviceAlive;
-
- // Mapping members are mutable depending on the current map state.
- enum class MapRequestType { Read, Write };
- struct MapRequest {
- FutureID futureID = kNullFutureID;
- size_t offset = 0;
- size_t size = 0;
- // Because validation for request type is validated via the backend, we use an optional type
- // here. This is nullopt when an invalid request type is passed to the wire.
- std::optional<MapRequestType> type;
- };
- enum class MapState {
- Unmapped,
- MappedForRead,
- MappedForWrite,
- MappedAtCreation,
- };
- std::optional<MapRequest> mPendingMapRequest = std::nullopt;
- MapState mMappedState = MapState::Unmapped;
- // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire.
- raw_ptr<void, DanglingUntriaged> mMappedData = nullptr;
- size_t mMappedOffset = 0;
- size_t mMappedSize = 0;
+ uint64_t mSize = 0;
+ WGPUBufferUsage mUsage;
// Only one mapped pointer can be active at a time
// TODO(enga): Use a tagged pointer to save space.
std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
+ bool mDestructWriteHandleOnUnmap = false;
+
+ // TODO(https://crbug.com/dawn/2345): Investigate `DanglingUntriaged` in dawn/wire.
+ raw_ptr<void, DanglingUntriaged> mMappedData = nullptr;
+ size_t mMapOffset = 0;
+ size_t mMapSize = 0;
};
} // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/ClientDoers.cpp b/src/dawn/wire/client/ClientDoers.cpp
index 2ed46d8..210cc53 100644
--- a/src/dawn/wire/client/ClientDoers.cpp
+++ b/src/dawn/wire/client/ClientDoers.cpp
@@ -88,6 +88,20 @@
return device->OnPopErrorScopeCallback(requestSerial, errorType, message);
}
+// TODO(dawn:2061) May be able to move this to Buffer.cpp once we move all mapping logic.
+bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
+ ObjectHandle eventManager,
+ WGPUFuture future,
+ uint32_t status,
+ 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(future, status, readDataUpdateInfoLength, readDataUpdateInfo);
+}
+
bool Client::DoDeviceCreateComputePipelineAsyncCallback(Device* device,
uint64_t requestSerial,
WGPUCreatePipelineAsyncStatus status,
diff --git a/src/dawn/wire/client/EventManager.h b/src/dawn/wire/client/EventManager.h
index 8ea33ee..071b825 100644
--- a/src/dawn/wire/client/EventManager.h
+++ b/src/dawn/wire/client/EventManager.h
@@ -114,7 +114,7 @@
}
std::unique_ptr<TrackedEvent> spontaneousEvent;
- WireResult result = mTrackedEvents.Use([&](auto trackedEvents) {
+ WIRE_TRY(mTrackedEvents.Use([&](auto trackedEvents) {
auto it = trackedEvents->find(futureID);
if (it == trackedEvents->end()) {
// If the future is not found, it must've already been completed.
@@ -128,9 +128,8 @@
DAWN_ASSERT(trackedEvent->GetType() == Event::kType);
return WireResult::FatalError;
}
-
- WireResult result = static_cast<Event*>(trackedEvent.get())
- ->ReadyHook(futureID, std::forward<ReadyArgs>(readyArgs)...);
+ static_cast<Event*>(trackedEvent.get())
+ ->ReadyHook(std::forward<ReadyArgs>(readyArgs)...);
trackedEvent->SetReady();
// If the event can be spontaneously completed, prepare to do so now.
@@ -138,14 +137,14 @@
spontaneousEvent = std::move(trackedEvent);
trackedEvents->erase(futureID);
}
- return result;
- });
+ return WireResult::Success;
+ }));
// Handle spontaneous completions.
if (spontaneousEvent) {
spontaneousEvent->Complete(futureID, EventCompletionType::Ready);
}
- return result;
+ return WireResult::Success;
}
void ProcessPollEvents();
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index 95718ad..e67fc83 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -54,13 +54,12 @@
EventType GetType() override { return kType; }
- WireResult ReadyHook(FutureID futureID,
- WGPURequestAdapterStatus status,
- const char* message,
- const WGPUAdapterProperties* properties,
- const WGPUSupportedLimits* limits,
- uint32_t featuresCount,
- const WGPUFeatureName* features) {
+ void ReadyHook(WGPURequestAdapterStatus status,
+ const char* message,
+ const WGPUAdapterProperties* properties,
+ const WGPUSupportedLimits* limits,
+ uint32_t featuresCount,
+ const WGPUFeatureName* features) {
DAWN_ASSERT(mAdapter != nullptr);
mStatus = status;
if (message != nullptr) {
@@ -71,7 +70,6 @@
mAdapter->SetLimits(limits);
mAdapter->SetFeatures(features, featuresCount);
}
- return WireResult::Success;
}
private:
diff --git a/src/dawn/wire/client/Queue.cpp b/src/dawn/wire/client/Queue.cpp
index d897ba7..5337853 100644
--- a/src/dawn/wire/client/Queue.cpp
+++ b/src/dawn/wire/client/Queue.cpp
@@ -48,10 +48,7 @@
EventType GetType() override { return kType; }
- WireResult ReadyHook(FutureID futureID, WGPUQueueWorkDoneStatus status) {
- mStatus = status;
- return WireResult::Success;
- }
+ void ReadyHook(WGPUQueueWorkDoneStatus status) { mStatus = status; }
private:
void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 22e2769..ca1471c 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -225,6 +225,8 @@
bool isSuccess = status == WGPUBufferMapAsyncStatus_Success;
ReturnBufferMapAsyncCallbackCmd cmd;
+ // TODO(dawn:2061) Should be able to remove buffer once mapping is updated.
+ cmd.buffer = data->buffer;
cmd.eventManager = data->eventManager;
cmd.future = data->future;
cmd.status = status;