зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1777535 - Use unsafe shmems. r=jimb
This commit makes WebGPU buffers use unsafe shmems. Instead of relying on moving the shmem back and forth between the two processes to ensure thread safety, we instead rely on the validation done on both sides. The upside is that it makes it much easier to implement said validation correctly. In the interest of splitting the buffer mapping rework into small-ish commits, this one puts some structural pieces in place but doesn't necessarily do justice to the final implementation: - The validation itself is coming in subsequent patches in this series. - Mapping sub-ranges of the buffer was somewhat implemented in some parts of the parent code and not in others, and was fairly broken as a whole. This commit always maps the entire buffer and proper logic for sub-ranges is coming in another commit. The main things this commit does put in place: - Each mappable buffer is associated with a Shmem that is accessible to both sides. - On the child side, if a buffer is not mappable, then Buffer::mShmem is in its default state (it doesn't point to any shared memory segment). - On the parent side, if a buffer is not mappable it does not have an entry in mSharedMemoryMap. - The shmem is always created by the child and destroyed by the parent. Depends on D151615 Differential Revision: https://phabricator.services.mozilla.com/D151616
This commit is contained in:
Родитель
74e928ab94
Коммит
6c728ff882
|
@ -43,8 +43,8 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Buffer)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
Buffer::Buffer(Device* const aParent, RawId aId, BufferAddress aSize,
|
||||
uint32_t aUsage)
|
||||
: ChildOf(aParent), mId(aId), mSize(aSize), mUsage(aUsage) {
|
||||
uint32_t aUsage, ipc::Shmem&& aShmem)
|
||||
: ChildOf(aParent), mId(aId), mSize(aSize), mUsage(aUsage), mShmem(aShmem) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
|
@ -74,23 +74,16 @@ void Buffer::Cleanup() {
|
|||
|
||||
auto bridge = mParent->GetBridge();
|
||||
if (bridge && bridge->IsOpen()) {
|
||||
// Note: even if the buffer is considered mapped,
|
||||
// the shmem may be empty before the mapAsync callback
|
||||
// is resolved.
|
||||
if (mMapped && mMapped->mShmem.IsReadable()) {
|
||||
// Note: if the bridge is closed, all associated shmems are already
|
||||
// deleted.
|
||||
bridge->DeallocShmem(mMapped->mShmem);
|
||||
}
|
||||
// Tell the parent side the about the imminent disparition of the shmem
|
||||
// *before* deallocating it.
|
||||
bridge->SendBufferDestroy(mId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::SetMapped(ipc::Shmem&& aShmem, bool aWritable) {
|
||||
void Buffer::SetMapped(bool aWritable) {
|
||||
MOZ_ASSERT(!mMapped);
|
||||
mMapped.emplace();
|
||||
mMapped->mShmem = std::move(aShmem);
|
||||
mMapped->mWritable = aWritable;
|
||||
}
|
||||
|
||||
|
@ -130,9 +123,7 @@ must be either GPUMapMode.READ or GPUMapMode.WRITE");
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
// Initialize with a dummy shmem, it will become real after the promise is
|
||||
// resolved.
|
||||
SetMapped(ipc::Shmem(), aMode == dom::GPUMapMode_Binding::WRITE);
|
||||
SetMapped(aMode == dom::GPUMapMode_Binding::WRITE);
|
||||
|
||||
const auto checked = aSize.WasPassed() ? CheckedInt<size_t>(aSize.Value())
|
||||
: CheckedInt<size_t>(mSize) - aOffset;
|
||||
|
@ -151,27 +142,20 @@ must be either GPUMapMode.READ or GPUMapMode.WRITE");
|
|||
|
||||
mappingPromise->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self](MaybeShmem&& aShmem) {
|
||||
switch (aShmem.type()) {
|
||||
case MaybeShmem::TShmem: {
|
||||
// TODO: This isn't quite aligned with the spec but it prevents from
|
||||
// crashing when unmapping before the promise is resolved.
|
||||
if (self->mMapped.isSome()) {
|
||||
self->mMapped->mShmem = std::move(aShmem);
|
||||
promise->MaybeResolve(0);
|
||||
} else {
|
||||
promise->MaybeRejectWithOperationError(
|
||||
"Unmapped or invalid buffer");
|
||||
}
|
||||
|
||||
[promise, self](BufferMapResult&& aResult) {
|
||||
switch (aResult.type()) {
|
||||
case BufferMapResult::TBufferMapSuccess: {
|
||||
promise->MaybeResolve(0);
|
||||
break;
|
||||
}
|
||||
case BufferMapResult::TBufferMapError: {
|
||||
// TODO: update mapping state.
|
||||
auto& error = aResult.get_BufferMapError();
|
||||
promise->MaybeRejectWithOperationError(error.message());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (self->mMapped.isSome()) {
|
||||
self->mMapped->mShmem = ipc::Shmem();
|
||||
}
|
||||
promise->MaybeRejectWithOperationError(
|
||||
"Attempted to map an invalid buffer");
|
||||
MOZ_CRASH("unreachable");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -195,17 +179,17 @@ void Buffer::GetMappedRange(JSContext* aCx, uint64_t aOffset,
|
|||
aRv.ThrowRangeError("Invalid mapped range");
|
||||
return;
|
||||
}
|
||||
if (!mMapped || !mMapped->IsReady()) {
|
||||
if (!mMapped || !mShmem.IsReadable()) {
|
||||
aRv.ThrowInvalidStateError("Buffer is not mapped");
|
||||
return;
|
||||
}
|
||||
if (checkedMinBufferSize.value() > mMapped->mShmem.Size<uint8_t>()) {
|
||||
if (checkedMinBufferSize.value() > mShmem.Size<uint8_t>()) {
|
||||
aRv.ThrowOperationError("Mapped range exceeds buffer size");
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const arrayBuffer = mParent->CreateExternalArrayBuffer(
|
||||
aCx, checkedOffset.value(), checkedSize.value(), mMapped->mShmem);
|
||||
aCx, checkedOffset.value(), checkedSize.value(), mShmem);
|
||||
if (!arrayBuffer) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
|
@ -240,8 +224,18 @@ void Buffer::Unmap(JSContext* aCx, ErrorResult& aRv) {
|
|||
}
|
||||
|
||||
UnmapArrayBuffers(aCx, aRv);
|
||||
mParent->UnmapBuffer(mId, std::move(mMapped->mShmem), mMapped->mWritable,
|
||||
Mappable());
|
||||
|
||||
bool hasMapFlags = mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
|
||||
dom::GPUBufferUsage_Binding::MAP_READ);
|
||||
|
||||
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 handle on this side. The parent side will deallocate it.
|
||||
mShmem = ipc::Shmem();
|
||||
}
|
||||
|
||||
mParent->UnmapBuffer(mId, mMapped->mWritable);
|
||||
mMapped.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ namespace webgpu {
|
|||
class Device;
|
||||
|
||||
struct MappedInfo {
|
||||
ipc::Shmem mShmem;
|
||||
// True if mapping is requested for writing.
|
||||
bool mWritable = false;
|
||||
// Populated by `GetMappedRange`.
|
||||
|
@ -37,7 +36,6 @@ struct MappedInfo {
|
|||
|
||||
MappedInfo() = default;
|
||||
MappedInfo(const MappedInfo&) = delete;
|
||||
bool IsReady() const { return mShmem.IsReadable(); }
|
||||
};
|
||||
|
||||
class Buffer final : public ObjectBase, public ChildOf<Device> {
|
||||
|
@ -45,9 +43,9 @@ class Buffer final : public ObjectBase, public ChildOf<Device> {
|
|||
GPU_DECL_CYCLE_COLLECTION(Buffer)
|
||||
GPU_DECL_JS_WRAP(Buffer)
|
||||
|
||||
Buffer(Device* const aParent, RawId aId, BufferAddress aSize,
|
||||
uint32_t aUsage);
|
||||
void SetMapped(ipc::Shmem&& aShmem, bool aWritable);
|
||||
Buffer(Device* const aParent, RawId aId, BufferAddress aSize, uint32_t aUsage,
|
||||
ipc::Shmem&& aShmem);
|
||||
void SetMapped(bool aWritable);
|
||||
|
||||
const RawId mId;
|
||||
|
||||
|
@ -65,6 +63,9 @@ class Buffer final : public ObjectBase, public ChildOf<Device> {
|
|||
nsString mLabel;
|
||||
// Information about the currently active mapping.
|
||||
Maybe<MappedInfo> mMapped;
|
||||
// mShmem does not point to a shared memory segment if the buffer is not
|
||||
// mappable.
|
||||
ipc::Shmem mShmem;
|
||||
|
||||
public:
|
||||
already_AddRefed<dom::Promise> MapAsync(uint32_t aMode, uint64_t aOffset,
|
||||
|
|
|
@ -122,7 +122,7 @@ dom::Promise* Device::GetLost(ErrorResult& aRv) {
|
|||
already_AddRefed<Buffer> Device::CreateBuffer(
|
||||
const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
|
||||
if (!mBridge->CanSend()) {
|
||||
RefPtr<Buffer> buffer = new Buffer(this, 0, aDesc.mSize, 0);
|
||||
RefPtr<Buffer> buffer = new Buffer(this, 0, aDesc.mSize, 0, ipc::Shmem());
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
|
@ -137,8 +137,7 @@ already_AddRefed<Buffer> Device::CreateBuffer(
|
|||
}
|
||||
const auto& size = checked.value();
|
||||
|
||||
// TODO: use `ShmemPool`?
|
||||
if (!mBridge->AllocShmem(size, &shmem)) {
|
||||
if (!mBridge->AllocUnsafeShmem(size, &shmem)) {
|
||||
aRv.ThrowAbortError(
|
||||
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
|
||||
return nullptr;
|
||||
|
@ -148,15 +147,16 @@ already_AddRefed<Buffer> Device::CreateBuffer(
|
|||
memset(shmem.get<uint8_t>(), 0, size);
|
||||
}
|
||||
|
||||
// If the buffer is not mapped at creation, and it has Shmem, we send it
|
||||
// to the GPU process. Otherwise, we keep it.
|
||||
RawId id = mBridge->DeviceCreateBuffer(mId, aDesc);
|
||||
RefPtr<Buffer> buffer = new Buffer(this, id, aDesc.mSize, aDesc.mUsage);
|
||||
MaybeShmem maybeShmem = mozilla::null_t();
|
||||
if (shmem.IsReadable()) {
|
||||
maybeShmem = shmem;
|
||||
}
|
||||
RawId id = mBridge->DeviceCreateBuffer(mId, aDesc, std::move(maybeShmem));
|
||||
|
||||
RefPtr<Buffer> buffer =
|
||||
new Buffer(this, id, aDesc.mSize, aDesc.mUsage, std::move(shmem));
|
||||
if (aDesc.mMappedAtCreation) {
|
||||
buffer->SetMapped(std::move(shmem),
|
||||
!(aDesc.mUsage & dom::GPUBufferUsage_Binding::MAP_READ));
|
||||
} else if (hasMapFlags) {
|
||||
mBridge->SendBufferReturnShmem(id, std::move(shmem));
|
||||
buffer->SetMapped(!(aDesc.mUsage & dom::GPUBufferUsage_Binding::MAP_READ));
|
||||
}
|
||||
|
||||
return buffer.forget();
|
||||
|
@ -191,10 +191,9 @@ RefPtr<MappingPromise> Device::MapBufferAsync(RawId aId, uint32_t aMode,
|
|||
return mBridge->SendBufferMap(aId, mode, offset.value(), size.value());
|
||||
}
|
||||
|
||||
void Device::UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush,
|
||||
bool aKeepShmem) {
|
||||
void Device::UnmapBuffer(RawId aId, bool aFlush) {
|
||||
if (mBridge->CanSend()) {
|
||||
mBridge->SendBufferUnmap(aId, std::move(aShmem), aFlush, aKeepShmem);
|
||||
mBridge->SendBufferUnmap(aId, aFlush);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,8 @@ class SupportedLimits;
|
|||
class Texture;
|
||||
class WebGPUChild;
|
||||
|
||||
using MappingPromise = MozPromise<MaybeShmem, ipc::ResponseRejectReason, true>;
|
||||
using MappingPromise =
|
||||
MozPromise<BufferMapResult, ipc::ResponseRejectReason, true>;
|
||||
|
||||
class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
|
||||
public:
|
||||
|
@ -102,8 +103,7 @@ class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
|
|||
RefPtr<MappingPromise> MapBufferAsync(RawId aId, uint32_t aMode,
|
||||
size_t aOffset, size_t aSize,
|
||||
ErrorResult& aRv);
|
||||
void UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush,
|
||||
bool aKeepShmem);
|
||||
void UnmapBuffer(RawId aId, bool aFlush);
|
||||
already_AddRefed<Texture> InitSwapChain(
|
||||
const dom::GPUCanvasConfiguration& aDesc,
|
||||
const layers::CompositableHandle& aHandle, gfx::SurfaceFormat aFormat,
|
||||
|
|
|
@ -41,16 +41,15 @@ parent:
|
|||
async CommandEncoderAction(RawId selfId, RawId aDeviceId, ByteBuf buf);
|
||||
async BumpImplicitBindGroupLayout(RawId pipelineId, bool isCompute, uint32_t index, RawId assignId);
|
||||
|
||||
async CreateBuffer(RawId deviceId, RawId bufferId, GPUBufferDescriptor desc);
|
||||
async CreateBuffer(RawId deviceId, RawId bufferId, GPUBufferDescriptor desc, MaybeShmem shmem);
|
||||
|
||||
async InstanceRequestAdapter(GPURequestAdapterOptions options, RawId[] ids) returns (ByteBuf byteBuf);
|
||||
async AdapterRequestDevice(RawId selfId, ByteBuf buf, RawId newId) returns (bool success);
|
||||
async AdapterDestroy(RawId selfId);
|
||||
// TODO: We want to return an array of compilation messages.
|
||||
async DeviceCreateShaderModule(RawId selfId, RawId bufferId, nsString label, nsCString code) returns (WebGPUCompilationMessage[] messages);
|
||||
async BufferReturnShmem(RawId selfId, Shmem shmem);
|
||||
async BufferMap(RawId selfId, WGPUHostMap hostMap, uint64_t offset, uint64_t size) returns (MaybeShmem sm);
|
||||
async BufferUnmap(RawId selfId, Shmem shmem, bool flush, bool keepShmem);
|
||||
async BufferMap(RawId selfId, WGPUHostMap hostMap, uint64_t offset, uint64_t size) returns (BufferMapResult result);
|
||||
async BufferUnmap(RawId selfId, bool flush);
|
||||
async BufferDestroy(RawId selfId);
|
||||
async TextureDestroy(RawId selfId);
|
||||
async TextureViewDestroy(RawId selfId);
|
||||
|
|
|
@ -12,5 +12,19 @@ namespace webgpu {
|
|||
null_t;
|
||||
};
|
||||
|
||||
struct BufferMapSuccess {
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct BufferMapError {
|
||||
nsCString message;
|
||||
};
|
||||
|
||||
union BufferMapResult {
|
||||
BufferMapSuccess;
|
||||
BufferMapError;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -355,9 +355,10 @@ Maybe<DeviceRequest> WebGPUChild::AdapterRequestDevice(
|
|||
}
|
||||
|
||||
RawId WebGPUChild::DeviceCreateBuffer(RawId aSelfId,
|
||||
const dom::GPUBufferDescriptor& aDesc) {
|
||||
const dom::GPUBufferDescriptor& aDesc,
|
||||
MaybeShmem&& aShmem) {
|
||||
RawId bufferId = ffi::wgpu_client_make_buffer_id(mClient.get(), aSelfId);
|
||||
if (!SendCreateBuffer(aSelfId, bufferId, aDesc)) {
|
||||
if (!SendCreateBuffer(aSelfId, bufferId, aDesc, aShmem)) {
|
||||
MOZ_CRASH("IPC failure");
|
||||
}
|
||||
return bufferId;
|
||||
|
|
|
@ -62,8 +62,8 @@ class WebGPUChild final : public PWebGPUChild, public SupportsWeakPtr {
|
|||
Maybe<DeviceRequest> AdapterRequestDevice(
|
||||
RawId aSelfId, const dom::GPUDeviceDescriptor& aDesc,
|
||||
ffi::WGPULimits* aLimits);
|
||||
RawId DeviceCreateBuffer(RawId aSelfId,
|
||||
const dom::GPUBufferDescriptor& aDesc);
|
||||
RawId DeviceCreateBuffer(RawId aSelfId, const dom::GPUBufferDescriptor& aDesc,
|
||||
MaybeShmem&& aShmem);
|
||||
RawId DeviceCreateTexture(RawId aSelfId,
|
||||
const dom::GPUTextureDescriptor& aDesc);
|
||||
RawId TextureCreateView(RawId aSelfId, RawId aDeviceId,
|
||||
|
|
|
@ -337,10 +337,26 @@ ipc::IPCResult WebGPUParent::RecvDeviceDestroy(RawId aDeviceId) {
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvCreateBuffer(
|
||||
RawId aDeviceId, RawId aBufferId, dom::GPUBufferDescriptor&& aDesc) {
|
||||
WebGPUParent::BufferMapData* WebGPUParent::GetBufferMapData(RawId aBufferId) {
|
||||
const auto iter = mSharedMemoryMap.find(aBufferId);
|
||||
if (iter == mSharedMemoryMap.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvCreateBuffer(RawId aDeviceId, RawId aBufferId,
|
||||
dom::GPUBufferDescriptor&& aDesc,
|
||||
MaybeShmem&& aShmem) {
|
||||
webgpu::StringHelper label(aDesc.mLabel);
|
||||
|
||||
if (aShmem.type() == MaybeShmem::TShmem) {
|
||||
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
|
||||
dom::GPUBufferUsage_Binding::MAP_READ);
|
||||
mSharedMemoryMap[aBufferId] = {aShmem.get_Shmem(), hasMapFlags};
|
||||
}
|
||||
|
||||
ErrorBuffer error;
|
||||
ffi::wgpu_server_device_create_buffer(mContext.get(), aDeviceId, aBufferId,
|
||||
label.Get(), aDesc.mSize, aDesc.mUsage,
|
||||
|
@ -349,49 +365,61 @@ ipc::IPCResult WebGPUParent::RecvCreateBuffer(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvBufferReturnShmem(RawId aBufferId,
|
||||
Shmem&& aShmem) {
|
||||
MOZ_LOG(sLogger, LogLevel::Info,
|
||||
("RecvBufferReturnShmem %" PRIu64 "\n", aBufferId));
|
||||
mSharedMemoryMap[aBufferId] = aShmem;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// TODO: maintain a list of the requests and disable them when the WebGPUParent
|
||||
// dies.
|
||||
struct MapRequest {
|
||||
const ffi::WGPUGlobal* const mContext;
|
||||
WebGPUParent* mParent;
|
||||
ffi::WGPUGlobal* mContext;
|
||||
ffi::WGPUBufferId mBufferId;
|
||||
ffi::WGPUHostMap mHostMap;
|
||||
uint64_t mOffset;
|
||||
ipc::Shmem mShmem;
|
||||
uint64_t mSize;
|
||||
WebGPUParent::BufferMapResolver mResolver;
|
||||
MapRequest(const ffi::WGPUGlobal* context, ffi::WGPUBufferId bufferId,
|
||||
ffi::WGPUHostMap hostMap, uint64_t offset, ipc::Shmem&& shmem,
|
||||
WebGPUParent::BufferMapResolver&& resolver)
|
||||
: mContext(context),
|
||||
mBufferId(bufferId),
|
||||
mHostMap(hostMap),
|
||||
mOffset(offset),
|
||||
mShmem(shmem),
|
||||
mResolver(resolver) {}
|
||||
MapRequest(WebGPUParent* aParent, ffi::WGPUGlobal* aContext,
|
||||
ffi::WGPUBufferId aBufferId, ffi::WGPUHostMap aHostMap,
|
||||
uint64_t aOffset, uint64_t aSize,
|
||||
WebGPUParent::BufferMapResolver&& aResolver)
|
||||
: mParent(aParent),
|
||||
mContext(aContext),
|
||||
mBufferId(aBufferId),
|
||||
mHostMap(aHostMap),
|
||||
mOffset(aOffset),
|
||||
mSize(aSize),
|
||||
mResolver(aResolver) {}
|
||||
};
|
||||
|
||||
static void MapCallback(ffi::WGPUBufferMapAsyncStatus status,
|
||||
uint8_t* userdata) {
|
||||
auto* req = reinterpret_cast<MapRequest*>(userdata);
|
||||
if (status != ffi::WGPUBufferMapAsyncStatus_Success) {
|
||||
req->mResolver(MaybeShmem(null_t()));
|
||||
} else if (req->mHostMap == ffi::WGPUHostMap_Read) {
|
||||
auto size = req->mShmem.Size<uint8_t>();
|
||||
const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
|
||||
req->mContext, req->mBufferId, req->mOffset, size);
|
||||
BufferMapResult result;
|
||||
|
||||
if (mapped.ptr != nullptr && mapped.length >= size) {
|
||||
memcpy(req->mShmem.get<uint8_t>(), mapped.ptr, size);
|
||||
// TODO: we'll need some logic here along the lines of:
|
||||
// if (!req->mParent->IPCOpen()) { ... }
|
||||
|
||||
auto bufferId = req->mBufferId;
|
||||
auto* mapData = req->mParent->GetBufferMapData(bufferId);
|
||||
MOZ_RELEASE_ASSERT(mapData);
|
||||
|
||||
if (status != ffi::WGPUBufferMapAsyncStatus_Success) {
|
||||
// TODO: construct a proper error message from the status code.
|
||||
nsCString errorString("mapAsync: Failed to map the buffer");
|
||||
result = BufferMapError(errorString);
|
||||
} else {
|
||||
if (req->mHostMap == ffi::WGPUHostMap_Read && req->mSize > 0) {
|
||||
auto size = req->mSize;
|
||||
const auto src = ffi::wgpu_server_buffer_get_mapped_range(
|
||||
req->mContext, req->mBufferId, req->mOffset, size);
|
||||
|
||||
if (src.ptr != nullptr && src.length >= size) {
|
||||
auto dstPtr = mapData->mShmem.get<uint8_t>();
|
||||
memcpy(dstPtr, src.ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
req->mResolver(MaybeShmem(std::move(req->mShmem)));
|
||||
result = BufferMapSuccess(req->mOffset, req->mSize);
|
||||
}
|
||||
|
||||
req->mResolver(std::move(result));
|
||||
delete req;
|
||||
}
|
||||
|
||||
|
@ -402,50 +430,67 @@ ipc::IPCResult WebGPUParent::RecvBufferMap(RawId aBufferId,
|
|||
MOZ_LOG(sLogger, LogLevel::Info,
|
||||
("RecvBufferMap %" PRIu64 " offset=%" PRIu64 " size=%" PRIu64 "\n",
|
||||
aBufferId, aOffset, aSize));
|
||||
auto& shmem = mSharedMemoryMap[aBufferId];
|
||||
if (!shmem.IsReadable()) {
|
||||
aResolver(MaybeShmem(mozilla::null_t()));
|
||||
MOZ_LOG(sLogger, LogLevel::Error, ("\tshmem is empty\n"));
|
||||
|
||||
auto* mapData = GetBufferMapData(aBufferId);
|
||||
|
||||
if (!mapData) {
|
||||
nsCString errorString("Buffer is not mappable");
|
||||
aResolver(BufferMapError(errorString));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
auto* request = new MapRequest(mContext.get(), aBufferId, aHostMap, aOffset,
|
||||
std::move(shmem), std::move(aResolver));
|
||||
// TODO: Actually only map the requested range instead of the whole buffer.
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = mapData->mShmem.Size<uint8_t>();
|
||||
|
||||
auto* request = new MapRequest(this, mContext.get(), aBufferId, aHostMap,
|
||||
offset, size, std::move(aResolver));
|
||||
|
||||
ffi::WGPUBufferMapCallbackC callback = {&MapCallback,
|
||||
reinterpret_cast<uint8_t*>(request)};
|
||||
ffi::wgpu_server_buffer_map(mContext.get(), aBufferId, aOffset, aSize,
|
||||
aHostMap, callback);
|
||||
ffi::wgpu_server_buffer_map(mContext.get(), aBufferId, offset, size, aHostMap,
|
||||
callback);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvBufferUnmap(RawId aBufferId, Shmem&& aShmem,
|
||||
bool aFlush, bool aKeepShmem) {
|
||||
if (aFlush) {
|
||||
auto size = aShmem.Size<uint8_t>();
|
||||
ipc::IPCResult WebGPUParent::RecvBufferUnmap(RawId aBufferId, bool aFlush) {
|
||||
MOZ_LOG(sLogger, LogLevel::Info,
|
||||
("RecvBufferUnmap %" PRIu64 " flush=%d\n", aBufferId, aFlush));
|
||||
|
||||
auto* mapData = GetBufferMapData(aBufferId);
|
||||
|
||||
if (mapData && aFlush) {
|
||||
// TODO: flush exact modified sub-range
|
||||
uint64_t offset = 0;
|
||||
uint64_t size = mapData->mShmem.Size<uint8_t>();
|
||||
uint8_t* srcPtr = mapData->mShmem.get<uint8_t>() + offset;
|
||||
|
||||
const auto mapped = ffi::wgpu_server_buffer_get_mapped_range(
|
||||
mContext.get(), aBufferId, 0, size);
|
||||
MOZ_ASSERT(mapped.ptr != nullptr);
|
||||
MOZ_ASSERT(mapped.length >= size);
|
||||
memcpy(mapped.ptr, aShmem.get<uint8_t>(), size);
|
||||
mContext.get(), aBufferId, offset, size);
|
||||
|
||||
if (mapped.ptr != nullptr && mapped.length >= size) {
|
||||
memcpy(mapped.ptr, srcPtr, size);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::wgpu_server_buffer_unmap(mContext.get(), aBufferId);
|
||||
|
||||
MOZ_LOG(sLogger, LogLevel::Info,
|
||||
("RecvBufferUnmap %" PRIu64 " flush=%d\n", aBufferId, aFlush));
|
||||
if (mapData && !mapData->mHasMapFlags) {
|
||||
// We get here if the buffer was mapped at creation without map flags.
|
||||
// We don't need the shared memory anymore.
|
||||
DeallocBufferShmem(aBufferId);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void WebGPUParent::DeallocBufferShmem(RawId aBufferId) {
|
||||
const auto iter = mSharedMemoryMap.find(aBufferId);
|
||||
if (iter != mSharedMemoryMap.end()) {
|
||||
iter->second = aShmem;
|
||||
} else if (aKeepShmem) {
|
||||
mSharedMemoryMap[aBufferId] = aShmem;
|
||||
} else {
|
||||
// we are here if the buffer was mapped at creation, but doesn't have any
|
||||
// mapping flags
|
||||
DeallocShmem(aShmem);
|
||||
DeallocShmem(iter->second.mShmem);
|
||||
mSharedMemoryMap.erase(iter);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult WebGPUParent::RecvBufferDestroy(RawId aBufferId) {
|
||||
|
@ -453,11 +498,8 @@ ipc::IPCResult WebGPUParent::RecvBufferDestroy(RawId aBufferId) {
|
|||
MOZ_LOG(sLogger, LogLevel::Info,
|
||||
("RecvBufferDestroy %" PRIu64 "\n", aBufferId));
|
||||
|
||||
const auto iter = mSharedMemoryMap.find(aBufferId);
|
||||
if (iter != mSharedMemoryMap.end()) {
|
||||
DeallocShmem(iter->second);
|
||||
mSharedMemoryMap.erase(iter);
|
||||
}
|
||||
DeallocBufferShmem(aBufferId);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,13 +35,13 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
ipc::IPCResult RecvAdapterDestroy(RawId aAdapterId);
|
||||
ipc::IPCResult RecvDeviceDestroy(RawId aDeviceId);
|
||||
ipc::IPCResult RecvCreateBuffer(RawId aDeviceId, RawId aBufferId,
|
||||
dom::GPUBufferDescriptor&& aDesc);
|
||||
dom::GPUBufferDescriptor&& aDesc,
|
||||
MaybeShmem&& aShmem);
|
||||
ipc::IPCResult RecvBufferReturnShmem(RawId aBufferId, Shmem&& aShmem);
|
||||
ipc::IPCResult RecvBufferMap(RawId aBufferId, ffi::WGPUHostMap aHostMap,
|
||||
uint64_t aOffset, uint64_t size,
|
||||
BufferMapResolver&& aResolver);
|
||||
ipc::IPCResult RecvBufferUnmap(RawId aBufferId, Shmem&& aShmem, bool aFlush,
|
||||
bool aKeepShmem);
|
||||
ipc::IPCResult RecvBufferUnmap(RawId aBufferId, bool aFlush);
|
||||
ipc::IPCResult RecvBufferDestroy(RawId aBufferId);
|
||||
ipc::IPCResult RecvTextureDestroy(RawId aTextureId);
|
||||
ipc::IPCResult RecvTextureViewDestroy(RawId aTextureViewId);
|
||||
|
@ -104,7 +104,17 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
struct BufferMapData {
|
||||
Shmem mShmem;
|
||||
// True if buffer's usage has MAP_READ or MAP_WRITE set.
|
||||
bool mHasMapFlags;
|
||||
};
|
||||
|
||||
BufferMapData* GetBufferMapData(RawId aBufferId);
|
||||
|
||||
private:
|
||||
void DeallocBufferShmem(RawId aBufferId);
|
||||
|
||||
virtual ~WebGPUParent();
|
||||
void MaintainDevices();
|
||||
bool ForwardError(RawId aDeviceId, ErrorBuffer& aError);
|
||||
|
@ -112,10 +122,11 @@ class WebGPUParent final : public PWebGPUParent {
|
|||
|
||||
UniquePtr<ffi::WGPUGlobal> mContext;
|
||||
base::RepeatingTimer<WebGPUParent> mTimer;
|
||||
/// Shmem associated with a mappable buffer has to be owned by one of the
|
||||
/// processes. We keep it here for every mappable buffer while the buffer is
|
||||
/// used by GPU.
|
||||
std::unordered_map<uint64_t, Shmem> mSharedMemoryMap;
|
||||
|
||||
/// A map from wgpu buffer ids to data about their shared memory segments.
|
||||
/// Includes entries about mappedAtCreation, MAP_READ and MAP_WRITE buffers,
|
||||
/// regardless of their state.
|
||||
std::unordered_map<uint64_t, BufferMapData> mSharedMemoryMap;
|
||||
/// Associated presentation data for each swapchain.
|
||||
std::unordered_map<uint64_t, RefPtr<PresentationData>> mCanvasMap;
|
||||
/// Associated stack of error scopes for each device.
|
||||
|
|
Загрузка…
Ссылка в новой задаче