gecko-dev/dom/webgpu/Buffer.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

156 строки
5.6 KiB
C
Исходник Постоянная ссылка Обычный вид История

/* -*- 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/. */
#ifndef GPU_BUFFER_H_
#define GPU_BUFFER_H_
#include "js/RootingAPI.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/webgpu/WebGPUTypes.h"
#include "nsTArray.h"
#include "ObjectModel.h"
#include "mozilla/ipc/RawShmem.h"
#include <memory>
namespace mozilla {
namespace webgpu {
struct MappedView;
} // namespace webgpu
} // namespace mozilla
// Give `nsTArray` some advice on how to handle `MappedInfo::mViews`.
//
// In the `mozilla::webgpu` namespace, `MappedInfo::mViews` is an
// `nsTArray<MappedView>`, and `MappedView::mArrayBuffer` is a `JS::Heap`
// pointer. This arrangement requires special handling.
//
// Normally, `nsTArray` wants its element type to be movable with simple byte
// copies, so that an `nsTArray` can efficiently resize its element buffer.
// However, `JS::Heap` is marked `MOZ_NON_MEMMOVABLE`, meaning that it cannot be
// safely moved by a simple byte-by-byte copy. Normally, this would cause
// `nsTArray` to reject `JS::Heap` as an element type, but `nsTArray.h`
// specializes `nsTArray_RelocationStrategy` to indicate that `JS::Heap` can be
// moved safely using its move constructor. This causes `nsTArray<JS::Heap<T>>`
// to perform element buffer moves using element-by-element move constructor
// application: slower, but safe for `JS::Heap`.
//
// However, while `MappedView` is automatically marked `MOZ_NON_MEMMOVABLE`
// because of its `mArrayBuffer` member, the `nsTArray_RelocationStrategy`
// specialization is not somehow similarly magically carried over from
// `JS::Heap` to `MappedView`. To use `MappedView` in `nsTArray`, we must spell
// out a relocation strategy for it.
template <>
struct nsTArray_RelocationStrategy<mozilla::webgpu::MappedView> {
// The default move constructors are fine for MappedView.
using Type =
nsTArray_RelocateUsingMoveConstructor<mozilla::webgpu::MappedView>;
};
namespace mozilla {
class ErrorResult;
namespace dom {
struct GPUBufferDescriptor;
template <typename T>
class Optional;
enum class GPUBufferMapState : uint8_t;
} // namespace dom
namespace webgpu {
class Device;
// A portion of the current mapped buffer range that is currently
// visible to JS as an ArrayBuffer.
struct MappedView {
BufferAddress mOffset;
BufferAddress mRangeEnd;
JS::Heap<JSObject*> mArrayBuffer;
MappedView(BufferAddress aOffset, BufferAddress aRangeEnd,
JSObject* aArrayBuffer)
: mOffset(aOffset), mRangeEnd(aRangeEnd), mArrayBuffer(aArrayBuffer) {}
};
struct MappedInfo {
// True if mapping is requested for writing.
bool mWritable = false;
// Populated by `GetMappedRange`.
nsTArray<MappedView> mViews;
Bug 1777535 - Track the buffer mapAsync promise. r=jimb Per spec (and discussion with someone on the chromium side where spec is vague), the correct behavior should be: - MapAsync validation happens on the device timeline, so we should reject the promise in mapAsync on the content side if we run into an internal error not described by the spec. - Unmap immediately rejects all pending mapping promises on the content side (there can be multiple of them since we have to catch that error on the device timeline). This patch tracks a single mapping promise at a time and immediately rejects on the content side any subseqent mapping request made until unmap is called. This means our current implementation deviates slightly from the current state of the spec in that: - The promise is rejected earlier on the content timeline, - If the first request fails, all subsequent requests will fail until either unmap or when the content side receives and processes the rejected promise, whereas Dawn's implementation would allow the first valid request to succed. There was some confusion around the the use of uint64_t and size_t which probably originated at point where this code was working differently. This patch uses uint64_t (=BufferAddress) more consistently removing the need for some of the casting and overflow checks. One notable change in the overall logic is that SetMapped is now called when the buffer is actually in the mapped state (before this patch it was called as soon as the buffer had a pending map request). Depends on D151618 Differential Revision: https://phabricator.services.mozilla.com/D151619
2022-08-10 18:55:05 +03:00
BufferAddress mOffset;
BufferAddress mSize;
MappedInfo() = default;
MappedInfo(const MappedInfo&) = delete;
};
class Buffer final : public ObjectBase, public ChildOf<Device> {
public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Buffer)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Buffer)
GPU_DECL_JS_WRAP(Buffer)
static already_AddRefed<Buffer> Create(Device* aDevice, RawId aDeviceId,
const dom::GPUBufferDescriptor& aDesc,
ErrorResult& aRv);
already_AddRefed<dom::Promise> MapAsync(uint32_t aMode, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
ErrorResult& aRv);
void GetMappedRange(JSContext* aCx, uint64_t aOffset,
const dom::Optional<uint64_t>& aSize,
JS::Rooted<JSObject*>* aObject, ErrorResult& aRv);
void Unmap(JSContext* aCx, ErrorResult& aRv);
void Destroy(JSContext* aCx, ErrorResult& aRv);
const RawId mId;
uint64_t Size() const { return mSize; }
uint32_t Usage() const { return mUsage; }
dom::GPUBufferMapState MapState() const;
private:
Buffer(Device* const aParent, RawId aId, BufferAddress aSize, uint32_t aUsage,
ipc::WritableSharedMemoryMapping&& aShmem);
virtual ~Buffer();
Device& GetDevice() { return *mParent; }
void Cleanup();
void UnmapArrayBuffers(JSContext* aCx, ErrorResult& aRv);
Bug 1777535 - Track the buffer mapAsync promise. r=jimb Per spec (and discussion with someone on the chromium side where spec is vague), the correct behavior should be: - MapAsync validation happens on the device timeline, so we should reject the promise in mapAsync on the content side if we run into an internal error not described by the spec. - Unmap immediately rejects all pending mapping promises on the content side (there can be multiple of them since we have to catch that error on the device timeline). This patch tracks a single mapping promise at a time and immediately rejects on the content side any subseqent mapping request made until unmap is called. This means our current implementation deviates slightly from the current state of the spec in that: - The promise is rejected earlier on the content timeline, - If the first request fails, all subsequent requests will fail until either unmap or when the content side receives and processes the rejected promise, whereas Dawn's implementation would allow the first valid request to succed. There was some confusion around the the use of uint64_t and size_t which probably originated at point where this code was working differently. This patch uses uint64_t (=BufferAddress) more consistently removing the need for some of the casting and overflow checks. One notable change in the overall logic is that SetMapped is now called when the buffer is actually in the mapped state (before this patch it was called as soon as the buffer had a pending map request). Depends on D151618 Differential Revision: https://phabricator.services.mozilla.com/D151619
2022-08-10 18:55:05 +03:00
void RejectMapRequest(dom::Promise* aPromise, nsACString& message);
void AbortMapRequest();
void SetMapped(BufferAddress aOffset, BufferAddress aSize, bool aWritable);
// Note: we can't map a buffer with the size that don't fit into `size_t`
// (which may be smaller than `BufferAddress`), but general not all buffers
// are mapped.
const BufferAddress mSize;
const uint32_t mUsage;
nsString mLabel;
// Information about the currently active mapping.
Maybe<MappedInfo> mMapped;
Bug 1777535 - Track the buffer mapAsync promise. r=jimb Per spec (and discussion with someone on the chromium side where spec is vague), the correct behavior should be: - MapAsync validation happens on the device timeline, so we should reject the promise in mapAsync on the content side if we run into an internal error not described by the spec. - Unmap immediately rejects all pending mapping promises on the content side (there can be multiple of them since we have to catch that error on the device timeline). This patch tracks a single mapping promise at a time and immediately rejects on the content side any subseqent mapping request made until unmap is called. This means our current implementation deviates slightly from the current state of the spec in that: - The promise is rejected earlier on the content timeline, - If the first request fails, all subsequent requests will fail until either unmap or when the content side receives and processes the rejected promise, whereas Dawn's implementation would allow the first valid request to succed. There was some confusion around the the use of uint64_t and size_t which probably originated at point where this code was working differently. This patch uses uint64_t (=BufferAddress) more consistently removing the need for some of the casting and overflow checks. One notable change in the overall logic is that SetMapped is now called when the buffer is actually in the mapped state (before this patch it was called as soon as the buffer had a pending map request). Depends on D151618 Differential Revision: https://phabricator.services.mozilla.com/D151619
2022-08-10 18:55:05 +03:00
RefPtr<dom::Promise> mMapRequest;
// A shared memory mapping for the entire buffer, or a zero-length
// mapping.
//
// If `mUsage` contains `MAP_READ` or `MAP_WRITE`, this mapping is
// created at `Buffer` construction, and destroyed at `Buffer`
// destruction.
//
// If `mUsage` contains neither of those flags, but `this` is mapped
// at creation, this mapping is created at `Buffer` construction,
// and destroyed when we first unmap the buffer, by clearing this
// `shared_ptr`.
//
// Otherwise, this points to `WritableSharedMemoryMapping()` (the
// default constructor), a zero-length mapping that doesn't point to
// any shared memory.
std::shared_ptr<ipc::WritableSharedMemoryMapping> mShmem;
};
} // namespace webgpu
} // namespace mozilla
#endif // GPU_BUFFER_H_