/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
bool allocSucceeded = false; if (hasMapFlags || aDesc.mMappedAtCreation) { // If shmem allocation fails, we continue and provide the parent side with // an empty shmem which it will interpret as an OOM situtation. constauto checked = CheckedInt<size_t>(aDesc.mSize); const size_t maxSize = WGPUMAX_BUFFER_SIZE; if (checked.isValid()) {
size_t size = checked.value();
if (size > 0 && size < maxSize) { auto maybeShmem = ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
// zero out memory
memset(mapping.Bytes().data(), 0, size);
}
}
if (size == 0) { // Zero-sized buffers is a special case. We don't create a shmem since // allocating the memory would not make sense, however mappable null // buffers are allowed by the spec so we just pass the null handle which // in practice deserializes into a null handle on the parent side and // behaves like a zero-sized allocation.
allocSucceeded = true;
}
}
}
// If mapped at creation and the shmem allocation failed, immediately throw // a range error and don't attempt to create the buffer. if (aDesc.mMappedAtCreation && !allocSucceeded) {
aRv.ThrowRangeError("Allocation failed"); return nullptr;
}
RefPtr<Buffer> buffer = new Buffer(aDevice, bufferId, aDesc.mSize,
aDesc.mUsage, std::move(mapping));
buffer->SetLabel(aDesc.mLabel);
if (aDesc.mMappedAtCreation) { // Mapped at creation's raison d'être is write access, since the buffer is // being created and there isn't anything interesting to read in it yet. bool writable = true;
buffer->SetMapped(0, aDesc.mSize, writable);
}
if (mMapped && !mMapped->mViews.IsEmpty()) { // The array buffers could live longer than us and our shmem, so make sure // we clear the external buffer bindings.
dom::AutoJSAPI jsapi; if (jsapi.Init(GetDevice().GetOwnerGlobal())) {
IgnoredErrorResult rv;
UnmapArrayBuffers(jsapi.cx(), rv);
}
}
mMapped.reset();
GetDevice().UntrackBuffer(this);
auto bridge = GetDevice().GetBridge(); if (!bridge) { return;
}
if (bridge->CanSend()) {
bridge->SendBufferDrop(mId);
}
if (GetDevice().IsLost()) {
promise->MaybeRejectWithOperationError("Device Lost"); return promise.forget();
}
if (mMapRequest) {
promise->MaybeRejectWithOperationError("Buffer mapping is already pending"); return promise.forget();
}
BufferAddress size = 0; if (aSize.WasPassed()) {
size = aSize.Value();
} elseif (aOffset <= mSize) { // Default to passing the reminder of the buffer after the provided offset.
size = mSize - aOffset;
} else { // The provided offset is larger than the buffer size. // The parent side will handle the error, we can let the requested size be // zero.
}
RefPtr<Buffer> self(this);
auto mappingPromise = GetDevice().GetBridge()->SendBufferMap(
GetDevice().mId, mId, aMode, aOffset, size);
MOZ_ASSERT(mappingPromise);
mMapRequest = promise;
mappingPromise->Then(
GetCurrentSerialEventTarget(), __func__,
[promise, self](BufferMapResult&& aResult) { // Unmap might have been called while the result was on the way back. if (promise->State() != dom::Promise::PromiseState::Pending) { return;
}
// mValid should be true or we should have called unmap while marking // the buffer invalid, causing the promise to be rejected and the branch // above to have early-returned.
MOZ_RELEASE_ASSERT(self->mValid);
void Buffer::GetMappedRange(JSContext* aCx, uint64_t aOffset, const dom::Optional<uint64_t>& aSize,
JS::Rooted<JSObject*>* aObject, ErrorResult& aRv) { // The WebGPU spec spells out the validation we must perform, but // use `CheckedInt<uint64_t>` anyway to catch our mistakes. Except // where we explicitly say otherwise, invalid `CheckedInt` values // should only arise when we have a bug, so just calling // `CheckedInt::value` where needed should be fine (it checks with // `MOZ_DIAGNOSTIC_ASSERT`).
// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-getmappedrange // // Content timeline steps: // // 1. If `size` is missing: // 1. Let `rangeSize` be `max(0, this.size - offset)`. // Otherwise, let `rangeSize` be `size`. constauto offset = CheckedInt<uint64_t>(aOffset);
CheckedInt<uint64_t> rangeSize; if (aSize.WasPassed()) {
rangeSize = aSize.Value();
} else { constauto bufferSize = CheckedInt<uint64_t>(mSize); // Use `CheckInt`'s underflow detection for `max(0, ...)`.
rangeSize = bufferSize - offset; if (!rangeSize.isValid()) {
rangeSize = 0;
}
}
// 2. If any of the following conditions are unsatisfied, throw an // `OperationError` and stop. // // - `this.[[mapping]]` is not `null`. if (!mMapped) {
aRv.ThrowOperationError("Buffer is not mapped"); return;
}
// - `offset` is a multiple of 8. // // (`operator!=` is not available on `CheckedInt`.) if (offset.value() % 8 != 0) {
aRv.ThrowOperationError("GetMappedRange offset is not a multiple of 8"); return;
}
// - `rangeSize` is a multiple of `4`. if (rangeSize.value() % 4 != 0) {
aRv.ThrowOperationError("GetMappedRange size is not a multiple of 4"); return;
}
// - `offset ≥ this.[[mapping]].range[0]`. if (offset.value() < mMapped->mOffset) {
aRv.ThrowOperationError( "GetMappedRange offset starts before buffer's mapped range"); return;
}
// - `offset + rangeSize ≤ this.[[mapping]].range[1]`. // // Perform the addition in `CheckedInt`, treating overflow as a validation // error. constauto rangeEndChecked = offset + rangeSize; if (!rangeEndChecked.isValid() ||
rangeEndChecked.value() > mMapped->mOffset + mMapped->mSize) {
aRv.ThrowOperationError( "GetMappedRange range extends beyond buffer's mapped range"); return;
}
// - `[offset, offset + rangeSize)` does not overlap another range // in `this.[[mapping]].views`. const uint64_t rangeEnd = rangeEndChecked.value(); for (constauto& view : mMapped->mViews) { if (view.mOffset < rangeEnd && offset.value() < view.mRangeEnd) {
aRv.ThrowOperationError( "GetMappedRange range overlaps with existing buffer view"); return;
}
}
// 3. Let `data` be `this.[[mapping]].data`. // // The creation of a *pointer to* a `shared_ptr` here seems redundant but is // unfortunately necessary: `JS::BufferContentsDeleter` requires that its // `userData` be a `void*`, and while `shared_ptr` can't be inter-converted // with `void*` (it's actually two pointers), `shared_ptr*` obviously can.
std::shared_ptr<ipc::WritableSharedMemoryMapping>* data = new std::shared_ptr<ipc::WritableSharedMemoryMapping>(mShmem);
// 4. Let `view` be (potentially fallible operation follows) create an // `ArrayBuffer` of size `rangeSize`, but with its pointer mutably // referencing the content of `data` at offset `(offset - // [[mapping]].range[0])`. // // Since `size_t` may not be the same as `uint64_t`, check, convert, and check // again. `CheckedInt<size_t>(x)` produces an invalid value if `x` is not in // range for `size_t` before any conversion is performed. constauto checkedSize = CheckedInt<size_t>(rangeSize.value()).value(); constauto checkedOffset = CheckedInt<size_t>(offset.value()).value(); constauto span = (*data)->Bytes().Subspan(checkedOffset, checkedSize);
UniquePtr<void, JS::BufferContentsDeleter> contents{
span.data(), {&ExternalBufferFreeCallback, data}};
JS::Rooted<JSObject*> view(
aCx, JS::NewExternalArrayBuffer(aCx, checkedSize, std::move(contents))); if (!view) {
aRv.NoteJSContextException(aCx); return;
}
if (!hasMapFlags) { // We get here if the buffer was mapped at creation without map flags. // It won't be possible to map the buffer again so we can get rid of // our shmem on this side.
mShmem = std::make_shared<ipc::WritableSharedMemoryMapping>();
}
if (!GetDevice().IsLost()) {
GetDevice().GetBridge()->SendBufferUnmap(GetDevice().mId, mId,
mMapped->mWritable);
}
if (!GetDevice().IsLost()) {
GetDevice().GetBridge()->SendBufferDestroy(mId);
} // TODO: we don't have to implement it right now, but it's used by the // examples
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.