Implement CreateBufferMapped in dawn_wire

Bug: dawn:7
Change-Id: I7112c87c3b671cd7a7774af7f3a094a189f2b4b0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7402
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn_wire.json b/dawn_wire.json
index 02338b4..fb7f979 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -25,6 +25,11 @@
             { "name": "data length", "type": "uint32_t" },
             { "name": "data", "type": "uint8_t", "annotation": "const*", "length": "data length" }
         ],
+        "device create buffer mapped": [
+            { "name": "device", "type": "device" },
+            { "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
+            { "name": "result", "type": "ObjectHandle", "handle_type": "buffer" }
+        ],
         "destroy object": [
             { "name": "object type", "type": "ObjectType" },
             { "name": "object id", "type": "ObjectId" }
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 13c931a..77a94c8 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -280,6 +280,7 @@
         result.buffer = reinterpret_cast<DawnBuffer>(buffer);
         result.data = data;
         result.dataLength = descriptor->size;
+        memset(result.data, 0, result.dataLength);
 
         return result;
     }
diff --git a/src/dawn_wire/client/ApiProcs.cpp b/src/dawn_wire/client/ApiProcs.cpp
index 04c46ff..32aefdd 100644
--- a/src/dawn_wire/client/ApiProcs.cpp
+++ b/src/dawn_wire/client/ApiProcs.cpp
@@ -71,9 +71,34 @@
     DawnCreateBufferMappedResult ClientDeviceCreateBufferMapped(
         DawnDevice cDevice,
         const DawnBufferDescriptor* descriptor) {
-        // TODO(enga): Not implemented
-        UNREACHABLE();
-        return {};
+        Device* device = reinterpret_cast<Device*>(cDevice);
+        Client* wireClient = device->GetClient();
+
+        auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
+        Buffer* buffer = bufferObjectAndSerial->object.get();
+        buffer->isWriteMapped = true;
+        // |mappedData| is freed in Unmap or the Buffer destructor.
+        // TODO(enga): Add dependency injection for buffer mapping so staging
+        // memory can live in shared memory.
+        buffer->mappedData = malloc(descriptor->size);
+        memset(buffer->mappedData, 0, descriptor->size);
+        buffer->mappedDataSize = descriptor->size;
+
+        DeviceCreateBufferMappedCmd cmd;
+        cmd.device = cDevice;
+        cmd.descriptor = descriptor;
+        cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->serial};
+
+        size_t requiredSize = cmd.GetRequiredSize();
+        char* allocatedBuffer = static_cast<char*>(wireClient->GetCmdSpace(requiredSize));
+        cmd.Serialize(allocatedBuffer, *wireClient);
+
+        DawnCreateBufferMappedResult result;
+        result.buffer = reinterpret_cast<DawnBuffer>(buffer);
+        result.data = reinterpret_cast<uint8_t*>(buffer->mappedData);
+        result.dataLength = descriptor->size;
+
+        return result;
     }
 
     uint64_t ClientFenceGetCompletedValue(DawnFence cSelf) {
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index 191f65d..313a425 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -108,6 +108,9 @@
 
             buffer->isWriteMapped = true;
             buffer->mappedDataSize = dataLength;
+            // |mappedData| is freed in Unmap or the Buffer destructor.
+            // TODO(enga): Add dependency injection for buffer mapping so staging
+            // memory can live in shared memory.
             buffer->mappedData = malloc(dataLength);
             memset(buffer->mappedData, 0, dataLength);
 
diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp
index b541177..4632182 100644
--- a/src/dawn_wire/server/ServerBuffer.cpp
+++ b/src/dawn_wire/server/ServerBuffer.cpp
@@ -61,6 +61,25 @@
         return true;
     }
 
+    bool Server::DoDeviceCreateBufferMapped(DawnDevice device,
+                                            const DawnBufferDescriptor* descriptor,
+                                            ObjectHandle bufferResult) {
+        auto* resultData = BufferObjects().Allocate(bufferResult.id);
+        if (resultData == nullptr) {
+            return false;
+        }
+        resultData->serial = bufferResult.serial;
+
+        DawnCreateBufferMappedResult result = mProcs.deviceCreateBufferMapped(device, descriptor);
+        ASSERT(result.buffer != nullptr);
+        ASSERT(result.data != nullptr);
+        resultData->handle = result.buffer;
+        resultData->mappedData = result.data;
+        resultData->mappedDataSize = result.dataLength;
+
+        return true;
+    }
+
     bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint32_t count, const uint8_t* data) {
         // The null object isn't valid as `self`
         if (bufferId == 0) {
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index 5872f41..64a46f0 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -269,6 +269,89 @@
     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, ZeroInitialized) {
+    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(*result.data, 0);
+    result.buffer.Unmap();
+}
+
+// Test that mapping a buffer is valid after CreateBufferMapped and Unmap
+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 myData2 = 1337;
+    dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
+    ASSERT_EQ(result.dataLength, descriptor.size);
+    memcpy(result.data, &myData, sizeof(myData));
+    result.buffer.Unmap();
+
+    EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
+
+    bool done = false;
+    result.buffer.MapWriteAsync(
+        [](DawnBufferMapAsyncStatus status, void* data, uint64_t, DawnCallbackUserdata userdata) {
+            ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status);
+            ASSERT_NE(nullptr, data);
+
+            *reinterpret_cast<uint32_t*>(data) = myData2;
+            *reinterpret_cast<bool*>(static_cast<uintptr_t>(userdata)) = true;
+        },
+        static_cast<DawnCallbackUserdata>(reinterpret_cast<uintptr_t>(&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 CreateBufferMapped
+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;
+    dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor);
+    ASSERT_EQ(result.dataLength, descriptor.size);
+    memcpy(result.data, &myData, sizeof(myData));
+
+    ASSERT_DEVICE_ERROR([&]() {
+        bool done = false;
+        result.buffer.MapWriteAsync(
+            [](DawnBufferMapAsyncStatus status, void* data, uint64_t,
+               DawnCallbackUserdata userdata) {
+                ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, status);
+                ASSERT_EQ(nullptr, data);
+
+                *reinterpret_cast<bool*>(static_cast<uintptr_t>(userdata)) = true;
+            },
+            static_cast<DawnCallbackUserdata>(reinterpret_cast<uintptr_t>(&done)));
+
+        while (!done) {
+            WaitABit();
+        }
+    }());
+
+    // CreateBufferMapped is unaffected by the MapWrite error.
+    result.buffer.Unmap();
+    EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
+}
+
 DAWN_INSTANTIATE_TEST(CreateBufferMappedTests,
                       D3D12Backend,
                       MetalBackend,
diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp
index 3c4fa88..4706d4a 100644
--- a/src/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -494,3 +494,146 @@
 
     FlushClient();
 }
+
+// Test successful CreateBufferMapped
+TEST_F(WireBufferMappingTests, CreateBufferMappedSuccess) {
+    DawnBufferDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.size = 4;
+
+    DawnBuffer apiBuffer = api.GetNewBuffer();
+    DawnCreateBufferMappedResult apiResult;
+    uint32_t apiBufferData = 1234;
+    apiResult.buffer = apiBuffer;
+    apiResult.data = reinterpret_cast<uint8_t*>(&apiBufferData);
+    apiResult.dataLength = 4;
+
+    DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor);
+
+    EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
+        .WillOnce(Return(apiResult))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    dawnBufferUnmap(result.buffer);
+    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+
+    FlushClient();
+}
+
+// Test that releasing after CreateBufferMapped does not call Unmap
+TEST_F(WireBufferMappingTests, ReleaseAfterCreateBufferMapped) {
+    DawnBufferDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.size = 4;
+
+    DawnBuffer apiBuffer = api.GetNewBuffer();
+    DawnCreateBufferMappedResult apiResult;
+    uint32_t apiBufferData = 1234;
+    apiResult.buffer = apiBuffer;
+    apiResult.data = reinterpret_cast<uint8_t*>(&apiBufferData);
+    apiResult.dataLength = 4;
+
+    DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor);
+
+    EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
+        .WillOnce(Return(apiResult))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    dawnBufferRelease(result.buffer);
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
+
+    FlushClient();
+}
+
+// Test that it is valid to map a buffer after CreateBufferMapped and Unmap
+TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapSuccess) {
+    DawnBufferDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.size = 4;
+
+    DawnBuffer apiBuffer = api.GetNewBuffer();
+    DawnCreateBufferMappedResult apiResult;
+    uint32_t apiBufferData = 9863;
+    apiResult.buffer = apiBuffer;
+    apiResult.data = reinterpret_cast<uint8_t*>(&apiBufferData);
+    apiResult.dataLength = 4;
+
+    DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor);
+
+    EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
+        .WillOnce(Return(apiResult))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    dawnBufferUnmap(result.buffer);
+    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+
+    FlushClient();
+
+    DawnCallbackUserdata userdata = 2499;
+    dawnBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t zero = 0;
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &apiBufferData, sizeof(uint32_t));
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                     Pointee(Eq(zero)), sizeof(uint32_t), userdata))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Test that it is invalid to map a buffer after CreateBufferMapped before Unmap
+TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapFailure) {
+    DawnBufferDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.size = 4;
+
+    DawnBuffer apiBuffer = api.GetNewBuffer();
+    DawnCreateBufferMappedResult apiResult;
+    uint32_t apiBufferData = 9863;
+    apiResult.buffer = apiBuffer;
+    apiResult.data = reinterpret_cast<uint8_t*>(&apiBufferData);
+    apiResult.dataLength = 4;
+
+    DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor);
+
+    EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
+        .WillOnce(Return(apiResult))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    DawnCallbackUserdata userdata = 2499;
+    dawnBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, userdata);
+
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, userdata))
+        .Times(1);
+
+    FlushServer();
+
+    dawnBufferUnmap(result.buffer);
+    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+
+    FlushClient();
+}