2018-11-30 19:52:05 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2018-06-13 17:43:48 +00:00
|
|
|
/* 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/. */
|
|
|
|
|
2019-12-10 17:07:18 +00:00
|
|
|
#include "js/ArrayBuffer.h"
|
|
|
|
#include "js/Value.h"
|
2020-02-24 22:54:54 +00:00
|
|
|
#include "mozilla/Logging.h"
|
2020-02-24 20:12:16 +00:00
|
|
|
#include "mozilla/ipc/Shmem.h"
|
|
|
|
#include "mozilla/dom/WebGPUBinding.h"
|
2018-06-13 17:43:48 +00:00
|
|
|
#include "Device.h"
|
2020-11-16 19:57:04 +00:00
|
|
|
#include "CommandEncoder.h"
|
|
|
|
#include "BindGroup.h"
|
2018-06-13 17:43:48 +00:00
|
|
|
|
|
|
|
#include "Adapter.h"
|
2020-02-24 20:12:16 +00:00
|
|
|
#include "Buffer.h"
|
2020-03-03 16:37:02 +00:00
|
|
|
#include "ComputePipeline.h"
|
|
|
|
#include "Queue.h"
|
|
|
|
#include "RenderPipeline.h"
|
2020-02-24 20:12:16 +00:00
|
|
|
#include "Sampler.h"
|
|
|
|
#include "Texture.h"
|
|
|
|
#include "TextureView.h"
|
2019-11-14 04:59:56 +00:00
|
|
|
#include "ipc/WebGPUChild.h"
|
2018-06-13 17:43:48 +00:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace webgpu {
|
|
|
|
|
2019-12-10 17:07:18 +00:00
|
|
|
mozilla::LazyLogModule gWebGPULog("WebGPU");
|
|
|
|
|
2020-01-22 07:31:51 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(Device, DOMEventTargetHelper, mBridge,
|
|
|
|
mQueue)
|
2019-10-02 16:46:03 +00:00
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device, DOMEventTargetHelper)
|
|
|
|
GPU_IMPL_JS_WRAP(Device)
|
2019-10-02 03:04:25 +00:00
|
|
|
|
2019-12-10 17:07:18 +00:00
|
|
|
static void mapFreeCallback(void* aContents, void* aUserData) {
|
|
|
|
Unused << aContents;
|
|
|
|
Unused << aUserData;
|
|
|
|
}
|
|
|
|
|
2020-04-06 22:29:18 +00:00
|
|
|
RefPtr<WebGPUChild> Device::GetBridge() { return mBridge; }
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
JSObject* Device::CreateExternalArrayBuffer(JSContext* aCx, size_t aOffset,
|
|
|
|
size_t aSize,
|
|
|
|
const ipc::Shmem& aShmem) {
|
|
|
|
MOZ_ASSERT(aOffset + aSize <= aShmem.Size<uint8_t>());
|
|
|
|
return JS::NewExternalArrayBuffer(aCx, aSize, aShmem.get<uint8_t>() + aOffset,
|
2019-12-10 17:07:18 +00:00
|
|
|
&mapFreeCallback, nullptr);
|
|
|
|
}
|
|
|
|
|
2019-11-14 04:59:56 +00:00
|
|
|
Device::Device(Adapter* const aParent, RawId aId)
|
|
|
|
: DOMEventTargetHelper(aParent->GetParentObject()),
|
2020-01-24 16:27:09 +00:00
|
|
|
mBridge(aParent->mBridge),
|
2020-01-22 07:31:51 +00:00
|
|
|
mId(aId),
|
2020-01-24 16:27:09 +00:00
|
|
|
mQueue(new Queue(this, aParent->mBridge, aId)) {}
|
2019-11-14 04:59:56 +00:00
|
|
|
|
2020-01-22 07:31:51 +00:00
|
|
|
Device::~Device() { Cleanup(); }
|
|
|
|
|
|
|
|
void Device::Cleanup() {
|
|
|
|
if (mValid && mBridge && mBridge->IsOpen()) {
|
|
|
|
mValid = false;
|
2019-11-14 04:59:56 +00:00
|
|
|
mBridge->SendDeviceDestroy(mId);
|
|
|
|
}
|
|
|
|
}
|
2018-06-13 17:43:48 +00:00
|
|
|
|
2019-10-02 16:46:03 +00:00
|
|
|
void Device::GetLabel(nsAString& aValue) const { aValue = mLabel; }
|
|
|
|
void Device::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
|
2018-06-13 17:43:48 +00:00
|
|
|
|
2020-01-22 07:31:51 +00:00
|
|
|
Queue* Device::DefaultQueue() const { return mQueue; }
|
|
|
|
|
2019-12-10 17:07:18 +00:00
|
|
|
already_AddRefed<Buffer> Device::CreateBuffer(
|
2020-10-19 17:19:12 +00:00
|
|
|
const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
|
2019-12-10 17:07:18 +00:00
|
|
|
ipc::Shmem shmem;
|
2020-10-19 17:19:12 +00:00
|
|
|
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
|
|
|
|
dom::GPUBufferUsage_Binding::MAP_READ);
|
|
|
|
if (hasMapFlags || aDesc.mMappedAtCreation) {
|
|
|
|
const auto checked = CheckedInt<size_t>(aDesc.mSize);
|
|
|
|
if (!checked.isValid()) {
|
|
|
|
aRv.ThrowRangeError("Mappable size is too large");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const auto& size = checked.value();
|
|
|
|
|
|
|
|
// TODO: use `ShmemPool`?
|
|
|
|
if (!mBridge->AllocShmem(size, ipc::Shmem::SharedMemory::TYPE_BASIC,
|
|
|
|
&shmem)) {
|
|
|
|
aRv.ThrowAbortError(
|
|
|
|
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// zero out memory
|
|
|
|
memset(shmem.get<uint8_t>(), 0, size);
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
// 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);
|
|
|
|
if (hasMapFlags && !aDesc.mMappedAtCreation) {
|
|
|
|
mBridge->SendBufferReturnShmem(id, std::move(shmem));
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
RefPtr<Buffer> buffer = new Buffer(this, id, aDesc.mSize);
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
if (aDesc.mMappedAtCreation) {
|
|
|
|
buffer->SetMapped(std::move(shmem),
|
|
|
|
!(aDesc.mUsage & dom::GPUBufferUsage_Binding::MAP_READ));
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
return buffer.forget();
|
2020-10-19 16:28:27 +00:00
|
|
|
}
|
2020-10-19 15:08:55 +00:00
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
RefPtr<MappingPromise> Device::MapBufferAsync(RawId aId, uint32_t aMode,
|
|
|
|
size_t aOffset, size_t aSize,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
ffi::WGPUHostMap mode;
|
|
|
|
switch (aMode) {
|
|
|
|
case dom::GPUMapMode_Binding::READ:
|
|
|
|
mode = ffi::WGPUHostMap_Read;
|
|
|
|
break;
|
|
|
|
case dom::GPUMapMode_Binding::WRITE:
|
|
|
|
mode = ffi::WGPUHostMap_Write;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
aRv.ThrowInvalidAccessError(
|
|
|
|
nsPrintfCString("Invalid map flag %u", aMode));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const CheckedInt<uint64_t> offset(aOffset);
|
|
|
|
if (!offset.isValid()) {
|
|
|
|
aRv.ThrowRangeError("Mapped offset is too large");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
const CheckedInt<uint64_t> size(aSize);
|
|
|
|
if (!size.isValid()) {
|
|
|
|
aRv.ThrowRangeError("Mapped size is too large");
|
2019-12-10 17:07:18 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
return mBridge->SendBufferMap(aId, mode, offset.value(), size.value());
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 17:19:12 +00:00
|
|
|
void Device::UnmapBuffer(RawId aId, ipc::Shmem&& aShmem, bool aFlush) {
|
|
|
|
mBridge->SendBufferUnmap(aId, std::move(aShmem), aFlush);
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-24 20:12:16 +00:00
|
|
|
already_AddRefed<Texture> Device::CreateTexture(
|
|
|
|
const dom::GPUTextureDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreateTexture(mId, aDesc);
|
|
|
|
RefPtr<Texture> texture = new Texture(this, id, aDesc);
|
|
|
|
return texture.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Sampler> Device::CreateSampler(
|
|
|
|
const dom::GPUSamplerDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreateSampler(mId, aDesc);
|
|
|
|
RefPtr<Sampler> sampler = new Sampler(this, id);
|
|
|
|
return sampler.forget();
|
|
|
|
}
|
|
|
|
|
2020-01-22 07:31:51 +00:00
|
|
|
already_AddRefed<CommandEncoder> Device::CreateCommandEncoder(
|
|
|
|
const dom::GPUCommandEncoderDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreateCommandEncoder(mId, aDesc);
|
|
|
|
RefPtr<CommandEncoder> encoder = new CommandEncoder(this, mBridge, id);
|
|
|
|
return encoder.forget();
|
|
|
|
}
|
|
|
|
|
2020-01-24 16:27:09 +00:00
|
|
|
already_AddRefed<BindGroupLayout> Device::CreateBindGroupLayout(
|
|
|
|
const dom::GPUBindGroupLayoutDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreateBindGroupLayout(mId, aDesc);
|
|
|
|
RefPtr<BindGroupLayout> object = new BindGroupLayout(this, id);
|
|
|
|
return object.forget();
|
|
|
|
}
|
|
|
|
already_AddRefed<PipelineLayout> Device::CreatePipelineLayout(
|
|
|
|
const dom::GPUPipelineLayoutDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreatePipelineLayout(mId, aDesc);
|
|
|
|
RefPtr<PipelineLayout> object = new PipelineLayout(this, id);
|
|
|
|
return object.forget();
|
|
|
|
}
|
|
|
|
already_AddRefed<BindGroup> Device::CreateBindGroup(
|
|
|
|
const dom::GPUBindGroupDescriptor& aDesc) {
|
|
|
|
RawId id = mBridge->DeviceCreateBindGroup(mId, aDesc);
|
|
|
|
RefPtr<BindGroup> object = new BindGroup(this, id);
|
|
|
|
return object.forget();
|
2020-01-24 05:05:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 16:27:09 +00:00
|
|
|
already_AddRefed<ShaderModule> Device::CreateShaderModule(
|
|
|
|
const dom::GPUShaderModuleDescriptor& aDesc) {
|
2020-10-19 17:19:12 +00:00
|
|
|
if (aDesc.mCode.IsString()) {
|
|
|
|
// we don't yet support WGSL
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-01-24 16:27:09 +00:00
|
|
|
RawId id = mBridge->DeviceCreateShaderModule(mId, aDesc);
|
|
|
|
RefPtr<ShaderModule> object = new ShaderModule(this, id);
|
|
|
|
return object.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<ComputePipeline> Device::CreateComputePipeline(
|
|
|
|
const dom::GPUComputePipelineDescriptor& aDesc) {
|
2020-11-13 14:15:49 +00:00
|
|
|
nsTArray<RawId> implicitBindGroupLayoutIds;
|
|
|
|
RawId id = mBridge->DeviceCreateComputePipeline(mId, aDesc,
|
|
|
|
&implicitBindGroupLayoutIds);
|
|
|
|
RefPtr<ComputePipeline> object =
|
|
|
|
new ComputePipeline(this, id, std::move(implicitBindGroupLayoutIds));
|
2020-01-24 16:27:09 +00:00
|
|
|
return object.forget();
|
2019-12-10 17:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 16:37:02 +00:00
|
|
|
already_AddRefed<RenderPipeline> Device::CreateRenderPipeline(
|
|
|
|
const dom::GPURenderPipelineDescriptor& aDesc) {
|
2020-11-13 14:15:49 +00:00
|
|
|
nsTArray<RawId> implicitBindGroupLayoutIds;
|
|
|
|
RawId id = mBridge->DeviceCreateRenderPipeline(mId, aDesc,
|
|
|
|
&implicitBindGroupLayoutIds);
|
|
|
|
RefPtr<RenderPipeline> object =
|
|
|
|
new RenderPipeline(this, id, std::move(implicitBindGroupLayoutIds));
|
2020-03-03 16:37:02 +00:00
|
|
|
return object.forget();
|
|
|
|
}
|
|
|
|
|
2020-04-06 22:29:18 +00:00
|
|
|
already_AddRefed<Texture> Device::InitSwapChain(
|
|
|
|
const dom::GPUSwapChainDescriptor& aDesc,
|
|
|
|
const dom::GPUExtent3DDict& aExtent3D, wr::ExternalImageId aExternalImageId,
|
|
|
|
gfx::SurfaceFormat aFormat) {
|
|
|
|
const layers::RGBDescriptor rgbDesc(
|
|
|
|
gfx::IntSize(AssertedCast<int>(aExtent3D.mWidth),
|
|
|
|
AssertedCast<int>(aExtent3D.mHeight)),
|
|
|
|
aFormat, false);
|
|
|
|
// buffer count doesn't matter much, will be created on demand
|
|
|
|
const size_t maxBufferCount = 10;
|
|
|
|
mBridge->DeviceCreateSwapChain(mId, rgbDesc, maxBufferCount,
|
|
|
|
aExternalImageId);
|
|
|
|
|
|
|
|
dom::GPUTextureDescriptor desc;
|
|
|
|
desc.mDimension = dom::GPUTextureDimension::_2d;
|
|
|
|
desc.mSize.SetAsGPUExtent3DDict() = aExtent3D;
|
|
|
|
desc.mFormat = aDesc.mFormat;
|
|
|
|
desc.mMipLevelCount = 1;
|
|
|
|
desc.mSampleCount = 1;
|
|
|
|
desc.mUsage = aDesc.mUsage | dom::GPUTextureUsage_Binding::COPY_SRC;
|
|
|
|
return CreateTexture(desc);
|
|
|
|
}
|
|
|
|
|
2018-06-13 17:43:48 +00:00
|
|
|
} // namespace webgpu
|
|
|
|
} // namespace mozilla
|