Implement CreateBufferMappedAsync in dawn_wire and dawn_native

Bug: dawn:7
Change-Id: I1a4e5eece53d83a4c5395ba6b41df2e20668c5bb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8120
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2019-06-14 17:35:56 +00:00 committed by Commit Bot service account
parent d1b4b5cba5
commit 21eba761b5
11 changed files with 596 additions and 97 deletions

View File

@ -160,6 +160,9 @@
} }
] ]
}, },
"buffer create mapped callback": {
"category": "natively defined"
},
"buffer copy view": { "buffer copy view": {
"category": "structure", "category": "structure",
"extensible": true, "extensible": true,
@ -417,6 +420,14 @@
{"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"} {"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"}
] ]
}, },
{
"name": "create buffer mapped async",
"args": [
{"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"},
{"name": "callback", "type": "buffer create mapped callback"},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},
{ {
"name": "create command encoder", "name": "create command encoder",
"returns": "command encoder" "returns": "command encoder"

View File

@ -36,6 +36,12 @@
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" }, { "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" } { "name": "result", "type": "ObjectHandle", "handle_type": "buffer" }
], ],
"device create buffer mapped async": [
{ "name": "device", "type": "device" },
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
{ "name": "request serial", "type": "uint32_t" },
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" }
],
"destroy object": [ "destroy object": [
{ "name": "object type", "type": "ObjectType" }, { "name": "object type", "type": "ObjectType" },
{ "name": "object id", "type": "ObjectId" } { "name": "object id", "type": "ObjectId" }
@ -75,6 +81,7 @@
"BufferSetSubData", "BufferSetSubData",
"BufferUnmap", "BufferUnmap",
"DeviceCreateBufferMapped", "DeviceCreateBufferMapped",
"DeviceCreateBufferMappedAsync",
"QueueCreateFence", "QueueCreateFence",
"FenceGetCompletedValue", "FenceGetCompletedValue",
"QueueSignal" "QueueSignal"

View File

@ -49,6 +49,9 @@
// Custom types depending on the target language // Custom types depending on the target language
typedef void (*DawnDeviceErrorCallback)(const char* message, void* userdata); typedef void (*DawnDeviceErrorCallback)(const char* message, void* userdata);
typedef void (*DawnBufferCreateMappedCallback)(DawnBufferMapAsyncStatus status,
DawnCreateBufferMappedResult result,
void* userdata);
typedef void (*DawnBufferMapReadCallback)(DawnBufferMapAsyncStatus status, typedef void (*DawnBufferMapReadCallback)(DawnBufferMapAsyncStatus status,
const void* data, const void* data,
uint64_t dataLength, uint64_t dataLength,

View File

@ -60,6 +60,17 @@ void ProcTableAsClass::DeviceSetErrorCallback(DawnDevice self,
OnDeviceSetErrorCallback(self, callback, userdata); OnDeviceSetErrorCallback(self, callback, userdata);
} }
void ProcTableAsClass::DeviceCreateBufferMappedAsync(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
void* userdata) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
object->createBufferMappedCallback = callback;
object->userdata1 = userdata;
OnDeviceCreateBufferMappedAsyncCallback(self, descriptor, callback, userdata);
}
void ProcTableAsClass::BufferMapReadAsync(DawnBuffer self, void ProcTableAsClass::BufferMapReadAsync(DawnBuffer self,
DawnBufferMapReadCallback callback, DawnBufferMapReadCallback callback,
void* userdata) { void* userdata) {
@ -95,12 +106,16 @@ void ProcTableAsClass::CallDeviceErrorCallback(DawnDevice device, const char* me
auto object = reinterpret_cast<ProcTableAsClass::Object*>(device); auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
object->deviceErrorCallback(message, object->userdata1); object->deviceErrorCallback(message, object->userdata1);
} }
void ProcTableAsClass::CallMapReadCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, const void* data, uint32_t dataLength) { void ProcTableAsClass::CallCreateBufferMappedCallback(DawnDevice device, DawnBufferMapAsyncStatus status, DawnCreateBufferMappedResult result) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
object->createBufferMappedCallback(status, result, object->userdata1);
}
void ProcTableAsClass::CallMapReadCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, const void* data, uint64_t dataLength) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(buffer); auto object = reinterpret_cast<ProcTableAsClass::Object*>(buffer);
object->mapReadCallback(status, data, dataLength, object->userdata1); object->mapReadCallback(status, data, dataLength, object->userdata1);
} }
void ProcTableAsClass::CallMapWriteCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, void* data, uint32_t dataLength) { void ProcTableAsClass::CallMapWriteCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, void* data, uint64_t dataLength) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(buffer); auto object = reinterpret_cast<ProcTableAsClass::Object*>(buffer);
object->mapWriteCallback(status, data, dataLength, object->userdata1); object->mapWriteCallback(status, data, dataLength, object->userdata1);
} }

View File

@ -54,6 +54,10 @@ class ProcTableAsClass {
void DeviceSetErrorCallback(DawnDevice self, void DeviceSetErrorCallback(DawnDevice self,
DawnDeviceErrorCallback callback, DawnDeviceErrorCallback callback,
void* userdata); void* userdata);
void DeviceCreateBufferMappedAsync(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
void* userdata);
void BufferMapReadAsync(DawnBuffer self, void BufferMapReadAsync(DawnBuffer self,
DawnBufferMapReadCallback callback, DawnBufferMapReadCallback callback,
void* userdata); void* userdata);
@ -69,6 +73,10 @@ class ProcTableAsClass {
virtual void OnDeviceSetErrorCallback(DawnDevice device, virtual void OnDeviceSetErrorCallback(DawnDevice device,
DawnDeviceErrorCallback callback, DawnDeviceErrorCallback callback,
void* userdata) = 0; void* userdata) = 0;
virtual void OnDeviceCreateBufferMappedAsyncCallback(DawnDevice self,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
void* userdata) = 0;
virtual void OnBufferMapReadAsyncCallback(DawnBuffer buffer, virtual void OnBufferMapReadAsyncCallback(DawnBuffer buffer,
DawnBufferMapReadCallback callback, DawnBufferMapReadCallback callback,
void* userdata) = 0; void* userdata) = 0;
@ -82,13 +90,15 @@ class ProcTableAsClass {
// Calls the stored callbacks // Calls the stored callbacks
void CallDeviceErrorCallback(DawnDevice device, const char* message); void CallDeviceErrorCallback(DawnDevice device, const char* message);
void CallMapReadCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, const void* data, uint32_t dataLength); void CallCreateBufferMappedCallback(DawnDevice device, DawnBufferMapAsyncStatus status, DawnCreateBufferMappedResult result);
void CallMapWriteCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, void* data, uint32_t dataLength); void CallMapReadCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, const void* data, uint64_t dataLength);
void CallMapWriteCallback(DawnBuffer buffer, DawnBufferMapAsyncStatus status, void* data, uint64_t dataLength);
void CallFenceOnCompletionCallback(DawnFence fence, DawnFenceCompletionStatus status); void CallFenceOnCompletionCallback(DawnFence fence, DawnFenceCompletionStatus status);
struct Object { struct Object {
ProcTableAsClass* procs = nullptr; ProcTableAsClass* procs = nullptr;
DawnDeviceErrorCallback deviceErrorCallback = nullptr; DawnDeviceErrorCallback deviceErrorCallback = nullptr;
DawnBufferCreateMappedCallback createBufferMappedCallback = nullptr;
DawnBufferMapReadCallback mapReadCallback = nullptr; DawnBufferMapReadCallback mapReadCallback = nullptr;
DawnBufferMapWriteCallback mapWriteCallback = nullptr; DawnBufferMapWriteCallback mapWriteCallback = nullptr;
DawnFenceOnCompletionCallback fenceOnCompletionCallback = nullptr; DawnFenceOnCompletionCallback fenceOnCompletionCallback = nullptr;
@ -124,6 +134,7 @@ class MockProcTable : public ProcTableAsClass {
{% endfor %} {% endfor %}
MOCK_METHOD3(OnDeviceSetErrorCallback, void(DawnDevice device, DawnDeviceErrorCallback callback, void* userdata)); MOCK_METHOD3(OnDeviceSetErrorCallback, void(DawnDevice device, DawnDeviceErrorCallback callback, void* userdata));
MOCK_METHOD4(OnDeviceCreateBufferMappedAsyncCallback, void(DawnDevice device, const DawnBufferDescriptor* descriptor, DawnBufferCreateMappedCallback callback, void* userdata));
MOCK_METHOD3(OnBufferMapReadAsyncCallback, void(DawnBuffer buffer, DawnBufferMapReadCallback callback, void* userdata)); MOCK_METHOD3(OnBufferMapReadAsyncCallback, void(DawnBuffer buffer, DawnBufferMapReadCallback callback, void* userdata));
MOCK_METHOD3(OnBufferMapWriteAsyncCallback, void(DawnBuffer buffer, DawnBufferMapWriteCallback callback, void* userdata)); MOCK_METHOD3(OnBufferMapWriteAsyncCallback, void(DawnBuffer buffer, DawnBufferMapWriteCallback callback, void* userdata));
MOCK_METHOD4(OnFenceOnCompletionCallback, MOCK_METHOD4(OnFenceOnCompletionCallback,

View File

@ -68,6 +68,7 @@ namespace dawn_native {
DeviceBase::~DeviceBase() { DeviceBase::~DeviceBase() {
// Devices must explicitly free the uploader // Devices must explicitly free the uploader
ASSERT(mDynamicUploader == nullptr); ASSERT(mDynamicUploader == nullptr);
ASSERT(mDeferredCreateBufferMappedAsyncResults.empty());
} }
void DeviceBase::HandleError(const char* message) { void DeviceBase::HandleError(const char* message) {
@ -291,6 +292,25 @@ namespace dawn_native {
return result; return result;
} }
void DeviceBase::CreateBufferMappedAsync(const BufferDescriptor* descriptor,
dawn::BufferCreateMappedCallback callback,
void* userdata) {
DawnCreateBufferMappedResult result = CreateBufferMapped(descriptor);
DawnBufferMapAsyncStatus status = DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS;
if (result.data == nullptr || result.dataLength != descriptor->size) {
status = DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR;
}
DeferredCreateBufferMappedAsync deferred_info;
deferred_info.callback = callback;
deferred_info.status = status;
deferred_info.result = result;
deferred_info.userdata = userdata;
// The callback is deferred so it matches the async behavior of WebGPU.
mDeferredCreateBufferMappedAsyncResults.push_back(deferred_info);
}
CommandEncoderBase* DeviceBase::CreateCommandEncoder() { CommandEncoderBase* DeviceBase::CreateCommandEncoder() {
return new CommandEncoderBase(this); return new CommandEncoderBase(this);
} }
@ -387,6 +407,12 @@ namespace dawn_native {
void DeviceBase::Tick() { void DeviceBase::Tick() {
TickImpl(); TickImpl();
{
auto deferredResults = std::move(mDeferredCreateBufferMappedAsyncResults);
for (const auto& deferred : deferredResults) {
deferred.callback(deferred.status, deferred.result, deferred.userdata);
}
}
mFenceSignalTracker->Tick(GetCompletedCommandSerial()); mFenceSignalTracker->Tick(GetCompletedCommandSerial());
} }

View File

@ -108,6 +108,9 @@ namespace dawn_native {
BindGroupLayoutBase* CreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor); BindGroupLayoutBase* CreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor);
BufferBase* CreateBuffer(const BufferDescriptor* descriptor); BufferBase* CreateBuffer(const BufferDescriptor* descriptor);
DawnCreateBufferMappedResult CreateBufferMapped(const BufferDescriptor* descriptor); DawnCreateBufferMappedResult CreateBufferMapped(const BufferDescriptor* descriptor);
void CreateBufferMappedAsync(const BufferDescriptor* descriptor,
dawn::BufferCreateMappedCallback callback,
void* userdata);
CommandEncoderBase* CreateCommandEncoder(); CommandEncoderBase* CreateCommandEncoder();
ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor); ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor);
PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor); PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor);
@ -202,7 +205,15 @@ namespace dawn_native {
struct Caches; struct Caches;
std::unique_ptr<Caches> mCaches; std::unique_ptr<Caches> mCaches;
struct DeferredCreateBufferMappedAsync {
dawn::BufferCreateMappedCallback callback;
DawnBufferMapAsyncStatus status;
DawnCreateBufferMappedResult result;
void* userdata;
};
std::unique_ptr<FenceSignalTracker> mFenceSignalTracker; std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
std::vector<DeferredCreateBufferMappedAsync> mDeferredCreateBufferMappedAsyncResults;
dawn::DeviceErrorCallback mErrorCallback = nullptr; dawn::DeviceErrorCallback mErrorCallback = nullptr;
void* mErrorUserdata = 0; void* mErrorUserdata = 0;

View File

@ -101,6 +101,57 @@ namespace dawn_wire { namespace client {
return result; return result;
} }
void ClientDeviceCreateBufferMappedAsync(DawnDevice cDevice,
const DawnBufferDescriptor* descriptor,
DawnBufferCreateMappedCallback callback,
void* userdata) {
Device* device = reinterpret_cast<Device*>(cDevice);
Client* wireClient = device->GetClient();
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
Buffer* buffer = bufferObjectAndSerial->object.get();
uint32_t serial = buffer->requestSerial++;
struct CreateBufferMappedInfo {
DawnBuffer buffer;
DawnBufferCreateMappedCallback callback;
void* userdata;
};
CreateBufferMappedInfo* info = new CreateBufferMappedInfo;
info->buffer = reinterpret_cast<DawnBuffer>(buffer);
info->callback = callback;
info->userdata = userdata;
Buffer::MapRequestData request;
request.writeCallback = [](DawnBufferMapAsyncStatus status, void* data, uint64_t dataLength,
void* userdata) {
auto info = std::unique_ptr<CreateBufferMappedInfo>(
static_cast<CreateBufferMappedInfo*>(userdata));
DawnCreateBufferMappedResult result;
result.buffer = info->buffer;
result.data = data;
result.dataLength = dataLength;
info->callback(status, result, info->userdata);
};
request.userdata = info;
request.isWrite = true;
buffer->requests[serial] = request;
DeviceCreateBufferMappedAsyncCmd cmd;
cmd.device = cDevice;
cmd.descriptor = descriptor;
cmd.requestSerial = serial;
cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->serial};
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(wireClient->GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer, *wireClient);
}
uint64_t ClientFenceGetCompletedValue(DawnFence cSelf) { uint64_t ClientFenceGetCompletedValue(DawnFence cSelf) {
auto fence = reinterpret_cast<Fence*>(cSelf); auto fence = reinterpret_cast<Fence*>(cSelf);
return fence->completedValue; return fence->completedValue;

View File

@ -86,6 +86,32 @@ namespace dawn_wire { namespace server {
return true; return true;
} }
bool Server::DoDeviceCreateBufferMappedAsync(DawnDevice device,
const DawnBufferDescriptor* descriptor,
uint32_t requestSerial,
ObjectHandle bufferResult) {
if (!DoDeviceCreateBufferMapped(device, descriptor, bufferResult)) {
return false;
}
auto* bufferData = BufferObjects().Get(bufferResult.id);
ASSERT(bufferData != nullptr);
ReturnBufferMapWriteAsyncCallbackCmd cmd;
cmd.buffer = ObjectHandle{bufferResult.id, bufferResult.serial};
cmd.requestSerial = requestSerial;
cmd.status = bufferData->mapWriteState == BufferMapWriteState::Mapped
? DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS
: DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR;
cmd.dataLength = bufferData->mappedDataSize;
size_t requiredSize = cmd.GetRequiredSize();
char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize));
cmd.Serialize(allocatedBuffer);
return true;
}
bool Server::DoBufferSetSubDataInternal(ObjectId bufferId, bool Server::DoBufferSetSubDataInternal(ObjectId bufferId,
uint64_t start, uint64_t start,
uint64_t offset, uint64_t offset,

View File

@ -226,6 +226,7 @@ DAWN_INSTANTIATE_TEST(BufferSetSubDataTests,
OpenGLBackend, OpenGLBackend,
VulkanBackend); VulkanBackend);
// TODO(enga): These tests should use the testing toggle to initialize resources to 1.
class CreateBufferMappedTests : public DawnTest { class CreateBufferMappedTests : public DawnTest {
protected: protected:
static void MapReadCallback(DawnBufferMapAsyncStatus status, static void MapReadCallback(DawnBufferMapAsyncStatus status,
@ -248,37 +249,97 @@ class CreateBufferMappedTests : public DawnTest {
return mappedData; return mappedData;
} }
void CheckResultStartsZeroed(const dawn::CreateBufferMappedResult& result, uint64_t size) {
ASSERT_EQ(result.dataLength, size);
for (uint64_t i = 0; i < result.dataLength; ++i) {
uint8_t value = *(reinterpret_cast<uint8_t*>(result.data) + i);
ASSERT_EQ(value, 0u);
}
}
dawn::CreateBufferMappedResult CreateBufferMapped(dawn::BufferUsageBit usage, uint64_t size) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = size;
descriptor.usage = usage;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
CheckResultStartsZeroed(result, size);
return result;
}
dawn::CreateBufferMappedResult CreateBufferMappedWithData(dawn::BufferUsageBit usage,
const std::vector<uint32_t>& data) {
size_t byteLength = data.size() * sizeof(uint32_t);
dawn::CreateBufferMappedResult result = CreateBufferMapped(usage, byteLength);
memcpy(result.data, data.data(), byteLength);
return result;
}
template <DawnBufferMapAsyncStatus expectedStatus = DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS>
dawn::CreateBufferMappedResult CreateBufferMappedAsyncAndWait(dawn::BufferUsageBit usage,
uint64_t size) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = size;
descriptor.usage = usage;
struct ResultInfo {
dawn::CreateBufferMappedResult result;
bool done = false;
} resultInfo;
device.CreateBufferMappedAsync(
&descriptor,
[](DawnBufferMapAsyncStatus status, DawnCreateBufferMappedResult result,
void* userdata) {
ASSERT_EQ(status, expectedStatus);
auto* resultInfo = reinterpret_cast<ResultInfo*>(userdata);
resultInfo->result.buffer = dawn::Buffer::Acquire(result.buffer);
resultInfo->result.data = result.data;
resultInfo->result.dataLength = result.dataLength;
resultInfo->done = true;
},
&resultInfo);
while (!resultInfo.done) {
WaitABit();
}
CheckResultStartsZeroed(resultInfo.result, size);
return resultInfo.result;
}
dawn::CreateBufferMappedResult CreateBufferMappedAsyncWithDataAndWait(
dawn::BufferUsageBit usage,
const std::vector<uint32_t>& data) {
size_t byteLength = data.size() * sizeof(uint32_t);
dawn::CreateBufferMappedResult result = CreateBufferMappedAsyncAndWait(usage, byteLength);
memcpy(result.data, data.data(), byteLength);
return result;
}
private: private:
const void* mappedData = nullptr; const void* mappedData = nullptr;
}; };
// Test that the simplest CreateBufferMapped works for MapWrite buffers. // Test that the simplest CreateBufferMapped works for MapWrite buffers.
TEST_P(CreateBufferMappedTests, MapWriteUsageSmall) { TEST_P(CreateBufferMappedTests, MapWriteUsageSmall) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
uint32_t myData = 230502; uint32_t myData = 230502;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); dawn::CreateBufferMappedResult result = CreateBufferMappedWithData(
ASSERT_EQ(result.dataLength, descriptor.size); dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
memcpy(result.data, &myData, sizeof(myData));
result.buffer.Unmap(); result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
} }
// Test that the simplest CreateBufferMapped works for MapRead buffers. // Test that the simplest CreateBufferMapped works for MapRead buffers.
TEST_P(CreateBufferMappedTests, MapReadUsageSmall) { TEST_P(CreateBufferMappedTests, MapReadUsageSmall) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapRead;
uint32_t myData = 230502; uint32_t myData = 230502;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); dawn::CreateBufferMappedResult result =
ASSERT_EQ(result.dataLength, descriptor.size); CreateBufferMappedWithData(dawn::BufferUsageBit::MapRead, {myData});
memcpy(result.data, &myData, sizeof(myData));
result.buffer.Unmap(); result.buffer.Unmap();
const void* mappedData = MapReadAsyncAndWait(result.buffer); const void* mappedData = MapReadAsyncAndWait(result.buffer);
@ -288,15 +349,9 @@ TEST_P(CreateBufferMappedTests, MapReadUsageSmall) {
// Test that the simplest CreateBufferMapped works for non-mappable buffers. // Test that the simplest CreateBufferMapped works for non-mappable buffers.
TEST_P(CreateBufferMappedTests, NonMappableUsageSmall) { TEST_P(CreateBufferMappedTests, NonMappableUsageSmall) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::TransferSrc;
uint32_t myData = 4239; uint32_t myData = 4239;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); dawn::CreateBufferMappedResult result =
ASSERT_EQ(result.dataLength, descriptor.size); CreateBufferMappedWithData(dawn::BufferUsageBit::TransferSrc, {myData});
memcpy(result.data, &myData, sizeof(myData));
result.buffer.Unmap(); result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
@ -310,14 +365,8 @@ TEST_P(CreateBufferMappedTests, MapWriteUsageLarge) {
myData.push_back(i); myData.push_back(i);
} }
dawn::BufferDescriptor descriptor; dawn::CreateBufferMappedResult result = CreateBufferMappedWithData(
descriptor.nextInChain = nullptr; dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
descriptor.size = static_cast<uint64_t>(kDataSize * sizeof(uint32_t));
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
ASSERT_EQ(result.dataLength, descriptor.size);
memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t));
result.buffer.Unmap(); result.buffer.Unmap();
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize); EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize);
@ -331,14 +380,8 @@ TEST_P(CreateBufferMappedTests, MapReadUsageLarge) {
myData.push_back(i); myData.push_back(i);
} }
dawn::BufferDescriptor descriptor; dawn::CreateBufferMappedResult result =
descriptor.nextInChain = nullptr; CreateBufferMappedWithData(dawn::BufferUsageBit::MapRead, myData);
descriptor.size = static_cast<uint64_t>(kDataSize * sizeof(uint32_t));
descriptor.usage = dawn::BufferUsageBit::MapRead;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
ASSERT_EQ(result.dataLength, descriptor.size);
memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t));
result.buffer.Unmap(); result.buffer.Unmap();
const void* mappedData = MapReadAsyncAndWait(result.buffer); const void* mappedData = MapReadAsyncAndWait(result.buffer);
@ -354,59 +397,19 @@ TEST_P(CreateBufferMappedTests, NonMappableUsageLarge) {
myData.push_back(i); myData.push_back(i);
} }
dawn::BufferDescriptor descriptor; dawn::CreateBufferMappedResult result =
descriptor.nextInChain = nullptr; CreateBufferMappedWithData(dawn::BufferUsageBit::TransferSrc, {myData});
descriptor.size = static_cast<uint64_t>(kDataSize * sizeof(uint32_t));
descriptor.usage = dawn::BufferUsageBit::TransferSrc;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
ASSERT_EQ(result.dataLength, descriptor.size);
memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t));
result.buffer.Unmap(); result.buffer.Unmap();
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize); EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize);
} }
// Test that CreateBufferMapped returns zero-initialized data
// TODO(enga): This should use the testing toggle to initialize resources to 1.
TEST_P(CreateBufferMappedTests, MappableZeroInitialized) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
ASSERT_EQ(result.dataLength, descriptor.size);
ASSERT_EQ(*reinterpret_cast<uint8_t*>(result.data), 0);
result.buffer.Unmap();
}
// Test that CreateBufferMapped returns zero-initialized data
// TODO(enga): This should use the testing toggle to initialize resources to 1.
TEST_P(CreateBufferMappedTests, NonMappableZeroInitialized) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::TransferSrc;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
ASSERT_EQ(result.dataLength, descriptor.size);
ASSERT_EQ(*reinterpret_cast<uint8_t*>(result.data), 0);
result.buffer.Unmap();
}
// Test that mapping a buffer is valid after CreateBufferMapped and Unmap // Test that mapping a buffer is valid after CreateBufferMapped and Unmap
TEST_P(CreateBufferMappedTests, CreateThenMapSuccess) { TEST_P(CreateBufferMappedTests, CreateThenMapSuccess) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
static uint32_t myData = 230502; static uint32_t myData = 230502;
static uint32_t myData2 = 1337; static uint32_t myData2 = 1337;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); dawn::CreateBufferMappedResult result = CreateBufferMappedWithData(
ASSERT_EQ(result.dataLength, descriptor.size); dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
memcpy(result.data, &myData, sizeof(myData));
result.buffer.Unmap(); result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
@ -432,15 +435,9 @@ TEST_P(CreateBufferMappedTests, CreateThenMapSuccess) {
// Test that is is invalid to map a buffer twice when using CreateBufferMapped // Test that is is invalid to map a buffer twice when using CreateBufferMapped
TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) { TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) {
dawn::BufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
uint32_t myData = 230502; uint32_t myData = 230502;
dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); dawn::CreateBufferMappedResult result = CreateBufferMappedWithData(
ASSERT_EQ(result.dataLength, descriptor.size); dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
memcpy(result.data, &myData, sizeof(myData));
ASSERT_DEVICE_ERROR([&]() { ASSERT_DEVICE_ERROR([&]() {
bool done = false; bool done = false;
@ -463,6 +460,140 @@ TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) {
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
} }
// Test that the simplest CreateBufferMappedAsync works for MapWrite buffers.
TEST_P(CreateBufferMappedTests, MapWriteUsageSmallAsync) {
uint32_t myData = 230502;
dawn::CreateBufferMappedResult result = CreateBufferMappedAsyncWithDataAndWait(
dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
}
// Test that the simplest CreateBufferMappedAsync works for MapRead buffers.
TEST_P(CreateBufferMappedTests, MapReadUsageSmallAsync) {
uint32_t myData = 230502;
dawn::CreateBufferMappedResult result =
CreateBufferMappedAsyncWithDataAndWait(dawn::BufferUsageBit::MapRead, {myData});
result.buffer.Unmap();
const void* mappedData = MapReadAsyncAndWait(result.buffer);
ASSERT_EQ(myData, *reinterpret_cast<const uint32_t*>(mappedData));
result.buffer.Unmap();
}
// Test that the simplest CreateBufferMappedAsync works for non-mappable buffers.
TEST_P(CreateBufferMappedTests, NonMappableUsageSmallAsync) {
uint32_t myData = 4239;
dawn::CreateBufferMappedResult result =
CreateBufferMappedAsyncWithDataAndWait(dawn::BufferUsageBit::TransferSrc, {myData});
result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
}
// Test CreateBufferMappedAsync for a large MapWrite buffer
TEST_P(CreateBufferMappedTests, MapWriteUsageLargeAsync) {
constexpr uint64_t kDataSize = 1000 * 1000;
std::vector<uint32_t> myData;
for (uint32_t i = 0; i < kDataSize; ++i) {
myData.push_back(i);
}
dawn::CreateBufferMappedResult result = CreateBufferMappedAsyncWithDataAndWait(
dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
result.buffer.Unmap();
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize);
}
// Test CreateBufferMappedAsync for a large MapRead buffer
TEST_P(CreateBufferMappedTests, MapReadUsageLargeAsync) {
constexpr uint64_t kDataSize = 1000 * 1000;
std::vector<uint32_t> myData;
for (uint32_t i = 0; i < kDataSize; ++i) {
myData.push_back(i);
}
dawn::CreateBufferMappedResult result =
CreateBufferMappedAsyncWithDataAndWait(dawn::BufferUsageBit::MapRead, {myData});
result.buffer.Unmap();
const void* mappedData = MapReadAsyncAndWait(result.buffer);
ASSERT_EQ(0, memcmp(mappedData, myData.data(), kDataSize * sizeof(uint32_t)));
result.buffer.Unmap();
}
// Test CreateBufferMappedAsync for a large non-mappable buffer
TEST_P(CreateBufferMappedTests, NonMappableUsageLargeAsync) {
constexpr uint64_t kDataSize = 1000 * 1000;
std::vector<uint32_t> myData;
for (uint32_t i = 0; i < kDataSize; ++i) {
myData.push_back(i);
}
dawn::CreateBufferMappedResult result =
CreateBufferMappedAsyncWithDataAndWait(dawn::BufferUsageBit::TransferSrc, {myData});
result.buffer.Unmap();
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize);
}
// Test that mapping a buffer is valid after CreateBufferMappedAsync and Unmap
TEST_P(CreateBufferMappedTests, CreateThenMapSuccessAsync) {
static uint32_t myData = 230502;
static uint32_t myData2 = 1337;
dawn::CreateBufferMappedResult result = CreateBufferMappedAsyncWithDataAndWait(
dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
bool done = false;
result.buffer.MapWriteAsync(
[](DawnBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) {
ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status);
ASSERT_NE(nullptr, data);
*static_cast<uint32_t*>(data) = myData2;
*static_cast<bool*>(userdata) = true;
},
&done);
while (!done) {
WaitABit();
}
result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData2, result.buffer, 0);
}
// Test that is is invalid to map a buffer twice when using CreateBufferMappedAsync
TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailureAsync) {
uint32_t myData = 230502;
dawn::CreateBufferMappedResult result = CreateBufferMappedAsyncWithDataAndWait(
dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc, {myData});
ASSERT_DEVICE_ERROR([&]() {
bool done = false;
result.buffer.MapWriteAsync(
[](DawnBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) {
ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, status);
ASSERT_EQ(nullptr, data);
*static_cast<bool*>(userdata) = true;
},
&done);
while (!done) {
WaitABit();
}
}());
// CreateBufferMappedAsync is unaffected by the MapWrite error.
result.buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
}
DAWN_INSTANTIATE_TEST(CreateBufferMappedTests, DAWN_INSTANTIATE_TEST(CreateBufferMappedTests,
D3D12Backend, D3D12Backend,
MetalBackend, MetalBackend,

View File

@ -59,6 +59,28 @@ namespace {
mockBufferMapWriteCallback->Call(status, lastMapWritePointer, dataLength, userdata); mockBufferMapWriteCallback->Call(status, lastMapWritePointer, dataLength, userdata);
} }
class MockBufferCreateMappedCallback {
public:
MOCK_METHOD5(Call,
void(DawnBufferMapAsyncStatus status,
DawnBuffer buffer,
uint32_t* ptr,
uint64_t dataLength,
void* userdata));
};
std::unique_ptr<StrictMock<MockBufferCreateMappedCallback>> mockCreateBufferMappedCallback;
uint32_t* lastCreateMappedPointer = nullptr;
void ToMockCreateBufferMappedCallback(DawnBufferMapAsyncStatus status,
DawnCreateBufferMappedResult result,
void* userdata) {
// Assume the data is uint32_t to make writing matchers easier
lastCreateMappedPointer = static_cast<uint32_t*>(result.data);
// Unpack DawnCreateBufferMappedResult to make writing matchers easier
mockCreateBufferMappedCallback->Call(status, result.buffer, lastCreateMappedPointer,
result.dataLength, userdata);
}
} // anonymous namespace } // anonymous namespace
class WireBufferMappingTests : public WireTest { class WireBufferMappingTests : public WireTest {
@ -72,6 +94,8 @@ class WireBufferMappingTests : public WireTest {
mockBufferMapReadCallback = std::make_unique<StrictMock<MockBufferMapReadCallback>>(); mockBufferMapReadCallback = std::make_unique<StrictMock<MockBufferMapReadCallback>>();
mockBufferMapWriteCallback = std::make_unique<StrictMock<MockBufferMapWriteCallback>>(); mockBufferMapWriteCallback = std::make_unique<StrictMock<MockBufferMapWriteCallback>>();
mockCreateBufferMappedCallback =
std::make_unique<StrictMock<MockBufferCreateMappedCallback>>();
DawnBufferDescriptor descriptor; DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr; descriptor.nextInChain = nullptr;
@ -91,6 +115,7 @@ class WireBufferMappingTests : public WireTest {
// Delete mocks so that expectations are checked // Delete mocks so that expectations are checked
mockBufferMapReadCallback = nullptr; mockBufferMapReadCallback = nullptr;
mockBufferMapWriteCallback = nullptr; mockBufferMapWriteCallback = nullptr;
mockCreateBufferMappedCallback = nullptr;
} }
void FlushServer() { void FlushServer() {
@ -98,6 +123,7 @@ class WireBufferMappingTests : public WireTest {
Mock::VerifyAndClearExpectations(&mockBufferMapReadCallback); Mock::VerifyAndClearExpectations(&mockBufferMapReadCallback);
Mock::VerifyAndClearExpectations(&mockBufferMapWriteCallback); Mock::VerifyAndClearExpectations(&mockBufferMapWriteCallback);
Mock::VerifyAndClearExpectations(&mockCreateBufferMappedCallback);
} }
protected: protected:
@ -612,3 +638,184 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapFailure) {
FlushClient(); FlushClient();
} }
// Test successful CreateBufferMappedAsync
TEST_F(WireBufferMappingTests, CreateBufferMappedAsyncSuccess) {
DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
DawnCreateBufferMappedResult apiResult;
uint32_t serverBufferContent = 31337;
apiResult.buffer = apiBuffer;
apiResult.data = reinterpret_cast<uint8_t*>(&serverBufferContent);
apiResult.dataLength = 4;
uint32_t updatedContent = 4242;
uint32_t zero = 0;
dawnDeviceCreateBufferMappedAsync(device, &descriptor, ToMockCreateBufferMappedCallback, nullptr);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
FlushClient();
DawnBuffer buffer;
// The callback always gets a buffer full of zeroes.
EXPECT_CALL(*mockCreateBufferMappedCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, _,
Pointee(Eq(zero)), sizeof(uint32_t), _))
.WillOnce(::testing::SaveArg<1>(&buffer));
FlushServer();
// Write something to the mapped pointer
*lastCreateMappedPointer = updatedContent;
dawnBufferUnmap(buffer);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
// After the buffer is unmapped, the content of the buffer is updated on the server
ASSERT_EQ(serverBufferContent, updatedContent);
}
// Test CreateBufferMappedAsync with map error
TEST_F(WireBufferMappingTests, CreateBufferMappedAsyncMapError) {
DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
DawnCreateBufferMappedResult apiResult;
apiResult.buffer = apiBuffer;
apiResult.data = nullptr; // error mapping
apiResult.dataLength = 4;
dawnDeviceCreateBufferMappedAsync(device, &descriptor, ToMockCreateBufferMappedCallback, nullptr);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
FlushClient();
DawnBuffer buffer;
EXPECT_CALL(*mockCreateBufferMappedCallback,
Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, _, nullptr, 0, _))
.WillOnce(::testing::SaveArg<1>(&buffer));
FlushServer();
dawnBufferUnmap(buffer);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
}
// Test that the CreateBufferMappedCallback isn't fired twice when unmap() is called inside the
// callback
TEST_F(WireBufferMappingTests, UnmapInsideCreateBufferMappedAsyncCallback) {
DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
DawnCreateBufferMappedResult apiResult;
uint32_t serverBufferContent = 31337;
apiResult.buffer = apiBuffer;
apiResult.data = reinterpret_cast<uint8_t*>(&serverBufferContent);
apiResult.dataLength = 4;
uint32_t zero = 0;
dawnDeviceCreateBufferMappedAsync(device, &descriptor, ToMockCreateBufferMappedCallback, nullptr);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
FlushClient();
DawnBuffer buffer;
// The callback always gets a buffer full of zeroes.
EXPECT_CALL(*mockCreateBufferMappedCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, _,
Pointee(Eq(zero)), sizeof(uint32_t), _))
.WillOnce(DoAll(::testing::SaveArg<1>(&buffer),
InvokeWithoutArgs([&]() { dawnBufferUnmap(buffer); })));
FlushServer();
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
}
// Test that the CreateBufferMappedCallback isn't fired twice when the buffer is deleted inside
// the callback
TEST_F(WireBufferMappingTests, ReleaseInsideCreateBufferMappedAsyncCallback) {
DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
DawnCreateBufferMappedResult apiResult;
uint32_t serverBufferContent = 31337;
apiResult.buffer = apiBuffer;
apiResult.data = reinterpret_cast<uint8_t*>(&serverBufferContent);
apiResult.dataLength = 4;
uint32_t zero = 0;
dawnDeviceCreateBufferMappedAsync(device, &descriptor, ToMockCreateBufferMappedCallback, nullptr);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
FlushClient();
DawnBuffer buffer;
// The callback always gets a buffer full of zeroes.
EXPECT_CALL(*mockCreateBufferMappedCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, _,
Pointee(Eq(zero)), sizeof(uint32_t), _))
.WillOnce(DoAll(::testing::SaveArg<1>(&buffer),
InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); })));
FlushServer();
EXPECT_CALL(api, BufferRelease(apiBuffer));
FlushClient();
}
// Test that the CreateBufferMappedCallback isn't fired twice when the buffer is destroyed inside
// the callback
TEST_F(WireBufferMappingTests, DestroyInsideCreateBufferMappedAsyncCallback) {
DawnBufferDescriptor descriptor;
descriptor.nextInChain = nullptr;
DawnCreateBufferMappedResult apiResult;
uint32_t serverBufferContent = 31337;
apiResult.buffer = apiBuffer;
apiResult.data = reinterpret_cast<uint8_t*>(&serverBufferContent);
apiResult.dataLength = 4;
uint32_t zero = 0;
dawnDeviceCreateBufferMappedAsync(device, &descriptor, ToMockCreateBufferMappedCallback, nullptr);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
FlushClient();
DawnBuffer buffer;
// The callback always gets a buffer full of zeroes.
EXPECT_CALL(*mockCreateBufferMappedCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, _,
Pointee(Eq(zero)), sizeof(uint32_t), _))
.WillOnce(DoAll(::testing::SaveArg<1>(&buffer),
InvokeWithoutArgs([&]() { dawnBufferDestroy(buffer); })));
FlushServer();
EXPECT_CALL(api, BufferDestroy(apiBuffer));
FlushClient();
}