diff --git a/dawn.json b/dawn.json index 6044c9635d..b7a18ce19c 100644 --- a/dawn.json +++ b/dawn.json @@ -213,6 +213,14 @@ "char": { "category": "native" }, + "create buffer mapped result": { + "category": "structure", + "members": [ + {"name": "buffer", "type": "buffer"}, + {"name": "data length", "type": "uint64_t"}, + {"name": "data", "type": "uint8_t", "annotation": "*", "length": "data length"} + ] + }, "color": { "category": "structure", "members": [ @@ -410,6 +418,13 @@ {"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"} ] }, + { + "name": "create buffer mapped", + "returns": "create buffer mapped result", + "args": [ + {"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"} + ] + }, { "name": "create command encoder", "returns": "command encoder" diff --git a/dawn_wire.json b/dawn_wire.json index f2c1dce8df..02338b42b3 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -58,6 +58,7 @@ ], "client_handwritten_commands": [ "BufferUnmap", + "DeviceCreateBufferMapped", "QueueCreateFence", "FenceGetCompletedValue", "QueueSignal" diff --git a/generator/main.py b/generator/main.py index 8518e5d005..2d63c081e3 100644 --- a/generator/main.py +++ b/generator/main.py @@ -230,9 +230,34 @@ def as_cppType(name): else: return name.CamelCase() +def convert_cType_to_cppType(typ, annotation, arg, indent=0): + if typ.category == 'native': + return arg + if annotation == 'value': + if typ.category == 'object': + return '{}::Acquire({})'.format(as_cppType(typ.name), arg) + elif typ.category == 'structure': + converted_members = [ + convert_cType_to_cppType( + member.type, member.annotation, + '{}.{}'.format(arg, as_varName(member.name)), + indent + 1) + for member in typ.members] + + converted_members = [(' ' * 4) + m for m in converted_members ] + converted_members = ',\n'.join(converted_members) + + return as_cppType(typ.name) + ' {\n' + converted_members + '\n}' + else: + return 'static_cast<{}>({})'.format(as_cppType(typ.name), arg) + else: + return 'reinterpret_cast<{} {}>({})'.format(as_cppType(typ.name), annotation, arg) + def decorate(name, typ, arg): if arg.annotation == 'value': return typ + ' ' + name + elif arg.annotation == '*': + return typ + ' * ' + name elif arg.annotation == 'const*': return typ + ' const * ' + name elif arg.annotation == 'const*const*': @@ -314,6 +339,7 @@ def get_renders_for_targets(api_params, wire_json, targets): 'as_cProc': as_cProc, 'as_cType': as_cType, 'as_cppType': as_cppType, + 'convert_cType_to_cppType': convert_cType_to_cppType, 'as_varName': as_varName, 'decorate': decorate, } diff --git a/generator/templates/apicpp.cpp b/generator/templates/apicpp.cpp index b353a8b071..5b61b2fd4c 100644 --- a/generator/templates/apicpp.cpp +++ b/generator/templates/apicpp.cpp @@ -96,13 +96,7 @@ namespace dawn { {{render_cpp_to_c_method_call(type, method)}}; {% else %} auto result = {{render_cpp_to_c_method_call(type, method)}}; - {% if method.return_type.category == "native" %} - return result; - {% elif method.return_type.category == "object" %} - return {{as_cppType(method.return_type.name)}}::Acquire(result); - {% else %} - return static_cast<{{as_cppType(method.return_type.name)}}>(result); - {% endif%} + return {{convert_cType_to_cppType(method.return_type, 'value', 'result') | indent(8)}}; {% endif %} } {% endfor %} diff --git a/generator/templates/dawn_wire/WireCmd.cpp b/generator/templates/dawn_wire/WireCmd.cpp index b1c952f74e..ca7a35f383 100644 --- a/generator/templates/dawn_wire/WireCmd.cpp +++ b/generator/templates/dawn_wire/WireCmd.cpp @@ -152,7 +152,7 @@ //* Serializes `record` into `transfer`, using `buffer` to get more space for pointed-to data //* and `provider` to serialize objects. - void {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer, + DAWN_DECLARE_UNUSED void {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer, char** buffer {%- if record.has_dawn_object -%} , const ObjectIdProvider& provider @@ -200,11 +200,12 @@ } {% endfor %} } + DAWN_UNUSED_FUNC({{Return}}{{name}}Serialize); //* Deserializes `transfer` into `record` getting more serialized data from `buffer` and `size` //* if needed, using `allocator` to store pointed-to values and `resolver` to translate object //* Ids to actual objects. - DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const {{Return}}{{name}}Transfer* transfer, + DAWN_DECLARE_UNUSED DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const {{Return}}{{name}}Transfer* transfer, const char** buffer, size_t* size, DeserializeAllocator* allocator {%- if record.has_dawn_object -%} , const ObjectIdResolver& resolver @@ -283,6 +284,7 @@ return DeserializeResult::Success; } + DAWN_UNUSED_FUNC({{Return}}{{name}}Deserialize); {% endmacro %} {% macro write_command_serialization_methods(command, is_return) %} diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp index 74ac084be9..08cb87196c 100644 --- a/src/dawn_native/Buffer.cpp +++ b/src/dawn_native/Buffer.cpp @@ -31,7 +31,24 @@ namespace dawn_native { ErrorBuffer(DeviceBase* device) : BufferBase(device, ObjectBase::kError) { } + static ErrorBuffer* MakeMapped(DeviceBase* device, + uint64_t size, + uint8_t** mappedPointer) { + ASSERT(mappedPointer != nullptr); + + ErrorBuffer* buffer = new ErrorBuffer(device); + buffer->mFakeMappedData = std::unique_ptr(new uint8_t[size]); + *mappedPointer = buffer->mFakeMappedData.get(); + + return buffer; + } + private: + MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override { + UNREACHABLE(); + return {}; + } + MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) override { @@ -45,11 +62,14 @@ namespace dawn_native { UNREACHABLE(); } void UnmapImpl() override { - UNREACHABLE(); + ASSERT(mFakeMappedData); + mFakeMappedData.reset(); } void DestroyImpl() override { UNREACHABLE(); } + + std::unique_ptr mFakeMappedData; }; } // anonymous namespace @@ -104,6 +124,13 @@ namespace dawn_native { return new ErrorBuffer(device); } + // static + BufferBase* BufferBase::MakeErrorMapped(DeviceBase* device, + uint64_t size, + uint8_t** mappedPointer) { + return ErrorBuffer::MakeMapped(device, size, mappedPointer); + } + uint32_t BufferBase::GetSize() const { ASSERT(!IsError()); return mSize; @@ -114,6 +141,22 @@ namespace dawn_native { return mUsage; } + MaybeError BufferBase::MapAtCreation(uint8_t** mappedPointer) { + ASSERT(!IsError()); + ASSERT(mappedPointer != nullptr); + + mState = BufferState::Mapped; + if ((mUsage & dawn::BufferUsageBit::MapWrite) == 0) { + // TODO(enga): Support non-mappable buffers with a staging buffer. + return DAWN_VALIDATION_ERROR("MapWrite usage required"); + } + + DAWN_TRY(MapAtCreationImpl(mappedPointer)); + ASSERT(*mappedPointer != nullptr); + + return {}; + } + MaybeError BufferBase::ValidateCanUseInSubmitNow() const { ASSERT(!IsError()); @@ -239,6 +282,11 @@ namespace dawn_native { } void BufferBase::Unmap() { + if (IsError()) { + // It is an error to call Unmap() on an ErrorBuffer, but we still need to reclaim the + // fake mapped staging data. + UnmapImpl(); + } if (GetDevice()->ConsumedError(ValidateUnmap())) { return; } diff --git a/src/dawn_native/Buffer.h b/src/dawn_native/Buffer.h index 46fe1fa002..70ae0e5318 100644 --- a/src/dawn_native/Buffer.h +++ b/src/dawn_native/Buffer.h @@ -45,10 +45,15 @@ namespace dawn_native { ~BufferBase(); static BufferBase* MakeError(DeviceBase* device); + static BufferBase* MakeErrorMapped(DeviceBase* device, + uint64_t size, + uint8_t** mappedPointer); uint32_t GetSize() const; dawn::BufferUsageBit GetUsage() const; + MaybeError MapAtCreation(uint8_t** mappedPointer); + MaybeError ValidateCanUseInSubmitNow() const; // Dawn API @@ -73,6 +78,7 @@ namespace dawn_native { void DestroyInternal(); private: + virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) = 0; virtual MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data); virtual void MapReadAsyncImpl(uint32_t serial) = 0; virtual void MapWriteAsyncImpl(uint32_t serial) = 0; diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index 753f709a4e..7d7c222445 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -237,6 +237,30 @@ namespace dawn_native { return result; } + DawnCreateBufferMappedResult DeviceBase::CreateBufferMapped( + const BufferDescriptor* descriptor) { + BufferBase* buffer = nullptr; + uint8_t* data = nullptr; + + if (ConsumedError(CreateBufferInternal(&buffer, descriptor)) || + ConsumedError(buffer->MapAtCreation(&data))) { + // Map failed. Replace the buffer with an error buffer. + if (buffer != nullptr) { + delete buffer; + } + buffer = BufferBase::MakeErrorMapped(this, descriptor->size, &data); + } + + ASSERT(buffer != nullptr); + ASSERT(data != nullptr); + + DawnCreateBufferMappedResult result = {}; + result.buffer = reinterpret_cast(buffer); + result.data = data; + result.dataLength = descriptor->size; + + return result; + } CommandEncoderBase* DeviceBase::CreateCommandEncoder() { return new CommandEncoderBase(this); } diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h index b9039e43d1..998e03562c 100644 --- a/src/dawn_native/Device.h +++ b/src/dawn_native/Device.h @@ -104,6 +104,7 @@ namespace dawn_native { BindGroupBase* CreateBindGroup(const BindGroupDescriptor* descriptor); BindGroupLayoutBase* CreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor); BufferBase* CreateBuffer(const BufferDescriptor* descriptor); + DawnCreateBufferMappedResult CreateBufferMapped(const BufferDescriptor* descriptor); CommandEncoderBase* CreateCommandEncoder(); ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor); PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor); diff --git a/src/dawn_native/d3d12/BufferD3D12.cpp b/src/dawn_native/d3d12/BufferD3D12.cpp index b0b5a1e49a..3a5ed9b28e 100644 --- a/src/dawn_native/d3d12/BufferD3D12.cpp +++ b/src/dawn_native/d3d12/BufferD3D12.cpp @@ -160,6 +160,13 @@ namespace dawn_native { namespace d3d12 { } } + MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { + mWrittenMappedRange = {0, GetSize()}; + ASSERT_SUCCESS( + mResource->Map(0, &mWrittenMappedRange, reinterpret_cast(mappedPointer))); + return {}; + } + void Buffer::MapReadAsyncImpl(uint32_t serial) { mWrittenMappedRange = {}; D3D12_RANGE readRange = {0, GetSize()}; diff --git a/src/dawn_native/d3d12/BufferD3D12.h b/src/dawn_native/d3d12/BufferD3D12.h index 9667085107..9208f53835 100644 --- a/src/dawn_native/d3d12/BufferD3D12.h +++ b/src/dawn_native/d3d12/BufferD3D12.h @@ -44,6 +44,8 @@ namespace dawn_native { namespace d3d12 { void UnmapImpl() override; void DestroyImpl() override; + virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + ComPtr mResource; bool mFixedResourceState = false; dawn::BufferUsageBit mLastUsage = dawn::BufferUsageBit::None; diff --git a/src/dawn_native/metal/BufferMTL.h b/src/dawn_native/metal/BufferMTL.h index 4cdbb5a650..7fb8c35217 100644 --- a/src/dawn_native/metal/BufferMTL.h +++ b/src/dawn_native/metal/BufferMTL.h @@ -34,11 +34,14 @@ namespace dawn_native { namespace metal { void OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite); private: + // Dawn API void MapReadAsyncImpl(uint32_t serial) override; void MapWriteAsyncImpl(uint32_t serial) override; void UnmapImpl() override; void DestroyImpl() override; + MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + id mMtlBuffer = nil; }; diff --git a/src/dawn_native/metal/BufferMTL.mm b/src/dawn_native/metal/BufferMTL.mm index 1d0828aa4b..7cc999a6bf 100644 --- a/src/dawn_native/metal/BufferMTL.mm +++ b/src/dawn_native/metal/BufferMTL.mm @@ -47,6 +47,11 @@ namespace dawn_native { namespace metal { } } + MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { + *mappedPointer = reinterpret_cast([mMtlBuffer contents]); + return {}; + } + void Buffer::MapReadAsyncImpl(uint32_t serial) { MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker(); tracker->Track(this, serial, false); diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp index 15db5c2ef1..eeaae7e0ef 100644 --- a/src/dawn_native/null/DeviceNull.cpp +++ b/src/dawn_native/null/DeviceNull.cpp @@ -184,13 +184,18 @@ namespace dawn_native { namespace null { : BufferBase(device, descriptor) { if (GetUsage() & (dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) { - mBackingData = std::unique_ptr(new char[GetSize()]); + mBackingData = std::unique_ptr(new uint8_t[GetSize()]); } } Buffer::~Buffer() { } + MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { + *mappedPointer = mBackingData.get(); + return {}; + } + void Buffer::MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite) { if (isWrite) { CallMapWriteCallback(serial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr, GetSize()); diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h index a523f3d9f6..0c770fceff 100644 --- a/src/dawn_native/null/DeviceNull.h +++ b/src/dawn_native/null/DeviceNull.h @@ -138,15 +138,17 @@ namespace dawn_native { namespace null { void MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite); private: + // Dawn API MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) override; void MapReadAsyncImpl(uint32_t serial) override; void MapWriteAsyncImpl(uint32_t serial) override; void UnmapImpl() override; void DestroyImpl() override; + MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; void MapAsyncImplCommon(uint32_t serial, bool isWrite); - std::unique_ptr mBackingData; + std::unique_ptr mBackingData; }; class CommandBuffer : public CommandBufferBase { diff --git a/src/dawn_native/opengl/BufferGL.cpp b/src/dawn_native/opengl/BufferGL.cpp index 225a5834ad..92fa601e7c 100644 --- a/src/dawn_native/opengl/BufferGL.cpp +++ b/src/dawn_native/opengl/BufferGL.cpp @@ -35,6 +35,13 @@ namespace dawn_native { namespace opengl { return mBuffer; } + MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + void* data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + *mappedPointer = reinterpret_cast(data); + return {}; + } + MaybeError Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) { glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBufferSubData(GL_ARRAY_BUFFER, start, count, data); diff --git a/src/dawn_native/opengl/BufferGL.h b/src/dawn_native/opengl/BufferGL.h index 5a2b4a5cce..1ad582f408 100644 --- a/src/dawn_native/opengl/BufferGL.h +++ b/src/dawn_native/opengl/BufferGL.h @@ -31,12 +31,15 @@ namespace dawn_native { namespace opengl { GLuint GetHandle() const; private: + // Dawn API MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) override; void MapReadAsyncImpl(uint32_t serial) override; void MapWriteAsyncImpl(uint32_t serial) override; void UnmapImpl() override; void DestroyImpl() override; + MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + GLuint mBuffer = 0; }; diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp index 137f1ad787..01e08bdb21 100644 --- a/src/dawn_native/vulkan/BufferVk.cpp +++ b/src/dawn_native/vulkan/BufferVk.cpp @@ -188,6 +188,11 @@ namespace dawn_native { namespace vulkan { mLastUsage = usage; } + MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { + *mappedPointer = mMemoryAllocation.GetMappedPointer(); + return {}; + } + void Buffer::MapReadAsyncImpl(uint32_t serial) { Device* device = ToBackend(GetDevice()); diff --git a/src/dawn_native/vulkan/BufferVk.h b/src/dawn_native/vulkan/BufferVk.h index 348f9be370..5cfb7c5e01 100644 --- a/src/dawn_native/vulkan/BufferVk.h +++ b/src/dawn_native/vulkan/BufferVk.h @@ -41,11 +41,14 @@ namespace dawn_native { namespace vulkan { void TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage); private: + // Dawn API void MapReadAsyncImpl(uint32_t serial) override; void MapWriteAsyncImpl(uint32_t serial) override; void UnmapImpl() override; void DestroyImpl() override; + MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + VkBuffer mHandle = VK_NULL_HANDLE; DeviceMemoryAllocation mMemoryAllocation; diff --git a/src/dawn_wire/client/ApiProcs.cpp b/src/dawn_wire/client/ApiProcs.cpp index 4289c17267..04c46ff1ba 100644 --- a/src/dawn_wire/client/ApiProcs.cpp +++ b/src/dawn_wire/client/ApiProcs.cpp @@ -68,6 +68,14 @@ namespace dawn_wire { namespace client { cmd.Serialize(allocatedBuffer); } + DawnCreateBufferMappedResult ClientDeviceCreateBufferMapped( + DawnDevice cDevice, + const DawnBufferDescriptor* descriptor) { + // TODO(enga): Not implemented + UNREACHABLE(); + return {}; + } + uint64_t ClientFenceGetCompletedValue(DawnFence cSelf) { auto fence = reinterpret_cast(cSelf); return fence->completedValue; diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp index eac536c583..5872f411d5 100644 --- a/src/tests/end2end/BufferTests.cpp +++ b/src/tests/end2end/BufferTests.cpp @@ -229,3 +229,48 @@ DAWN_INSTANTIATE_TEST(BufferSetSubDataTests, MetalBackend, OpenGLBackend, VulkanBackend); + +class CreateBufferMappedTests : public DawnTest {}; + +// Test that the simplest CreateBufferMapped works. +TEST_P(CreateBufferMappedTests, SmallSyncWrite) { + 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)); + result.buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); +} + +// Test CreateBufferMapped for a large buffer +TEST_P(CreateBufferMappedTests, LargeSyncWrite) { + constexpr uint64_t kDataSize = 1000 * 1000; + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + dawn::BufferDescriptor descriptor; + descriptor.nextInChain = nullptr; + descriptor.size = static_cast(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(); + + EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize); +} + +DAWN_INSTANTIATE_TEST(CreateBufferMappedTests, + D3D12Backend, + MetalBackend, + OpenGLBackend, + VulkanBackend); diff --git a/src/tests/unittests/validation/BufferValidationTests.cpp b/src/tests/unittests/validation/BufferValidationTests.cpp index 3d25d67e69..69efe5d4c0 100644 --- a/src/tests/unittests/validation/BufferValidationTests.cpp +++ b/src/tests/unittests/validation/BufferValidationTests.cpp @@ -82,6 +82,15 @@ class BufferValidationTest : public ValidationTest { return device.CreateBuffer(&descriptor); } + dawn::CreateBufferMappedResult CreateBufferMapped(uint64_t size, + dawn::BufferUsageBit usage) { + dawn::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + + return device.CreateBufferMapped(&descriptor); + } + dawn::Queue queue; private: @@ -183,6 +192,14 @@ TEST_F(BufferValidationTest, MapWriteSuccess) { buf.Unmap(); } +// Test the success case for CreateBufferMapped +TEST_F(BufferValidationTest, CreateBufferMappedSuccess) { + dawn::CreateBufferMappedResult result = CreateBufferMapped(4, dawn::BufferUsageBit::MapWrite); + ASSERT_NE(result.data, nullptr); + ASSERT_EQ(result.dataLength, 4u); + result.buffer.Unmap(); +} + // Test map reading a buffer with wrong current usage TEST_F(BufferValidationTest, MapReadWrongUsage) { dawn::BufferDescriptor descriptor;