diff --git a/dawn_wire.json b/dawn_wire.json index 02338b42b3..fb7f979d5d 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 13c931a10c..77a94c824c 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -280,6 +280,7 @@ namespace dawn_native { result.buffer = reinterpret_cast(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 04c46ff1ba..32aefdd681 100644 --- a/src/dawn_wire/client/ApiProcs.cpp +++ b/src/dawn_wire/client/ApiProcs.cpp @@ -71,9 +71,34 @@ namespace dawn_wire { namespace client { DawnCreateBufferMappedResult ClientDeviceCreateBufferMapped( DawnDevice cDevice, const DawnBufferDescriptor* descriptor) { - // TODO(enga): Not implemented - UNREACHABLE(); - return {}; + Device* device = reinterpret_cast(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(wireClient->GetCmdSpace(requiredSize)); + cmd.Serialize(allocatedBuffer, *wireClient); + + DawnCreateBufferMappedResult result; + result.buffer = reinterpret_cast(buffer); + result.data = reinterpret_cast(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 191f65d3e4..313a4255e6 100644 --- a/src/dawn_wire/client/ClientDoers.cpp +++ b/src/dawn_wire/client/ClientDoers.cpp @@ -108,6 +108,9 @@ namespace dawn_wire { namespace client { 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 b5411773be..4632182350 100644 --- a/src/dawn_wire/server/ServerBuffer.cpp +++ b/src/dawn_wire/server/ServerBuffer.cpp @@ -61,6 +61,25 @@ namespace dawn_wire { namespace server { 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 5872f411d5..64a46f0375 100644 --- a/src/tests/end2end/BufferTests.cpp +++ b/src/tests/end2end/BufferTests.cpp @@ -269,6 +269,89 @@ TEST_P(CreateBufferMappedTests, LargeSyncWrite) { 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(data) = myData2; + *reinterpret_cast(static_cast(userdata)) = true; + }, + static_cast(reinterpret_cast(&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(static_cast(userdata)) = true; + }, + static_cast(reinterpret_cast(&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 3c4fa88d6e..4706d4a05f 100644 --- a/src/tests/unittests/wire/WireBufferMappingTests.cpp +++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp @@ -494,3 +494,146 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) { 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(&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(&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(&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(&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(); +}