// Copyright 2019 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "common/Assert.h" #include "dawn_wire/BufferConsumer_impl.h" #include "dawn_wire/WireCmd_autogen.h" #include "dawn_wire/server/Server.h" #include namespace dawn_wire { namespace server { bool Server::PreHandleBufferUnmap(const BufferUnmapCmd& cmd) { auto* buffer = BufferObjects().Get(cmd.selfId); DAWN_ASSERT(buffer != nullptr); if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) { // This indicates the writeHandle is for mappedAtCreation only. Destroy on unmap // writeHandle could have possibly been deleted if buffer is already destroyed so we // don't assert it's non-null buffer->writeHandle = nullptr; } buffer->mapWriteState = BufferMapWriteState::Unmapped; return true; } bool Server::PreHandleBufferDestroy(const BufferDestroyCmd& cmd) { // Destroying a buffer does an implicit unmapping. auto* buffer = BufferObjects().Get(cmd.selfId); DAWN_ASSERT(buffer != nullptr); // The buffer was destroyed. Clear the Read/WriteHandle. buffer->readHandle = nullptr; buffer->writeHandle = nullptr; buffer->mapWriteState = BufferMapWriteState::Unmapped; return true; } bool Server::DoBufferMapAsync(ObjectId bufferId, uint64_t requestSerial, WGPUMapModeFlags mode, uint64_t offset64, uint64_t size64) { // These requests are just forwarded to the buffer, with userdata containing what the // client will require in the return command. // The null object isn't valid as `self` if (bufferId == 0) { return false; } auto* buffer = BufferObjects().Get(bufferId); if (buffer == nullptr) { return false; } std::unique_ptr userdata = MakeUserdata(); userdata->buffer = ObjectHandle{bufferId, buffer->generation}; userdata->bufferObj = buffer->handle; userdata->requestSerial = requestSerial; userdata->mode = mode; if (offset64 > std::numeric_limits::max() || size64 > std::numeric_limits::max()) { OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get()); return true; } size_t offset = static_cast(offset64); size_t size = static_cast(size64); userdata->offset = offset; userdata->size = size; mProcs.bufferMapAsync( buffer->handle, mode, offset, size, ForwardToServer::Func<&Server::OnBufferMapAsyncCallback>(), userdata.release()); return true; } bool Server::DoDeviceCreateBuffer(ObjectId deviceId, const WGPUBufferDescriptor* descriptor, ObjectHandle bufferResult, uint64_t readHandleCreateInfoLength, const uint8_t* readHandleCreateInfo, uint64_t writeHandleCreateInfoLength, const uint8_t* writeHandleCreateInfo) { auto* device = DeviceObjects().Get(deviceId); if (device == nullptr) { return false; } // Create and register the buffer object. auto* resultData = BufferObjects().Allocate(bufferResult.id); if (resultData == nullptr) { return false; } resultData->generation = bufferResult.generation; resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor); resultData->deviceInfo = device->info.get(); resultData->usage = descriptor->usage; resultData->mappedAtCreation = descriptor->mappedAtCreation; if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) { return false; } // isReadMode and isWriteMode could be true at the same time if usage contains // WGPUMapMode_Read and buffer is mappedAtCreation bool isReadMode = descriptor->usage & WGPUMapMode_Read; bool isWriteMode = descriptor->usage & WGPUMapMode_Write || descriptor->mappedAtCreation; // This is the size of data deserialized from the command stream to create the read/write // handle, which must be CPU-addressable. if (readHandleCreateInfoLength > std::numeric_limits::max() || writeHandleCreateInfoLength > std::numeric_limits::max() || readHandleCreateInfoLength > std::numeric_limits::max() - writeHandleCreateInfoLength) { return false; } if (isWriteMode) { MemoryTransferService::WriteHandle* writeHandle = nullptr; // Deserialize metadata produced from the client to create a companion server handle. if (!mMemoryTransferService->DeserializeWriteHandle( writeHandleCreateInfo, static_cast(writeHandleCreateInfoLength), &writeHandle)) { return false; } ASSERT(writeHandle != nullptr); resultData->writeHandle.reset(writeHandle); writeHandle->SetDataLength(descriptor->size); if (descriptor->mappedAtCreation) { void* mapping = mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size); if (mapping == nullptr) { // A zero mapping is used to indicate an allocation error of an error buffer. // This is a valid case and isn't fatal. Remember the buffer is an error so as // to skip subsequent mapping operations. resultData->mapWriteState = BufferMapWriteState::MapError; return true; } ASSERT(mapping != nullptr); writeHandle->SetTarget(mapping); resultData->mapWriteState = BufferMapWriteState::Mapped; } } if (isReadMode) { MemoryTransferService::ReadHandle* readHandle = nullptr; // Deserialize metadata produced from the client to create a companion server handle. if (!mMemoryTransferService->DeserializeReadHandle( readHandleCreateInfo, static_cast(readHandleCreateInfoLength), &readHandle)) { return false; } ASSERT(readHandle != nullptr); resultData->readHandle.reset(readHandle); } return true; } bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint64_t writeDataUpdateInfoLength, const uint8_t* writeDataUpdateInfo, uint64_t offset, uint64_t size) { // The null object isn't valid as `self` if (bufferId == 0) { return false; } if (writeDataUpdateInfoLength > std::numeric_limits::max() || offset > std::numeric_limits::max() || size > std::numeric_limits::max()) { return false; } auto* buffer = BufferObjects().Get(bufferId); if (buffer == nullptr) { return false; } switch (buffer->mapWriteState) { case BufferMapWriteState::Unmapped: return false; case BufferMapWriteState::MapError: // The buffer is mapped but there was an error allocating mapped data. // Do not perform the memcpy. return true; case BufferMapWriteState::Mapped: break; } if (!buffer->writeHandle) { // This check is performed after the check for the MapError state. It is permissible // to Unmap and attempt to update mapped data of an error buffer. return false; } // Deserialize the flush info and flush updated data from the handle into the target // of the handle. The target is set via WriteHandle::SetTarget. return buffer->writeHandle->DeserializeDataUpdate( writeDataUpdateInfo, static_cast(writeDataUpdateInfoLength), static_cast(offset), static_cast(size)); } void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) { // Skip sending the callback if the buffer has already been destroyed. auto* bufferData = BufferObjects().Get(data->buffer.id); if (bufferData == nullptr || bufferData->generation != data->buffer.generation) { return; } bool isRead = data->mode & WGPUMapMode_Read; bool isSuccess = status == WGPUBufferMapAsyncStatus_Success; ReturnBufferMapAsyncCallbackCmd cmd; cmd.buffer = data->buffer; cmd.requestSerial = data->requestSerial; cmd.status = status; cmd.readDataUpdateInfoLength = 0; cmd.readDataUpdateInfo = nullptr; const void* readData = nullptr; if (isSuccess) { if (isRead) { // Get the serialization size of the message to initialize ReadHandle data. readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size); cmd.readDataUpdateInfoLength = bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size); } else { ASSERT(data->mode & WGPUMapMode_Write); // The in-flight map request returned successfully. bufferData->mapWriteState = BufferMapWriteState::Mapped; // Set the target of the WriteHandle to the mapped buffer data. // writeHandle Target always refers to the buffer base address. // but we call getMappedRange exactly with the range of data that is potentially // modified (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a // subset of the buffer is actually mapped) in case the implementation does some // range tracking. bufferData->writeHandle->SetTarget( static_cast( mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) - data->offset); } } SerializeCommand(cmd, cmd.readDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) { if (isSuccess && isRead) { char* readHandleBuffer; WIRE_TRY(serializeBuffer->NextN(cmd.readDataUpdateInfoLength, &readHandleBuffer)); // The in-flight map request returned successfully. bufferData->readHandle->SerializeDataUpdate(readData, data->offset, data->size, readHandleBuffer); } return WireResult::Success; }); } }} // namespace dawn_wire::server