Implement Buffer::MapAsync
MapAsync in dawn_native is fully implemented and only missing
a couple cleanups that can be done once MapRead/WriteAsync are
removed.
MapAsync in dawn_wire is left as a pure shim on top of
MapRead/WriteAsync and will be transitioned to its own commands
in follow-ups.
All MapRead/WriteAsync end2end and validation tests are duplicated
for MapAsync.
Bug: dawn:445
Change-Id: Ib1430b9257149917be19a84f13e0ddd2a8eccc32
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24260
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp
index 1db64a7..f83b590 100644
--- a/src/dawn_native/Buffer.cpp
+++ b/src/dawn_native/Buffer.cpp
@@ -72,6 +72,10 @@
UNREACHABLE();
return {};
}
+ MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override {
+ UNREACHABLE();
+ return {};
+ }
void* GetMappedPointerImpl() override {
return mFakeMappedData.get();
}
@@ -245,6 +249,22 @@
}
}
+ void BufferBase::CallMapCallback(uint32_t serial, WGPUBufferMapAsyncStatus status) {
+ ASSERT(!IsError());
+ if (mMapCallback != nullptr && serial == mMapSerial) {
+ // Tag the callback as fired before firing it, otherwise it could fire a second time if
+ // for example buffer.Unmap() is called inside the application-provided callback.
+ WGPUBufferMapCallback callback = mMapCallback;
+ mMapCallback = nullptr;
+
+ if (GetDevice()->IsLost()) {
+ callback(WGPUBufferMapAsyncStatus_DeviceLost, mMapUserdata);
+ } else {
+ callback(status, mMapUserdata);
+ }
+ }
+ }
+
void BufferBase::SetSubData(uint64_t start, uint64_t count, const void* data) {
if (count > uint64_t(std::numeric_limits<size_t>::max())) {
GetDevice()->HandleError(InternalErrorType::Validation, "count too big");
@@ -252,11 +272,14 @@
Ref<QueueBase> queue = AcquireRef(GetDevice()->GetDefaultQueue());
GetDevice()->EmitDeprecationWarning(
- "Buffer::SetSubData is deprecated, use Queue::WriteBuffer instead");
+ "Buffer::SetSubData is deprecate. Use Queue::WriteBuffer instead");
queue->WriteBuffer(this, start, data, static_cast<size_t>(count));
}
void BufferBase::MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata) {
+ GetDevice()->EmitDeprecationWarning(
+ "Buffer::MapReadAsync is deprecated. Use Buffer::MapAsync instead");
+
WGPUBufferMapAsyncStatus status;
if (GetDevice()->ConsumedError(ValidateMap(wgpu::BufferUsage::MapRead, &status))) {
callback(status, nullptr, 0, userdata);
@@ -270,6 +293,7 @@
mMapSerial++;
mMapReadCallback = callback;
mMapUserdata = userdata;
+ mMapOffset = 0;
mState = BufferState::Mapped;
if (GetDevice()->ConsumedError(MapReadAsyncImpl())) {
@@ -278,10 +302,13 @@
}
MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker();
- tracker->Track(this, mMapSerial, false);
+ tracker->Track(this, mMapSerial, MapType::Read);
}
void BufferBase::MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata) {
+ GetDevice()->EmitDeprecationWarning(
+ "Buffer::MapReadAsync is deprecated. Use Buffer::MapAsync instead");
+
WGPUBufferMapAsyncStatus status;
if (GetDevice()->ConsumedError(ValidateMap(wgpu::BufferUsage::MapWrite, &status))) {
callback(status, nullptr, 0, userdata);
@@ -295,6 +322,7 @@
mMapSerial++;
mMapWriteCallback = callback;
mMapUserdata = userdata;
+ mMapOffset = 0;
mState = BufferState::Mapped;
if (GetDevice()->ConsumedError(MapWriteAsyncImpl())) {
@@ -303,7 +331,45 @@
}
MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker();
- tracker->Track(this, mMapSerial, true);
+ tracker->Track(this, mMapSerial, MapType::Write);
+ }
+
+ void BufferBase::MapAsync(wgpu::MapMode mode,
+ size_t offset,
+ size_t size,
+ WGPUBufferMapCallback callback,
+ void* userdata) {
+ // Handle the defaulting of size required by WebGPU, even if in webgpu_cpp.h it is not
+ // possible to default the function argument (because there is the callback later in the
+ // argument list)
+ if (size == 0 && offset < mSize) {
+ size = mSize - offset;
+ }
+
+ WGPUBufferMapAsyncStatus status;
+ if (GetDevice()->ConsumedError(ValidateMapAsync(mode, offset, size, &status))) {
+ if (callback) {
+ callback(status, userdata);
+ }
+ return;
+ }
+ ASSERT(!IsError());
+
+ // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
+ mMapSerial++;
+ mMapMode = mode;
+ mMapOffset = offset;
+ mMapCallback = callback;
+ mMapUserdata = userdata;
+ mState = BufferState::Mapped;
+
+ if (GetDevice()->ConsumedError(MapAsyncImpl(mode, offset, size))) {
+ CallMapCallback(mMapSerial, WGPUBufferMapAsyncStatus_DeviceLost);
+ return;
+ }
+
+ MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker();
+ tracker->Track(this, mMapSerial, MapType::Async);
}
void* BufferBase::GetMappedRange() {
@@ -322,12 +388,12 @@
}
if (mStagingBuffer != nullptr) {
- return mStagingBuffer->GetMappedPointer();
+ return static_cast<uint8_t*>(mStagingBuffer->GetMappedPointer()) + mMapOffset;
}
if (mSize == 0) {
return reinterpret_cast<uint8_t*>(intptr_t(0xCAFED00D));
}
- return GetMappedPointerImpl();
+ return static_cast<uint8_t*>(GetMappedPointerImpl()) + mMapOffset;
}
void BufferBase::Destroy() {
@@ -389,6 +455,7 @@
// CreateBufferMapped.
CallMapReadCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u);
CallMapWriteCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u);
+ CallMapCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown);
UnmapImpl();
mMapReadCallback = nullptr;
@@ -418,7 +485,7 @@
switch (mState) {
case BufferState::Mapped:
case BufferState::MappedAtCreation:
- return DAWN_VALIDATION_ERROR("Buffer already mapped");
+ return DAWN_VALIDATION_ERROR("Buffer is already mapped");
case BufferState::Destroyed:
return DAWN_VALIDATION_ERROR("Buffer is destroyed");
case BufferState::Unmapped:
@@ -433,6 +500,60 @@
return {};
}
+ MaybeError BufferBase::ValidateMapAsync(wgpu::MapMode mode,
+ size_t offset,
+ size_t size,
+ WGPUBufferMapAsyncStatus* status) const {
+ *status = WGPUBufferMapAsyncStatus_DeviceLost;
+ DAWN_TRY(GetDevice()->ValidateIsAlive());
+
+ *status = WGPUBufferMapAsyncStatus_Error;
+ DAWN_TRY(GetDevice()->ValidateObject(this));
+
+ if (offset % 4 != 0) {
+ return DAWN_VALIDATION_ERROR("offset must be a multiple of 4");
+ }
+
+ if (size % 4 != 0) {
+ return DAWN_VALIDATION_ERROR("size must be a multiple of 4");
+ }
+
+ if (uint64_t(offset) > mSize || uint64_t(size) > mSize - uint64_t(offset)) {
+ return DAWN_VALIDATION_ERROR("size + offset must fit in the buffer");
+ }
+
+ switch (mState) {
+ case BufferState::Mapped:
+ case BufferState::MappedAtCreation:
+ return DAWN_VALIDATION_ERROR("Buffer is already mapped");
+ case BufferState::Destroyed:
+ return DAWN_VALIDATION_ERROR("Buffer is destroyed");
+ case BufferState::Unmapped:
+ break;
+ }
+
+ bool isReadMode = mode & wgpu::MapMode::Read;
+ bool isWriteMode = mode & wgpu::MapMode::Write;
+ if (!(isReadMode ^ isWriteMode)) {
+ return DAWN_VALIDATION_ERROR("Exactly one of Read or Write mode must be set");
+ }
+
+ if (mode & wgpu::MapMode::Read) {
+ if (!(mUsage & wgpu::BufferUsage::MapRead)) {
+ return DAWN_VALIDATION_ERROR("The buffer must have the MapRead usage");
+ }
+ } else {
+ ASSERT(mode & wgpu::MapMode::Write);
+
+ if (!(mUsage & wgpu::BufferUsage::MapWrite)) {
+ return DAWN_VALIDATION_ERROR("The buffer must have the MapWrite usage");
+ }
+ }
+
+ *status = WGPUBufferMapAsyncStatus_Success;
+ return {};
+ }
+
bool BufferBase::CanGetMappedRange(bool writable) const {
// Note that:
//
@@ -448,6 +569,8 @@
return true;
case BufferState::Mapped:
+ // TODO(dawn:445): When mapRead/WriteAsync is removed, check against mMapMode
+ // instead of mUsage
ASSERT(bool(mUsage & wgpu::BufferUsage::MapRead) ^
bool(mUsage & wgpu::BufferUsage::MapWrite));
return !writable || (mUsage & wgpu::BufferUsage::MapWrite);
@@ -495,16 +618,19 @@
mState = BufferState::Destroyed;
}
- bool BufferBase::IsMapped() const {
- return mState == BufferState::Mapped;
- }
-
- void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) {
- void* data = GetMappedRangeInternal(isWrite);
- if (isWrite) {
- CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
- } else {
- CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, data, GetSize());
+ void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, MapType type) {
+ switch (type) {
+ case MapType::Read:
+ CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success,
+ GetMappedRangeInternal(false), GetSize());
+ break;
+ case MapType::Write:
+ CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success,
+ GetMappedRangeInternal(true), GetSize());
+ break;
+ case MapType::Async:
+ CallMapCallback(mapSerial, WGPUBufferMapAsyncStatus_Success);
+ break;
}
}