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 "common/Assert.h" 16 #include "dawn_wire/BufferConsumer_impl.h" 17 #include "dawn_wire/WireCmd_autogen.h" 18 #include "dawn_wire/server/Server.h" 19 20 #include <memory> 21 22 namespace dawn_wire { namespace server { 23 PreHandleBufferUnmap(const BufferUnmapCmd & cmd)24 bool Server::PreHandleBufferUnmap(const BufferUnmapCmd& cmd) { 25 auto* buffer = BufferObjects().Get(cmd.selfId); 26 DAWN_ASSERT(buffer != nullptr); 27 28 if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) { 29 // This indicates the writeHandle is for mappedAtCreation only. Destroy on unmap 30 // writeHandle could have possibly been deleted if buffer is already destroyed so we 31 // don't assert it's non-null 32 buffer->writeHandle = nullptr; 33 } 34 35 buffer->mapWriteState = BufferMapWriteState::Unmapped; 36 37 return true; 38 } 39 PreHandleBufferDestroy(const BufferDestroyCmd & cmd)40 bool Server::PreHandleBufferDestroy(const BufferDestroyCmd& cmd) { 41 // Destroying a buffer does an implicit unmapping. 42 auto* buffer = BufferObjects().Get(cmd.selfId); 43 DAWN_ASSERT(buffer != nullptr); 44 45 // The buffer was destroyed. Clear the Read/WriteHandle. 46 buffer->readHandle = nullptr; 47 buffer->writeHandle = nullptr; 48 buffer->mapWriteState = BufferMapWriteState::Unmapped; 49 50 return true; 51 } 52 DoBufferMapAsync(ObjectId bufferId,uint64_t requestSerial,WGPUMapModeFlags mode,uint64_t offset64,uint64_t size64)53 bool Server::DoBufferMapAsync(ObjectId bufferId, 54 uint64_t requestSerial, 55 WGPUMapModeFlags mode, 56 uint64_t offset64, 57 uint64_t size64) { 58 // These requests are just forwarded to the buffer, with userdata containing what the 59 // client will require in the return command. 60 61 // The null object isn't valid as `self` 62 if (bufferId == 0) { 63 return false; 64 } 65 66 auto* buffer = BufferObjects().Get(bufferId); 67 if (buffer == nullptr) { 68 return false; 69 } 70 71 std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>(); 72 userdata->buffer = ObjectHandle{bufferId, buffer->generation}; 73 userdata->bufferObj = buffer->handle; 74 userdata->requestSerial = requestSerial; 75 userdata->mode = mode; 76 77 // Make sure that the deserialized offset and size are no larger than 78 // std::numeric_limits<size_t>::max() so that they are CPU-addressable, and size is not 79 // WGPU_WHOLE_MAP_SIZE, which is by definition std::numeric_limits<size_t>::max(). Since 80 // client does the default size computation, we should always have a valid actual size here 81 // in server. All other invalid actual size can be caught by dawn native side validation. 82 if (offset64 > std::numeric_limits<size_t>::max() || size64 >= WGPU_WHOLE_MAP_SIZE) { 83 OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get()); 84 return true; 85 } 86 87 size_t offset = static_cast<size_t>(offset64); 88 size_t size = static_cast<size_t>(size64); 89 90 userdata->offset = offset; 91 userdata->size = size; 92 93 mProcs.bufferMapAsync( 94 buffer->handle, mode, offset, size, 95 ForwardToServer<decltype( 96 &Server::OnBufferMapAsyncCallback)>::Func<&Server::OnBufferMapAsyncCallback>(), 97 userdata.release()); 98 99 return true; 100 } 101 DoDeviceCreateBuffer(ObjectId deviceId,const WGPUBufferDescriptor * descriptor,ObjectHandle bufferResult,uint64_t readHandleCreateInfoLength,const uint8_t * readHandleCreateInfo,uint64_t writeHandleCreateInfoLength,const uint8_t * writeHandleCreateInfo)102 bool Server::DoDeviceCreateBuffer(ObjectId deviceId, 103 const WGPUBufferDescriptor* descriptor, 104 ObjectHandle bufferResult, 105 uint64_t readHandleCreateInfoLength, 106 const uint8_t* readHandleCreateInfo, 107 uint64_t writeHandleCreateInfoLength, 108 const uint8_t* writeHandleCreateInfo) { 109 auto* device = DeviceObjects().Get(deviceId); 110 if (device == nullptr) { 111 return false; 112 } 113 114 // Create and register the buffer object. 115 auto* resultData = BufferObjects().Allocate(bufferResult.id); 116 if (resultData == nullptr) { 117 return false; 118 } 119 resultData->generation = bufferResult.generation; 120 resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor); 121 resultData->deviceInfo = device->info.get(); 122 resultData->usage = descriptor->usage; 123 resultData->mappedAtCreation = descriptor->mappedAtCreation; 124 if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) { 125 return false; 126 } 127 128 // isReadMode and isWriteMode could be true at the same time if usage contains 129 // WGPUMapMode_Read and buffer is mappedAtCreation 130 bool isReadMode = descriptor->usage & WGPUMapMode_Read; 131 bool isWriteMode = descriptor->usage & WGPUMapMode_Write || descriptor->mappedAtCreation; 132 133 // This is the size of data deserialized from the command stream to create the read/write 134 // handle, which must be CPU-addressable. 135 if (readHandleCreateInfoLength > std::numeric_limits<size_t>::max() || 136 writeHandleCreateInfoLength > std::numeric_limits<size_t>::max() || 137 readHandleCreateInfoLength > 138 std::numeric_limits<size_t>::max() - writeHandleCreateInfoLength) { 139 return false; 140 } 141 142 if (isWriteMode) { 143 MemoryTransferService::WriteHandle* writeHandle = nullptr; 144 // Deserialize metadata produced from the client to create a companion server handle. 145 if (!mMemoryTransferService->DeserializeWriteHandle( 146 writeHandleCreateInfo, static_cast<size_t>(writeHandleCreateInfoLength), 147 &writeHandle)) { 148 return false; 149 } 150 ASSERT(writeHandle != nullptr); 151 resultData->writeHandle.reset(writeHandle); 152 writeHandle->SetDataLength(descriptor->size); 153 154 if (descriptor->mappedAtCreation) { 155 void* mapping = 156 mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size); 157 if (mapping == nullptr) { 158 // A zero mapping is used to indicate an allocation error of an error buffer. 159 // This is a valid case and isn't fatal. Remember the buffer is an error so as 160 // to skip subsequent mapping operations. 161 resultData->mapWriteState = BufferMapWriteState::MapError; 162 return true; 163 } 164 ASSERT(mapping != nullptr); 165 writeHandle->SetTarget(mapping); 166 167 resultData->mapWriteState = BufferMapWriteState::Mapped; 168 } 169 } 170 171 if (isReadMode) { 172 MemoryTransferService::ReadHandle* readHandle = nullptr; 173 // Deserialize metadata produced from the client to create a companion server handle. 174 if (!mMemoryTransferService->DeserializeReadHandle( 175 readHandleCreateInfo, static_cast<size_t>(readHandleCreateInfoLength), 176 &readHandle)) { 177 return false; 178 } 179 ASSERT(readHandle != nullptr); 180 181 resultData->readHandle.reset(readHandle); 182 } 183 184 return true; 185 } 186 DoBufferUpdateMappedData(ObjectId bufferId,uint64_t writeDataUpdateInfoLength,const uint8_t * writeDataUpdateInfo,uint64_t offset,uint64_t size)187 bool Server::DoBufferUpdateMappedData(ObjectId bufferId, 188 uint64_t writeDataUpdateInfoLength, 189 const uint8_t* writeDataUpdateInfo, 190 uint64_t offset, 191 uint64_t size) { 192 // The null object isn't valid as `self` 193 if (bufferId == 0) { 194 return false; 195 } 196 197 if (writeDataUpdateInfoLength > std::numeric_limits<size_t>::max() || 198 offset > std::numeric_limits<size_t>::max() || 199 size > std::numeric_limits<size_t>::max()) { 200 return false; 201 } 202 203 auto* buffer = BufferObjects().Get(bufferId); 204 if (buffer == nullptr) { 205 return false; 206 } 207 switch (buffer->mapWriteState) { 208 case BufferMapWriteState::Unmapped: 209 return false; 210 case BufferMapWriteState::MapError: 211 // The buffer is mapped but there was an error allocating mapped data. 212 // Do not perform the memcpy. 213 return true; 214 case BufferMapWriteState::Mapped: 215 break; 216 } 217 if (!buffer->writeHandle) { 218 // This check is performed after the check for the MapError state. It is permissible 219 // to Unmap and attempt to update mapped data of an error buffer. 220 return false; 221 } 222 223 // Deserialize the flush info and flush updated data from the handle into the target 224 // of the handle. The target is set via WriteHandle::SetTarget. 225 return buffer->writeHandle->DeserializeDataUpdate( 226 writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength), 227 static_cast<size_t>(offset), static_cast<size_t>(size)); 228 } 229 OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status,MapUserdata * data)230 void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) { 231 // Skip sending the callback if the buffer has already been destroyed. 232 auto* bufferData = BufferObjects().Get(data->buffer.id); 233 if (bufferData == nullptr || bufferData->generation != data->buffer.generation) { 234 return; 235 } 236 237 bool isRead = data->mode & WGPUMapMode_Read; 238 bool isSuccess = status == WGPUBufferMapAsyncStatus_Success; 239 240 ReturnBufferMapAsyncCallbackCmd cmd; 241 cmd.buffer = data->buffer; 242 cmd.requestSerial = data->requestSerial; 243 cmd.status = status; 244 cmd.readDataUpdateInfoLength = 0; 245 cmd.readDataUpdateInfo = nullptr; 246 247 const void* readData = nullptr; 248 if (isSuccess) { 249 if (isRead) { 250 // Get the serialization size of the message to initialize ReadHandle data. 251 readData = 252 mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size); 253 cmd.readDataUpdateInfoLength = 254 bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size); 255 } else { 256 ASSERT(data->mode & WGPUMapMode_Write); 257 // The in-flight map request returned successfully. 258 bufferData->mapWriteState = BufferMapWriteState::Mapped; 259 // Set the target of the WriteHandle to the mapped buffer data. 260 // writeHandle Target always refers to the buffer base address. 261 // but we call getMappedRange exactly with the range of data that is potentially 262 // modified (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a 263 // subset of the buffer is actually mapped) in case the implementation does some 264 // range tracking. 265 bufferData->writeHandle->SetTarget( 266 static_cast<uint8_t*>( 267 mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) - 268 data->offset); 269 } 270 } 271 272 SerializeCommand(cmd, cmd.readDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) { 273 if (isSuccess && isRead) { 274 char* readHandleBuffer; 275 WIRE_TRY(serializeBuffer->NextN(cmd.readDataUpdateInfoLength, &readHandleBuffer)); 276 // The in-flight map request returned successfully. 277 bufferData->readHandle->SerializeDataUpdate(readData, data->offset, data->size, 278 readHandleBuffer); 279 } 280 return WireResult::Success; 281 }); 282 } 283 284 }} // namespace dawn_wire::server 285