• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_wire/client/Buffer.h"
16 
17 #include "dawn_wire/BufferConsumer_impl.h"
18 #include "dawn_wire/WireCmd_autogen.h"
19 #include "dawn_wire/client/Client.h"
20 #include "dawn_wire/client/Device.h"
21 
22 namespace dawn_wire { namespace client {
23 
24     // static
Create(Device * device,const WGPUBufferDescriptor * descriptor)25     WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor) {
26         Client* wireClient = device->client;
27 
28         bool mappable =
29             (descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 ||
30             descriptor->mappedAtCreation;
31         if (mappable && descriptor->size >= std::numeric_limits<size_t>::max()) {
32             device->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage");
33             return device->CreateErrorBuffer();
34         }
35 
36         std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
37         std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
38 
39         DeviceCreateBufferCmd cmd;
40         cmd.deviceId = device->id;
41         cmd.descriptor = descriptor;
42         cmd.readHandleCreateInfoLength = 0;
43         cmd.readHandleCreateInfo = nullptr;
44         cmd.writeHandleCreateInfoLength = 0;
45         cmd.writeHandleCreateInfo = nullptr;
46 
47         if (mappable) {
48             if ((descriptor->usage & WGPUBufferUsage_MapRead) != 0) {
49                 // Create the read handle on buffer creation.
50                 readHandle.reset(
51                     wireClient->GetMemoryTransferService()->CreateReadHandle(descriptor->size));
52                 if (readHandle == nullptr) {
53                     device->InjectError(WGPUErrorType_OutOfMemory,
54                                         "Failed to create buffer mapping");
55                     return device->CreateErrorBuffer();
56                 }
57                 cmd.readHandleCreateInfoLength = readHandle->SerializeCreateSize();
58             }
59 
60             if ((descriptor->usage & WGPUBufferUsage_MapWrite) != 0 ||
61                 descriptor->mappedAtCreation) {
62                 // Create the write handle on buffer creation.
63                 writeHandle.reset(
64                     wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
65                 if (writeHandle == nullptr) {
66                     device->InjectError(WGPUErrorType_OutOfMemory,
67                                         "Failed to create buffer mapping");
68                     return device->CreateErrorBuffer();
69                 }
70                 cmd.writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
71             }
72         }
73 
74         // Create the buffer and send the creation command.
75         // This must happen after any potential device->CreateErrorBuffer()
76         // as server expects allocating ids to be monotonically increasing
77         auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
78         Buffer* buffer = bufferObjectAndSerial->object.get();
79         buffer->mDevice = device;
80         buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
81         buffer->mSize = descriptor->size;
82         buffer->mDestructWriteHandleOnUnmap = false;
83 
84         if (descriptor->mappedAtCreation) {
85             // If the buffer is mapped at creation, a write handle is created and will be
86             // destructed on unmap if the buffer doesn't have MapWrite usage
87             // The buffer is mapped right now.
88             buffer->mMapState = MapState::MappedAtCreation;
89 
90             // This flag is for write handle created by mappedAtCreation
91             // instead of MapWrite usage. We don't have such a case for read handle
92             buffer->mDestructWriteHandleOnUnmap =
93                 (descriptor->usage & WGPUBufferUsage_MapWrite) == 0;
94 
95             buffer->mMapOffset = 0;
96             buffer->mMapSize = buffer->mSize;
97             ASSERT(writeHandle != nullptr);
98             buffer->mMappedData = writeHandle->GetData();
99         }
100 
101         cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
102 
103         wireClient->SerializeCommand(
104             cmd, cmd.readHandleCreateInfoLength + cmd.writeHandleCreateInfoLength,
105             [&](SerializeBuffer* serializeBuffer) {
106                 if (readHandle != nullptr) {
107                     char* readHandleBuffer;
108                     WIRE_TRY(
109                         serializeBuffer->NextN(cmd.readHandleCreateInfoLength, &readHandleBuffer));
110                     // Serialize the ReadHandle into the space after the command.
111                     readHandle->SerializeCreate(readHandleBuffer);
112                     buffer->mReadHandle = std::move(readHandle);
113                 }
114                 if (writeHandle != nullptr) {
115                     char* writeHandleBuffer;
116                     WIRE_TRY(serializeBuffer->NextN(cmd.writeHandleCreateInfoLength,
117                                                     &writeHandleBuffer));
118                     // Serialize the WriteHandle into the space after the command.
119                     writeHandle->SerializeCreate(writeHandleBuffer);
120                     buffer->mWriteHandle = std::move(writeHandle);
121                 }
122 
123                 return WireResult::Success;
124             });
125         return ToAPI(buffer);
126     }
127 
128     // static
CreateError(Device * device)129     WGPUBuffer Buffer::CreateError(Device* device) {
130         auto* allocation = device->client->BufferAllocator().New(device->client);
131         allocation->object->mDevice = device;
132         allocation->object->mDeviceIsAlive = device->GetAliveWeakPtr();
133 
134         DeviceCreateErrorBufferCmd cmd;
135         cmd.self = ToAPI(device);
136         cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
137         device->client->SerializeCommand(cmd);
138 
139         return ToAPI(allocation->object.get());
140     }
141 
~Buffer()142     Buffer::~Buffer() {
143         ClearAllCallbacks(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
144         FreeMappedData();
145     }
146 
CancelCallbacksForDisconnect()147     void Buffer::CancelCallbacksForDisconnect() {
148         ClearAllCallbacks(WGPUBufferMapAsyncStatus_DeviceLost);
149     }
150 
ClearAllCallbacks(WGPUBufferMapAsyncStatus status)151     void Buffer::ClearAllCallbacks(WGPUBufferMapAsyncStatus status) {
152         mRequests.CloseAll([status](MapRequestData* request) {
153             if (request->callback != nullptr) {
154                 request->callback(status, request->userdata);
155             }
156         });
157     }
158 
MapAsync(WGPUMapModeFlags mode,size_t offset,size_t size,WGPUBufferMapCallback callback,void * userdata)159     void Buffer::MapAsync(WGPUMapModeFlags mode,
160                           size_t offset,
161                           size_t size,
162                           WGPUBufferMapCallback callback,
163                           void* userdata) {
164         if (client->IsDisconnected()) {
165             return callback(WGPUBufferMapAsyncStatus_DeviceLost, userdata);
166         }
167 
168         // Handle the defaulting of size required by WebGPU.
169         if ((size == WGPU_WHOLE_MAP_SIZE) && (offset <= mSize)) {
170             size = mSize - offset;
171         }
172 
173         // Create the request structure that will hold information while this mapping is
174         // in flight.
175         MapRequestData request = {};
176         request.callback = callback;
177         request.userdata = userdata;
178         request.offset = offset;
179         request.size = size;
180         if (mode & WGPUMapMode_Read) {
181             request.type = MapRequestType::Read;
182         } else if (mode & WGPUMapMode_Write) {
183             request.type = MapRequestType::Write;
184         }
185 
186         uint64_t serial = mRequests.Add(std::move(request));
187 
188         // Serialize the command to send to the server.
189         BufferMapAsyncCmd cmd;
190         cmd.bufferId = this->id;
191         cmd.requestSerial = serial;
192         cmd.mode = mode;
193         cmd.offset = offset;
194         cmd.size = size;
195 
196         client->SerializeCommand(cmd);
197     }
198 
OnMapAsyncCallback(uint64_t requestSerial,uint32_t status,uint64_t readDataUpdateInfoLength,const uint8_t * readDataUpdateInfo)199     bool Buffer::OnMapAsyncCallback(uint64_t requestSerial,
200                                     uint32_t status,
201                                     uint64_t readDataUpdateInfoLength,
202                                     const uint8_t* readDataUpdateInfo) {
203         MapRequestData request;
204         if (!mRequests.Acquire(requestSerial, &request)) {
205             return false;
206         }
207 
208         auto FailRequest = [&request]() -> bool {
209             if (request.callback != nullptr) {
210                 request.callback(WGPUBufferMapAsyncStatus_DeviceLost, request.userdata);
211             }
212             return false;
213         };
214 
215         // Take into account the client-side status of the request if the server says it is a success.
216         if (status == WGPUBufferMapAsyncStatus_Success) {
217             status = request.clientStatus;
218         }
219 
220         if (status == WGPUBufferMapAsyncStatus_Success) {
221             switch (request.type) {
222                 case MapRequestType::Read: {
223                     if (readDataUpdateInfoLength > std::numeric_limits<size_t>::max()) {
224                         // This is the size of data deserialized from the command stream, which must
225                         // be CPU-addressable.
226                         return FailRequest();
227                     }
228 
229                     // Validate to prevent bad map request; buffer destroyed during map request
230                     if (mReadHandle == nullptr) {
231                         return FailRequest();
232                     }
233                     // Update user map data with server returned data
234                     if (!mReadHandle->DeserializeDataUpdate(
235                             readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
236                             request.offset, request.size)) {
237                         return FailRequest();
238                     }
239                     mMapState = MapState::MappedForRead;
240                     mMappedData = const_cast<void*>(mReadHandle->GetData());
241                     break;
242                 }
243                 case MapRequestType::Write: {
244                     if (mWriteHandle == nullptr) {
245                         return FailRequest();
246                     }
247                     mMapState = MapState::MappedForWrite;
248                     mMappedData = mWriteHandle->GetData();
249                     break;
250                 }
251                 default:
252                     UNREACHABLE();
253             }
254 
255             mMapOffset = request.offset;
256             mMapSize = request.size;
257         }
258 
259         if (request.callback) {
260             request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
261         }
262 
263         return true;
264     }
265 
GetMappedRange(size_t offset,size_t size)266     void* Buffer::GetMappedRange(size_t offset, size_t size) {
267         if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
268             return nullptr;
269         }
270         return static_cast<uint8_t*>(mMappedData) + offset;
271     }
272 
GetConstMappedRange(size_t offset,size_t size)273     const void* Buffer::GetConstMappedRange(size_t offset, size_t size) {
274         if (!(IsMappedForWriting() || IsMappedForReading()) ||
275             !CheckGetMappedRangeOffsetSize(offset, size)) {
276             return nullptr;
277         }
278         return static_cast<uint8_t*>(mMappedData) + offset;
279     }
280 
Unmap()281     void Buffer::Unmap() {
282         // Invalidate the local pointer, and cancel all other in-flight requests that would
283         // turn into errors anyway (you can't double map). This prevents race when the following
284         // happens, where the application code would have unmapped a buffer but still receive a
285         // callback:
286         //   - Client -> Server: MapRequest1, Unmap, MapRequest2
287         //   - Server -> Client: Result of MapRequest1
288         //   - Unmap locally on the client
289         //   - Server -> Client: Result of MapRequest2
290 
291         // mWriteHandle can still be nullptr if buffer has been destroyed before unmap
292         if ((mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation) &&
293             mWriteHandle != nullptr) {
294             // Writes need to be flushed before Unmap is sent. Unmap calls all associated
295             // in-flight callbacks which may read the updated data.
296 
297             // Get the serialization size of data update writes.
298             size_t writeDataUpdateInfoLength =
299                 mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
300 
301             BufferUpdateMappedDataCmd cmd;
302             cmd.bufferId = id;
303             cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
304             cmd.writeDataUpdateInfo = nullptr;
305             cmd.offset = mMapOffset;
306             cmd.size = mMapSize;
307 
308             client->SerializeCommand(
309                 cmd, writeDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
310                     char* writeHandleBuffer;
311                     WIRE_TRY(serializeBuffer->NextN(writeDataUpdateInfoLength, &writeHandleBuffer));
312 
313                     // Serialize flush metadata into the space after the command.
314                     // This closes the handle for writing.
315                     mWriteHandle->SerializeDataUpdate(writeHandleBuffer, cmd.offset, cmd.size);
316 
317                     return WireResult::Success;
318                 });
319 
320             // If mDestructWriteHandleOnUnmap is true, that means the write handle is merely
321             // for mappedAtCreation usage. It is destroyed on unmap after flush to server
322             // instead of at buffer destruction.
323             if (mMapState == MapState::MappedAtCreation && mDestructWriteHandleOnUnmap) {
324                 mWriteHandle = nullptr;
325                 if (mReadHandle) {
326                     // If it's both mappedAtCreation and MapRead we need to reset
327                     // mMappedData to readHandle's GetData(). This could be changed to
328                     // merging read/write handle in future
329                     mMappedData = const_cast<void*>(mReadHandle->GetData());
330                 }
331             }
332         }
333 
334         // Free map access tokens
335         mMapState = MapState::Unmapped;
336         mMapOffset = 0;
337         mMapSize = 0;
338 
339         // Tag all mapping requests still in flight as unmapped before callback.
340         mRequests.ForAll([](MapRequestData* request) {
341             if (request->clientStatus == WGPUBufferMapAsyncStatus_Success) {
342                 request->clientStatus = WGPUBufferMapAsyncStatus_UnmappedBeforeCallback;
343             }
344         });
345 
346         BufferUnmapCmd cmd;
347         cmd.self = ToAPI(this);
348         client->SerializeCommand(cmd);
349     }
350 
Destroy()351     void Buffer::Destroy() {
352         // Remove the current mapping and destroy Read/WriteHandles.
353         FreeMappedData();
354 
355         // Tag all mapping requests still in flight as destroyed before callback.
356         mRequests.ForAll([](MapRequestData* request) {
357             if (request->clientStatus == WGPUBufferMapAsyncStatus_Success) {
358                 request->clientStatus = WGPUBufferMapAsyncStatus_DestroyedBeforeCallback;
359             }
360         });
361 
362         BufferDestroyCmd cmd;
363         cmd.self = ToAPI(this);
364         client->SerializeCommand(cmd);
365     }
366 
IsMappedForReading() const367     bool Buffer::IsMappedForReading() const {
368         return mMapState == MapState::MappedForRead;
369     }
370 
IsMappedForWriting() const371     bool Buffer::IsMappedForWriting() const {
372         return mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation;
373     }
374 
CheckGetMappedRangeOffsetSize(size_t offset,size_t size) const375     bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
376         if (offset % 8 != 0 || size % 4 != 0) {
377             return false;
378         }
379 
380         if (size > mMapSize || offset < mMapOffset) {
381             return false;
382         }
383 
384         size_t offsetInMappedRange = offset - mMapOffset;
385         return offsetInMappedRange <= mMapSize - size;
386     }
387 
FreeMappedData()388     void Buffer::FreeMappedData() {
389 #if defined(DAWN_ENABLE_ASSERTS)
390         // When in "debug" mode, 0xCA-out the mapped data when we free it so that in we can detect
391         // use-after-free of the mapped data. This is particularly useful for WebGPU test about the
392         // interaction of mapping and GC.
393         if (mMappedData) {
394             memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
395         }
396 #endif  // defined(DAWN_ENABLE_ASSERTS)
397 
398         mMapOffset = 0;
399         mMapSize = 0;
400         mReadHandle = nullptr;
401         mWriteHandle = nullptr;
402         mMappedData = nullptr;
403     }
404 
405 }}  // namespace dawn_wire::client
406