blob: 3a3642f7b3125f96ab9203dfbd9c20e325a71cb5 [file] [log] [blame] [edit]
// Copyright 2019 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <limits>
#include <memory>
#include "dawn/common/Assert.h"
#include "dawn/wire/BufferConsumer_impl.h"
#include "dawn/wire/WireCmd_autogen.h"
#include "dawn/wire/WireResult.h"
#include "dawn/wire/server/Server.h"
namespace dawn::wire::server {
WireResult Server::PreHandleBufferUnmap(const BufferUnmapCmd& cmd) {
Known<WGPUBuffer> buffer;
WIRE_TRY(Objects<WGPUBuffer>().Get(cmd.selfId, &buffer));
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 WireResult::Success;
}
WireResult Server::PreHandleBufferDestroy(const BufferDestroyCmd& cmd) {
// Destroying a buffer does an implicit unmapping.
Known<WGPUBuffer> buffer;
WIRE_TRY(Objects<WGPUBuffer>().Get(cmd.selfId, &buffer));
// The buffer was destroyed. Clear the Read/WriteHandle.
buffer->readHandle = nullptr;
buffer->writeHandle = nullptr;
buffer->mapWriteState = BufferMapWriteState::Unmapped;
return WireResult::Success;
}
WireResult Server::DoBufferMapAsync(Known<WGPUBuffer> buffer,
ObjectHandle eventManager,
WGPUFuture future,
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.
std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
userdata->buffer = buffer.AsHandle();
userdata->eventManager = eventManager;
userdata->bufferObj = buffer->handle;
userdata->future = future;
userdata->mode = mode;
// Make sure that the deserialized offset and size are no larger than
// std::numeric_limits<size_t>::max() so that they are CPU-addressable, and size is not
// WGPU_WHOLE_MAP_SIZE, which is by definition std::numeric_limits<size_t>::max(). Since
// client does the default size computation, we should always have a valid actual size here
// in server. All other invalid actual size can be caught by dawn native side validation.
if (offset64 > std::numeric_limits<size_t>::max()) {
OnBufferMapAsyncCallback(userdata.get(), WGPUBufferMapAsyncStatus_OffsetOutOfRange);
return WireResult::Success;
}
if (size64 >= WGPU_WHOLE_MAP_SIZE) {
OnBufferMapAsyncCallback(userdata.get(), WGPUBufferMapAsyncStatus_SizeOutOfRange);
return WireResult::Success;
}
size_t offset = static_cast<size_t>(offset64);
size_t size = static_cast<size_t>(size64);
userdata->offset = offset;
userdata->size = size;
mProcs.bufferMapAsync(buffer->handle, mode, offset, size,
ForwardToServer<&Server::OnBufferMapAsyncCallback>, userdata.release());
return WireResult::Success;
}
WireResult Server::DoDeviceCreateBuffer(Known<WGPUDevice> device,
const WGPUBufferDescriptor* descriptor,
ObjectHandle bufferHandle,
uint64_t readHandleCreateInfoLength,
const uint8_t* readHandleCreateInfo,
uint64_t writeHandleCreateInfoLength,
const uint8_t* writeHandleCreateInfo) {
// Create and register the buffer object.
Reserved<WGPUBuffer> buffer;
WIRE_TRY(Objects<WGPUBuffer>().Allocate(&buffer, bufferHandle));
buffer->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
buffer->usage = descriptor->usage;
buffer->mappedAtCreation = descriptor->mappedAtCreation;
// 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<size_t>::max() ||
writeHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
readHandleCreateInfoLength >
std::numeric_limits<size_t>::max() - writeHandleCreateInfoLength) {
return WireResult::FatalError;
}
if (isWriteMode) {
MemoryTransferService::WriteHandle* writeHandle = nullptr;
// Deserialize metadata produced from the client to create a companion server handle.
if (!mMemoryTransferService->DeserializeWriteHandle(
writeHandleCreateInfo, static_cast<size_t>(writeHandleCreateInfoLength),
&writeHandle)) {
return WireResult::FatalError;
}
DAWN_ASSERT(writeHandle != nullptr);
buffer->writeHandle.reset(writeHandle);
writeHandle->SetDataLength(descriptor->size);
if (descriptor->mappedAtCreation) {
void* mapping = mProcs.bufferGetMappedRange(buffer->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.
buffer->mapWriteState = BufferMapWriteState::MapError;
return WireResult::Success;
}
DAWN_ASSERT(mapping != nullptr);
writeHandle->SetTarget(mapping);
buffer->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<size_t>(readHandleCreateInfoLength),
&readHandle)) {
return WireResult::FatalError;
}
DAWN_ASSERT(readHandle != nullptr);
buffer->readHandle.reset(readHandle);
}
return WireResult::Success;
}
WireResult Server::DoBufferUpdateMappedData(Known<WGPUBuffer> buffer,
uint64_t writeDataUpdateInfoLength,
const uint8_t* writeDataUpdateInfo,
uint64_t offset,
uint64_t size) {
if (writeDataUpdateInfoLength > std::numeric_limits<size_t>::max() ||
offset > std::numeric_limits<size_t>::max() || size > std::numeric_limits<size_t>::max()) {
return WireResult::FatalError;
}
switch (buffer->mapWriteState) {
case BufferMapWriteState::Unmapped:
return WireResult::FatalError;
case BufferMapWriteState::MapError:
// The buffer is mapped but there was an error allocating mapped data.
// Do not perform the memcpy.
return WireResult::Success;
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 WireResult::FatalError;
}
// Deserialize the flush info and flush updated data from the handle into the target
// of the handle. The target is set via WriteHandle::SetTarget.
if (!buffer->writeHandle->DeserializeDataUpdate(
writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
static_cast<size_t>(offset), static_cast<size_t>(size))) {
return WireResult::FatalError;
}
return WireResult::Success;
}
void Server::OnBufferMapAsyncCallback(MapUserdata* data, WGPUBufferMapAsyncStatus status) {
// Skip sending the callback if the buffer has already been destroyed.
Known<WGPUBuffer> buffer;
if (Objects<WGPUBuffer>().Get(data->buffer.id, &buffer) != WireResult::Success ||
buffer->generation != data->buffer.generation) {
return;
}
bool isRead = data->mode & WGPUMapMode_Read;
bool isSuccess = status == WGPUBufferMapAsyncStatus_Success;
ReturnBufferMapAsyncCallbackCmd cmd;
cmd.eventManager = data->eventManager;
cmd.future = data->future;
cmd.status = status;
cmd.readDataUpdateInfoLength = 0;
cmd.readDataUpdateInfo = nullptr;
const void* readData = nullptr;
size_t readDataUpdateInfoLength = 0;
if (isSuccess) {
if (isRead) {
// Get the serialization size of the message to initialize ReadHandle data.
readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
readDataUpdateInfoLength =
buffer->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
cmd.readDataUpdateInfoLength = readDataUpdateInfoLength;
} else {
DAWN_ASSERT(data->mode & WGPUMapMode_Write);
// The in-flight map request returned successfully.
buffer->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.
buffer->writeHandle->SetTarget(static_cast<uint8_t*>(mProcs.bufferGetMappedRange(
data->bufferObj, data->offset, data->size)) -
data->offset);
}
}
SerializeCommand(cmd, CommandExtension{readDataUpdateInfoLength, [&](char* readHandleBuffer) {
if (isSuccess && isRead) {
// The in-flight map request returned
// successfully.
buffer->readHandle->SerializeDataUpdate(
readData, data->offset, data->size,
readHandleBuffer);
}
}});
}
} // namespace dawn::wire::server